☰
Language Server Protocol 文件创建事件通知:workspace/didCreateFiles 消息规范与实现解析
2026/10/6 7:49:15 网站建设 项目流程
  • 开发工具

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

Defines a common protocol for language servers.

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

导读

workspace/didCreateFiles是 Language Server Protocol(LSP)3.16 版本引入的通知消息,用于在客户端(编辑器/IDE)内部创建文件后,将这一事件告知语言服务器,帮助服务器及时更新索引、缓存与依赖关系。本文以 didCreateFiles.md 为骨架,结合 3.17 规范完整文档、metaModel.json 机器可读模型及相邻的 willCreateFiles 请求定义,深入讲解该通知的语义、两端能力协商、参数结构、注册选项与配套的过滤模式,并给出完整的服务端实现示例。读完本文,你将掌握 LSP 文件操作事件消息的完整收发链路,能够在自己实现的语言服务器中正确注册、解析并响应文件创建事件。

消息概览:客户端到服务器的单向通知

workspace/didCreateFiles是一条notification(通知),即单向消息——客户端发送后不期望服务器返回任何响应。其核心语义在 didCreateFiles.md 中表述为:

当文件在客户端内部被创建时,客户端向服务器发送 did create files 通知。

关键约束是"from within the client":该通知只覆盖由客户端内部发起的创建操作(例如用户在资源管理器中新建文件、重命名对话框中选择新建等),而不是服务器通过workspace/applyEdit自行发起的创建。客户端自己触发的创建会走另一条路径(workspace/willCreateFiles请求 + 该通知),以便在创建前给服务器一个拦截修改的机会。

在 metaModel.json 中,该消息被建模为:

{ "method": "workspace/didCreateFiles", "messageDirection": "clientToServer", "params": { "kind": "reference", "name": "CreateFilesParams" }, "registrationOptions": { "kind": "reference", "name": "FileOperationRegistrationOptions" }, "documentation": "The did create files notification is sent from the client to the server when files were created from within the client.", "since": "3.16.0" }

messageDirection: clientToServer在机器可读层面确认了该消息的方向;since: 3.16.0表明它最早在 3.16 版本加入规范,并持续保留到 3.17(该版本新增了stringValue等类型与 inline completion 等能力,但文件操作事件模型保持一致)。该消息在 3.17 完整规范 中通过{% include_relative workspace/didCreateFiles.md %}嵌入,是全量规范文本的一部分。

能力协商:Client Capability 与 Server Capability

与 LSP 中所有消息一致,workspace/didCreateFiles的使用需要两端在初始化阶段协商能力,避免不兼容的客户端或服务器间出现静默失败。

Client Capability(客户端能力)

  • 属性名(可选):workspace.fileOperations.didCreate
  • 属性类型:boolean

该能力表明客户端支持发送workspace/didCreateFiles通知。只有当客户端在initialize请求的capabilities中声明该字段为true时,服务器才可以期望在文件创建后收到该通知。

Server Capability(服务器能力)

  • 属性名(可选):workspace.fileOperations.didCreate
  • 属性类型:FileOperationRegistrationOptions

该能力表明服务器有兴趣接收workspace/didCreateFiles通知,并通过FileOperationRegistrationOptions声明自己关注哪些文件(通过过滤器与 glob 模式限定)。在 metaModel.json 中,服务器侧对应能力点的文档注释也直接写明"the server is interested in receiving didCreateFiles notifications"(见 metaModel.json 附近的 capability 定义),客户端侧则对应 "the client has support for sending didCreateFiles notifications"(见 metaModel.json 附近)。

能力协商的典型流程为:

  1. 客户端在initialize请求中声明capabilities.workspace.fileOperations.didCreate: true;
  2. 服务器在initialize响应中声明capabilities.workspace.fileOperations.didCreate为FileOperationRegistrationOptions(即{ "filters": [...] });
  3. 此后客户端在内部创建文件后,向服务器发送workspace/didCreateFiles通知。

消息参数:CreateFilesParams 与 FileCreate

通知的参数类型为CreateFilesParams,其定义完整继承自 3.16 版本,且在 willCreateFiles.md(同一创建流程的前置请求)中同样被复用,说明创建流程的两个消息共享同一组参数结构:

/** * The parameters sent in notifications/requests for user-initiated creation * of files. * * @since 3.16.0 */ export interface CreateFilesParams { /** * An array of all files/folders created in this operation. */ files: FileCreate[]; }

其中FileCreate描述单个文件/文件夹的创建信息:

/** * Represents information on a file/folder create. * * @since 3.16.0 */ export interface FileCreate { /** * A file:// URI for the location of the file/folder being created. */ uri: string; }

