markerService.ts ×21

Frontier kind: Code frontier

unlabeled · c_cadb87ea111a

17 tests · 6112 LOC · 31 files · introduces 0 tests · 226 LOC · 1 file

Introduces — evidence that enters the hierarchy at this concept

Code
21 ranges226 lines · 1 files
Tests
0 tests

Contains — complete concept membership

All code (extent)
900 ranges6112 lines · 31 files · Browse complete extent
All tests (intent)
17 testsBrowse complete intent

Neighbourhood graph

The orange circle is the focus. Violet and green circles are every ancestor and descendant, broader and narrower, at any distance; blue squares and pink diamonds are the introduced files and exact introduced tests of every visible concept, not only the focus's. Arrows point from broader to narrower concepts and bridge only concepts omitted from this view. Undirected links show source or test introduction. Concept and file size follows LOC; exact test nodes use test-count units.

Introduced files, introduced tests, and structurally relevant concept specialization

In the embedded map, ordinary wheel input scrolls the page; use the visible controls to zoom and drag to pan. Open the full-screen map for canvas navigation: wheel pans, Ctrl/Command plus wheel zooms, and arrow keys pan when this region is focused. On touch screens, open the full-screen map to pan or pinch. If JavaScript or WebGL is unavailable, use the native relationship evidence on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the native relationship evidence on this page while the interactive map is unavailable.

Native relationship evidence

Every exact file and test below is linked only from the concept that introduces it.

Introduced tests

Every collected test enters the hierarchy at exactly one concept.

No tests are introduced at this concept. Its intent tests are introduced by other concepts.

Introduced code

Every collected source range enters the hierarchy at exactly one concept.

1 file ranked by introduced lines: 226 introduced LOC across 21 ranges. Expand a file to inspect source; the > gutter marks introduced lines.

src/vs/platform/markers/common/markerService.ts 226 introduced LOC · 21 ranges

Open complete file

