链接与颜色
让文档中的文本「可点击、可调色」:URL 与文件路径变成可跳转链接,颜色代码变成可视化选择器。这些细节让代码编辑更直观。
文档链接 registerDocumentLinkProvider
registerDocumentLinkProvider 让文本变可点击链接:
typescript
import * as vscode from 'vscode';
vscode.languages.registerDocumentLinkProvider(
'plaintext',
{
provideDocumentLinks(
document: vscode.TextDocument,
token: vscode.CancellationToken
): vscode.DocumentLink[] {
// 返回链接数组
return [
new vscode.DocumentLink(
new vscode.Range(0, 0, 0, 20), // 链接范围
vscode.Uri.parse('https://example.com') // 目标
)
];
}
}
);DocumentLink 属性
typescript
const link = new vscode.DocumentLink(
range, // 可点击的文本范围
target, // 跳转目标
tooltip // 悬停提示(可选)
);| 属性 | 说明 |
|---|---|
range | 链接显示范围 |
target | 跳转目标 Uri |
tooltip | 悬停提示 |
实现链接检测
typescript
class LinkProvider implements vscode.DocumentLinkProvider {
provideDocumentLinks(document): vscode.DocumentLink[] {
const links: vscode.DocumentLink[] = [];
const text = document.getText();
// 1. 检测 URL
const urlRegex = /https?:\/\/[\w\-./?=&%]+/g;
let match: RegExpExecArray | null;
while ((match = urlRegex.exec(text))) {
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + match[0].length);
links.push(
new vscode.DocumentLink(
new vscode.Range(start, end),
vscode.Uri.parse(match[0])
)
);
}
// 2. 检测文件路径(./ 或 ../ 开头)
const fileRegex = /\.\.?\/[\w\-./\\]+\.\w+/g;
while ((match = fileRegex.exec(text))) {
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + match[0].length);
links.push(
new vscode.DocumentLink(
new vscode.Range(start, end),
vscode.Uri.file(match[0])
)
);
}
return links;
}
// 点击时解析完整目标(延迟处理)
resolveDocumentLink(link: vscode.DocumentLink) {
return link;
}
}颜色注册 registerColorProvider
registerColorProvider 提供颜色选择器:
typescript
vscode.languages.registerColorProvider(
'plaintext',
{
provideDocumentColors(
document: vscode.TextDocument,
token: vscode.CancellationToken
): vscode.ColorInformation[] {
// 返回颜色信息数组
return [
new vscode.ColorInformation(
new vscode.Range(0, 0, 0, 7), // 颜色文本范围
new vscode.Color(1, 0, 0, 1) // 颜色值(RGBA)
)
];
},
provideColorPresentations(
color: vscode.Color,
context: { document: vscode.TextDocument; range: vscode.Range }
): vscode.ColorPresentation[] {
// 返回颜色格式化方案
return [
new vscode.ColorPresentation('#ff0000')
];
}
}
);ColorInformation
typescript
const colorInfo = new vscode.ColorInformation(
range, // 颜色文本范围
new vscode.Color(1, 0, 0, 1) // R, G, B, A(0-1)
);颜色解析:Hex 与 RGB
typescript
class ColorProvider implements vscode.DocumentColorProvider {
provideDocumentColors(document): vscode.ColorInformation[] {
const colors: vscode.ColorInformation[] = [];
const text = document.getText();
// Hex 颜色 #RRGGBB
const hexRegex = /#([0-9a-fA-F]{6}|[0-9a-fA-F]{3})\b/g;
let match: RegExpExecArray | null;
while ((match = hexRegex.exec(text))) {
const hex = match[1];
const r = parseInt(hex.slice(0, 2), 16) / 255;
const g = parseInt(hex.slice(2, 4), 16) / 255;
const b = parseInt(hex.slice(4, 6), 16) / 255;
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + match[0].length);
colors.push(
new vscode.ColorInformation(
new vscode.Range(start, end),
new vscode.Color(r, g, b, 1)
)
);
}
// RGB 颜色 rgb(r, g, b)
const rgbRegex = /rgb\(\s*(\d+)\s*,\s*(\d+)\s*,\s*(\d+)\s*\)/g;
while ((match = rgbRegex.exec(text))) {
const [_, r, g, b] = match.map(Number);
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + match[0].length);
colors.push(
new vscode.ColorInformation(
new vscode.Range(start, end),
new vscode.Color(r / 255, g / 255, b / 255, 1)
)
);
}
return colors;
}
}ColorPresentation 颜色格式化
用户调整颜色后,选择格式化方案写回:
typescript
provideColorPresentations(
color: vscode.Color,
context: { document: vscode.TextDocument; range: vscode.Range }
): vscode.ColorPresentation[] {
const r = Math.round(color.red * 255);
const g = Math.round(color.green * 255);
const b = Math.round(color.blue * 255);
// 多种格式供用户选择
return [
// Hex 格式
new vscode.ColorPresentation(
`#${toHex(r)}${toHex(g)}${toHex(b)}`
),
// RGB 格式
new vscode.ColorPresentation(`rgb(${r}, ${g}, ${b})`),
// 带透明度格式
new vscode.ColorPresentation(
`rgba(${r}, ${g}, ${b}, ${color.alpha.toFixed(2)})`
)
];
}
function toHex(n: number): string {
return n.toString(16).padStart(2, '0').toUpperCase();
}ColorPresentation 附加文本
typescript
const presentation = new vscode.ColorPresentation('#ff0000');
presentation.textEdit = new vscode.TextEdit(
context.range, // 替换范围
'#ff0000' // 替换文本
);完整示例:CSS 颜色工具
typescript
import * as vscode from 'vscode';
// 命名颜色表
const NAMED_COLORS: Record<string, string> = {
red: '#ff0000',
green: '#00ff00',
blue: '#0000ff',
orange: '#ffa500',
purple: '#800080'
};
export function activate(context: vscode.ExtensionContext) {
// 链接 Provider:检测 URL 与文件
context.subscriptions.push(
vscode.languages.registerDocumentLinkProvider(
'plaintext',
{
provideDocumentLinks(document) {
const links: vscode.DocumentLink[] = [];
const text = document.getText();
const regex = /(https?:\/\/[^\s]+|\.\/[\w\-./]+\.\w+)/g;
let match: RegExpExecArray | null;
while ((match = regex.exec(text))) {
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + match[0].length);
const target = match[0].startsWith('http')
? vscode.Uri.parse(match[0])
: vscode.Uri.file(match[0]);
links.push(
new vscode.DocumentLink(
new vscode.Range(start, end),
target,
match[0]
)
);
}
return links;
}
}
)
);
// 颜色 Provider:命名颜色高亮
context.subscriptions.push(
vscode.languages.registerColorProvider(
'plaintext',
{
provideDocumentColors(document) {
const colors: vscode.ColorInformation[] = [];
Object.entries(NAMED_COLORS).forEach(([name, hex]) => {
const regex = new RegExp(`\\b${name}\\b`, 'g');
const text = document.getText();
let match: RegExpExecArray | null;
while ((match = regex.exec(text))) {
const start = document.positionAt(match.index);
const end = document.positionAt(match.index + name.length);
const r = parseInt(hex.slice(1, 3), 16) / 255;
const g = parseInt(hex.slice(3, 5), 16) / 255;
const b = parseInt(hex.slice(5, 7), 16) / 255;
colors.push(
new vscode.ColorInformation(
new vscode.Range(start, end),
new vscode.Color(r, g, b, 1)
)
);
}
});
return colors;
},
provideColorPresentations(color, context) {
const r = Math.round(color.red * 255);
const g = Math.round(color.green * 255);
const b = Math.round(color.blue * 255);
return [
new vscode.ColorPresentation(
`#${toHex(r)}${toHex(g)}${toHex(b)}`
),
new vscode.ColorPresentation(
`rgb(${r}, ${g}, ${b})`
)
];
}
}
)
);
}
function toHex(n: number): string {
return n.toString(16).padStart(2, '0');
}体验优化
| 场景 | 处理 |
|---|---|
| 误检测链接 | 收紧正则范围 |
| 颜色显示异常 | RGBA 值 0-1 归一化 |
| 多格式支持 | 提供多个 ColorPresentation |
| 性能 | 限制检测数量 |
常见问题
| 问题 | 处理 |
|---|---|
| 链接不可点 | 确认 target 是有效 Uri |
| 颜色选择器不出现 | 检查 ColorInformation 范围 |
| 颜色修改不写回 | 实现 provideColorPresentations |
| 覆盖范围过大 | 精确定位颜色文本范围 |
链接与颜色让文档内容「可视化、可交互」,是语言体验的锦上添花能力。