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

时间:2009-02-12 04:13:08

标签: emacs latex fill

我在StackOverflow上seen至少有两个recommendations在编辑LaTeX文档时在句子之间插入换行符。原因是该练习有助于源代码控制,diff和协作编辑。

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

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

我认为想要的是

  • 文字的软包装(例如使用longlines(set long-lines-auto-wrap 't))。这是因为我不想对我的协作者编辑施加要求,有时会使用其他unix工具来检查这些文件。

我认为想要的是

  • fill-paragraph填充新行,这些新行看起来像是标记句子的结尾。
  • auto-fill-mode一起使用的解决方案将是一个奖励。

那是:

  聊天聊天聊天。
  新句子   需要修理的包装过度   嘟m嘟嘟

转换为:

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

感谢您的意见和建议。


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


同一问题的更一般版本:Editor showdown: Maintain newlines at the ends of sentences。谢谢,dreeves

10 个答案:

答案 0 :(得分:8)

这是what I use,其中大部分来自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)))

答案 1 :(得分:3)

如果您在每个句子的末尾添加注释标记,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并对其进行修改,以便行尾的句尾标点与注释的工作方式类似。

答案 2 :(得分:3)

我一直想要永远这样做,我最近发现this blog post对我来说相当不错。所以这是我几天使用的(略微修改过的版本)。

(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 :(得分:2)

可能无法在所有情况下都有效,但是:

(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模式挂钩(我认为)。

答案 4 :(得分:1)

我假设你知道elisp。

您可以采取一些方法:

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

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

  • 设置after-change-hook以调用确定方式的函数  填写句子。每次都会调用此函数  更改为缓冲区,因此请高效执行。 (这个机制是  由font-lock使用,所以不要太担心。听起来  慢,但实际上不是 - 人们慢慢打字。)

一旦你迷上了正确的地方,你就必须实施 填充逻辑。 sentence-at-point(来自thingatpt)的来源可能是 指导性的。

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

答案 5 :(得分: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

答案 6 :(得分:1)

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

应用以下hack后,新选项newline-after-sentence将设置为true。标准M-qfill-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)))))

...

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

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

将黑客附加到~/.emacs并试一试。评论和错误报告都是受欢迎的。

答案 7 :(得分:1)

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

答案 8 :(得分:1)

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

答案 9 :(得分:1)

我编写了以下内容,循环遍历某个区域并插入换行符。我没有使用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))))

为了能够修复格式不正确的文本,例如示例&#34;聊天聊天...&#34;,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)