Pull Diagnostics

当前,服务器使用通知将诊断信息发布到客户端。此模型的优点是,对于工作区范围的诊断,服务器可以自由地在服务器首选的时间点计算它们。另一方面,这种方法的缺点是服务器无法对用户键入的文件或在编辑器中可见的文件进行计算。从 textDocument/didOpentextDocument/didChange 通知推断客户端的 UI 状态可能会导致误报,因为这些通知是所有权转移通知。

因此,该规范引入了诊断拉取请求的概念,使客户端能够更好地控制应为其计算诊断的文档以及在哪个时间点进行诊断。

客户端能力(Client capability):

  • 属性路径: textDocument.diagnostic
  • 属性类型: DiagnosticClientCapabilities, 定义如下:
/**
 * Client capabilities specific to diagnostic pull requests.
 *
 * @since 3.17.0
 */
export interface DiagnosticClientCapabilities {
	/**
	 * Whether implementation supports dynamic registration. If this is set to
	 * `true` the client supports the new
	 * `(TextDocumentRegistrationOptions & StaticRegistrationOptions)`
	 * return value for the corresponding server capability as well.
	 */
	dynamicRegistration?: boolean;

	/**
	 * Whether the clients supports related documents for document diagnostic
	 * pulls.
	 */
	relatedDocumentSupport?: boolean;
}

服务端能力(Server capability):

  • 属性路径: diagnosticProvider
  • 属性类型: DiagnosticOptions, 定义如下:
/**
 * Diagnostic options.
 *
 * @since 3.17.0
 */
export interface DiagnosticOptions extends WorkDoneProgressOptions {
	/**
	 * An optional identifier under which the diagnostics are
	 * managed by the client.
	 */
	identifier?: string;

	/**
	 * Whether the language has inter file dependencies meaning that
	 * editing code in one file can result in a different diagnostic
	 * set in another file. Inter file dependencies are common for
	 * most programming languages and typically uncommon for linters.
	 */
	interFileDependencies: boolean;

	/**
	 * The server provides support for workspace diagnostics as well.
	 */
	workspaceDiagnostics: boolean;
}

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

/**
 * Diagnostic registration options.
 *
 * @since 3.17.0
 */
export interface DiagnosticRegistrationOptions extends
	TextDocumentRegistrationOptions, DiagnosticOptions,
	StaticRegistrationOptions {
}

Document Diagnostics

文本文档诊断请求从客户端发送到服务器,以要求服务器计算给定文档的诊断。与其他拉取请求一样,系统要求服务器计算当前同步的文档版本的诊断。

请求(Request):

  • method: "textDocument/diagnostic"
  • params: DocumentDiagnosticParams, 定义如下:
/**
 * Parameters of the document diagnostic request.
 *
 * @since 3.17.0
 */
export interface DocumentDiagnosticParams extends WorkDoneProgressParams,
	PartialResultParams {
	/**
	 * The text document.
	 */
	textDocument: TextDocumentIdentifier;

	/**
	 * The additional identifier  provided during registration.
	 */
	identifier?: string;

	/**
	 * The result id of a previous response if provided.
	 */
	previousResultId?: string;
}

响应(Response):

  • result: DocumentDiagnosticReport, 定义如下:
/**
 * The result of a document diagnostic pull request. A report can
 * either be a full report containing all diagnostics for the
 * requested document or a unchanged report indicating that nothing
 * has changed in terms of diagnostics in comparison to the last
 * pull request.
 *
 * @since 3.17.0
 */
export type DocumentDiagnosticReport = RelatedFullDocumentDiagnosticReport
	| RelatedUnchangedDocumentDiagnosticReport;
/**
 * The document diagnostic report kinds.
 *
 * @since 3.17.0
 */
export namespace DocumentDiagnosticReportKind {
	/**
	 * A diagnostic report with a full
	 * set of problems.
	 */
	export const Full = 'full';

	/**
	 * A report indicating that the last
	 * returned report is still accurate.
	 */
	export const Unchanged = 'unchanged';
}

export type DocumentDiagnosticReportKind = 'full' | 'unchanged';
/**
 * A diagnostic report with a full set of problems.
 *
 * @since 3.17.0
 */
export interface FullDocumentDiagnosticReport {
	/**
	 * A full document diagnostic report.
	 */
	kind: DocumentDiagnosticReportKind.Full;

