如何让Emacs填充句子,而不是段落?

时间:2023-02-09 20:21:53

I've seen at least two recommendations on * to insert newlines between sentences when editing LaTeX documents. The reason being that the practice facilitates source control, diffing, and collaborative editing.

我已经看到*上至少有两条建议在编辑LaTeX文档时在句子之间插入换行符。原因是该练习有助于源代码控制,差异化和协作编辑。

I'm basically convinced, but I'm lazy, and I don't want to have to think about it.

我基本上确信,但我很懒,而且我不想考虑它。

So I'm searching for some emacs incantation to handle it for me. Could be a minor mode, could be a set of variables that need to be set.

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

I think what I don't want is

我想我不想要的是

  • Soft wrapping of text (say using the longlines and (set long-lines-auto-wrap 't)). This is because I don't want to impose requirements on my collaborators' editors, and I sometimes use other unix tools to examine these files.
  • 文本的软包装(比如使用longlines和(设置long-lines-auto-wrap't))。这是因为我不想对协作者的编辑强加要求,有时我会使用其他unix工具来检查这些文件。

I think what I do want is

我想我想要的是

  • For fill-paragraph to fill between newlines that look like they mark the end of a sentence.
  • 填充段落填充新行之间,看起来像是标记句子的结尾。

  • A solution that works with auto-fill-mode would be a bonus.
  • 使用自动填充模式的解决方案将是一个奖励。

That is:

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

聊天聊天。一个新的句子,需要修复包装。嘟m嘟

Transformed to:

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

聊天聊天。一个新的句子,需要修复包装。嘟m嘟

Your comments and suggestions are appreciated.

您的意见和建议表示赞赏。


Edit: The suggestion by Jouni K. Seppänen pointed me at LaTeX-fill-break-at-separators, which suggests that emacs almost knows how to do this already. Anyway, I'm off to read some code, and will report back. Thanks again.

编辑:JouniK.Seppänen的建议指出我在LaTeX-fill-break-at-separators,这表明emacs几乎知道如何做到这一点。无论如何,我要读一些代码,然后报告回来。再次感谢。


More general version of the same question: Editor showdown: Maintain newlines at the ends of sentences. Thanks, dreeves.

同一问题的更一般版本:编辑摊牌:在句子末尾保持换行符。谢谢,dreeves。

10 个解决方案

#1


Here's what I use, which was mostly cribbed from Luca de Alfaro:

这就是我使用的东西,其中大部分来自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))))))

I bind this to M-j with

我将它绑定到M-j

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

The references to "LaTeX" are for AUCTeX support. If you don't use AUCTeX, the let can be simplified to

对“LaTeX”的引用是针对AUCTeX的支持。如果您不使用AUCTeX,则let可以简化为

(let (beg (point))
  (forward-sentence)
  (fill-region-as-paragraph beg (point)))

#2


If you put a comment marker at the end of each sentence, Emacs knows not to move the next line inside the comment:

如果您在每个句子的末尾添加注释标记,Emacs知道不会移动注释中的下一行:

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

Then M-q fills each sentence separately, at least in AUCTeX 11.85. (If you test this in Emacs, there seems to be a bug where if this is the first paragraph in the buffer and you type M-q, you get an error message. Just put a newline before the text to work around it.)

然后M-q分别填写每个句子,至少在AUCTeX 11.85中。 (如果你在Emacs中进行测试,似乎有一个错误,如果这是缓冲区中的第一个段落并且你输入M-q,你会收到一条错误信息。只需在文本前加一个换行符来解决它。)

If you don't want to type the comment characters, you could take LaTeX-fill-paragraph and modify it so that sentence-ending punctuation at end of line works similarly to comments.

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

#3


I have been meaning to do this forever and I recently found this blog post which worked fairly well for me. So here is (a slightly modified version of) what I have been using for a few days.

我一直想要永远这样做,我最近发现这篇博文对我来说相当不错。所以这是(我的一个稍微修改过的版本)我已经使用了几天。

