LSP(语言服务协议)全文翻译!!!

768 阅读40分钟

语言服务协议介绍

语言服务协议 (LSP) 定义了编辑器或 IDE 与语言服务器之间使用的协议,该服务器提供自动完成、转到定义、查找所有引用等语言功能。语言服务器索引格式(LSIF,发音类似于“else if”)的目标是支持开发工具或 Web UI 中的丰富代码导航,而无需源代码的本地副本。

协议原文:Specification (microsoft.github.io)

如果您对语言服务器感兴趣,您可以访问 语言服务器开发指南 (lsp.fosp.cc)

基础协议

基础协议由标头和内容组成。标头和内容之间使用 \r\n 分隔。

标头

标头由标头字段组成。每个标头字段包含一个名称和值,用 : (冒号和空格)分隔。标头字段的结构符合 HTTP 语义。每个标头字段都以 \r\n 结尾。考虑到每个标头字段和整个标头都以 \r\n 结尾,并且至少有一个标头字段,这意味着在内容之前始终都有两个 \r\n

目前支持的标头字段:

标头字段名值类型描述
Content-Lengthnumber内容部分以字节计算的长度,这个标头是必须的。
Content-Typestring内容部分的 MIME 类型。默认是 application/vscode-jsonrpc; charset=utf-8

标头部分使用 ascii 编码进行编码。这包括分隔标题和内容部分的 \r\n

内容

包含消息实际的内容。消息的内容部分使用 JSON-RPC 来描述请求响应通知。内容部分使用 Content-Type 字段中提供的字符集进行编码。它默认为 utf-8,这是目前唯一支持的编码。如果服务器客户端收到的标头编码与 utf-8 不同,则应响应错误。

该协议的早期版本使用字符串常量 utf8,根据规范,它不是正确的编码常量。为了向后兼容,强烈建议客户端和服务器将字符串 utf8 视为 utf-8

示例:

Content-Length: ...\r\n
\r\n
{
	"jsonrpc": "2.0",
	"id": 1,
	"method": "textDocument/completion",
	"params": {
		...
	}
}

基础协议 JSON 结构

以下 TypeScript 定义描述了基本 JSON-RPC 协议:

基础类型

该协议对整数、无符号整数、十进制数、对象和数组使用以下定义:

/**
 * Defines an integer number in the range of -2^31 to 2^31 - 1.
 */
export type integer = number;
/**
 * Defines an unsigned integer number in the range of 0 to 2^31 - 1.
 */
export type uinteger = number;
/**
 * Defines a decimal number. Since decimal numbers are very
 * rare in the language server specification we denote the
 * exact range with every decimal using the mathematics
 * interval notation (e.g. [0, 1] denotes all decimals d with
 * 0 <= d <= 1.
 */
export type decimal = number;
/**
 * The LSP any type
 *
 * @since 3.17.0
 */
export type LSPAny = LSPObject | LSPArray | string | integer | uinteger |
	decimal | boolean | null;
/**
 * LSP object definition.
 *
 * @since 3.17.0
 */
export type LSPObject = { [key: string]: LSPAny };
/**
 * LSP arrays.
 *
 * @since 3.17.0
 */
export type LSPArray = LSPAny[];

抽象消息

由 JSON-RPC 定义的常规消息。语言服务器协议始终使用 "2.0" 作为 jsonrpc 版本。

interface Message {
	jsonrpc: string;
}

请求消息

用于描述客户端和服务器之间的请求的请求消息(可以由客户端发出,也可以由服务端发出)。每个已处理的请求都必须将响应发送回请求的发送方。

interface RequestMessage extends Message {

	/**
	 * The request id.
	 */
	id: integer | string;

	/**
	 * The method to be invoked.
	 */
	method: string;

	/**
	 * The method's params.
	 */
	params?: array | object;
}

响应消息

响应消息作为请求的结果发送。如果请求未提供结果值,请求的接收方仍需要返回响应消息以符合 JSON-RPC 规范。在这种情况下,应将 ResponseMessageresult 属性设置为 null 以表示请求成功。

interface ResponseMessage extends Message {
	/**
	 * The request id.
	 */
	id: integer | string | null;

	/**
	 * The result of a request. This member is REQUIRED on success.
	 * This member MUST NOT exist if there was an error invoking the method.
	 */
	result?: string | number | boolean | array | object | null;

	/**
	 * The error object in case a request fails.
	 */
	error?: ResponseError;
}
interface ResponseError {
	/**
	 * A number indicating the error type that occurred.
	 */
	code: integer;

	/**
	 * A string providing a short description of the error.
	 */
	message: string;

	/**
	 * A primitive or structured value that contains additional
	 * information about the error. Can be omitted.
	 */
	data?: string | number | boolean | array | object | null;
}
export namespace ErrorCodes {
	// Defined by JSON-RPC
	export const ParseError: integer = -32700;
	export const InvalidRequest: integer = -32600;
	export const MethodNotFound: integer = -32601;
	export const InvalidParams: integer = -32602;
	export const InternalError: integer = -32603;

	/**
	 * This is the start range of JSON-RPC reserved error codes.
	 * It doesn't denote a real error code. No LSP error codes should
	 * be defined between the start and end range. For backwards
	 * compatibility the `ServerNotInitialized` and the `UnknownErrorCode`
	 * are left in the range.
	 *
	 * @since 3.16.0
	 */
	export const jsonrpcReservedErrorRangeStart: integer = -32099;
	/** @deprecated use jsonrpcReservedErrorRangeStart */
	export const serverErrorStart: integer = jsonrpcReservedErrorRangeStart;

	/**
	 * Error code indicating that a server received a notification or
	 * request before the server has received the `initialize` request.
	 */
	export const ServerNotInitialized: integer = -32002;
	export const UnknownErrorCode: integer = -32001;

	/**
	 * This is the end range of JSON-RPC reserved error codes.
	 * It doesn't denote a real error code.
	 *
	 * @since 3.16.0
	 */
	export const jsonrpcReservedErrorRangeEnd = -32000;
	/** @deprecated use jsonrpcReservedErrorRangeEnd */
	export const serverErrorEnd: integer = jsonrpcReservedErrorRangeEnd;

	/**
	 * This is the start range of LSP reserved error codes.
	 * It doesn't denote a real error code.
	 *
	 * @since 3.16.0
	 */
	export const lspReservedErrorRangeStart: integer = -32899;

	/**
	 * A request failed but it was syntactically correct, e.g the
	 * method name was known and the parameters were valid. The error
	 * message should contain human readable information about why
	 * the request failed.
	 *
	 * @since 3.17.0
	 */
	export const RequestFailed: integer = -32803;

	/**
	 * The server cancelled the request. This error code should
	 * only be used for requests that explicitly support being
	 * server cancellable.
	 *
	 * @since 3.17.0
	 */
	export const ServerCancelled: integer = -32802;

	/**
	 * The server detected that the content of a document got
	 * modified outside normal conditions. A server should
	 * NOT send this error code if it detects a content change
	 * in it unprocessed messages. The result even computed
	 * on an older state might still be useful for the client.
	 *
	 * If a client decides that a result is not of any use anymore
	 * the client should cancel the request.
	 */
	export const ContentModified: integer = -32801;

	/**
	 * The client has canceled a request and a server has detected
	 * the cancel.
	 */
	export const RequestCancelled: integer = -32800;

	/**
	 * This is the end range of LSP reserved error codes.
	 * It doesn't denote a real error code.
	 *
	 * @since 3.16.0
	 */
	export const lspReservedErrorRangeEnd: integer = -32800;
}

通知消息

通知消息。已处理的通知消息不得发回响应。它们像事件一样工作。

interface NotificationMessage extends Message {
	/**
	 * The method to be invoked.
	 */
	method: string;

	/**
	 * The notification's params.
	 */
	params?: array | object;
}

$通知和请求

方法以 $/ 开头的通知和请求是依赖于协议(由客户端和服务器自行约定)实现的消息,可能无法在所有客户端或服务器中实现。例如,如果服务器实现使用单线程同步编程语言,则服务器几乎无法对 $/cancelRequest 通知做出反应。如果服务器或客户端收到以 $/ 开头的通知,则可以忽略该通知。如果服务器或客户端收到以 $/ 开头的请求,则必须使用错误代码 MethodNotFound (例如 -32601)响应请求。

支持取消

基本协议支持取消请求。若要取消请求,将发送具有以下属性的通知消息:

NotificationMessage:

  • method: "$/cancelRequest"
  • params: CancelParams 定义如下:
interface CancelParams {
	/**
	 * The request id to cancel.
	 */
	id: integer | string;
}

