New Emacs 构想

这是指能够准确评估正视问题吗

  1. cxx-qt一直在改进
  2. 不知道使用jsonrpc 是不是可以满足性能要求,emacs29带elisp jsonrpc实现,rust有jsonrpsee
  3. 先用pyo3复用EAF的代码,渐进的transfer

另外EAF太庞大了,可以先整个精减的核心

我最有疑虑的就是加钩子使得 emacs lisp 插件编辑的内容同步到外部程序,这事不比完成多线程改造简单多少,目前我没有看到具体可行的解决办法。

1 个赞

我觉得Emacs很难进化的是下面诸多偏见:

  1. 图形的快就是多线程要做好, 不是说语言执行有多快, 只要是多线程实现的好, 一个后台代码是1x的速度还是10x的速度, 只要不要卡住主线程图形绘制, 用户就会觉得很快, 所以很多Emacser理解不了, 用户说的图形流畅其实等于多线程不卡主线程, 而不是图形流畅就等于最快的执行速度, 如果rust也只能单线程, 一个任务的时间超过一个周期的耗时, rust也会卡的
  2. GIL主要是在多个线程高并发的执行时会出问题, 但是从图形编程来说, 只要 threading 处理好, 子线程和主线程只交换简单的处理结果, GIL不会阻碍多线程图形编程, 但是很多没有写过图形编程的人, 从来就没有写过一行图形多线程代码的人, 就会把后台多线程遇到的GIL问题归结为也会影响图形多线程
  3. 很多Emacser分不清楚多线程和协程, 因为协程式的多线程本质是单线程快速切换, Emacs没法解决全局变量/hook/advice的问题(这也是Emacs好hacking的优势), 所以用了协程式的方式实现, 但是协程式的方法就没法解决图形多线程的问题, 因为任何一段代码卡死, 整个事件循环都会卡住, 因为切换不出去了
  4. gnus可以比较好的实现就是 blink-search 或者 telega 这种方式重写, 用外部进程来解决性能, 然后界面部分依然用 elisp 来实现, 只是虚拟窗口的界面实现要比超长buffer渲染的逻辑要更加复杂, 因为要有很多 screen offset 处理, 但是从我的 blink-search 实践看, 完全可行, gnus 或者 magit 都可以用外部进程来最小化代码改动来解决这些性能问题, 而不用大幅度修改Emacs本身代码

上面这些仅仅是一部分偏见, 我觉得这些偏见的来源于是大部分Emacser没有足够的知识去了解多线程、图形编程、协程、 GC瓶颈等细致的区别, 导致我说再多, 大家只会进入个人抬杠。

我现在基本只分享我的经验, 如果某些杠精(或者查理芒格说的基于忌妒心理来抬高自己)来故意抬杠, 我会说: ”对对对, 你说的都是对的”。

我总体对Emacs的态度来说, EAF + lsp-bridge 已经让我很幸福了, 我自己的小愿望已经实现, 同时不用对Emacs改造什么就很舒服了。

至于为什么要发这个帖子, 是因为我原来认真思考了所有逻辑, 我认为只有EAF这一个方案可行, 但是 @DogLooksGood 狗哥启发了我, 其实还有 Emacs Overlay Client + New Graphics Library + Multi-Thread 这一条路, 虽然我认为 Emacs Overlay Client 的难度不小, 但是依然是理论上可行的方案, 如果实现了, 效果要比 EAF 好很多。

所以我发了这个帖子, 但是我不期望大家理解, 只是分享。 懂的人自然懂, 说多无益。

15 个赞

不是这样的, 只是Qt目前有GPU镜像技术可以实现Emacs Buffer/Window的设计, 其他的图形库我还没有看到可以这样, 最多只能像 VSCode 那样左右分屏只能是不同的Buffer。

Qt只是举例, 但是更重要的是 Emacs Overlay Client 的思想。

jsonrpc 还是 epc, 还是 websocket 都是可以的, 用什么库通讯不是重要的, 关键是外部进程可以把Emacs本身的性能瓶颈卸掉 90% 以上的负担。

EAF本身只是 github 项目多, 但是 install-eaf.py 可以只安装 eaf-core 的, 不用安装任何应用。

EAF的核心并不大, 都是必要的协议处理, 只是你们一看到 webengine 你们就会联想到 javascript, 但是你默认可以不用的, 因为不安装 eaf-browser 就不会执行这些代码。

这个 lsp-bridge 已经实现了, 具体代码在

  1. after-change-functions hook 后发送 text diff 给 lsp-bridge 远端的 Python 进程: https://github.com/manateelazycat/lsp-bridge/blob/61b2bcb841c1e1f14aafdb9b9371fe1613aa4f63/lsp-bridge.el#L1357
  2. 远端接受 text diff 后, 在内存中重建最新的文件: https://github.com/manateelazycat/lsp-bridge/blob/61b2bcb841c1e1f14aafdb9b9371fe1613aa4f63/core/remote_file.py#L195
  3. 远端重建内存文件后, 就可以用远端 LSP Server 来对内存文件进行补全计算

