中英文折行终极解决方案

解决如下问题

  • toggle-word-wrap:遇到中英文混合文本会折在空格,即使空格离右面还很远。因为他只能折在空格和tab上:

  • fill-paragraph:只能折等宽字体,遇到非等宽字体会有些难看。

效果对比,上面是新方法,下面是fill:

新方法

用法:M-x flywrap-mode RET,自动实时折行。

特性:

  • 实时折行
  • 标点悬挂
  • 支持中英文混合
  • 支持varaible pitch

效果:

14赞

这是 noto serif ?

对。字数补丁。

报错…

Debugger entered--Lisp error: (wrong-number-of-arguments recenter 2)
  (recenter 1 t)
  (if (pos-visible-in-window-p) nil (recenter 1 t))
  (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or (sfill-point-at-column-variable-pitch sfill-variable-pitch-column end) end)))
  (if sfill-variale-pitch (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or (sfill-point-at-column-variable-pitch sfill-variable-pitch-column end) end))) (sfill-forward-column sfill-column))
  (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or (sfill-point-at-column-variable-pitch sfill-variable-pitch-column end) end))) (sfill-forward-column sfill-column)) (if (< (point) end) (progn (fill-move-to-break-point linebeg) (skip-chars-forward " \011") (sfill-insert "\n"))))
  (let (linebeg) (goto-char beg) (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or (sfill-point-at-column-variable-pitch sfill-variable-pitch-column end) end))) (sfill-forward-column sfill-column)) (if (< (point) end) (progn (fill-move-to-break-point linebeg) (skip-chars-forward " \011") (sfill-insert "\n")))))
  (save-excursion (sfill-clear-overlay beg (1+ end)) (let (linebeg) (goto-char beg) (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or (sfill-point-at-column-variable-pitch sfill-variable-pitch-column end) end))) (sfill-forward-column sfill-column)) (if (< (point) end) (progn (fill-move-to-break-point linebeg) (skip-chars-forward " \011") (sfill-insert "\n"))))))
  (progn (save-excursion (sfill-clear-overlay beg (1+ end)) (let (linebeg) (goto-char beg) (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn (if (pos-visible-in-window-p) nil (recenter 1 t)) (goto-char (or ... end))) (sfill-forward-column sfill-column)) (if (< (point) end) (progn (fill-move-to-break-point linebeg) (skip-chars-forward " \011") (sfill-insert "\n")))))))
  (unwind-protect (progn (save-excursion (sfill-clear-overlay beg (1+ end)) (let (linebeg) (goto-char beg) (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn (if ... nil ...) (goto-char ...)) (sfill-forward-column sfill-column)) (if (< (point) end) (progn (fill-move-to-break-point linebeg) (skip-chars-forward " \011") (sfill-insert "\n"))))))) (set-window-configuration wconfig))
  (let ((wconfig (current-window-configuration))) (unwind-protect (progn (save-excursion (sfill-clear-overlay beg (1+ end)) (let (linebeg) (goto-char beg) (while (< (point) end) (setq linebeg (point)) (if sfill-variale-pitch (progn ... ...) (sfill-forward-column sfill-column)) (if (< ... end) (progn ... ... ...)))))) (set-window-configuration wconfig)))
  sfill-region(1 21)
  sfill-wrap-line()
  (save-excursion (goto-char beg) (sfill-wrap-line) (while (re-search-forward "\n" end t) (sfill-wrap-line)))
  sfill-jit-lock-fn(1 11113)
  (progn (add-to-list (quote jit-lock-functions) (function sfill-jit-lock-fn)) (sfill-jit-lock-fn (point-min) (point-max)))
  (if sfill-mode (progn (add-to-list (quote jit-lock-functions) (function sfill-jit-lock-fn)) (sfill-jit-lock-fn (point-min) (point-max))) (setq jit-lock-functions (remove (quote sfill-jit-lock-fn) jit-lock-functions)) (sfill-unfill))
  (let ((last-message (current-message))) (setq sfill-mode (if (eq arg (quote toggle)) (not sfill-mode) (> (prefix-numeric-value arg) 0))) (if sfill-mode (progn (add-to-list (quote jit-lock-functions) (function sfill-jit-lock-fn)) (sfill-jit-lock-fn (point-min) (point-max))) (setq jit-lock-functions (remove (quote sfill-jit-lock-fn) jit-lock-functions)) (sfill-unfill)) (run-hooks (quote sfill-mode-hook) (if sfill-mode (quote sfill-mode-on-hook) (quote sfill-mode-off-hook))) (if (called-interactively-p (quote any)) (progn nil (if (and (current-message) (not (equal last-message (current-message)))) nil (let ((local " in current buffer")) (message "Sfill mode %sabled%s" (if sfill-mode "en" "dis") local))))))
  sfill-mode(toggle)
  funcall-interactively(sfill-mode toggle)
  call-interactively(sfill-mode record nil)
  command-execute(sfill-mode record)
  counsel-M-x-action("sfill-mode")
  ivy-call()
  ivy-read("M-x " ("sfill-mode" "toggle-debug-on-error" "visual-line-mode" "run-python" "recentf-cleanup" "tramp-cleanup-all-connections" "pdf-tools-install" "straight-update-all" "iedit-mode" "company-diag" "load-theme" "find-function" "describe-char" "straight-use-package" "customize-group" "view-mode" "ivy-bibtex" "gitignore" "find-library" "mwim-beginning" "find-variable" "ediff" "json-mode" "mac-auto-operator-composition-mode" "vlf" "org-mode" "find-file" "flush-lines" "rainbow-mode" "ediff-buffers" "flyspell-word" "org-rich-yank" "isearch-forward" "dired-do-compress-to" "find-function-on-key" "display-line-numbers-mode" "highlight-indentation-mode" "man" "esup" "shell" "helm-M-x" "ivy-mode" "text-mode" "disk-usage" "org-refile" "counsel-fzf" "image-dired" "counsel-mode" "flymake-mode" "company-elisp" ...) :predicate #f(compiled-function (x) #<bytecode 0x7a97634d>) :require-match t :history counsel-M-x-history :action counsel-M-x-action :keymap (keymap (67108908 . counsel--info-lookup-symbol) (67108910 . counsel-find-symbol)) :initial-input nil :caller counsel-M-x)
  counsel-M-x()
  funcall-interactively(counsel-M-x)
  call-interactively(counsel-M-x nil nil)
  command-execute(counsel-M-x)

Emacs version:

GNU Emacs 26.3 (build 1, x86_64-apple-darwin19.3.0, Carbon Version 162 AppKit 1894.3) of 2020-02-06

recenter

recenter is an interactive built-in function in ‘C source code’.

(recenter &optional ARG)

Center point in selected window and maybe redisplay frame.
With a numeric prefix argument ARG, recenter putting point on screen line ARG
relative to the selected window.  If ARG is negative, it counts up from the
bottom of the window.  (ARG should be less than the height of the window.)

If ARG is omitted or nil, then recenter with point on the middle line of
the selected window; if the variable ‘recenter-redisplay’ is non-nil,
also erase the entire frame and redraw it (when ‘auto-resize-tool-bars’
is set to ‘grow-only’, this resets the tool-bar’s height to the minimum
height needed); if ‘recenter-redisplay’ has the special value ‘tty’,
then only tty frames are redrawn.

Just C-u as prefix means put point in the center of the window
and redisplay normally--don’t erase and redraw the frame.

[back]

出错的时候一部分拆行了,一部分没有。

另外mode刚enable的时候有部分拆了的地方,和原来visual-line-mode差不多,也是在英文单词那里过早折行。我重新在英文单词后加一个字符再删一个字符(即稍作编辑但又回复原始文本),又正常了。

你的Emacs版本是多少?C-h f recenter的输出是什么?开启sfill-mode的时候buffer里是什么样?你得告诉我更多信息才行。

看来是Emacs 27里recenter函数改了。我改成兼容Emacs 26的了。第二个问题怎么复现?

暂时没找到第二个问题的比较简单的case。另外,修改过后的代码,mode enable的时候会从buffer第一行开始一直自动滚屏到最后一行…

怎么重现?你的buffer里是什么?

好耶!紫薯布丁

就随便打开一个文件,然后sfill-modeemacs -q同样结果。

https://youtu.be/rop2gOu66Qk

啊,我明白了,谢谢。我去掉了redisplay,现在应该不会有滚屏了。

提取成个独立的package吧大佬~

我好像明白了什么😅

另,我觉得发帖字数限制真的没什么必要。

2赞

最好是加个comment功能,简短或者无关的回复就放在comment里,像SO一样。

1赞

不错的主意。

应该突出重点回复,而不是无差别平铺所有回复。

目前超长行的性能非常差,先想办法解决这个问题。

目前 Emacs 里这部分是C语言实现的,elisp的话可能很难绕过性能问题。

考虑拆出一个单独的包吗?这个功能挺不错的,我觉得会有许多人用

理论上可以优化到长行基本可用的程度,我还在探索…

我希望完善以后加入上游(Emacs 富文本编辑第一步

3赞

加了一点优化,现在等宽字体不管多长都很快,不等宽字体只要不是太长的行(超过几千个字符)都没问题。动图太大发不了,我发视频了。

不等宽 | 等宽

2赞