已取消的请求仍需要从服务器返回并发送回响应。它不能保持打开/挂起。这符合 JSON-RPC 协议,该协议要求每个请求都发送回响应。此外,它还允许在取消时返回部分结果。如果请求在取消时返回错误响应,建议将错误代码设置为 ErrorCodes.RequestCancelled。

支持进度

Since version 3.15.0

基本协议还支持以通用方式报告进度。此机制可用于报告任何类型的进度,包括已完成的工作进度(通常用于使用进度条在用户界面中报告进度)和部分结果进度,以支持结果流式处理。

进度通知具有以下属性:

NotificationMessage:

  • method: "$/progress"
  • params: ProgressParams 定义如下:
interface ProgressParams<T> {
	/**
	 * The progress token provided by the client or server.
	 */
	token: ProgressToken;

	/**
	 * The progress data.
	 */
	value: T;
}
type ProgressToken = integer | string;

泛型 T 由协议约定

针对 token 报告进度。token 与请求 id 不同,它允许带外数据报告进度。

语言服务协议

语言服务器协议定义了一组 JSON-RPC 请求、响应和通知消息,这些消息使用 基本协议 进行交换。本节开始介绍协议中使用的基本 JSON 结构。本文档使用 TypeScript 在严格模式下的 interface 来描述这些内容。这意味着,例如,必须显式列出 null 值,并且必须列出必需属性,即使可能存在窜改值。基于基本的 JSON 结构,描述了实际请求及其响应和通知。

例如,从客户端向服务器发送请求,以请求文本文档中特定位置的符号的悬停提示。请求的方法将是 textDocument/hover,其参数如下:

interface HoverParams {
	textDocument: string; /** The text document's URI in string form */
	position: { line: uinteger; character: uinteger; };
}

请求的结果是将要呈现的悬浮提示。在其简单形式中,它可以是一个字符串。所以结果如下所示:

interface HoverResult {
	value: string;
}

另请注意,响应返回值为 null 表示没有结果。它不会告诉客户端重新发送请求。

通常,语言服务协议支持 JSON-RPC 消息,但是,我们约定传递给 请求通知消息的参数为对象类型(如果传递的话)。但是,这并不禁止在自定义消息中使用数组参数类型。

该协议目前假定一个服务器服务于一个工具。该协议目前不支持在不同工具之间共享一个服务器。这种共享将需要额外的协议,例如锁定文档以支持并发编辑。

能力 (Capabilities)

并非每个语言服务器都可以支持协议定义的所有功能。LSP 因此提供了 capabilities。一个能力组对应一套语言功能。开发工具和语言服务器使用 capabilities 宣布其支持的功能。例如,例如,服务器宣布它可以处理 textDocument/hover 请求,但可能无法处理 workspace/symbol 请求。同样,开发工具宣布其能够在保存文档之前提供关于保存的通知,以便服务器可以在保存之前计算文本以格式化编辑的文档。

在初始化 initialize 请求期间,客户端和服务器之间交换 capabilities 的集合。

请求、响应和通知的顺序

对请求的响应的发送顺序应与请求在服务器端或客户端上出现的顺序大致相同。因此,例如,如果服务器收到 textDocument/completion 请求,然后收到 textDocument/signatureHelp 请求,它通常会首先返回 textDocument/completion 的响应,然后返回 textDocument/signatureHelp 的响应。

但是,服务器可能决定使用并行执行策略,并且可能希望以与收到请求不同的顺序返回响应。只要此重新排序不影响响应的正确性,服务器就可以这样做。例如,允许对 textDocument/completiontextDocument/signatureHelp 的结果重新排序,因为这些请求中的每一个通常不会影响另一个请求的输出。另一方面,服务器很可能不应该对 textDocument/definitiontextDocument/rename 请求重新排序,因为执行后者可能会影响前者的结果。

消息文档

和前面一样,LSP 定义了一组请求、响应和通知。在后面对这些消息的描述中,每一项都使用以下格式进行描述:

  • 描述请求的标头

  • 一个可选的 客户端能力(Client capability) 部分,描述了请求的客户端能力。这包括客户端能力属性路径和 json 结构。

  • 一个可选的 服务端能力(Server Capability) 部分,描述了请求的服务端能力,这包括服务端能力属性路径和 json 结构。客户端应该忽略他们不理解的服务端功能(例如,在这种情况下,初始化请求不应该失败)。

  • 一个可选的 注册选项(Registration Options) 部分,描述请求或通知支持动态功能注册时的注册选项。请参阅 注册注销 请求,详细了解其工作原理。

  • 一个 请求(Request) 部分描述发送请求的格式。该方法是标识请求的字符串,参数使用 TypeScript 接口进行记录。它还记录了请求是否支持已完成的工作进度和部分结果进度。

  • 一个 响应(Response) 部分描述了响应的格式。结果项描述成功时返回的数据。可选的部分结果项描述部分结果通知的返回数据。error.data 描述发生错误时返回的数据。请记住,如果失败,响应已包含 error.codeerror.message 字段。仅当协议强制使用某些错误代码或消息时,才指定这些字段。如果服务器可以自由决定这些值,则此处未列出这些值。

基础 JSON 结构

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

URI

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

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

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

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

LocationLink

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

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

生命周期消息

当前的协议规范定义了服务器的生命周期由客户端(例如VS Code或Emacs等工具)管理。由客户端决定何时启动(进程方面)以及何时关闭服务器。

initialize 请求

初始化请求作为从客户端发送到服务器的第一个请求。如果服务器在初始化请求之前收到请求或通知,则应按如下方式操作:

  • 对于请求,响应应为 code: -32002 的错误。服务器可以选择处理其中的消息。

  • 除退出通知外,应丢弃通知。这将允许在没有初始化请求的情况下退出服务器。

在服务器使用 InitializeResult 响应初始化请求之前,客户端不得向服务器发送任何其他请求或通知。此外,在使用 InitializeResult 做出响应之前,不允许服务器向客户端发送任何请求或通知,但在初始化请求期间,允许服务器向客户端发送通知 window/showMessagewindow/logMessagetelemetry/event 以及 window/showMessageRequest 请求。如果客户端在初始化参数中设置了进度令牌(例如属性 workDoneToken),则还允许服务器使用从服务器发送到客户端的 $/progress 通知来使用该令牌(并且仅使用该令牌)。

初始化请求只能发送一次。

请求(Request):

  • method: "initialize"
  • params: InitializeParams 定义如下:
interface InitializeParams extends WorkDoneProgressParams {
	/**
	 * The process Id of the parent process that started the server. Is null if
	 * the process has not been started by another process. If the parent
	 * process is not alive then the server should exit (see exit notification)
	 * its process.
	 */
	processId: integer | null;

	/**
	 * Information about the client
	 *
	 * @since 3.15.0
	 */
	clientInfo?: {
		/**
		 * The name of the client as defined by the client.
		 */
		name: string;

		/**
		 * The client's version as defined by the client.
		 */
		version?: string;
	};

	/**
	 * The locale the client is currently showing the user interface
	 * in. This must not necessarily be the locale of the operating
	 * system.
	 *
	 * Uses IETF language tags as the value's syntax
	 * (See https://en.wikipedia.org/wiki/IETF_language_tag)
	 *
	 * @since 3.16.0
	 */
	locale?: string;

	/**
	 * The rootPath of the workspace. Is null
	 * if no folder is open.
	 *
	 * @deprecated in favour of `rootUri`.
	 */
	rootPath?: string | null;

	/**
	 * The rootUri of the workspace. Is null if no
	 * folder is open. If both `rootPath` and `rootUri` are set
	 * `rootUri` wins.
	 *
	 * @deprecated in favour of `workspaceFolders`
	 */
	rootUri: DocumentUri | null;

	/**
	 * User provided initialization options.
	 */
	initializationOptions?: LSPAny;

	/**
	 * The capabilities provided by the client (editor or tool)
	 */
	capabilities: ClientCapabilities;

	/**
	 * The initial trace setting. If omitted trace is disabled ('off').
	 */
	trace?: TraceValue;

	/**
	 * The workspace folders configured in the client when the server starts.
	 * This property is only available if the client supports workspace folders.
	 * It can be `null` if the client supports workspace folders but none are
	 * configured.
	 *
	 * @since 3.6.0
	 */
	workspaceFolders?: WorkspaceFolder[] | null;
}

TextDocumentClientCapabilities

TextDocumentClientCapabilities 定义编辑器/工具在文本文档上提供的功能。

/**
 * Text document specific client capabilities.
 */
export interface TextDocumentClientCapabilities {

