编程语言服务器协议

上个月,Apple 在 Swift.org 论坛上宣布,它正在开始采用 Swift 和 C 语言的语言服务器协议(LSP)。

在 Apple,我们优先考虑为所有 Swift 开发者提供高质量的工具,包括那些在不在 Apple 平台上开发的开发者。我们希望和开源社区合作并且集中精力建设可以由 Xcode 和其他编辑器和平台共享的公共基础设施,因此,我们选择采用 LSP。

Argyrios Kyrtzidis,2018年10月15日

这可以说是自 2014 年 Swift 开源发布以来苹果公司对 Swift 做出的最重要的决定。这是苹果应用开发者的大事件,更是在其它平台的 Swift 开发者的大事件。

为什么这件事情这么重要?本文会对语言服务协议进行介绍,讲解它能解决什么问题,如何解决这些问题,以及可能会带来什么样的长期影响。

我们假设有这样一个表格,它的行代表不同的编程语言(Swift、JavaScript、Ruby、Python 等),每一列代表不同的代码编辑器(XCode、Visual Studio、Vim、Atom 等)。这样一来,表内的每个单元格都表示某个编辑器对某种语言的支持程度。

现在,你会发现不同的组合之间兼容性很差。有一些编辑器对少数几种语言进行了深度集成,几乎不支持任何其他语言,也有一些编辑器则致力于支持语言的通用特性,对大量的语言进行了较为浅层的支持。(IDE 通常是指前者。)

举例来说,不使用 XCode 开发苹果应用是件很难理解的事情,而使用 XCode 开发其它东西简直就是傻。

为了让编辑器对语言支持得更好,它需要编写集成语言特性的代码 —— 直接在项目代码中编写,或者采用插件系统的方式。语言和编辑器之间会存在差异,比如在 Vim 中改进了对 Ruby 的支持并不能同时让 Vim 对 Python 支持得更好,也不能让 Ruby 在 Atom 中更好地工作。结果就是:对各种技术之的支持不能达成一致,会浪费大量的精力。

这里描述的情况通常被称为 M × N 问题,它要处理的问题数是 M 种编辑器和 N 语言的乘积。语言服务协议要做的就是把 M × N 的问题变成 M + N 的问题。

编辑器不用支持每一种语言,只需要支持 LSP。然后,所有支持 LSP 的语言都会在这个编辑器中得到相同级别的支持。

omohiro Matsuyama 在 2010 年写了一篇标题为 “Emacs は死んだ” (“Emacs 已死”) 的文章,对这个问题进行了很好地总结。Matsuyama 在描述 Emacs 脚本语言的局限(没有多线程、低层次的 API 少、用户群不大)时提出他的观点。他认为插件应该与外部程序接口,而不应该是本地实现。

语言服务器协议对其支持的语言提供了一系列通用的功能,包括:

  • 语法高亮

  • 自动格式化

  • 自动完成

  • 语法(检查)

  • 提示

  • 内部诊断

  • 跳转到定义

  • 在项目中查找引用

  • 高级文本或符合搜索

在 LSP 的支持下,工具和编辑器可以更加专注于处理可用性和高级功能,而不需要为每个新技术做重复性的工作。

语言服务协议是怎么起作用的

如果你是 iOS 开发者,你可能最熟悉的“服务”和“协议”概念是建立在 Web 应用基于 HTTP 和 JSON 的通信技术之上。这实际上与语言服务协议如何工作并没多大关系。

对于 LSP 来说,客户端就是编辑器 —— 或者更普适性的说法是工具 —— 而服务端是指运行在本地另一个进程中的外部程序。

作为协议本身,LSP 有点像 HTTP 的简单版:

每个消息都包含头和内容两个部分。

头部需要用 Content-Length 参数来描述内容部分的字节数,以及可选的 Content-Type 参数(默认是 application/vscode-jsonrpc; charset=utf-8)

内容部分是请求、响应和通知的内容,其数据结构遵循 JSON-RPC 规范。

一旦工具有变化,比如用户条到了一个标记的定义之类的,工具都会向服务端发送一个请求。服务端收到这个请求之后返回对应的响应。

例如,想象一下用户在一个支持编程语言服务协议的类Xcode的编辑器中打开如下Swfit代码:

Swift

当用户 ⌘-单击第二行的继承语句中的 Parent 标记的时候,编辑器跳到了第一行的 Parent 类定义处。

以下是LSP如何在银幕背后让这个交互实现的:

首先,当用户打开 Swfit 代码的时候,编辑器在一个单独的进程中启动了他的 Swift语言服务器,如果没有运行起来的话,会实施一些额外的设置。

