如何使用 TextEditor.setDecorations() 动态更改 VSCode 中的装订线图标
How to dynamically change gutter icons in VSCode with TextEditor.setDecorations()
使用 TextEditor.setDecorations() https://code.visualstudio.com/api/references/vscode-api#TextEditor.
更新 VSCode 中的装订线图标时出现一些奇怪的行为
激活我的 VSCode 扩展后,下面的 class 被实例化,它的构造函数被称为“一次性”创建 3 个 TextEditorDecorationType 的 https://code.visualstudio.com/api/references/vscode-api#TextEditorDecorationType,每个状态对应一个测试结果可以用 NONE、PASS、FAIL 图标表示,最后调用 triggerUpdateDecorations()
以在 3 个数组中收集当前 globalResults
并使用 vscode.window.activeTextEditor.setDecorations()
[ 设置装订线图标=20=]
目前一切正常。没有测试 运行 并且编辑器中显示的每个测试都更新为 NONE 装订线图标。
现在每个测试都在编辑器中 运行,完成后,将再次调用 triggerUpdateDecorations()
来收集结果并更新装订线图标。
如果编辑器中有例如 10 个测试,每个测试都有一个 NONE 装订线图标,如果我 运行 测试正确的单个测试更新为通过或失败装订线图标。此行为会在所有后续测试 运行 中重复出现,但最后一个除外。最后一个测试 运行 仍然设置为 NONE 装订线图标。它与特定测试无关,因为我可以跳来跳去,并且行为遵循上次测试 运行.
我尝试将虚拟 NONE 图标添加到装订线中未与测试绑定的随机位置,这允许所有与测试绑定的装订线图标更新为通过或失败装订线图标。
我一直在尝试解决这个问题,但似乎找不到根本原因。非常感谢有关如何解决此问题的任何见解。
请注意,部分代码源自此处显示的 VSCode 示例
https://github.com/microsoft/vscode-extension-samples/blob/main/decorator-sample/src/extension.ts#L58
import * as path from 'path';
import * as vscode from 'vscode';
class ProviderDecorations
{
private timeout: NodeJS.Timeout;
private context: vscode.ExtensionContext;
private activeEditor: vscode.TextEditor;
private readonly decorationNone: vscode.TextEditorDecorationType;
private readonly decorationPass: vscode.TextEditorDecorationType;
private readonly decorationFail: vscode.TextEditorDecorationType;
constructor(context: vscode.ExtensionContext)
{
this.context = context;
this.activeEditor = vscode.window.activeTextEditor;
this.decorationNone = this.getDecorationType(ENTRY_STATE.NONE);
this.decorationPass = this.getDecorationType(ENTRY_STATE.PASS);
this.decorationFail = this.getDecorationType(ENTRY_STATE.FAIL);
vscode.window.onDidChangeActiveTextEditor(editor =>
{
this.activeEditor = editor;
if (editor)
{
this.triggerUpdateDecorations();
}
}, null, this.context.subscriptions);
vscode.workspace.onDidChangeTextDocument(event =>
{
if (this.activeEditor && event.document === this.activeEditor.document)
{
this.triggerUpdateDecorations();
}
}, null, this.context.subscriptions);
this.triggerUpdateDecorations();
}
public updateDecorations()
{
if (!this.activeEditor)
{
return;
}
let rangeNone = [];
let rangePass = [];
let rangeFail = [];
globalResults.forEach((result) =>
{
let range = new vscode.Range(result.line, 0, result.line, 0);
switch (result.state)
{
case ENTRY_STATE.NONE:
rangeNone.push({ range });
break;
case ENTRY_STATE.PASS:
rangePass.push({ range });
break;
case ENTRY_STATE.FAIL:
rangeFail.push({ range });
break;
}
});
if (rangePass.length > 0)
{
this.activeEditor.setDecorations(this.decorationPass, rangePass);
}
if (rangeFail.length > 0)
{
this.activeEditor.setDecorations(this.decorationFail, rangeFail);
}
if (rangeNone.length > 0)
{
this.activeEditor.setDecorations(this.decorationNone, rangeNone);
}
}
private getDecorationType(state: ENTRY_STATE): vscode.TextEditorDecorationType
{
let icon = 'none.svg';
if (state === ENTRY_STATE.PASS)
{
icon = 'pass.svg';
}
else if (state === ENTRY_STATE.FAIL)
{
icon = 'fail.svg';
}
const decorationType = vscode.window.createTextEditorDecorationType(
{
light:
{
gutterIconPath: path.join(__dirname, '..', 'resources', 'light', icon),
gutterIconSize: '85%',
},
dark:
{
gutterIconPath: path.join(__dirname, '..', 'resources', 'dark', icon),
gutterIconSize: '85%'
}
});
return decorationType;
}
public triggerUpdateDecorations()
{
if (this.timeout)
{
clearTimeout(this.timeout);
this.timeout = undefined;
}
this.timeout = setTimeout(() =>
{
this.updateDecorations();
}, 250);
}
}
export default ProviderDecorations;
您永远不会清除 Decorator 类型,删除 if (rangePass.length > 0)
部分
this.activeEditor.setDecorations(this.decorationPass, rangePass);
this.activeEditor.setDecorations(this.decorationFail, rangeFail);
this.activeEditor.setDecorations(this.decorationNone, rangeNone);
使用 TextEditor.setDecorations() https://code.visualstudio.com/api/references/vscode-api#TextEditor.
更新 VSCode 中的装订线图标时出现一些奇怪的行为激活我的 VSCode 扩展后,下面的 class 被实例化,它的构造函数被称为“一次性”创建 3 个 TextEditorDecorationType 的 https://code.visualstudio.com/api/references/vscode-api#TextEditorDecorationType,每个状态对应一个测试结果可以用 NONE、PASS、FAIL 图标表示,最后调用 triggerUpdateDecorations()
以在 3 个数组中收集当前 globalResults
并使用 vscode.window.activeTextEditor.setDecorations()
[ 设置装订线图标=20=]
目前一切正常。没有测试 运行 并且编辑器中显示的每个测试都更新为 NONE 装订线图标。
现在每个测试都在编辑器中 运行,完成后,将再次调用 triggerUpdateDecorations()
来收集结果并更新装订线图标。
如果编辑器中有例如 10 个测试,每个测试都有一个 NONE 装订线图标,如果我 运行 测试正确的单个测试更新为通过或失败装订线图标。此行为会在所有后续测试 运行 中重复出现,但最后一个除外。最后一个测试 运行 仍然设置为 NONE 装订线图标。它与特定测试无关,因为我可以跳来跳去,并且行为遵循上次测试 运行.
我尝试将虚拟 NONE 图标添加到装订线中未与测试绑定的随机位置,这允许所有与测试绑定的装订线图标更新为通过或失败装订线图标。
我一直在尝试解决这个问题,但似乎找不到根本原因。非常感谢有关如何解决此问题的任何见解。
请注意,部分代码源自此处显示的 VSCode 示例 https://github.com/microsoft/vscode-extension-samples/blob/main/decorator-sample/src/extension.ts#L58
import * as path from 'path';
import * as vscode from 'vscode';
class ProviderDecorations
{
private timeout: NodeJS.Timeout;
private context: vscode.ExtensionContext;
private activeEditor: vscode.TextEditor;
private readonly decorationNone: vscode.TextEditorDecorationType;
private readonly decorationPass: vscode.TextEditorDecorationType;
private readonly decorationFail: vscode.TextEditorDecorationType;
constructor(context: vscode.ExtensionContext)
{
this.context = context;
this.activeEditor = vscode.window.activeTextEditor;
this.decorationNone = this.getDecorationType(ENTRY_STATE.NONE);
this.decorationPass = this.getDecorationType(ENTRY_STATE.PASS);
this.decorationFail = this.getDecorationType(ENTRY_STATE.FAIL);
vscode.window.onDidChangeActiveTextEditor(editor =>
{
this.activeEditor = editor;
if (editor)
{
this.triggerUpdateDecorations();
}
}, null, this.context.subscriptions);
vscode.workspace.onDidChangeTextDocument(event =>
{
if (this.activeEditor && event.document === this.activeEditor.document)
{
this.triggerUpdateDecorations();
}
}, null, this.context.subscriptions);
this.triggerUpdateDecorations();
}
public updateDecorations()
{
if (!this.activeEditor)
{
return;
}
let rangeNone = [];
let rangePass = [];
let rangeFail = [];
globalResults.forEach((result) =>
{
let range = new vscode.Range(result.line, 0, result.line, 0);
switch (result.state)
{
case ENTRY_STATE.NONE:
rangeNone.push({ range });
break;
case ENTRY_STATE.PASS:
rangePass.push({ range });
break;
case ENTRY_STATE.FAIL:
rangeFail.push({ range });
break;
}
});
if (rangePass.length > 0)
{
this.activeEditor.setDecorations(this.decorationPass, rangePass);
}
if (rangeFail.length > 0)
{
this.activeEditor.setDecorations(this.decorationFail, rangeFail);
}
if (rangeNone.length > 0)
{
this.activeEditor.setDecorations(this.decorationNone, rangeNone);
}
}
private getDecorationType(state: ENTRY_STATE): vscode.TextEditorDecorationType
{
let icon = 'none.svg';
if (state === ENTRY_STATE.PASS)
{
icon = 'pass.svg';
}
else if (state === ENTRY_STATE.FAIL)
{
icon = 'fail.svg';
}
const decorationType = vscode.window.createTextEditorDecorationType(
{
light:
{
gutterIconPath: path.join(__dirname, '..', 'resources', 'light', icon),
gutterIconSize: '85%',
},
dark:
{
gutterIconPath: path.join(__dirname, '..', 'resources', 'dark', icon),
gutterIconSize: '85%'
}
});
return decorationType;
}
public triggerUpdateDecorations()
{
if (this.timeout)
{
clearTimeout(this.timeout);
this.timeout = undefined;
}
this.timeout = setTimeout(() =>
{
this.updateDecorations();
}, 250);
}
}
export default ProviderDecorations;
您永远不会清除 Decorator 类型,删除 if (rangePass.length > 0)
部分
this.activeEditor.setDecorations(this.decorationPass, rangePass);
this.activeEditor.setDecorations(this.decorationFail, rangeFail);
this.activeEditor.setDecorations(this.decorationNone, rangeNone);