讨论一下lsp-mode作者在emacs里实现的异步非阻塞json-rpc

其实我一直有个疑问,lsp-bridge 及 deno-bridge,最终会由外部进程 eval-in-emacs 来传递信息或编辑 buffer,相当于一定程度上把控制权交给了外部进程,如果以这种模式的各种插件生态起来的话,是不是会有很多冲突问题,如果是同步的话还好,现在这种全异步的方式,就和之前 lsp-bridge issue 里提到的 buffer 变乱的问题,感觉目前存在一个状态同步的问题

我觉得这种情况不只是emacs用户才有吧,非emacs用户也是这样的,比如我就遇到过某些写nodejs的,就想所有功能都用nodejs实现,其它语言的解决方案听都不听。

1 个赞

他这个确实使用线程做的解析。

想尝试的话,自己 clone 一份代码,然后合并一下就行,冲突不多,性能没细分析过。

只是不喜欢搞一大堆什么乱七八糟的依赖,要是用golang c rust之类的容易接受很多,也就是多个几mb的文件。什么py js乱七八糟的一大堆东西有点恶心,也不利于分发。虽然硬盘流量都不值钱但就是不喜欢。

1 个赞

我也是这种感觉。python我觉得还好,node+js那真的是一点兴趣都没有。

如果有多个插件同时以异步方式操作当前buffer,感觉是会有这个问题。这也是我在lspce用半异步的方式的原因(emacs-module-rs本身支持调用elisp方法,不过我没用),所有的动作都由Emacs触发和执行。也是到现在都不支持lsp server的request的原因。

Python 有标准库,相比 node 随便个小功能就拖家带口一大堆包要好很多。

查看本机信息、本地IP、时区,字母大小写转换这类小工具(Alfred Workflow),用 shell 或者 python 或者哪怕纯 js 都是几行代码的事儿,node 都能搞成几兆的一坨东西。

后来我干脆把几乎所有用到 node 的小工具全部删掉换成同类替代的 shell/python/rust/go 实现的,简直神清气爽,再也不会被 GitHub 提示什么 package security、version bump 了。

以上仅是我非程序员视角的观察。

插件 a 用 commonlisp,插件 b 用 python ,插件 c 用 nodejs,想要改的话要学 3 种不同语言。 如果大家都用 elisp 写的话,大家就都看得懂能够做贡献, 用不懂的语言写的插件只能对作者提需求,需要麻烦作者 作者放弃维护就没法自己接过来维护,只能放弃使用。

而且依赖变多以后,安装难度容易增加。

用其他语言的话就没法用 advice 一类的功能,尽管功能更强大了,但细节没能力修改,对 emacs 的定制性有所影响。

我认为就和 “车同轨,书同文” 一样,大家能够相互理解各自的代码,包之间的代码可以复用,这样才长久吧。

2 个赞

呃。。理性讨论,注意语气。。。

可能是太久没打这么长串了,抱歉

尝试改了下,你看语气还好吗?

:+1: 语气或者个人意愿过于强烈的话,可能会引起反感 :)

语言可以有喜好, 这个没有问题, 但是Python和JS不如Golang、 C、 Rust好分发这一点很难认同, 脚本语言基本上下载好了就可以直接跑, 任何Linux系统都会内置 Python, JS只要你有浏览器就可以直接运行, 请不要把 JavaScript 和 Node等同起来, NodeJS 确实依赖很多, 但并不代表 JavaSciprt 和 TypeScript 运行一定需要 npm 依赖, 可以搜索一下 Deno 看看, Deno 基本上运行 TypeScript 就是 Python 一样单文件。

可以不喜欢脚本语言, 但Golang、 C、 Rust扩展Emacs哪个比脚本语言更容易? 你写过 rust-module 就知道, 基本上没有什么生产力。 ABI的问题让分发更困难, 而浏览器都在做跨芯片和跨操作系统的WASM了, 以后说不定 WASM 才是超级 ELF 格式, 一次编译到处分发。

Emacs同步没有多线程虽然没有你说的这种问题,但是多线程冲突的本质要区分, 如果Elisp和外部语言都是一个插件的内容, 其实不管是否在一个线程中, 先后顺序都是可以控制的。 如果Elisp支持真正并发的多线程模型, Elisp也会有这种问题。

所以回答是, 你说的冲突不是多种语言或者RPC模型导致的, 而是任何支持多线程的语言和工具都会有这种问题, 需要开发者自己控制就好了。 如果多个工具对一个文件进行操作, 没有统一的控制, 其实都不知道怎么解决的 (比如 Emacs、 VSCode和Git同时对一个文件操作), 这和lsp-bridge的实现无关。

我一直想强调的是, 多语言共存去扩展Emacs是一种 “更为经济和效率“ 的扩展方式。 对, 大家就喜欢和我抬杠说 C 语言香, 你可以用C语言实现一切, 但是现实吗? 一个人的精力有多少? 大家都有自己的喜好, 这个没有问题, 但是脱离实事求是去争论个人对错很重要吗? Emacs缺乏很多库、 Elisp生态包袱导致多线程很难做好、 Emacser宁愿自己裸写C写跨平台也不承认接纳外面世界, 这些不都是事实吗?

