- 开发工具
【免费下载链接】language-server-protocol
Defines a common protocol for language servers.
导读
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 附近)。
能力协商的典型流程为:
- 客户端在
initialize请求中声明capabilities.workspace.fileOperations.didCreate: true; - 服务器在
initialize响应中声明capabilities.workspace.fileOperations.didCreate为FileOperationRegistrationOptions(即{ "filters": [...] }); - 此后客户端在内部创建文件后,向服务器发送
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/willCreateFiles | Request | CreateFilesParams | FileOperationRegistrationOptions | 文件创建之前,可返回WorkspaceEdit干预 |
workspace/didCreateFiles | Notification | CreateFilesParams | FileOperationRegistrationOptions | 文件创建之后,单向通知 |
workspace/willRenameFiles | Request | RenameFilesParams | 同上 | 重命名之前,可返回WorkspaceEdit |
workspace/didRenameFiles | Notification | RenameFilesParams | 同上 | 重命名之后 |
workspace/willDeleteFiles | Request | DeleteFilesParams | 同上 | 删除之前 |
workspace/didDeleteFiles | Notification | DeleteFilesParams | 同上 | 删除之后 |
六个消息构成三对"先 will 后 did"的完整生命周期,全部沿用FileOperationRegistrationOptions作为注册选项。will*请求允许服务器返回WorkspaceEdit,在操作真正发生前对工作区做调整(注意:不能修改将要创建/删除的文件自身内容);did*通知则用于操作完成后的状态同步——这正是workspace/didCreateFiles的定位:事后通知,用于索引更新与状态同步,不参与操作前干预。
在 3.17 规范的变更记录 中,这三对消息被一并列出:
- Add support for
workspace/didCreateFilesnotifications andworkspace/willCreateFilesrequests.- Add support for
workspace/didRenameFilesnotifications andworkspace/willRenameFilesrequests.- Add support for
workspace/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.
相关推荐
Context7 MCP 服务器安装与使用完全指南:为 LLM 与 AI 编程助手注入实时更新的库文档
Context7 MCP 服务器安装与使用完全指南:为 LLM 与 AI 编程助手注入实时更新的库文档 Context7 是一个面向 LLM 与 AI 编程助手
开发工具深入解析Language Server Protocol 3.14版本规范
深入解析Language Server Protocol 3.14版本规范 协议概述 Language Server Protocol(LSP)是微软开发的一套
开发工具Language Server Protocol 终极指南:深入解析消息格式与通信机制
Language Server Protocol 终极指南:深入解析消息格式与通信机制 Language Server Protocol(语言服务器协议)是现代
开发工具
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考