testing: test feedback for decorations

Fixes https://github.com/microsoft/vscode/issues/117019
This commit is contained in:
Connor Peet 2021-02-19 11:12:26 -08:00
parent d29d7e023b
commit e66f74e0c2
No known key found for this signature in database
GPG key ID: CF8FD2EA0DBC61BD
2 changed files with 61 additions and 7 deletions

View file

@ -36,6 +36,16 @@ export class TestingDecorations extends Disposable implements IEditorContributio
private currentUri?: URI;
private lastDecorations: ITestDecoration[] = [];
/**
* List of messages that should be hidden because an editor changed their
* underlying ranges. I think this is good enough, because:
* - Message decorations are never shown across reloads; this does not
* need to persist
* - Message instances are stable for any completed test results for
* the duration of the session.
*/
private invalidatedMessages = new WeakSet<ITestMessage>();
constructor(
private readonly editor: ICodeEditor,
@ITestService private readonly testService: ITestService,
@ -53,6 +63,28 @@ export class TestingDecorations extends Disposable implements IEditorContributio
}
}
}));
this._register(this.editor.onDidChangeModelContent(e => {
if (!this.currentUri) {
return;
}
let update = false;
for (const change of e.changes) {
for (const deco of this.lastDecorations) {
if (deco instanceof TestMessageDecoration
&& deco.location.range.startLineNumber >= change.range.startLineNumber
&& deco.location.range.endLineNumber <= change.range.endLineNumber
) {
this.invalidatedMessages.add(deco.testMessage);
update = true;
}
}
}
if (update) {
this.setDecorations(this.currentUri);
}
}));
this._register(this.results.onTestChanged(({ item: result }) => {
if (this.currentUri && result.item.location?.uri.toString() === this.currentUri.toString()) {
@ -99,9 +131,13 @@ export class TestingDecorations extends Disposable implements IEditorContributio
}
const [result, stateItem] = stateLookup;
if (stateItem.retired) {
continue; // do not show decorations for outdated tests
}
for (let i = 0; i < stateItem.state.messages.length; i++) {
const m = stateItem.state.messages[i];
if (hasValidLocation(uri, m)) {
if (!this.invalidatedMessages.has(m) && hasValidLocation(uri, m)) {
const uri = buildTestUri({
type: TestUriType.ResultActualOutput,
messageIndex: i,
@ -284,15 +320,14 @@ class TestMessageDecoration implements ITestDecoration {
private readonly decorationId = `testmessage-${generateUuid()}`;
constructor(
{ message, severity }: ITestMessage,
public readonly testMessage: ITestMessage,
private readonly messageUri: URI,
location: IRichLocation,
public readonly location: IRichLocation,
private readonly editor: ICodeEditor,
@ICodeEditorService private readonly editorService: ICodeEditorService,
@IThemeService themeService: IThemeService,
) {
severity = severity || TestMessageSeverity.Error;
const { severity = TestMessageSeverity.Error, message } = testMessage;
const colorTheme = themeService.getColorTheme();
editorService.registerDecorationType(this.decorationId, {
after: {
@ -310,6 +345,7 @@ class TestMessageDecoration implements ITestDecoration {
options.zIndex = 10; // todo: in spite of the z-index, this appears behind gitlens
options.className = `testing-inline-message-margin testing-inline-message-severity-${severity}`;
options.isWholeLine = true;
options.stickiness = TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges;
const rulerColor = severity === TestMessageSeverity.Error
? overviewRulerError
@ -332,7 +368,7 @@ class TestMessageDecoration implements ITestDecoration {
}
if (e.target.element?.className.includes(this.decorationId)) {
TestingOutputPeekController.get(this.editor).show(this.messageUri);
TestingOutputPeekController.get(this.editor).toggle(this.messageUri);
}
return false;

View file

@ -150,6 +150,11 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
*/
private readonly peek = this._register(new MutableDisposable<TestingOutputPeek>());
/**
* URI of the currently-visible peek, if any.
*/
private currentPeekUri: URI | undefined;
/**
* Context key updated when the peek is visible/hidden.
*/
@ -174,11 +179,22 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
this._register(testResults.onTestChanged((evt) => this.closePeekOnTestChange(evt)));
}
/**
* Toggles peek visibility for the URI.
*/
public toggle(uri: URI) {
if (this.currentPeekUri?.toString() === uri.toString()) {
this.peek.clear();
} else {
this.show(uri);
}
}
/**
* Shows a peek for the message in th editor.
*/
public async show(uri: URI) {
const dto = await this.retrieveTest(uri);
const dto = this.retrieveTest(uri);
if (!dto) {
return;
}
@ -195,6 +211,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
this.peek.value = this.instantiationService.createInstance(ctor, this.editor);
this.peek.value.onDidClose(() => {
this.visible.set(false);
this.currentPeekUri = undefined;
this.peek.value = undefined;
});
}
@ -206,6 +223,7 @@ export class TestingOutputPeekController extends Disposable implements IEditorCo
alert(message.message.toString());
this.peek.value!.setModel(dto);
this.currentPeekUri = uri;
}
/**