基础 JSON 结构

有相当多的 JSON 结构在不同的请求和通知之间共享。本节介绍了它们的结构和功能。

URI

URI 作为字符串传输。URI 的格式在 https://tools.ietf.org/html/rfc3986 中定义

 foo://example.com:8042/over/there?name=ferret#nose
  \_/   \______________/\_________/ \_________/ \__/
   |           |            |            |        |
scheme     authority       path        query   fragment
   |   _____________________|__
  / \ /                        \
  urn:example:animal:ferret:nose

LSP 团队也维护了一个 node 模块,用于将字符串解析为 schemeauthoritypathqueryfragment 的 URI 组件。Github 仓库地址是 https://github.com/Microsoft/vscode-uri, 以及 npm 包地址 https://www.npmjs.com/package/vscode-uri

许多 interface 定义中都包含与文档的 URI 相对应的字段。为清楚起见,此类字段的类型声明为 DocumentUri。通过网络,它仍将作为字符串传输,但这可以保证该字符串的内容可以解析为有效的 URI。

应注意处理 URI 中的编码。例如,某些客户端(如 VS Code)可能会对驱动器号中的冒号进行编码,而其他客户端则不会。下面的 URI 都是有效的,但客户端和服务器应与它们自己使用的表单一致,以确保另一方不会将它们解释为不同的 URI。客户端和服务器不应假定彼此的编码方式相同(例如,在驱动器号中编码冒号的客户端不能假定服务器响应将具有编码的冒号)。这同样适用于驱动器号的大小写 - 一方不应假设另一方将返回与自身大小写相同的驱动器号的路径。

file:///c:/project/readme.md
file:///C%3A/project/readme.md
type DocumentUri = string;

还有一个用于普通非文档 URI 的标记接口。它也映射到字符串。

type URI = string;

正则表达式

正则表达式是一个强大的工具,在语言服务器协议中也有它们的实际用例。然而,它们的缺点是几乎每种编程语言都有自己的一组正则表达式功能,因此规范不能简单地将它们称为正则表达式。因此,LSP 使用两步方法来支持正则表达式:

  • 客户端将宣布它将使用哪个正则表达式引擎。这将允许为非常特定的客户端编写的服务器充分利用客户端的正则表达式功能

  • 该规范将定义一组客户端应支持的正则表达式功能。LSP 不会编写新规范,而是引用 ECMAScript 正则表达式规范,并从中删除在 LSP 上下文中不需要或难以为其他客户端实现的功能。

Client capability:

以下客户端能力用于宣布客户端的正则表达式引擎

  • 属性路径: general.regularExpressions

  • 属性类型: RegularExpressionsClientCapabilities 定义如下:

/**
 * Client capabilities specific to regular expressions.
 */
export interface RegularExpressionsClientCapabilities {
	/**
	 * The engine's name.
	 */
	engine: string;

	/**
	 * The engine's version.
	 */
	version?: string;
}

下表列出了众所周知的引擎值。请注意,该表应由将 LSP 集成到现有客户端的社区驱动。该规范的目标不是列出所有可用的正则表达式引擎。

EngineVersionDocumentation
ECMAScriptES2020ECMAScript 2020 & MDN

正则表达式子集:

ECMAScript 2020 正则表达式规范中的以下功能对于客户端不是必需的:

  • 断言:前瞻断言、否定前瞻断言、后视断言、否定后瞻断言。

  • 字符类:使用插入符号匹配控制字符(例如 \cX)和匹配 UTF-16 代码单元(例如 \uhhhh)。

  • 组和范围:命名捕获组。

  • Unicode 属性转义:不需要支持任何功能。

客户端需要支持的唯一正则表达式标志是“i”,用于指定不区分大小写的搜索。

枚举

该协议支持两种枚举:(a) 基于整数的枚举和 (b) 基于字符串的枚举。基于整数的枚举通常以 1 开头。之前编写的,不是以1开头的,也被保留以保持向后兼容。如果合适,枚举的值集由定义端(例如客户端或服务器)宣布,并在初始化握手期间传输到另一端。一个示例是 CompletionItemKind 枚举。它由客户端使用 textDocument.completion.completionItemKind 客户端属性宣布。

为了支持枚举的演变,枚举的使用端不应在它不知道的枚举值上失败。它应该简单地忽略它作为一个可以使用的价值,并尽最大努力在往返时保持这个价值。让我们再次以 CompletionItemKind 枚举为例:如果在规范的未来版本中,客户端添加并宣布了值为 n 的附加完成项类型,则不知道该值的(较旧的)服务器不应失败,而只是忽略该值作为可用项类型的值。