	/**
	 * An optional result id. If provided it will
	 * be sent on the next diagnostic request for the
	 * same document.
	 */
	resultId?: string;

	/**
	 * The actual items.
	 */
	items: Diagnostic[];
}
/**
 * A diagnostic report indicating that the last returned
 * report is still accurate.
 *
 * @since 3.17.0
 */
export interface UnchangedDocumentDiagnosticReport {
	/**
	 * A document diagnostic report indicating
	 * no changes to the last result. A server can
	 * only return `unchanged` if result ids are
	 * provided.
	 */
	kind: DocumentDiagnosticReportKind.Unchanged;

	/**
	 * A result id which will be sent on the next
	 * diagnostic request for the same document.
	 */
	resultId: string;
}
/**
 * A full diagnostic report with a set of related documents.
 *
 * @since 3.17.0
 */
export interface RelatedFullDocumentDiagnosticReport extends
	FullDocumentDiagnosticReport {
	/**
	 * Diagnostics of related documents. This information is useful
	 * in programming languages where code in a file A can generate
	 * diagnostics in a file B which A depends on. An example of
	 * such a language is C/C++ where marco definitions in a file
	 * a.cpp and result in errors in a header file b.hpp.
	 *
	 * @since 3.17.0
	 */
	relatedDocuments?: {
		[uri: string /** DocumentUri */]:
			FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport;
	};
}
/**
 * An unchanged diagnostic report with a set of related documents.
 *
 * @since 3.17.0
 */
export interface RelatedUnchangedDocumentDiagnosticReport extends
	UnchangedDocumentDiagnosticReport {
	/**
	 * Diagnostics of related documents. This information is useful
	 * in programming languages where code in a file A can generate
	 * diagnostics in a file B which A depends on. An example of
	 * such a language is C/C++ where marco definitions in a file
	 * a.cpp and result in errors in a header file b.hpp.
	 *
	 * @since 3.17.0
	 */
	relatedDocuments?: {
		[uri: string /** DocumentUri */]:
			FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport;
	};
}
  • partial result: 第一个文本发送需要是 DocumentDiagnosticReport,后跟 n 个 DocumentDiagnosticReportPartialResult 文本,定义如下:
/**
 * A partial result for a document diagnostic report.
 *
 * @since 3.17.0
 */
export interface DocumentDiagnosticReportPartialResult {
	relatedDocuments: {
		[uri: string /** DocumentUri */]:
			FullDocumentDiagnosticReport | UnchangedDocumentDiagnosticReport;
	};
}
  • error: codemessage,以防在请求期间发生异常。还允许服务器返回代码为 ServerCancelled 的错误,指示服务器现在无法计算结果。服务器可以返回 DiagnosticServerCancellationData 数据,以指示客户端是否应重新触发请求。如果未提供任何数据,则默认为 { retriggerRequest: true }
/**
 * Cancellation data returned from a diagnostic request.
 *
 * @since 3.17.0
 */
export interface DiagnosticServerCancellationData {
	retriggerRequest: boolean;
}

Workspace Diagnostics

工作区诊断请求从客户端发送到服务器,要求服务器计算工作区范围的诊断,以前从服务器推送到客户端。与文档诊断请求相比,工作区请求可以长时间运行,并且不绑定到特定的工作区或文档状态。如果客户端支持对工作区诊断拉取进行流式处理,则为同一文档 URI 多次提供文档诊断报告是合法的。最后一份报告将胜过以前的报告。

如果客户端收到工作区诊断请求中文档的诊断报告,而客户端还为其发出单个文档诊断拉取请求,则客户端需要决定哪个诊断胜出并应显示哪个诊断。通常:

  • 较高文档版本的诊断应胜过较低文档版本的诊断(例如,请注意文档版本正在稳步增加)

  • 来自文档拉取的诊断应胜过来自工作区拉取的诊断。

请求(Request):

  • method: "workspace/diagnostic’"
  • params: WorkspaceDiagnosticParams, 定义如下:
/**
 * Parameters of the workspace diagnostic request.
 *
 * @since 3.17.0
 */
export interface WorkspaceDiagnosticParams extends WorkDoneProgressParams,
	PartialResultParams {
	/**
	 * The additional identifier provided during registration.
	 */
	identifier?: string;

	/**
	 * The currently known diagnostic reports with their
	 * previous result ids.
	 */
	previousResultIds: PreviousResultId[];
}
/**
 * A previous result id in a workspace pull request.
 *
 * @since 3.17.0
 */
