☰
Language Server Protocol 3.19 Partial Results(部分结果流式返回)机制完全指南
2026/10/7 1:48:05 网站建设 项目流程
  • 开发工具

【免费下载链接】language-server-protocol

Defines a common protocol for language servers.

项目地址:https://gitcode.com/gh_mirrors/la/language-server-protocol
点击查看免费下载

导读

本文基于本仓库_specifications/lsp/3.19/types/partialResults.md规范文档,系统讲解 LSP 3.15 起引入的 Partial Result Progress(部分结果进度)机制:服务器如何在客户端允许的前提下,通过通用的$/progress通知将workspace/symbol、textDocument/reference等请求的超大结果集分批增量返回,以及错误发生时客户端应如何正确处理已收到的部分结果。读完本文,你将掌握partialResultToken的传递方式、$/progress通知的载荷结构、结果追加与最终响应置空的协议约定,并能据此在语言服务器与客户端中实现可流式返回的结果通道。

一、Partial Result 机制的由来与定位

1.1 版本与协议地位

Partial Result Progress 是 Language Server Protocol 自3.15.0版本起正式引入的能力。它在协议中归属于请求参数层面的通用基础设施(与 Work Done Progress 并列),由以下规范文档共同定义:

  • Partial Result Progress 说明:本文核心,定义机制与错误处理规则;
  • PartialResultParams 类型定义:承载partialResultToken的参数字面量接口;
  • Work Done Progress 类型定义:同源于$/progress通知的兄弟机制。

注意:虽然本节仓库同时维护了 3.17、3.18、3.19 三个版本目录,但 partialResults.md 在 3.18 与 3.19 中的内容完全一致,均标注 Since 3.15.0。Partial Result 语义自 3.15 定型后保持稳定,本文以 3.19 版本为准展开。

1.2 为什么需要 Partial Result

在未引入该机制之前,像workspace/symbol(工作区符号检索)、textDocument/reference(查找引用)这类请求,服务器必须等全部结果收集完毕后再一次性返回完整数组。当结果集规模巨大(例如全仓引用、全局符号索引)时:

  • 客户端 UI 长时间空白无反馈;
  • 网络传输单个超大 JSON 响应,首屏渲染延迟高;
  • 服务器内存被一次性的大结果集占用。

Partial Result 解决的是"结果本身"的分批投递问题:服务器边收集边推送,客户端边接收边展示,配合 Work Done Progress 的进度提示,形成完整的"流式结果 + 进度反馈"体验。

二、核心机制:$/progress通知 +partialResultToken

2.1 通用进度通知$/progress

Partial Result 与 Work Done Progress 共享同一个通用的$/progress通知通道。从本仓库的 3.19 metaModel.json 可见其在元模型中的定义:

{ "method": "$/progress", "typeName": "ProgressNotification", "messageDirection": "both", "params": { "kind": "reference", "name": "ProgressParams" } }

要点:$/progress的messageDirection为both,即客户端与服务器双向都可发送;其参数结构ProgressParams形如:

{ "token": "<ProgressToken>", "value": { ... } }

其中token用于把通知关联到某一次具体的请求,value则根据通知类型携带不同的载荷(Work Done 的 begin/report/end 或 Partial Result 的结果数组)。

2.2 什么请求支持 Partial Result

并非所有请求都支持部分结果。从 3.19 metaModel.json 中可以看到,大量请求结构的mixins同时注入了WorkDoneProgressParams与PartialResultParams,例如ImplementationParams(metaModel.json 第 2413-2430 行):

{ "name": "ImplementationParams", "extends": [{ "kind": "reference", "name": "TextDocumentPositionParams" }], "mixins": [ { "kind": "reference", "name": "WorkDoneProgressParams" }, { "kind": "reference", "name": "PartialResultParams" } ] }

同理,textDocument/reference、workspace/symbol、textDocument/documentSymbol、workspace/executeCommand等请求在元模型中均以PartialResultParams作为 mixin(全文检索可见PartialResultParams在 metaModel.json 中出现 26 处,覆盖了语言特性类与工作区类的主要查询请求)。

PartialResultParams本身的完整定义见 partialResultParams.md:

export interface PartialResultParams { /** * An optional token that a server can use to report partial results (e.g. * streaming) to the client. */ partialResultToken?: ProgressToken; }

ProgressToken类型(metaModel.json 第 15863 行)为integer | string的联合类型,即令牌既可以是整数编号,也可以是字符串 UUID。