文本文档

当前协议是为内容可以表示为字符串的文本文档量身定制的。目前不支持二进制文档。文档中的位置(请参阅下面的位置定义)表示为从零开始的行和字符的偏移量。

3.17 中的新功能

在 3.17 之前,偏移量始终基于 UTF-16 字符串表示。因此,在 a𐐨b 形式的字符串中,字符 a 的字符偏移量为 0,𐐨 的字符偏移量为 1,b 的字符偏移量为 3,因为 𐐀 使用 UTF-16 中的两个代码单元表示。从 3.17 开始,客户端和服务器可以就不同的字符串编码表示(例如 UTF-8)达成一致。客户端通过客户端功能 general.positionEncoding 宣布支持编码。该值是客户端支持的位置编码数组,优先级递减(例如,索引 0 处的编码是首选编码)。为了保持向后兼容,唯一的强制性编码是 UTF-16,通过字符串 utf-16 表示。服务器可以选择客户端提供的编码之一,并通过初始化结果的属性 capabilities.positionCoding 将该编码信号传回客户端。如果客户端的功能中缺少字符串值 utf-16,则 general.positionEncodings 服务器可以安全地假定客户端支持 UTF-16。如果服务器在其初始化结果中省略了位置编码,则编码默认为字符串值 utf-16。实现注意事项:由于从一种编码到另一种编码的转换需要文件/行的内容,因此最好在读取文件的位置进行转换,这通常是在服务器端。

为了确保客户端和服务器将字符串拆分为相同的行表示形式,该协议指定了以下行尾序列:"\n"、"\r\n" 和 "\r"。位置与行尾字符无关。因此,您不能指定表示 \r|\n 或 \n| 的位置,其中 | 表示字符偏移量。

export const EOL: string[] = ['\n', '\r\n', '\r'];

Position

在文本文档中的位置,表示为从零开始的行和从零开始的字符偏移量。位置位于两个字符之间,就像编辑器中的 "插入" 光标一样。不支持特殊值,例如 -1 表示行尾。

interface Position {
	/**
	 * Line position in a document (zero-based).
	 */
	line: uinteger;

	/**
	 * Character offset on a line in a document (zero-based). The meaning of this
	 * offset is determined by the negotiated `PositionEncodingKind`.
	 *
	 * If the character value is greater than the line length it defaults back
	 * to the line length.
	 */
	character: uinteger;
}

在描述位置时,协议需要指定如何解释偏移量(特别是字符偏移量)。相应的 PositionEncodingKind 在初始化期间在客户端和服务器之间协商。

/**
 * A type indicating how positions are encoded,
 * specifically what column offsets mean.
 *
 * @since 3.17.0
 */
export type PositionEncodingKind = string;

/**
 * A set of predefined position encoding kinds.
 *
 * @since 3.17.0
 */
export namespace PositionEncodingKind {

	/**
	 * Character offsets count UTF-8 code units (e.g bytes).
	 */
	export const UTF8: PositionEncodingKind = 'utf-8';

	/**
	 * Character offsets count UTF-16 code units.
	 *
	 * This is the default and must always be supported
	 * by servers
	 */
	export const UTF16: PositionEncodingKind = 'utf-16';

	/**
	 * Character offsets count UTF-32 code units.
	 *
	 * Implementation note: these are the same as Unicode code points,
	 * so this `PositionEncodingKind` may also be used for an
	 * encoding-agnostic representation of character offsets.
	 */
	export const UTF32: PositionEncodingKind = 'utf-32';
}

Range

文本文档中的范围,表示为(从零开始)开始和结束位置。范围与编辑器中的选区相当。因此,结束位置是排他性的。如果要指定包含包含行尾字符的行的范围,请使用表示下一行开头的结束位置。例如:

{
    start: { line: 5, character: 23 },
    end : { line: 6, character: 0 }
}
interface Range {
	/**
	 * The range's start position.
	 */
	start: Position;

	/**
	 * The range's end position.
	 */
	end: Position;
}

TextDocumentItem

用于将文本文档从客户端传输到服务器的项。

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;
}

文本文档具有语言标识符,用于在处理多种语言时在服务器端标识文档,以避免重新解释文件扩展名。如果文档引用下面列出的编程语言之一,则建议客户端使用这些 ID。

