@codingame/monaco-vscode-testing-service-override 2.0.3 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +8 -8
- package/vscode/src/vs/workbench/contrib/testing/browser/codeCoverageDecorations.js +567 -0
- package/vscode/src/vs/workbench/contrib/testing/browser/icons.js +22 -2
- package/vscode/src/vs/workbench/contrib/testing/browser/media/testing.css.js +1 -1
- package/vscode/src/vs/workbench/contrib/testing/browser/testCoverageBars.js +43 -22
- package/vscode/src/vs/workbench/contrib/testing/browser/testCoverageView.js +380 -50
- package/vscode/src/vs/workbench/contrib/testing/browser/testExplorerActions.js +333 -117
- package/vscode/src/vs/workbench/contrib/testing/browser/testing.contribution.js +10 -7
- package/vscode/src/vs/workbench/contrib/testing/browser/testingDecorations.js +148 -57
- package/vscode/src/vs/workbench/contrib/testing/browser/testingExplorerView.js +4 -4
- package/vscode/src/vs/workbench/contrib/testing/browser/testingOutputPeek.js +82 -48
- package/vscode/src/vs/workbench/contrib/testing/browser/testingProgressUiService.js +1 -1
- package/vscode/src/vs/workbench/contrib/testing/browser/theme.js +131 -2
- package/vscode/src/vs/workbench/contrib/testing/common/configuration.js +22 -1
- package/vscode/src/vs/workbench/contrib/testing/common/mainThreadTestCollection.js +3 -3
- package/vscode/src/vs/workbench/contrib/testing/common/testServiceImpl.js +12 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@codingame/monaco-vscode-testing-service-override",
|
|
3
|
-
"version": "2.0
|
|
3
|
+
"version": "2.1.0",
|
|
4
4
|
"keywords": [],
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "CodinGame",
|
|
@@ -18,13 +18,13 @@
|
|
|
18
18
|
"module": "index.js",
|
|
19
19
|
"types": "index.d.ts",
|
|
20
20
|
"dependencies": {
|
|
21
|
-
"vscode": "npm:@codingame/monaco-vscode-api@2.0
|
|
22
|
-
"@xterm/addon-canvas": "0.6.0-beta.
|
|
23
|
-
"@xterm/addon-image": "0.7.0-beta.
|
|
24
|
-
"@xterm/addon-search": "0.14.0-beta.
|
|
25
|
-
"@xterm/addon-serialize": "0.12.0-beta.
|
|
26
|
-
"@xterm/addon-unicode11": "0.7.0-beta.
|
|
27
|
-
"@xterm/addon-webgl": "0.17.0-beta.
|
|
21
|
+
"vscode": "npm:@codingame/monaco-vscode-api@2.1.0",
|
|
22
|
+
"@xterm/addon-canvas": "0.6.0-beta.20",
|
|
23
|
+
"@xterm/addon-image": "0.7.0-beta.18",
|
|
24
|
+
"@xterm/addon-search": "0.14.0-beta.20",
|
|
25
|
+
"@xterm/addon-serialize": "0.12.0-beta.20",
|
|
26
|
+
"@xterm/addon-unicode11": "0.7.0-beta.20",
|
|
27
|
+
"@xterm/addon-webgl": "0.17.0-beta.20",
|
|
28
28
|
"vscode-marked": "npm:marked@=3.0.2"
|
|
29
29
|
}
|
|
30
30
|
}
|
|
@@ -0,0 +1,567 @@
|
|
|
1
|
+
import { __decorate, __param } from '../../../../../../../external/tslib/tslib.es6.js';
|
|
2
|
+
import { $, append, clearNode } from 'vscode/vscode/vs/base/browser/dom';
|
|
3
|
+
import { HoverWidget } from 'vscode/vscode/vs/base/browser/ui/hover/hoverWidget';
|
|
4
|
+
import { mapFindFirst } from 'vscode/vscode/vs/base/common/arraysFind';
|
|
5
|
+
import { CancellationTokenSource } from 'vscode/vscode/vs/base/common/cancellation';
|
|
6
|
+
import { MarkdownString } from 'vscode/vscode/vs/base/common/htmlContent';
|
|
7
|
+
import { KeyChord } from 'vscode/vscode/vs/base/common/keyCodes';
|
|
8
|
+
import { Lazy } from 'vscode/vscode/vs/base/common/lazy';
|
|
9
|
+
import { Disposable, DisposableStore, toDisposable } from 'vscode/vscode/vs/base/common/lifecycle';
|
|
10
|
+
import { ThemeIcon } from 'vscode/vscode/vs/base/common/themables';
|
|
11
|
+
import { MarkdownRenderer } from 'vscode/vscode/vs/editor/browser/widget/markdownRenderer/browser/markdownRenderer';
|
|
12
|
+
import { Position } from 'vscode/vscode/vs/editor/common/core/position';
|
|
13
|
+
import { Range } from 'vscode/vscode/vs/editor/common/core/range';
|
|
14
|
+
import { InjectedTextCursorStops } from 'vscode/vscode/vs/editor/common/model';
|
|
15
|
+
import { HoverOperation } from 'vscode/vscode/vs/editor/contrib/hover/browser/hoverOperation';
|
|
16
|
+
import { localizeWithPath } from 'vscode/vscode/vs/nls';
|
|
17
|
+
import { Categories } from 'vscode/vscode/vs/platform/action/common/actionCommonCategories';
|
|
18
|
+
import { registerAction2, Action2 } from 'vscode/vscode/vs/platform/actions/common/actions';
|
|
19
|
+
import { IInstantiationService } from 'vscode/vscode/vs/platform/instantiation/common/instantiation';
|
|
20
|
+
import { IKeybindingService } from 'vscode/vscode/vs/platform/keybinding/common/keybinding';
|
|
21
|
+
import { ILogService } from 'vscode/vscode/vs/platform/log/common/log';
|
|
22
|
+
import { testingCoverageMissingBranch } from './icons.js';
|
|
23
|
+
import { ITestCoverageService } from 'vscode/vscode/vs/workbench/contrib/testing/common/testCoverageService';
|
|
24
|
+
import { TestingContextKeys } from 'vscode/vscode/vs/workbench/contrib/testing/common/testingContextKeys';
|
|
25
|
+
import { observableValue } from 'vscode/vscode/vs/base/common/observableInternal/base';
|
|
26
|
+
import { observableFromEvent } from 'vscode/vscode/vs/base/common/observableInternal/utils';
|
|
27
|
+
import { derived } from 'vscode/vscode/vs/base/common/observableInternal/derived';
|
|
28
|
+
import { autorun } from 'vscode/vscode/vs/base/common/observableInternal/autorun';
|
|
29
|
+
|
|
30
|
+
var CodeCoverageDecorations_1, LineHoverWidget_1;
|
|
31
|
+
const MAX_HOVERED_LINES = 30;
|
|
32
|
+
const CLASS_HIT = 'coverage-deco-hit';
|
|
33
|
+
const CLASS_MISS = 'coverage-deco-miss';
|
|
34
|
+
const TOGGLE_INLINE_COMMAND_TEXT = ( localizeWithPath(
|
|
35
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
36
|
+
'testing.toggleInlineCoverage',
|
|
37
|
+
'Toggle Inline Coverage'
|
|
38
|
+
));
|
|
39
|
+
const TOGGLE_INLINE_COMMAND_ID = 'testing.toggleInlineCoverage';
|
|
40
|
+
const BRANCH_MISS_INDICATOR_CHARS = 4;
|
|
41
|
+
let CodeCoverageDecorations = class CodeCoverageDecorations extends Disposable {
|
|
42
|
+
static { CodeCoverageDecorations_1 = this; }
|
|
43
|
+
static { this.showInline = observableValue('inlineCoverage', false); }
|
|
44
|
+
static { this.fileCoverageDecorations = ( new WeakMap()); }
|
|
45
|
+
constructor(editor, instantiationService, coverage, log) {
|
|
46
|
+
super();
|
|
47
|
+
this.editor = editor;
|
|
48
|
+
this.log = log;
|
|
49
|
+
this.displayedStore = this._register(( new DisposableStore()));
|
|
50
|
+
this.hoveredStore = this._register(( new DisposableStore()));
|
|
51
|
+
this.decorationIds = ( new Map());
|
|
52
|
+
this.lineHoverWidget = ( new Lazy(
|
|
53
|
+
() => this._register(instantiationService.createInstance(LineHoverWidget, this.editor))
|
|
54
|
+
));
|
|
55
|
+
const modelObs = observableFromEvent(editor.onDidChangeModel, () => editor.getModel());
|
|
56
|
+
const configObs = observableFromEvent(editor.onDidChangeConfiguration, i => i);
|
|
57
|
+
const fileCoverage = derived(reader => {
|
|
58
|
+
const report = coverage.selected.read(reader);
|
|
59
|
+
if (!report) {
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const model = modelObs.read(reader);
|
|
63
|
+
if (!model) {
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
return report.getUri(model.uri);
|
|
67
|
+
});
|
|
68
|
+
this._register(autorun(reader => {
|
|
69
|
+
const c = fileCoverage.read(reader);
|
|
70
|
+
if (c) {
|
|
71
|
+
this.apply(editor.getModel(), c, CodeCoverageDecorations_1.showInline.read(reader));
|
|
72
|
+
}
|
|
73
|
+
else {
|
|
74
|
+
this.clear();
|
|
75
|
+
}
|
|
76
|
+
}));
|
|
77
|
+
this._register(autorun(reader => {
|
|
78
|
+
const c = fileCoverage.read(reader);
|
|
79
|
+
if (c) {
|
|
80
|
+
const evt = configObs.read(reader);
|
|
81
|
+
if (evt?.hasChanged(66 ) !== false) {
|
|
82
|
+
this.updateEditorStyles();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}));
|
|
86
|
+
this._register(editor.onMouseMove(e => {
|
|
87
|
+
const model = editor.getModel();
|
|
88
|
+
if (e.target.type === 3 && model) {
|
|
89
|
+
this.hoverLineNumber(editor.getModel(), e.target.position.lineNumber);
|
|
90
|
+
}
|
|
91
|
+
else if (this.lineHoverWidget.hasValue && this.lineHoverWidget.value.getDomNode().contains(e.target.element)) ;
|
|
92
|
+
else if (CodeCoverageDecorations_1.showInline.get() && e.target.type === 6 && model) {
|
|
93
|
+
this.hoverInlineDecoration(model, e.target.position);
|
|
94
|
+
}
|
|
95
|
+
else {
|
|
96
|
+
this.hoveredStore.clear();
|
|
97
|
+
}
|
|
98
|
+
}));
|
|
99
|
+
this._register(editor.onWillChangeModel(() => {
|
|
100
|
+
const model = editor.getModel();
|
|
101
|
+
if (!this.details || !model) {
|
|
102
|
+
return;
|
|
103
|
+
}
|
|
104
|
+
for (const decoration of model.getAllDecorations()) {
|
|
105
|
+
const own = this.decorationIds.get(decoration.id);
|
|
106
|
+
if (own) {
|
|
107
|
+
own.detail.range = decoration.range;
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
}));
|
|
111
|
+
}
|
|
112
|
+
updateEditorStyles() {
|
|
113
|
+
const lineHeight = this.editor.getOption(66 );
|
|
114
|
+
const { style } = this.editor.getContainerDomNode();
|
|
115
|
+
style.setProperty('--vscode-testing-coverage-lineHeight', `${lineHeight}px`);
|
|
116
|
+
}
|
|
117
|
+
hoverInlineDecoration(model, position) {
|
|
118
|
+
const allDecorations = model.getDecorationsInRange(Range.fromPositions(position));
|
|
119
|
+
const decoration = mapFindFirst(allDecorations, ({ id }) => ( this.decorationIds.has(id)) ? { id, deco: this.decorationIds.get(id) } : undefined);
|
|
120
|
+
if (decoration === this.hoveredSubject) {
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
123
|
+
this.hoveredStore.clear();
|
|
124
|
+
this.hoveredSubject = decoration;
|
|
125
|
+
if (!decoration) {
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
model.changeDecorations(e => {
|
|
129
|
+
e.changeDecorationOptions(decoration.id, {
|
|
130
|
+
...decoration.deco.options,
|
|
131
|
+
className: `${decoration.deco.options.className} coverage-deco-hovered`,
|
|
132
|
+
});
|
|
133
|
+
});
|
|
134
|
+
this.hoveredStore.add(toDisposable(() => {
|
|
135
|
+
this.hoveredSubject = undefined;
|
|
136
|
+
model.changeDecorations(e => {
|
|
137
|
+
e.changeDecorationOptions(decoration.id, decoration.deco.options);
|
|
138
|
+
});
|
|
139
|
+
}));
|
|
140
|
+
}
|
|
141
|
+
hoverLineNumber(model, lineNumber) {
|
|
142
|
+
if (lineNumber === this.hoveredSubject || !this.details) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.hoveredStore.clear();
|
|
146
|
+
this.hoveredSubject = lineNumber;
|
|
147
|
+
const todo = [{ line: lineNumber, dir: 0 }];
|
|
148
|
+
const toEnable = ( new Set());
|
|
149
|
+
const inlineEnabled = CodeCoverageDecorations_1.showInline.get();
|
|
150
|
+
if (!CodeCoverageDecorations_1.showInline.get()) {
|
|
151
|
+
for (let i = 0; i < todo.length && i < MAX_HOVERED_LINES; i++) {
|
|
152
|
+
const { line, dir } = todo[i];
|
|
153
|
+
let found = false;
|
|
154
|
+
for (const decoration of model.getLineDecorations(line)) {
|
|
155
|
+
if (( this.decorationIds.has(decoration.id))) {
|
|
156
|
+
toEnable.add(decoration.id);
|
|
157
|
+
found = true;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
if (found) {
|
|
161
|
+
if (dir <= 0) {
|
|
162
|
+
todo.push({ line: line - 1, dir: -1 });
|
|
163
|
+
}
|
|
164
|
+
if (dir >= 0) {
|
|
165
|
+
todo.push({ line: line + 1, dir: 1 });
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
model.changeDecorations(e => {
|
|
170
|
+
for (const id of toEnable) {
|
|
171
|
+
const { applyHoverOptions, options } = this.decorationIds.get(id);
|
|
172
|
+
const dup = { ...options };
|
|
173
|
+
applyHoverOptions(dup);
|
|
174
|
+
e.changeDecorationOptions(id, dup);
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
if (toEnable.size || inlineEnabled) {
|
|
179
|
+
this.lineHoverWidget.value.startShowingAt(lineNumber);
|
|
180
|
+
}
|
|
181
|
+
this.hoveredStore.add(this.editor.onMouseLeave(() => {
|
|
182
|
+
this.hoveredStore.clear();
|
|
183
|
+
}));
|
|
184
|
+
this.hoveredStore.add(toDisposable(() => {
|
|
185
|
+
this.lineHoverWidget.value.hide();
|
|
186
|
+
this.hoveredSubject = undefined;
|
|
187
|
+
model.changeDecorations(e => {
|
|
188
|
+
for (const id of toEnable) {
|
|
189
|
+
const deco = this.decorationIds.get(id);
|
|
190
|
+
if (deco) {
|
|
191
|
+
e.changeDecorationOptions(id, deco.options);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
});
|
|
195
|
+
}));
|
|
196
|
+
}
|
|
197
|
+
async apply(model, coverage, showInlineByDefault) {
|
|
198
|
+
const details = this.details = await this.loadDetails(coverage, model);
|
|
199
|
+
if (!details) {
|
|
200
|
+
return this.clear();
|
|
201
|
+
}
|
|
202
|
+
this.displayedStore.clear();
|
|
203
|
+
model.changeDecorations(e => {
|
|
204
|
+
for (const detailRange of details.ranges) {
|
|
205
|
+
const { metadata: { detail, description }, range, primary } = detailRange;
|
|
206
|
+
if (detail.type === 2 ) {
|
|
207
|
+
const hits = detail.detail.branches[detail.branch].count;
|
|
208
|
+
const cls = hits ? CLASS_HIT : CLASS_MISS;
|
|
209
|
+
const showMissIndicator = !hits && range.isEmpty() && ( detail.detail.branches.some(b => b.count));
|
|
210
|
+
const options = {
|
|
211
|
+
showIfCollapsed: showMissIndicator,
|
|
212
|
+
description: 'coverage-gutter',
|
|
213
|
+
lineNumberClassName: `coverage-deco-gutter ${cls}`,
|
|
214
|
+
};
|
|
215
|
+
const applyHoverOptions = (target) => {
|
|
216
|
+
target.hoverMessage = description;
|
|
217
|
+
if (showMissIndicator) {
|
|
218
|
+
target.after = {
|
|
219
|
+
content: '\xa0'.repeat(BRANCH_MISS_INDICATOR_CHARS),
|
|
220
|
+
inlineClassName: `coverage-deco-branch-miss-indicator ${ThemeIcon.asClassName(testingCoverageMissingBranch)}`,
|
|
221
|
+
inlineClassNameAffectsLetterSpacing: true,
|
|
222
|
+
cursorStops: InjectedTextCursorStops.None,
|
|
223
|
+
};
|
|
224
|
+
}
|
|
225
|
+
else {
|
|
226
|
+
target.className = `coverage-deco-inline ${cls}`;
|
|
227
|
+
if (primary) {
|
|
228
|
+
target.before = countBadge(hits);
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
};
|
|
232
|
+
if (showInlineByDefault) {
|
|
233
|
+
applyHoverOptions(options);
|
|
234
|
+
}
|
|
235
|
+
this.decorationIds.set(e.addDecoration(range, options), { options, applyHoverOptions, detail: detailRange });
|
|
236
|
+
}
|
|
237
|
+
else if (detail.type === 1 ) {
|
|
238
|
+
const cls = detail.count ? CLASS_HIT : CLASS_MISS;
|
|
239
|
+
const options = {
|
|
240
|
+
showIfCollapsed: false,
|
|
241
|
+
description: 'coverage-inline',
|
|
242
|
+
lineNumberClassName: `coverage-deco-gutter ${cls}`,
|
|
243
|
+
};
|
|
244
|
+
const applyHoverOptions = (target) => {
|
|
245
|
+
target.className = `coverage-deco-inline ${cls}`;
|
|
246
|
+
target.hoverMessage = description;
|
|
247
|
+
if (primary) {
|
|
248
|
+
target.before = countBadge(detail.count);
|
|
249
|
+
}
|
|
250
|
+
};
|
|
251
|
+
if (showInlineByDefault) {
|
|
252
|
+
applyHoverOptions(options);
|
|
253
|
+
}
|
|
254
|
+
this.decorationIds.set(e.addDecoration(range, options), { options, applyHoverOptions, detail: detailRange });
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
this.displayedStore.add(toDisposable(() => {
|
|
259
|
+
model.changeDecorations(e => {
|
|
260
|
+
for (const decoration of ( this.decorationIds.keys())) {
|
|
261
|
+
e.removeDecoration(decoration);
|
|
262
|
+
}
|
|
263
|
+
this.decorationIds.clear();
|
|
264
|
+
});
|
|
265
|
+
}));
|
|
266
|
+
}
|
|
267
|
+
clear() {
|
|
268
|
+
this.loadingCancellation?.cancel();
|
|
269
|
+
this.loadingCancellation = undefined;
|
|
270
|
+
this.displayedStore.clear();
|
|
271
|
+
this.hoveredStore.clear();
|
|
272
|
+
}
|
|
273
|
+
async loadDetails(coverage, textModel) {
|
|
274
|
+
const existing = CodeCoverageDecorations_1.fileCoverageDecorations.get(coverage);
|
|
275
|
+
if (existing) {
|
|
276
|
+
return existing;
|
|
277
|
+
}
|
|
278
|
+
const cts = this.loadingCancellation = ( new CancellationTokenSource());
|
|
279
|
+
this.displayedStore.add(this.loadingCancellation);
|
|
280
|
+
try {
|
|
281
|
+
const details = await coverage.details(this.loadingCancellation.token);
|
|
282
|
+
if (cts.token.isCancellationRequested) {
|
|
283
|
+
return;
|
|
284
|
+
}
|
|
285
|
+
const model = CodeCoverageDecorations_1.fileCoverageDecorations.get(coverage)
|
|
286
|
+
|| ( new CoverageDetailsModel(details, textModel));
|
|
287
|
+
CodeCoverageDecorations_1.fileCoverageDecorations.set(coverage, model);
|
|
288
|
+
return model;
|
|
289
|
+
}
|
|
290
|
+
catch (e) {
|
|
291
|
+
this.log.error('Error loading coverage details', e);
|
|
292
|
+
}
|
|
293
|
+
return undefined;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
CodeCoverageDecorations = CodeCoverageDecorations_1 = ( __decorate([
|
|
297
|
+
( __param(1, IInstantiationService)),
|
|
298
|
+
( __param(2, ITestCoverageService)),
|
|
299
|
+
( __param(3, ILogService))
|
|
300
|
+
], CodeCoverageDecorations));
|
|
301
|
+
const countBadge = (count) => {
|
|
302
|
+
if (count === 0) {
|
|
303
|
+
return undefined;
|
|
304
|
+
}
|
|
305
|
+
return {
|
|
306
|
+
content: `${count > 99 ? '99+' : count}x`,
|
|
307
|
+
cursorStops: InjectedTextCursorStops.None,
|
|
308
|
+
inlineClassName: `coverage-deco-inline-count`,
|
|
309
|
+
inlineClassNameAffectsLetterSpacing: true,
|
|
310
|
+
};
|
|
311
|
+
};
|
|
312
|
+
class CoverageDetailsModel {
|
|
313
|
+
constructor(details, textModel) {
|
|
314
|
+
this.details = details;
|
|
315
|
+
this.ranges = [];
|
|
316
|
+
const detailRanges = ( details.map(detail => ({
|
|
317
|
+
range: tidyLocation(detail.location),
|
|
318
|
+
primary: true,
|
|
319
|
+
metadata: { detail, description: this.describe(detail, textModel) }
|
|
320
|
+
})));
|
|
321
|
+
for (const { range, metadata: { detail } } of detailRanges) {
|
|
322
|
+
if (detail.type === 1 && detail.branches) {
|
|
323
|
+
for (let i = 0; i < detail.branches.length; i++) {
|
|
324
|
+
const branch = { type: 2 , branch: i, detail };
|
|
325
|
+
detailRanges.push({
|
|
326
|
+
range: tidyLocation(detail.branches[i].location || Range.fromPositions(range.getEndPosition())),
|
|
327
|
+
primary: true,
|
|
328
|
+
metadata: {
|
|
329
|
+
detail: branch,
|
|
330
|
+
description: this.describe(branch, textModel),
|
|
331
|
+
},
|
|
332
|
+
});
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
detailRanges.sort((a, b) => Range.compareRangesUsingStarts(a.range, b.range) || a.metadata.detail.type - b.metadata.detail.type);
|
|
337
|
+
const stack = [];
|
|
338
|
+
const result = this.ranges = [];
|
|
339
|
+
const pop = () => {
|
|
340
|
+
const next = stack.pop();
|
|
341
|
+
const prev = stack[stack.length - 1];
|
|
342
|
+
if (prev) {
|
|
343
|
+
prev.range = prev.range.setStartPosition(next.range.endLineNumber, next.range.endColumn);
|
|
344
|
+
}
|
|
345
|
+
result.push(next);
|
|
346
|
+
};
|
|
347
|
+
for (const item of detailRanges) {
|
|
348
|
+
const start = item.range.getStartPosition();
|
|
349
|
+
while (stack[stack.length - 1]?.range.containsPosition(start) === false) {
|
|
350
|
+
pop();
|
|
351
|
+
}
|
|
352
|
+
if (item.range.isEmpty()) {
|
|
353
|
+
result.push(item);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
const prev = stack[stack.length - 1];
|
|
357
|
+
if (prev) {
|
|
358
|
+
const primary = prev.primary;
|
|
359
|
+
const si = prev.range.setEndPosition(start.lineNumber, start.column);
|
|
360
|
+
prev.range = prev.range.setStartPosition(item.range.endLineNumber, item.range.endColumn);
|
|
361
|
+
prev.primary = false;
|
|
362
|
+
if (prev.range.isEmpty()) {
|
|
363
|
+
stack.pop();
|
|
364
|
+
}
|
|
365
|
+
result.push({ range: si, primary, metadata: prev.metadata });
|
|
366
|
+
}
|
|
367
|
+
stack.push(item);
|
|
368
|
+
}
|
|
369
|
+
while (stack.length) {
|
|
370
|
+
pop();
|
|
371
|
+
}
|
|
372
|
+
}
|
|
373
|
+
describe(detail, model) {
|
|
374
|
+
if (detail.type === 0 ) {
|
|
375
|
+
return ( new MarkdownString()).appendMarkdown(( localizeWithPath(
|
|
376
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
377
|
+
'coverage.fnExecutedCount',
|
|
378
|
+
'Function `{0}` was executed {1} time(s).',
|
|
379
|
+
detail.name,
|
|
380
|
+
detail.count
|
|
381
|
+
)));
|
|
382
|
+
}
|
|
383
|
+
else if (detail.type === 1 ) {
|
|
384
|
+
const text = wrapName(model.getValueInRange(tidyLocation(detail.location)).trim() || `<empty statement>`);
|
|
385
|
+
const str = ( new MarkdownString());
|
|
386
|
+
if (detail.branches?.length) {
|
|
387
|
+
const covered = detail.branches.filter(b => b.count > 0).length;
|
|
388
|
+
str.appendMarkdown(( localizeWithPath(
|
|
389
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
390
|
+
'coverage.branches',
|
|
391
|
+
'{0} of {1} of branches in {2} were covered.',
|
|
392
|
+
covered,
|
|
393
|
+
detail.branches.length,
|
|
394
|
+
text
|
|
395
|
+
)));
|
|
396
|
+
}
|
|
397
|
+
else {
|
|
398
|
+
str.appendMarkdown(( localizeWithPath(
|
|
399
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
400
|
+
'coverage.codeExecutedCount',
|
|
401
|
+
'{0} was executed {1} time(s).',
|
|
402
|
+
text,
|
|
403
|
+
detail.count
|
|
404
|
+
)));
|
|
405
|
+
}
|
|
406
|
+
return str;
|
|
407
|
+
}
|
|
408
|
+
else if (detail.type === 2 ) {
|
|
409
|
+
const text = wrapName(model.getValueInRange(tidyLocation(detail.detail.location)).trim() || `<empty statement>`);
|
|
410
|
+
const { count, label } = detail.detail.branches[detail.branch];
|
|
411
|
+
const label2 = label ? wrapInBackticks(label) : `#${detail.branch + 1}`;
|
|
412
|
+
if (count === 0) {
|
|
413
|
+
return ( new MarkdownString()).appendMarkdown(( localizeWithPath(
|
|
414
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
415
|
+
'coverage.branchNotCovered',
|
|
416
|
+
'Branch {0} in {1} was not covered.',
|
|
417
|
+
label2,
|
|
418
|
+
text
|
|
419
|
+
)));
|
|
420
|
+
}
|
|
421
|
+
else {
|
|
422
|
+
return ( new MarkdownString()).appendMarkdown(( localizeWithPath(
|
|
423
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
424
|
+
'coverage.branchCovered',
|
|
425
|
+
'Branch {0} in {1} was executed {2} time(s).',
|
|
426
|
+
label2,
|
|
427
|
+
text,
|
|
428
|
+
count
|
|
429
|
+
)));
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
return undefined;
|
|
433
|
+
}
|
|
434
|
+
}
|
|
435
|
+
function tidyLocation(location) {
|
|
436
|
+
if (location instanceof Position) {
|
|
437
|
+
return Range.fromPositions(location, ( new Position(location.lineNumber, 0x7FFFFFFF)));
|
|
438
|
+
}
|
|
439
|
+
return location;
|
|
440
|
+
}
|
|
441
|
+
let LineHoverComputer = class LineHoverComputer {
|
|
442
|
+
constructor(keybindingService) {
|
|
443
|
+
this.keybindingService = keybindingService;
|
|
444
|
+
this.line = -1;
|
|
445
|
+
}
|
|
446
|
+
computeSync() {
|
|
447
|
+
const strs = [];
|
|
448
|
+
const s = ( new MarkdownString()).appendMarkdown(`[${TOGGLE_INLINE_COMMAND_TEXT}](command:${TOGGLE_INLINE_COMMAND_ID})`);
|
|
449
|
+
s.isTrusted = true;
|
|
450
|
+
const binding = this.keybindingService.lookupKeybinding(TOGGLE_INLINE_COMMAND_ID);
|
|
451
|
+
if (binding) {
|
|
452
|
+
s.appendText(` (${binding.getLabel()})`);
|
|
453
|
+
}
|
|
454
|
+
strs.push(s);
|
|
455
|
+
return strs;
|
|
456
|
+
}
|
|
457
|
+
};
|
|
458
|
+
LineHoverComputer = ( __decorate([
|
|
459
|
+
( __param(0, IKeybindingService))
|
|
460
|
+
], LineHoverComputer));
|
|
461
|
+
function wrapInBackticks(str) {
|
|
462
|
+
return '`' + str.replace(/[\n\r`]/g, '') + '`';
|
|
463
|
+
}
|
|
464
|
+
function wrapName(functionNameOrCode) {
|
|
465
|
+
if (functionNameOrCode.length > 50) {
|
|
466
|
+
functionNameOrCode = functionNameOrCode.slice(0, 40) + '...';
|
|
467
|
+
}
|
|
468
|
+
return wrapInBackticks(functionNameOrCode);
|
|
469
|
+
}
|
|
470
|
+
let LineHoverWidget = class LineHoverWidget extends Disposable {
|
|
471
|
+
static { LineHoverWidget_1 = this; }
|
|
472
|
+
static { this.ID = 'editor.contrib.testingCoverageLineHoverWidget'; }
|
|
473
|
+
constructor(editor, instantiationService) {
|
|
474
|
+
super();
|
|
475
|
+
this.editor = editor;
|
|
476
|
+
this.hover = this._register(( new HoverWidget()));
|
|
477
|
+
this.renderDisposables = this._register(( new DisposableStore()));
|
|
478
|
+
this.computer = instantiationService.createInstance(LineHoverComputer);
|
|
479
|
+
this.markdownRenderer = this._register(instantiationService.createInstance(MarkdownRenderer, { editor: this.editor }));
|
|
480
|
+
this.hoverOperation = this._register(( new HoverOperation(this.editor, this.computer)));
|
|
481
|
+
this.hover.containerDomNode.classList.add('hidden');
|
|
482
|
+
this.hoverOperation.onResult(result => {
|
|
483
|
+
if (result.value.length) {
|
|
484
|
+
this.render(result.value);
|
|
485
|
+
}
|
|
486
|
+
else {
|
|
487
|
+
this.hide();
|
|
488
|
+
}
|
|
489
|
+
});
|
|
490
|
+
this.editor.addOverlayWidget(this);
|
|
491
|
+
}
|
|
492
|
+
getId() {
|
|
493
|
+
return LineHoverWidget_1.ID;
|
|
494
|
+
}
|
|
495
|
+
getDomNode() {
|
|
496
|
+
return this.hover.containerDomNode;
|
|
497
|
+
}
|
|
498
|
+
getPosition() {
|
|
499
|
+
return null;
|
|
500
|
+
}
|
|
501
|
+
dispose() {
|
|
502
|
+
this.editor.removeOverlayWidget(this);
|
|
503
|
+
super.dispose();
|
|
504
|
+
}
|
|
505
|
+
startShowingAt(lineNumber) {
|
|
506
|
+
this.hide();
|
|
507
|
+
const textModel = this.editor.getModel();
|
|
508
|
+
if (!textModel) {
|
|
509
|
+
return;
|
|
510
|
+
}
|
|
511
|
+
this.computer.line = lineNumber;
|
|
512
|
+
this.hoverOperation.start(0 );
|
|
513
|
+
}
|
|
514
|
+
hide() {
|
|
515
|
+
this.hoverOperation.cancel();
|
|
516
|
+
this.hover.containerDomNode.classList.add('hidden');
|
|
517
|
+
}
|
|
518
|
+
render(elements) {
|
|
519
|
+
const { hover: h, editor: editor } = this;
|
|
520
|
+
const fragment = document.createDocumentFragment();
|
|
521
|
+
for (const msg of elements) {
|
|
522
|
+
const markdownHoverElement = $('div.hover-row.markdown-hover');
|
|
523
|
+
const hoverContentsElement = append(markdownHoverElement, $('div.hover-contents'));
|
|
524
|
+
const renderedContents = this.renderDisposables.add(this.markdownRenderer.render(msg));
|
|
525
|
+
hoverContentsElement.appendChild(renderedContents.element);
|
|
526
|
+
fragment.appendChild(markdownHoverElement);
|
|
527
|
+
}
|
|
528
|
+
clearNode(h.contentsDomNode);
|
|
529
|
+
h.contentsDomNode.appendChild(fragment);
|
|
530
|
+
h.containerDomNode.classList.remove('hidden');
|
|
531
|
+
const editorLayout = editor.getLayoutInfo();
|
|
532
|
+
const topForLineNumber = editor.getTopForLineNumber(this.computer.line);
|
|
533
|
+
const editorScrollTop = editor.getScrollTop();
|
|
534
|
+
const lineHeight = editor.getOption(66 );
|
|
535
|
+
const nodeHeight = h.containerDomNode.clientHeight;
|
|
536
|
+
const top = topForLineNumber - editorScrollTop - ((nodeHeight - lineHeight) / 2);
|
|
537
|
+
const left = editorLayout.lineNumbersLeft + editorLayout.lineNumbersWidth;
|
|
538
|
+
h.containerDomNode.style.left = `${left}px`;
|
|
539
|
+
h.containerDomNode.style.top = `${Math.max(Math.round(top), 0)}px`;
|
|
540
|
+
}
|
|
541
|
+
};
|
|
542
|
+
LineHoverWidget = LineHoverWidget_1 = ( __decorate([
|
|
543
|
+
( __param(1, IInstantiationService))
|
|
544
|
+
], LineHoverWidget));
|
|
545
|
+
registerAction2(class ToggleInlineCoverage extends Action2 {
|
|
546
|
+
constructor() {
|
|
547
|
+
super({
|
|
548
|
+
id: TOGGLE_INLINE_COMMAND_ID,
|
|
549
|
+
title: { value: ( localizeWithPath(
|
|
550
|
+
'vs/workbench/contrib/testing/browser/codeCoverageDecorations',
|
|
551
|
+
'coverage.toggleInline',
|
|
552
|
+
"Toggle Inline Coverage"
|
|
553
|
+
)), original: 'Toggle Inline Coverage' },
|
|
554
|
+
category: Categories.Test,
|
|
555
|
+
keybinding: {
|
|
556
|
+
weight: 200 ,
|
|
557
|
+
primary: KeyChord(2048 | 85 , 2048 | 1024 | 39 ),
|
|
558
|
+
},
|
|
559
|
+
precondition: TestingContextKeys.isTestCoverageOpen,
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
run() {
|
|
563
|
+
CodeCoverageDecorations.showInline.set(!CodeCoverageDecorations.showInline.get(), undefined);
|
|
564
|
+
}
|
|
565
|
+
});
|
|
566
|
+
|
|
567
|
+
export { CodeCoverageDecorations, CoverageDetailsModel };
|
|
@@ -40,6 +40,16 @@ const testingDebugIcon = registerIcon('testing-debug-icon', Codicon.debugAltSmal
|
|
|
40
40
|
'testingDebugIcon',
|
|
41
41
|
'Icon of the "debug test" action.'
|
|
42
42
|
)));
|
|
43
|
+
const testingCoverageIcon = registerIcon('testing-coverage-icon', Codicon.heartFilled, ( localizeWithPath(
|
|
44
|
+
'vs/workbench/contrib/testing/browser/icons',
|
|
45
|
+
'testingCoverageIcon',
|
|
46
|
+
'Icon of the "run test with coverage" action.'
|
|
47
|
+
)));
|
|
48
|
+
const testingCoverageAllIcon = registerIcon('testing-coverage-all-icon', Codicon.heartFilled, ( localizeWithPath(
|
|
49
|
+
'vs/workbench/contrib/testing/browser/icons',
|
|
50
|
+
'testingRunAllWithCoverageIcon',
|
|
51
|
+
'Icon of the "run all tests with coverage" action.'
|
|
52
|
+
)));
|
|
43
53
|
const testingCancelIcon = registerIcon('testing-cancel-icon', Codicon.debugStop, ( localizeWithPath(
|
|
44
54
|
'vs/workbench/contrib/testing/browser/icons',
|
|
45
55
|
'testingCancelIcon',
|
|
@@ -95,11 +105,21 @@ const testingCancelRefreshTests = registerIcon('testing-cancel-refresh-tests', C
|
|
|
95
105
|
'testingCancelRefreshTests',
|
|
96
106
|
'Icon on the button to cancel refreshing tests.'
|
|
97
107
|
)));
|
|
98
|
-
const
|
|
108
|
+
const testingCoverageReport = registerIcon('testing-coverage', Codicon.lightBulb, ( localizeWithPath(
|
|
99
109
|
'vs/workbench/contrib/testing/browser/icons',
|
|
100
110
|
'testingCoverage',
|
|
101
111
|
'Icon representing test coverage'
|
|
102
112
|
)));
|
|
113
|
+
const testingWasCovered = registerIcon('testing-was-covered', Codicon.check, ( localizeWithPath(
|
|
114
|
+
'vs/workbench/contrib/testing/browser/icons',
|
|
115
|
+
'testingWasCovered',
|
|
116
|
+
'Icon representing that an element was covered'
|
|
117
|
+
)));
|
|
118
|
+
const testingCoverageMissingBranch = registerIcon('testing-missing-branch', Codicon.question, ( localizeWithPath(
|
|
119
|
+
'vs/workbench/contrib/testing/browser/icons',
|
|
120
|
+
'testingMissingBranch',
|
|
121
|
+
'Icon representing a uncovered block without a range'
|
|
122
|
+
)));
|
|
103
123
|
const testingStatesToIcons = ( new Map([
|
|
104
124
|
[6 , registerIcon('testing-error-icon', Codicon.issues, ( localizeWithPath(
|
|
105
125
|
'vs/workbench/contrib/testing/browser/icons',
|
|
@@ -151,4 +171,4 @@ registerThemingParticipant((theme, collector) => {
|
|
|
151
171
|
`);
|
|
152
172
|
});
|
|
153
173
|
|
|
154
|
-
export { testingCancelIcon, testingCancelRefreshTests, testingContinuousIsOn,
|
|
174
|
+
export { testingCancelIcon, testingCancelRefreshTests, testingContinuousIsOn, testingCoverageAllIcon, testingCoverageIcon, testingCoverageMissingBranch, testingCoverageReport, testingDebugAllIcon, testingDebugIcon, testingFilterIcon, testingHiddenIcon, testingRefreshTests, testingRerunIcon, testingResultsIcon, testingRunAllIcon, testingRunIcon, testingStatesToIcons, testingTurnContinuousRunOff, testingTurnContinuousRunOn, testingUpdateProfiles, testingViewIcon, testingWasCovered };
|