- 开发工具
【免费下载链接】language-server-protocol
Defines a common protocol for language servers.
导读
window/workDoneProgress/create是 Language Server Protocol(LSP)中由**服务器主动向客户端发起工作进度(Work Done Progress)**的核心请求,解决了服务器在请求上下文之外执行耗时任务(如重新索引数据库、批量编译、依赖解析)时无法向用户呈现进度的问题。本文基于本仓库_specifications/lsp/3.17目录下的规范文档,结合 协议元模型 中对该请求的机器可读定义,完整讲解该请求的协议形态、token 生命周期、与$/progress通知的配合方式、取消机制及客户端能力协商,帮助你正确实现服务器端的进度上报。
一、背景:两种工作进度发起方式
在 LSP 3.15 及之后版本中,进度上报通过通用的$/progress通知完成,其值负载(value payload)有三种形态:WorkDoneProgressBegin、WorkDoneProgressReport和WorkDoneProgressEnd,对应进度的"开始—更新—结束"三个阶段(详见 类型定义)。
按发起方不同,Work Done Progress 分为两类:
| 发起方式 | 触发途径 | 典型场景 |
|---|---|---|
| 客户端发起(client initiated) | 客户端在请求参数中加入workDoneToken属性 | 客户端发起的textDocument/reference等请求上附带进度 token |
| 服务器发起(server initiated) | 服务器发送window/workDoneProgress/create请求 | 服务器需要在某个请求之外自行上报进度,如后台重新索引数据库 |
本篇文章聚焦第二种方式——服务器发起的进度。
二、协议定义:方法、参数与响应
请求方向与方法名
window/workDoneProgress/create是一个从服务器发往客户端(server-to-client)的请求,用于请求客户端创建一个工作进度实例。这一方向性在协议元模型中有明确记录:在 metaModel.json 中,该请求的messageDirection字段为serverToClient,result类型为null,文档注释为:
The
window/workDoneProgress/createrequest is sent from the server to the client to initiate progress reporting from the server.
请求参数:WorkDoneProgressCreateParams
请求参数类型定义如下:
export interface WorkDoneProgressCreateParams { /** * The token to be used to report progress. */ token: ProgressToken; }其中ProgressToken是integer | string的联合类型(见 specification.md 与 metaModel.json)。服务器在发起 create 请求时需自行生成一个唯一 token(实践中常用 UUID 字符串),该 token 将作为后续所有$/progress通知中标识此进度实例的键。
响应与错误处理
- 成功响应:
result为void(即无返回值,客户端确认已创建进度)。 - 错误响应:若请求处理过程中发生异常,客户端返回
error.code与error.message。
规范对错误情形有一个关键约束:如果 create 请求出错,服务器绝不能使用该 token 发送任何进度通知。这保证了错误发生后客户端不会收到与已失败进度关联的幽灵更新,是保证进度 UI 一致性的底线规则。
三、服务器发起进度的完整生命周期
根据 types/workDoneProgress.md 中的"Server Initiated Progress"一节,服务器发起的进度遵循以下完整流程:
1. 创建进度(create)
服务器在需要上报进度时(例如准备开始重索引),先向客户端发送:
{ "jsonrpc": "2.0", "id": 10, "method": "window/workDoneProgress/create", "params": { "token": "2f8a4c12-9d3b-4e76-9c1a-7b0f3a56e2d1" } }2. 发送 begin 通知
创建成功后,服务器通过$/progress通知发送WorkDoneProgressBegin负载,title为必填项,用于简短说明正在执行的操作类型:
{ "jsonrpc": "2.0", "method": "$/progress", "params": { "token": "2f8a4c12-9d3b-4e76-9c1a-7b0f3a56e2d1", "value": { "kind": "begin", "title": "Indexing workspace", "cancellable": true, "message": "Scanning project/src", "percentage": 0 } } }WorkDoneProgressBegin的字段语义(类型定义):
| 字段 | 类型 | 必填 | 说明 |
|---|---|---|---|
kind | 'begin' | 是 | 负载形态标记 |
title | string | 是 | 进度的标题,如"Indexing"或"Linking dependencies" |
cancellable | boolean | 否 | 是否显示取消按钮;不支持取消的客户端可忽略 |
message | string | 否 | 更详细的进度消息,如"3/25 files";未设置时沿用上一次消息 |
percentage | uinteger | 否 | 进度百分比,100视为 100%;不提供则视为无限进度,取值范围[0, 100],应保持单调递增 |
3. 周期性发送 report 通知
任务执行过程中,服务器发送WorkDoneProgressReport更新进度:
{ "jsonrpc": "2.0", "method": "$/progress", "params": { "token": "2f8a4c12-9d3b-4e76-9c1a-7b0f3a56e2d1", "value": { "kind": "report", "message": "12/50 files", "percentage": 24 } } }WorkDoneProgressReport支持cancellable、message、percentage三个可选字段;其中cancellable仅在 begin 中请求了取消按钮时有效。
4. 发送 end 通知收尾
任务完成或失败时发送WorkDoneProgressEnd:
{ "jsonrpc": "2.0", "method": "$/progress", "params": { "token": "2f8a4c12-9d3b-4e76-9c1a-7b0f3a56e2d1", "value": { "kind": "end", "message": "Indexing finished" } } }WorkDoneProgressEnd仅含可选的message字段,可用于说明操作结果。
token 的使用约束
规范明确要求:create 请求中提供的 token 只能使用一次——即对该 token 应恰好发送一个begin、任意多个report和一个end通知。这与客户端发起的进度形成对比:客户端通过请求参数中的workDoneToken传入的 token,其有效期只持续到该请求返回响应为止。
四、取消机制:window/workDoneProgress/cancel
服务器发起的进度同样支持取消。客户端通过window/workDoneProgress/cancel通知(client-to-server 方向)取消进度,参数类型为:
export interface WorkDoneProgressCancelParams { /** * The token to be used to report progress. */ token: ProgressToken; }协议要点(见 workDoneProgressCancel.md):
- 取消的进度无需在 begin 中标记为
cancellable——也就是说,即使服务器未提供取消按钮,客户端仍然可以主动取消进度; - 客户端可能因多种原因取消进度:发生错误、重载工作区等;
- 服务器收到该通知后,应终止对应任务并发送
end通知收尾(或依据自身实现决定处理方式)。
此外,对于客户端发起的进度,取消则直接通过取消对应请求(如$/cancelRequest)完成,无需单独的 cancel 通知。
五、客户端能力协商与向后兼容
为保持协议向后兼容,服务器只有在客户端通过能力声明明确支持时,才能使用window/workDoneProgress/create请求。客户端在 initialize 握手阶段返回的ClientCapabilities中声明:
window?: { /** * Whether client supports server initiated progress using the * `window/workDoneProgress/create` request. */ workDoneProgress?: boolean; };对应客户端能力属性为window.workDoneProgress,类型为boolean,可选。服务器在发起 create 请求前必须检查该能力位;若客户端未声明支持,服务器应退回到客户端发起的方式,或在请求参数中附带的workDoneToken上上报进度,甚至放弃进度展示。
与之相对,客户端发起方式有一个特别之处:不存在专门的客户端能力位来声明"是否会在每个请求上发送进度 token"。因为这在很多客户端中并非静态属性,甚至同一请求类型的不同请求实例都可能不同,所以客户端能力通过每个请求参数中是否出现workDoneToken属性来按实例动态表达(见 types/workDoneProgress.md 中 "Client Initiated Progress" 一节)。
同时,为避免客户端在发送请求前建立进度 UI 而服务器实际不报进度,服务器需要在对应功能的 server capability 中声明workDoneProgress支持,例如:
{ "referencesProvider": { "workDoneProgress": true } }六、从元模型看协议定义的一致性
本仓库在 metaModel 目录 中提供了 LSP 3.17 的机器可读元模型(metaModel.json、metaModel.schema.json与对应的 TypeScript 模型metaModel.ts),可用于校验与代码生成。其中与本文主题相关的定义包括:
- window/workDoneProgress/create 请求定义:
messageDirection: "serverToClient"、result: null、params: WorkDoneProgressCreateParams; - WorkDoneProgressCreateParams 结构:仅含
token: ProgressToken一个属性; - WorkDoneProgressCancelParams 结构:同样仅含
token: ProgressToken; - ProgressToken 类型:
integer | string。
元模型中的这些定义与各 Markdown 规范文档完全一致,说明该请求在协议中作为一等公民被完整建模。如果你在实现语言服务器 SDK 时使用元模型驱动代码生成,window/workDoneProgress/create会自然生成对应的请求类型、参数类型与文档注释。
七、实现建议小结
- 能力先行:发送 create 请求前,务必检查客户端能力
window.workDoneProgress是否为true。 - token 唯一且单次使用:每个进度实例使用独立的
ProgressToken,遵守"一个 begin、多个 report、一个 end"的规则。 - 正确处理 create 失败:create 请求报错后,该 token 立即作废,不得再发送任何
$/progress通知。 - 响应取消:监听
window/workDoneProgress/cancel通知,收到后尽快终止任务并发送end负载。 - 善用元模型:以 metaModel.json 为单一事实来源生成类型定义,避免手写结构与规范漂移。
通过以上机制,语言服务器可以在索引、编译、依赖分析等请求外的长耗时操作中,向用户提供可取消、可感知的进度反馈,显著改善编辑器的交互体验——这正是 LSP 3.15 引入工作进度机制、并让服务器侧发起进度的设计初衷。
- 开发工具
【免费下载链接】language-server-protocol
Defines a common protocol for language servers.
相关推荐
KOReader 上手教程:免费在 Kindle 和 Kobo 上读 20 多种电子书格式
KOReader 上手教程:免费在 Kindle 和 Kobo 上读 20 多种电子书格式 KOReader 是一款免费开源的电子书阅读器,主要解决设备自带阅读
开发工具TeleChat2.5-35B的vLLM服务化部署实战教程:10个步骤快速搭建AI推理服务
TeleChat2.5 35B的vLLM服务化部署实战教程:10个步骤快速搭建AI推理服务 TeleChat2.5 35B是中国电信人工智能研究院研发的35B参
如何快速搭建高效Node.js服务器:example-node-server完整指南
如何快速搭建高效Node.js服务器:example node server完整指南 example node server 是一个基于Babel的轻量级Nod
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考