LanguageIdentifier
ABAPabap
Windows Batbat
BibTeXbibtex
Clojureclojure
Coffeescriptcoffeescript
Cc
C++cpp
C#csharp
CSScss
Diffdiff
Dartdart
Dockerfiledockerfile
Elixirelixir
Erlangerlang
F#fsharp
Gitgit-commit and git-rebase
Gogo
Groovygroovy
Handlebarshandlebars
HTMLhtml
Iniini
Javajava
JavaScriptjavascript
JavaScript Reactjavascriptreact
JSONjson
LaTeXlatex
Lessless
Lualua
Makefilemakefile
Markdownmarkdown
Objective-Cobjective-c
Objective-C++objective-cpp
Perlperl
Perl 6perl6
PHPphp
Powershellpowershell
Pugjade
Pythonpython
Rr
Razor (cshtml)razor
Rubyruby
Rustrust
SCSSscss (syntax using curly brackets), sass (indented syntax)
Scalascala
ShaderLabshaderlab
Shell Script (Bash)shellscript
SQLsql
Swiftswift
TypeScripttypescript
TypeScript Reacttypescriptreact
TeXtex
VisualBasic vb
XMLxml
XSLxsl
YAMLyaml

TextDocumentIdentifier

文本文档使用 URI 进行标识。在协议级别,URI 作为字符串传递。相应的 JSON 结构如下所示:

interface TextDocumentIdentifier {
	/**
	 * The text document's URI.
	 */
	uri: DocumentUri;
}

VersionedTextDocumentIdentifier

表示文本文档的特定版本的标识符。此信息通常从客户端流向服务器。

interface VersionedTextDocumentIdentifier extends TextDocumentIdentifier {
	/**
	 * The version number of this document.
	 *
	 * The version number of a document will increase after each change,
	 * including undo/redo. The number doesn't need to be consecutive.
	 */
	version: integer;
}

一个标识符,可选择性地表示文本文档的特定版本。此信息通常从服务器流向客户端。

interface OptionalVersionedTextDocumentIdentifier extends TextDocumentIdentifier {
	/**
	 * The version number of this document. If an optional versioned text document
	 * identifier is sent from the server to the client and the file is not
	 * open in the editor (the server has not received an open notification
	 * before) the server can send `null` to indicate that the version is
	 * known and the content on disk is the master (as specified with document
	 * content ownership).
	 *
	 * The version number of a document will increase after each change,
	 * including undo/redo. The number doesn't need to be consecutive.
	 */
	version: integer | null;
}

TextDocumentPositionParams

是 1.0 中的 TextDocumentPosition,带有内联参数。

用于传递文本文档和该文档内位置的请求中使用的参数文本。在发出文本文档请求时,由客户决定如何将选择转换为位置。例如,客户端可以遵循或忽略选择方向,以使 LSP 请求与内部实现的功能一致。

interface TextDocumentPositionParams {
	/**
	 * The text document.
	 */
	textDocument: TextDocumentIdentifier;

	/**
	 * The position inside the text document.
	 */
	position: Position;
}

DocumentFilter

文档筛选器通过 languageschemepattern 等属性来表示文档。例如,适用于磁盘上的 TypeScript 文件的筛选器。另一个示例是适用于名称为 package.json 的 JSON 文件的筛选器:

{ language: 'typescript', scheme: 'file' }
{ language: 'json', pattern: '**/package.json' }
export interface DocumentFilter {
	/**
	 * A language id, like `typescript`.
	 */
	language?: string;

	/**
	 * A Uri [scheme](#Uri.scheme), like `file` or `untitled`.
	 */
	scheme?: string;

	/**
	 * A glob pattern, like `*.{ts,js}`.
	 *
	 * Glob patterns can have the following syntax:
	 * - `*` to match one 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`)
	 */
	pattern?: string;
}

请注意,要使文档筛选器有效,必须至少设置 languageschemepattern 的一个属性。为了保持类型定义的简单性,所有属性都标记为可选。

文档选择器是一个或多个文档筛选器的组合。

export type DocumentSelector = DocumentFilter[];

TextEdit & AnnotatedTextEdit

3.16 新版功能: 支持 AnnotatedTextEdit

适用于文本文档的文本编辑。

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;
}

从 3.16.0 开始,还有带注释的文本编辑的概念,它支持向文本编辑添加注释。批注可以添加描述文本编辑更改的信息。

/**
 * Additional information that describes document changes.
 *
 * @since 3.16.0
 */
