专栏名称: 奇舞精选
《奇舞精选》是由奇舞团维护的前端技术公众号。除周五外,每天向大家推荐一篇前端相关技术文章,每周五向大家推送汇总周刊内容。
目录
相关文章推荐
常青藤爸爸  ·  《哪吒3》没出来之前,看看它也过瘾啊! ·  昨天  
夏天的陈小舒  ·  原来人生定“胜负”的核心是它 ·  2 天前  
夏天的陈小舒  ·  原来人生定“胜负”的核心是它 ·  2 天前  
51好读  ›  专栏  ›  奇舞精选

Visual Studio Code 插件开发中的语言功能

奇舞精选  · 公众号  ·  · 2024-09-11 18:00

主要观点总结

本文介绍了如何使用Visual Studio Code的插件开发语言功能,以CodeLens功能为例,通过vscode.languages API进行调用。文章涵盖了如何使用官方示例注册CodeLensProvider和注册命令,以及如何在命令面板和设置面板中控制CodeLens的显示。

关键观点总结

关键观点1: CodeLens功能介绍

CodeLens是Visual Studio Code中的一项实用功能,可以为开发者提供关于代码的上下文信息和快速操作能力。它可以在编辑器中的特定代码行上方显示可操作的、与上下文相关的信息,如代码引用次数、单元测试结果、代码更改历史等。

关键观点2: 使用vscode.languages.* API调用语言功能

Visual Studio Code通过提供一组API来实现丰富的语言功能。例如,通过调用vscode.languages.* API,可以注册CodeLensProvider以提供语言功能。

关键观点3: 注册CodeLensProvider

官方示例演示了如何注册CodeLensProvider。在extension.ts文件中,通过调用languages.registerCodeLensProvider()来注册CodeLensProvider。需要提供两个参数:语言ID和CodeLensProvider实例。

关键观点4: 注册命令

在extension.ts文件中,使用commands.registerCommand()来注册命令。可以注册多个命令,并在命令的响应函数中实现相应的功能。

关键观点5: 在命令面板控制CodeLens的显示

通过在package.json文件中配置commands和contribution,可以在命令面板中看到并控制CodeLens的显示。注册命令时绑定相应的响应函数,并在设置面板中控制CodeLens的显示开关。

关键观点6: 在设置中控制CodeLens的显示

在package.json文件的configuration部分配置属性,以在配置面板中显示并控制CodeLens的显示开关。例如,通过workspace.getConfiguration().get()获取配置值,并在响应函数中进行相应的操作。


正文

本文作者为 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"truetrue);
 });

 commands.registerCommand("codelens-sample.disableCodeLens"() => {
  workspace.getConfiguration("codelens-sample").update("enableCodeLens"falsetrue);
 });

 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 注册 Provider
  • registerCommand 注册命令

这里先来看下 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 {
  ......
 }

 public resolveCodeLens(codeLens: vscode.CodeLens, token: vscode.CancellationToken) {
  ......
 }
}

CodeLensProvider 需要提供两个方法

  • provideCodeLenses
  • resolveCodeLens
provideCodeLenses

展开看下代码:

public provideCodeLenses(document: vscode.TextDocument, token: vscode.CancellationToken): vscode.CodeLens[] | Thenable {

    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 文件中还注册了另外两个命令:







请到「今天看啥」查看全文