大家怎么看待`external-completion.el`加入emacs-29,并在Eglot中使用。

相关连接:

这就是lsp-bridge的思路吧?

1 个赞

看到文档里面有这句话

The table and external tool are fully in control of the matching of the pattern string to the potential candidates of completion. When external' is in use, the usual styles configured by the user or other in completion-styles’ are ignored. This compromise is for speed: all other styles need the full data set to be available in Emacs’ addressing space, which is often slow if not completely unfeasible.

所以这个的意思是它会比原来还慢?eglot的速度本来就只是“勉强可以接受”,如果比原来还慢。。。不敢想。

你理解错文档的意思了,应该会比原来的更快(起码也是同等)。最后一段的意思是,用了 external 以后,其他的 completion-styles 会被忽略,这是考虑到其他的 completion-styles 要使用整个数据集合,通常会比较慢。

所以,用了 external 就没法和 flex, basic 一起用了。在老版本的 eglot 中,默认是使用 flex 的。

我还没编译最新的,等 use-package 合并后再编译了。估计这两天就会合并。

看了一下,我理解应该不会对性能有什么影响。

Emacs 自己一般要求补全函数提供的 table 是所有符号的集合,然后 Emacs 自己再用 completion-styles 过滤,completion-at-point-functions 的文档里就有写。但依赖外部工具的补全后端应该都不可能这么干,事实上也没有这么干。external-completion.el 只能算是把这种用法扶正了而已。

lsp-bridge 形式上解决了LSP补全慢(主要外部进程多线程过滤和大数据缓存避免Elisp GC)的问题。

但是 lsp-bridge 更大的意义是宣扬 “外部RPC协作框架”, 而不是Emacs社区传统的 ”外部命令行工具 + Subprocess Filter + Elisp GC" 的方式, 这种以外部语言提供RPC API的意义是:

遇到性能问题时, 我们可以基于RPC的方案立马就开发出性能足够高的插件, 而不用什么都用 Elisp C 实现, 需要等 Emacs 特定的版本合并后才能用

很多Emacser没有想清楚的是, 要把Emacs在搜索领域的性能提高到极致必须结合:

  1. 多线程: 避免GUI输入的时候等计算结果造成卡顿
  2. 外部进程: 缓存数据在外部进程, 只有渲染的时候才给Emacs传递数据, 这样做是避免全部大数据塞给Emacs(传统的 subprocess filter) 触发Elisp GC(Elisp遇到瞬间建立超多Object时一定会有GC介入), 导致卡顿甚至卡死
  3. 可编程API: 有了API就可以精细化控制, 没有API, 就会对所有 subprocess 输出进行正则全文搜索, 反而会加重计算负担和触发更多GC

只有这三个情况都满足的情况下, Emacs 才能做到 VSCode 那样快, lsp-bridge 和 blink-search 就是最好的实践结果。

大多数Emacser缺乏对图形编程、多线程以及GC的深刻理解, 经常分不清楚多线程和协程的区别, 分不清楚libevent和concurrent thread的区别, 分不清楚GIL和多线程的区别, 导致大多数Emacs补全方案都是修修补补, 而没法彻底解决性能问题。

只有深刻理解补全整个流程前前后后所有的性能瓶颈才能从架构设计上解决问题。


备注: 我有时候超级烦躁的是, 很多和我讨论的人自己多线程的知识储备不够的情况下还一本正经的胡说八道让我感觉到厌烦, 而反方最有实力的开发者 – corfu 的作者通过长时间思考和对比测试, 最后他自己都完全同意 lsp-bridge 的设计确实从根源上解决了性能问题 Blazingly fast completion ;) · Issue #52 · minad/cape · GitHub , 这样基于同等知识的讨论, 虽然有很多个人观点, 最起码是相互之间能够理解对方的意思。 而不是社区很多人, 不说事实, 说不清楚就开始扯生态、 兼容性和模块化等等一系列个人情感的东西出来辩论, 当对方的知识差距太远, 不客观还超级傲慢的非要和你胡扯时, 结果就是一个, 烦。

