LSP 协议概述
LSP(Language Server Protocol,语言服务器协议)是现代编辑器语言支持的事实标准:一次实现,多种编辑器复用。理解 LSP 是构建大型语言插件的关键。
LSP 是什么
LSP 由微软提出,定义了语言服务器与编辑器客户端之间的通信协议:
| 特性 | 说明 |
|---|---|
| 本质 | JSON-RPC 协议规范 |
| 目标 | 语言能力一次实现,多编辑器复用 |
| 生态 | VS Code、Neovim、Emacs、JetBrains 均支持 |
| 场景 | 补全、诊断、跳转、悬停等语言特性 |
解决的问题
传统方案每个编辑器都要为每种语言写一遍语言工具。LSP 把「语言分析」独立成服务器进程,任何编辑器接入即可获得完整语言能力。
客户端-服务器架构
┌─────────────────────────────┐
│ 编辑器(Client) │
│ ┌───────────────────────┐ │
│ │ 插件 / LSP Client │ │
│ └──────────┬────────────┘ │
└─────────────┼───────────────┘
│ JSON-RPC (stdin/stdout 或 socket)
┌─────────────┼───────────────┐
│ ▼ │
│ Language Server(独立进程) │
│ ┌───────────────────────┐ │
│ │ 语言分析引擎(AST 等) │ │
│ └───────────────────────┘ │
└─────────────────────────────┘角色分工
| 角色 | 职责 |
|---|---|
| Client | 编辑器侧:发送文档内容、展示结果 |
| Server | 语言分析:补全、诊断、跳转计算 |
| 通信 | JSON-RPC 消息(stdin/stdout) |
进程隔离
Server 是独立进程,与编辑器隔离:
- 语言分析崩溃不影响编辑器
- 支持 CPU 密集型计算
- 可跨语言实现(TS/Python/Rust/Java 均可写 Server)
JSON-RPC 传输层
LSP 基于 JSON-RPC 2.0 通信:
三种消息
| 类型 | 说明 | 示例 |
|---|---|---|
| Request | 请求 + 响应 | 补全请求 |
| Response | 请求结果 | 补全结果 |
| Notification | 单向通知 | 文档变更推送 |
消息结构
json
// Request
{
"jsonrpc": "2.0",
"id": 1,
"method": "textDocument/completion",
"params": {
"textDocument": { "uri": "file:///app.ts" },
"position": { "line": 10, "character": 5 }
}
}json
// Response
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"items": [ { "label": "map" } ]
}
}内容帧格式
LSP 消息通过 Content-Length 头帧化:
Content-Length: 123\r\n
\r\n
{JSON 消息体}vscode-languageclient 包
Client 侧使用的官方库:
json
{
"dependencies": {
"vscode-languageclient": "^9.0.0"
}
}typescript
import {
LanguageClient,
LanguageClientOptions,
ServerOptions
} from 'vscode-languageclient/node';Client 能力
| 能力 | 说明 |
|---|---|
| 管理 Server 进程 | 启动/停止 |
| 转发语言事件 | 文档、编辑、光标 |
| 接收结果 | 补全/诊断渲染 |
| 错误处理 | 崩溃重启 |
vscode-languageserver 包
Server 侧使用的官方库:
json
{
"dependencies": {
"vscode-languageserver": "^9.0.0"
}
}typescript
import {
createConnection,
TextDocuments,
ProposedFeatures
} from 'vscode-languageserver/node';Server 能力
| 能力 | 说明 |
|---|---|
| 协议解析 | JSON-RPC 编解码 |
| 文档管理 | TextDocuments 管理器 |
| 能力注册 | 声明支持的特性 |
| 结果推送 | 诊断等主动通知 |
Client/Server 进程通信模式
启动模式
| 模式 | 说明 | 场景 |
|---|---|---|
| 子进程 | spawn 启动 Node 进程 | 常规 |
| 调试模式 | 附加到调试器 | 开发调试 |
| 独立进程 | 已运行的进程 | 复用 |
typescript
// 子进程模式
const serverOptions: ServerOptions = {
run: {
module: path.join(__dirname, 'server.js'),
transport: TransportKind.ipc
},
debug: {
module: path.join(__dirname, 'server.js'),
transport: TransportKind.ipc,
options: { execArgv: ['--inspect=6009'] }
}
};传输方式
| 传输 | 说明 |
|---|---|
TransportKind.ipc | Node IPC 通道 |
TransportKind.stdio | 标准输入输出 |
TransportKind.pipe | 命名管道 |
LSP 支持的语⾔特性
| 类别 | 特性 |
|---|---|
| 编辑 | 补全、格式化、代码操作 |
| 导航 | 定义、引用、高亮、符号 |
| 诊断 | 错误/警告推送 |
| 信息 | 悬停、签名、文档链接 |
| 语义 | 语义着色、InlayHint |
| 重构 | 重命名、提取 |
特性与协议方法
| 特性 | LSP 方法 |
|---|---|
| 补全 | textDocument/completion |
| 定义 | textDocument/definition |
| 诊断 | textDocument/publishDiagnostics |
| 悬停 | textDocument/hover |
| 格式化 | textDocument/formatting |
| 重命名 | textDocument/rename |
何时使用 LSP
| 场景 | 选择 |
|---|---|
| 简单语言特性 | 普通 Provider(更快) |
| 复杂语言分析 | LSP(AST/类型系统) |
| 多编辑器支持 | LSP(复用) |
| 性能敏感 | LSP(独立进程) |
与普通 Provider 对比
| 对比 | 普通 Provider | LSP |
|---|---|---|
| 进程 | 编辑器内 | 独立进程 |
| 复杂度 | 低 | 高 |
| 性能 | 受限 | 可扩展 |
| 复用 | 无 | 多编辑器 |
常见问题
| 问题 | 处理 |
|---|---|
| Server 不启动 | 检查 module 路径 |
| 通信失败 | 确认 transport 一致 |
| 特性不生效 | 检查 capabilities 声明 |
| 崩溃重启 | Client 自动重启配置 |
LSP 是语言支持的「工业级方案」,理解架构后,下一章开始搭建实际的 Client 与 Server。