诊断与问题面板
诊断(Diagnostic)是编辑器的「健康检查」:错误、警告、提示信息显示在编辑器波浪线与问题面板中。它是语言服务最直观的能力。
创建诊断集合
languages.createDiagnosticCollection 创建诊断集合:
typescript
import * as vscode from 'vscode';
// 创建集合(名称全局唯一)
const diagnostics =
vscode.languages.createDiagnosticCollection('myExt');
// 添加诊断到文档
diagnostics.set(
document.uri,
[
new vscode.Diagnostic(
new vscode.Range(0, 0, 0, 10), // 范围
'这是一个诊断消息', // 消息
vscode.DiagnosticSeverity.Warning // 严重程度
)
]
);
// 清理
diagnostics.delete(document.uri); // 删除某文档
diagnostics.clear(); // 清空全部
diagnostics.dispose(); // 销毁Diagnostic 属性详解
typescript
const diagnostic = new vscode.Diagnostic(
range, // 出错范围
message, // 消息文本
severity // 严重程度
);
// 附加属性
diagnostic.source = 'myExt'; // 来源(问题面板显示)
diagnostic.code = 'E1001'; // 错误码
diagnostic.tags = [vscode.DiagnosticTag.Deprecated]; // 标签
diagnostic.relatedInformation = [ // 关联信息
{
location: new vscode.Location(
document.uri,
new vscode.Position(5, 0)
),
message: '此处定义'
}
];属性表
| 属性 | 作用 |
|---|---|
range | 波浪线范围 |
message | 显示消息 |
severity | 严重程度 |
source | 来源标识 |
code | 错误码 |
tags | 标签(弃用/未使用) |
relatedInformation | 关联位置信息 |
DiagnosticSeverity 严重程度
| 级别 | 波浪线 | 问题面板图标 |
|---|---|---|
Error | 红色 | 红色叉 |
Warning | 黄色 | 黄色三角 |
Information | 蓝色 | 蓝色圆圈 |
Hint | 灰色 | 灰色灯泡 |
typescript
// 严重程度映射
vscode.DiagnosticSeverity.Error
vscode.DiagnosticSeverity.Warning
vscode.DiagnosticSeverity.Information
vscode.DiagnosticSeverity.Hint实时诊断:文档变更联动
监听文档变更,实时更新诊断:
typescript
export function activate(context: vscode.ExtensionContext) {
const diagnostics =
vscode.languages.createDiagnosticCollection('myExt');
// 监听文档变更
context.subscriptions.push(
vscode.workspace.onDidChangeTextDocument((event) => {
// 只处理目标语言
if (event.document.languageId !== 'plaintext') {
return;
}
validateDocument(event.document, diagnostics);
})
);
// 监听打开文档
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument((document) => {
if (document.languageId === 'plaintext') {
validateDocument(document, diagnostics);
}
})
);
// 销毁清理
context.subscriptions.push(diagnostics);
}
// 文档验证逻辑
function validateDocument(
document: vscode.TextDocument,
collection: vscode.DiagnosticCollection
) {
const diagnostics: vscode.Diagnostic[] = [];
// 遍历每行检查
for (let line = 0; line < document.lineCount; line++) {
const text = document.lineAt(line).text;
// 检查行长度
if (text.length > 80) {
diagnostics.push(
new vscode.Diagnostic(
new vscode.Range(line, 80, line, text.length),
'行过长(超过 80 字符)',
vscode.DiagnosticSeverity.Warning
)
);
}
// 检查末尾空格
if (/\s+$/.test(text)) {
const match = text.match(/\s+$/);
if (match) {
diagnostics.push(
new vscode.Diagnostic(
new vscode.Range(line, match.index!, line, text.length),
'行尾有多余空格',
vscode.DiagnosticSeverity.Hint
)
);
}
}
}
// 更新集合
collection.set(document.uri, diagnostics);
}CodeActionProvider 代码快速修复
诊断配合代码操作实现「一键修复」:
typescript
import * as vscode from 'vscode';
// 代码操作提供器
class FixProvider implements vscode.CodeActionProvider {
provideCodeActions(
document: vscode.TextDocument,
range: vscode.Range,
context: vscode.CodeActionContext
): vscode.CodeAction[] {
const actions: vscode.CodeAction[] = [];
// 遍历当前诊断
for (const diagnostic of context.diagnostics) {
// 处理行过长诊断
if (diagnostic.message.includes('行过长')) {
const action = new vscode.CodeAction(
'自动换行',
vscode.CodeActionKind.QuickFix
);
action.diagnostics = [diagnostic];
action.isPreferred = true;
// 修复编辑:在 80 列处换行
action.edit = new vscode.WorkspaceEdit();
const pos = new vscode.Position(
diagnostic.range.start.line,
80
);
action.edit.insert(document.uri, pos, '\n');
actions.push(action);
}
// 处理行尾空格诊断
if (diagnostic.message.includes('行尾')) {
const action = new vscode.CodeAction(
'移除行尾空格',
vscode.CodeActionKind.QuickFix
);
action.diagnostics = [diagnostic];
action.edit = new vscode.WorkspaceEdit();
action.edit.delete(document.uri, diagnostic.range);
actions.push(action);
}
}
return actions;
}
}
export function activate(context: vscode.ExtensionContext) {
// 注册代码操作
context.subscriptions.push(
vscode.languages.registerCodeActionsProvider(
'plaintext',
new FixProvider()
)
);
}CodeAction 属性
| 属性 | 作用 |
|---|---|
title | 操作显示名称 |
kind | 操作类型(QuickFix/Refactor) |
diagnostics | 关联的诊断 |
edit | 修复编辑内容 |
command | 执行命令(备选) |
isPreferred | 是否首选修复 |
问题面板联动
诊断自动出现在「问题」面板:
问题面板
┌────────────────────────────────┐
│ [错误] 行 12: 缺少分号 │
│ [警告] 行 45: 行过长 │
└────────────────────────────────┘面板显示来源与错误码:
typescript
diagnostic.source = 'myExt';
diagnostic.code = 'E1001';完整示例:简单代码检查器
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
const diagnostics =
vscode.languages.createDiagnosticCollection('lint');
// 校验函数
const validate = (document: vscode.TextDocument) => {
const list: vscode.Diagnostic[] = [];
const text = document.getText();
// 检查 TODO 注释
if (text.includes('TODO')) {
list.push(
new vscode.Diagnostic(
new vscode.Range(0, 0, 0, 4),
'存在未完成的 TODO 标记',
vscode.DiagnosticSeverity.Information
)
);
}
// 检查重复空行
const blankRegex = /\n\s*\n\s*\n/g;
let match: RegExpExecArray | null;
while ((match = blankRegex.exec(text))) {
const position = document.positionAt(match.index);
list.push(
new vscode.Diagnostic(
new vscode.Range(
position,
document.positionAt(match.index + match[0].length)
),
'存在多余空行',
vscode.DiagnosticSeverity.Hint
)
);
}
diagnostics.set(document.uri, list);
};
// 触发时机
context.subscriptions.push(
vscode.workspace.onDidOpenTextDocument(validate),
vscode.workspace.onDidChangeTextDocument(
(event) => validate(event.document)
)
);
context.subscriptions.push(diagnostics);
}诊断性能优化
| 场景 | 处理 |
|---|---|
| 大文件 | 防抖校验(延迟触发) |
| 频繁输入 | 合并变更,减少校验次数 |
| 过时诊断 | 及时 delete 或 clear |
| 重复校验 | 只校验目标语言 |
常见问题
| 问题 | 处理 |
|---|---|
| 波浪线不显示 | 检查 range 是否有效 |
| 问题面板不出现 | 确认 severity 正确 |
| 修复不生效 | 检查 WorkspaceEdit 内容 |
| 诊断残留 | 文档关闭时 delete |
诊断与问题面板让插件能「指出问题」,配合代码修复让用户「一键解决」,是代码质量工具的核心能力。