2.3 一次完整的 Partial Result 交互

规范文档给出了一个同时支持 Work Done 与 Partial Result 的textDocument/reference请求示例:

{ "textDocument": { "uri": "file:///folder/file.ts" }, "position": { "line": 9, "character": 5 }, "context": { "includeDeclaration": true }, // The token used to report work done progress. "workDoneToken": "1d546990-40a3-4b77-b134-46622995f6ae", // The token used to report partial result progress. "partialResultToken": "5f6f349e-4f81-4a3b-afff-ee04bff96804" }

字段解读:

字段含义
textDocument.uri目标文档的file://URI
position.line/position.character引用符号所在位置(0 基)
context.includeDeclaration是否包含声明位置本身
workDoneToken供服务器发送进度 begin/report/end 通知的令牌
partialResultToken供服务器发送部分结果通知的令牌

服务器收到请求后,用partialResultToken作为$/progress通知的token字段,逐批推送结果。例如查找引用返回前两批结果时:

{ "token": "5f6f349e-4f81-4a3b-afff-ee04bff96804", "value": [ { "uri": "file:///folder/file.ts", "range": { "start": { "line": 12, "character": 8 }, "end": { "line": 12, "character": 16 } } } ] }

(后续批次继续以同一 token 追加发送。)

三、服务器端的协议约定(发送方规则)

规范对服务器发送 Partial Result 提出了两条刚性约定,这是实现时最容易出错、也最需要严格遵守的部分:

3.1 全部结果必须走$/progress,最终响应结果必须为空

If a server reports partial result via a corresponding$/progress, the whole result must be reported using$/progressnotifications, each of which appends items to the result. The final response has to be empty in terms of result values.

即:一旦服务器决定用$/progress上报部分结果,就必须把所有结果都通过$/progress通知发出,每一条通知向结果集追加一批条目;最终请求响应中的结果值必须为空。

例如textDocument/reference的正常响应类型为Location[],走 Partial Result 时最终响应应为:

{ "jsonrpc": "2.0", "id": 1, "result": [] }

(即空数组 / null 结果值。)

之所以这样设计,规范给出的理由是"避免混淆最终结果应如何解读"——例如收到完整结果时,客户端无法判断它到底是又一个部分结果、还是一个替换性的最终结果。通过"响应结果为空 + 所有数据走通知"的明确约定,客户端可以无歧义地合并所有$/progress载荷。

3.2 载荷结构与最终结果类型一致

The value payload of a partial result progress notification is in most cases the same as the final result.

部分结果的载荷类型与最终结果类型基本一致。例如workspace/symbol的最终结果是SymbolInformation[] | WorkspaceSymbol[],那么每条$/progress的value也是SymbolInformation[] | WorkspaceSymbol[]——即每条通知携带一段同类型的数组切片,而不是单个条目。

3.3 一个 token 的一次性使用

与 Work Done Progress 的服务器主动进度(window/workDoneProgress/create创建的 token 仅能使用一次)不同,Partial Result 的 token 由客户端随请求下发,其生命周期绑定本次请求:token 仅在"请求发出后、响应返回前"这段时间内有效。服务器应保证在响应返回前完成所有部分结果通知的发送。

四、客户端处理规则(接收方与错误语义)

4.1 正常路径:增量合并

客户端收到以partialResultToken为 token 的$/progress通知后,应将通知value中的条目追加到该请求的结果集合中。多个$/progress通知之间是有序追加关系(append),客户端按收到顺序合并即可,无需去重或排序。

4.2 错误路径:按错误码区分处理

规范明确规定,若请求最终响应报错,已收到的部分结果按以下规则处理:

响应错误code客户端处理方式
RequestCancelled(即-32800,请求被取消)可以使用已收到的结果,但必须向用户明确该请求已被取消、结果可能不完整
其他所有错误码丢弃已收到的部分结果,不得使用

用伪代码表述客户端逻辑:

onRequestError(error, collectedPartialResults) { if (error.code === RequestCancelled) { // 可用,但需标注"请求已取消,结果可能不完整" ui.showResults(collectedPartialResults, { incomplete: true }); } else { // 其他错误:丢弃 discard(collectedPartialResults); } }

4.3 客户端能力信号:按请求实例逐次声明

值得注意:协议中没有"客户端是否支持 Partial Result"的静态能力声明(如clientCapabilities中的固定开关)。原因正如 Work Done Progress 章节所解释的——对多数客户端而言这不是静态属性,同一个请求类型在不同实例上可能不同。因此客户端是否接受部分结果,通过每个请求参数中是否存在partialResultToken字段来逐次声明。服务器不应假设客户端总是支持,也不应要求客户端预先声明全局能力。

(服务器侧对应的"支持情况"通过具体请求的能力项声明,例如referencesProvider中可带workDoneProgress选项,但 Partial Result 本身同样不设独立的静态 capability,全部依赖请求实例上的 token 存在性。)

五、从元模型看 Partial Result 在 3.19 的覆盖范围

为了验证哪些请求真正接入了 Partial Result,可在 3.19 metaModel.json 中检索"name": "PartialResultParams"出现的 26 处位置,它们分别出现在以下请求/注册结构的mixins中(节选):

  • ImplementationParams(第 2413-2430 行)
  • TypeDefinitionParams、DeclarationParams、ReferencesParams(同构结构)
  • DocumentSymbolParams、WorkspaceSymbolParams、CodeLensParams、DocumentLinkParams、DocumentColorParams、FoldingRangeParams、SelectionRangeParams、InlayHintParams、CallHierarchy*Params、MonikerParams等

据此可以总结出三类典型场景:

  1. 引用/定义类查询:textDocument/reference、textDocument/declaration、textDocument/typeDefinition、textDocument/implementation——结果是一批Location,天然适合流式返回;
  2. 文档/工作区符号:textDocument/documentSymbol、workspace/symbol——全仓或大文件符号集合可能很大;
  3. 文档内散点数据:textDocument/codeLens、textDocument/documentLink、textDocument/foldingRange、textDocument/documentColor、textDocument/inlayHint等——文档级请求虽然结果相对可控,但协议同样允许流式返回以降低单次响应体积。

以上请求名单为从元模型 mixins 中可推断的实现事实;具体到某个语言服务器是否真的对某个请求启用 Partial Result,取决于该服务器实现,客户端仍需按"token 存在即支持"的规则适配。

六、实现要点与最佳实践

综合规范与元模型证据,落地 Partial Result 时建议遵循以下要点:

  1. 客户端侧:仅在确实需要流式展示(如引用面板渐进填充)时,才在请求参数中附加partialResultToken;收到$/progress通知时按 token 路由到对应请求,并持续追加合并;收到最终响应后,若result为空数组而此前已有部分结果,则直接用合并结果,若响应报错则按第 4.2 节规则处理。
  2. 服务器侧:当请求携带partialResultToken时,不要等全部结果收集完再一次性返回;而是分批构造$/progress通知(token = partialResultToken,value = 结果切片),全部发完后返回空结果;最终响应结果置空是强制性约定,切不可"通知发一半、响应再带部分数据",否则客户端将无法区分追加与替换语义。
  3. 取消语义:若请求被取消,响应应带RequestCancelled错误码,此时客户端可保留已收到的部分结果并标注不完整;其他错误则必须丢弃部分结果,避免把错误场景的残缺数据当作有效结果展示。
  4. 与 Work Done Progress 协同:两者共享$/progress通道但语义不同(进度 vs 数据)。实现时可同时下发workDoneToken与partialResultToken,进度与数据并行推进,但务必区分通知载荷类型,避免混淆。

七、小结

Partial Result Progress 是 LSP 3.15 引入、在 3.19 中保持稳定的通用流式结果机制:客户端通过请求参数中的partialResultToken逐实例声明支持,服务器通过$/progress通知以"追加切片"的方式推送与最终结果同类型的数据,最终响应结果置空;错误时按RequestCancelled与其他错误码分别决定"可保留但标注不完整"或"整体丢弃"。

该机制与 Work Done Progress 共同构成了 LSP 面向长耗时、大结果量请求的完整反馈方案,是workspace/symbol、textDocument/reference等高成本查询在现代语言服务器与编辑器集成中实现"边算边出"体验的基础设施。

  • 开发工具

【免费下载链接】language-server-protocol

Defines a common protocol for language servers.

项目地址:https://gitcode.com/gh_mirrors/la/language-server-protocol
点击查看免费下载

相关推荐

上一篇:探索 psutils:Windows 上的实用 PowerShell 命令工具集
下一篇:Compositional Numeric Library核心特性解析:从fixed-precision到scaled integers

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询