export interface ChangeAnnotation {
	/**
	 * A human-readable string describing the actual change. The string
	 * is rendered prominent 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 prominent in
	 * the user interface.
	 */
	description?: string;
}

通常,客户端会提供选项,以根据与更改关联的注释对更改进行分组。为了在协议中支持这一点,编辑或资源操作使用标识符(ChangeAnnotationIdentifier) 引用 更改注释(ChangeAnnotation),而不是直接使用 更改注释(ChangeAnnotation) 字面量。这允许服务器在多个编辑或资源操作中使用相同的注释,然后允许客户端将操作分组到该更改注释(ChangeAnnotation)下。实际的更改注释及其标识符由 WorkspaceEdit 通过新属性 changeAnnotations 进行管理。

/**
 * An identifier referring to a change annotation managed by a workspace
 * edit.
 *
 * @since 3.16.0.
 */
export type ChangeAnnotationIdentifier = string;

/**
 * A special text edit with an additional change annotation.
 *
 * @since 3.16.0.
 */
export interface AnnotatedTextEdit extends TextEdit {
	/**
	 * The actual annotation identifier.
	 */
	annotationId: ChangeAnnotationIdentifier;
}

TextEdit[]

复杂的文本操作用 TextEditAnnotatedTextEdit 的数组来描述,表示对文档的单个更改。

所有文本编辑范围均是指在应用所以更改前的文本文档中的位置。因此,它们将文档从状态 S1 移动到 S2,而不描述任何中间状态。文本编辑范围绝不能重叠,这意味着原始文档的任何部分都不得由多个编辑操作。但是,多个编辑可能具有相同的起始位置:多个插入,或任意数量的插入,然后进行一次删除或替换编辑。如果多个插入具有相同的位置,则数组中的顺序定义插入的字符串在生成的文本中的显示顺序。

TextDocumentEdit

3.16 新版功能: 支持 AnnotatedTextEdit, 此支持由客户端功能 workspace.workspaceEdit.changeAnnotationSupport 守护,如果客户端未发出该功能的信号,则服务器不应将 AnnotatedTextEdit 字面量发送回客户端。

描述单个文本文档上的文本更改。文本文档被引用为 OptionalVersionedTextDocumentIdentifier,以允许客户端在应用编辑之前检查文本文档版本。TextDocumentEdit 描述版本 Si 上的所有更改,并在应用这些更改后将文档移动到版本 Si+1。因此,TextDocumentEdit 的创建者无需对编辑数组进行排序或进行任何排序。但是,编辑必须不重叠。

export 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`
	 */
	edits: (TextEdit | AnnotatedTextEdit)[];
}

Location

表示资源内的位置,如文本文件中的一行。

interface Location {
	uri: DocumentUri;
	range: Range;
}

表示源位置和目标位置之间的链接。

interface LocationLink {

	/**
	 * Span of the origin of this link.
	 *
	 * Used as the underlined span for mouse interaction. Defaults to the word
	 * range at the mouse position.
	 */
	originSelectionRange?: Range;

	/**
	 * The target resource identifier of this link.
	 */
	targetUri: DocumentUri;

	/**
	 * The full target range of this link. If the target for example is a symbol
	 * then target range is the range enclosing this symbol not including
	 * leading/trailing whitespace but everything else like comments. This
	 * information is typically used to highlight the range in the editor.
	 */
	targetRange: Range;

	/**
	 * The range that should be selected and revealed when this link is being
	 * followed, e.g the name of a function. Must be contained by the
	 * `targetRange`. See also `DocumentSymbol#range`
	 */
	targetSelectionRange: Range;
}

Diagnostic

表示诊断,如编译器错误或警告。诊断对象仅在资源范围内有效。

export interface Diagnostic {
	/**
	 * The range at which the message applies.
	 */
	range: Range;

	/**
	 * The diagnostic's severity. Can be omitted. If omitted it is up to the
	 * client to interpret diagnostics as error, warning, info or hint.
	 */
	severity?: DiagnosticSeverity;

	/**
	 * The diagnostic's code, which might appear in the user interface.
	 */
	code?: integer | string;

	/**
	 * An optional property to describe the error code.
	 *
	 * @since 3.16.0
	 */
	codeDescription?: CodeDescription;

	/**
	 * A human-readable string describing the source of this
	 * diagnostic, e.g. 'typescript' or 'super lint'.
	 */
	source?: string;

