Reputation: 410
I am getting some odd behavior when updating gutter icons in VSCode using TextEditor.setDecorations() https://code.visualstudio.com/api/references/vscode-api#TextEditor.
On activate of my VSCode extension the class below is instantiated and it's constructor called which "one-time" creates 3 TextEditorDecorationType's https://code.visualstudio.com/api/references/vscode-api#TextEditorDecorationType, one for each state that a test result can be in represented by a NONE, PASS, FAIL icon and finally triggerUpdateDecorations()
is called to collect the current globalResults
within 3 arrays and set the gutter icons with vscode.window.activeTextEditor.setDecorations()
So far everything works as expected. No tests have been run and every test shown in the editor is updated with a NONE gutter icon.
Now as each test is run in the editor, on completion, triggerUpdateDecorations()
is called once again to collect results and update the gutter icons.
If there are for example 10 tests in the editor, each with a NONE gutter icon, if I run a single test that test correctly updates with either a PASS or FAIL gutter icon. This behavior repeats itself for all subsequent tests run, except for the last one. The last test run remains set with its NONE gutter icon. It's not tied to a specific test as I can jump around and the behavior follows the last test run.
I've tried adding a dummy NONE icon to a random place in the gutter not tied to a test and that allows all gutter icons tied to a test to be updated with PASS or FAIL gutter icons.
I've been experimenting a lot to try and solve this and can't seem to find the root cause. Greatly appreciate any insights on how to solve this.
Note some of this code stems from the VSCode Samples shown here 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;
Upvotes: 1
Views: 1792
Reputation: 28753
You never clear a Decorator type, remove the if (rangePass.length > 0)
parts
this.activeEditor.setDecorations(this.decorationPass, rangePass);
this.activeEditor.setDecorations(this.decorationFail, rangeFail);
this.activeEditor.setDecorations(this.decorationNone, rangeNone);
Upvotes: 1