☰
Language Server Protocol TextDocumentItem 详解:客户端向服务器传输文档的四大字段与语言标识符规范
2026/10/7 9:58:28 网站建设 项目流程
  • 开发工具

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

Defines a common protocol for language servers.

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

TextDocumentItem是 Language Server Protocol(LSP)中客户端向服务器传输文本文档内容的核心数据结构,承载文档的 URI、语言标识符、版本号与完整文本内容。本文基于本仓库_specifications/lsp/3.19/types/textDocumentItem.md的完整定义,结合textDocument/didOpen、notebookDocument同步等协议用法与 metaModel 机器可读定义,讲解该类型的字段语义、语言标识符取值规范以及实际传输场景,读完即可准确实现 LSP 客户端的文档打开与同步逻辑。

TextDocumentItem:文本文档传输的最小载体

在 LSP 的文本同步机制中,客户端负责管理已打开文档的内容所有权,而服务器则需要通过一种统一的结构接收文档的完整快照。TextDocumentItem正是为此而生的数据传输对象,协议原文对其定位只有一句话:"An item to transfer a text document from the client to the server."(一个用于把文本文档从客户端传输到服务器的数据项),但它承载了服务器理解文档所需的全部信息。

其 TypeScript 定义如下(见 textDocumentItem.md):

interface TextDocumentItem { /** * The text document's URI. */ uri: DocumentUri; /** * The text document's language identifier. */ languageId: string; /** * The version number of this document (it will increase after each * change, including undo/redo). */ version: integer; /** * The content of the opened text document. */ text: string; }

该结构一共只有四个必填字段,却构成了服务器端文档模型的完整骨架:

字段类型语义
uriDocumentUri文档在客户端中的唯一资源标识,服务器据此索引与管理文档
languageIdstring文档的语言标识符(如python、typescript),用于多语言场景下避免重新解析文件扩展名
versioninteger文档版本号,每次内容变化(包括撤销/重做)后递增
textstring文档被打开时的完整文本内容

uri:文档的唯一身份标识

uri的类型是DocumentUri。在本仓库的 uri.md 中有关于 URI 的专门说明:文档的 URI 在 LSP 中通常使用file:之类的 scheme,服务器需要把它当作文档的全局唯一标识来缓存状态。值得注意的是,与TextDocumentItem不同,某些场景(如 notebook 单元格文档)中协议刻意让 URI 保持"不透明"——服务器不应依赖其 scheme 或路径格式,这一点在 notebook 同步章节中会进一步展开。

version:版本号驱动的增量同步基础

version字段是 LSP 文档状态机同步的关键。协议明确规定:版本号在每次变更后递增,包括撤销(undo)与重做(redo),但版本号并不要求连续(见 versionedTextDocumentIdentifier.md 中 "The number doesn't need to be consecutive" 的说明)。

TextDocumentItem中的version与VersionedTextDocumentIdentifier中的version语义一致,区别仅在于:前者在文档打开时随完整内容一并送达服务器,而后者用于textDocument/didChange通知中标记"变更之后"的文档版本。在 didChange.md 给出的同步示例中可以看到这套版本机制的实际协作方式:

文档版本用户输入客户端行为请求
5文档变更一将文档v5同步给服务器textDocument/didChange
5-基于文档v5向服务器发起请求textDocument/completion
6文档变更二将文档v6同步给服务器textDocument/didChange

即:客户端在发起请求(如补全、签名帮助)之前,必须先把文档的最新版本同步给服务器,而TextDocumentItem正是"文档打开"这个同步起点的数据载体。

languageId:语言标识符的作用与推荐取值表

languageId字段的协议原意是:当服务器同时处理多种语言时,用它来识别文档属于哪种语言,从而避免对文件扩展名进行二次推断。也就是说,语言判断的第一依据是客户端显式声明的languageId,而不是文件后缀。

协议建议:当文档属于下列编程语言之一时,客户端应使用下表推荐的标识符(该表为协议正式规范的一部分,完整继承自 textDocumentItem.md):

语言标识符
ABAPabap
Windows Batbat
BibTeXbibtex
Clojureclojure
Coffeescriptcoffeescript
Cc
C++cpp
C#csharp
CSScss
Dd(@since 3.18.0)
Delphipascal(@since 3.18.0)
Diffdiff
Dartdart
Dockerfiledockerfile
Elixirelixir
Erlangerlang
F#fsharp
Gitgit-commit和git-rebase
Gogo
Groovygroovy
Handlebarshandlebars
Haskellhaskell
HTMLhtml
Iniini
Javajava
JavaScriptjavascript
JavaScript Reactjavascriptreact
JSONjson
LaTeXlatex
Lessless
Lualua
Makefilemakefile
Markdownmarkdown
Objective-Cobjective-c
Objective-C++objective-cpp
Pascalpascal(@since 3.18.0)
Perlperl
Perl 6perl6
PHPphp
Plaintextplaintext
Powershellpowershell
Pugjade
Pythonpython
Rr
Razor (cshtml)razor
Rubyruby
Rustrust
SCSSscss(花括号语法)、sass(缩进语法)
Scalascala
ShaderLabshaderlab
Shell Script (Bash)shellscript
SQLsql
Swiftswift
TypeScripttypescript
TypeScript Reacttypescriptreact
TeXtex
Text (plain)plaintext
Visual Basicvb
XMLxml
XSLxsl
YAMLyaml

几点值得注意的细节:

  • 一语言多标识符:SCSS 区分了scss(花括号语法)与sass(缩进语法);Git 相关文档则使用git-commit和git-rebase两个标识符。
  • 同标识符复用:Delphi 与 Pascal 都使用pascal,Plaintext 与 Text (plain) 都使用plaintext。
  • 版本演进:d、pascal三个条目是 3.18.0 版本才补充进规范表的(表中以@since 3.18.0标注),说明语言标识符表会随协议版本持续扩充。
  • 该表是"建议"而非强制:协议原文用 "it is recommended that clients use those ids" 表述,客户端可以按自身需求使用表外的自定义标识符,但服务器在实现多语言支持时应以该表为基准。