	/**
	 * The diagnostic's message.
	 */
	message: string;

	/**
	 * Additional metadata about the diagnostic.
	 *
	 * @since 3.15.0
	 */
	tags?: DiagnosticTag[];

	/**
	 * An array of related diagnostic information, e.g. when symbol-names within
	 * a scope collide all definitions can be marked via this property.
	 */
	relatedInformation?: DiagnosticRelatedInformation[];

	/**
	 * A data entry field that is preserved between a
	 * `textDocument/publishDiagnostics` notification and
	 * `textDocument/codeAction` request.
	 *
	 * @since 3.16.0
	 */
	data?: unknown;
}
export namespace DiagnosticSeverity {
	/**
	 * Reports an error.
	 */
	export const Error: 1 = 1;
	/**
	 * Reports a warning.
	 */
	export const Warning: 2 = 2;
	/**
	 * Reports an information.
	 */
	export const Information: 3 = 3;
	/**
	 * Reports a hint.
	 */
	export const Hint: 4 = 4;
}

export type DiagnosticSeverity = 1 | 2 | 3 | 4;
/**
 * The diagnostic tags.
 *
 * @since 3.15.0
 */
export namespace DiagnosticTag {
	/**
	 * Unused or unnecessary code.
	 *
	 * Clients are allowed to render diagnostics with this tag faded out
	 * instead of having an error squiggle.
	 */
	export const Unnecessary: 1 = 1;
	/**
	 * Deprecated or obsolete code.
	 *
	 * Clients are allowed to rendered diagnostics with this tag strike through.
	 */
	export const Deprecated: 2 = 2;
}

export type DiagnosticTag = 1 | 2;
/**
 * Represents a related message and source code location for a diagnostic.
 * This should be used to point to code locations that cause or are related to
 * a diagnostics, e.g when duplicating a symbol in a scope.
 */
export interface DiagnosticRelatedInformation {
	/**
	 * The location of this related diagnostic information.
	 */
	location: Location;

	/**
	 * The message of this related diagnostic information.
	 */
	message: string;
}
/**
 * Structure to capture a description for an error code.
 *
 * @since 3.16.0
 */
export interface CodeDescription {
	/**
	 * An URI to open with more information about the diagnostic error.
	 */
	href: URI;
}

Command

表示对命令的引用。提供一个标题,该标题将用于表示 UI 中的命令。命令由字符串标识符标识。如果客户端和服务器提供相应的功能,则建议处理命令的方法是在服务器端执行命令。或者,工具扩展代码可以处理该命令。该协议当前未指定一组已知命令。

interface Command {
	/**
	 * Title of the command, like `save`.
	 */
	title: string;
	/**
	 * The identifier of the actual command handler.
	 */
	command: string;
	/**
	 * Arguments that the command handler should be
	 * invoked with.
	 */
	arguments?: LSPAny[];
}

MarkupContent

MarkupContent 表示一个字符串值,该值的内容可以用不同的格式表示。目前支持 plaintextMarkdown 格式。MarkupContent 通常用于结果文本(如 CompletionItemSignatureInformation)的文档属性。如果格式为 Markdown,则内容应遵循 GitHub Flavored Markdown 规范。

/**
 * Describes the content type that a client supports in various
 * result literals like `Hover`, `ParameterInfo` or `CompletionItem`.
 *
 * Please note that `MarkupKinds` must not start with a `$`. This kinds
 * are reserved for internal usage.
 */
export namespace MarkupKind {
	/**
	 * Plain text is supported as a content format
	 */
	export const PlainText: 'plaintext' = 'plaintext';

	/**
	 * Markdown is supported as a content format
	 */
	export const Markdown: 'markdown' = 'markdown';
}
export type MarkupKind = 'plaintext' | 'markdown';
/**
 * A `MarkupContent` literal represents a string value which content is
 * interpreted base on its kind flag. Currently the protocol supports
 * `plaintext` and `markdown` as markup kinds.
 *
 * If the kind is `markdown` then the value can contain fenced code blocks like
 * in GitHub issues.
 *
 * Here is an example how such a string can be constructed using
 * JavaScript / TypeScript:
 * ```typescript
 * let markdown: MarkdownContent = {
 * 	kind: MarkupKind.Markdown,
 * 	value: [
 * 		'# Header',
 * 		'Some text',
 * 		'```typescript',
 * 		'someCode();',
 * 		'```'
 * 	].join('\n')
 * };
 * ```
 *
 * *Please Note* that clients might sanitize the return markdown. A client could
 * decide to remove HTML from the markdown to avoid script execution.
 */