	synchronization?: TextDocumentSyncClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/completion` request.
	 */
	completion?: CompletionClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/hover` request.
	 */
	hover?: HoverClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/signatureHelp` request.
	 */
	signatureHelp?: SignatureHelpClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/declaration` request.
	 *
	 * @since 3.14.0
	 */
	declaration?: DeclarationClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/definition` request.
	 */
	definition?: DefinitionClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/typeDefinition` request.
	 *
	 * @since 3.6.0
	 */
	typeDefinition?: TypeDefinitionClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/implementation` request.
	 *
	 * @since 3.6.0
	 */
	implementation?: ImplementationClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/references` request.
	 */
	references?: ReferenceClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/documentHighlight` request.
	 */
	documentHighlight?: DocumentHighlightClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/documentSymbol` request.
	 */
	documentSymbol?: DocumentSymbolClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/codeAction` request.
	 */
	codeAction?: CodeActionClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/codeLens` request.
	 */
	codeLens?: CodeLensClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/documentLink` request.
	 */
	documentLink?: DocumentLinkClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/documentColor` and the
	 * `textDocument/colorPresentation` request.
	 *
	 * @since 3.6.0
	 */
	colorProvider?: DocumentColorClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/formatting` request.
	 */
	formatting?: DocumentFormattingClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/rangeFormatting` request.
	 */
	rangeFormatting?: DocumentRangeFormattingClientCapabilities;

	/** request.
	 * Capabilities specific to the `textDocument/onTypeFormatting` request.
	 */
	onTypeFormatting?: DocumentOnTypeFormattingClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/rename` request.
	 */
	rename?: RenameClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/publishDiagnostics`
	 * notification.
	 */
	publishDiagnostics?: PublishDiagnosticsClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/foldingRange` request.
	 *
	 * @since 3.10.0
	 */
	foldingRange?: FoldingRangeClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/selectionRange` request.
	 *
	 * @since 3.15.0
	 */
	selectionRange?: SelectionRangeClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/linkedEditingRange` request.
	 *
	 * @since 3.16.0
	 */
	linkedEditingRange?: LinkedEditingRangeClientCapabilities;

	/**
	 * Capabilities specific to the various call hierarchy requests.
	 *
	 * @since 3.16.0
	 */
	callHierarchy?: CallHierarchyClientCapabilities;

	/**
	 * Capabilities specific to the various semantic token requests.
	 *
	 * @since 3.16.0
	 */
	semanticTokens?: SemanticTokensClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/moniker` request.
	 *
	 * @since 3.16.0
	 */
	moniker?: MonikerClientCapabilities;

	/**
	 * Capabilities specific to the various type hierarchy requests.
	 *
	 * @since 3.17.0
	 */
	typeHierarchy?: TypeHierarchyClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/inlineValue` request.
	 *
	 * @since 3.17.0
	 */
	inlineValue?: InlineValueClientCapabilities;

	/**
	 * Capabilities specific to the `textDocument/inlayHint` request.
	 *
	 * @since 3.17.0
	 */
	inlayHint?: InlayHintClientCapabilities;

	/**
	 * Capabilities specific to the diagnostic pull model.
	 *
	 * @since 3.17.0
	 */
	diagnostic?: DiagnosticClientCapabilities;
}

NotebookDocumentClientCapabilities

NotebookDocumentClientCapabilities 定义编辑器/工具在 notebook 文档上提供的功能。

/**
 * Capabilities specific to the notebook document support.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentClientCapabilities {
	/**
	 * Capabilities specific to notebook document synchronization
	 *
	 * @since 3.17.0
	 */
	synchronization: NotebookDocumentSyncClientCapabilities;
}

ClientCapabilities

ClientCapabilities 定义客户端支持的动态注册、工作区和文本文档功能的功能。ClientCapabilities.experimental 选项可用于正在开发的实验能力。为了实现将来的兼容性,ClientCapabilities 对象文本可以设置比当前定义的属性更多的属性。接收具有未知属性的 ClientCapabilities 对象的服务器应忽略这些属性。缺少的属性应解释为缺少该功能。如果缺少的属性是子属性,则所有缺少的子属性都应解释为缺少相应的功能。

客户端功能随协议 3.0 版一起引入。因此,它们仅描述在 3.x 或更高版本中引入的功能。协议 2.x 版本中存在的功能对于客户端仍然是必需的。客户不能选择不提供它们。因此,即使客户端省略了 ClientCapabilities.textDocument.synchronization,客户端仍然需要提供文本文档同步(例如打开、更改和关闭的通知)。

interface ClientCapabilities {
	/**
	 * Workspace specific client capabilities.
	 */
	workspace?: {
		/**
		 * The client supports applying batch edits
		 * to the workspace by supporting the request
		 * 'workspace/applyEdit'
		 */
		applyEdit?: boolean;

		/**
		 * Capabilities specific to `WorkspaceEdit`s
		 */
		workspaceEdit?: WorkspaceEditClientCapabilities;

		/**
		 * Capabilities specific to the `workspace/didChangeConfiguration`
		 * notification.
		 */
		didChangeConfiguration?: DidChangeConfigurationClientCapabilities;

		/**
		 * Capabilities specific to the `workspace/didChangeWatchedFiles`
		 * notification.
		 */
		didChangeWatchedFiles?: DidChangeWatchedFilesClientCapabilities;

		/**
		 * Capabilities specific to the `workspace/symbol` request.
		 */
		symbol?: WorkspaceSymbolClientCapabilities;

		/**
		 * Capabilities specific to the `workspace/executeCommand` request.
		 */
		executeCommand?: ExecuteCommandClientCapabilities;

		/**
		 * The client has support for workspace folders.
		 *
		 * @since 3.6.0
		 */
		workspaceFolders?: boolean;

		/**
		 * The client supports `workspace/configuration` requests.
		 *
		 * @since 3.6.0
		 */
		configuration?: boolean;

		/**
		 * Capabilities specific to the semantic token requests scoped to the
		 * workspace.
		 *
		 * @since 3.16.0
		 */
		 semanticTokens?: SemanticTokensWorkspaceClientCapabilities;

		/**
		 * Capabilities specific to the code lens requests scoped to the
		 * workspace.
		 *
		 * @since 3.16.0
		 */
		codeLens?: CodeLensWorkspaceClientCapabilities;

		/**
		 * The client has support for file requests/notifications.
		 *
		 * @since 3.16.0
		 */
		fileOperations?: {
			/**
			 * Whether the client supports dynamic registration for file
			 * requests/notifications.
			 */
			dynamicRegistration?: boolean;

			/**
			 * The client has support for sending didCreateFiles notifications.
			 */
			didCreate?: boolean;

			/**
			 * The client has support for sending willCreateFiles requests.
			 */
			willCreate?: boolean;

			/**
			 * The client has support for sending didRenameFiles notifications.
			 */
			didRename?: boolean;

			/**
			 * The client has support for sending willRenameFiles requests.
			 */
			willRename?: boolean;

			/**
			 * The client has support for sending didDeleteFiles notifications.
			 */
			didDelete?: boolean;

			/**
			 * The client has support for sending willDeleteFiles requests.
			 */
			willDelete?: boolean;
		};

		/**
		 * Client workspace capabilities specific to inline values.
		 *
		 * @since 3.17.0
		 */
		inlineValue?: InlineValueWorkspaceClientCapabilities;

		/**
		 * Client workspace capabilities specific to inlay hints.
		 *
		 * @since 3.17.0
		 */
		inlayHint?: InlayHintWorkspaceClientCapabilities;

		/**
		 * Client workspace capabilities specific to diagnostics.
		 *
		 * @since 3.17.0.
		 */
		diagnostics?: DiagnosticWorkspaceClientCapabilities;
	};

	/**
	 * Text document specific client capabilities.
	 */
	textDocument?: TextDocumentClientCapabilities;

	/**
	 * Capabilities specific to the notebook document support.
	 *
	 * @since 3.17.0
	 */
	notebookDocument?: NotebookDocumentClientCapabilities;

	/**
	 * Window specific client capabilities.
	 */
	window?: {
		/**
		 * It indicates whether the client supports server initiated
		 * progress using the `window/workDoneProgress/create` request.
		 *
		 * The capability also controls Whether client supports handling
		 * of progress notifications. If set servers are allowed to report a
		 * `workDoneProgress` property in the request specific server
		 * capabilities.
		 *
		 * @since 3.15.0
		 */
		workDoneProgress?: boolean;

		/**
		 * Capabilities specific to the showMessage request
		 *
		 * @since 3.16.0
		 */
		showMessage?: ShowMessageRequestClientCapabilities;

		/**
		 * Client capabilities for the show document request.
		 *
		 * @since 3.16.0
		 */
		showDocument?: ShowDocumentClientCapabilities;
	};

	/**
	 * General client capabilities.
	 *
	 * @since 3.16.0
	 */
	general?: {
		/**
		 * Client capability that signals how the client
		 * handles stale requests (e.g. a request
		 * for which the client will not process the response
		 * anymore since the information is outdated).
		 *
		 * @since 3.17.0
		 */
		staleRequestSupport?: {
			/**
			 * The client will actively cancel the request.
			 */
			cancel: boolean;

			/**
			 * The list of requests for which the client
			 * will retry the request if it receives a
			 * response with error code `ContentModified``
			 */
			 retryOnContentModified: string[];
		}

		/**
		 * Client capabilities specific to regular expressions.
		 *
		 * @since 3.16.0
		 */
		regularExpressions?: RegularExpressionsClientCapabilities;

		/**
		 * Client capabilities specific to the client's markdown parser.
		 *
		 * @since 3.16.0
		 */
		markdown?: MarkdownClientCapabilities;

		/**
		 * The position encodings supported by the client. Client and server
		 * have to agree on the same position encoding to ensure that offsets
		 * (e.g. character position in a line) are interpreted the same on both
		 * side.
		 *
		 * To keep the protocol backwards compatible the following applies: if
		 * the value 'utf-16' is missing from the array of position encodings
		 * servers can assume that the client supports UTF-16. UTF-16 is
		 * therefore a mandatory encoding.
		 *
		 * If omitted it defaults to ['utf-16'].
		 *
		 * Implementation considerations: since the conversion from one encoding
		 * into another requires the content of the file / line the conversion
		 * is best done where the file is read which is usually on the server
		 * side.
		 *
		 * @since 3.17.0
		 */
		positionEncodings?: PositionEncodingKind[];
	};

	/**
	 * Experimental client capabilities.
	 */
	experimental?: LSPAny;
}

