悬停提示 Hover
鼠标悬停在代码上弹出的信息卡片就是 Hover。它是最轻量的「按需文档」:不打扰阅读,需要时呈现详细说明、类型签名、文档链接。
注册 Hover Provider
languages.registerHoverProvider 为指定语言注册悬停提示:
typescript
import * as vscode from 'vscode';
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.languages.registerHoverProvider(
'typescript', // 目标语言 ID
{
provideHover(document, position, token) {
// 返回 Hover 内容
return new vscode.Hover(
'这是悬停提示内容'
);
}
}
)
);
}多语言注册
typescript
// 数组形式注册多种语言
vscode.languages.registerHoverProvider(
['typescript', 'javascript', 'vue'],
provider
);provideHover 返回值
provideHover 接收文档与位置,返回 Hover 对象:
typescript
provideHover(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken
): vscode.ProviderResult<vscode.Hover> {
// 返回 null/undefined 表示该位置无提示
return new vscode.Hover(contents);
}Hover 构造
typescript
// 构造 Hover(内容 + 可选范围)
const hover = new vscode.Hover(
contents, // MarkdownString | 字符串 | 数组
range // 可选:高亮范围
);返回 null
provideHover 返回 undefined 表示该位置没有悬停内容,VS Code 会继续查找其他 Provider:
typescript
provideHover(document, position, token) {
// 检查悬停位置
const word = document.getText(
document.getWordRangeAtPosition(position)
);
if (!shouldShowHover(word)) {
return undefined; // 让其他 Provider 处理
}
return new vscode.Hover(...);
}MarkdownString 内容
悬停内容用 MarkdownString 支持富文本:
typescript
provideHover(document, position) {
const contents = new vscode.MarkdownString();
// 追加段落文本
contents.appendMarkdown('### 函数说明\n');
contents.appendMarkdown('该函数用于**计算平均值**。\n\n');
// 追加代码块
contents.appendCodeblock(
'const avg = average([1, 2, 3]);',
'typescript'
);
// 支持主题图标
contents.supportThemeIcons = true;
contents.appendMarkdown('$(check) 已实现\n');
return new vscode.Hover(contents);
}MarkdownString 能力
| 方法 | 作用 |
|---|---|
appendMarkdown(text) | 追加 Markdown 文本 |
appendCodeblock(code, lang) | 追加代码块 |
appendText(text) | 追加纯文本(自动转义) |
supportThemeIcons | 启用 $(图标) 语法 |
isTrusted | 是否信任(允许命令链接) |
CodeDescription 附加代码位置
悬停内容可附带代码位置跳转(如跳转到定义):
typescript
provideHover(document, position) {
// 附加代码引用位置
const contents = new vscode.MarkdownString(
'`Array.prototype.map`\n\n对数组每个元素执行回调'
);
// 附加可跳转的代码描述
contents.appendMarkdown(
'\n\n[查看实现](command:vscode.open?'
+ encodeURIComponent(
JSON.stringify({ uri: 'file:///lib/impl.ts' })
) + ')'
);
return new vscode.Hover(contents);
}条件触发:语言与位置
语言条件
注册时已限定语言;运行时可通过 document.languageId 二次校验:
typescript
provideHover(document, position) {
if (document.languageId !== 'typescript') {
return undefined;
}
// ...
}位置条件
只对特定位置显示悬停:
typescript
provideHover(document, position) {
// 获取当前单词
const range = document.getWordRangeAtPosition(position);
if (!range) {
return undefined;
}
const word = document.getText(range);
// 只对已知关键字显示
const known = ['map', 'filter', 'reduce'];
if (!known.includes(word)) {
return undefined;
}
return new vscode.Hover(createDoc(word));
}上下文判断
结合文档内容做上下文感知:
typescript
provideHover(document, position) {
const line = document.lineAt(position.line).text;
// 注释中不显示提示
if (line.trim().startsWith('//')) {
return undefined;
}
// import 语句特殊处理
if (line.trim().startsWith('import')) {
return new vscode.Hover('导入语句');
}
return this.getGeneralHover(document, position);
}悬停内容动态加载
悬停内容可以异步生成,适合需要查询的场景:
typescript
provideHover(document, position) {
const word = getWordAtPosition(document, position);
if (!word) {
return undefined;
}
// 返回 Promise,异步加载悬停内容
return this.loadHoverContent(word);
}
async loadHoverContent(word: string): Promise<vscode.Hover> {
// 模拟异步查询
await new Promise((resolve) => setTimeout(resolve, 100));
// 从缓存或配置读取
const doc = this.docs.get(word) ?? '未找到文档';
return new vscode.Hover(
new vscode.MarkdownString(
`**${word}**\n\n${doc}`
)
);
}完整示例:自定义函数文档
typescript
import * as vscode from 'vscode';
// 内置函数文档表
const FUNC_DOCS: Record<string, string> = {
'average': '计算一组数字的平均值。\n\n```typescript\nconst avg = average([1, 2, 3]); // 2\n```',
'median': '计算一组数字的中位数。',
'mode': '计算一组数字的众数。'
};
export function activate(context: vscode.ExtensionContext) {
context.subscriptions.push(
vscode.languages.registerHoverProvider('plaintext', {
provideHover(document, position) {
// 获取悬停位置的单词
const range = document.getWordRangeAtPosition(position);
if (!range) {
return undefined;
}
const word = document.getText(range);
// 查找函数文档
const doc = FUNC_DOCS[word];
if (!doc) {
return undefined;
}
// 构建富文本悬停内容
const contents = new vscode.MarkdownString();
contents.appendMarkdown(`### \`${word}\`\n\n`);
contents.appendMarkdown(doc);
contents.appendMarkdown(
'\n\n$(book) 内置函数'
);
return new vscode.Hover(contents, range);
}
})
);
}Hover 显示优化
| 场景 | 处理 |
|---|---|
| 内容过长 | 精简 Markdown,避免大段文字 |
| 代码块 | 用 appendCodeblock 指定语言 |
| 图标 | supportThemeIcons 启用 |
| 多 Provider 冲突 | 返回 undefined 放行 |
常见问题
| 问题 | 处理 |
|---|---|
| 悬停不显示 | 检查语言 ID 注册是否匹配 |
| 内容空白 | 确认 provideHover 返回非 undefined |
| Markdown 不渲染 | 使用 MarkdownString 而非字符串 |
| 异步内容不更新 | 返回 Promise 并正确 resolve |
Hover 是语言服务的入门特性,掌握后可为任何语言补充「按需文档」体验。