export interface MarkupContent {
	/**
	 * The type of the Markup
	 */
	kind: MarkupKind;

	/**
	 * The content itself
	 */
	value: string;
}

此外,客户端应通过版本 3.16.0 中引入的客户端能力 general.markdown 向他们正在使用的 Markdown 解析器发出信号,定义如下:

/**
 * Client capabilities specific to the used markdown parser.
 *
 * @since 3.16.0
 */
export interface MarkdownClientCapabilities {
	/**
	 * The name of the parser.
	 */
	parser: string;

	/**
	 * The version of the parser.
	 */
	version?: string;

	/**
	 * A list of HTML tags that the client allows / supports in
	 * Markdown.
	 *
	 * @since 3.17.0
	 */
	allowedTags?: string[];
}

客户端目前使用的已知 Markdown 解析器有:

ParserVersionDocumentation
marked1.1.0Marked Documentation
Python-Markdown3.2.2Python-Markdown Documentation

文件资源更改

3.13 新版功能.从版本 3.16 开始,文件资源更改可以携带额外的属性 changeAnnotation,以更详细地描述实际更改。客户端是否支持 更改注释(ChangeAnnotation) 由客户端功能 workspace.workspaceEdit.changeAnnotationSupport 守护。

文件资源更改允许服务器通过客户端创建、重命名和删除文件和文件夹。请注意,这些名称谈论的是文件,但这些操作应该适用于文件和文件夹。这与语言服务器协议中的其他命名一致(请参阅可以监视文件和文件夹的文件观察程序)。相应的变更类型如下所示:

/**
 * Options to create a file.
 */
export interface CreateFileOptions {
	/**
	 * Overwrite existing file. Overwrite wins over `ignoreIfExists`
	 */
	overwrite?: boolean;

	/**
	 * Ignore if exists.
	 */
	ignoreIfExists?: boolean;
}
/**
 * Create file operation
 */
export interface CreateFile {
	/**
	 * A create
	 */
	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;
}
/**
 * Rename file options
 */
export interface RenameFileOptions {
	/**
	 * Overwrite target if existing. Overwrite wins over `ignoreIfExists`
	 */
	overwrite?: boolean;

	/**
	 * Ignores if target exists.
	 */
	ignoreIfExists?: boolean;
}
/**
 * Rename file operation
 */
export interface RenameFile {
	/**
	 * A rename
	 */
	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;
}
/**
 * Delete file options
 */
export interface DeleteFileOptions {
	/**
	 * Delete the content recursively if a folder is denoted.
	 */
	recursive?: boolean;

	/**
	 * Ignore the operation if the file doesn't exist.
	 */
	ignoreIfNotExists?: boolean;
}
/**
 * Delete file operation
 */
export interface DeleteFile {
	/**
	 * A delete
	 */
	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;
}

WorkspaceEdit

工作区编辑表示对工作区中管理的许多资源的更改。编辑应提供 changesdocumentChanges。如果客户端可以处理版本化文档编辑,并且存在 documentChanges,则后者优先于 changes

从版本 3.13.0 开始,工作区编辑也可以包含资源操作(创建、删除或重命名文件和文件夹)。如果存在资源操作,客户端需要按照提供操作的顺序执行操作。例如,工作区编辑可以包含以下两个更改:(1)创建文件a.txt和 (2)文本文档编辑,将文本插入文件a.txt。无效的序列(例如(1)删除文件a.txt和(2)在文件 a.txt 中插入文本)将导致操作失败。客户端能力描述了客户端如何从故障中恢复:workspace.workspaceEdit.failureHandling

export 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 `TextDocumentEdit`s 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 `TextDocumentEdit`s 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 `TextEdit`s
	 * using the `changes` property are supported.
	 */
	documentChanges?: (
		TextDocumentEdit[] |
		(TextDocumentEdit | CreateFile | RenameFile | DeleteFile)[]
	);

	/**
	 * A map of change annotations that can be referenced in
	 * `AnnotatedTextEdit`s 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;
	};
}

WorkspaceEditClientCapabilities

3.13 新版功能:ResourceOperationKindFailureHandlingKind 以及客户端功能 workspace.workspaceEdit.resourceOperations 以及 workspace.workspaceEdit.failureHandling

工作区编辑的能力随着时间的推移而发展。客户端可以使用以下客户端能力描述其支持:

Client Capability:

  • 属性路径: workspace.workspaceEdit

  • 属性类型: WorkspaceEditClientCapabilities 定义如下:

export interface WorkspaceEditClientCapabilities {
	/**
	 * The client supports versioned document changes in `WorkspaceEdit`s
	 */
	documentChanges?: boolean;

	/**
	 * The resource operations the client supports. Clients should at least
	 * support 'create', 'rename' and 'delete' 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?: {
		/**
		 * 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;
	};
}
/**
 * The kind of resource operations supported by the client.
 */
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';
}
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 transactional. 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 transactional. 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';
}