(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


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

要使其与自动填充模式一起使用,请将(setq normal-auto-fill-function'my-fill-sentence)添加到LaTeX模式钩子(我认为)。

#5


I am assuming you know elisp.

我假设你知道elisp。

There are a few approaches you can take:

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

  • Hook into auto-fill-mode. There are a lot of hard-coded conditionals there, so it might not work for you. You can potentially play with auto-fill-function and see if you have the hook you need there.

    挂钩进入自动填充模式。那里有很多硬编码条件,所以它可能不适合你。你可以玩自动填充功能,看看你是否有你需要的钩子。

  • Make a character (probably .) "electric" so that when you press it, it inserts itself and then calls a function to determine how to fill the line you're on.

    制作一个角色(可能是。)“电动”,这样当你按下它时,它会插入自己,然后调用一个函数来确定如何填充你所在的线。

  • Set an after-change-hook to call a function that determines how to fill the sentence. This function will be called after every change to the buffer, so do it efficiently. (This mechanism is used by font-lock, so don't worry about it too much. It sounds slow, but really isn't -- people type slowly.)

    设置一个更改后挂钩来调用一个确定如何填充句子的函数。每次更改缓冲区后都会调用此函数,因此请高效执行。 (这种机制由字体锁使用,所以不要太担心。听起来很慢,但事实并非如此 - 人们输入的速度很慢。)

Once you have hooked in at the right place, you just have to implement the filling logic. The source for sentence-at-point (from thingatpt) may be instructive.

一旦你在正确的地方上钩,你只需要实现填充逻辑。逐句(来自thingatpt)的来源可能是有益的。

Anyway, I've never heard of anyone doing this... but it is definitely possible. Like most things in Emacs, it's just a Simple Matter Of Programming.

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

#6


If the other answers are too automatic, here's a semiautomatic approach. It's basically what you would do repeatedly if you were going to manually reformat, but condensed so you can hit a single key repeatedly instead.

如果其他答案太自动,这是一种半自动的方法。如果您要手动重新格式化,它基本上会反复执行,但会缩减,因此您可以反复按一个键。

;; - 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

#7


I like Chris Conway's macro a lot but it only works after you manually line-break each sentence. I'm a lazy guy so I want emacs to do it for me. This morning I finally sat down and looked into the problem. The solution I have now is to hack the built-in macro fill-region-as-paragraph.

我很喜欢克里斯康威的宏,但只有在你手动换行每个句子后它才有效。我是个懒人,所以我想让emacs为我做这件事。今天早上我终于坐下来调查了这个问题。我现在的解决方案是破解内置的宏fill-region-as-paragraph。

After applying the following hack, a new option newline-after-sentence will be set to true. The standard M-q (fill-paragraph) will automatically fill and create line-breaks between sentences. Note that tests are only done with GNU Emacs 23.3.1 — use it at your own risk.

应用以下hack后,新选项newline-after-sentence将设置为true。标准M-q(fill-paragraph)将自动填充并在句子之间创建换行符。请注意,只能使用GNU Emacs 23.3.1进行测试 - 使用它需要您自担风险。

The full macro is long so I won't post it here. The idea is to add the following loops in fill-region-as-paragraph

完整的宏很长,所以我不会在这里发布。我们的想法是在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)))))

...

You can find the full macro in my git repository. Some details are also written in my blog. In case you don't want to read my poor English, you can simply use

您可以在我的git存储库中找到完整的宏。一些细节也写在我的博客中。如果你不想读我糟糕的英语,你可以简单地使用

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

to append the hack to your ~/.emacs and give it a try. Comments and bug reports are all welcome.

将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


An alternative approach would be to leave your .tex file as is, and use a tool like latexdiff (described in this StackExchange post) instead of Unix diff. This produces a .tex file with Word-style track changes marks, and handles whitespace correctly so you don't have to worry about where your sentences end.

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

#10


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.

我写了以下内容循环一个区域并插入换行符。我没有使用对我不起作用的前向句子,而是使用re-search-forward“[。?!] [] \”')}] * \\(\\)“,它只查找所有句子两个空格(正则表达式是一个修改过的句子结尾)。换行符是使用换行符和缩进词。

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

为了能够修复格式不正确的文本,例如“聊天聊天聊天...”,填充句子在区域中首先调用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)))

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

我使用视觉线模式并将我的默认段落填充M-q替换为句子中的句子填充(global-set-key“\ M-q”'fill-sentences-in-paragraph)。

#1


Here's what I use, which was mostly cribbed from Luca de Alfaro:

这就是我使用的东西,其中大部分来自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))))))

I bind this to M-j with

我将它绑定到M-j

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

The references to "LaTeX" are for AUCTeX support. If you don't use AUCTeX, the let can be simplified to

对“LaTeX”的引用是针对AUCTeX的支持。如果您不使用AUCTeX,则let可以简化为

(let (beg (point))
  (forward-sentence)
  (fill-region-as-paragraph beg (point)))

#2


If you put a comment marker at the end of each sentence, Emacs knows not to move the next line inside the comment:

如果您在每个句子的末尾添加注释标记,Emacs知道不会移动注释中的下一行:

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

Then M-q fills each sentence separately, at least in AUCTeX 11.85. (If you test this in Emacs, there seems to be a bug where if this is the first paragraph in the buffer and you type M-q, you get an error message. Just put a newline before the text to work around it.)

然后M-q分别填写每个句子,至少在AUCTeX 11.85中。 (如果你在Emacs中进行测试,似乎有一个错误,如果这是缓冲区中的第一个段落并且你输入M-q,你会收到一条错误信息。只需在文本前加一个换行符来解决它。)