1 > /*--------------------------------------------------------------------------------------------- markerService.ts
2 > * Copyright (c) Microsoft Corporation. All rights reserved.
3 > * Licensed under the MIT License. See License.txt in the project root for license information.
4 > *--------------------------------------------------------------------------------------------*/
5 >
6 > import { isFalsyOrEmpty, isNonEmptyArray } from '../../../base/common/arrays.js';
7 > import { MicrotaskEmitter } from '../../../base/common/event.js';
8 > import { Iterable } from '../../../base/common/iterator.js';
9 > import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js';
10 > import { ResourceMap, ResourceSet } from '../../../base/common/map.js';
11 > import { Schemas } from '../../../base/common/network.js';
12 > import { URI } from '../../../base/common/uri.js';
13 > import { localize } from '../../../nls.js';
14 > import { IMarker, IMarkerData, IMarkerReadOptions, IMarkerService, IResourceMarker, MarkerSeverity, MarkerStatistics } from './markers.js';
15 >
16 > export const unsupportedSchemas = new Set([
17 > Schemas.inMemory,
18 > Schemas.vscodeSourceControl,
19 > Schemas.walkThrough,
20 > Schemas.walkThroughSnippet,
21 > Schemas.vscodeChatCodeBlock,
22 > Schemas.vscodeTerminal
23 > ]);
24 >
25 > class DoubleResourceMap<V> {
26 >
27 > private _byResource = new ResourceMap<Map<string, V>>();
28 > private _byOwner = new Map<string, ResourceMap<V>>();
29 >
30 > set(resource: URI, owner: string, value: V) {
31 > let ownerMap = this._byResource.get(resource);
32 > if (!ownerMap) {
33 > ownerMap = new Map();
34 > this._byResource.set(resource, ownerMap);
35 > }
36 > ownerMap.set(owner, value);
37 >
38 > let resourceMap = this._byOwner.get(owner);
39 > if (!resourceMap) {
40 > resourceMap = new ResourceMap();
41 > this._byOwner.set(owner, resourceMap);
42 > }
43 > resourceMap.set(resource, value);
44 > }
45 >
46 > get(resource: URI, owner: string): V | undefined {
47 const ownerMap = this._byResource.get(resource);
48 return ownerMap?.get(owner);
49 }
51 > delete(resource: URI, owner: string): boolean {
52 let removedA = false;
53 let removedB = false;
65 return removedA && removedB;
66 }
68 > values(key?: URI | string): Iterable<V> {
69 > if (typeof key === 'string') {
70 return this._byOwner.get(key)?.values() ?? Iterable.empty();
71 }
72 > if (URI.isUri(key)) { markerService.ts
73 > return this._byResource.get(key)?.values() ?? Iterable.empty();
74 > }
75
76 return Iterable.map(Iterable.concat(...this._byOwner.values()), map => map[1]);
78 > }
79 >
80 > class MarkerStats implements MarkerStatistics {
81 >
82 > errors: number = 0;
83 > infos: number = 0;
84 > warnings: number = 0;
85 > unknowns: number = 0;
86 >
87 > private readonly _data = new ResourceMap<MarkerStatistics>();
88 > private readonly _service: IMarkerService;
89 > private readonly _subscription: IDisposable;
90 >
91 > constructor(service: IMarkerService) {
92 > this._service = service;
93 > this._subscription = service.onMarkerChanged(this._update, this);
94 > }
95 >
96 > dispose(): void {
97 > this._subscription.dispose();
98 > }
99 >
100 > private _update(resources: readonly URI[]): void {
101 > for (const resource of resources) {
102 > const oldStats = this._data.get(resource);
103 > if (oldStats) {
104 this._substract(oldStats);
105 }
106 > const newStats = this._resourceStats(resource); markerService.ts
107 > this._add(newStats);
108 > this._data.set(resource, newStats);
109 > }
110 > }
111 >
112 > private _resourceStats(resource: URI): MarkerStatistics {
113 > const result: MarkerStatistics = { errors: 0, warnings: 0, infos: 0, unknowns: 0 };
114 >
115 > // TODO this is a hack
116 > if (unsupportedSchemas.has(resource.scheme)) {
117 return result;
118 }
120 > for (const { severity } of this._service.read({ resource })) {
121 if (severity === MarkerSeverity.Error) {
122 result.errors += 1;
129 }
130 }
132 > return result;
133 > }
134 >
135 > private _substract(op: MarkerStatistics) {
136 this.errors -= op.errors;
137 this.warnings -= op.warnings;
139 this.unknowns -= op.unknowns;
140 }
142 > private _add(op: MarkerStatistics) {
143 > this.errors += op.errors;
144 > this.warnings += op.warnings;
145 > this.infos += op.infos;
146 > this.unknowns += op.unknowns;
147 > }
148 > }
149 >
150 > export class MarkerService implements IMarkerService {
151 >
152 > declare readonly _serviceBrand: undefined;
153 >
154 > private readonly _onMarkerChanged = new MicrotaskEmitter<readonly URI[]>({
155 > merge: MarkerService._merge
156 > });
157 >
158 > readonly onMarkerChanged = this._onMarkerChanged.event;
159 >
160 > private readonly _data = new DoubleResourceMap<IMarker[]>();
161 > private readonly _stats = new MarkerStats(this);
162 > private readonly _filteredResources = new ResourceMap<string[]>();
163 >
164 > dispose(): void {
165 > this._stats.dispose();
166 > this._onMarkerChanged.dispose();
167 > }
168 >
169 > getStatistics(): MarkerStatistics {
170 return this._stats;
171 }
173 > remove(owner: string, resources: URI[]): void {
174 for (const resource of resources || []) {
175 this.changeOne(owner, resource, []);
176 }
177 }
179 > changeOne(owner: string, resource: URI, markerData: IMarkerData[]): void {
180
181 if (isFalsyOrEmpty(markerData)) {
199 }
200 }
202 > installResourceFilter(resource: URI, reason: string): IDisposable {
203 let reasons = this._filteredResources.get(resource);
204
225 });
226 }
228 > private static _toMarker(owner: string, resource: URI, data: IMarkerData): IMarker | undefined {
229 > let {
230 > code, severity,
231 > message, source,
232 > startLineNumber, startColumn, endLineNumber, endColumn,
233 > relatedInformation,
234 > modelVersionId,
235 > tags, origin
236 > } = data;
237 >
238 > if (!message) {
239 return undefined;
240 }
242 > // santize data
243 > startLineNumber = startLineNumber > 0 ? startLineNumber : 1;
244 > startColumn = startColumn > 0 ? startColumn : 1;
245 > endLineNumber = endLineNumber >= startLineNumber ? endLineNumber : startLineNumber;
246 > endColumn = endColumn > 0 ? endColumn : startColumn;
247 >
248 > return {
249 > resource,
250 > owner,
251 > code,
252 > severity,
253 > message,
254 > source,
255 > startLineNumber,
256 > startColumn,
257 > endLineNumber,
258 > endColumn,
259 > relatedInformation,
260 > modelVersionId,
261 > tags,
262 > origin
263 > };
264 > }
265 >
266 > changeAll(owner: string, data: IResourceMarker[]): void {
267 const changes: URI[] = [];
268
309 }
310 }
312 > /**
313 > * Creates an information marker for filtered resources
314 > */
315 > private _createFilteredMarker(resource: URI, reasons: string[]): IMarker {
316 const message = reasons.length === 1
317 ? localize('filtered', "Problems are paused because: \"{0}\"", reasons[0])
329 };
330 }
332 > read(filter: IMarkerReadOptions = Object.create(null)): IMarker[] {
333 >
334 > let { owner, resource, severities, take } = filter;
335 >
336 > if (!take || take < 0) {
337 > take = -1;
338 > }
339 >
340 > if (owner && resource) {
341 // exactly one owner AND resource
342 const reasons = !filter.ignoreResourceFilters ? this._filteredResources.get(resource) : undefined;
366 return result;
367
368 > } else { markerService.ts
369 > // of one resource OR owner
370 > const iterable = !owner && !resource
371 ? this._data.values()
372 > : this._data.values(resource ?? owner!); markerService.ts
373 >
374 > const result: IMarker[] = [];
375 > const filtered = new ResourceSet();
376 >
377 > for (const markers of iterable) {
378 > for (const data of markers) {
379 > if (filtered.has(data.resource)) {
380 continue;
381 }
382 > if (take > 0 && result.length === take) { markerService.ts
383 break;
384 }
385 > const reasons = !filter.ignoreResourceFilters ? this._filteredResources.get(data.resource) : undefined; markerService.ts
386 > if (reasons?.length) {
387 result.push(this._createFilteredMarker(data.resource, reasons));
388 filtered.add(data.resource);
389
390 > } else if (MarkerService._accept(data, severities)) { markerService.ts
391 > result.push(data);
392 > }
393 > }
394 > }
395 > return result;
396 > }
397 > }
398 >
399 > private static _accept(marker: IMarker, severities?: number): boolean {
400 > return severities === undefined || (severities & marker.severity) === marker.severity;
401 > }
402 >
403 > // --- event debounce logic
404 >
405 > private static _merge(all: (readonly URI[])[]): URI[] {
406 > const set = new ResourceMap<boolean>();
407 > for (const array of all) {
408 > for (const item of array) {
409 > set.set(item, true);
410 > }
411 > }
412 > return Array.from(set.keys());
413 > }
414 > }