You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何开发VSCode扩展实现代码局部变暗效果?

实现VSCode扩展的代码选择性变暗功能

核心方案:使用VSCode文本装饰器(Text Editor Decorations)API

这是官方支持的最直接实现方式,无需修改原有语法token,只需在非焦点区域叠加自定义样式即可达到变暗效果。


具体实现步骤

1. 创建自定义装饰类型

针对Dark Mode定义变暗后的文本样式,包括深色前景色和取消加粗:

// 根据原有高亮色调定义对应深色值,比如亮蓝色关键字对应暗蓝色,亮黄色名称对应暗黄色
const dimKeywordDecoration = vscode.window.createTextEditorDecorationType({
    color: '#3a4b70', // 暗蓝色,匹配原有亮蓝色关键字
    fontWeight: 'normal'
});

const dimNameDecoration = vscode.window.createTextEditorDecorationType({
    color: '#8a7a4c', // 暗黄色,匹配原有亮黄色名称
    fontWeight: 'normal'
});

// 如果不需要区分token类型,可使用全局变暗装饰(复用主题内置样式,适配性更好)
const globalDimDecoration = vscode.window.createTextEditorDecorationType({
    color: { id: 'editor.dimmedForeground' },
    fontWeight: 'normal'
});

2. 计算非焦点区域范围

获取编辑器选中的焦点区域,推导文档中其余需要变暗的范围:

function getNonFocusRanges(editor: vscode.TextEditor, focusRange: vscode.Range): vscode.Range[] {
    const nonRanges: vscode.Range[] = [];
    const doc = editor.document;
    const docStart = new vscode.Position(0, 0);
    
    // 添加焦点区域之前的内容范围
    if (docStart.isBefore(focusRange.start)) {
        nonRanges.push(new vscode.Range(docStart, focusRange.start));
    }
    
    // 添加焦点区域之后的内容范围
    const docEnd = doc.lineAt(doc.lineCount - 1).range.end;
    if (focusRange.end.isBefore(docEnd)) {
        nonRanges.push(new vscode.Range(focusRange.end, docEnd));
    }
    
    return nonRanges;
}

3. 应用装饰到非焦点区域

将创建的装饰类型应用到计算出的非焦点范围:

function updateDimDecorations() {
    const editor = vscode.window.activeTextEditor;
    if (!editor) return;
    
    const selection = editor.selection;
    const focusRange = new vscode.Range(selection.start, selection.end);
    const nonFocusRanges = getNonFocusRanges(editor, focusRange);
    
    // 若使用全局变暗,直接应用
    editor.setDecorations(globalDimDecoration, nonFocusRanges);
    
    // 若需区分token类型,需结合语义token筛选后应用对应装饰(见下文进阶实现)
}

4. 监听编辑器状态变化,动态更新装饰

监听选中区域变化、主题切换等事件,确保装饰实时生效:

// 监听选中区域变化
vscode.window.onDidChangeTextEditorSelection(event => {
    if (event.textEditor === vscode.window.activeTextEditor) {
        updateDimDecorations();
    }
});

// 监听主题切换,重新创建适配当前主题的装饰
vscode.window.onDidChangeActiveColorTheme(() => {
    // 销毁旧装饰
    globalDimDecoration.dispose();
    dimKeywordDecoration.dispose();
    dimNameDecoration.dispose();
    
    // 重新创建装饰(重复步骤1的装饰创建逻辑)
    updateDimDecorations();
});

进阶实现:区分token类型的精准变暗

如果需要针对关键字、名称等不同token类型设置不同的变暗效果,可结合vscode.languages.getDocumentSemanticTokens API获取当前文档的语义token,筛选非焦点区域的token并应用对应装饰:

async function applyTokenSpecificDim(editor: vscode.TextEditor, focusRange: vscode.Range) {
    const doc = editor.document;
    const semanticTokens = await vscode.languages.getDocumentSemanticTokens(doc);
    if (!semanticTokens) return;
    
    const keywordRanges: vscode.Range[] = [];
    const nameRanges: vscode.Range[] = [];
    
    // 解码语义token(处理token的偏移量、长度、类型索引)
    const data = semanticTokens.data;
    let prevLine = 0;
    let prevChar = 0;
    
    for (let i = 0; i < data.length; i += 5) {
        const deltaLine = data[i];
        const deltaChar = data[i + 1];
        const length = data[i + 2];
        const tokenTypeIndex = data[i + 3];
        
        const line = prevLine + deltaLine;
        const startChar = deltaLine === 0 ? prevChar + deltaChar : deltaChar;
        const endChar = startChar + length;
        
        const tokenRange = new vscode.Range(line, startChar, line, endChar);
        
        // 判断token是否在非焦点区域
        if (!focusRange.contains(tokenRange)) {
            // 根据token类型索引匹配对应类型(需结合当前语言的token类型定义)
            const tokenType = semanticTokens.tokenTypes[tokenTypeIndex];
            if (tokenType === 'keyword') {
                keywordRanges.push(tokenRange);
            } else if (tokenType === 'variable' || tokenType === 'function') {
                nameRanges.push(tokenRange);
            }
        }
        
        prevLine = line;
        prevChar = endChar;
    }
    
    // 应用对应装饰
    editor.setDecorations(dimKeywordDecoration, keywordRanges);
    editor.setDecorations(dimNameDecoration, nameRanges);
}

对原有思路的说明

  1. 修改原有token不可行:VSCode不允许直接访问或修改已有语义token provider的输出,DocumentSemanticTokensProvider仅用于自定义token的提供,无法覆盖或修改内置provider的结果。
  2. 无需绘制半透明矩形:文本装饰器API直接支持设置文本的颜色、字体粗细等样式,完全可以实现代码变暗的需求,无需额外绘制图形。

内容的提问来源于stack exchange,提问作者James Koppel

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.06 17:23:21