我一直都说是要客观承认Emacs和Elisp不足(包括多线程、图形性能和GC性能), 考虑实现成本和效率接纳外部语言和库, 这个和大家说的不冲突啊, 用C写Emacs代码就可以解决Elisp多线程和图形性能? 外部语言的目标是解决Elisp不擅长的事情, Elisp擅长的事情(比如文本光标操作)肯定尽量用Elisp写啊, 外部语言的模型和你们争论的是一件事情吗?

那些一直争论所有都用Elisp实现香的道友们, 我想问一个问题, 有多少人写Elisp代码比我写的多? 有多少人真正写过GUI代码? 有多少人写过并发多线程和抢占式协程代码? 大家的讨论能否基于技术原理和现实事实? 不要只基于自己的情感?

你说的这些都是事实, NodeJS确实不爽, 但是 Deno 直接执行 TypeScript, 体验已经和 Python 一样了, 基本上就是单文件和简单 Import, 没有 NodeJS 的依赖地狱。

这个我分享给你几张截图吧:

社区是否可持续, 取决于开源项目是否有价值、开发接口和文档写的足够好, 是否有很多人参加, 不是说语言多就没法参加, 很多开源项目从底层到上层都是多语言, 比如 Deno 就是 Rust, C++, C, TypeScript 都有, V8也是类似的, 就不类举了。

不影响的, 用别的语言并不是没有Elisp, 只要有Elisp和通信机制, 总是可以定制的。

前半句没错, 如果Elisp本身就没啥毛病, 谁不愿意都用一种语言, 不是图形性能不够需要浏览器和流畅PDF阅读器吗? 不是Elisp性能太弱, 搜索过滤上万数据卡吗? 多语言不是无奈之举吗?

如果啥都是Emacs C可以解决的, 为啥很多问题迟迟解决不了呢? 还不是限制太多, 开发者从零造4个平台的轮子精力不够吗?

最后

我还是和大家说, 讨论基于技术原理和数据事实, 这样更有说服力一点, 实事求是的沟通可以相互学习, 但是不承认事实, 就只是说我不喜欢啥啥啥, 又没有说服力, 世界在高速进步, 而Emacser大多数固步自封。

我和大家在说开发者精力和实现功能的经济性问题,并不代表Emacs不准用 C 和 Elisp实现了, 我个人会15门以上的编程语言, 我这么多年的经验就是没有啥语言是最好的, 只有最合适的语言, 大家越讨论越极端, 这是我沮丧的原因。

这样争论下去, 请别的管理员关闭这个帖子吧, 我该说的都说了。

14 个赞

有一个想法不知道是不是切合实际,外部进程只负责计算,计算完成后只告诉一下 emacs 我算好了,而不是直接给结果或者直接控制 emacs,emacs 收到完成通知后,去拉数据,再进行后续操作,就是最后一层由 emacs 自身同步来处理,而不是把控制权交出去

lsp-bridge 就是这样实现的呀? 最后结果都是让 Elisp 来控制, 难道源代码不是这样实现的吗?

我不知道 lsp-bridge git checkout 那个 issue 是否是你反馈的? 你的那个问题的根源是 git checkout 这个操作, Emacs 不知道是谁进行操作的, lsp-bridge不知道是谁操作的, 这种多个进程工具对同一个文件的操作, 在来源不清楚的情况下, 没法统一控制。

但是我过几天写了一个补丁 We use `lsp-bridge-revert-buffer-flag' var avoid lsp-bridge send chan… · manateelazycat/lsp-bridge@86ca73a · GitHub , 就是不管哪个进程操作 git 的(比如 magit, eaf-git 或者 外部 git 命令), 最后执行 revert-buffer 操作的时候, lsp-bridge 就重新读取文件了呀。

我一直不明白的是, 你为什么会觉得外部进程和外部线程会导致 Emacs 失控? Emacs 是产生数据和操作的源头, 也是最后处理外部工具(不管动态模块、 命令行还是RPC)的结尾, 一切都是 Emacs 自己处理的呀, 为啥控制权要和多进程、 多线程这些绕在一起呢?

2 个赞

现在是外部进程 eval-in-emacs 的形式,相当是一个推的模型,可能会出现多个线程同时操作的情况,我刚刚表达的是一个拉的模型,外部进程计算结果会是一个结果集,由 emacs 同步去拉取执行,这样可能会牺牲一些性能,但同步上会更好吧? 多线程情况比较复杂,这里只是单纯的想讨论一下这个问题,没有别的意思

eval-in-emacs 的形式最终也是调用 elisp 代码呀, elisp 函数可以在推的时候做消息队列管理再按照顺序执行, 这样真正有多个线程也没有问题(问题的关键是, 你担心的这一点多线程的返回已经在 lsp-bridge python 端已经排队处理了, 不会导致多个 lsp server 返回结果改一个文件的事情发生, 具体实现可以看:

我想 你的, 多线程并不意味着失控, 只要多线程结果返回的时候做队列处理就不会有竞争条件出现, 但是和Python(或者任何其他工具) 是多线程推送Emacs还是Emacs去轮询拉没有关系。

线程的冲突在于设计者自己控制冲突, 和同步拉, 和性能啥的没有关系, 更和 eval-in-emacs 没有关系。

如果你真正写过几万行多线程代码, 你就明白我上面说的是什么意思。

了解了,感谢解答

@oldosfan 满足你说的这三个要求,而且他不同意你的观点。