Work Done Progress(工作完成进度)

从版本 3.15.0 开始

使用通用的 $/progress 通知报告已完成的工作进度。已完成工作进度通知的值有效负载可以有三种不同的形式。

Work Done Progress Begin

若要开始进度报告,必须发送包含以下有效负载的 $/progress 通知:

export interface WorkDoneProgressBegin {

	kind: 'begin';

	/**
	 * Mandatory title of the progress operation. Used to briefly inform about
	 * the kind of operation being performed.
	 *
	 * Examples: "Indexing" or "Linking dependencies".
	 */
	title: string;

	/**
	 * Controls if a cancel button should show to allow the user to cancel the
	 * long running operation. Clients that don't support cancellation are
	 * allowed to ignore the setting.
	 */
	cancellable?: boolean;

	/**
	 * Optional, more detailed associated progress message. Contains
	 * complementary information to the `title`.
	 *
	 * Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
	 * If unset, the previous progress message (if any) is still valid.
	 */
	message?: string;

	/**
	 * Optional progress percentage to display (value 100 is considered 100%).
	 * If not provided infinite progress is assumed and clients are allowed
	 * to ignore the `percentage` value in subsequent in report notifications.
	 *
	 * The value should be steadily rising. Clients are free to ignore values
	 * that are not following this rule. The value range is [0, 100]
	 */
	percentage?: uinteger;
}

Work Done Progress Report

使用以下有效负载报告进度:

export interface WorkDoneProgressReport {

	kind: 'report';

	/**
	 * Controls enablement state of a cancel button. This property is only valid
	 * if a cancel button got requested in the `WorkDoneProgressBegin` payload.
	 *
	 * Clients that don't support cancellation or don't support control the
	 * button's enablement state are allowed to ignore the setting.
	 */
	cancellable?: boolean;

	/**
	 * Optional, more detailed associated progress message. Contains
	 * complementary information to the `title`.
	 *
	 * Examples: "3/25 files", "project/src/module2", "node_modules/some_dep".
	 * If unset, the previous progress message (if any) is still valid.
	 */
	message?: string;

	/**
	 * Optional progress percentage to display (value 100 is considered 100%).
	 * If not provided infinite progress is assumed and clients are allowed
	 * to ignore the `percentage` value in subsequent in report notifications.
	 *
	 * The value should be steadily rising. Clients are free to ignore values
	 * that are not following this rule. The value range is [0, 100]
	 */
	percentage?: uinteger;
}

Work Done Progress End

使用以下有效负载发出进度报告结束的信号:

export interface WorkDoneProgressEnd {

	kind: 'end';

	/**
	 * Optional, a final message indicating to for example indicate the outcome
	 * of the operation.
	 */
	message?: string;
}

Initiating Work Done Progress

可以通过两种不同的方式启动 Work Done progress 进度:

  • 由请求的发送方(主要是客户端)在请求参数中预定义 workDoneToken 属性。本文将这种进度称为 客户端启动的进度

  • 由服务器使用请求 window/workDoneProgress/create。本文将这种进度称为服务器启动的进度

客户端启动的进度

假设客户端向服务器发送 textDocument/reference 请求,并且客户端接受有关该请求的 Work Done Progress

为了向服务器发出此信号,客户端为请求参数添加 workDoneToken 属性。像这样:

{
	"textDocument": {
		"uri": "file:///folder/file.ts"
	},
	"position": {
		"line": 9,
		"character": 5
	},
	"context": {
		"includeDeclaration": true
	},
	// The token used to report work done progress.
	"workDoneToken": "1d546990-40a3-4b77-b134-46622995f6ae"
}

参数属性的相应类型定义如下所示:

export interface WorkDoneProgressParams {
	/**
	 * An optional token that a server can use to report work done progress.
	 */
	workDoneToken?: ProgressToken;
}

