程序化语言功能

程序化语言特性是一套由 vscode.languages.* API 驱动的智能编辑功能。在 Visual Studio Code 中提供动态语言特性通常有两种方式。让我们以 悬停 (Hover) 为例。

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

如上所示,vscode.languages.registerHoverProvider API 提供了一种为 JavaScript 文件提供悬停内容的简便方法。在该扩展激活后,每当你将鼠标悬停在某些 JavaScript 代码上时,VS Code 就会向所有针对 JavaScript 的 HoverProvider 发起查询,并将结果显示在悬停控件中。下方的语言特性列表和示意图为你提供了一种简单的方法,用于定位你的扩展需要使用哪种 VS Code API 或 LSP 方法。

另一种方法是实现一个支持 语言服务器协议 (Language Server Protocol) 的语言服务器。其工作方式如下:

  • 扩展为 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)
registerInlineCompletionItemProvider
registerHoverProvider Hover
registerSignatureHelpProvider SignatureHelp
registerDefinitionProvider Definition
registerTypeDefinitionProvider TypeDefinition
registerImplementationProvider Implementation
registerReferenceProvider 参考
registerDocumentHighlightProvider DocumentHighlight
registerDocumentSymbolProvider DocumentSymbol
registerCodeActionsProvider CodeAction
registerCodeLensProvider CodeLens & CodeLens 解析
registerDocumentLinkProvider DocumentLink & DocumentLink 解析
registerColorProvider DocumentColor & 颜色展示 (Color Presentation)
registerDocumentFormattingEditProvider 格式化
registerDocumentRangeFormattingEditProvider RangeFormatting
registerOnTypeFormattingEditProvider OnTypeFormatting
registerRenameProvider 重命名 (Rename) & 准备重命名 (Prepare Rename)
registerFoldingRangeProvider FoldingRange

提供诊断 (Diagnostics)

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

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 providers)。

高级

支持解析器,能够为用户选择的补全建议计算额外信息。该信息将显示在所选项的旁边。

显示内联补全

内联补全直接在编辑器中呈现多标记建议(幽灵文本)。

Inline Completions suggesting code as ghost text while writing code

直接实现

vscode.languages.registerInlineCompletionItemProvider({ language: 'javascript' }, {
    provideInlineCompletionItems(document, position, context, token) {
        const result: vscode.InlineCompletionList = {
            items: [],
            commands: [],
        };

        ...

        return result;
    }
});

你可以在 内联补全示例扩展 中查看完整示例。

基础

仅针对特定语言,根据当前行内容,为众所周知的模式返回内联补全。

高级

基于整个文档或工作区的内容以及更复杂的模式返回内联补全。

显示悬停提示

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

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 方法时,你的语言服务器需要声明它提供转到定义 (goto-definition) 的位置。

{
    ...
    "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 方法时,你的语言服务器需要声明它提供代码操作 (Code Actions)。

{
    ...
    "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()));
    ...
}

基础

为修正错误/警告的操作提供代码操作。

高级

此外,提供诸如重构之类的源代码操作。例如,提取方法 (Extract Method)

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

基础

不提供重命名支持。

高级

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

© . This site is unofficial and not affiliated with Microsoft.