LSP 的设计探讨

感觉 LSP 是个 IDE 的关键。
看到 @ice1000 微软的 LSP 的根本性设计错误
@Peefy 也有 相关工作

个人拙见,抛砖引玉。

微软的 LSP 的根本性设计错误 一文中指出:

目前的实现是 client 给 server 发送 location/range,然后 server 处理后发一些字符串,或者新的 location/range 给 client。

没理解错的话,当前各种反馈信息比如报错也是以“反馈文本信息+位置/范围信息”的形式传递的。

那么,延续文中的思路,如果反馈信息能够以更结构化、与语法树结合更紧密的方式传递给客户端,那么客户端也可以完成更多分析功能、提供更全面或简约的信息。比方说,当某个变量名改错了,与其出现几百个引用报错,可以整合为一个修正提议;或者如果推理中发现两句有矛盾,可以准确定位两句分别在哪里。

Anders Hejlsberg on Modern Compiler Construction | Microsoft Learn 根据 anders 视频中提到的那样,client/editor 扮演一个无知的角色,所有信息由 lang-server 提供,相当于要在 compiler 中实现错误恢复,增量编译等难度较高的 feature,为了用户体验,甚至还需要配合类似一些索引工具所做的工作如 grep,仅仅靠语法树、符号表这些玩意算法复杂度还是太高。不同语言 lang-server 确实会重复做一些工作,技术架构仍有很大提升空间。不过从 client 的角度,lang-server 只要实现一次即可给大部分 IDE/Editor 使用,还是要比一个 Editor 都要实现一次有了一些进步。

目前在 KCL 中也在做类似的工作,虽然是一个领域编程语言,但是还是期望能够给用户提供与通用语言一样卓越的编程体验。因为我们某些场景的 KCL 代码量已经到达了 300-400 文件量级,各种 Editor 事件处理如补全和跳转等平均是 100-200 ms,按照人类打字及反应时间,想要在编写 KCL 代码感觉不卡,仅靠编译器本身去 loop 性能已经不足够 (编译器本身是 Rust 写的,性能已经很快了),需要进一步靠增量编译,索引查询系统去加持才能足够。

1 个赞

以及个人认为 LSP 只是标准 client-server 的连接层或者规范层,目标是将 client 和 server 各自工作侧重点分离,它本身不承担过重的工作

结合 客户端-服务端 的架构思路,感觉 @ice1000 的设想是富客户端的方向。以 KCL 的例子,如果客户端对代码有更多知识(如语法树),那么可以在客户端对(至少部分)输入作初步分析后作出几乎实时的响应而无需访问服务端。

从当前市场看来,常用 IDE 已经很少。尤其对新创语言来说,初期往往对最多两个常用 IDE 作支持。

用户的 IDE 插件一般是本地启动的lsp服务器,访问 server 的 rpc 请求可以忽略不计,client 端一般同时需要纯文本和语法树,client 当然可以拥有和处理语法树。目前市场上所有流行语言插件也不是全部都写在 server 端的,比如 Python 这种,微软的 pyright 插件就是全部用 typescript 写的(包括语法树,parser 等)。

新语言一般支持微软系、Jetbrain 系、Vim 即可,其他的可能就需要社区大家一起来了,比如 Vue Compiler 由官方编写,但是其他所有的 IDE 插件都是社区维护的

推荐自行实现一个 language server, 你就对它里面的架构有一个非常完整的认知了. 的确是可以完全使用 ts 实现 client 端的, 但我觉得这部分代码也有应该复用的地方

LSP类似RPC工作模式,这里的client其实也可以直接调用内联的函数,server只是一个抽象的概念

1 个赞