Как мне получить 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.
Ответы
Ответ 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)
.