11 个赞

大佬太牛逼了

期待大佬的测试结果,这样是不是eglot也可以跟lsp-bridge的速度不相上下了?

我上面写了三条原则,都到达才可能速度在一个量级,缺一不可。

我的一些非技术和技术方面的看法:

非技术方面:

  1. 各人对速度追求不一样。有人追求极致的速度,但对另一些人来说,足够快就好了,速度并不是最重要的。

  2. 习惯的力量是强大的,而且习惯的差异很大。我平时读代码基本不用lsp,而是用ctags和rg;只有在写代码的时候才会使用lsp。

技术方面:

Emacs这种由全世界不同贡献者合力推动发展的软件,模块化和扩展性是非常重要的,所以在很多方面需要的是协议(像capf,xref),而不是特定的实现。acm如果要对标capf,需要提供大家都可用的协议。

3 个赞

看了下eglot代码,目前好像也只是在xref相关的部分里用了

即使是非lsp领域,比如rg等都是需要异步补全框架的,可以对比下blink-seach和ivy在用rg搜索root目录的性能差别,ivy遇到大数据会导致GC卡顿一下。

capf这种同步思想下实现的接口先天对性能支持就有缺陷,一个有先天缺陷的接口要它干嘛用?

时间是检验真理最好的手段,习惯是可以改变的,我相信在性能面前大家会用脚投票的。

我对不同观点的偏好不关心,其实每个人都有自己的选择,尊重你的选择。

所以还是需求和期望不一样,Emacs is all about choice。自己用得开心最重要。

对,你说得对,只要技术原理说清楚,大家相互理解就好了。

至于选择,随着自己喜好吧,emacs最大的价值是跨越很多年,大家都可以找到自己喜欢的插件搭配。

我一直用的 eglot,其实它的速度对我来说 Emacs 28 以后一直是流畅的(主要用的 clangd, pyright 和 haskell-language-server 这几个后端)。

我主要在 macOS 和 Windows 上用,也尝试过几次 lsp-bridge,不过在 macOS上用 lsp-bridge 时发现弹窗延迟很明显,后来就没再用了。估计是因为我用的 Emacs29 和 macOS,如果是 Linux 也许会很流畅。

回头我再试试最新的 lsp-brigde。

比较认同这个观点。

首先设计优雅的“接口”,一开始可以给出粗糙一点的“实现”。

当使用场景/频率越来越多,“实现” 就可以获得更多的精力来进行优化, 而“接口” 是基本上不会大变的

我们习惯的或需要肌肉记忆就是这些 “接口”,当 “接口” 能兼容或被扩展到更多的场景的时候,那就是个好的设计,少即是多。

性能在目前CPU/内存低廉的情况下应该不是最重要需要被考虑的因素

我可能更在乎 anything as code

Emacs跑在容器里,数据挂载过去(这里数据还包括配置,以及静态链接的 emacs-dyn.so文件)

比如 开会时用 surface go 通过ssh-agent 一条命令 ssh -t emacsclient -t 随时随地继续我的工作

@guanghui.qu 大佬,我今天在 Windows 10 上编译最新的 emacs-29 分支,在 C++ 和 Python 试了下 eglot ,很顺滑,体验上和 vscode 接近了 :grinning:。最明显的提升是跳转的时候是瞬时的。

大家可以多试试,分享下体验。

1 个赞

windows试下拷贝一份sqlite3.c源码,在中间段编辑某个函数呢,之前测试eglot比lsp mode要卡才转lsp mode,现在不知道咋样了

电脑性能足够强,是不是能抹平差异呀。 :grin:

我试了,一样很流畅。不过这样直接拷贝的话,项目是不完整的。你可以自己再试试。

可能也有这方面原因 :smile:

我用的是 Dell Precision 7530 笔记本电脑

CPU:Intel(R) Core™ i7-8850H CPU @ 2.60GHz 2.59 GHz
内存:64G
显卡:NVIDIA Quadro P1000