InitializeResult

响应(Response):

  • result: InitializeResult 定义如下:
interface InitializeResult {
	/**
	 * The capabilities the language server provides.
	 */
	capabilities: ServerCapabilities;

	/**
	 * Information about the server.
	 *
	 * @since 3.15.0
	 */
	serverInfo?: {
		/**
		 * The name of the server as defined by the server.
		 */
		name: string;

		/**
		 * The server's version as defined by the server.
		 */
		version?: string;
	};
}
  • error.code:
/**
 * Known error codes for an `InitializeErrorCodes`;
 */
export namespace InitializeErrorCodes {

	/**
	 * If the protocol version provided by the client can't be handled by
	 * the server.
	 *
	 * @deprecated This initialize error got replaced by client capabilities.
	 * There is no version handshake in version 3.0x
	 */
	export const unknownProtocolVersion: 1 = 1;
}

export type InitializeErrorCodes = 1;
  • error.data:
interface InitializeError {
	/**
	 * Indicates whether the client execute the following retry logic:
	 * (1) show the message provided by the ResponseError to the user
	 * (2) user selects retry or cancel
	 * (3) if user selected retry the initialize method is sent again.
	 */
	retry: boolean;
}

ServerCapabilities

服务器可以发出以下能力信号:

interface ServerCapabilities {

	/**
	 * The position encoding the server picked from the encodings offered
	 * by the client via the client capability `general.positionEncodings`.
	 *
	 * If the client didn't provide any position encodings the only valid
	 * value that a server can return is 'utf-16'.
	 *
	 * If omitted it defaults to 'utf-16'.
	 *
	 * @since 3.17.0
	 */
	positionEncoding?: PositionEncodingKind;

	/**
	 * Defines how text documents are synced. Is either a detailed structure
	 * defining each notification or for backwards compatibility the
	 * TextDocumentSyncKind number. If omitted it defaults to
	 * `TextDocumentSyncKind.None`.
	 */
	textDocumentSync?: TextDocumentSyncOptions | TextDocumentSyncKind;

	/**
	 * Defines how notebook documents are synced.
	 *
	 * @since 3.17.0
	 */
	notebookDocumentSync?: NotebookDocumentSyncOptions
		| NotebookDocumentSyncRegistrationOptions;

	/**
	 * The server provides completion support.
	 */
	completionProvider?: CompletionOptions;

	/**
	 * The server provides hover support.
	 */
	hoverProvider?: boolean | HoverOptions;

	/**
	 * The server provides signature help support.
	 */
	signatureHelpProvider?: SignatureHelpOptions;

	/**
	 * The server provides go to declaration support.
	 *
	 * @since 3.14.0
	 */
	declarationProvider?: boolean | DeclarationOptions
		| DeclarationRegistrationOptions;

	/**
	 * The server provides goto definition support.
	 */
	definitionProvider?: boolean | DefinitionOptions;

	/**
	 * The server provides goto type definition support.
	 *
	 * @since 3.6.0
	 */
	typeDefinitionProvider?: boolean | TypeDefinitionOptions
		| TypeDefinitionRegistrationOptions;

	/**
	 * The server provides goto implementation support.
	 *
	 * @since 3.6.0
	 */
	implementationProvider?: boolean | ImplementationOptions
		| ImplementationRegistrationOptions;

	/**
	 * The server provides find references support.
	 */
	referencesProvider?: boolean | ReferenceOptions;

	/**
	 * The server provides document highlight support.
	 */
	documentHighlightProvider?: boolean | DocumentHighlightOptions;

	/**
	 * The server provides document symbol support.
	 */
	documentSymbolProvider?: boolean | DocumentSymbolOptions;

	/**
	 * The server provides code actions. The `CodeActionOptions` return type is
	 * only valid if the client signals code action literal support via the
	 * property `textDocument.codeAction.codeActionLiteralSupport`.
	 */
	codeActionProvider?: boolean | CodeActionOptions;

	/**
	 * The server provides code lens.
	 */
	codeLensProvider?: CodeLensOptions;

	/**
	 * The server provides document link support.
	 */
	documentLinkProvider?: DocumentLinkOptions;

	/**
	 * The server provides color provider support.
	 *
	 * @since 3.6.0
	 */
	colorProvider?: boolean | DocumentColorOptions
		| DocumentColorRegistrationOptions;

	/**
	 * The server provides document formatting.
	 */
	documentFormattingProvider?: boolean | DocumentFormattingOptions;

	/**
	 * The server provides document range formatting.
	 */
	documentRangeFormattingProvider?: boolean | DocumentRangeFormattingOptions;

	/**
	 * The server provides document formatting on typing.
	 */
	documentOnTypeFormattingProvider?: DocumentOnTypeFormattingOptions;

	/**
	 * The server provides rename support. RenameOptions may only be
	 * specified if the client states that it supports
	 * `prepareSupport` in its initial `initialize` request.
	 */
	renameProvider?: boolean | RenameOptions;

	/**
	 * The server provides folding provider support.
	 *
	 * @since 3.10.0
	 */
	foldingRangeProvider?: boolean | FoldingRangeOptions
		| FoldingRangeRegistrationOptions;

	/**
	 * The server provides execute command support.
	 */
	executeCommandProvider?: ExecuteCommandOptions;

	/**
	 * The server provides selection range support.
	 *
	 * @since 3.15.0
	 */
	selectionRangeProvider?: boolean | SelectionRangeOptions
		| SelectionRangeRegistrationOptions;

	/**
	 * The server provides linked editing range support.
	 *
	 * @since 3.16.0
	 */
	linkedEditingRangeProvider?: boolean | LinkedEditingRangeOptions
		| LinkedEditingRangeRegistrationOptions;

	/**
	 * The server provides call hierarchy support.
	 *
	 * @since 3.16.0
	 */
	callHierarchyProvider?: boolean | CallHierarchyOptions
		| CallHierarchyRegistrationOptions;

	/**
	 * The server provides semantic tokens support.
	 *
	 * @since 3.16.0
	 */
	semanticTokensProvider?: SemanticTokensOptions
		| SemanticTokensRegistrationOptions;

	/**
	 * Whether server provides moniker support.
	 *
	 * @since 3.16.0
	 */
	monikerProvider?: boolean | MonikerOptions | MonikerRegistrationOptions;

	/**
	 * The server provides type hierarchy support.
	 *
	 * @since 3.17.0
	 */
	typeHierarchyProvider?: boolean | TypeHierarchyOptions
		 | TypeHierarchyRegistrationOptions;

	/**
	 * The server provides inline values.
	 *
	 * @since 3.17.0
	 */
	inlineValueProvider?: boolean | InlineValueOptions
		 | InlineValueRegistrationOptions;

	/**
	 * The server provides inlay hints.
	 *
	 * @since 3.17.0
	 */
	inlayHintProvider?: boolean | InlayHintOptions
		 | InlayHintRegistrationOptions;

