扩展宿主和语言服务器怎么通信?
结论
扩展宿主里的语言客户端和语言服务器进程之间走 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不是这条链路。语言协议不要绕进网页里再转一圈。 - 一个客户端可以管一个服务器进程。多个窗口是否共享服务器,要单独设计,默认每个窗口一份更简单。