跳到主要内容

扩展宿主和语言服务器怎么通信?

结论​

扩展宿主里的语言客户端和语言服务器进程之间走 JSON-RPC。桌面端最常见是客户端用 stdio 或 IPC 拉起服务器子进程。消息分请求、响应和通知。

展开​

标准栈是 vscode-languageclient 加重语言的服务器实现。

const serverOptions: ServerOptions = {
run: { command: serverModule, transport: TransportKind.ipc },
debug: { command: serverModule, transport: TransportKind.ipc, options: { execArgv: ["--inspect=6009"] } }
};

const client = new LanguageClient("myLanguage", "我的语言", serverOptions, clientOptions);
context.subscriptions.push(client.start());

传输方式:

  • stdio:stdin/stdout 传消息,日志不要写到 stdout,否则把协议流弄脏。日志走 stderr 或专门的输出通道。
  • ipc:Node 进程间通信,VS Code 客户端里很常见。
  • socket:服务器独立监听端口,适合已经常驻的服务。

消息形态:

  • 请求带 id,必须有响应,例如补全。
  • 通知不带响应,例如 didChange、publishDiagnostics。
  • 客户端和服务器都可以主动发。

客户端启动时告诉服务器工作区根、能力、初始化选项。服务器在 initialize 的结果里声明自己支持补全、增量同步、代码操作等。客户端只注册服务器声明过的能力。

远程开发时,这个子进程应该在工作区那一侧拉起,这样服务器和源码、编译器在同一台机器。

容易讲错的点​

  • 不要自己用 console.log 往 stdio 服务器的标准输出打调试信息。
  • Webview 的 postMessage 不是这条链路。语言协议不要绕进网页里再转一圈。
  • 一个客户端可以管一个服务器进程。多个窗口是否共享服务器,要单独设计,默认每个窗口一份更简单。