流畅编辑超大文件的解决方案

Emacs在编辑超大文件的时候会巨卡, 甚至必须 kill 掉Emacs, 太长的行即使关闭语法高亮也不行。

怎么解决?

图形编程的性能秘诀是, 任何长度的内容渲染性能都要做到常量计算才能让用户感到流畅。


下面是解决方案:

  1. 给 find-file 做一个 advice, 打开任意文件的时候, 用工具快速检测是否有超长行, 没有就正常打开, 发现有超长行的文件, 用新的插件打开
  2. 打开超长行的时候, 用外部进程打开大文件, 可以采用 python-bridge 来实现, Python 和 Elisp 的双向通讯基础设施代码已经打磨好
  3. 外部进程打开文件后, 根据 row/column 的 offset 以及 window 的大小, 截取出窗口显示所需的字符串, 从 Python 实时发送给 Elisp 进行渲染, 实时交换整个 Window 内容的性能非常快, blink-search 已经实践过
  4. 在Emacs移动光标后, 外部进程重新计算 Window 内容再实时发送给Emacs渲染
  5. 如果在窗口内编辑, 根据 before-change-hook 和 after-change-hook 来计算 text diff 序列, 在Emacs端更改内容后, 发送 text diff 给 Python 端更新文件最新内容并回写到磁盘
  6. 如果遇到一些超出屏幕的编辑操作, 比如 Ctrl + k 删除到行尾这种, Emacs端删除以后, 发送命令到Python那边, 由Python来模拟字符串删除到行尾的操作
  7. 如果要做的精细点, 可以在Python端对超大文件进行全局渲染, 然后根据 Window 的坐标, 发送高亮信息给Emacs, 再实时的由Emacs来进行 overlay 渲染, 这样就可以做到超大文件的实时语法高亮

实现了上面7个步骤, 应该就可以彻底解决Emacs打开大文件卡死的问题。

如果哪位大佬有精力, 可以尝试实现一下, 我最近比较忙, 可能只有到年底之前抽时间实现一下。

11 个赞

都 2023 年了,流畅编辑大文件/长行这么基本的需求还是奢望,这就是 Emacs 让人又爱又恨的原因。

最理想的情况是由 Emacs 内部实现,这样就能保证在 emacs -Q 上也有同样的体验。

7 个赞

只能期待各位大佬了

内部实现难, 还是多线程的问题, 我就不重复了。

真正的原因和这些没啥关系,而是两个“少数人才会用到但是别的代码编辑器还真压根不会想到去实现的富文本编辑特色功能”让经常遇到大文件的大多数人 pay the price

  • GUI Emacs 要计算每行高度,这个功能是为了支持行内图片显示,和多种字体混合,正常文本文件用的字体不会有差距,做这个计算完全是多余的,但是为了显示不出现不正常不可能不做

  • 计算 bidi (为了从右往左书写的文本如阿拉伯文和其他文本混合能正常显示),这个功能一般的中文英文用户都用不上,可以选择关了

(setq-default bidi-display-reordering nil)

换句话就是,用字符界面就能避免

12 个赞

那有没有可能在 so-long-mode 下直接 disable 这两个功能呢

其实不只这两个,在 NEWS 文件里 grep 一下 long line 还有很多可以设置的,都对长行有影响。

我觉得本质问题还是在 emacs 带了太多“一般人用不到的功能”,和很多“臭写代码的”的正常期待是冲突的。

一方面把这些没用的功能砍掉肯定能让写代码的体验充分提升,另一方面没有这些没用的功能就不是 Emacs 的风格了。

4 个赞

赞同不应该砍掉这些功能,我觉得选择性地在一些 modes(比如 prog-mode / fundamental-mode)里关掉或许会是一个比较好的选项

用隔壁的例子看来这个 bidi 真的影响很大。建议配置里加上

(setq-default bidi-display-reordering nil)
(setq bidi-inhibit-bpa t
      long-line-threshold 1000
      large-hscroll-threshold 1000
      syntax-wholeline-max 1000)

全局关了。不是做比如阿语外包真的用不上 bidi。有人可能看到 bidi-display-reordering 的 docstring 说「不应该改」,但是首先是因为在 emacs 维护者眼里为了大文件性能牺牲 bidi 是个不正常的需求,像我用 emacs 这么久了从来没有在意过大文件性能,所以他们测试功能的时候从来没有关闭过 bidi 的缘故不推荐,其次设置了这个变量 emacs 也不会原地爆炸,Emacs 25 其实就有人在用了,最多就是有些滥用 bidi 功能的包出点问题,给不同模式单独设置这个变量就行了。然后如果实在觉得用这个邪道设置心里不安,Emacs 维护者推荐的替代设置是 Doom Emacs 也用的

(setq-default bidi-paragraph-direction 'left-to-right)

还有这几个 threshold 变量默认值都很大(五万)而且还在文档里说「没有必要改」,但我觉得当正常文件都不会超过 80 字一行,偶尔不小心打开个大文件也不会有五万字这么多,就是应该改小点,一千都已经太保守了,五百可能就值得优化了。

建议大家可以把这个分享出去,我自己是不常用大文件的,但如果这个能对大文件 90% 有效那我觉得也没必要再 round trip 用外部程序代理一遍了。用井行是可以在体感上提升很多,不过我估计很多人也是愿恴用一个有轻微不足但是简单的方案的。

51 个赞

更新:经测试上面大佬的设置比doomemacs的要快多了

我从doomemacs这样抄的

3 个赞

问一个傻瓜问题,如果我都用setq-default来设置这几个变量效果应该是一样的把?因为我想把所有类似的设置放到一起

只要你不在意编译出来的字节码这种细节的话,从效果上讲没区別

2 个赞

我也是抄doom的,这样的话我也考虑直接设置为nil了

感謝分享!很實用啊!:heart_eyes:

(setq-default bidi-display-reordering nil)

这样设置有个问题,就是在 Telega 当中的 telega root 会默认在行末,这样回车是无法进入 chat buffer 的,需要向前移动光标后回车才可以进入。

在 mode hook 里 setq 就行了

2 个赞

你几个配置确实很管用啊, 我随便拿 npm 的一个js压缩包, 实验了一个一行包括 28 万列的超长行, 测试一直滚动, 没有设置之前基本上语法高亮是跟不上的, 设置以后, 虽然CPU占用差不多, 但是语法高亮基本上都可以跟上了。

2 个赞

感谢大佬,这个确实非常有用,在windows上效果更是非常明显,我之前用外部程序php读取json数据在org-mode中显示,一遇到超长行就卡死,于是做了个判断,一行大于9999列就不显示了,现在我可以在这个判断上在加一个9,一行10万列基本没什么问题

2 个赞

跑来感谢的,非常管用,设置了之后,emacs 的流畅度肉眼可见提升了

拯救世界了,打开大的 log 明显流畅了

1 个赞