👩🏻🚀 本项目仍然处于实验阶段。欢迎贡献者和先行者!
LSPService 是一个本地 Web 服务,允许编辑器通过 WebSocket 与任何本地 LSP 语言服务器通信。
LSPService 本身是用 Swift 编写的,并且主要使用 Swift 语言服务器 (sourcekit-lsp) 进行测试。但原则上,LSPService 可以连接到所有语言服务器,并且将来可以轻松添加 Linux 支持。
LSPService 包本身包含的代码非常少,因为它 a) 大量利用了 Vapor,并且 b) 我将其中的大部分功能提取到了 SwiftLSP 和 FoundationToolz 中。
语言服务器协议是软件开发工具的现在和未来。但将其用于工具项目 оказалось 很困难。
例如,我通过 Mac App Store 分发 一个开发者工具,所以它必须是沙盒化的,这使得它无法直接处理语言服务器或技术世界的任何其他“工具”。
所以我想:如果语言服务器只是一个本地 Web 服务会怎么样? 可能的好处:
如果 LSPServiceConfig.json
文件尚不存在,LSPService
将在启动时创建一个。 如果文件存在,它会从文件中加载服务器配置。
用户或管理员应该通过编辑 LSPServiceConfig.json
来配置 LSPService
。 将来,LSPService
创建的配置文件将包含所选已安装语言服务器的条目。 目前,自动检测仅适用于 Swift。
LSPService
。 它将在终端中运行,只要它在那里运行,该服务就可用。 检查:https://:8080LSPServiceConfig.json
并重新启动 LSPService
。 LSPService
创建的 LSPServiceConfig.json
文件至少包含一个条目,并且 JSON 结构非常不言自明。swift build --configuration release --arch arm64
swift build --configuration release --arch x86_64
.build/<target architecture>/release/LSPService
获取它们LSPService
LSPService 的唯一目的是通过 WebSockets 使本地 LSP 语言服务器可访问。
LSPService 将来自某个编辑器(通过 WebSockets 传入)的 LSP 消息转发到某个语言服务器(传出到 stdin),反之亦然。 它对 LSP 标准本身一无所知(除了如何检测语言服务器输出中的 LSP 数据包)。 编码和解码 LSP 消息以及通常使用正确的类型表示 LSP 仍然是编辑器关心的问题。
另一方面,编辑器对如何与语言服务器通信、定位和启动一无所知。 这些仍然是 LSPService 关心的问题。
请参阅 LSPServiceKit 作为示例代码,或者如果你的客户端是用 Swift 编写的,则直接使用它。
LSPService 基本上只有一个端点。 你连接到它的一个 websocket 以便与与语言 <lang>
关联的语言服务器通信。
http://127.0.0.1:8080/lspservice/api/language/<lang>/websocket
根据你使用的框架,你可能需要显式设置 URL 方案 ws://
。
根据 LSP 规范编码 LSP 消息,包括标头和内容部分。
通过 WebSocket 的数据通道发送和接收 LSP 消息。 数据通道专门用于 LSP 消息。 它永远不会输出任何其他类型的数据。 它输出的每个数据消息都是一个 LSP 数据包(标头 + 内容),因为 LSPService 从语言服务器输出中将数据包拼凑在一起。
LSP 响应消息可能会通知错误。 这些 LSP 错误是编辑器的重要反馈。
除了 LSP 消息之外,WebSocket 只有两种方式提供实时反馈
以下是 LSPService 的内部架构(组成和基本依赖关系)
上面的图像是用 Codeface 生成的。
从版本/标签 0.1.0 开始,LSPService 遵循 语义版本控制。 因此,在达到 1.0.0 之前,REST API 或设置机制可能仍然会频繁中断,但这将在版本升级中得到体现。
LSPService 已经在生产中使用,但 Codeface 仍然是其主要客户端。 LSPService 将在以下情况立即升级到 1.0.0 版本
使用 WebSockets 和 sourcekit-lsp 实现概念验证
为所有语言提供一个动态端点,如 127.0.0.1:<service port>/lspservice/api/<language>
让 LSPService 为 Swift 端点定位 sourcekit-lsp
评估客户端编辑器是否需要接收来自语言服务器进程的 错误输出。
探索是否可以调整 sourcekit-lsp 以在无法解码传入数据时发送错误反馈。 这可能会加速 LSPService 和其他 sourcekit-lsp 客户端的开发。
添加一个端点,供客户端编辑器检测哪些语言可用
正确处理不可用语言的 websocket 连接尝试:发送反馈,然后关闭连接。
将日志记录和错误处理提升到 Vapor 的最佳实践。 确保启动主机应用程序的用户在终端中看到所有错误,并且客户端获得正确的错误响应。
允许使用多个不同的语言服务器。 通过支持/测试 Python 语言服务器来验证概念
添加一个 CLI,以便用户可以从命令行管理语言服务器列表
清理接口:面向未来并重新思考 API 结构,然后对齐 CLI、API 和 Web 前端
记录如何使用 LSPService
评估是否构建一个 Swift 包,以帮助 LSPService 的客户端(用 Swift 编写)定义、编码和解码 LSP 消息。 考虑建议从 SwiftLSPClient 和/或从 sourcekit-lsp 中提取该类型系统到一个专用的包中。
LSPBindings
的形式发生。 但是,LSPBindings
不适用于解码,因为它的解码与将请求与响应匹配纠缠在一起。在继续使用 LSPService 之前,让一个 sourcekit-lsp 客户端项目能够完全使用 sourcekit-lsp 工作
删除“进程 ID 注入”。 添加提供进程 ID 的端点。
正确检测 LSP 数据包(从服务器进程输出中将它们拼凑在一起)
将通用 LSP 类型系统(不是 LSPService 特定的)提取到包 SwiftLSP 中
构建一个 Swift 包,以帮助用 Swift 编写的客户端编辑器使用 LSPService:LSPServiceKit
通过 LSPService 实现“查找引用”请求
为客户端开发者添加 问题排查指南 到 sourcekit-lsp 仓库 (来自开发 LSPService 和 SwiftLSP 的经验)
用 JSON 文件替换 CLI,该文件定义服务器路径、参数和环境变量。 这也使得仅用于配置的 Web 前端变得不必要,增加了持久性,并提高了可用性 ...
调整 API 和文档:删除除 ProcessID 和 websocket 之外的所有路由。 如果我们在将来提供配置 API,它将基于适当的语言配置类型/ JSON。
修复此问题:客户端 (至少 Codeface) 在处理像 sourcekit-lsp 这样的大型 Swift 包时会丢失与 LSPService 的 websocket 连接。 是否有些 LSP 消息太大,无法通过 websockets 以单个块发送?
因为 此 PR 已完成:立即拒绝升级到 Websocket 协议以用于不可用的语言,而不是打开连接、发送反馈然后再次关闭它。
将 LSPServiceKit 调整为大幅精简的 API ...
里程碑 "可发布性": 审核代码和错误日志、版本控制、上传 Intel 和 Apple 芯片的二进制文件 ...
探索有效需要 LSPService 的应用程序是否可以通过 Mac App Store 审核。
结果:它通过了 🥳。 第二次更新也获得了通过,其中全面推广了依赖于 LSPService 的功能,但仍然仅从应用程序内部引用 LSPService。
研究:有新的迹象表明,我们可能能够从沙箱通过 XPC 启动 LSP 服务器。 这将使用户(Codeface 的用户)感到高兴,并为 LSPService 添加一个全新的技术途径(和软件包产品)。
🐍 再次尝试 python 语言服务器 (并使其工作)
🐣 使 LSPService 独立于 LSP,并将其转变为允许任何应用程序使用任何本地命令和工具的服务。
✍🏻 签名/公证 LSPService,以便更容易安装和信任
🔢 添加版本控制机制,以便在多个编辑器/客户端依赖它时可以开发 LSPService。 这可能需要涉及
📢 将此项目发布出去:文档、宣传、协作、联系潜在的客户端应用程序等 ...
确保 sourcekit-lsp 可用于支持 C、C++ 和 Objective-c
对于无论如何都无法在 App Store 中发布的客户端,并且希望将 LSPService 功能作为导入的 Swift 包而不是本地 Web 服务,该怎么办? 这可能需要将更多功能移动到 SwiftLSP,并为其定义精确的边界/抽象。
在 Linux 上构建/运行 LSPService 怎么样? LSPService 和 SwiftLSP 依赖于 Foundation,可能需要编译器指令,或者通常坚持使用 这个。
如果多个客户端同时需要相同语言的服务怎么办?