本文作者为 360 奇舞团前端开发工程师
本文将以 CodeLens 功能为例,通过 vscode.languages.*
API 调用来讲解如何使用 Visual Studio Code 插件开发中的语言功能(Language Feature)。
Visual Studio Code 的语言功能可以提供智能编辑能力。VS Code 本身并不提供内置的语言支持,而是提供了一组 API 来实现丰富的语言功能。例如,VS Code 包含一个 HTML 扩展,使 VS Code 能够为 HTML 文件显示语法高亮。
CodeLens 则是 Visual Studio Code 中一项非常实用的功能,它可以为开发者提供关于代码的上下文信息和快速操作的能力。
CodeLens 在编辑器中的特定代码行上方显示可操作的、与上下文相关的信息。这些信息可以是代码引用次数、单元测试结果、代码更改历史、Bug 追踪、任务关联等。
如下图示例:
VS Code 的语言功能(Language Feature)
语言功能大致分为两类:
声明式语言功能
语法高亮
代码片段补全
括号匹配
括号自动闭合
括号自动包围
注释切换
自动缩进
折叠(通过标记)
程序化语言功能
自动补全
错误检查
跳转到定义位置
本文的例子 CodeLens 功能属于程序化语言功能。
程序化语言功能通常由语言服务器提供支持,语言服务器是一个通过分析项目代码以提供动态功能的程序。
有两种方法可以调用程序化语言功能:
1. 通过 vscode.languages.*
API 调用。
2. 通过启动一个语言服务器,这个服务器需要支持语言服务器协议。
下面我们使用第一种方式实现 CodeLens 功能。
1. 使用语言服务 API 实现 CodeLens 功能
官方给了一个简单的例子 codelens-sample[1]
官方例子演示:
启动 codelens-sample 项目
首先,从地址将示例插件下载到本地,使用命令npm install
安装依赖。然后在调试视图中运行插件,就会在新打开的 VS code 窗口中运行插件了。
可以看到 codelens-sample 示例插件为每一行代码都添加了 CodeLens 功能。
codelens-sample 项目的主要文件有两个:
extension.ts
文件的任务是注册 Provider ,以及注册相关命令(registerCommand)。CodelensProvider.ts
则是定义了一个 codelensProvider 供extension.ts
注册时使用。
extension.ts
文件
内容如下:
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import { ExtensionContext, languages, commands, Disposable, workspace, window } from 'vscode';
import { CodelensProvider } from './CodelensProvider';
// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
let disposables: Disposable[] = [];
export function activate(context: ExtensionContext) {
const codelensProvider = new CodelensProvider();
languages.registerCodeLensProvider("*", codelensProvider);
commands.registerCommand("codelens-sample.enableCodeLens", () => {
workspace.getConfiguration("codelens-sample").update("enableCodeLens", true, true);
});
commands.registerCommand("codelens-sample.disableCodeLens", () => {
workspace.getConfiguration("codelens-sample").update("enableCodeLens", false, true);
});
commands.registerCommand("codelens-sample.codelensAction", (args: any) => {
window.showInformationMessage(`CodeLens action clicked with args=${args}`);
});
}
// this method is called when your extension is deactivated
export function deactivate() {
if (disposables) {
disposables.forEach(item => item.dispose());
}
disposables = [];
}
文件主要做了两件事情:
registerCodeLensProvider
注册 ProviderregisterCommand
注册命令
这里先来看下 registerCodeLensProvider
,下个标题再说 registerCommand
。
registerCodeLensProvider
languages.registerCodeLensProvider("*", codelensProvider)
注册时调用了 API languages.registerCodeLens
。
languages.registerCodeLensProvider
需要两个参数。
第一个参数是语言 ID,决定了我们注册的这个 Provider 会被应用于哪些类型的文件。*
表示所有文件都会被应用这个 Provider。
如果换成 typescript
:
languages.registerCodeLensProvider("typescript", codelensProvider);
我们就只能在 .ts
后缀的文件中看到 codelens。
如果换成 ["typescript", "javascript"]
:
languages.registerCodeLensProvider(["typescript", 'javascript'], codelensProvider);
则只能在 .ts
和 .js
后缀的文件中看到 codelens。以此类推。
第二个参数要求是 CodeLensProvider 类型。这个 Provider 的作用就是在代码中以一横排的方式显示命令。
CodelensProvider.ts
文件
看下如何定义 CodelensProvider。
import * as vscode from 'vscode';
/**
* CodelensProvider
*/
export class CodelensProvider implements vscode.CodeLensProvider {
private codeLenses: vscode.CodeLens[] = [];
......
constructor() {
this.regex = /(.+)/g;
vscode.workspace.onDidChangeConfiguration((_) => {
......
});
}
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
......
}
public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) {
......
}
}
CodeLensProvider 需要提供两个方法
provideCodeLenses
resolveCodeLens
provideCodeLenses
展开看下代码:
public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) {
this.codeLenses = [];
const regex = new RegExp(this.regex);
const text = document.getText();
let matches;
while ((matches = regex.exec(text)) !== null) {
const line = document.lineAt(document.positionAt(matches.index).line);
const indexOf = line.text.indexOf(matches[0]);
const position = new vscode.Position(line.lineNumber, indexOf);
const range = document.getWordRangeAtPosition(position, new RegExp(this.regex));
if (range) {
this.codeLenses.push(new vscode.CodeLens(range));
}
}
return this.codeLenses;
}
return [];
}
document
参数是指当前文档。通过 document.getText()
方法可以获取到文本内容。
const text = document.getText();
然后将文档内容与定义的正则表达式进行匹配。
this.regex = /(.+)/g;
......
const regex = new RegExp(this.regex);
matches = regex.exec(text)
codelens-sample 示例之所以会在每一行都显示 codelens 就是因为在这里匹配到了每一行。
provideCodeLenses 要求返回一个数组、 undefined
或 null
。数组项要是 CodeLens 实例。
this.codeLenses.push(new vscode.CodeLens(range))
上行代码中的 range
是指代码范围,这个范围不能超过一行。
最终每一个匹配到的代码上方都会出现 CodeLens 命令。
resolveCodeLens
命令行的内容则是在 resolveCodeLens 方法中添加的。
public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) {
if (vscode.workspace.getConfiguration("codelens-sample").get("enableCodeLens", true)) {
codeLens.command = {
title: "Codelens provided by sample extension",
tooltip: "Tooltip provided by sample extension",
command: "codelens-sample.codelensAction",
arguments: ["Argument 1", false]
};
return codeLens;
}
return null;
}
resolveCodeLens 的参数 codeLens
就是 provideCodeLenses 匹配到并返回的 codeLens 实例 。在 codeLens.command
中配置 UI 显示的命令文字。
command 各属性的含义如下:
command.title
:在 UI 中显示的命令名command.tooltip
:在 UI 中展示的命令提示,如图:command.command
:实际命令处理程序的标识符(由commands.registerCommand
进行注册)command.arguments
:命令处理程序的参数
registerCommand
现在回到上文 extension.ts
文件中提到的 registerCommand 命令注册。
command.command
中用到的程序标识符 codelens-sample.codelensAction
是在 extension.ts
文件中定义的:
commands.registerCommand("codelens-sample.codelensAction", (args: any, aa) => {
window.showInformationMessage(`CodeLens action clicked with args=${args}`);
});
args
参数就是通过 command.arguments
传过来的。
例子中点击命令会在右下角弹出一个提示,提示信息可以拿到参数值 Argument 1
并显示出来。
在命令面板中控制 codelens 是否显示
在 Visual Studio Code 中最常用的就是命令面板,那么如何在命令面板注册命令呢?
需要两个步骤:
第一步:在代码中注册命令,并添加响应函数。
第二步:在 package.json
中绑定命令标识。这样才能在命令面板中看到命令。
例子中是有做这个功能的。
可以注意到 extension.ts
文件中还注册了另外两个命令:
commands.registerCommand("codelens-sample.enableCodeLens", () => {
workspace.getConfiguration("codelens-sample").update("enableCodeLens", true, true);
});
commands.registerCommand("codelens-sample.disableCodeLens", () => {
workspace.getConfiguration("codelens-sample").update("enableCodeLens", false, true);
});
这里绑定了命令触发时的响应函数。两个命令都是在被触发时去改变 enableCodeLens
的值。在 CodelensProvider.ts
文件中 codelens 会被显示的前提条件就是 enableCodeLens
的值为 true
。这个值是是从哪里取的呢?会在后标题“在设置中控制 codelens 的显示”中说到。
接下来看 package.json
文件的配置:
{
"contributes": {
"commands": [
{
"title": "Enable CodeLens",
"command": "codelens-sample.enableCodeLens",
"category": "CodeLens Sample"
},
{
"title": "Disable Codelens",
"command": "codelens-sample.disableCodeLens",
"category": "CodeLens Sample"
}
]
}
}
字段含义如前边 commands
一样。
完成这两步,就可以在命令面板中控制 codelens 是否显示了。
在设置中控制 codelens 的显示
前文提到过 enableCodeLens
,这个值就是从设置配置里面获取的。
设置配置也是在 package.json
配置的:
"contributes": {
"configuration": {
"properties": {
"codelens-sample.enableCodeLens": {
"type": "boolean",
"default": false
}
}
}
}
配置的的 enableCodeLens
属性会以 Enable Code Lens 的格式在配置面板中显示。
codelens-sample
则是一个命名空间。codelens-sample.enableCodeLens
的值可以通过:
workspace.getConfiguration("codelens-sample").get("enableCodeLens")
获取到,同时可以进行更改值等操作。
以上就是如何在 VS Code 添加 CodeLens 功能的简单例子,希望可以对你有帮助。
感谢阅读~
参考资料
[1]
官方例子(codelens-sample): https://github.com/microsoft/vscode-extension-samples/tree/main/codelens-sample
- END -
如果您关注前端+AI 相关领域可以扫码进群交流
添加小编微信进群😊
关于奇舞团
奇舞团是 360 集团最大的大前端团队,非常重视人才培养,有工程师、讲师、翻译官、业务接口人、团队 Leader 等多种发展方向供员工选择,并辅以提供相应的技术力、专业力、通用力、领导力等培训课程。奇舞团以开放和求贤的心态欢迎各种优秀人才关注和加入奇舞团。