程序化语言功能
程序化语言特性是一套由 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 方法
提供诊断 (Diagnostics)
诊断是指出代码问题的一种方式。

语言服务器协议
你的语言服务器向语言客户端发送 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(), '.', '\"'));
...
}
基础
不支持解析器 (resolve providers)。
高级
支持解析器,能够为用户选择的补全建议计算额外信息。该信息将显示在所选项的旁边。
显示内联补全
内联补全直接在编辑器中呈现多标记建议(幽灵文本)。

直接实现
vscode.languages.registerInlineCompletionItemProvider({ language: 'javascript' }, {
provideInlineCompletionItems(document, position, context, token) {
const result: vscode.InlineCompletionList = {
items: [],
commands: [],
};
...
return result;
}
});
你可以在 内联补全示例扩展 中查看完整示例。
基础
仅针对特定语言,根据当前行内容,为众所周知的模式返回内联补全。
高级
基于整个文档或工作区的内容以及更复杂的模式返回内联补全。
显示悬停提示
悬停提示显示鼠标光标下方符号/对象的相关信息。这通常是符号的类型和描述。

语言服务器协议
在响应 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()));
...
}
基础
显示类型信息,并在可用时包含文档说明。
高级
以与代码着色相同的风格对方法签名进行着色。
辅助函数和方法签名
当用户输入函数或方法时,显示正在调用的函数/方法的相关信息。

语言服务器协议
在响应 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 方法时,你的语言服务器需要声明它提供转到定义 (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()));
...
}
基础
如果一个符号存在歧义,你可以显示多个定义。
高级
无其他附加内容。
查找符号的所有引用
允许用户查看特定变量/函数/方法/符号在源代码中的所有使用位置。

语言服务器协议
在响应 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 方法时,你的语言服务器需要声明它提供代码操作 (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 - 在源代码中显示可操作的上下文信息
为用户提供显示在源代码中间的可操作上下文信息。

语言服务器协议
在响应 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/documentColor 和 textDocument/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()));
...
}
基础
不提供重命名支持。
高级
返回需要执行的所有工作区编辑列表,例如跨所有包含该符号引用的文件的编辑。