当用户执行 “跳转到定义” 的命令时,编辑器将如下请求发送到 Swift语言服务器:

JSON

收到这个请求之后, Swift 语言服务使用像 SourceKit 这样的编译工具把代码实体标识出来,并在前面的代码中查找其声明位置。语言服务随后响应这样的消息:

JSON

最后,编辑器导航到相应的文件(示例中就是打开的这个文件),将光标移动到响应指示的位置,把标识高亮显示出来。

这种方法的美妙之处在于编辑器在做这些事情的时候,不需要知道任何与 Swift 编译语言相关的事情,它在乎 .swift 文件与 Swift 代码之间的关联。编辑器需要做的事情就是与语言服务对话,并更新 UI。只要编辑器能做这件事情,那就可以按照相同的处理过程,实现任意语言的代码与相应的语言服务之间进行交互。

Clang/LLVM的语言服务协议支持
如果上面的M + N的图看起来很熟悉,可能是因为LLVM采用了相同的方法。

LLVM的核心是一个中间表示(IR)。支持的语言使用前端编译器生成IR,然后该IR可以生成任何后端编译器所支持的平台的机器码。

Swift和C语言使用的LLVM前端编译器称为Clang。在最近的5.0.0发布版本中,Clang添加了一个名为 Clangd 的新工具,即LLVM的语言服务协议实现。

在2018年4月,苹果公司在LLVM邮件宣布,它将把开发工作的重点从 libclang 转向 Clangd,以此作为创建交互工具的主要方式。

现在你也许会想,“那又怎样?” 苹果公司是LLVM项目的最重要的支持者之一——不算其他,它聘请了该项目的创始人 Chris Lattner 超过10年。苹果公司的从一个复杂的Clang工具切换为另一个的决定似乎只是一个实现细节(可以这么说)。

这一声明非常有趣的是,Clangd似乎是完全在苹果公司外部创建的,谷歌和其他公司都做出了重大贡献。这一声明标志着工具开发方向的重大转变——这一点将在6个月后的 Swift.org 得到证实。

苹果公司支持语言服务协议的可能结果
根据苹果公司10月份发布的LSP的公告,我们预计在未来几周内看到该项目的首批代码(从撰写本文时;到11月中旬之前)。

要感觉到这些发展的影响则需要更长的时间,但是相信我:你的耐心会得到回报的。以下是在未来的几个月或者几年内我相信会发生的一些事情。

Swift作为一种通用编程语言变得越来越有吸引力
尽管Swift主要用于应用开发,但它从一开始是作为一个强大的通用编程语言来设计的。在 TensorFlow 的 Swift,SwiftNIO,和其他项目之中,我们看到Swift 在 App Store 之外开始崭露头角。

目前阻碍 Swift 成为主流编程语言的最大因素之一是它对Xcode的依赖。

让一个web开发者或者机器学习工程师下载Xcode就只是为了尝试一下 Swift,这个要求太多了,而且现在有很多强大的可替代工具的门槛低的多。对语言服务协议的支持应该对那些在苹果生态之外的人们来说,衡量Swift和其他他们所使用的同类工具要比之前简单得多。

Xcode 会变得更好
采用了LSP不仅仅是为了让 Swift 能在其他编辑工具中更好地运行;Xcode也将从中获益良多。

考虑苹果公司 Swift 项目主管 Ted Kremenek 的这篇文章:

LSP服务[Argyrios] 描述的功能比现在的SourceKit更强大。

LSP为 Xcode 团队提供了一个机遇,使其可以采用一种全新的方式集成 Swift,还可以利用其自1.0发布以来的四年对语言和工具的所有改进。

Xcode (最终) 会变得更强大
LSP 的好处并不局限于 Swift 和 Objective-C。Argyrios 在另一篇文章中说:

Xcode 使用我们新的 LSP 服务,就意味着它也能使用其它 LSP 服务。我们对此非常有兴趣,但到目前为止,我们还没有制定出与之相关的具体计划。

目前工作的重点是改进 Swift 相关的东西。这件事件一旦完成,就可以通过 LSP 支持很容易地把这些好处应用到其它语言。

软件架构能反应创建该软件的组织结构和价值。但也并不总是如此。

苹果公司曾经承诺要让 Swift 在苹果生态系统之外也获得成功,而这个承诺通过向 Xcode 开放语言服务协议标准正得以兑现。我认为这是可靠的:使用工具(或缺乏工具)通常是某种技术获得关注的关键决定因素。但我相信一件可能更重要的事情:这一决定表明公司内部(至少有一小部分人)愿意加强合作并提高透明度。

本文转载自:开源中国社区

最新
分类
最新
标签