Подтвердить что ты не робот

Как мне получить Emacs для заполнения предложений, но не абзацев?

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

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

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

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

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

Я думаю, что я хочу, чтобы

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

То есть:

чат-чат.
Новое предложение
с закрытой упаковкой, которая должна быть исправлена.
Mumble mumble

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

чат-чат.
Новое предложение с завершающей упаковкой, которая должна быть исправлена.
Mumble mumble

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


Изменить: предложение Jouni K. Seppänen указал мне на LaTeX-fill-break-at-separators, что говорит о том, что emacs почти знает, как это сделать. Во всяком случае, я прочь прочитать какой-то код и отчитаться. Еще раз спасибо.


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

4b9b3361

Ответ 1

Здесь то, что я использую, которое в основном было вырезано из 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)))

Ответ 2

Если вы поместите маркер комментария в конце каждого предложения, 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-paragraph и изменить его, чтобы препинание в конце строки заканчивалось так же, как и комментарии.

Ответ 3

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

(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)

Ответ 4

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

(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 (я думаю).

Ответ 5

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

Есть несколько подходов, которые вы можете предпринять:

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

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

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

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

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

Ответ 6

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

;; - go to the end of the line,
;; - do ^d to suck the previous line onto this one, 
;; - make sure there 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

Ответ 7

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

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

Полный макрос длинный, поэтому я не буду публиковать его здесь. Идея состоит в том, чтобы добавить следующие циклы в 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

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

Ответ 8

(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)

Ответ 9

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

Ответ 10

Я написал следующее, которое пересекает область и вставляет новые строки. Вместо использования forward-sentence, который не работал у меня, я использую re-search-forward "[.?!][]\"')}]*\\( \\)", который находит все предложения, которые следуют только двумя пробелами (regexp является измененным 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).