Language Server Protocol 3.18 PublishDiagnostics 通知:服务端诊断推送与客户端能力协商全解析

发布时间:2026/10/7 13:30:10
Language Server Protocol 3.18 PublishDiagnostics 通知:服务端诊断推送与客户端能力协商全解析 开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载导读textDocument/publishDiagnostics是 LSPLanguage Server Protocol中由**语言服务器Server单向推送给客户端Client**的核心通知用于把校验validation结果——即诊断信息Diagnostic如编译错误、警告、提示实时呈现在编辑器界面中。本文以 3.18 版 publishDiagnostics 规范文档 为骨架结合 3.18 元模型 与相关类型定义系统讲解诊断的“所有权”与清理规则、完整替换语义、PublishDiagnosticsClientCapabilities能力协商、PublishDiagnosticsParams参数结构以及它与 codeAction、pull diagnostics 之间的协作关系。读完本文你将能够依据规范正确实现服务端诊断推送并理解客户端如何在初始化阶段声明自身能力。一、通知定位服务端到客户端的单向消息在 LSP 消息体系中textDocument/publishDiagnostics被定义为一个serverToClient 方向的通知Notification。在 3.18 元模型 metaModel.json 中该消息的登记条目如下{ method: textDocument/publishDiagnostics, typeName: PublishDiagnosticsNotification, messageDirection: serverToClient, clientCapability: textDocument.publishDiagnostics, params: { kind: reference, name: PublishDiagnosticsParams }, documentation: Diagnostics notification are sent from the server to the client to signal\nresults of validation runs. }三个关键事实由此确认消息方向serverToClient即服务端主动推送客户端只负责接收与渲染方法名textDocument/publishDiagnostics关联能力项clientCapability指向textDocument.publishDiagnostics说明客户端是否支持/启用该通知是在initialize阶段通过能力协商声明的。需要留意发送时机约束在 initialize 请求规范 中规定服务器在未以InitializeResult响应initialize请求之前不允许向客户端发送任何请求或通知window/showMessage、window/logMessage、telemetry/event与window/showMessageRequest除外。因此textDocument/publishDiagnostics通知必须在服务器完成 initialize 握手、客户端声明了textDocument.publishDiagnostics能力之后才能发送。二、诊断的所有权谁负责清理规范文档开篇即强调一个核心原则Diagnostics are owned by the server, so it is the servers responsibility to clear them if necessary.即诊断由服务端“拥有”因此是否清理、何时清理完全由服务端负责。文档以 VS Code 生态中生成诊断的服务器为例给出了两类典型的清理规则单文件语言single file only例如 HTML。当文件被关闭close时服务端清理该文件的诊断。带项目系统的语言project system例如 C#。文件关闭时不会清理诊断当整个项目被打开时所有文件的诊断被重新计算或从缓存读取。文档同时提醒了一个容易误解的细节open / close 事件并不一定反映用户在界面上看到的内容——这些事件本质上是“所有权ownership事件”。因此按当前规范版本可能出现“文件已不在界面中显示但问题仍未清除”的情况因为客户端尚未真正关闭该文件。这要求服务端实现者在设计清理逻辑时必须以所有权事件为准而不是以 UI 可见性为准。三、重新计算与完整替换语义规范明确规定了服务端的更新职责当文件发生变化时服务端有责任重新计算诊断并推送给客户端如果计算出的诊断集合为空服务端必须推送空数组以清除此前的诊断新推送的诊断总是完整替换之前推送的诊断客户端侧不存在任何合并merging行为。这是一条非常关键的实现约束客户端不会把你上一次推送的部分诊断与本次推送的诊断做交集或并集界面上的诊断状态就是“最后一次推送的内容”。因此服务端不能只在“新增了错误”时推送还必须能够在错误修复后推送空数组或缩减后的列表否则陈旧诊断会一直停留在界面上。四、通知与参数定义PublishDiagnosticsParamsNotificationmethodtextDocument/publishDiagnosticsparamsPublishDiagnosticsParamsPublishDiagnosticsParams的 3.18 版 TypeScript 定义如下出自 publishDiagnostics.mdinterface PublishDiagnosticsParams { /** * The URI for which diagnostic information is reported. */ uri: DocumentUri; /** * Optionally, the version number of the document the diagnostics are * published for. * * since 3.15.0 */ version?: integer; /** * An array of diagnostic information items. */ diagnostics: Diagnostic[]; }参数逐项说明字段类型必填含义uriDocumentUri是本次诊断信息所对应的文档 URIversioninteger否3.15.0 起诊断所对应文档的版本号客户端是否解读该字段取决于其能力versionSupportdiagnosticsDiagnostic[]是诊断信息条目数组为空数组即表示清除该 URI 下此前的全部诊断五、客户端能力协商PublishDiagnosticsClientCapabilitiesClient Capability属性名可选textDocument.publishDiagnostics属性类型PublishDiagnosticsClientCapabilities该能力由客户端在initialize请求的capabilities字段中声明。3.18 版完整定义如下export interface PublishDiagnosticsClientCapabilities { /** * Whether the clients accepts diagnostics with related information. */ relatedInformation?: boolean; /** * Client supports the tag property to provide meta data about a diagnostic. * Clients supporting tags have to handle unknown tags gracefully. * * since 3.15.0 */ tagSupport?: ClientDiagnosticsTagOptions; /** * Whether the client interprets the version property of the * textDocument/publishDiagnostics notifications parameter. * * since 3.15.0 */ versionSupport?: boolean; /** * Client supports a codeDescription property. * * since 3.16.0 */ codeDescriptionSupport?: boolean; /** * Whether code action supports the data property which is * preserved between a textDocument/publishDiagnostics and * textDocument/codeAction request. * * since 3.16.0 */ dataSupport?: boolean; }其中ClientDiagnosticsTagOptions定义为export type ClientDiagnosticsTagOptions { /** * The tags supported by the client. */ valueSet: DiagnosticTag[]; };各能力字段的语义与实现注意点relatedInformation客户端是否接受带有“关联信息”relatedInformation的诊断。若未声明为true服务端应避免发送含关联信息的诊断以免客户端无法呈现。tagSupport客户端对诊断标签DiagnosticTag的支持情况通过valueSet列出支持的标签集合。规范特别强调支持标签的客户端必须优雅处理未知标签——即使收到valueSet之外的标签值也不得崩溃或报错。versionSupport客户端是否会解读PublishDiagnosticsParams.version字段。若为false服务端可省略该字段。codeDescriptionSupport客户端是否支持诊断的codeDescription属性3.16.0 起。dataSupport客户端是否支持Diagnostic.data属性该字段会在textDocument/publishDiagnostics通知与后续的textDocument/codeAction请求之间原样保留3.16.0 起。六、Diagnostic 数据结构诊断内容从哪来通知中的diagnostics数组元素类型为Diagnostic其完整定义位于 3.18 types/diagnostic.md。3.18 版相比此前版本最值得注意的新特性是诊断消息支持 MarkupContent但该能力受客户端能力textDocument.diagnostic.markupMessageSupport约束——如果客户端未声明该能力服务端不应发送MarkupContent形式的诊断消息。Diagnostic接口要点3.18 版export interface Diagnostic { range: Range; severity?: DiagnosticSeverity; code?: integer | string; codeDescription?: CodeDescription; // since 3.16.0 source?: string; message: string | MarkupContent; // since 3.18.0 支持 MarkupContent tags?: DiagnosticTag[]; // since 3.15.0 relatedInformation?: DiagnosticRelatedInformation[]; data?: LSPAny; // since 3.16.0 }range诊断消息作用的代码范围必填severity严重级别。为避免同一服务器在不同客户端下产生解读差异强烈建议服务端总是提供 severity 值若省略建议客户端将其按 Error错误严重级别解读code诊断代码可为整数或字符串通常展示在用户界面codeDescription可选用于描述错误代码href为提供更多信息的 URI3.16.0 起source诊断来源的人类可读描述例如typescript或super lintmessage诊断消息3.18.0 起支持MarkupContent受能力门控tags诊断元数据标签3.15.0 起relatedInformation相关诊断信息数组例如作用域内符号名冲突时可用该属性标记所有冲突定义data在发布诊断通知与 codeAction 请求之间保留的数据字段3.16.0 起。严重级别与标签枚举协议目前支持以下诊断严重级别与标签export namespace DiagnosticSeverity { export const Error: 1 1; // 报告错误 export const Warning: 2 2; // 报告警告 export const Information: 3 3; // 报告信息 export const Hint: 4 4; // 报告提示 } export type DiagnosticSeverity 1 | 2 | 3 | 4; export namespace DiagnosticTag { export const Unnecessary: 1 1; // 无用或不必要的代码客户端可淡显渲染 export const Deprecated: 2 2; // 弃用或过时代码客户端可加删除线渲染 } export type DiagnosticTag 1 | 2;标签的意义在于让客户端可以“美化”呈现Unnecessary无用代码允许客户端以淡出faded out代替错误波浪线Deprecated弃用代码允许客户端以删除线渲染。这两个标签均自 3.15.0 引入。关联信息与代码描述export interface DiagnosticRelatedInformation { location: Location; // 相关诊断信息的位置 message: string; // 相关诊断信息的消息 } export interface CodeDescription { // since 3.16.0 href: URI; // 打开以查看更多诊断错误信息的 URI }DiagnosticRelatedInformation用于“指出导致或与当前诊断相关的代码位置”例如作用域内重复声明符号时把冲突的所有声明位置都列出来。CodeDescription.href则让诊断错误码可以链接到外部说明页面。七、与 Pull Diagnostics 的关系两种模式并存值得注意的是从 3.17.0 起 LSP 引入了另一种诊断获取方式——Pull Diagnostics诊断拉取定义于 3.18 language/pullDiagnostics.md。两种模式的关系如下Push本文主题服务端按自己选择的时间点计算并推送诊断。优点是适合“工作区级诊断”workspace wide diagnostics服务端拥有计算时机上的自由度缺点是服务端无法为“用户正在输入或正在编辑器中可见的文件”做优先级排序——因为仅靠didOpen/didChange通知推断客户端 UI 状态会产生误判这些通知本质上是所有权转移通知。Pull3.17.0 起由客户端发起textDocument/diagnostic请求客户端对“为哪些文档、在哪个时间点计算诊断”拥有更强的控制权。Pull 模式下的客户端能力DiagnosticClientCapabilities同样包含relatedInformation、tagSupport、codeDescriptionSupport、dataSupport等字段并在 3.18.0 新增了markupMessageSupport布尔能力用于声明客户端是否支持诊断消息中的MarkupContent——这正是前述 Diagnostic 消息类型扩展的能力门控来源。服务端实现者应明确两种模式是并存的协议特性具体启用哪一种取决于客户端能力与服务端能力声明而非互相替代。八、与 Code Action 的联动data 与消息门控诊断推送并非孤立功能它直接服务于“快速修复”场景。在 3.18 language/codeAction.md 的CodeActionContext中客户端会把与当前范围重叠的、已知的客户端侧诊断随textDocument/codeAction请求一并带给服务端export interface CodeActionContext { /** * An array of diagnostics known on the client side overlapping the range * provided to the textDocument/codeAction request. ... */ diagnostics: Diagnostic[]; ... }两个联动要点值得服务端与客户端实现者同时注意data字段的跨消息保留PublishDiagnosticsClientCapabilities.dataSupport与Diagnostic.data字段的设计目的是让同一份诊断数据在“推送诊断 → 请求 code action”的流程中无损传递。服务端可以在发布诊断时携带自定义data如内部错误码、快照信息后续收到带这些诊断的 codeAction 请求时直接复用无需重新计算。Markup 消息的门控检查规范在 codeAction 文档中特别提示——客户端在向服务端发送含 markup 消息的诊断之前应检查服务端是否支持textDocument.diagnostic.markupMessageSupport能力对不支持的服务端应剔除exclude含 markup 消息的诊断。这保证了即使 3.18 扩展了消息类型旧服务端与新客户端之间依然能够平滑互操作。九、实现要点速查综合规范文档与源码定义为服务端实现者整理一份可直接落地的检查清单初始化阶段在initialize请求的响应中完成握手并在之后才发送textDocument/publishDiagnostics客户端则在capabilities.textDocument.publishDiagnostics中声明自身支持的能力字段。推送时机文件内容发生变化如收到textDocument/didChange后重新校验并推送对单文件语言在didClose时清理。空集合处理校验结果为空时推送diagnostics: []以清除历史诊断永远依赖“整体替换”语义不要假设客户端会做增量合并。能力感知根据客户端的relatedInformation、tagSupport、codeDescriptionSupport、dataSupport、versionSupport声明裁剪诊断字段在 Pull 模式下还需尊重markupMessageSupport对MarkupContent消息的限制。severity 必填规范强烈建议始终提供severity避免不同客户端对缺失严重级别的解读不一致。数据模型一致性推送通知与 codeAction 请求共用Diagnostic类型与data字段语义两者之间的字段约定应在服务端内部保持一致。十、相关文档导航PublishDiagnostics 通知3.18 版本文主体规范Diagnostic 类型定义3.18 版Diagnostic、DiagnosticSeverity、DiagnosticTag、DiagnosticRelatedInformation、CodeDescriptionPull Diagnostics3.18 版与推送模式对比、markupMessageSupport能力CodeAction3.18 版CodeActionContext 与诊断联动3.18 元模型PublishDiagnosticsNotification 的机器可读定义Initialize 请求3.18 版能力协商与消息发送时机约束历史版本对照3.17 版 publishDiagnostics 与 3.19 版 publishDiagnostics 中的定义与 3.18 版保持一致赞分享开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载相关推荐Language Server Protocol 3.18 window/logMessage 通知详解从服务器向客户端传递日志消息Language Server Protocol 3.18 window/logMessage 通知详解从服务器向客户端传递日志消息 window/logMe开发工具从新手到上手newbie-guide-cj中RectF指定高亮区域与4种形状参数详解从新手到上手newbie guide cj中RectF指定高亮区域与4种形状参数详解 newbie guide cj 是一款面向 Cangjie UI 的高亮开发工具Language Server Protocol 3.17 补全Completion请求完全指南从客户端能力协商到 Snippet 语法Language Server Protocol 3.17 补全Completion请求完全指南从客户端能力协商到 Snippet 语法 导读 本文以 L开发工具创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考