31

我在 StackOverflow 上看到了至少两个建议,在编辑 LaTeX 文档时在句子之间插入换行符。原因是这种做法有助于源代码控制、学习diff和协作编辑。

我基本信服,只是我懒,不想多想。

所以我正在寻找一些emacs咒语来为我处理它。可能是次要模式,可能是一组需要设置的变量。

我想我想要的是

  • 文本的软换行(比如使用长线(set long-lines-auto-wrap 't))。这是因为我不想对合作者的编辑提出要求,而且有时会使用其他 unix 工具来检查这些文件。

我想我想要的是

  • 用于fill-paragraph在看起来像是标记句子结尾的换行符之间填充。
  • 一个可行的解决方案auto-fill-mode将是一个奖励。

那是:

聊天聊天。
一个带有错误包装的新句子
,需要修复。
喃喃自语

转换为:

聊天聊天。
一个带有错误包装的新句子,需要修复。
喃喃自语

感谢您的意见和建议。


编辑:Jouni K. Seppänen的建议指出了我LaTeX-fill-break-at-separators,这表明 emacs 几乎已经知道如何做到这一点。无论如何,我要去阅读一些代码,并会报告。再次感谢。


同一问题的更通用版本:编辑摊牌:在句子末尾保持换行符。谢谢,德雷夫斯

4

10 回答 10

10

是我使用的,主要来自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)))
于 2009-02-12T22:29:34.007 回答
5

我一直想永远这样做,最近我发现这篇博文对我来说效果很好。所以这是我几天来一直在使用的(稍微修改过的版本)。

(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)
于 2012-02-22T11:20:29.980 回答
4

如果你在每个句子的末尾放置一个注释标记,Emacs 就知道不要在注释中移动下一行:

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

然后 Mq 分别填充每个句子,至少在 AUCTeX 11.85 中。(如果您在 Emacs 中对此进行测试,似乎存在一个错误,如果这是缓冲区中的第一段并且您键入 Mq,您会收到一条错误消息。只需在文本前添加一个换行符即可解决它。)

如果您不想输入注释字符,您可以使用 LaTeX-fill-paragraph 并对其进行修改,以便行尾的句尾标点符号与注释类似。

于 2009-02-12T10:35:20.560 回答
3
(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)
于 2012-01-29T10:20:36.030 回答
2

May not work in all circumstances, but:

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

To make it work with auto-fill-mode, add (setq normal-auto-fill-function 'my-fill-sentence) to your LaTeX mode hook (I think).

于 2009-02-12T14:28:24.447 回答
1

I wrote the following which loops over a region and inserts newlines. Instead of using forward-sentence which didn't work for me, I use re-search-forward "[.?!][]\"')}]*\\( \\)", which finds all sentences followed only by two spaces (the regexp is a modified sentence-end). The newline is made using 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))))

To be able to fix improperly formatted text such as the example "chat chat chat...", fill-sentences-in-region first calls unfill-region which gets rid of sentence-breaking whitespace:

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

I use visual-line-mode and replace my default paragraph fill M-q to fill-sentences-in-paragraph with (global-set-key "\M-q" 'fill-sentences-in-paragraph).

于 2014-05-05T10:10:52.640 回答
1

如果其他答案过于自动,这是一种半自动的方法。如果您要手动重新格式化,这基本上是您会反复执行的操作,但经过浓缩,您可以反复敲击单个键。

;; - 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
于 2009-02-12T15:43:30.787 回答
1

我假设你知道 elisp。

您可以采取以下几种方法:

  • 钩入auto-fill-mode. 那里有很多硬编码的条件,所以它可能不适合你。你可能会玩,auto-fill-function看看你是否有你需要的钩子。

  • 使一个字符(可能.)“电动”,这样当你按下它时,它会插入自己,然后调用一个函数来确定如何填充你所在的行。

  • 设置一个after-change-hook来调用一个确定如何填充句子的函数。每次更改缓冲区后都会调用此函数,因此请有效地执行此操作。(这个机制被 使用font-lock,所以不要太担心它。听起来很慢,但实际上不是——人们打字很慢。)

一旦你在正确的位置上钩了,你只需要实现填充逻辑。sentence-at-point(from )的来源thingatpt可能具有指导意义。

无论如何,我从未听说过有人这样做……但这绝对是可能的。像 Emacs 中的大多数东西一样,它只是一个简单的编程问题。

于 2009-02-12T10:37:18.340 回答
1

我非常喜欢 Chris Conway 的宏,但它仅在您手动换行每个句子后才有效。我是一个懒惰的人,所以我希望 emacs 为我做这件事。今天早上我终于坐下来研究了这个问题。我现在的解决方案是破解内置宏fill-region-as-paragraph

应用以下 hack 后,一个新选项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并尝试一下。欢迎评论和错误报告。

于 2011-05-23T21:42:11.827 回答
1

另一种方法是保留您的 .tex 文件,并使用类似 latexdiff 的工具(在StackExchange帖子中描述)而不是 Unix diff。这会生成一个带有 Word 样式的跟踪更改标记的 .tex 文件,并正确处理空格,因此您不必担心句子的结尾。

于 2013-09-05T18:08:08.000 回答