如何创建VS Code扩展实现自定义脚本文本可见性的命令/视图切换?
自定义脚本内容显示切换的最优实现方案
核心思路
基于VS Code原生API,结合你正在开发的语言工具链,实现标识符的显示/隐藏切换,以下是三个不同复杂度的方案,按需选择:
方案一:文本装饰隐藏(快速上手)
这是最适合新手的快速实现方式,通过VS Code的文本装饰API把标识符“隐藏”,不会修改原始文档内容:
- 先定义一个隐藏样式的装饰类型:
// 扩展激活时创建装饰类型 const hiddenIdDecoration = vscode.window.createTextEditorDecorationType({ opacity: '0', // 完全透明 letterSpacing: '-999px', // 消除占位宽度,避免布局偏移 rangeBehavior: vscode.DecorationRangeBehavior.ClosedClosed }); - 编写扫描标识符位置的函数:
function getIdentifierRanges(doc: vscode.TextDocument): vscode.DecorationOptions[] { const ranges: vscode.DecorationOptions[] = []; const text = doc.getText(); // 替换成你匹配标识符的正则,比如假设ID格式是 [XXX] 结尾 const regex = /\s*\[\w+\]$/gm; let match; while ((match = regex.exec(text)) !== null) { const start = doc.positionAt(match.index); const end = doc.positionAt(match.index + match[0].length); ranges.push({ range: new vscode.Range(start, end) }); } return ranges; } - 注册切换命令,控制装饰显示:
let showIdentifiers = true; vscode.commands.registerCommand('yourExt.toggleStringIds', () => { showIdentifiers = !showIdentifiers; const activeEditor = vscode.window.activeTextEditor; if (activeEditor && activeEditor.document.languageId === '你的自定义语言ID') { const decorations = showIdentifiers ? [] : getIdentifierRanges(activeEditor.document); activeEditor.setDecorations(hiddenIdDecoration, decorations); } }); - 监听文档变化,自动更新装饰:
vscode.window.onDidChangeActiveTextEditor(updateDecorations); vscode.workspace.onDidChangeTextDocument(e => { if (vscode.window.activeTextEditor?.document === e.document) { updateDecorations(); } }); function updateDecorations() { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor || activeEditor.document.languageId !== '你的自定义语言ID') return; const decorations = showIdentifiers ? [] : getIdentifierRanges(activeEditor.document); activeEditor.setDecorations(hiddenIdDecoration, decorations); }
方案二:虚拟文档双视图(完全分离)
如果需要同时打开“作者视图”和“开发者视图”,可以用虚拟文档实现两个视图内容独立:
- 注册虚拟文档内容提供者:
class AuthorViewProvider implements vscode.TextDocumentContentProvider { provideTextDocumentContent(uri: vscode.Uri): string { // 从查询参数获取原始文档URI const originalUri = vscode.Uri.parse(uri.query); const originalDoc = vscode.workspace.textDocuments.find(d => d.uri.toString() === originalUri.toString()); if (!originalDoc) return ''; // 过滤每行标识符,替换成你的实际逻辑 return originalDoc.getText().split('\n') .map(line => line.replace(/\s*\[\w+\]$/, '')) // 移除结尾的[ID] .join('\n'); } } // 注册提供者 vscode.workspace.registerTextDocumentContentProvider('author-view', new AuthorViewProvider()); - 注册打开作者视图的命令:
vscode.commands.registerCommand('yourExt.openAuthorView', () => { const activeEditor = vscode.window.activeTextEditor; if (!activeEditor || activeEditor.document.languageId !== '你的自定义语言ID') return; // 构造虚拟文档URI const virtualUri = vscode.Uri.parse(`author-view:${activeEditor.document.uri.path}?${activeEditor.document.uri.toString()}`); // 在侧边打开虚拟文档 vscode.window.showTextDocument(virtualUri, { viewColumn: vscode.ViewColumn.Beside, preserveFocus: true }); }); - 在
package.json中给虚拟文档关联自定义语法:"contributes": { "languages": [ { "id": "你的自定义语言ID", "filenames": ["*.yourExt"], "aliases": ["你的语言名"], "configuration": "./language-configuration.json" } ], "grammars": [ { "language": "你的自定义语言ID", "scopeName": "source.yourLang", "path": "./syntaxes/yourLang.tmLanguage.json" } ], "commands": [ { "command": "yourExt.openAuthorView", "title": "打开作者视图" }, { "command": "yourExt.toggleStringIds", "title": "切换标识符显示" } ] }
方案三:语言服务器集成(长期最优)
结合你正在开发的语言服务器,利用语义分析精准控制标识符显示:
- 在语言服务器中,对每个标识符做语义标记,通过
semanticTokens或自定义通知把位置信息发给客户端 - 客户端收到语义信息后,应用装饰逻辑实现显示/隐藏切换
- 优势:能处理复杂脚本结构(比如嵌套、多行标识符),支持跨文档状态同步,后续扩展更灵活
推荐方案
- 新手优先选方案一,代码量少,半天就能跑通,不影响现有开发流程
- 需要双视图并行编辑/查看,选方案二
- 长期维护扩展,选方案三,和语言服务器能力结合更紧密
内容的提问来源于stack exchange,提问作者Lex C.
相关产品推荐
相关产品推荐