metaModel.json 对这两个类型也有完全一致的机器可读建模:CreateFilesParams包含一个files数组属性,元素类型引用FileCreate(见 metaModel.json);FileCreate仅含一个uri: string属性,文档注明 "A file:// URI for the location of the file/folder being created"(见 metaModel.json)。

一个真实的创建事件参数示例(同时创建两个文件):

{ "files": [ { "uri": "file:///Users/me/project/src/foo.ts" }, { "uri": "file:///Users/me/project/src/bar.ts" } ] }

需要注意两点:

  • files数组中的条目既可以是文件也可以是文件夹(文档原文为 "all files/folders created in this operation");
  • uri必须使用file://scheme 的绝对 URI,字符串形式的 URI 不做百分号编码之外的任何转换。

注册选项:FileOperationRegistrationOptions 与过滤模式

服务器在声明workspace.fileOperations.didCreate能力时,携带的FileOperationRegistrationOptions决定了哪些文件的创建事件会被投递给服务器。该类型及配套的过滤/模式类型完整定义在 willCreateFiles.md 中(因为 didCreate 与 willCreate 共享同一注册选项类型),本节完整继承其定义。

FileOperationRegistrationOptions

/** * The options to register for file operations. * * @since 3.16.0 */ interface FileOperationRegistrationOptions { /** * The actual filters. */ filters: FileOperationFilter[]; }

FileOperationFilter

/** * A filter to describe in which file operation requests or notifications * the server is interested in. * * @since 3.16.0 */ export interface FileOperationFilter { /** * A Uri like `file` or `untitled`. */ scheme?: string; /** * The actual file operation pattern. */ pattern: FileOperationPattern; }

scheme可选,用于限定 URI scheme(如file、untitled);不设置则匹配所有 scheme。

FileOperationPattern

/** * A pattern to describe in which file operation requests or notifications * the server is interested in. * * @since 3.16.0 */ interface FileOperationPattern { /** * The glob pattern to match. Glob patterns can have the following syntax: * - `*` to match zero or more characters in a path segment * - `?` to match on one character in a path segment * - `**` to match any number of path segments, including none * - `{}` to group sub patterns into an OR expression. (e.g. `**​/*.{ts,js}` * matches all TypeScript and JavaScript files) * - `[]` to declare a range of characters to match in a path segment * (e.g., `example.[0-9]` to match on `example.0`, `example.1`, …) * - `[!...]` to negate a range of characters to match in a path segment * (e.g., `example.[!0-9]` to match on `example.a`, `example.b`, but * not `example.0`) */ glob: string; /** * Whether to match files or folders with this pattern. * * Matches both if undefined. */ matches?: FileOperationPatternKind; /** * Additional options used during matching. */ options?: FileOperationPatternOptions; }

FileOperationPatternKind 与 FileOperationPatternOptions

export namespace FileOperationPatternKind { /** * The pattern matches a file only. */ export const file: 'file' = 'file'; /** * The pattern matches a folder only. */ export const folder: 'folder' = 'folder'; } export type FileOperationPatternKind = 'file' | 'folder';
export interface FileOperationPatternOptions { /** * The pattern should be matched ignoring casing. */ ignoreCase?: boolean; }

glob 语法速查表

语法含义示例
*匹配路径段内零个或多个字符*.ts匹配a.ts、foo.ts
?匹配路径段内的单个字符a?.ts匹配ab.ts,不匹配abc.ts
**匹配任意数量的路径段(含零个)**/*.ts匹配任意目录层级下的.ts文件
{}子模式分组 OR**/*.{ts,js}匹配所有 TypeScript 与 JavaScript 文件
[]路径段内字符范围example.[0-9]匹配example.0、example.1…
[!...]路径段内字符范围取反example.[!0-9]匹配example.a,不匹配example.0

一个完整的注册选项示例

{ "workspace": { "fileOperations": { "didCreate": { "filters": [ { "scheme": "file", "pattern": { "glob": "**/*.{ts,tsx}", "matches": "file" } }, { "pattern": { "glob": "**/src/**", "matches": "folder" } } ] } } } }

该示例表示:服务器只关注filescheme 下所有.ts/.tsx文件的创建,以及任意src目录层级下文件夹的创建。

服务器端实现示例(TypeScript)

结合上述规范,一个使用 Node.js/TypeScript 的语言服务器可以这样接收并处理workspace/didCreateFiles:

