ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Language Server Protocol 3.19 WorkspaceEdit 全解析:从批量文本编辑到文件资源操作

Language Server Protocol 3.19 WorkspaceEdit 全解析:从批量文本编辑到文件资源操作 开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载导读WorkspaceEdit是 Language Server ProtocolLSP中语言服务器向客户端批量下发工作区变更的通用载体覆盖跨文档的文本修改、文件的创建/重命名/删除等资源操作以及带标签的变更注解Change Annotation。本文基于仓库中 LSP 3.19 规范的 workspaceEdit.md 文档完整讲解WorkspaceEdit的结构、客户端能力协商、资源操作与失败处理策略并结合 textDocumentEdit.md、resourceChanges.md、textEdit.md 与 applyEdit.md 等关联文档展开源码级细节帮助你理解并正确构造可被客户端安全执行的 WorkspaceEdit。WorkspaceEdit 是什么一次编辑多处资源根据规范定义一个 workspace edit 表示对工作区中管理的多个资源resources的变更集合。它既可以修改多个文本文件的内容也可以创建、删除或重命名文件与文件夹。该类型是众多请求的返回载体例如textDocument/rename、textDocument/codeAction、workspace/willCreateFiles等请求都可能以WorkspaceEdit作为结果返回最终通过workspace/applyEdit请求由客户端执行。在 LSP 3.19 的机器可读元数据 metaModel.json 中WorkspaceEdit被建模为包含三个可选属性的结构changes、documentChanges与changeAnnotations与 TypeScript 接口定义完全一致可用于校验与代码生成。WorkspaceEdit 接口结构WorkspaceEdit的核心接口定义如下摘自 workspaceEdit.mdexport interface WorkspaceEdit { /** * Holds changes to existing resources. */ changes?: { [uri: DocumentUri]: TextEdit[]; }; /** * Depending on the client capability * workspace.workspaceEdit.resourceOperations document changes are either * an array of TextDocumentEdits to express changes to n different text * documents where each text document edit addresses a specific version of * a text document. Or it can contain above TextDocumentEdits mixed with * create, rename and delete file / folder operations. * * Whether a client supports versioned document edits is expressed via * workspace.workspaceEdit.documentChanges client capability. * * If a client neither supports documentChanges nor * workspace.workspaceEdit.resourceOperations then only plain TextEdits * using the changes property are supported. */ documentChanges?: ( TextDocumentEdit[] | (TextDocumentEdit | CreateFile | RenameFile | DeleteFile)[] ); /** * A map of change annotations that can be referenced in * AnnotatedTextEdits or create, rename and delete file / folder * operations. * * Whether clients honor this property depends on the client capability * workspace.changeAnnotationSupport. * * since 3.16.0 */ changeAnnotations?: { [id: string /* ChangeAnnotationIdentifier */]: ChangeAnnotation; }; }三个属性各有明确职责属性类型说明changes{ [uri: DocumentUri]: TextEdit[] }以文档 URI 为键、TextEdit数组为值的映射表达对现有资源的纯文本修改不携带版本信息documentChangesTextDocumentEdit[]或(TextDocumentEdit \| CreateFile \| RenameFile \| DeleteFile)[]版本化的文档编辑数组可混入创建、重命名、删除文件/文件夹的资源操作changeAnnotations{ [id: ChangeAnnotationIdentifier]: ChangeAnnotation }变更注解映射表供AnnotatedTextEdit与资源操作通过annotationId引用自 3.16.0 引入changes 与 documentChanges 的取舍规则规范明确要求编辑要么提供changes要么提供documentChanges。当客户端支持版本化文档编辑即声明workspace.workspaceEdit.documentChanges能力且服务端提供了documentChanges时后者优先于changes。changes的适用场景客户端既不支持documentChanges也不支持resourceOperations时服务端只能回退到纯TextEdit的changes形式。它不携带文档版本客户端无法在应用前校验文档是否过期。documentChanges的优势每个TextDocumentEdit都通过OptionalVersionedTextDocumentIdentifier携带文档版本允许客户端在应用编辑前检查版本一致性避免基于过期快照的编辑被错误应用。TextDocumentEdit单个文档的版本化变更当需要在多个文本文档上做精确的版本化编辑时documentChanges数组中的元素类型为TextDocumentEdit见 textDocumentEdit.mdexport interface TextDocumentEdit { /** * The text document to change. */ textDocument: OptionalVersionedTextDocumentIdentifier; /** * The edits to be applied. * * since 3.16.0 - support for AnnotatedTextEdit. This is guarded by the * client capability workspace.workspaceEdit.changeAnnotationSupport * * since 3.18.0 - support for SnippetTextEdit. This is guarded by the * client capability workspace.workspaceEdit.snippetEditSupport */ edits: (TextEdit | AnnotatedTextEdit | SnippetTextEdit)[]; }该类型描述了对单个文本文档的全部文本变更文档从版本 Si 开始编辑应用后进入版本 Si1。因此服务端不需要对edits数组排序但必须保证各编辑之间互不重叠non overlapping。edits数组的元素可以是三种类型之一TextEdit基础文本编辑包含range与newText两个字段。插入文本时令start end删除操作则使用空字符串newText见 textEdit.mdAnnotatedTextEdit自 3.16.0 起在TextEdit基础上追加annotationId以关联变更注解SnippetTextEdit自 3.18.0 起插入交互式 snippet 而非纯文本通过snippet: StringValue携带 snippet 内容见 textEdit.md。文件资源操作create、rename、delete自 3.13.0 起WorkspaceEdit可以包含资源操作。资源操作同样以文件/文件夹为对象命名沿用“file”但实际同时支持文件夹相关字面量定义详见 resourceChanges.md。CreateFile 创建操作export interface CreateFileOptions { /** * Overwrite existing file. Overwrite wins over ignoreIfExists. */ overwrite?: boolean; /** * Ignore if exists. */ ignoreIfExists?: boolean; } export interface CreateFile { /** * This is a create operation. */ kind: create; /** * The resource to create. */ uri: DocumentUri; /** * Additional options. */ options?: CreateFileOptions; /** * An optional annotation identifier describing the operation. * * since 3.16.0 */ annotationId?: ChangeAnnotationIdentifier; }创建操作通过kind: create标识options.overwrite与options.ignoreIfExists控制目标已存在时的行为且overwrite优先于ignoreIfExists。RenameFile 重命名操作export interface RenameFileOptions { /** * Overwrite target if existing. Overwrite wins over ignoreIfExists. */ overwrite?: boolean; /** * Ignores if target exists. */ ignoreIfExists?: boolean; } export interface RenameFile { /** * This is a rename operation. */ kind: rename; /** * The old (existing) location. */ oldUri: DocumentUri; /** * The new location. */ newUri: DocumentUri; /** * Rename options. */ options?: RenameFileOptions; /** * An optional annotation identifier describing the operation. * * since 3.16.0 */ annotationId?: ChangeAnnotationIdentifier; }重命名通过oldUri与newUri描述迁移路径options语义与创建操作类似overwrite覆盖已存在的目标且优先于ignoreIfExists。DeleteFile 删除操作export interface DeleteFileOptions { /** * Delete the content recursively if a folder is denoted. */ recursive?: boolean; /** * Ignore the operation if the file doesnt exist. */ ignoreIfNotExists?: boolean; } export interface DeleteFile { /** * This is a delete operation. */ kind: delete; /** * The file to delete. */ uri: DocumentUri; /** * Delete options. */ options?: DeleteFileOptions; /** * An optional annotation identifier describing the operation. * * since 3.16.0 */ annotationId?: ChangeAnnotationIdentifier; }删除文件夹时可通过options.recursive递归删除内容options.ignoreIfNotExists用于在文件不存在时静默忽略该操作。资源操作的顺序约束当WorkspaceEdit包含资源操作时客户端必须严格按照数组中给出的顺序执行操作。例如一个合法的编辑可以是(1) 创建文件a.txt(2) 向a.txt插入文本的文本编辑。而非法序列——例如 (1) 删除a.txt、(2) 再向a.txt插入文本——将导致操作失败。失败后的恢复方式由客户端能力workspace.workspaceEdit.failureHandling决定见下文。变更注解Change Annotation给编辑加上可读标签自 3.16.0 起WorkspaceEdit引入了changeAnnotations属性用于为文本编辑与资源操作附加人类可读的描述信息。ChangeAnnotation定义见 textEdit.mdexport interface ChangeAnnotation { /** * A human-readable string describing the actual change. The string * is rendered prominently in the user interface. */ label: string; /** * A flag which indicates that user confirmation is needed * before applying the change. */ needsConfirmation?: boolean; /** * A human-readable string which is rendered less prominently in * the user interface. */ description?: string; }协议的设计要点是编辑或资源操作通过ChangeAnnotationIdentifierstring引用注解而不是内嵌注解字面量。这使服务端可以在多个编辑或资源操作间复用同一个注解 ID客户端据此将具有相同注解的编辑分组展示例如统一标记为Changes in Strings。注解的引用形式有两种AnnotatedTextEdit extends TextEdit追加必填的annotationId: ChangeAnnotationIdentifierCreateFile、RenameFile、DeleteFile各自携带可选的annotationId。需要特别说明AnnotatedTextEdit的使用受客户端能力workspace.workspaceEdit.changeAnnotationSupport约束若客户端未声明该能力服务端不应发送AnnotatedTextEdit字面量。WorkspaceEditClientCapabilities客户端能力协商WorkspaceEdit的能力随协议版本不断演进客户端通过workspace.workspaceEdit属性路径声明自身支持程度见 workspaceEdit.md。其完整结构如下export interface WorkspaceEditClientCapabilities { /** * The client supports versioned document changes in WorkspaceEdits. */ documentChanges?: boolean; /** * The resource operations the client supports. Clients should at least * support create, rename, and delete for files and folders. * * since 3.13.0 */ resourceOperations?: ResourceOperationKind[]; /** * The failure handling strategy of a client if applying the workspace edit * fails. * * since 3.13.0 */ failureHandling?: FailureHandlingKind; /** * Whether the client normalizes line endings to the client specific * setting. * If set to true, the client will normalize line ending characters * in a workspace edit to the client specific new line character(s). * * since 3.16.0 */ normalizesLineEndings?: boolean; /** * Whether the client in general supports change annotations on text edits, * create file, rename file, and delete file changes. * * since 3.16.0 */ changeAnnotationSupport?: ChangeAnnotationsSupportOptions; /** * Whether the client supports WorkspaceEditMetadata in WorkspaceEdits. * * since 3.18.0 */ metadataSupport?: boolean; /** * Whether the client supports snippets as text edits. * * since 3.18.0 */ snippetEditSupport?: boolean; }各能力字段说明documentChanges客户端是否支持WorkspaceEdit中的版本化文档变更resourceOperations客户端支持哪些资源操作其取值来自ResourceOperationKind枚举。规范建议客户端至少支持create、rename、delete三种文件与文件夹failureHandling应用编辑失败时的恢复策略取值为FailureHandlingKindnormalizesLineEndings若为true客户端会将编辑中的行尾字符归一化为客户端特定的换行符changeAnnotationSupport客户端是否支持文本编辑与资源操作上的变更注解其类型为ChangeAnnotationsSupportOptionsmetadataSupport自 3.18.0 起客户端是否支持WorkspaceEditMetadatasnippetEditSupport自 3.18.0 起客户端是否支持 snippet 形式的文本编辑。ChangeAnnotationsSupportOptions还提供了标签分组选项export type ChangeAnnotationsSupportOptions { /** * Whether the client groups edits with equal labels into tree nodes, * for instance all edits labelled with Changes in Strings would * be a tree node. */ groupsOnLabel?: boolean; };当groupsOnLabel为true时客户端会把具有相同标签的编辑归并到同一个树节点中展示。ResourceOperationKind 枚举export type ResourceOperationKind create | rename | delete; export namespace ResourceOperationKind { /** * Supports creating new files and folders. */ export const Create: ResourceOperationKind create; /** * Supports renaming existing files and folders. */ export const Rename: ResourceOperationKind rename; /** * Supports deleting existing files and folders. */ export const Delete: ResourceOperationKind delete; }该枚举用于resourceOperations数组字符串字面量为create、rename、delete。FailureHandlingKind失败处理策略当客户端应用编辑失败时其恢复方式由FailureHandlingKind描述共四种取值export type FailureHandlingKind abort | transactional | undo | textOnlyTransactional; export namespace FailureHandlingKind { /** * Applying the workspace change is simply aborted if one of the changes * provided fails. All operations executed before the failing operation * stay executed. */ export const Abort: FailureHandlingKind abort; /** * All operations are executed transactionally. That means they either all * succeed or no changes at all are applied to the workspace. */ export const Transactional: FailureHandlingKind transactional; /** * If the workspace edit contains only textual file changes they are * executed transactionally. If resource changes (create, rename or delete * file) are part of the change the failure handling strategy is abort. */ export const TextOnlyTransactional: FailureHandlingKind textOnlyTransactional; /** * The client tries to undo the operations already executed. But there is no * guarantee that this is succeeding. */ export const Undo: FailureHandlingKind undo; }四种策略的实际语义取值语义abort任一变更失败即中止失败前已执行的操作保持生效transactional所有操作事务化执行要么全部成功要么不向工作区应用任何变更textOnlyTransactional仅含文本变更时按事务执行若包含资源变更创建/重命名/删除文件则退化为abort策略undo客户端尝试撤销已执行的操作但不保证一定成功服务端在构造包含多步骤资源操作的编辑时应依据客户端声明的failureHandling判断对于包含“创建文件后向其中插入文本”这种强依赖链的编辑选择声明transactional或textOnlyTransactional的客户端更安全对于仅声明abort的客户端应尽量把相互依赖的操作收敛到单个TextDocumentEdit内。通过 workspace/applyEdit 下发编辑WorkspaceEdit最终由服务端通过workspace/applyEdit请求发送给客户端执行见 applyEdit.md。请求参数与结果定义如下export interface ApplyWorkspaceEditParams { /** * An optional label of the workspace edit. This label is * presented in the user interface, for example, on an undo * stack to undo the workspace edit. */ label?: string; /** * The edits to apply. */ edit: WorkspaceEdit; /** * Additional data about the edit. * * since 3.18.0 */ metadata?: WorkspaceEditMetadata; }其中label会呈现在用户界面例如撤销栈上便于用户识别与撤销整组编辑。metadata自 3.18.0携带附加信息export interface WorkspaceEditMetadata { /** * Signal to the editor that this edit is a refactoring. */ isRefactoring?: boolean; }执行结果ApplyWorkspaceEditResult会向服务端反馈是否应用成功export interface ApplyWorkspaceEditResult { /** * Indicates whether the edit was applied or not. */ applied: boolean; /** * An optional textual description for why the edit was not applied. * This may be used by the server for diagnostic logging or to provide * a suitable error for a request that triggered the edit. */ failureReason?: string; /** * Depending on the clients failure handling strategy, failedChange * might contain the index of the change that failed. This property is * only available if the client signals a failureHandling strategy * in its client capabilities. */ failedChange?: uinteger; }applied编辑是否被应用failureReason未应用时的可选文本描述服务端可用于诊断日志或作为触发该编辑的请求的合适错误信息failedChange取决于客户端的失败处理策略可能携带失败变更的索引仅在客户端声明了failureHandling能力时可用。构造 WorkspaceEdit 的实践建议结合上述规范要点服务端构造WorkspaceEdit时应遵循以下原则先协商后构造根据客户端在initialize阶段上报的workspace.workspaceEdit能力选择承载形式。客户端支持documentChanges时优先使用版本化编辑否则回退到changes映射。按序排列资源操作documentChanges数组中资源操作的顺序即客户端执行顺序。创建文件与写入内容的编辑必须保证“先创建后写入”删除与写入则不得共存于同一次编辑。版本化编辑保持非重叠单个TextDocumentEdit内的多个编辑可以任意顺序排列但range不得重叠跨文档编辑分别落在各自的TextDocumentEdit中。善用变更注解客户端声明changeAnnotationSupport时使用changeAnnotations映射 annotationId引用为编辑附加label/description并可在needsConfirmation为true时请求用户确认未声明该能力时不要发送AnnotatedTextEdit。尊重 snippet 能力边界仅在客户端声明snippetEditSupport后发送SnippetTextEdit交互式 snippet 只会应用到活动编辑器中打开的文件对未打开的文件客户端会降级为普通文本编辑。以上约定共同保证了WorkspaceEdit在跨客户端实现中的可移植性与可预测性——无论编辑对象是纯文本文件还是文件系统资源服务端都能构造出客户端可以安全、有序执行的批量变更。赞分享开发工具【免费下载链接】language-server-protocolDefines a common protocol for language servers.项目地址https://gitcode.com/gh_mirrors/la/language-server-protocol点击查看免费下载相关推荐LSP WorkspaceEdit 深度解析Language Server Protocol 中跨文件编辑与资源操作协议详解LSP WorkspaceEdit 深度解析Language Server Protocol 中跨文件编辑与资源操作协议详解 导读 WorkspaceEdit开发工具Language Server Protocol 3.17 TextDocumentEdit 全解析文本编辑的版本一致性、注释分组与 WorkspaceEdit 集成Language Server Protocol 3.17 TextDocumentEdit 全解析文本编辑的版本一致性、注释分组与 WorkspaceEdi开发工具Language Server Protocol 中的 TextEdit[]文档批量文本编辑的规则、约束与实战Language Server Protocol 中的 TextEdit 文档批量文本编辑的规则、约束与实战 导读 本文聚焦 Language Server开发工具上一篇CANN/cann-bench: CrossformerAttention算子下一篇Indicator Go测试框架深度解析确保量化策略的可靠性创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表