大家好,我是编程小6,很高兴遇见你,有问题可以及时留言哦。
编程语言特性(Programmatic Language Features)是由 vscode.languages.* 系列接口提供的智能编辑能力,通常有两种方式来提供动态语言特性,以Hover为例:
vscode.languages.registerHoverProvider('javascript', {
provideHover(document, position, token) {
return {
contents: ['Hover Content']
};
}
});
如上所见 vscode.languages.registerHoverProvider 接口实现了一个方便的为JavaScript文件提供hover contents
的方式。本文第一章节的“语言特性列表”可以帮助你找到插件所需要的API
/LSP
除此之外,另一种方式是实现一个遵循语言服务器协议(Language Server Protocol,下文简称LSP)的语言服务器(Language Server),其原理如下:
LC
可以看做是一个基本的VS Code
插件,运行在Node.js插件上下文中,当LC
被激活时它将会启动LS
进程并利用LSP
与之通信VS Code
hover了一段JavaScript代码VS Code
将hover告知LC
LC
从LS
处查询hover的结果,让后将其返回给VS Code
VS Code
展示hover的结果上述过程比较繁琐,但有两个比较大的长处:
LS
是语言无关的,可以用任何开发语言LS
是可以在其它编辑器里被复用以后会有专门文章介绍更多关于LS
的信息。本文将会重点介绍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 |
我们利用代码诊断指出当前代码存在的问题
LS
发送textDocument/publishDiagnostics
消息给LC
,消息是一个数组,每一项是诊断结果。需要注意的是,消息的传递不是LC
发送请求,而是由LS
主动推送给LC
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);
});
})
}
在每次保存的时候做最低限度的代码诊断,如果做的好一些的话,可以仅对本次保存更改的内容做诊断,仅针对打开的文件
给文件夹下的每一个文件无论是否打开,都做代码诊断
代码补全主要是为用户提供与上下文相关的建议
LS
在onInitialize
方法中申明支持代码补全以及是否支持completionItem\resolve
方法提供额外的代码补全信息
{
...
"capabilities" : {
"completionProvider" : {
"resolveProvider": "true",
"triggerCharacters": [ '.' ]
}
...
}
}
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(), '(', ','));
...
}
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(), '.', '\"'));
...
}
不提供completionItem\resolve
提供completionItem\resolve
支持,给用户额外的复杂建议的附加信息
hover功能会展示当前鼠标下代码的信息,通常是描述或数据类型
需要LS
在onInitialize
方法中申明支持hover功能:
{
...
"capabilities" : {
"hoverProvider" : "true",
...
}
}
此外,LS
需要对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()));
// ...
}
展示类型信息,如果可能的话增加一些备注
用与代码着色相同的样式着色方法签名
当用户输入方法或函数时展示其信息
需要LS
在onInitialize
方法中申明支持帮助功能:
{
...
"capabilities" : {
"signatureHelpProvider" : {
"triggerCharacters": [ '(' ]
}
...
}
}
此外,LS
需要对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(), '(', ','));
...
}
为函数或class方法提供参数的文档
允许用户在变量、函数、方法使用的地方查看其定义
需要LS
在onInitialize
方法中申明支持跳转到定义位置的功能:
{
...
"capabilities" : {
"definitionProvider" : "true"
...
}
}
此外,LS
需要对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()));
...
}
支持展示多个定义
支持用户查看某个变量、函数、方法、符号的所有使用位置
需要LS
在onInitialize
方法中申明支持符号引用定位的功能:
{
...
"capabilities" : {
"referencesProvider" : "true"
...
}
}
此外,LS
需要对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和范围)
方便用户查看整个文档中同一个字符的使用情况
需要LS在onInitialize方法中申明支持符号文档定位的功能:
{
...
"capabilities" : {
"documentHighlightProvider" : "true"
...
}
}
此外,LS
需要对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()));
...
}
返回符号的引用位置
支持用户快速浏览一个打开的文档里的全部符号定义
需要LS
在onInitialize
方法中申明支持符号文档定位的功能:
{
...
"capabilities" : {
"documentSymbolProvider" : "true"
...
}
}
此外,LS
需要对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()));
...
}
返回一个文档里的全部应用,定义好符号的类型,如变量、函数、class、方法等
支持用户快速浏览工作区内的所有符号定义
需要LS
在onInitialize
方法中申明支持全局符号定位的功能:
{
...
"capabilities" : {
"workspaceSymbolProvider" : "true"
...
}
}
此外,LS
需要对textDocument/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()));
...
}
返回工作区内全部的字符定义,定义好符号的类型,如变量、函数、class、方法等
在错误或警告旁边向用户提供可能的纠正措施,通过一个灯泡来提示用户,当用户点击了灯泡时显示一系列可用的修复措施
需要LS
在onInitialize
方法中申明支持编码指导的功能:
{
...
"capabilities" : {
"codeActionProvider" : "true"
...
}
}
此外,LS
需要对textDocument/codeAction
做出回应
class GoCodeActionProvider implements vscode.CodeActionProvider {
public provideCodeActions(
document: vscode.TextDocument, range: vscode.Range,
context: vscode.CodeActionContext, token: vscode.CancellationToken):
Thenable<vscode.Command[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
GO_MODE, new GoCodeActionProvider()));
...
}
提供错误或告警的修复措施
提供诸如代码重构等自动化措施
为用户提供可操作的上下文信息,显示在源代码中
需要LS
在onInitialize
方法中申明支持CodeLens功能以及是否支持codeLens\resolve
:
{
...
"capabilities" : {
"codeLensProvider" : {
"resolveProvider": "true"
}
...
}
}
此外,LS
需要对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
结果绑定给一个命令作为对codeLens/resolve
的回应
支持用户预览和修改文档中的颜色
需要LS
在onInitialize
方法中申明提供颜色信息的功能:
{
...
"capabilities" : {
"colorProvider" : "true"
...
}
}
此外,LS
需要对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)提供颜色表示
提供用户格式化整个文档的能力
需要LS
在onInitialize
方法中申明支持文档格式化功能:
{
...
"capabilities" : {
"documentFormattingProvider" : "true"
...
}
}
此外,LS
需要对textDocument/formatting
做出回应
class GoDocumentFormatter implements vscode.DocumentFormattingEditProvider {
public formatDocument(document: vscode.TextDocument):
Thenable<vscode.TextEdit[]> {
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider(
GO_MODE, new GoDocumentFormatter()));
...
}
不提供格式化支持
返回尽可能小的导致源代码格式化的文本编辑。以确保如诊断结果的调整是正确的,而不是丢失
需要LS
在onInitialize
方法中申明支持格式化选中内容的功能:
{
...
"capabilities" : {
"documentRangeFormattingProvider" : "true"
...
}
}
此外,LS
需要对textDocument/rangeFormatting
做出回应
class GoDocumentRangeFormatter implements vscode.DocumentRangeFormattingEditProvider{
public provideDocumentRangeFormattingEdits(
document: vscode.TextDocument, range: vscode.Range,
options: vscode.FormattingOptions, token: vscode.CancellationToken):
Thenable<vscode.TextEdit[]>;
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerDocumentRangeFormattingEditProvider(
GO_MODE, new GoDocumentRangeFormatter()));
...
}
不提供格式化支持
返回尽可能小的导致源代码格式化的文本编辑。以确保如诊断结果的调整是正确的,而不是丢失
支持用户在键入一行后自动格式化,用户配置的editor.formatOnType
是否启用此功能
需要LS在onInitialize方法中申明支持自动格式化的功能,同时还需要告知LC
哪个字符会触发自动格式化,moreTriggerCharacters
是可选的:
{
...
"capabilities" : {
"documentOnTypeFormattingProvider" : {
"firstTriggerCharacter": "}",
"moreTriggerCharacter": [";", ","]
}
...
}
}
此外,LS
需要对textDocument/onTypeFormatting
做出回应
class GoOnTypingFormatter implements vscode.OnTypeFormattingEditProvider{
public provideOnTypeFormattingEdits(
document: vscode.TextDocument, position: vscode.Position,
ch: string, options: vscode.FormattingOptions, token: vscode.CancellationToken):
Thenable<vscode.TextEdit[]>;
...
}
}
export function activate(ctx: vscode.ExtensionContext): void {
...
ctx.subscriptions.push(
vscode.languages.registerOnTypeFormattingEditProvider(
GO_MODE, new GoOnTypingFormatter()));
...
}
不提供格式化支持
返回尽可能小的导致源代码格式化的文本编辑。以确保如诊断结果的调整是正确的,而不是丢失
允许用户重命名一个符号并且自动将所有引用都更新
需要LS
在onInitialize
方法中申明支持重命名功能:
{
...
"capabilities" : {
"renameProvider" : "true"
...
}
}
此外,LS
需要对textDocument/references
做出回应
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()));
...
}
不提供重命名
返回需要执行的所有工作空间编辑列表,例如,跨文件的字符引用编辑
VS Code插件开发教程(1) Overview
VS Code插件开发教程(2) 起步 Get Started
VS Code插件开发教程(3) 插件能力一览 Capabilities
VS Code插件开发教程(4) 插件指南 Extension Guidesd
VS Code插件开发教程(5) 命令的使用 Command
VS Code插件开发教程(6) 颜色主题一览 Color Theme
VS Code插件开发教程(7) 树视图 Tree View
VS Code插件开发教程(8) Webview
VS Code插件开发教程(9)构建自定义编辑器 Custom Editor
VS Code插件开发教程(10)编程式语言特性 Programmatic Language Features
VS Code插件开发教程(11)语言服务器入门 Language Server Extension Guide