import { Connection, CreateFilesParams, InitializeParams, InitializeResult, } from 'vscode-languageserver/node'; export function registerFileOperationHandlers(connection: Connection) { // 1. 处理通知:文件创建后更新服务器内部索引 connection.onDidCreateFiles(async (params: CreateFilesParams) => { for (const created of params.files) { const uri = created.uri; // file:///... 形式 console.log(`[file-ops] created: ${uri}`); // 在此处增量更新符号索引、依赖图或缓存 await indexer.addFile(uri); } }); } // 2. 在 initialize 响应中声明服务器能力 export function getServerCapabilities(): InitializeResult['capabilities'] { return { workspace: { fileOperations: { didCreate: { filters: [ { pattern: { glob: '**/*.{ts,tsx}', matches: 'file', }, }, ], }, }, }, }; } // 3. 客户端侧(在实现 LSP 客户端的代码中):发送通知 function sendDidCreateFiles(connection: Connection, uris: string[]) { connection.sendNotification('workspace/didCreateFiles', { files: uris.map((uri) => ({ uri })), } satisfies CreateFilesParams); }

从源码结构看,onDidCreateFiles这类助手 API 会在各主流 LSP SDK(如 vscode-languageserver-node、lsp 系列 SDK)中将消息名与参数类型绑定,从而避免手写sendNotification时的魔法字符串拼写错误。

与文件操作家族消息的关系

workspace/didCreateFiles并不是孤立存在的,它是 LSP 文件操作事件(file operations)家族中的一员。在 workspace 目录下,同族消息还包括:

消息类型参数注册选项触发时机
workspace/willCreateFilesRequestCreateFilesParamsFileOperationRegistrationOptions文件创建之前,可返回WorkspaceEdit干预
workspace/didCreateFilesNotificationCreateFilesParamsFileOperationRegistrationOptions文件创建之后,单向通知
workspace/willRenameFilesRequestRenameFilesParams同上重命名之前,可返回WorkspaceEdit
workspace/didRenameFilesNotificationRenameFilesParams同上重命名之后
workspace/willDeleteFilesRequestDeleteFilesParams同上删除之前
workspace/didDeleteFilesNotificationDeleteFilesParams同上删除之后

六个消息构成三对"先 will 后 did"的完整生命周期,全部沿用FileOperationRegistrationOptions作为注册选项。will*请求允许服务器返回WorkspaceEdit,在操作真正发生前对工作区做调整(注意:不能修改将要创建/删除的文件自身内容);did*通知则用于操作完成后的状态同步——这正是workspace/didCreateFiles的定位:事后通知,用于索引更新与状态同步,不参与操作前干预。

在 3.17 规范的变更记录 中,这三对消息被一并列出:

  • Add support forworkspace/didCreateFilesnotifications andworkspace/willCreateFilesrequests.
  • Add support forworkspace/didRenameFilesnotifications andworkspace/willRenameFilesrequests.
  • Add support forworkspace/didDeleteFilesnotifications andworkspace/willDeleteFilesrequests.

说明该家族自 3.16 起整体加入规范,并在 3.17 中保持稳定。

版本演进与文档导航

在_data/specification-3-17-toc.yml中,didCreateFiles与willCreateFiles等文件操作消息被编排在Workspace章节下;3.18、3.19 版本的 workspace 目录 与 3.19 对应目录 均保留该通知的独立文档页,说明该消息在后续版本中持续有效,未发生破坏性变更。

小结

workspace/didCreateFiles是 LSP 文件操作事件模型中"创建之后"的客户端到服务器通知:

  • 触发条件:仅限客户端内部发起的文件/文件夹创建操作;
  • 能力协商:客户端需声明workspace.fileOperations.didCreate: true,服务器需声明FileOperationRegistrationOptions形式的对应能力;
  • 参数结构:CreateFilesParams.files: FileCreate[],每条FileCreate仅含一个file://URI;
  • 注册过滤:通过FileOperationFilter+ glob 模式精确限定关注范围,支持*、**、{}、[]、[!...]语法与file/folder匹配类型;
  • 实现要点:服务器在收到通知后应及时增量更新索引、依赖图或缓存,保持与客户端文件系统的同步。

对于任何依赖文件系统状态的语言服务器(补全、跳转、诊断、符号索引等),正确处理该通知是保证跨文件引用数据新鲜度的关键一环;若需要拦截创建过程本身,则应配合workspace/willCreateFiles请求(willCreateFiles.md)一起使用。

  • 开发工具

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

Defines a common protocol for language servers.

项目地址:https://gitcode.com/gh_mirrors/la/language-server-protocol
点击查看免费下载
上一篇:GetQzonehistory:一键备份QQ空间历史说说,永久珍藏你的青春记忆
下一篇:如何用M9A智能助手解放你的游戏时间:重返未来1999自动化终极指南

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

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

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

立即咨询