服务器使用 workDoneToken 报告特定 textDocument/reference 的进度。对于上述请求,$/progress 通知参数如下所示:

{
	"token": "1d546990-40a3-4b77-b134-46622995f6ae",
	"value": {
		"kind": "begin",
		"title": "Finding references for A#foo",
		"cancellable": false,
		"message": "Processing file X.ts",
		"percentage": 0
	}
}

仅当请求未发送响应时,请求的参数中的 workDoneToken 属性接收的 token 才有效。取消 Work Done Progress 只需取消相应的请求即可。

没有特定的客户端功能来指示客户端是否将每个请求发送进度令牌。这样做的原因是,这在许多客户端中不是静态的,甚至可能对于同一请求类型的每个请求实例而更改。因此,该功能在每个请求实例上都通过存在 workDoneToken 属性发出信号。

为了避免客户端在发送请求之前设置进度监视器用户界面,但服务器实际上并未报告任何进度,服务器需要在相应的 服务器能力(Server Capability) 中发出支持常规 Work Done Progress 的信号。对于上述查找引用示例,服务器将通过在服务器功能中设置 referencesProvider 属性来发出此类支持的信号,如下所示:

{
	"referencesProvider": {
		"workDoneProgress": true
	}
}

服务器能力(Server Capability) 的相应类型定义如下所示:

export interface WorkDoneProgressOptions {
	workDoneProgress?: boolean;
}

服务器启动的进度

服务器还可以使用 window/workDoneProgress/create 请求启动进度报告。如果服务器需要报告请求之外的进度(例如,服务器需要重新索引数据库),这将非常有用。然后,可以使用 workDoneToken 与客户端启动的进度相同的通知来报告进度。创建请求中提供的令牌只能使用一次(例如,只应向其发送一个开始、多个报告和一个结束通知)。

为了保持协议向后兼容,允许服务器使用 window/workDoneProgress/create 请求的前提是客户端使用 客户端能力(Client capability) window.workDoneProgress 发出相应的支持信号,其定义如下:

    /**
     * Window specific client capabilities.
     */
    window?: {
        /**
         * Whether client supports server initiated progress using the
         * `window/workDoneProgress/create` request.
         */
        workDoneProgress?: boolean;
    };

Partial Result Progress(部分结果进度)

从版本 3.15.0 开始

部分结果也使用通用的 $/progress 通知进行报告。在大多数情况下,部分结果进度通知的有效负载值与最终结果相同。例如,workspace/symbol 请求,有 SymbolInformation[] | WorkspaceSymbol[] 作为结果类型。因此,部分结果的类型也是 SymbolInformation[] | WorkspaceSymbol[]。客户端是否接受请求的部分结果通知是通过向请求参数添加 partialResultToken 来指示的。例如,同时支持已完成工作和部分结果进度的 textDocument/reference 请求可能如下所示:

{
	"textDocument": {
		"uri": "file:///folder/file.ts"
	},
	"position": {
		"line": 9,
		"character": 5
	},
	"context": {
		"includeDeclaration": true
	},
	// The token used to report work done progress.
	"workDoneToken": "1d546990-40a3-4b77-b134-46622995f6ae",
	// The token used to report partial result progress.
	"partialResultToken": "5f6f349e-4f81-4a3b-afff-ee04bff96804"
}

然后,使用 partialResultToken 报告查找引用请求的部分结果。

如果服务器通过相应的 $/progress 报告部分结果,则必须使用 n 个 $/progress 通知报告整个结果。每个 $/progress 通知都会将项目附加到结果中。就结果值而言,最终响应必须为空。这避免了对最终结果应如何解释的混淆,例如,作为另一个部分结果或作为替换结果。

如果响应错误,则应按如下方式处理提供的部分结果:

  • code 等于 RequestCancelled: 客户端可以自由使用提供的结果,但应明确请求已取消并且可能不完整。

  • 在所有其他情况下,不应使用提供的部分结果。

PartialResultParams

用于传递部分结果标记的参数:

export interface PartialResultParams {
	/**
	 * An optional token that a server can use to report partial results (e.g.
	 * streaming) to the client.
	 */
	partialResultToken?: ProgressToken;
}

TraceValue

TraceValue 表示服务器使用 $/logTrace 通知系统地报告其执行跟踪的详细程度。初始跟踪值由客户端在初始化时设置,以后可以使用 $/setTrace 通知进行修改。

export type TraceValue = 'off' | 'messages' | 'verbose';