文本文档同步

客户端对 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;
}