🚀 在 VS Code 中

程序化语言特性

程序化语言特性是由 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 都会查询所有 JavaScript 的 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 方法
createDiagnosticCollection PublishDiagnostics
registerCompletionItemProvider Completion & Completion Resolve
registerHoverProvider Hover
registerSignatureHelpProvider SignatureHelp
registerDefinitionProvider Definition
registerTypeDefinitionProvider TypeDefinition
registerImplementationProvider Implementation
registerReferenceProvider 参考
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

提供诊断

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

Diagnostics indicating a misspelled method name

语言服务器协议

您的语言服务器将 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);
    });
  })
}

基本

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

高级

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

显示代码补全建议

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

Code Completion prompting variable, method, and parameter names while writing code

语言服务器协议

在对 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(), '.', '\"'));
    ...
}

基本

您不支持 resolve 提供程序。

高级

您支持 resolve 提供程序,这些提供程序为用户选择的补全建议计算附加信息。此信息与选定的项一起显示。

显示悬停提示

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

Showing details about a workspace and a method when hovering over them

语言服务器协议

在对 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()));
    ...
}

基本

显示类型信息,并在可用时包含文档。

高级

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

函数和方法签名帮助

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

Showing information about the getPackageInfo method including the necessary parameters

语言服务器协议

在对 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(), '(', ','));
    ...
}

基本

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

高级

没有其他内容。

显示符号的定义

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

Right click a variable, function, or method and select "Go to Definition" to jump to the definition

语言服务器协议

在对 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()));
    ...
}

基本

如果一个符号是二义性的,您可以显示多个定义。

高级

没有其他内容。

查找符号的所有引用

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

Right clicking and selecting "Find All References" to highlight all the locations where that symbol is used

语言服务器协议

在对 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 和范围)。

高级

没有其他内容。

高亮显示文档中符号的所有出现位置

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

Select a symbol to highlight all occurrences

语言服务器协议

在对 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()));
    ...
}

基本

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

高级

没有其他内容。

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

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

Navigate to a symbol definition in the open editor using @

语言服务器协议

在对 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 中打开的文件夹(工作区)中任何位置的符号定义。

Navigate to symbol definitions in the workspace using #

语言服务器协议

在对 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()));
    ...
}

基本

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

高级

没有其他内容。

错误或警告的可能操作

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

Selecting a light bulb to view a list of available Code Actions

语言服务器协议

在对 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 providing context

语言服务器协议

在对 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 结果绑定到命令。

显示颜色装饰器

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

Showing the color picker

语言服务器协议

在对 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(...))提供颜色表示。

高级

没有其他内容。

格式化编辑器中的源代码

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

Right click and select format code

语言服务器协议

在对 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()));
    ...
}

基本

不提供格式化支持。

高级

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

格式化编辑器中选定的行

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

Select lines, right click, and select format code

语言服务器协议

在对 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 控制是否在用户键入时格式化源代码。

Visual indicators for formatting as code is typed

语言服务器协议

在对 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()));
    ...
}

基本

不提供格式化支持。

高级

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

重命名符号

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

Rename a symbol and update all references to the new name

语言服务器协议

在对 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()));
    ...
}

基本

不提供重命名支持。

高级

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