编程语言特性

编程语言特性是一组由vscode.languages.* API 驱动的智能编辑功能。在 Visual Studio Code 中,有两种常见的方式来提供动态语言特性。让我们以悬停为例:

vscode.languages.registerHoverProvider('javascript', {
  provideHover(document, position, token) {
    return {
      contents: ['Hover Content']
    };
  }
});

如上所示,vscode.languages.registerHoverProvider API 提供了一种简单的方法来为 JavaScript 文件提供悬停内容。在此扩展激活后,每当您悬停在某个 JavaScript 代码上时,VS Code 会查询所有 HoverProvider 并显示在悬停小部件中。下面的语言功能列表和图示 gif 为您提供了一种简单的方法来定位您的扩展所需的 VS Code API / LSP 方法。

另一种方法是实现一个使用语言服务器协议的语言服务器。其工作原理如下:

  • 一个扩展为JavaScript提供了语言客户端和语言服务器。
  • 语言客户端就像任何其他VS Code扩展一样,运行在Node.js扩展主机上下文中。当它被激活时,它会在另一个进程中生成语言服务器,并通过语言服务器协议与之通信。
  • 你在 VS Code 中悬停在 JavaScript 代码上
  • VS Code 通知语言客户端关于悬停的信息
  • 语言客户端向语言服务器查询悬停结果并将其发送回VS Code
  • VS Code 在悬停小部件中显示悬停结果

这个过程似乎更复杂,但它提供了两个主要好处:

  • 语言服务器可以用任何语言编写
  • 语言服务器可以重复使用,为多个编辑器提供智能编辑功能

如需更深入的指南,请前往语言服务器扩展指南


语言功能列表

此列表包括每个语言特性的以下项目:

  • VS Code 中语言特性的示例
  • 相关的 VS Code API
  • 相关的LSP方法
VS Code API LSP method
createDiagnosticCollection PublishDiagnostics
registerCompletionItemProvider Completion & Completion Resolve
registerHoverProvider Hover
registerSignatureHelpProvider SignatureHelp
registerDefinitionProvider Definition
registerTypeDefinitionProvider TypeDefinition
registerImplementationProvider Implementation
registerReferenceProvider References
registerDocumentHighlightProvider DocumentHighlight
registerDocumentSymbolProvider DocumentSymbol
registerCodeActionsProvider CodeAction
registerCodeLensProvider CodeLens & CodeLens Resolve
registerDocumentLinkProvider DocumentLink & DocumentLink Resolve
registerColorProvider DocumentColor & Color Presentation
registerDocumentFormattingEditProvider Formatting
registerDocumentRangeFormattingEditProvider RangeFormatting
registerOnTypeFormattingEditProvider OnTypeFormatting
registerRenameProvider Rename & Prepare Rename
registerFoldingRangeProvider FoldingRange

提供诊断

诊断是表示代码问题的一种方式。

诊断显示方法名称拼写错误

语言服务器协议

您的语言服务器向语言客户端发送textDocument/publishDiagnostics消息。该消息携带了一个资源URI的诊断项数组。

注意: 客户端不会向服务器请求诊断信息。服务器会将诊断信息推送给客户端。

直接实现

let diagnosticCollection: vscode.DiagnosticCollection;

export function activate(ctx: vscode.ExtensionContext): void {
  ...
  ctx.subscriptions.push(getDisposable());
  diagnosticCollection = vscode.languages.createDiagnosticCollection('go');
  ctx.subscriptions.push(diagnosticCollection);
  ...
}

function onChange() {
  let uri = document.uri;
  check(uri.fsPath, goConfig).then(errors => {
    diagnosticCollection.clear();
    let diagnosticMap: Map<string, vscode.Diagnostic[]> = new Map();
    errors.forEach(error => {
      let canonicalFile = vscode.Uri.file(error.file).toString();
      let range = new vscode.Range(error.line-1, error.startColumn, error.line-1, error.endColumn);
      let diagnostics = diagnosticMap.get(canonicalFile);
      if (!diagnostics) { diagnostics = []; }
      diagnostics.push(new vscode.Diagnostic(range, error.msg, error.severity));
      diagnosticMap.set(canonicalFile, diagnostics);
    });
    diagnosticMap.forEach((diags, file) => {
      diagnosticCollection.set(vscode.Uri.parse(file), diags);
    });
  })
}

基础

为打开的编辑器报告诊断信息。至少,这需要在每次保存时发生。更好的情况是,诊断信息应该基于编辑器的未保存内容进行计算。

