VisualStudioCode插件开发中的语言功能

以云看科技 2024-09-13 01:43:05

本文将以 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 belowimport { 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 executedlet 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 deactivatedexport function deactivate() { if (disposables) { disposables.forEach(item => item.dispose()); } disposables = [];}

文件主要做了两件事情:

registerCodeLensProvider 注册 ProviderregisterCommand 注册命令

这里先来看下 registerCodeLensProvider ,下个标题再说 registerCommand。

registerCodeLensProviderlanguages.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 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 需要提供两个方法

provideCodeLensesresolveCodeLensprovideCodeLenses

展开看下代码:

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

作者:崔丽

来源-微信公众号:奇舞精选

出处:https://mp.weixin.qq.com/s/dvTFEYPaY4xA-l4ZLo0M7Q

0 阅读:18

以云看科技

简介:感谢大家的关注