If you don't want to type the comment characters, you could take LaTeX-fill-paragraph and modify it so that sentence-ending punctuation at end of line works similarly to comments.

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

#3


I have been meaning to do this forever and I recently found this blog post which worked fairly well for me. So here is (a slightly modified version of) what I have been using for a few days.

我一直想要永远这样做,我最近发现这篇博文对我来说相当不错。所以这是(我的一个稍微修改过的版本)我已经使用了几天。

(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


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

要使其与自动填充模式一起使用,请将(setq normal-auto-fill-function'my-fill-sentence)添加到LaTeX模式钩子(我认为)。

#5


I am assuming you know elisp.

我假设你知道elisp。

There are a few approaches you can take:

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

  • Hook into auto-fill-mode. There are a lot of hard-coded conditionals there, so it might not work for you. You can potentially play with auto-fill-function and see if you have the hook you need there.

    挂钩进入自动填充模式。那里有很多硬编码条件,所以它可能不适合你。你可以玩自动填充功能,看看你是否有你需要的钩子。

  • Make a character (probably .) "electric" so that when you press it, it inserts itself and then calls a function to determine how to fill the line you're on.

    制作一个角色(可能是。)“电动”,这样当你按下它时,它会插入自己,然后调用一个函数来确定如何填充你所在的线。

  • Set an after-change-hook to call a function that determines how to fill the sentence. This function will be called after every change to the buffer, so do it efficiently. (This mechanism is used by font-lock, so don't worry about it too much. It sounds slow, but really isn't -- people type slowly.)

    设置一个更改后挂钩来调用一个确定如何填充句子的函数。每次更改缓冲区后都会调用此函数,因此请高效执行。 (这种机制由字体锁使用,所以不要太担心。听起来很慢,但事实并非如此 - 人们输入的速度很慢。)

Once you have hooked in at the right place, you just have to implement the filling logic. The source for sentence-at-point (from thingatpt) may be instructive.

一旦你在正确的地方上钩,你只需要实现填充逻辑。逐句(来自thingatpt)的来源可能是有益的。

Anyway, I've never heard of anyone doing this... but it is definitely possible. Like most things in Emacs, it's just a Simple Matter Of Programming.

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

#6


If the other answers are too automatic, here's a semiautomatic approach. It's basically what you would do repeatedly if you were going to manually reformat, but condensed so you can hit a single key repeatedly instead.

如果其他答案太自动,这是一种半自动的方法。如果您要手动重新格式化,它基本上会反复执行,但会缩减,因此您可以反复按一个键。

;; - 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

#7


I like Chris Conway's macro a lot but it only works after you manually line-break each sentence. I'm a lazy guy so I want emacs to do it for me. This morning I finally sat down and looked into the problem. The solution I have now is to hack the built-in macro fill-region-as-paragraph.

我很喜欢克里斯康威的宏,但只有在你手动换行每个句子后它才有效。我是个懒人,所以我想让emacs为我做这件事。今天早上我终于坐下来调查了这个问题。我现在的解决方案是破解内置的宏fill-region-as-paragraph。

After applying the following hack, a new option newline-after-sentence will be set to true. The standard M-q (fill-paragraph) will automatically fill and create line-breaks between sentences. Note that tests are only done with GNU Emacs 23.3.1 — use it at your own risk.

应用以下hack后,新选项newline-after-sentence将设置为true。标准M-q(fill-paragraph)将自动填充并在句子之间创建换行符。请注意,只能使用GNU Emacs 23.3.1进行测试 - 使用它需要您自担风险。

The full macro is long so I won't post it here. The idea is to add the following loops in fill-region-as-paragraph

完整的宏很长,所以我不会在这里发布。我们的想法是在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)))))

...

You can find the full macro in my git repository. Some details are also written in my blog. In case you don't want to read my poor English, you can simply use

您可以在我的git存储库中找到完整的宏。一些细节也写在我的博客中。如果你不想读我糟糕的英语,你可以简单地使用

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

to append the hack to your ~/.emacs and give it a try. Comments and bug reports are all welcome.

将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


An alternative approach would be to leave your .tex file as is, and use a tool like latexdiff (described in this StackExchange post) instead of Unix diff. This produces a .tex file with Word-style track changes marks, and handles whitespace correctly so you don't have to worry about where your sentences end.

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

#10


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.

我写了以下内容循环一个区域并插入换行符。我没有使用对我不起作用的前向句子,而是使用re-search-forward“[。?!] [] \”')}] * \\(\\)“,它只查找所有句子两个空格(正则表达式是一个修改过的句子结尾)。换行符是使用换行符和缩进词。

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

为了能够修复格式不正确的文本,例如“聊天聊天聊天...”,填充句子在区域中首先调用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)))

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

我使用视觉线模式并将我的默认段落填充M-q替换为句子中的句子填充(global-set-key“\ M-q”'fill-sentences-in-paragraph)。