高级

报告诊断不仅针对打开的编辑器,还针对打开文件夹中的所有资源,无论它们是否曾在编辑器中打开过。

显示代码补全建议

代码补全为用户提供上下文相关的建议。

代码补全提示变量、方法和参数名称,在编写代码时

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供补全功能,以及是否支持completionItem\resolve方法来为计算的补全项提供额外信息。

{
    ...
    "capabilities" : {
        "completionProvider" : {
            "resolveProvider": "true",
            "triggerCharacters": [ '.' ]
        }
        ...
    }
}

直接实现

class GoCompletionItemProvider implements vscode.CompletionItemProvider {
    public provideCompletionItems(
        document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
        Thenable<vscode.CompletionItem[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(getDisposable());
    ctx.subscriptions.push(
        vscode.languages.registerCompletionItemProvider(
            GO_MODE, new GoCompletionItemProvider(), '.', '\"'));
    ...
}

基础

您不支持解析提供者。

高级

您支持解析提供者,这些提供者计算用户选择的完成建议的附加信息。此信息与所选项目一起显示。

显示悬停

悬停显示鼠标光标下方符号/对象的信息。这通常是符号的类型和描述。

悬停时显示工作区和方法详细信息

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供悬停功能。

{
    ...
    "capabilities" : {
        "hoverProvider" : "true",
        ...
    }
}

此外,您的语言服务器需要响应textDocument/hover请求。

直接实现

class GoHoverProvider implements HoverProvider {
    public provideHover(
        document: TextDocument, position: Position, token: CancellationToken):
        Thenable<Hover> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerHoverProvider(
            GO_MODE, new GoHoverProvider()));
    ...
}

基础

显示类型信息,如果可用则包括文档。

高级

以与代码着色相同的样式对方法签名进行着色。

帮助理解函数和方法签名

当用户输入一个函数或方法时,显示有关正在调用的函数/方法的信息。

显示有关getPackageInfo方法的信息,包括必要的参数

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供签名帮助。

{
    ...
    "capabilities" : {
        "signatureHelpProvider" : {
            "triggerCharacters": [ '(' ]
        }
        ...
    }
}

此外,您的语言服务器需要响应textDocument/signatureHelp请求。

直接实现

class GoSignatureHelpProvider implements SignatureHelpProvider {
    public provideSignatureHelp(
        document: TextDocument, position: Position, token: CancellationToken):
        Promise<SignatureHelp> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerSignatureHelpProvider(
            GO_MODE, new GoSignatureHelpProvider(), '(', ','));
    ...
}

基础

确保签名帮助包含函数或方法参数的文档。

高级

没有额外的内容。

显示符号的定义

允许用户在使用变量/函数/方法的地方直接查看其定义。

右键点击一个变量、函数或方法,然后选择“转到定义”以跳转到定义

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供跳转到定义位置的功能。

