程序化语言功能
程序化语言功能是一套由 vscode.languages.* API 驱动的智能编辑功能。在 Visual Studio Code 中提供动态语言功能的常见方法有两种。让我们以鼠标悬停为例
vscode.languages.registerHoverProvider('javascript', {
provideHover(document, position, token) {
return {
contents: ['Hover Content']
};
}
});
如上所示,vscode.languages.registerHoverProvider API 提供了一种为 JavaScript 文件提供鼠标悬停内容(hover contents)的简便方法。当此扩展激活后,每当您将鼠标悬停在某些 JavaScript 代码上时,VS Code 都会查询所有 JavaScript 的 HoverProvider,并在“悬停”小部件中显示结果。下面的语言功能列表和说明性 gif 图,可以方便您查找您的扩展需要哪个 VS Code API / LSP 方法。
另一种方法是实现一个支持 语言服务器协议 的语言服务器。其工作方式如下:
- 扩展提供了 JavaScript 的语言客户端(Language Client)和语言服务器(Language Server)。
- 语言客户端就像任何其他 VS Code 扩展一样,在 Node.js 扩展宿主(Extension Host)环境中运行。当它激活时,它会在另一个进程中启动语言服务器,并通过 语言服务器协议 与之通信。
- 您在 VS Code 中将鼠标悬停在 JavaScript 代码上
- VS Code 将鼠标悬停事件通知给语言客户端
- 语言客户端向语言服务器查询鼠标悬停结果,并将其发送回 VS Code
- VS Code 在“悬停”小部件中显示鼠标悬停结果
这个过程看起来更复杂,但它提供了两个主要优点:
- 语言服务器可以用任何语言编写
- 语言服务器可以被重用,为多个编辑器提供智能编辑功能
有关更深入的指南,请参阅语言服务器扩展指南。
语言功能列表
此列表包含每种语言功能的以下项目:
- VS Code 中语言功能的演示
- 相关的 VS Code API
- 相关的 LSP 方法
提供诊断信息
诊断信息(Diagnostics)是一种表示代码问题的方式。

语言服务器协议
您的语言服务器会向语言客户端发送 textDocument/publishDiagnostics 消息。该消息包含一个资源 URI 的诊断项数组。
注意:客户端不会主动向服务器请求诊断信息。服务器会将诊断信息推(push)给客户端。
直接实现
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)。
高级
您支持解析提供程序,该提供程序会计算用户选择的补全建议的额外信息。这些信息会与所选项目一起显示。
显示内联补全
内联补全(Inline completions)将多令牌(multi-token)的建议直接显示在编辑器中(*幽灵文本*)。

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

语言服务器协议
在响应 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 方法时,您的语言服务器需要声明它提供签名帮助(signature help)功能。
{
...
"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 方法时,您的语言服务器需要声明它提供符号引用位置(symbol reference locations)的功能。
{
...
"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 方法时,您的语言服务器需要声明它提供符号文档位置(symbol document locations)的功能。
{
...
"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 方法时,您的语言服务器需要声明它提供符号文档位置(symbol document locations)的功能。
{
...
"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 方法时,您的语言服务器需要声明它提供全局符号位置(global symbol locations)的功能。
{
...
"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()));
...
}
基本
返回打开文件夹中源代码定义的所有符号。定义符号的种类,例如变量、函数、类、方法等。
高级
无附加操作。
错误或警告的可能操作
在错误或警告旁边为用户提供可能的纠正操作。如果存在可用操作,则错误或警告旁边会出现一个灯泡。当用户点击灯泡时,会显示一个可用代码操作(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()));
...
}
基本
为错误/警告纠正操作提供代码操作。
高级
此外,还提供源代码操作,例如重构。例如,提取方法。
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()));
...
}
基本
不提供格式化支持。
高级
您应该始终返回最小化的文本编辑(text edits),以实现源代码的格式化。这对于确保诊断结果等标记能够正确调整并且不丢失至关重要。
格式化编辑器中的选定行
为用户提供格式化文档中选定行范围的支持。

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