	/**
	 * The server has support for pull model diagnostics.
	 *
	 * @since 3.17.0
	 */
	diagnosticProvider?: DiagnosticOptions | DiagnosticRegistrationOptions;

	/**
	 * The server provides workspace symbol support.
	 */
	workspaceSymbolProvider?: boolean | WorkspaceSymbolOptions;

	/**
	 * Workspace specific server capabilities
	 */
	workspace?: {
		/**
		 * The server supports workspace folder.
		 *
		 * @since 3.6.0
		 */
		workspaceFolders?: WorkspaceFoldersServerCapabilities;

		/**
		 * The server is interested in file notifications/requests.
		 *
		 * @since 3.16.0
		 */
		fileOperations?: {
			/**
			 * The server is interested in receiving didCreateFiles
			 * notifications.
			 */
			didCreate?: FileOperationRegistrationOptions;

			/**
			 * The server is interested in receiving willCreateFiles requests.
			 */
			willCreate?: FileOperationRegistrationOptions;

			/**
			 * The server is interested in receiving didRenameFiles
			 * notifications.
			 */
			didRename?: FileOperationRegistrationOptions;

			/**
			 * The server is interested in receiving willRenameFiles requests.
			 */
			willRename?: FileOperationRegistrationOptions;

			/**
			 * The server is interested in receiving didDeleteFiles file
			 * notifications.
			 */
			didDelete?: FileOperationRegistrationOptions;

			/**
			 * The server is interested in receiving willDeleteFiles file
			 * requests.
			 */
			willDelete?: FileOperationRegistrationOptions;
		};
	};

	/**
	 * Experimental server capabilities.
	 */
	experimental?: LSPAny;
}

Initialized 通知

在客户端收到 initialize 请求的结果之后,但在客户端向服务器发送任何其他请求或通知之前,Initialized 通知将从客户端发送到服务器。例如,服务器可以使用 initialized 通知来动态注册功能。初始化的通知只能发送一次。

通知(Notification):

  • method: "initialized"
  • params: InitializedParams 定义如下:
interface InitializedParams {
}

注册能力

client/registerCapability 请求从服务器发送到客户端,以在客户端注册新功能。并非所有客户端都需要支持动态功能注册。客户端通过特定客户端功能的 dynamicRegistration 属性选择加入。客户端甚至可以为功能 A 提供动态注册,但不能为功能 B 提供动态注册(请参阅 TextDocumentClientCapabilities 作为示例)。

服务器不得动态注册与初始化结果中静态注册相同的功能,也不得为同一文档选择器动态注册相同的功能。如果服务器想要同时支持静态和动态注册,则需要在初始化请求中检查客户端功能,并且仅在客户端不支持该功能的动态注册时才静态注册该功能。

请求(Request):

  • method: "client/registerCapability"
  • params: RegistrationParams 定义如下:
export interface RegistrationParams {
	registrations: Registration[];
}
/**
 * General parameters to register for a capability.
 */
export interface Registration {
	/**
	 * The id used to register the request. The id can be used to deregister
	 * the request again.
	 */
	id: string;

	/**
	 * The method / capability to register for.
	 */
	method: string;

	/**
	 * Options necessary for the registration.
	 */
	registerOptions?: LSPAny;
}

由于大多数注册选项都需要指定文档选择器,因此可以使用基础 interface。请参阅 TextDocumentRegistrationOptions

用于在客户端动态注册 textDocument/willSaveWaitUntil 功能的示例 JSON-RPC 消息如下所示(仅显示详细信息):

{
	"method": "client/registerCapability",
	"params": {
		"registrations": [
			{
				"id": "79eee87c-c409-4664-8102-e03263673f6f",
				"method": "textDocument/willSaveWaitUntil",
				"registerOptions": {
					"documentSelector": [
						{ "language": "javascript" }
					]
				}
			}
		]
	}
}

此消息从服务器发送到客户端,在客户端成功执行请求后,进一步的 textDocument/willSaveWaitUntil 请求将从客户端发送到服务器。

响应(Response):

  • result: void.
  • error: codemessage,以防在请求期间发生异常。

StaticRegistrationOptions 可用于使用给定的服务器控件 ID 在初始化结果中注册功能,以便以后能够取消注册该功能。

/**
 * Static registration options to be returned in the initialize request.
 */
export interface StaticRegistrationOptions {
	/**
	 * The id used to register the request. The id can be used to deregister
	 * the request again. See also Registration#id.
	 */
	id?: string;
}

TextDocumentRegistrationOptions 可用于动态注册一组文本文档的请求。

/**
 * General text document registration options.
 */
export interface TextDocumentRegistrationOptions {
	/**
	 * A document selector to identify the scope of the registration. If set to
	 * null the document selector provided on the client side will be used.
	 */
	documentSelector: DocumentSelector | null;
}

取消注册能力

client/unregisterCapability 请求从服务器发送到客户端,以注销以前注册的能力。

请求(Request):

  • method: "client/unregisterCapability"
  • params: UnregistrationParams 定义如下:
export interface UnregistrationParams {
	// This should correctly be named `unregistrations`. However changing this
	// is a breaking change and needs to wait until we deliver a 4.x version
	// of the specification.
	unregisterations: Unregistration[];
}
/**
 * General parameters to unregister a capability.
 */
export interface Unregistration {
	/**
	 * The id used to unregister the request or notification. Usually an id
	 * provided during the register request.
	 */
	id: string;

	/**
	 * The method / capability to unregister for.
	 */
	method: string;
}

用于注销上述已注册的 textDocument/willSaveWaitUntil 能力的示例 JSON-RPC 消息如下所示:

{
	"method": "client/unregisterCapability",
	"params": {
		"unregisterations": [
			{
				"id": "79eee87c-c409-4664-8102-e03263673f6f",
				"method": "textDocument/willSaveWaitUntil"
			}
		]
	}
}

响应(Response):

  • result: void.
  • error: codemessage,以防在请求期间发生异常。

SetTrace 通知

客户端应使用该通知来修改服务器的跟踪设置。

通知(Notification):

  • method: "$/setTrace"
  • params: SetTraceParams 定义如下:
interface SetTraceParams {
	/**
	 * The new value that should be assigned to the trace setting.
	 */
	value: TraceValue;
}

LogTrace 通知

用于记录服务器执行跟踪的通知。这些通知的数量和内容取决于当前跟踪配置。如果 trace 为 "off",则服务器不应发送任何 logTrace 通知。如果 trace 为 "messages",则服务器不应在 LogTraceParams 中添加 verbose 字段。

$/logTrace 应用于系统跟踪报告。对于单个调试消息,服务器应发送 window/logMessage 通知。

通知(Notification):

  • method: "$/logTrace"
  • params: LogTraceParams 定义如下:
interface LogTraceParams {
	/**
	 * The message to be logged.
	 */
	message: string;
	/**
	 * Additional information that can be computed if the `trace` configuration
	 * is set to `'verbose'`
	 */
	verbose?: string;
}

Shutdown 请求

关闭请求从客户端发送到服务器。它要求服务器关闭,但不退出(否则可能无法将响应正确传递到客户端)。有一个单独的退出通知,要求服务器退出。客户端不得向已向其发送关闭请求的服务器发送除退出通知之外的任何请求和通知。客户端还应等待发送退出通知,直到收到来自关闭请求的响应。

如果服务器在关闭请求后收到请求,则这些请求应出现错误,并显示 InvalidRequest

请求(Request):

  • method: "shutdown"
  • params: none

响应(Response):

  • result: null
  • error: codemessage,以防在请求期间发生异常。

Exit 通知

要求服务器退出其进程的通知。如果之前已收到关闭请求,则服务器应退出并显示成功代码 0; 否则,error 代码为 1。

通知(Notification):

  • method: "exit"
  • params: none

文本文档同步

客户端对 textDocument/didOpentextDocument/didChangetextDocument/didClose 通知的支持在协议中是强制性的,客户端无法选择不支持它们。这包括 textDocument/didChange 通知中的完全同步和增量同步。此外,服务器必须实现所有这三个,或者不实现。因此,它们是通过客户端能力和服务端能力来联合控制的。仅当客户端显示的文档是只读的时,选择退出文本文档同步才有意义。否则,服务器可能会收到文档请求,这些文档的内容在客户端中进行管理(例如,它们可能已更改)。

客户端能力(Client capability):

  • 属性路径: textDocument.synchronization.dynamicRegistration
  • 属性类型: boolean

控制文本文档同步是否支持动态注册。

服务端能力(Server capability):

  • 属性路径: textDocumentSync
  • 属性类型: TextDocumentSyncKind | TextDocumentSyncOptions。下面的 TextDocumentSyncOptions 定义仅涵盖特定于打开、更改和关闭通知的属性:
