LSP 3.19 `textDocument/willSave` 保存前通知详解:协议语义、能力协商与参数结构
开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载导读textDocument/willSave是 Language Server Protocol 中保存前回调机制的核心通知客户端在实际写入磁盘之前向语言服务器广播一条clientToServer方向的单向通知让服务器有机会感知即将发生一次保存这一事件。本文基于当前仓库中 3.19 版规范文档完整讲解该通知的触发时机、客户端与服务端的双向能力声明、WillSaveTextDocumentParams参数结构与TextDocumentSaveReason保存原因枚举并结合仓库中的 metaModel 结构化定义与配套的willSaveWaitUntil、didSave文档厘清保存前通知 → 保存前编辑请求 → 保存后通知的完整事件链。读完本文你将能够在自己的语言服务器与客户端中正确声明并处理 willSave 能力并根据保存原因手动、延时、失焦实现差异化的保存前逻辑。通知语义在真实保存发生之前触发根据 willSave.md 的定义textDocument/willSave是一条notification通知由客户端编辑器/工具在文档尚未真正保存时发送给服务器。它属于clientToServer方向的消息方法名为textDocument/willSave。该通知带有两个重要的隐含约束文档必须处于打开状态如果服务器已注册 open / close 事件即声明了textDocument/didOpen、textDocument/didClose同步能力那么客户端在发送willSave之前必须确保目标文档已通过didOpen打开。原因是客户端在未获得文件所有权转移ownership transferal的情况下不能改变文件内容——只有处于打开状态且内容由客户端托管的文档才适合触发保存前回调。通知不携带返回值作为 notificationwillSave只能通知不能向客户端返回修改结果。如果服务器需要在保存前修改文档内容应使用配套的请求类型textDocument/willSaveWaitUntil详见后文保存事件链一节。从协议方向上看willSave通知的触发时机严格介于用户/编辑器发起保存与磁盘写入完成之间因此它适合执行无副作用或只读性的收尾动作例如更新服务器内部的内存状态索引、符号表、诊断缓存记录该文档将于 reason 原因被保存的日志触发与保存相关的统计分析。能力协商客户端与服务端的双向声明willSave不是强制的协议特性而是通过**能力协商capability negotiation**在initialize阶段开启的。双方都需要显式声明且任何一方缺失都会导致该特性不生效。客户端能力Client Capability客户端在initialize请求的ClientCapabilities中通过textDocument.synchronization.willSave声明自己支持发送textDocument/willSave通知属性值property name可选textDocument.synchronization.willSaveproperty typeboolean该能力为boolean类型置为true表示客户端在每次保存操作前会向服务器广播willSave通知。它与 3.x 协议的其它同步能力一样在 initialize.md 中定义的TextDocumentSyncClientCapabilities结构textDocument.synchronization字段下声明。服务端能力Server Capability服务器在initialize响应InitializeResult.capabilities中通过textDocumentSync.willSave声明自己感兴趣接收textDocument/willSave通知属性值property name可选textDocumentSync.willSaveproperty typeboolean置为true表示服务器希望接收该通知。注意textDocumentSync字段既可以是一个详细结构TextDocumentSyncOptions也可以为了向后兼容直接使用TextDocumentSyncKind数字willSave布尔开关属于前者——只有当服务器以结构化的TextDocumentSyncOptions声明同步方式时willSave子字段才有意义。若textDocumentSync省略协议默认取TextDocumentSyncKind.None见 initialize.md 中ServerCapabilities的定义。一个完整的协商结果是双向都开启客户端声明textDocument.synchronization.willSave: true服务器声明textDocumentSync.willSave: true。只有这样保存前通知才会真正发送。注册选项TextDocumentRegistrationOptionswillSave通知的动态注册使用TextDocumentRegistrationOptions作为注册选项。该选项用于约束通知适用的文档选择器document selector使服务器可以仅对特定语言/模式的文档开启保存前回调而不是全量接收。它同样被textDocument/willSaveWaitUntil请求使用见 willSaveWaitUntil.md。参数结构WillSaveTextDocumentParamstextDocument/willSave通知的参数类型为WillSaveTextDocumentParams在 willSave.md 中定义如下/** * The parameters send in a will save text document notification. */ export interface WillSaveTextDocumentParams { /** * The document that will be saved. */ textDocument: TextDocumentIdentifier; /** * The TextDocumentSaveReason. */ reason: TextDocumentSaveReason; }该接口包含两个字段textDocument: TextDocumentIdentifier即将被保存的文档标识。TextDocumentIdentifier在 textDocumentIdentifier.md 中定义仅含一个uri: DocumentUri字段——协议层面的 URI 一律以字符串传递interface TextDocumentIdentifier { /** * The text documents URI. */ uri: DocumentUri; }服务器可据此定位到自身内存中对应的文档快照前提是已通过didOpen打开。reason: TextDocumentSaveReason本次保存的触发原因用于让服务器区分保存场景详下一节。一个实际的 JSON-RPC 通知负载示例如下{ jsonrpc: 2.0, method: textDocument/willSave, params: { textDocument: { uri: file:///home/user/project/src/main.py }, reason: 1 } }保存原因枚举TextDocumentSaveReasonTextDocumentSaveReason用于描述为什么文档会被保存帮助服务器按场景差异化处理。它在 willSave.md 中定义为一个常量命名空间与联合类型的组合/** * Represents reasons why a text document is saved. */ export namespace TextDocumentSaveReason { /** * Manually triggered, e.g. by the user pressing save, by starting * debugging, or by an API call. */ export const Manual 1; /** * Automatic after a delay. */ export const AfterDelay 2; /** * When the editor lost focus. */ export const FocusOut 3; } export type TextDocumentSaveReason 1 | 2 | 3;三个取值及其典型场景如下取值常量名触发场景1Manual用户按下保存快捷键、启动调试、或代码显式调用保存 API2AfterDelay编辑器按自动保存策略在延迟后触发保存3FocusOut编辑器失去焦点时触发的保存TextDocumentSaveReason的类型为字面量联合1 | 2 | 3与namespace中的常量一一对应。服务器在处理willSave时可按reason分流例如对Manual可执行完整的收尾逻辑对AfterDelay/FocusOut这类高频自动保存则采用更轻量的处理避免不必要的工作。保存事件链willSave → willSaveWaitUntil → didSavewillSave只是保存三阶段中的第一环。当前仓库的 3.19 目录下三个保存相关文档共同描述了一条完整的事件链willSave.md通知保存前广播服务器只能感知、不能修改。willSaveWaitUntil.md请求同样是保存前发送但这是一个request请求服务器可以在响应中返回一个TextEdit[]数组或null这些编辑会被应用到文档上再执行保存。其方法名为textDocument/willSaveWaitUntil参数同样复用WillSaveTextDocumentParams客户端能力为textDocument.synchronization.willSaveWaitUntil服务端能力为textDocumentSync.willSaveWaitUntil注册选项同为TextDocumentRegistrationOptions。didSave.md通知保存完成之后客户端再发送textDocument/didSave通知参数为DidSaveTextDocumentParams其中text?: string字段是否携带完整内容取决于服务端声明textDocumentSync.save时是否将SaveOptions.includeText置为true。对willSaveWaitUntil的响应TextEdit结构在 textEdit.md 中定义interface TextEdit { /** * The range of the text document to be manipulated. To insert * text into a document, create a range where start end. */ range: Range; /** * The string to be inserted. For delete operations, use an * empty string. */ newText: string; }需要特别注意的是 willSaveWaitUntil.md 中给出的客户端实现约束如果计算编辑耗时过长或服务器在该请求上持续失败客户端可能会丢弃其结果——这是为了保持保存操作快速且可靠。因此服务器不应把关键、不可逆的逻辑寄托于willSaveWaitUntil而应将它视为尽力而为的保存前修正机会。这一约束同样隐含地提醒我们willSave通知以及willSaveWaitUntil请求都必须保持轻量、快速避免阻塞用户保存操作。元模型佐证metaModel 中的结构化定义仓库 metaModel.json3.19 版元模型以机器可读的 JSON 形式对textDocument/willSave通知做了与规范文档一致的结构化描述可作为实现语言绑定如 LSP 客户端/服务器 SDK时的权威依据。其核心记录约位于第 2333-2347 行如下{ method: textDocument/willSave, typeName: WillSaveTextDocumentNotification, messageDirection: clientToServer, clientCapability: textDocument.synchronization.willSave, serverCapability: textDocumentSync.willSave, params: { kind: reference, name: WillSaveTextDocumentParams }, registrationOptions: { kind: reference, name: TextDocumentRegistrationOptions }, documentation: A document will save notification is sent from the client to the server before\nthe document is actually saved. }从该记录可以确认几项实现事实方向messageDirection为clientToServer与文档中客户端发送、服务器接收的语义一致能力键clientCapability与serverCapability分别对应textDocument.synchronization.willSave与textDocumentSync.willSave与规范文档中的属性名完全一致参数与注册选项分别引用WillSaveTextDocumentParams与TextDocumentRegistrationOptions与规范文档一一对应同一文件中还定义了配套的textDocument/willSaveWaitUntil记录clientCapability: textDocument.synchronization.willSaveWaitUntil、serverCapability: textDocumentSync.willSaveWaitUntil以及TextDocumentSyncClientCapabilities结构中willSave、willSaveWaitUntil两个能力字段的元模型声明。也就是说规范文档人类可读与 metaModel机器可读互为印证任何实现语言服务器 SDK 的工具链如代码生成器都可以直接消费 metaModel.json 生成WillSaveTextDocumentParams、TextDocumentSaveReason等类型与消息分发的样板代码。服务器实现要点小结综合规范文档与元模型实现textDocument/willSave支持时应注意协商先行在initialize响应中设置capabilities.textDocumentSync.willSave true并确认客户端在textDocument.synchronization.willSave中声明了支持若采用动态注册则注册选项为TextDocumentRegistrationOptions可带文档选择器限定范围。保证文档已打开只在客户端已通过didOpen打开文档后才可能收到本通知服务器应始终维护打开文档 → 内容快照的映射以便按textDocument.uri定位。按 reason 分流使用TextDocumentSaveReasonManual/AfterDelay/FocusOut决定处理深度对自动保存场景保持轻量。保持轻量快速willSave是单向通知、没有响应若需要在保存前修改内容应改用willSaveWaitUntil请求并返回TextEdit[]同时接受客户端可能丢弃结果的行为。与保存后通知配合若还需感知保存完成应同时声明textDocumentSync.save可携带SaveOptions.includeText并处理textDocument/didSave形成完整的保存生命周期闭环。以上能力键、参数结构与枚举值均以本仓库 3.19 版规范 willSave.md 为准该文档同时存在于 3.17、3.18 目录中定义保持一致适用于对应协议版本的语言服务器实现。赞分享开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载相关推荐Language Server Protocol 3.17 保存前通知 textDocument/willSave 全解析协议定义、参数结构与能力协商Language Server Protocol 3.17 保存前通知 textDocument/willSave 全解析协议定义、参数结构与能力协商 导读开发工具LSP 3.19 Hover 请求textDocument/hover协议详解从能力协商到响应构造LSP 3.19 Hover 请求textDocument/hover协议详解从能力协商到响应构造 hover悬停是 LSP 中最常用、用户感知最直观开发工具LSP 文本格式化请求textDocument/formatting协议详解从能力协商到 FormattingOptionsLSP 文本格式化请求textDocument/formatting协议详解从能力协商到 FormattingOptions 本篇指南聚焦语言服务器协议开发工具上一篇DDrawCompat终极指南如何让经典DirectX游戏在现代Windows上完美运行下一篇让电脑静如处子Windows最强风扇控制神器Fan Control全面指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考