TextDocumentItem 的两个关键使用场景

场景一:textDocument/didOpen 通知——文档打开即快照

TextDocumentItem最典型的使用场景是textDocument/didOpen通知。该通知由客户端发给服务器,用于宣告新打开的文本文档,其参数结构如下(见 didOpen.md):

interface DidOpenTextDocumentParams { /** * The document that was opened. */ textDocument: TextDocumentItem; }

协议对 didOpen 语义有几点严格约定,理解这些约定有助于正确构造TextDocumentItem:

  • 内容所有权归客户端:文档一旦 open,其内容就由客户端管理,服务器不得再尝试用文档 URI 去磁盘读取内容。因此textDocument/didOpen中必须携带完整的text快照,这是服务器获得文档内容的唯一途径。
  • open/close 必须配对:同一文档在未发送对应 close 通知前,不允许重复发送 open;同一时刻每个文档的 open 计数最多为 1。
  • 语言变更需重开:如果文档的语言标识符发生变化(且服务器支持新语言),客户端必须先发送textDocument/didClose,再以新languageId重新发送textDocument/didOpen。这正是languageId字段在协议流程中的具体作用点。
  • open 不等于编辑器展示:open 仅表示"文档内容由客户端管理",并不要求其内容一定显示在编辑器中。

在 2.0 版本之后,didChange 参数中也引入了规范的版本号(见 didChange.md),TextDocumentItem.version与后续VersionedTextDocumentIdentifier.version构成了"打开即定版本、变更则递增"的连续同步链路。

场景二:Notebook 单元格同步——TextDocumentItem 复用

自 3.17.0 起,LSP 增加了 notebook 文档同步能力,TextDocumentItem被复用于同步 notebook 单元格的文本内容。在 notebook.md 中可以看到:

  • DidOpenNotebookDocumentParams通过cellTextDocuments: TextDocumentItem[]数组,一次性把所有已打开单元格的文本文档快照发给服务器;
  • notebook 单元格的文本文档 URI 是"不透明"的,由客户端自行生成,服务器不应依赖其格式——因此TextDocumentItem.uri在单元格场景下只作为唯一标识,不承载路径语义;
  • 单元格文本文档被视为普通文本文档,始终以增量同步(incremental sync)方式与服务器保持同步。

这体现了TextDocumentItem作为协议基础类型的通用性:无论普通文档还是 notebook 单元格,客户端向服务器"交底"文档内容时使用的都是同一结构。

从 metaModel 验证结构定义的机器可读形态

本仓库在_specifications/lsp/3.19/metaModel/目录下提供了协议的机器可读元模型,其中 metaModel.json 对TextDocumentItem做了与文档一致的 JSON 描述:

{ "name": "TextDocumentItem", "properties": [ { "name": "uri", "type": { "kind": "base", "name": "DocumentUri" }, "documentation": "The text document's uri." }, { "name": "languageId", "type": { "kind": "reference", "name": "LanguageKind" }, "documentation": "The text document's language identifier." }, { "name": "version", "type": { "kind": "base", "name": "integer" }, "documentation": "The version number of this document (it will increase after each change, including undo/redo)." }, { "name": "text", "type": { "kind": "base", "name": "string" }, "documentation": "The content of the opened text document." } ], "documentation": "An item to transfer a text document from the client to the server." }

从该元模型可以看出:languageId在元模型中被引用为LanguageKind类型(对应文档中的语言标识符表),uri被定义为DocumentUri基础类型,version为integer,text为string,四个字段均为必填(无optional标记)。对应的 TypeScript 类型定义见 metaModel.ts,其中声明了DocumentUri、integer、string等基础类型(BaseTypes),可作为实现代码生成器或协议校验器的直接依据。

实现要点与常见误区

结合以上协议细节,客户端与服务器在实现TextDocumentItem时应注意以下几点:

  1. 必填字段不可省略:uri、languageId、version、text四字段全部必填,缺少任一字段都会导致服务器无法建立完整的文档快照。
  2. 首次打开必须携带全量文本:didOpen是一次性的全量传输,服务器不会也不会去磁盘读取文档,所以text必须是打开时刻的完整内容。
  3. languageId 优先于扩展名:服务器做语言分派时应优先信任languageId,不要为每个请求重新根据 URI 后缀推断语言;多语言场景下这正是该字段的设计初衷。
  4. 版本号只增不减:version在每次变更(含撤销/重做)后递增,不需要连续,但必须单调递增;服务器可用它做请求与内容快照的匹配。
  5. Notebook 单元格 URI 保持不透明:当TextDocumentItem用于 notebook 单元格时,不要对 URI 的 scheme 或路径做任何假设。

相关文档导航

  • 类型定义原文:textDocumentItem.md(本文核心依据,3.18 版本定义见 3.18 版本)
  • 使用场景:didOpen.md、didChange.md、notebook.md
  • 关联类型:versionedTextDocumentIdentifier.md、uri.md
  • 机器可读定义:metaModel.json、metaModel.ts
  • 开发工具

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

Defines a common protocol for language servers.

项目地址:https://gitcode.com/gh_mirrors/la/language-server-protocol
点击查看免费下载
上一篇:Elsa.Diagnostics.StructuredLogs 模块重构与结构化日志能力完整落地指南(tasks.md 全解析)
下一篇:V8 回归测试实战指南:从零手写高质量 mjsunit 复现程序

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

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

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

立即咨询