export interface TextDocumentSyncOptions {
	/**
	 * Open and close notifications are sent to the server. If omitted open
	 * close notifications should not be sent.
	 */
	openClose?: boolean;

	/**
	 * Change notifications are sent to the server. See
	 * TextDocumentSyncKind.None, TextDocumentSyncKind.Full and
	 * TextDocumentSyncKind.Incremental. If omitted it defaults to
	 * TextDocumentSyncKind.None.
	 */
	change?: TextDocumentSyncKind;
}
/**
 * Defines how the host (editor) should sync document changes to the language
 * server.
 */
export namespace TextDocumentSyncKind {
	/**
	 * Documents should not be synced at all.
	 */
	export const None = 0;

	/**
	 * Documents are synced by always sending the full content
	 * of the document.
	 */
	export const Full = 1;

	/**
	 * Documents are synced by sending the full content on open.
	 * After that only incremental updates to the document are
	 * sent.
	 */
	export const Incremental = 2;
}

export type TextDocumentSyncKind = 0 | 1 | 2;

DidOpenTextDocument 通知

文档打开通知从客户端发送到服务器,以发出新打开的文本文档的信号。文档的内容现在由客户端管理,服务器不得尝试使用文档的 Uri 读取文档的内容。从这个意义上说,Open 意味着它由客户端管理。这并不一定意味着它的内容在编辑器中呈现。如果没有相应的关闭通知,则不得多次发送打开通知。这意味着打开和关闭通知必须平衡,并且特定 textDocument 的最大打开计数为 1。请注意,服务器满足请求的能力与文本文档是打开还是关闭无关。

DidOpenTextDocumentParams 包含与文档关联的语言 ID。如果文档的语言 ID 发生更改,则客户端需要向服务器发送 textDocument/didClose,如果服务器也处理新的语言 ID,则需要发送具有新语言 ID 的 textDocument/didOpen

客户端能力(Client capability): 请参阅通用文本文档同步的 客户端能力

服务端能力(Server Capability): 请参阅通用文本文档同步的 服务端能力

注册选项(Registration Options): TextDocumentRegistrationOptions

通知(Notification):

  • method: "textDocument/didOpen"
  • params: DidOpenTextDocumentParams 定义如下:
interface DidOpenTextDocumentParams {
	/**
	 * The document that was opened.
	 */
	textDocument: TextDocumentItem;
}

DidChangeTextDocument 通知

文档更改通知从客户端发送到服务器,以发出对文本文档的更改信号。在客户端可以更改文本文档之前,它必须使用 textDocument/didOpen 通知声明其内容的所有权。在 2.0 中,参数的形状已更改为包含正确的版本号。

客户端能力(Client capability): 请参阅通用文本文档同步的 客户端能力

服务端能力(Server Capability): 请参阅通用文本文档同步的 服务端能力

注册选项(Registration Options): TextDocumentChangeRegistrationOptions 定义如下:

/**
 * Describe options to be used when registering for text document change events.
 */
export interface TextDocumentChangeRegistrationOptions
	extends TextDocumentRegistrationOptions {
	/**
	 * How documents are synced to the server. See TextDocumentSyncKind.Full
	 * and TextDocumentSyncKind.Incremental.
	 */
	syncKind: TextDocumentSyncKind;
}

通知(Notification):

  • method: "textDocument/didChange"
  • params: DidChangeTextDocumentParams 定义如下:
interface DidChangeTextDocumentParams {
	/**
	 * The document that did change. The version number points
	 * to the version after all provided content changes have
	 * been applied.
	 */
	textDocument: VersionedTextDocumentIdentifier;

	/**
	 * The actual content changes. The content changes describe single state
	 * changes to the document. So if there are two content changes c1 (at
	 * array index 0) and c2 (at array index 1) for a document in state S then
	 * c1 moves the document from S to S' and c2 from S' to S''. So c1 is
	 * computed on the state S and c2 is computed on the state S'.
	 *
	 * To mirror the content of a document using change events use the following
	 * approach:
	 * - start with the same initial content
	 * - apply the 'textDocument/didChange' notifications in the order you
	 *   receive them.
	 * - apply the `TextDocumentContentChangeEvent`s in a single notification
	 *   in the order you receive them.
	 */
	contentChanges: TextDocumentContentChangeEvent[];
}
/**
 * An event describing a change to a text document. If only a text is provided
 * it is considered to be the full content of the document.
 */
export type TextDocumentContentChangeEvent = {
	/**
	 * The range of the document that changed.
	 */
	range: Range;

	/**
	 * The optional length of the range that got replaced.
	 *
	 * @deprecated use range instead.
	 */
	rangeLength?: uinteger;

	/**
	 * The new text for the provided range.
	 */
	text: string;
} | {
	/**
	 * The new text of the whole document.
	 */
	text: string;
};

WillSaveTextDocument 通知

在实际保存文档之前,文档将保存通知从客户端发送到服务器。如果服务器已注册打开/关闭事件,则客户端应确保在发送 willSave 通知之前打开文档,因为客户端无法在不转移所有权的情况下更改文件的内容。

客户端能力(Client capability):

  • 属性路径: textDocument.synchronization.willSave
  • 属性类型: boolean

服务端能力(Server capability):

  • 属性路径: textDocumentSync.willSave
  • 属性类型: boolean

注册选项(Registration Options): TextDocumentRegistrationOptions

通知(Notification):

  • method: "textDocument/willSave"
  • params: WillSaveTextDocumentParams 定义如下:
/**
 * The parameters send in a will save text document notification.
 */
export interface WillSaveTextDocumentParams {
	/**
	 * The document that will be saved.
	 */
	textDocument: TextDocumentIdentifier;

	/**
	 * The 'TextDocumentSaveReason'.
	 */
	reason: TextDocumentSaveReason;
}
/**
 * Represents reasons why a text document is saved.
 */
export namespace TextDocumentSaveReason {

	/**
	 * Manually triggered, e.g. by the user pressing save, by starting
	 * debugging, or by an API call.
	 */
	export const Manual = 1;

	/**
	 * Automatic after a delay.
	 */
	export const AfterDelay = 2;

	/**
	 * When the editor lost focus.
	 */
	export const FocusOut = 3;
}

export type TextDocumentSaveReason = 1 | 2 | 3;

WillSaveWaitUntilTextDocument 请求

在文档实际保存之前,文档将保存请求从客户端发送到服务器。该请求可以返回一个 TextEdits 数组,该数组将在保存文本文档之前应用于文本文档。请注意,如果计算文本编辑花费的时间过长,或者服务器在此请求上不断失败,则客户端可能会丢弃结果。这样做是为了保持保存的快速和可靠。如果服务器已注册打开/关闭事件,则客户端应确保在发送 willSaveWaitUntil 通知之前打开文档,因为客户端无法在不转移所有权的情况下更改文件的内容。

客户端能力(Client capability):

  • 属性路径: textDocument.synchronization.willSaveWaitUntil
  • 属性类型: boolean

服务端能力(Server capability):

  • 属性路径: textDocumentSync.willSaveWaitUntil
  • 属性类型: boolean

注册选项(Registration Options): TextDocumentRegistrationOptions

请求(Request):

  • method: "textDocument/willSaveWaitUntil"
  • params: WillSaveTextDocumentParams

响应(Response):

  • result: TextEdit[] | null
  • error: codemessage,以防在请求期间发生异常。

DidSaveTextDocument 通知

当文档在客户端中被保存时,文档保存通知将从客户端发送到服务器。

客户端能力(Client capability):

  • 属性路径: textDocument.synchronization.didSave
  • 属性类型: boolean

服务端能力(Server capability):

  • 属性路径: textDocumentSync.save
  • 属性类型: boolean | SaveOptionsSaveOptions 定义如下:
export interface SaveOptions {
	/**
	 * The client is supposed to include the content on save.
	 */
	includeText?: boolean;
}

注册选项(Registration Options): TextDocumentSaveRegistrationOptions, 定义如下:

export interface TextDocumentSaveRegistrationOptions extends TextDocumentRegistrationOptions {
	/**
	 * The client is supposed to include the content on save.
	 */
	includeText?: boolean;
}

通知(Notification):

  • method: "textDocument/didSave"
  • params: DidSaveTextDocumentParams, 定义如下:
interface DidSaveTextDocumentParams {
	/**
	 * The document that was saved.
	 */
	textDocument: TextDocumentIdentifier;

	/**
	 * Optional the content when saved. Depends on the includeText value
	 * when the save notification was requested.
	 */
	text?: string;
}

DidCloseTextDocument 通知

