上一节我们介绍了语言扩展的大致情况, 这一节我们开始深入一些细节.
诊断信息
语言扩展中一个重要的功能是代码扫描的诊断信息. 这个诊断信息是以 vscode.Diagnostic 为载体呈现的.
我们来看一下 vscode.Diagnostic 类的成员和与相关类的关系:
以小到大, 这些类为:
Position: 定位到一行上的一个字符的坐标
Range: 由起点和终点两个 Position 决定
Location: 一个 Range 配上一个 URI
DiagnosticRelatedInformation: 一个 Location 配一个 message
Diagnostic: 主体是一个 message 字符串, 一个 Range 和一个 DiagnosticRelatedInformation.
构造一个诊断信息
下面我们来构造一个诊断信息.
我们随便造一个 BASIC 语言的例子吧, 保存为 test.bas:
- dim i as integer
- for i = 1 to 10 step 1
- for i = 1 to 10 step 1
- print "*";
- next i
- next i
这个例子中, 循环控制变量在外循环和内循环中被重用, 导致外循环失效.
出现问题的 Range 是第 4 行的第 9 字符到第 10 字符. 位置是以 0 开始的, 所以我们构造 (3,8) 到(3,9)这样两个 Position 为首尾的 Range.
- new vscode.Range(
- new vscode.Position(3, 8), new vscode.Position(3, 9),
- )
有了 Range, 加上问题描述字符串, 和问题的严重程序三项, 就可以构造一个 Diagnostic 来.
- let diag1: vscode.Diagnostic = new vscode.Diagnostic(
- new vscode.Range(
- new vscode.Position(3, 8), new vscode.Position(3, 9),
- ),
- '循环变量重复赋值',
- vscode.DiagnosticSeverity.Hint,
- )
诊断相关信息
上一节提到, 有 Range, 有 message, 有严重程度这三项, 就可以构造一个 Diagnostic 信息出来.
除此之外, 还可以设置一些高级信息.
第一个是来源, 比如来自 eslint 某版本, 使用了某某规则之类的. 这个可以写到 Diagnostic 的 source 属性中.
diag1.source = 'basic-lint';
第二个是错误码, 有助于分类和查询. 这个是 code 属性来表示的, 既可以是一个数字, 也可以是一个字符串.
diag1.code = 102;
第三个是相关信息. 以上节例子来说, 我们说 i 已经被赋值过了, 那么可以进一步告诉开发者是在哪里被赋值过了. 所以要有一个 uri, 能找到代码的地址. 还要有一个 Range, 告诉在 uri 中的具体位置. 前面介绍过了, 这是一个 vscode.Location 结构.
- diag1.relatedInformation = [new vscode.DiagnosticRelatedInformation(
- new vscode.Location(document.uri,
- new vscode.Range(new vscode.Position(2, 4), new vscode.Position(2, 5))),
- '第一次赋值')];
下面我们把它们集合起来, 针对上面的 test.bas 进行错误提示. 主要就是将上面的提示信息写到传参进来的 DiagnosticCollection 中.
- import * as vscode from 'vscode';
- import * as path from 'path';
- export function updateDiags(document: vscode.TextDocument,
- collection: vscode.DiagnosticCollection): void {
- let diag1: vscode.Diagnostic = new vscode.Diagnostic(
- new vscode.Range(
- new vscode.Position(3, 8), new vscode.Position(3, 9),
- ),
- '循环变量重复赋值',
- vscode.DiagnosticSeverity.Hint,
- );
- diag1.source = 'basic-lint';
- diag1.relatedInformation = [new vscode.DiagnosticRelatedInformation(
- new vscode.Location(document.uri,
- new vscode.Range(new vscode.Position(2, 4), new vscode.Position(2, 5))),
- '第一次赋值')];
- diag1.code = 102;
- if (document && path.basename(document.uri.fsPath) === 'test.bas') {
- collection.set(document.uri, [diag1]);
- } else {
- collection.clear();
- }
- }
触发诊断信息的事件
下面我们在 plugin 的 activate 函数中增加到于刚才写的 updateDiags 函数的调用.
- const diag_coll = vscode.languages.createDiagnosticCollection('basic-lint-1');
- if (vscode.Windows.activeTextEditor) {
- diag.updateDiags(vscode.Windows.activeTextEditor.document, diag_coll);
- }
- context.subscriptions.push(vscode.Windows.onDidChangeActiveTextEditor(
- (e: vscode.TextEditor | undefined) => {
- if (e !== undefined) {
- diag.updateDiags(e.document, diag_coll);
- }
- }));
运行一下, 在新启动的 vscode 中打开 test.bas, 然后在最后任意编辑一下代码, 激活事情就可以触发. 运行界面如下:
从中可以看到, 第 4 行的 i 变量下面有一个提示, 错误码 102,source 是 basic-lint. 第二行是 DiagnosticRelatedInformation 的信息.
来源: https://yq.aliyun.com/articles/702631