Как заставить Emacs заполнять предложения, а не абзацы? - PullRequest
27 голосов
/ 12 февраля 2009

Я видел как минимум две рекомендации в StackOverflow для вставки новых строк между предложениями при редактировании документов LaTeX. Причина в том, что эта практика облегчает контроль исходного кода, diff ing и совместное редактирование.

Я в основном убежден, но мне лень, и я не хочу думать об этом.

Так что я ищу заклинание emacs, чтобы справиться с ним. Может быть второстепенным режимом, может быть набором переменных, которые необходимо установить.

Я думаю, что я не хочу, это

  • Мягкая переноска текста (скажем, с использованием ярусов и (set long-lines-auto-wrap 't)). Это потому, что я не хочу навязывать требования редакторам моих соавторов, и I иногда использует другие инструменты Unix для проверки этих файлов.

Я думаю, что я делаю хочу это

  • Для fill-paragraph, чтобы заполнить между новыми строками, которые выглядят так, как будто они отмечают конец предложения.
  • Решение, которое работает с auto-fill-mode, будет бонусом.

То есть:

чат чат чат.
Новое предложение
с дурацкой оберткой, которую нужно починить.
Бормотать бормотать

Преобразовано в:

чат чат чат.
Новое предложение с дурацкой упаковкой, которую нужно исправить.
Бормотать бормотать

Ваши комментарии и предложения приветствуются.


Редактировать: Предложение Джуни К. Сеппанен указало мне на LaTeX-fill-break-at-separators, что говорит о том, что emacs уже почти знает, как это сделать. Во всяком случае, я прочитаю код и сообщу. Еще раз спасибо.


Более общая версия того же вопроса: Редактор вскрытия: поддержка новых строк в конце предложений . Спасибо, Дреев .

Ответы [ 10 ]

8 голосов
/ 13 февраля 2009

Вот что я использую , которое в основном было взято из Luca de Alfaro :

(defun fill-sentence ()
  (interactive)
  (save-excursion
    (or (eq (point) (point-max)) (forward-char))
    (forward-sentence -1)
    (indent-relative t)
    (let ((beg (point))
          (ix (string-match "LaTeX" mode-name)))
      (forward-sentence)
      (if (and ix (equal "LaTeX" (substring mode-name ix)))
          (LaTeX-fill-region-as-paragraph beg (point))
        (fill-region-as-paragraph beg (point))))))

Я связываю это с M-j с

(global-set-key (kbd "M-j") 'fill-sentence)

Ссылки на "LaTeX" предназначены для поддержки AUCTeX . Если вы не используете AUCTeX, let можно упростить до

(let (beg (point))
  (forward-sentence)
  (fill-region-as-paragraph beg (point)))
3 голосов
/ 22 февраля 2012

Я хотел сделать это навсегда, и недавно я нашел этот блог , который работал довольно хорошо для меня. Итак, вот (слегка измененная версия) то, что я использовал в течение нескольких дней.

(defun auto-fill-by-sentences ()
  (if (looking-back (sentence-end))
      ;; Break at a sentence
      (progn
        (LaTeX-newline)
        t)
    ;; Fall back to the default
    (do-auto-fill)))
(add-hook 'LaTeX-mode-hook (lambda () (setq auto-fill-function 'auto-fill-by-sentences)))

;; Modified from http://pleasefindattached.blogspot.com/2011/12/emacsauctex-sentence-fill-greatly.html
(defadvice LaTeX-fill-region-as-paragraph (around LaTeX-sentence-filling)
  "Start each sentence on a new line."
  (let ((from (ad-get-arg 0))
        (to-marker (set-marker (make-marker) (ad-get-arg 1)))
        tmp-end)
    (while (< from (marker-position to-marker))
      (forward-sentence)
      ;; might have gone beyond to-marker---use whichever is smaller:
      (ad-set-arg 1 (setq tmp-end (min (point) (marker-position to-marker))))
      ad-do-it
      (ad-set-arg 0 (setq from (point)))
      (unless (or (looking-back "^\\s *")
                  (looking-at "\\s *$"))
        (LaTeX-newline)))
    (set-marker to-marker nil)))
(ad-activate 'LaTeX-fill-region-as-paragraph)
3 голосов
/ 12 февраля 2009

Если вы поставите маркер комментария в конце каждого предложения, Emacs знает, что не следует перемещать следующую строку внутри комментария:

chat chat chat.%
A new sentence
with goofed up wrapping that needs to be fixed.%
Mumble mumble%

Тогда M-q заполняет каждое предложение отдельно, по крайней мере, в AUCTeX 11.85. (Если вы проверяете это в Emacs, кажется, есть ошибка, когда, если это первый абзац в буфере и вы вводите Mq, вы получаете сообщение об ошибке. Просто поместите новую строку перед текстом, чтобы обойти это.)

Если вы не хотите вводить символы комментария, вы можете взять LaTeX-fill-абзац и изменить его так, чтобы пунктуация, заканчивающаяся предложением, в конце строки работала аналогично комментариям.

2 голосов
/ 12 февраля 2009

Может работать не при всех обстоятельствах, но:

(defun my-fill-sentence ()
  "Fill sentence separated by punctuation or blank lines."
  (interactive)
  (let (start end)
    (save-excursion
      (re-search-backward "\\(^\\s-*$\\|[.?!]\\)" nil t)
      (skip-syntax-forward "^w")
      (setq start (point-at-bol)))
    (save-excursion
      (re-search-forward "\\(^\\s-*$\\|[.?!]\\)" nil t)
      (setq end (point-at-eol)))
    (save-restriction
      (narrow-to-region start end)
      (fill-paragraph nil))))

Чтобы заставить его работать с auto-fill-mode, добавьте (setq normal-auto-fill-function 'my-fill-sentence) к вашей ловушке режима LaTeX (я думаю).

1 голос
/ 05 мая 2014

Я написал следующее, которое проходит по региону и вставляет переводы строки. Вместо использования forward-sentence, который не работал для меня, я использую re-search-forward "[.?!][]\"')}]*\\( \\)", который находит все предложения, за которыми следуют только два пробела (регулярное выражение является модифицированным sentence-end). Новая строка сделана с использованием newline-and-indent.

(defun fill-sentences-in-paragraph ()
  "Put a newline at the end of each sentence in paragraph."
  (interactive)
  (save-excursion
    (mark-paragraph)
    (call-interactively 'fill-sentences-in-region)))

(defun fill-sentences-in-region (start end)
  "Put a newline at the end of each sentence in region."
  (interactive "*r")
  (call-interactively 'unfill-region)
  (save-excursion
    (goto-char start)
    (while (re-search-forward "[.?!][]\"')}]*\\(  \\)" end t)
      (newline-and-indent))))

Чтобы иметь возможность исправить неправильно отформатированный текст, такой как пример "чат чат чат ...", fill-sentences-in-region первые вызовы unfill-region, который избавляет от пробела, разбивающего предложения:

   (defun unfill-region (beg end)
      "Unfill the region, joining text paragraphs into a
       single logical line.  This is useful, e.g., for use
       with 'visual-line-mode'."
      (interactive "*r")
      (let ((fill-column (point-max)))
        (fill-region beg end)))

Я использую visual-line-mode и заменяю заполнение абзаца по умолчанию M-q на fill-sentences-in-paragraph на (global-set-key "\M-q" 'fill-sentences-in-paragraph).

1 голос
/ 05 сентября 2013

Альтернативным подходом было бы оставить ваш файл .tex как есть и использовать такой инструмент, как latexdiff (описано в этом сообщении StackExchange) вместо различий Unix. Это создает файл .tex с метками изменений трека в стиле Word и корректно обрабатывает пробелы, поэтому вам не нужно беспокоиться о том, где заканчиваются ваши предложения.

1 голос
/ 29 января 2012
(defun wrap-at-sentences ()
  "Fills the current paragraph, but starts each sentence on a new line."
  (interactive)
  (save-excursion
    ;; Select the entire paragraph.
    (mark-paragraph)
    ;; Move to the start of the paragraph.
    (goto-char (region-beginning))
    ;; Record the location of the end of the paragraph.
    (setq end-of-paragraph (region-end))
    ;; Wrap lines with 'hard' newlines (i.e., real line breaks).
    (let ((use-hard-newlines 't))
      ;; Loop over each sentence in the paragraph.
      (while (< (point) end-of-paragraph)
        ;; Determine the region spanned by the sentence.
        (setq start-of-sentence (point))
        (forward-sentence)
        ;; Wrap the sentence with hard newlines.
        (fill-region start-of-sentence (point))
        ;; Delete the whitespace following the period, if any.
        (while (char-equal (char-syntax (preceding-char)) ?\s)
          (delete-char -1))
        ;; Insert a newline before the next sentence.
        (insert "\n")))))

(global-set-key (kbd "M-q") 'wrap-at-sentences)
1 голос
/ 24 мая 2011

Мне очень нравится макрос Криса Конвея, но он работает только после того, как вы вручную разбиваете каждое предложение. Я ленивый парень, поэтому хочу, чтобы emacs сделал это для меня. Этим утром я наконец сел и изучил проблему. Решение, которое я имею сейчас, состоит в том, чтобы взломать встроенный макрос fill-region-as-paragraph.

После применения следующего хака новая опция newline-after-sentence будет установлена ​​в значение true. Стандартный M-q (fill-paragraph) автоматически заполнит и создаст разрывы строк между предложениями. Обратите внимание, что тесты выполняются только с GNU Emacs 23.3.1 & mdash; используйте его на свой страх и риск.

Полный макрос длинный, поэтому я не буду публиковать его здесь. Идея состоит в том, чтобы добавить следующие циклы в fill-region-as-paragraph

...

;; Insert a line break after each sentence
(while (< (point) to)
  (forward-sentence)
  (if (< (point) to) (fill-newline)))

;; This is the actual filling loop.
(goto-char from)
(let (sentbeg sentend)
  (while (< (point) to)
    (setq sentbeg (point))
    (end-of-line)
    (setq sentend (point))
    (fill-one-line sentbeg sentend justify) ;; original filling loop
    (forward-line)))))

...

Полный макрос можно найти в моем репозитории git . Некоторые подробности также написаны в моем блоге . Если вы не хотите читать мой плохой английский, вы можете просто использовать

$ curl http://fermi.mycloudnas.com/cgit.cgi/fill/plain/hack.el >> ~/.emacs

, чтобы добавить хак к вашему ~/.emacs и попробовать. Все комментарии и сообщения об ошибках приветствуются.

1 голос
/ 12 февраля 2009

Если другие ответы тоже автоматические, вот полуавтоматический подход. Это в основном то, что вы бы делали многократно, если бы вы собирались переформатировать вручную, но сжато, чтобы вместо этого вы могли нажимать одну клавишу несколько раз.

;; - go to the end of the line,
;; - do ^d to suck the previous line onto this one, 
;; - make sure there's only one space between the now-concatenated
;;   lines, and then 
;; - jump to the end and hit space so that (with auto-fill-mode)
;;   the line nicely rewraps itself:
;;   (turn on auto-fill-mode with M-x auto-fill-mode)
(defalias 'fill-sentence
  (read-kbd-macro "C-e C-d SPC M-x just- one- space RET C-e SPC <backspace>"))

(define-key global-map [f4] 'fill-sentence)  ; or whatever key you like
1 голос
/ 12 февраля 2009

Я предполагаю, что вы знаете elisp.

Существует несколько подходов:

  • Зацепиться за auto-fill-mode. Есть много жестко закодированных условности там, так что это может не сработать для вас. Вы можете потенциально играть с auto-fill-function и посмотреть, есть ли у вас крючок, который вам нужен.

  • Сделайте символ (вероятно, .) "электрическим", чтобы при нажатии это, он вставляет себя и затем вызывает функцию, чтобы определить, как чтобы заполнить строку, на которой вы находитесь.

  • Установите after-change-hook для вызова функции, которая определяет, как заполнить предложение. Эта функция будет вызываться после каждого Перейдите в буфер, так что делайте это эффективно. (Этот механизм используется font-lock, так что не беспокойтесь об этом. Звучит медленно, но на самом деле нет - люди печатают медленно.)

Как только вы зацепились в нужном месте, вам просто нужно реализовать логика заполнения. Источник для sentence-at-point (из thingatpt) может быть поучительно.

В любом случае, я никогда не слышал, чтобы кто-нибудь делал это ... но это определенно возможно. Как и большинство вещей в Emacs, это просто вопрос программирования.

...