当文档在客户端中关闭时,文档关闭通知将从客户端发送到服务器。文档的 Uri 现在存在于文档的 Uri 指向的位置(例如,如果文档的 Uri 是文件 Uri,则主文件现在存在于磁盘上)。与打开通知一样,发送关闭通知意味着它不再由客户端管理,收到关闭通知并不意味着文档之前已在编辑器中打开。只有打开通知发送后,才能发送关闭通知。请注意,服务器满足请求的能力与文本文档是打开还是关闭无关。

客户端能力(Client capability): 请参阅通用文本文档同步的 客户端能力

服务端能力(Server Capability): 请参阅通用文本文档同步的 服务端能力

注册选项(Registration Options): TextDocumentRegistrationOptions

通知(Notification):

  • method: "textDocument/didClose"
  • params: DidCloseTextDocumentParams, 定义如下:
interface DidCloseTextDocumentParams {
	/**
	 * The document that was closed.
	 */
	textDocument: TextDocumentIdentifier;
}

重命名文档

文档重命名应向服务器发出信号,使用文档的旧名称发送文档关闭通知,然后使用文档的新名称发送打开通知。这样做的主要原因是除了名称之外,其他属性也可以更改,例如与文档关联的语言。此外,服务器可能不再对新文档感兴趣。

服务器可以通过订阅 workspace/didRenameFiles 通知或 workspace/willRenameFiles 请求来参与文档重命名。

TextDocumentSyncClientCapabilitiesTextDocumentSyncOptions 服务器选项的最终结构如下所示:

export interface TextDocumentSyncClientCapabilities {
	/**
	 * Whether text document synchronization supports dynamic registration.
	 */
	dynamicRegistration?: boolean;

	/**
	 * The client supports sending will save notifications.
	 */
	willSave?: boolean;

	/**
	 * The client supports sending a will save request and
	 * waits for a response providing text edits which will
	 * be applied to the document before it is saved.
	 */
	willSaveWaitUntil?: boolean;

	/**
	 * The client supports did save notifications.
	 */
	didSave?: boolean;
}
export interface TextDocumentSyncOptions {
	/**
	 * Open and close notifications are sent to the server. If omitted open
	 * close notification should not be sent.
	 */
	openClose?: boolean;
	/**
	 * Change notifications are sent to the server. See
	 * TextDocumentSyncKind.None, TextDocumentSyncKind.Full and
	 * TextDocumentSyncKind.Incremental. If omitted it defaults to
	 * TextDocumentSyncKind.None.
	 */
	change?: TextDocumentSyncKind;
	/**
	 * If present will save notifications are sent to the server. If omitted
	 * the notification should not be sent.
	 */
	willSave?: boolean;
	/**
	 * If present will save wait until requests are sent to the server. If
	 * omitted the request should not be sent.
	 */
	willSaveWaitUntil?: boolean;
	/**
	 * If present save notifications are sent to the server. If omitted the
	 * notification should not be sent.
	 */
	save?: boolean | SaveOptions;
}

Notebook 文档同步

Notebook 正变得越来越流行。通过向语言服务器协议添加对它们的支持,笔记本编辑器可以分别在 NotebookNotebook cell 中重用服务器提供的语言智能。为了重用协议部分,从而重用服务器实现,在 LSP 中按以下方式对笔记本进行建模:

  • Notebook document: 通常存储在磁盘文件中的笔记本单元的集合。笔记本文档具有类型,可以使用资源 URI 进行唯一标识。

  • notebook cell: 保存实际的文本内容。单元格有类型(代码或 markdown)。单元格的实际文本内容存储在文本文档中,该文本文档可以像所有其他文本文档一样同步到服务器。单元格文本文档具有 URI,但服务器不应依赖此 URI 的任何格式,因为如何创建这些 URI 取决于客户端。URI 在所有 notebook cell 中必须是唯一的,因此可用于唯一标识 notebook cellnotebook cell 的文本文档。

这两个概念的定义如下:

/**
 * A notebook document.
 *
 * @since 3.17.0
 */
export interface NotebookDocument {

	/**
	 * The notebook document's URI.
	 */
	uri: URI;

	/**
	 * The type of the notebook.
	 */
	notebookType: string;

	/**
	 * The version number of this document (it will increase after each
	 * change, including undo/redo).
	 */
	version: integer;

	/**
	 * Additional metadata stored with the notebook
	 * document.
	 */
	metadata?: LSPObject;

	/**
	 * The cells of a notebook.
	 */
	cells: NotebookCell[];
}
/**
 * A notebook cell.
 *
 * A cell's document URI must be unique across ALL notebook
 * cells and can therefore be used to uniquely identify a
 * notebook cell or the cell's text document.
 *
 * @since 3.17.0
 */
export interface NotebookCell {

	/**
	 * The cell's kind
	 */
	kind: NotebookCellKind;

	/**
	 * The URI of the cell's text document
	 * content.
	 */
	document: DocumentUri;

	/**
	 * Additional metadata stored with the cell.
	 */
	metadata?: LSPObject;

	/**
	 * Additional execution summary information
	 * if supported by the client.
	 */
	executionSummary?: ExecutionSummary;
}
/**
 * A notebook cell kind.
 *
 * @since 3.17.0
 */
export namespace NotebookCellKind {

	/**
	 * A markup-cell is formatted source that is used for display.
	 */
	export const Markup: 1 = 1;

	/**
	 * A code-cell is source code.
	 */
	export const Code: 2 = 2;
}
export interface ExecutionSummary {
	/**
	 * A strict monotonically increasing value
	 * indicating the execution order of a cell
	 * inside a notebook.
	 */
	executionOrder: uinteger;

	/**
	 * Whether the execution was successful or
	 * not if known by the client.
	 */
	success?: boolean;
}

接下来,我们将介绍如何将笔记本、笔记本单元格和笔记本单元格的内容同步到语言服务器。同步单元格的文本内容相对容易,因为客户端应将它们建模为文本文档。但是,由于笔记本单元的文本文档的 URI 应该是不透明的,因此服务器无法知道其 schemepath。所知道的只是笔记本文档本身。因此,我们为笔记本单元格文档引入了一个特殊的过滤器:

/**
 * A notebook cell text document filter denotes a cell text
 * document by different properties.
 *
 * @since 3.17.0
 */
export interface NotebookCellTextDocumentFilter {
	/**
	 * A filter that matches against the notebook
	 * containing the notebook cell. If a string
	 * value is provided it matches against the
	 * notebook type. '*' matches every notebook.
	 */
	notebook: string | NotebookDocumentFilter;

	/**
	 * A language id like `python`.
	 *
	 * Will be matched against the language id of the
	 * notebook cell document. '*' matches every language.
	 */
	language?: string;
}
/**
 * A notebook document filter denotes a notebook document by
 * different properties.
 *
 * @since 3.17.0
 */
export type NotebookDocumentFilter = {
	/** The type of the enclosing notebook. */
	notebookType?: string;

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

	/** A glob pattern. */
	pattern?: string;
}

NotebookDocumentFilter 中的 notebookType, schemepattern 至少存在一个必填,这里为了简化,都定义为了可选。

给定这些结构,可以按如下方式识别 Jupyter Notebook 中存储在磁盘上的 Python 单元文档,该文档的路径中包含 books1 的文件夹中;

{
	notebook: {
		scheme: 'file',
		pattern '**/books1/**',
		notebookType: 'jupyter-notebook'
	},
	language: 'python'
}

NotebookCellTextDocumentFilter 可用于为某些请求(如代码完成或悬停)注册提供程序。如果注册了此类提供程序,则客户端将使用单元格文本文档的 URI 作为文档 URI 向服务器发送相应的 textDocument/* 请求。

在某些情况下,仅仅知道单元格的文本内容不足以让服务器推理单元格内容并提供良好的语言智能。有时需要了解笔记本文档的所有单元格,包括笔记本文档本身。考虑一个笔记本,它有两个 JavaScript 单元格,其中包含以下内容:

单元格一:

function add(a, b) {
	return a + b;
}

单元格二:

add/*<cursor>*/;

在标记的光标位置的第二个单元格中请求代码辅助应该建议函数 add,这只有在服务器知道单元格 1 和单元格 2 并且知道它们属于同一个笔记本文档时才有可能。

因此,在同步单元格文本内容时,该协议将支持两种模式:

  • cellContent: 在此模式下,仅使用标准 textDocument/did* 通知将单元格文本内容同步到服务器。没有 Notebook document,也没有单元格结构同步。此模式对于 Notebook 服务器便于采用,因为服务器可以重用大部分实现逻辑。

  • notebook: 在此模式下,Notebook 文档、Notebook 单元格和 Notebook 单元格文本内容将同步到服务器。为了允许服务器创建与 Notebook 文档一致的视图,不使用标准 textDocument/did* 通知同步单元格文本内容。相反,它使用特殊的 notebookDocument/did* 通知进行同步。这可确保单元格及其文本内容使用一个 open、change 或 close 事件到达服务器。

