Tubi 赞助与组织的第八期 Elixir Meetup 于五月底顺利结束,三位 Elixir 资深使用者 Horvo、Scott 和杨淼,与 660 多名线上线下的函数式编程爱好者分享了 Elixir 的相关应用与实践经验。本文回顾了 Scott 带来的分享“ Lexical ——下一代的语言服务器”,回顾笔记来自观众 Rylynn。
欢迎关注比图科技公众号,查看 Horvo 带来的分享《用 Elixir 开发 HLS 直播后端服务》。
什么是 Language Server Protocol (LSP) ?
为编程语言添加自动补全、转到定义或悬停文档等功能需要大量的工作。传统上,每个编辑器为实现相同的特性提供了不同的 API,因此这些工作在每个编辑器中都必须重复。
语言服务器旨在提供特定于语言的智能,并通过支持进程间通信的协议与开发工具进行通信。语言服务器协议 (LSP) 背后的思想是标准化这些服务器和开发工具如何通信的协议。通过这种方式,单个语言服务器可以在多个开发工具中重用,这反过来又可以最小的工作量支持多种语言。
LSP 是语言提供者和工具供应商的双赢!
编辑器与 LS 的通信简化过程
设想一下,如果你需要跳转一个函数的定义到源文件,使用 LSP 协议会有多简单呢?
在你打开文件之后,你的光标选中引用函数之后,客户端 (VScode/nvim/Emacs) 会向语言服务器 (LS) 发送一个 method 为 textDocument/definition
的请求,参数为当前光标位置,即行与列,LS 接收到请求之后,则返回目标文件的 URI、Location 信息,之后客户端便可依据这些信息进行跳转了。
Lexical 的发展背景
虽然 Scott 没有直接比较 Lexical 与 旧有 Elixir-ls 的差异,但是通过“一个理想的语言服务器具有哪些特点”的分享,我们也可以对 Lexical 的优势有清晰的了解。
一个理想的语言服务器,其终极的目标是为开发者写代码和读代码提供愉悦的使用体验。要达成这个⽬标,Scott 认为 LS 需在以下几个方面有出色表现:
· 可靠,不轻易崩溃,若是崩溃,也能明确缘由
· 基础功能精确,⼀致且完整
· 体验极佳的反馈循环
· 易于贡献
当我们可以在一个语言服务器中实现前三点时,便能极大地保障开发者的心流体验不被打断。关于第四点,一个理想的语言服务器,应让使⽤该语⾔的开发者能轻易贡献或增强 LS 本身,这也是 Lexical 设计之初的⽬标。
Lexical 从四个方面考虑并突出其“易于贡献”这一特点。
⾸先,架构和设计都应该尽可能简单,这样才易于理解,降低参与的门槛;其⼆,需要易于测试,因为好的测试能给开发者充分的信⼼,继续做出贡献;其三,它应该让开发者尽可能关注业务逻辑,⽽非协议数据间的转换;其四,它需要具备强⼤的扩展能⼒,每个团队、每个⼈都可基于自身对编辑器的需求而进行扩展。
关于架构简单、易于测试、关注业务逻辑这三点如何实施,Scott 在后面的分享中做了详细介绍,请继续阅读本文。如果你对第四点“扩展能力”有感兴趣,请观看视频回放!
Lexical 的解决方案
双节点解决请求与业务逻辑穿插问题
为了解决与项目之间的版本依赖问题,Lexical 引入了 Project Node VM。将请求与业务逻辑剥离,Server Node 与 Project Node 各司其职,不再相互耦合。这是 Lexical 的核心,可以有效地解决上一代 Elixir-ls 将所有项目库的依赖都在一个 Node 中运行而出现污染的情况。
其中:
Server Node 主要做与编译器的交互,同时它会启动⼀个 Project Node,启动的时候会注⼊少量 App 到你的项⽬中,以便让两个 Node 通信更加⽅便。Peoject Node 是编译工具,可对原数据进行提取,与客户端实现交互。
清晰的模块划分使得整体 Lexical 更易维护
在双节点系统设计的基础上,Scott 深入分享了 Lexical 在容器层级和组件层级的架构与实现。
整体的 Server Node 分别由五个 App 组成,分别用于解决数据一致性、通信和业务模块集成等问题。其中,特别需要关注的是 Server App 和 Remote Control App,这里往往包含真正的业务逻辑。
通过这样的设计,我们可以清楚地看到 Lexical 极大地降低了 LS 的复杂度,继而可以确保一致性与准确性,甚至让以前在 Code Review 的很多工作,比如没使用某个公共函数、代码复杂度过高、alias 没展开等,都有可能集成到 LS 中,极大地缩短了反馈时间。
如何实现 LS 功能?
Scott 以 Server App 的⼀些组件为例,从同步和异步的⻆度帮助大家理解 LS 的功能。
⽐如 Navigation 的相关功能是同步的,我们会在 Server Genserver 接收请求后直接转发给 Provider,⽽通知类的功能则是异步的,我们会在 Server App 的 Project ⽂件夹下有一系列 Genserver,用于接收 Project Node 发出的消息,并把它们转化成编辑器需要的 Response。
Lexical 的后续发展
在认识了 Lexical 的发展背景和架构设计之后,Scott 又带大家详细地了解了 Lexical 的后续计划:
· Pluggability:这是⽬前的⼯作重⼼,但还处于架构阶段
· Navigation:这是排在 Plugin 之后的第一重点,但它的复杂度很⾼
· Diagnostics, Completions 等现有功能的提升
目前,Lexical 整体上仅有 Steve Cohen 等几位核心开发者在维护着,为提供更丰富的功能,还有一段路要走。
Scott 在现场手把手地演示了 Elixir 用户可以如何创造属于自己的 LS 功能,鼓励更多人参与贡献 Lexical,共同为社区创造一些价值。如果你感兴趣,也欢迎参与到Lexical 项目中!
Elixir 在 Tubi 的其他应用案例
加入 Tubi 一起成长变强!
热招岗位:https://tubi.tech/careers/
微信公众号:【活动回顾】Lexical ——下一代语言服务器