export interface PreviousResultId {
	/**
	 * The URI for which the client knows a
	 * result id.
	 */
	uri: DocumentUri;

	/**
	 * The value of the previous result id.
	 */
	value: string;
}

响应(Response):

  • result: WorkspaceDiagnosticReport, 定义如下:
/**
 * A workspace diagnostic report.
 *
 * @since 3.17.0
 */
export interface WorkspaceDiagnosticReport {
	items: WorkspaceDocumentDiagnosticReport[];
}
/**
 * A full document diagnostic report for a workspace diagnostic result.
 *
 * @since 3.17.0
 */
export interface WorkspaceFullDocumentDiagnosticReport extends
	FullDocumentDiagnosticReport {

	/**
	 * The URI for which diagnostic information is reported.
	 */
	uri: DocumentUri;

	/**
	 * The version number for which the diagnostics are reported.
	 * If the document is not marked as open `null` can be provided.
	 */
	version: integer | null;
}
/**
 * An unchanged document diagnostic report for a workspace diagnostic result.
 *
 * @since 3.17.0
 */
export interface WorkspaceUnchangedDocumentDiagnosticReport extends
	UnchangedDocumentDiagnosticReport {

	/**
	 * The URI for which diagnostic information is reported.
	 */
	uri: DocumentUri;

	/**
	 * The version number for which the diagnostics are reported.
	 * If the document is not marked as open `null` can be provided.
	 */
	version: integer | null;
}
/**
 * A workspace diagnostic document report.
 *
 * @since 3.17.0
 */
export type WorkspaceDocumentDiagnosticReport =
	WorkspaceFullDocumentDiagnosticReport
	| WorkspaceUnchangedDocumentDiagnosticReport;
  • partial result: 第一个文本发送需要是 WorkspaceDiagnosticReport,后跟 n 个 WorkspaceDiagnosticReportPartialResult 文本,定义如下:
/**
 * A partial result for a workspace diagnostic report.
 *
 * @since 3.17.0
 */
export interface WorkspaceDiagnosticReportPartialResult {
	items: WorkspaceDocumentDiagnosticReport[];
}
  • error: codemessage,以防在请求期间发生异常。还允许服务器返回错误,代码为 ServerCancelled,指示服务器现在无法计算结果。服务器可以返回 DiagnosticServerCancellationData 数据,以指示客户端是否应重新触发请求。如果未提供任何数据,则默认为 { retriggerRequest: true }

Diagnostics Refresh

请求从服务器发送到客户端。服务器可以使用它来要求客户端刷新所有需要的文档和工作区诊断。如果服务器检测到需要重新计算所有诊断的项目范围的配置更改,这将非常有用。

客户端能力(Client capability):

  • 属性路径: workspace.diagnostics
  • 属性类型: DiagnosticWorkspaceClientCapabilities, 定义如下:
/**
 * Workspace client capabilities specific to diagnostic pull requests.
 *
 * @since 3.17.0
 */
export interface DiagnosticWorkspaceClientCapabilities {
	/**
	 * Whether the client implementation supports a refresh request sent from
	 * the server to the client.
	 *
	 * Note that this event is global and will force the client to refresh all
	 * pulled diagnostics currently shown. It should be used with absolute care
	 * and is useful for situation where a server for example detects a project
	 * wide change that requires such a calculation.
	 */
	refreshSupport?: boolean;
}

请求(Request):

  • method: "workspace/diagnostic/refresh"
  • params: none

响应(Response):

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

实现注意事项

通常,语言服务器规范不会强制执行任何特定的客户端实现,因为这些实现通常取决于客户端 UI 的行为方式。但是,由于可以在文档和工作区级别提供诊断,因此以下是一些提示:

  • 客户端应主动拉取用户键入的文档。

  • 如果服务器发出文件间依赖关系的信号,客户端还应拉取可见文档以确保准确的诊断。但是,拉动的发生频率应该较低。

  • 如果服务器支持工作区拉取,则客户端还应拉取工作区诊断。建议客户端实现工作区拉取的部分结果进度,以允许服务器长时间保持请求打开状态。如果服务器关闭工作区诊断拉取请求,则客户端应重新触发该请求。