要请求单元格内容,只能使用普通的文档选择器。例如,选择器 [{ language: 'python' }] 会将 Python Notebook 文档单元格同步到服务器。但是,由于这也可能同步不需要的文档,因此文档筛选器也可以是 NotebookCellTextDocumentFilter。所以 { notebook: { scheme: 'file', notebookType: 'jupyter-notebook' }, language: 'python' } 同步存储在磁盘上的 Jupyter Notebook 中的所有 Python 单元格。

若要同步整个笔记本文档,服务器在其服务器能力中提供 notebookDocumentSync。例如:

{
	notebookDocumentSync: {
		notebookSelector: [
			{
				notebook: { scheme: 'file', notebookType: 'jupyter-notebook' },
				cells: [{ language: 'python' }]
			}
		]
	}
}

如果 Notebook 存储在磁盘上,则将 Notebook(包括所有 Python 单元)同步到服务器。

客户端能力(Client capability):

  • 属性路径: notebookDocument.synchronization
  • 属性类型: NotebookDocumentSyncClientCapabilities, 定义如下:
/**
 * Notebook specific client capabilities.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentSyncClientCapabilities {

	/**
	 * Whether implementation supports dynamic registration. If this is
	 * set to `true` the client supports the new
	 * `(NotebookDocumentSyncRegistrationOptions & NotebookDocumentSyncOptions)`
	 * return value for the corresponding server capability as well.
	 */
	dynamicRegistration?: boolean;

	/**
	 * The client supports sending execution summary data per cell.
	 */
	executionSummarySupport?: boolean;
}

服务端能力(Server capability):

  • 属性路径: notebookDocumentSync
  • 属性类型: NotebookDocumentSyncOptions | NotebookDocumentSyncRegistrationOptions, NotebookDocumentOptions 定义如下:
/**
 * Options specific to a notebook plus its cells
 * to be synced to the server.
 *
 * If a selector provides a notebook document
 * filter but no cell selector all cells of a
 * matching notebook document will be synced.
 *
 * If a selector provides no notebook document
 * filter but only a cell selector all notebook
 * documents that contain at least one matching
 * cell will be synced.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentSyncOptions {
	/**
	 * The notebooks to be synced
	 */
	notebookSelector: ({
		/**
		 * The notebook to be synced. If a string
		 * value is provided it matches against the
		 * notebook type. '*' matches every notebook.
		 */
		notebook: string | NotebookDocumentFilter;

		/**
		 * The cells of the matching notebook to be synced.
		 */
		cells?: { language: string }[];
	} | {
		/**
		 * The notebook to be synced. If a string
		 * value is provided it matches against the
		 * notebook type. '*' matches every notebook.
		 */
		notebook?: string | NotebookDocumentFilter;

		/**
		 * The cells of the matching notebook to be synced.
		 */
		cells: { language: string }[];
	})[];

	/**
	 * Whether save notification should be forwarded to
	 * the server. Will only be honored if mode === `notebook`.
	 */
	save?: boolean;
}

注册选项(Registration Options): notebookDocumentSyncRegistrationOptions, 定义如下:

/**
 * Registration options specific to a notebook.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentSyncRegistrationOptions extends
	NotebookDocumentSyncOptions, StaticRegistrationOptions {
}

DidOpenNotebookDocument 通知

打开 Notebook 文档时,打开通知将从客户端发送到服务器。仅当服务器提供 notebookDocumentSync 能力时,客户端才会发送它。

通知(Notification):

  • method: "notebookDocument/didOpen"
  • params: DidOpenNotebookDocumentParams, 定义如下:
/**
 * The params sent in an open notebook document notification.
 *
 * @since 3.17.0
 */
export interface DidOpenNotebookDocumentParams {

	/**
	 * The notebook document that got opened.
	 */
	notebookDocument: NotebookDocument;

	/**
	 * The text documents that represent the content
	 * of a notebook cell.
	 */
	cellTextDocuments: TextDocumentItem[];
}

DidChangeNotebookDocument 通知

当笔记本文档发生更改时,更改通知将从客户端发送到服务器。仅当服务器提供 notebookDocumentSync 能力时,客户端才会发送它。

通知(Notification):

  • method: "notebookDocument/didChange"
  • params: DidChangeNotebookDocumentParams, 定义如下:
/**
 * The params sent in a change notebook document notification.
 *
 * @since 3.17.0
 */
export interface DidChangeNotebookDocumentParams {

	/**
	 * The notebook document that did change. The version number points
	 * to the version after all provided changes have been applied.
	 */
	notebookDocument: VersionedNotebookDocumentIdentifier;

	/**
	 * The actual changes to the notebook document.
	 *
	 * The change describes single state change to the notebook document.
	 * So it moves a notebook document, its cells and its cell text document
	 * contents from state S to S'.
	 *
	 * To mirror the content of a notebook using change events use the
	 * following approach:
	 * - start with the same initial content
	 * - apply the 'notebookDocument/didChange' notifications in the order
	 *   you receive them.
	 */
	change: NotebookDocumentChangeEvent;
}
/**
 * A versioned notebook document identifier.
 *
 * @since 3.17.0
 */
export interface VersionedNotebookDocumentIdentifier {

	/**
	 * The version number of this notebook document.
	 */
	version: integer;

	/**
	 * The notebook document's URI.
	 */
	uri: URI;
}
/**
 * A change event for a notebook document.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentChangeEvent {
	/**
	 * The changed meta data if any.
	 */
	metadata?: LSPObject;

	/**
	 * Changes to cells
	 */
	cells?: {
		/**
		 * Changes to the cell structure to add or
		 * remove cells.
		 */
		structure?: {
			/**
			 * The change to the cell array.
			 */
			array: NotebookCellArrayChange;

			/**
			 * Additional opened cell text documents.
			 */
			didOpen?: TextDocumentItem[];

			/**
			 * Additional closed cell text documents.
			 */
			didClose?: TextDocumentIdentifier[];
		};

		/**
		 * Changes to notebook cells properties like its
		 * kind, execution summary or metadata.
		 */
		data?: NotebookCell[];

		/**
		 * Changes to the text content of notebook cells.
		 */
		textContent?: {
			document: VersionedTextDocumentIdentifier;
			changes: TextDocumentContentChangeEvent[];
		}[];
	};
}
/**
 * A change describing how to move a `NotebookCell`
 * array from state S to S'.
 *
 * @since 3.17.0
 */
export interface NotebookCellArrayChange {
	/**
	 * The start offset of the cell that changed.
	 */
	start: uinteger;

	/**
	 * The deleted cells
	 */
	deleteCount: uinteger;

	/**
	 * The new cells, if any
	 */
	cells?: NotebookCell[];
}

DidSaveNotebookDocument 通知

保存笔记本文档时,保存通知将从客户端发送到服务器。仅当服务器提供 notebookDocumentSync 能力时,客户端才会发送它。

通知(Notification):

  • method: "notebookDocument/didSave"
  • params: DidSaveNotebookDocumentParams, 定义如下:
/**
 * The params sent in a save notebook document notification.
 *
 * @since 3.17.0
 */
export interface DidSaveNotebookDocumentParams {
	/**
	 * The notebook document that got saved.
	 */
	notebookDocument: NotebookDocumentIdentifier;
}

DidCloseNotebookDocument 通知

关闭笔记本文档时,关闭通知将从客户端发送到服务器。仅当服务器提供 notebookDocumentSync 能力时,客户端才会发送它。

通知(Notification):

  • method: "notebookDocument/didClose"
  • params: DidCloseNotebookDocumentParams, 定义如下:
/**
 * The params sent in a close notebook document notification.
 *
 * @since 3.17.0
 */
export interface DidCloseNotebookDocumentParams {

	/**
	 * The notebook document that got closed.
	 */
	notebookDocument: NotebookDocumentIdentifier;

	/**
	 * The text documents that represent the content
	 * of a notebook cell that got closed.
	 */
	cellTextDocuments: TextDocumentIdentifier[];
}
/**
 * A literal to identify a notebook document in the client.
 *
 * @since 3.17.0
 */
export interface NotebookDocumentIdentifier {
	/**
	 * The notebook document's URI.
	 */
	uri: URI;
}

由于字数限制,暂时仅包含 基础协议、语言服务协议、基础 JSON 结构、生命周期消息、文本文档同步、Notebook 文档同步,语言功能、窗口功能和杂项此处未包含,全文请查看:语言服务器开发指南 (lsp.fosp.cc)