这个原理对应到Emacs Overlay Client 来说:

  1. Emacs启动后不显示窗口,但是执行任意Elisp插件代码
  2. 不要管Elisp插件干了啥, 监控 before-change-functions 和 after-change-functions 这两个 hook 就可以计算出 text diff
  3. text diff 发送到 Emacs Overlay Client, 就可以在新的前端重建和Emacs实时一样的Buffer内容
  4. Elisp插件在改变文本的时候, 大多数都在同步操作 overlay 和 font-lock, 针对 overlay, 只需要在新的前端正确渲染 overlay 的效果就好了, font-lock 完全可以在新的前端自己加载 tree-sitter 和 regex 来渲染

只要文本实时重建到新的前端, 同时把文本着色处理对, 就可以实现 Emacs 文本界面到新的前端的完整迁移。

当然像 org-mode 这种超级复杂的插件, 我更建议新的前端实现后, 重写 org-mode 的前端渲染代码, 只要键盘操作同时界面更好, 大家应该会接受现代化的 org-mode 界面。

5 个赞

不太明白 Emacs 不显示窗口的情况下,怎么监听和转发 InsertEnter 等用户交互事件?

在新的前端加载 treesitter 和 regex 来渲染,意思是完全绕开 Emacs 的机制?在 Qt 上重新实现一套着色方案吗?需要与 Emacs 的原有渲染完全一致,感觉会对原有插件的兼容造成很大影响啊。

有几种方式哈:

  1. 图形库可以设置 accept focs, 但是同时设置 paint mask 为透明, 这样其实焦点在窗口上, 但是你看不到窗口画出来了, 这种技术在 windows 和 linux 上非常成熟
  2. 利用系统全局键盘监听事件技术, 只用输入焦点在emacs的时候记录, 这种技术在 GitHub - manateelazycat/key-echo: Key-Echo is an Emacs plugin that uses XRecord technology to listen to system key events 中得到验证
  3. New Client 上接受输入事件, 然后发送事件到Emacs端直接查 mode-map 调用Emacs命令

我建议用第三种方式, 直接隐藏Emacs窗口, 在 New Client 上接受事件, Emacs只执行事件对应的命令即可。

1 个赞

New Client 实现了 Emacs 的所有事件吗?感觉不大可能。

不会的呀, Elisp插件本质都在做三件事情:

  1. 更改Buffer的文本
  2. 语法高亮, treesit 或者 regex
  3. 叠加渲染 overlay

用New Client的方式来处理:

  1. 更改 Buffer, 用 text diff 的方式来进行文本内容实时同步, 需要注意的是, 文本的高度和Emacs保持一致就好了, 这样窗口内的行数是一致的
  2. 语法高亮, New Client来画会性能更高, 比如自然滚动, Emacs端完全关闭语法高亮, Emacs的性能会快很多, 比如打开大文件也不会卡, 变相提高Emacs大文件性能, 又不用改Emacs代码
  3. 叠加渲染 Overlay , 就是按照实时获取 Overlay 字符串各种属性值, 严格按照 Overlay 规范绘制就好了

只要从下到上按照 text → highlight → overlay 依此渲染, 完全可以做到绘制效果和Emacs一模一样。

1 个赞

能有啥事件是不能实现的吗?

不太清楚啊,Emacs 一些特性比如 undo,overlay,宏记录,终端打开,rpc 通道打开等有事件吗?New Client 有对应的吗?

你说的这些都是 Elisp 插件的功能, Emacs执行就好了呀, New Client 只会渲染文本和多媒体, 顺便用新的语言和多线程能力。

New Client 并不会去管 Elisp 逻辑, Elisp逻辑和图形不相关的就Emacs处理, 和图形相关的(文本、语法高亮、Overlay)由New Client同步绘制就好了。

undo 只是修改文本, overlay 是绘制, 宏纯粹是elisp逻辑, Emacs终端是文本, rpc这种主要是调用外部进程, 你说的这些都不是事件, 除了 overlay New Client 会绘制, 其他都是Emacs一行代码不改就可以兼容的。

Neovim 里这些都是事件。能不能理解成 Emacs 里有某些事件,只是通过 Elisp 暴露出来而已?

New Client 与 Emacs 感觉可以为双向同步?

我猜,我的主要疑惑是:Emacs 以及 Neovim 窗口是不是很容易绕过?

Emacs本质是一个Elisp解释器, 它没有很多抽象出来的事件概念, 你可以认为所有Elisp插件只是一个全局变量的脚本而已。

海牛懒猫兄可以写一份在macOS上安装EAF的比较详细的说明书吗?我在macOS上尝试着安装了好几次,都没有成功。特别想尝试EAF上的PDF-Viewer,貌似功能比Emacs生态环境里的pdf-tools强大不少。

VScode这个技术其实不简单, emacs实现起来有难度.

Emacs几乎没有图形api吧? 除了buffer里显示的widget, 接近于无. 真要支持完整图形编程的话, 这工作量就大了, 比如可能需要把所有gtk接口绑定到elisp.