{
    ...
    "capabilities" : {
        "definitionProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/definition请求。

直接实现

class GoDefinitionProvider implements vscode.DefinitionProvider {
    public provideDefinition(
        document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
        Thenable<vscode.Location> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerDefinitionProvider(
            GO_MODE, new GoDefinitionProvider()));
    ...
}

基础

如果一个符号是模棱两可的,你可以显示多个定义。

高级

没有额外的内容。

查找符号的所有引用

允许用户查看某个变量/函数/方法/符号被使用的所有源代码位置。

右键点击并选择“查找所有引用”以高亮显示该符号使用的所有位置

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供符号引用位置。

{
    ...
    "capabilities" : {
        "referencesProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/references请求。

直接实现

class GoReferenceProvider implements vscode.ReferenceProvider {
    public provideReferences(
        document: vscode.TextDocument, position: vscode.Position,
        options: { includeDeclaration: boolean }, token: vscode.CancellationToken):
        Thenable<vscode.Location[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerReferenceProvider(
            GO_MODE, new GoReferenceProvider()));
    ...
}

基础

返回所有引用的位置(资源URI和范围)。

高级

没有额外的内容。

高亮文档中所有出现的符号

允许用户查看打开的编辑器中某个符号的所有出现位置。

选择一个符号以高亮显示所有出现的位置

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供符号文档位置。

{
    ...
    "capabilities" : {
        "documentHighlightProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/documentHighlight请求。

直接实现

class GoDocumentHighlightProvider implements vscode.DocumentHighlightProvider {
    public provideDocumentHighlights(
        document: vscode.TextDocument, position: vscode.Position, token: vscode.CancellationToken):
        vscode.DocumentHighlight[] | Thenable<vscode.DocumentHighlight[]>;
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerDocumentHighlightProvider(
            GO_MODE, new GoDocumentHighlightProvider()));
    ...
}

基础

您返回编辑器的文档中找到引用的范围。

高级

没有额外的内容。

显示文档中的所有符号定义

允许用户快速导航到打开编辑器中的任何符号定义。

在打开的编辑器中使用@导航到符号定义

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供符号文档位置。

{
    ...
    "capabilities" : {
        "documentSymbolProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/documentSymbol请求。

直接实现

class GoDocumentSymbolProvider implements vscode.DocumentSymbolProvider {
    public provideDocumentSymbols(
        document: vscode.TextDocument, token: vscode.CancellationToken):
        Thenable<vscode.SymbolInformation[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerDocumentSymbolProvider(
            GO_MODE, new GoDocumentSymbolProvider()));
    ...
}

基础

返回文档中的所有符号。定义符号的种类,如变量、函数、类、方法等。

高级

没有额外的内容。

显示文件夹中的所有符号定义

允许用户快速导航到在VS Code中打开的文件夹(工作区)中任何位置的符号定义。

使用#在工作空间中导航到符号定义

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供全局符号位置。

{
    ...
    "capabilities" : {
        "workspaceSymbolProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应workspace/symbol请求。

直接实现

class GoWorkspaceSymbolProvider implements vscode.WorkspaceSymbolProvider {
    public provideWorkspaceSymbols(
        query: string, token: vscode.CancellationToken):
        Thenable<vscode.SymbolInformation[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerWorkspaceSymbolProvider(
            new GoWorkspaceSymbolProvider()));
    ...
}

基础

返回由打开文件夹中的源代码定义的所有符号。定义符号的种类,如变量、函数、类、方法等。

高级

没有额外的内容。

错误或警告的可能操作

在错误或警告旁边向用户提供可能的纠正措施。如果有可用的操作,错误或警告旁边会出现一个灯泡图标。当用户点击灯泡图标时,会显示可用的代码操作列表。

选择一个灯泡以查看可用的代码操作列表

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供代码操作。

{
    ...
    "capabilities" : {
        "codeActionProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/codeAction请求。

直接实现

class GoCodeActionProvider implements vscode.CodeActionProvider<vscode.CodeAction> {
    public provideCodeActions(
        document: vscode.TextDocument, range: vscode.Range | vscode.Selection,
        context: vscode.CodeActionContext, token: vscode.CancellationToken):
        Thenable<vscode.CodeAction[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerCodeActionsProvider(
            GO_MODE, new GoCodeActionProvider()));
    ...
}

基础

提供代码操作以纠正错误/警告操作。

高级

此外,提供源代码操作动作,如重构。例如,提取方法

CodeLens - 在源代码中显示可操作的上下文信息

向用户提供可操作的、上下文相关的信息,这些信息与源代码交错显示。

CodeLens 提供上下文

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供CodeLens结果,以及是否支持codeLens\resolve方法将CodeLens绑定到其命令。

{
    ...
    "capabilities" : {
        "codeLensProvider" : {
            "resolveProvider": "true"
        }
        ...
    }
}

此外,您的语言服务器需要响应textDocument/codeLens请求。

直接实现

class GoCodeLensProvider implements vscode.CodeLensProvider {
    public provideCodeLenses(document: TextDocument, token: CancellationToken):
        CodeLens[] | Thenable<CodeLens[]> {
    ...
    }

    public resolveCodeLens?(codeLens: CodeLens, token: CancellationToken):
         CodeLens | Thenable<CodeLens> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerCodeLensProvider(
            GO_MODE, new GoCodeLensProvider()));
    ...
}

基础

定义可用于文档的CodeLens结果。

高级

通过响应codeLens/resolve将CodeLens结果绑定到命令。

显示颜色装饰器

允许用户在文档中预览和修改颜色。

显示颜色选择器

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供颜色信息。

{
    ...
    "capabilities" : {
        "colorProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/documentColortextDocument/colorPresentation请求。

直接实现

class GoColorProvider implements vscode.DocumentColorProvider {
    public provideDocumentColors(
        document: vscode.TextDocument, token: vscode.CancellationToken):
        Thenable<vscode.ColorInformation[]> {
    ...
    }
    public provideColorPresentations(
        color: Color, context: { document: TextDocument, range: Range }, token: vscode.CancellationToken):
        Thenable<vscode.ColorPresentation[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerColorProvider(
            GO_MODE, new GoColorProvider()));
    ...
}

基础

返回文档中的所有颜色引用。为支持的颜色格式(例如rgb(...), hsl(...))提供颜色表示。

高级

没有额外的内容。

在编辑器中格式化源代码

为用户提供对整个文档格式化的支持。

右键点击并选择格式化代码

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供文档格式化功能。

{
    ...
    "capabilities" : {
        "documentFormattingProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/formatting请求。

直接实现

class GoDocumentFormatter implements vscode.DocumentFormattingEditProvider {
    provideDocumentFormattingEdits(
        document: vscode.TextDocument, options: vscode.FormattingOptions, token: vscode.CancellationToken)
        : vscode.ProviderResult<vscode.TextEdit[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerDocumentFormattingEditProvider(
            GO_MODE, new GoDocumentFormatter()));
    ...
}

基础

不提供格式化支持。

高级

您应始终返回导致源代码格式化的最小可能文本编辑。这对于确保诸如诊断结果之类的标记被正确调整且不会丢失至关重要。

在编辑器中格式化选定的行

为用户提供支持,以格式化文档中选定的行范围。

选择行,右键点击,然后选择格式化代码

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它提供对行范围格式化的支持。

{
    ...
    "capabilities" : {
        "documentRangeFormattingProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/rangeFormatting请求。

直接实现

class GoDocumentRangeFormatter implements vscode.DocumentRangeFormattingEditProvider{
    public provideDocumentRangeFormattingEdits(
        document: vscode.TextDocument, range: vscode.Range,
        options: vscode.FormattingOptions, token: vscode.CancellationToken):
        vscode.ProviderResult<vscode.TextEdit[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerDocumentRangeFormattingEditProvider(
            GO_MODE, new GoDocumentRangeFormatter()));
    ...
}

基础

不提供格式化支持。

高级

您应始终返回导致源代码格式化的最小可能文本编辑。这对于确保诸如诊断结果之类的标记被正确调整且不会丢失至关重要。

随着用户输入逐步格式化代码

为用户提供在输入时格式化文本的支持。

注意: 用户设置 editor.formatOnType 控制着在用户输入时源代码是否会被格式化。

输入代码时的格式化视觉指示器

语言服务器协议

在响应initialize方法时,您的语言服务器需要声明它提供用户输入时的格式化功能。它还需要告诉客户端在哪些字符上应触发格式化。moreTriggerCharacters是可选的。

{
    ...
    "capabilities" : {
        "documentOnTypeFormattingProvider" : {
            "firstTriggerCharacter": "}",
            "moreTriggerCharacter": [";", ","]
        }
        ...
    }
}

此外,您的语言服务器需要响应textDocument/onTypeFormatting请求。

直接实现

class GoOnTypingFormatter implements vscode.OnTypeFormattingEditProvider{
    public provideOnTypeFormattingEdits(
        document: vscode.TextDocument, position: vscode.Position,
        ch: string, options: vscode.FormattingOptions, token: vscode.CancellationToken):
        vscode.ProviderResult<vscode.TextEdit[]> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerOnTypeFormattingEditProvider(
            GO_MODE, new GoOnTypingFormatter()));
    ...
}

基础

不提供格式化支持。

高级

您应始终返回导致源代码格式化的最小可能文本编辑。这对于确保诸如诊断结果之类的标记被正确调整且不会丢失至关重要。

重命名符号

允许用户重命名符号并更新对该符号的所有引用。

重命名符号并更新所有引用到新名称

语言服务器协议

在响应initialize方法时,您的语言服务器需要宣布它支持重命名。

{
    ...
    "capabilities" : {
        "renameProvider" : "true"
        ...
    }
}

此外,您的语言服务器需要响应textDocument/rename请求。

直接实现

class GoRenameProvider implements vscode.RenameProvider {
    public provideRenameEdits(
        document: vscode.TextDocument, position: vscode.Position,
        newName: string, token: vscode.CancellationToken):
        Thenable<vscode.WorkspaceEdit> {
    ...
    }
}

export function activate(ctx: vscode.ExtensionContext): void {
    ...
    ctx.subscriptions.push(
        vscode.languages.registerRenameProvider(
            GO_MODE, new GoRenameProvider()));
    ...
}

基础

不提供重命名支持。

高级

返回需要执行的所有工作区编辑的列表,例如包含对符号引用的所有文件中的所有编辑。