src/vs/workbench/contrib/tasks/common/problemMatcher.ts

2031 LOC · 1618 covered · 413 uncovered · 234 ranges · 186 concepts · 42 introducers · 83 tests

File neighbourhood

The centred file is linked to every concept that introduces one of its ranges, every test that runs code from the file, and the gray connector concepts standing between those tests and the file's own introducer concepts. Undirected links join concepts to every file where they introduce source and concepts to the tests they introduce; arrows show specialization between the displayed concepts and bridge only concepts omitted from this view. Concept colors match the source ranges below; connector concepts have no source color and are shown in gray.

Focused file, its introducer and connector concepts, their introduced files, and tests that run code from the file

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 related-file, concept, and source links on this page.

Graph controls are ready.

Interactive rendering requires JavaScript and WebGL. Use the related-file, concept, and source links on this page while the interactive map is unavailable.

1 > /*--------------------------------------------------------------------------------------------- problemMatcher.ts ×64
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 { localize } from '../../../../nls.js';
7 >
8 > import * as Objects from '../../../../base/common/objects.js';
9 > import * as Strings from '../../../../base/common/strings.js';
10 > import * as Assert from '../../../../base/common/assert.js';
11 > import { join, normalize } from '../../../../base/common/path.js';
12 > import * as Types from '../../../../base/common/types.js';
13 > import * as UUID from '../../../../base/common/uuid.js';
14 > import * as Platform from '../../../../base/common/platform.js';
15 > import Severity from '../../../../base/common/severity.js';
16 > import { URI } from '../../../../base/common/uri.js';
17 > import { IJSONSchema } from '../../../../base/common/jsonSchema.js';
18 > import { ValidationStatus, ValidationState, IProblemReporter, Parser } from '../../../../base/common/parsers.js';
19 > import { IStringDictionary } from '../../../../base/common/collections.js';
20 > import { asArray } from '../../../../base/common/arrays.js';
21 > import { Schemas as NetworkSchemas } from '../../../../base/common/network.js';
22 >
23 > import { IMarkerData, MarkerSeverity } from '../../../../platform/markers/common/markers.js';
24 > import { ExtensionsRegistry, ExtensionMessageCollector } from '../../../services/extensions/common/extensionsRegistry.js';
25 > import { Event, Emitter } from '../../../../base/common/event.js';
26 > import { FileType, IFileService, IFileStatWithPartialMetadata, IFileSystemProvider } from '../../../../platform/files/common/files.js';
27 > import { ILogService } from '../../../../platform/log/common/log.js';
28 >
29 > export enum FileLocationKind {
30 > Default,
31 > Relative,
32 > Absolute,
33 > AutoDetect,
34 > Search
35 > }
36 >
37 > export namespace FileLocationKind {
38 > export function fromString(value: string): FileLocationKind | undefined {
39 > value = value.toLowerCase(); problemMatcher.ts ×4
40 > if (value === 'absolute') {
41 > return FileLocationKind.Absolute; problemMatcher.ts ×9
42 > } else if (value === 'relative') { problemMatcher.ts ×4
43 > return FileLocationKind.Relative; problemMatcher.ts ×3
44 > } else if (value === 'autodetect') {
45 return FileLocationKind.AutoDetect;
46 } else if (value === 'search') {
47 return FileLocationKind.Search;
48 } else {
49 return undefined;
50 }
53 >
54 > export enum ProblemLocationKind {
55 > File,
56 > Location
57 > }
58 >
59 > export namespace ProblemLocationKind {
60 > export function fromString(value: string): ProblemLocationKind | undefined {
61 > value = value.toLowerCase(); problemMatcher.ts ×3
62 > if (value === 'file') {
63 > return ProblemLocationKind.File;
64 > } else if (value === 'location') {
65 return ProblemLocationKind.Location;
66 } else {
67 return undefined;
68 }
71 >
72 > export interface IProblemPattern {
73 > regexp: RegExp;
74 >
75 > kind?: ProblemLocationKind;
76 >
77 > file?: number;
78 >
79 > message?: number;
80 >
81 > location?: number;
82 >
83 > line?: number;
84 >
85 > character?: number;
86 >
87 > endLine?: number;
88 >
89 > endCharacter?: number;
90 >
91 > code?: number;
92 >
93 > severity?: number;
94 >
95 > loop?: boolean;
96 > }
97 >
98 > export interface INamedProblemPattern extends IProblemPattern {
99 > name: string;
100 > }
101 >
102 > export type MultiLineProblemPattern = IProblemPattern[];
103 >
104 > export interface IWatchingPattern {
105 > regexp: RegExp;
106 > file?: number;
107 > }
108 >
109 > export interface IWatchingMatcher {
110 > activeOnStart: boolean;
111 > beginsPattern: IWatchingPattern;
112 > endsPattern: IWatchingPattern;
113 > }
114 >
115 > export enum ApplyToKind {
116 > allDocuments,
117 > openDocuments,
118 > closedDocuments
119 > }
120 >
121 > export namespace ApplyToKind {
122 > export function fromString(value: string): ApplyToKind | undefined {
123 > value = value.toLowerCase(); problemMatcher.ts ×9
124 > if (value === 'alldocuments') {
125 return ApplyToKind.allDocuments;
126 > } else if (value === 'opendocuments') { problemMatcher.ts ×9
127 return ApplyToKind.openDocuments;
128 > } else if (value === 'closeddocuments') { problemMatcher.ts ×9
129 > return ApplyToKind.closedDocuments;
130 > } else {
131 return undefined;
132 }
135 >
136 > export interface ProblemMatcher {
137 > owner: string;
138 > source?: string;
139 > applyTo: ApplyToKind;
140 > fileLocation: FileLocationKind;
141 > filePrefix?: string | Config.SearchFileLocationArgs;
142 > pattern: Types.SingleOrMany<IProblemPattern>;
143 > severity?: Severity;
144 > watching?: IWatchingMatcher;
145 > uriProvider?: (path: string) => URI;
146 > }
147 >
148 > export interface INamedProblemMatcher extends ProblemMatcher {
149 > name: string;
150 > label: string;
151 > deprecated?: boolean;
152 > }
153 >
154 > export interface INamedMultiLineProblemPattern {
155 > name: string;
156 > label: string;
157 > patterns: MultiLineProblemPattern;
158 > }
159 >
160 > export function isNamedProblemMatcher(value: ProblemMatcher | undefined): value is INamedProblemMatcher {
161 return value && Types.isString((<INamedProblemMatcher>value).name) ? true : false;
162 }
164 > interface ILocation {
165 > startLineNumber: number;
166 > startCharacter: number;
167 > endLineNumber: number;
168 > endCharacter: number;
169 > }
170 >
171 > interface IProblemData {
172 > kind?: ProblemLocationKind;
173 > file?: string;
174 > location?: string;
175 > line?: string;
176 > character?: string;
177 > endLine?: string;
178 > endCharacter?: string;
179 > message?: string;
180 > severity?: string;
181 > code?: string;
182 > }
183 >
184 > export interface IProblemMatch {
185 > resource: Promise<URI>;
186 > marker: IMarkerData;
187 > description: ProblemMatcher;
188 > }
189 >
190 > export interface IHandleResult {
191 > match: IProblemMatch | null;
192 > continue: boolean;
193 > }
194 >
195 >
196 > export async function getResource(filename: string, matcher: ProblemMatcher, fileService?: IFileService): Promise<URI> { problemMatcher.ts ×24
197 > const kind = matcher.fileLocation;
198 > let fullPath: string | undefined;
199 > if (kind === FileLocationKind.Absolute) {
200 > fullPath = filename;
201 > } else if ((kind === FileLocationKind.Relative) && matcher.filePrefix && Types.isString(matcher.filePrefix)) {
202 fullPath = join(matcher.filePrefix, filename);
203 } else if (kind === FileLocationKind.AutoDetect) {
204 const matcherClone = Objects.deepClone(matcher);
205 matcherClone.fileLocation = FileLocationKind.Relative;
206 if (fileService) {
207 const relative = await getResource(filename, matcherClone);
208 let stat: IFileStatWithPartialMetadata | undefined = undefined;
209 try {
210 stat = await fileService.stat(relative);
211 } catch (ex) {
212 // Do nothing, we just need to catch file resolution errors.
213 }
214 if (stat) {
215 return relative;
216 }
217 }
218
219 matcherClone.fileLocation = FileLocationKind.Absolute;
220 return getResource(filename, matcherClone);
221 } else if (kind === FileLocationKind.Search && fileService) {
222 const fsProvider = fileService.getProvider(NetworkSchemas.file);
223 if (fsProvider) {
224 const uri = await searchForFileLocation(filename, fsProvider, matcher.filePrefix as Config.SearchFileLocationArgs);
225 fullPath = uri?.path;
226 }
227
228 if (!fullPath) {
229 const absoluteMatcher = Objects.deepClone(matcher);
230 absoluteMatcher.fileLocation = FileLocationKind.Absolute;
231 return getResource(filename, absoluteMatcher);
232 }
233 }
234 > if (fullPath === undefined) { problemMatcher.ts ×24
235 throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.');
236 }
237 > fullPath = normalize(fullPath); problemMatcher.ts ×24
238 > fullPath = fullPath.replace(/\\/g, '/');
239 > if (fullPath[0] !== '/') {
240 > fullPath = '/' + fullPath; problemMatcher.ts ×1
241 > }
242 > if (matcher.uriProvider !== undefined) { problemMatcher.ts ×24
243 return matcher.uriProvider(fullPath);
244 > } else { problemMatcher.ts ×24
245 > return URI.file(fullPath);
246 > }
247 > }
249 async function searchForFileLocation(filename: string, fsProvider: IFileSystemProvider, args: Config.SearchFileLocationArgs): Promise<URI | undefined> {
250 const exclusions = new Set(asArray(args.exclude || []).map(x => URI.file(x).path));
251 async function search(dir: URI): Promise<URI | undefined> {
252 if (exclusions.has(dir.path)) {
253 return undefined;
254 }
255
256 const entries = await fsProvider.readdir(dir);
257 const subdirs: URI[] = [];
258
259 for (const [name, fileType] of entries) {
260 if (fileType === FileType.Directory) {
261 subdirs.push(URI.joinPath(dir, name));
262 continue;
263 }
264
265 if (fileType === FileType.File) {
266 /**
267 * Note that sometimes the given `filename` could be a relative
268 * path (not just the "name.ext" part). For example, the
269 * `filename` can be "/subdir/name.ext". So, just comparing
270 * `name` as `filename` is not sufficient. The workaround here
271 * is to form the URI with `dir` and `name` and check if it ends
272 * with the given `filename`.
273 */
274 const fullUri = URI.joinPath(dir, name);
275 if (fullUri.path.endsWith(filename)) {
276 return fullUri;
277 }
278 }
279 }
280
281 for (const subdir of subdirs) {
282 const result = await search(subdir);
283 if (result) {
284 return result;
285 }
286 }
287 return undefined;
288 }
289
290 for (const dir of asArray(args.include || [])) {
291 const hit = await search(URI.file(dir));
292 if (hit) {
293 return hit;
294 }
295 }
296 return undefined;
297 }
299 > export interface ILineMatcher {
300 > matchLength: number;
301 > next(line: string): IProblemMatch | null;
302 > handle(lines: string[], start?: number): IHandleResult;
303 > }
304 >
305 > export function createLineMatcher(matcher: ProblemMatcher, fileService?: IFileService, logService?: ILogService): ILineMatcher {
306 > const pattern = matcher.pattern; problemMatcher.ts ×8
307 > if (Array.isArray(pattern)) {
308 return new MultiLineMatcher(matcher, fileService, logService);
309 > } else { problemMatcher.ts ×8
310 > return new SingleLineMatcher(matcher, fileService, logService);
311 > }
312 > }
314 > const endOfLine: string = Platform.OS === Platform.OperatingSystem.Windows ? '\r\n' : '\n';
315 >
316 > abstract class AbstractLineMatcher implements ILineMatcher {
317 > private matcher: ProblemMatcher;
318 > private fileService?: IFileService;
319 > private logService?: ILogService;
320 >
321 > constructor(matcher: ProblemMatcher, fileService?: IFileService, logService?: ILogService) {
322 > this.matcher = matcher; problemMatcher.ts ×8
323 > this.fileService = fileService;
324 > this.logService = logService;
325 > }
327 > public handle(lines: string[], start: number = 0): IHandleResult {
328 return { match: null, continue: false };
329 }
331 > public next(line: string): IProblemMatch | null {
332 return null;
333 }
335 > public abstract get matchLength(): number;
336 >
337 > protected regexpExec(regexp: RegExp, line: string): RegExpExecArray | null {
338 > const start = Date.now(); problemMatcher.ts ×8
339 > const result = regexp.exec(line);
340 > const elapsed = Date.now() - start;
341 > if (elapsed > 5) {
342 this.logService?.trace(`ProblemMatcher: slow regexp took ${elapsed}ms to execute`, regexp.source);
343 }
344 > return result; problemMatcher.ts ×8
345 > }
347 > protected fillProblemData(data: IProblemData | undefined, pattern: IProblemPattern, matches: RegExpExecArray): data is IProblemData {
348 > if (data) { problemMatcher.ts ×24
349 > this.fillProperty(data, 'file', pattern, matches, true);
350 > this.appendProperty(data, 'message', pattern, matches, true);
351 > this.fillProperty(data, 'code', pattern, matches, true);
352 > this.fillProperty(data, 'severity', pattern, matches, true);
353 > this.fillProperty(data, 'location', pattern, matches, true);
354 > this.fillProperty(data, 'line', pattern, matches);
355 > this.fillProperty(data, 'character', pattern, matches);
356 > this.fillProperty(data, 'endLine', pattern, matches);
357 > this.fillProperty(data, 'endCharacter', pattern, matches);
358 > return true;
359 > } else {
360 return false;
361 }
364 > private appendProperty(data: IProblemData, property: keyof IProblemData, pattern: IProblemPattern, matches: RegExpExecArray, trim: boolean = false): void {
365 > const patternProperty = pattern[property]; problemMatcher.ts ×24
366 > if (Types.isUndefined(data[property])) {
367 > this.fillProperty(data, property, pattern, matches, trim);
368 > }
369 else if (!Types.isUndefined(patternProperty) && patternProperty < matches.length) {
370 let value = matches[patternProperty];
371 if (trim) {
372 value = Strings.trim(value)!;
373 }
374 (data as Record<string, string | undefined>)[property] = data[property]! + endOfLine + value;
375 }
378 > private fillProperty(data: IProblemData, property: keyof IProblemData, pattern: IProblemPattern, matches: RegExpExecArray, trim: boolean = false): void {
379 > const patternAtProperty = pattern[property]; problemMatcher.ts ×24
380 > if (Types.isUndefined(data[property]) && !Types.isUndefined(patternAtProperty) && patternAtProperty < matches.length) {
381 > let value = matches[patternAtProperty];
382 > if (value !== undefined) {
383 > if (trim) {
384 > value = Strings.trim(value)!;
385 > }
386 > (data as Record<string, string | undefined>)[property] = value;
387 > }
388 > }
389 > }
391 > protected getMarkerMatch(data: IProblemData): IProblemMatch | undefined {
393 > const location = this.getLocation(data);
394 > if (data.file && location && data.message) {
395 > const marker: IMarkerData = {
396 > severity: this.getSeverity(data),
397 > startLineNumber: location.startLineNumber,
398 > startColumn: location.startCharacter,
399 > endLineNumber: location.endLineNumber,
400 > endColumn: location.endCharacter,
401 > message: data.message
402 > };
403 > if (data.code !== undefined) {
404 > marker.code = data.code; problemMatcher.ts ×1
405 > }
406 > if (this.matcher.source !== undefined) { problemMatcher.ts ×24
407 marker.source = this.matcher.source;
408 }
409 > return { problemMatcher.ts ×24
410 > description: this.matcher,
411 > resource: this.getResource(data.file),
412 > marker: marker
413 > };
414 > }
415 > } catch (err) {
416 console.error(`Failed to convert problem data into match: ${JSON.stringify(data)}`);
417 }
418 return undefined;
421 > protected getResource(filename: string): Promise<URI> {
422 > return getResource(filename, this.matcher, this.fileService); problemMatcher.ts ×24
423 > }
425 > private getLocation(data: IProblemData): ILocation | null {
426 > if (data.kind === ProblemLocationKind.File) { problemMatcher.ts ×24
427 > return this.createLocation(0, 0, 0, 0); problemMatcher.ts ×2
428 > }
429 > if (data.location) { problemMatcher.ts ×5
430 > return this.parseLocationInfo(data.location);
431 > }
432 if (!data.line) {
433 return null;
434 }
435 const startLine = parseInt(data.line);
436 > const startColumn = data.character ? parseInt(data.character) : undefined; problemMatcher.ts ×24
437 > const endLine = data.endLine ? parseInt(data.endLine) : undefined;
438 > const endColumn = data.endCharacter ? parseInt(data.endCharacter) : undefined;
439 > return this.createLocation(startLine, startColumn, endLine, endColumn);
440 > }
442 > private parseLocationInfo(value: string): ILocation | null {
443 > if (!value || !value.match(/(\d+|\d+,\d+|\d+,\d+,\d+,\d+)/)) { problemMatcher.ts ×5
444 return null;
445 }
446 > const parts = value.split(','); problemMatcher.ts ×5
447 > const startLine = parseInt(parts[0]);
448 > const startColumn = parts.length > 1 ? parseInt(parts[1]) : undefined;
449 > if (parts.length > 3) {
450 > return this.createLocation(startLine, startColumn, parseInt(parts[2]), parseInt(parts[3])); problemMatcher.ts ×8
451 > } else { problemMatcher.ts ×5
452 > return this.createLocation(startLine, startColumn, undefined, undefined); problemMatcher.ts ×2
453 > }
456 > private createLocation(startLine: number, startColumn: number | undefined, endLine: number | undefined, endColumn: number | undefined): ILocation {
457 > if (startColumn !== undefined && endColumn !== undefined) { problemMatcher.ts ×24
458 > return { startLineNumber: startLine, startCharacter: startColumn, endLineNumber: endLine || startLine, endCharacter: endColumn }; problemMatcher.ts ×1
459 > }
460 > if (startColumn !== undefined) { problemMatcher.ts ×2
461 > return { startLineNumber: startLine, startCharacter: startColumn, endLineNumber: startLine, endCharacter: startColumn }; problemMatcher.ts ×1
462 > }
463 > return { startLineNumber: startLine, startCharacter: 1, endLineNumber: startLine, endCharacter: 2 ** 31 - 1 }; // See https://github.com/microsoft/vscode/issues/80288#issuecomment-650636442 for discussion problemMatcher.ts ×1
466 > private getSeverity(data: IProblemData): MarkerSeverity {
467 > let result: Severity | null = null; problemMatcher.ts ×24
468 > if (data.severity) {
469 > const value = data.severity;
470 > if (value) {
471 > result = Severity.fromValue(value);
472 > if (result === Severity.Ignore) {
473 > if (value === 'E') { problemMatcher.ts ×8
474 result = Severity.Error;
475 > } else if (value === 'W') { problemMatcher.ts ×8
476 result = Severity.Warning;
477 > } else if (value === 'I') { problemMatcher.ts ×8
478 result = Severity.Info;
479 > } else if (Strings.equalsIgnoreCase(value, 'hint')) { problemMatcher.ts ×8
480 result = Severity.Info;
481 > } else if (Strings.equalsIgnoreCase(value, 'note')) { problemMatcher.ts ×8
482 result = Severity.Info;
483 }
486 > }
487 > if (result === null || result === Severity.Ignore) {
488 > result = this.matcher.severity || Severity.Error; problemMatcher.ts ×8
489 > }
490 > return MarkerSeverity.fromSeverity(result); problemMatcher.ts ×24
491 > }
493 >
494 > class SingleLineMatcher extends AbstractLineMatcher {
495 >
496 > private pattern: IProblemPattern;
497 >
498 > constructor(matcher: ProblemMatcher, fileService?: IFileService, logService?: ILogService) {
499 > super(matcher, fileService, logService); problemMatcher.ts ×8
500 > this.pattern = <IProblemPattern>matcher.pattern;
501 > }
503 > public get matchLength(): number {
504 return 1;
505 }
507 > public override handle(lines: string[], start: number = 0): IHandleResult {
508 > Assert.ok(lines.length - start === 1); problemMatcher.ts ×8
509 > const data: IProblemData = Object.create(null);
510 > if (this.pattern.kind !== undefined) {
511 > data.kind = this.pattern.kind;
512 > }
513 > const matches = this.regexpExec(this.pattern.regexp, lines[start]);
514 > if (matches) {
515 > this.fillProblemData(data, this.pattern, matches); problemMatcher.ts ×24
516 > if (data.kind === ProblemLocationKind.Location && !data.location && !data.line && data.file) {
517 > data.kind = ProblemLocationKind.File; problemMatcher.ts ×2
518 > }
519 > const match = this.getMarkerMatch(data); problemMatcher.ts ×24
520 > if (match) {
521 > return { match: match, continue: false };
522 > }
523 > }
524 > return { match: null, continue: false }; problemMatcher.ts ×1
527 > public override next(line: string): IProblemMatch | null {
528 return null;
529 }
531 >
532 > class MultiLineMatcher extends AbstractLineMatcher {
533 >
534 > private patterns: IProblemPattern[];
535 > private data: IProblemData | undefined;
536 >
537 > constructor(matcher: ProblemMatcher, fileService?: IFileService, logService?: ILogService) {
538 super(matcher, fileService, logService);
539 this.patterns = <IProblemPattern[]>matcher.pattern;
540 }
542 > public get matchLength(): number {
543 return this.patterns.length;
544 }
546 > public override handle(lines: string[], start: number = 0): IHandleResult {
547 Assert.ok(lines.length - start === this.patterns.length);
548 this.data = Object.create(null);
549 let data = this.data!;
550 data.kind = this.patterns[0].kind;
551 for (let i = 0; i < this.patterns.length; i++) {
552 const pattern = this.patterns[i];
553 const matches = this.regexpExec(pattern.regexp, lines[i + start]);
554 if (!matches) {
555 return { match: null, continue: false };
556 } else {
557 // Only the last pattern can loop
558 if (pattern.loop && i === this.patterns.length - 1) {
559 data = Objects.deepClone(data);
560 }
561 this.fillProblemData(data, pattern, matches);
562 }
563 }
564 const loop = !!this.patterns[this.patterns.length - 1].loop;
565 if (!loop) {
566 this.data = undefined;
567 }
568 const markerMatch = data ? this.getMarkerMatch(data) : null;
569 return { match: markerMatch ? markerMatch : null, continue: loop };
570 }
572 > public override next(line: string): IProblemMatch | null {
573 const pattern = this.patterns[this.patterns.length - 1];
574 Assert.ok(pattern.loop === true && this.data !== null);
575 const matches = this.regexpExec(pattern.regexp, line);
576 if (!matches) {
577 this.data = undefined;
578 return null;
579 }
580 const data = Objects.deepClone(this.data);
581 let problemMatch: IProblemMatch | undefined;
582 if (this.fillProblemData(data, pattern, matches)) {
583 problemMatch = this.getMarkerMatch(data);
584 }
585 return problemMatch ? problemMatch : null;
586 }
588 >
589 > export namespace Config {
590 >
591 > export interface IProblemPattern {
592 >
593 > /**
594 > * The regular expression to find a problem in the console output of an
595 > * executed task.
596 > */
597 > regexp?: string;
598 >
599 > /**
600 > * Whether the pattern matches a whole file, or a location (file/line)
601 > *
602 > * The default is to match for a location. Only valid on the
603 > * first problem pattern in a multi line problem matcher.
604 > */
605 > kind?: string;
606 >
607 > /**
608 > * The match group index of the filename.
609 > * If omitted 1 is used.
610 > */
611 > file?: number;
612 >
613 > /**
614 > * The match group index of the problem's location. Valid location
615 > * patterns are: (line), (line,column) and (startLine,startColumn,endLine,endColumn).
616 > * If omitted the line and column properties are used.
617 > */
618 > location?: number;
619 >
620 > /**
621 > * The match group index of the problem's line in the source file.
622 > *
623 > * Defaults to 2.
624 > */
625 > line?: number;
626 >
627 > /**
628 > * The match group index of the problem's column in the source file.
629 > *
630 > * Defaults to 3.
631 > */
632 > column?: number;
633 >
634 > /**
635 > * The match group index of the problem's end line in the source file.
636 > *
637 > * Defaults to undefined. No end line is captured.
638 > */
639 > endLine?: number;
640 >
641 > /**
642 > * The match group index of the problem's end column in the source file.
643 > *
644 > * Defaults to undefined. No end column is captured.
645 > */
646 > endColumn?: number;
647 >
648 > /**
649 > * The match group index of the problem's severity.
650 > *
651 > * Defaults to undefined. In this case the problem matcher's severity
652 > * is used.
653 > */
654 > severity?: number;
655 >
656 > /**
657 > * The match group index of the problem's code.
658 > *
659 > * Defaults to undefined. No code is captured.
660 > */
661 > code?: number;
662 >
663 > /**
664 > * The match group index of the message. If omitted it defaults
665 > * to 4 if location is specified. Otherwise it defaults to 5.
666 > */
667 > message?: number;
668 >
669 > /**
670 > * Specifies if the last pattern in a multi line problem matcher should
671 > * loop as long as it does match a line consequently. Only valid on the
672 > * last problem pattern in a multi line problem matcher.
673 > */
674 > loop?: boolean;
675 > }
676 >
677 > export interface ICheckedProblemPattern extends IProblemPattern {
678 > /**
679 > * The regular expression to find a problem in the console output of an
680 > * executed task.
681 > */
682 > regexp: string;
683 > }
684 >
685 > export namespace CheckedProblemPattern {
686 > export function is(value: unknown): value is ICheckedProblemPattern {
687 > const candidate: IProblemPattern = value as IProblemPattern; problemMatcher.ts ×1
688 > return candidate && Types.isString(candidate.regexp);
689 > }
691 >
692 > export interface INamedProblemPattern extends IProblemPattern {
693 > /**
694 > * The name of the problem pattern.
695 > */
696 > name: string;
697 >
698 > /**
699 > * A human readable label
700 > */
701 > label?: string;
702 > }
703 >
704 > export namespace NamedProblemPattern {
705 > export function is(value: unknown): value is INamedProblemPattern {
706 > const candidate: INamedProblemPattern = value as INamedProblemPattern; problemMatcher.ts ×4
707 > return candidate && Types.isString(candidate.name);
708 > }
710 >
711 > export interface INamedCheckedProblemPattern extends INamedProblemPattern {
712 > /**
713 > * The regular expression to find a problem in the console output of an
714 > * executed task.
715 > */
716 > regexp: string;
717 > }
718 >
719 > export namespace NamedCheckedProblemPattern {
720 > export function is(value: unknown): value is INamedCheckedProblemPattern {
721 > const candidate: INamedProblemPattern = value as INamedProblemPattern; problemMatcher.ts ×4
722 > return candidate && NamedProblemPattern.is(candidate) && Types.isString(candidate.regexp);
723 > }
725 >
726 > export type MultiLineProblemPattern = IProblemPattern[];
727 >
728 > export namespace MultiLineProblemPattern {
729 > export function is(value: unknown): value is MultiLineProblemPattern {
730 > return Array.isArray(value); problemMatcher.ts ×9
731 > }
733 >
734 > export type MultiLineCheckedProblemPattern = ICheckedProblemPattern[];
735 >
736 > export namespace MultiLineCheckedProblemPattern {
737 > export function is(value: unknown): value is MultiLineCheckedProblemPattern {
738 > if (!MultiLineProblemPattern.is(value)) { problemMatcher.ts ×9
739 > return false; problemMatcher.ts ×8
740 > }
741 > for (const element of value) { problemMatcher.ts ×1
742 > if (!Config.CheckedProblemPattern.is(element)) { problemMatcher.ts ×2
743 > return false; problemMatcher.ts ×2
744 > }
746 > return true; problemMatcher.ts ×5
749 >
750 > export interface INamedMultiLineCheckedProblemPattern {
751 > /**
752 > * The name of the problem pattern.
753 > */
754 > name: string;
755 >
756 > /**
757 > * A human readable label
758 > */
759 > label?: string;
760 >
761 > /**
762 > * The actual patterns
763 > */
764 > patterns: MultiLineCheckedProblemPattern;
765 > }
766 >
767 > export namespace NamedMultiLineCheckedProblemPattern {
768 > export function is(value: unknown): value is INamedMultiLineCheckedProblemPattern {
769 > const candidate = value as INamedMultiLineCheckedProblemPattern; problemMatcher.ts ×9
770 > return candidate && Types.isString(candidate.name) && Array.isArray(candidate.patterns) && MultiLineCheckedProblemPattern.is(candidate.patterns);
771 > }
773 >
774 > export type NamedProblemPatterns = (Config.INamedProblemPattern | Config.INamedMultiLineCheckedProblemPattern)[];
775 >
776 > /**
777 > * A watching pattern
778 > */
779 > export interface IWatchingPattern {
780 > /**
781 > * The actual regular expression
782 > */
783 > regexp?: string;
784 >
785 > /**
786 > * The match group index of the filename. If provided the expression
787 > * is matched for that file only.
788 > */
789 > file?: number;
790 > }
791 >
792 > /**
793 > * A description to track the start and end of a watching task.
794 > */
795 > export interface IBackgroundMonitor {
796 >
797 > /**
798 > * If set to true the watcher starts in active mode. This is the
799 > * same as outputting a line that matches beginsPattern when the
800 > * task starts.
801 > */
802 > activeOnStart?: boolean;
803 >
804 > /**
805 > * If matched in the output the start of a watching task is signaled.
806 > */
807 > beginsPattern?: string | IWatchingPattern;
808 >
809 > /**
810 > * If matched in the output the end of a watching task is signaled.
811 > */
812 > endsPattern?: string | IWatchingPattern;
813 > }
814 >
815 > /**
816 > * A description of a problem matcher that detects problems
817 > * in build output.
818 > */
819 > export interface ProblemMatcher {
820 >
821 > /**
822 > * The name of a base problem matcher to use. If specified the
823 > * base problem matcher will be used as a template and properties
824 > * specified here will replace properties of the base problem
825 > * matcher
826 > */
827 > base?: string;
828 >
829 > /**
830 > * The owner of the produced VSCode problem. This is typically
831 > * the identifier of a VSCode language service if the problems are
832 > * to be merged with the one produced by the language service
833 > * or a generated internal id. Defaults to the generated internal id.
834 > */
835 > owner?: string;
836 >
837 > /**
838 > * A human-readable string describing the source of this problem.
839 > * E.g. 'typescript' or 'super lint'.
840 > */
841 > source?: string;
842 >
843 > /**
844 > * Specifies to which kind of documents the problems found by this
845 > * matcher are applied. Valid values are:
846 > *
847 > * "allDocuments": problems found in all documents are applied.
848 > * "openDocuments": problems found in documents that are open
849 > * are applied.
850 > * "closedDocuments": problems found in closed documents are
851 > * applied.
852 > */
853 > applyTo?: string;
854 >
855 > /**
856 > * The severity of the VSCode problem produced by this problem matcher.
857 > *
858 > * Valid values are:
859 > * "error": to produce errors.
860 > * "warning": to produce warnings.
861 > * "info": to produce infos.
862 > *
863 > * The value is used if a pattern doesn't specify a severity match group.
864 > * Defaults to "error" if omitted.
865 > */
866 > severity?: string;
867 >
868 > /**
869 > * Defines how filename reported in a problem pattern
870 > * should be read. Valid values are:
871 > * - "absolute": the filename is always treated absolute.
872 > * - "relative": the filename is always treated relative to
873 > * the current working directory. This is the default.
874 > * - ["relative", "path value"]: the filename is always
875 > * treated relative to the given path value.
876 > * - "autodetect": the filename is treated relative to
877 > * the current workspace directory, and if the file
878 > * does not exist, it is treated as absolute.
879 > * - ["autodetect", "path value"]: the filename is treated
880 > * relative to the given path value, and if it does not
881 > * exist, it is treated as absolute.
882 > * - ["search", { include?: "" | []; exclude?: "" | [] }]: The filename
883 > * needs to be searched under the directories named by the "include"
884 > * property and their nested subdirectories. With "exclude" property
885 > * present, the directories should be removed from the search. When
886 > * `include` is not unprovided, the current workspace directory should
887 > * be used as the default.
888 > */
889 > fileLocation?: Types.SingleOrMany<string> | ['search', SearchFileLocationArgs];
890 >
891 > /**
892 > * The name of a predefined problem pattern, the inline definition
893 > * of a problem pattern or an array of problem patterns to match
894 > * problems spread over multiple lines.
895 > */
896 > pattern?: string | Types.SingleOrMany<IProblemPattern>;
897 >
898 > /**
899 > * A regular expression signaling that a watched tasks begins executing
900 > * triggered through file watching.
901 > */
902 > watchedTaskBeginsRegExp?: string;
903 >
904 > /**
905 > * A regular expression signaling that a watched tasks ends executing.
906 > */
907 > watchedTaskEndsRegExp?: string;
908 >
909 > /**
910 > * @deprecated Use background instead.
911 > */
912 > watching?: IBackgroundMonitor;
913 > background?: IBackgroundMonitor;
914 > }
915 >
916 > export type SearchFileLocationArgs = {
917 > include?: Types.SingleOrMany<string>;
918 > exclude?: Types.SingleOrMany<string>;
919 > };
920 >
921 > export type ProblemMatcherType = string | ProblemMatcher | Array<string | ProblemMatcher>;
922 >
923 > export interface INamedProblemMatcher extends ProblemMatcher {
924 > /**
925 > * This name can be used to refer to the
926 > * problem matcher from within a task.
927 > */
928 > name: string;
929 >
930 > /**
931 > * A human readable label.
932 > */
933 > label?: string;
934 > }
935 >
936 > export function isNamedProblemMatcher(value: ProblemMatcher): value is INamedProblemMatcher {
937 > return Types.isString((<INamedProblemMatcher>value).name); problemMatcher.ts ×30
938 > }
940 >
941 > export class ProblemPatternParser extends Parser {
942 >
943 > constructor(logger: IProblemReporter) {
944 > super(logger); problemMatcher.ts ×9
945 > }
947 > public parse(value: Config.IProblemPattern): IProblemPattern;
948 > public parse(value: Config.MultiLineProblemPattern): MultiLineProblemPattern;
949 > public parse(value: Config.INamedProblemPattern): INamedProblemPattern;
950 > public parse(value: Config.INamedMultiLineCheckedProblemPattern): INamedMultiLineProblemPattern;
951 > public parse(value: Config.IProblemPattern | Config.MultiLineProblemPattern | Config.INamedProblemPattern | Config.INamedMultiLineCheckedProblemPattern): IProblemPattern | MultiLineProblemPattern | INamedProblemPattern | INamedMultiLineProblemPattern | null {
952 > if (Config.NamedMultiLineCheckedProblemPattern.is(value)) { problemMatcher.ts ×9
953 return this.createNamedMultiLineProblemPattern(value);
954 > } else if (Config.MultiLineCheckedProblemPattern.is(value)) { problemMatcher.ts ×9
955 > return this.createMultiLineProblemPattern(value); problemMatcher.ts ×5
956 > } else if (Config.NamedCheckedProblemPattern.is(value)) { problemMatcher.ts ×9
957 const result = this.createSingleProblemPattern(value) as INamedProblemPattern;
958 result.name = value.name;
959 return result;
960 > } else if (Config.CheckedProblemPattern.is(value)) { problemMatcher.ts ×4
961 > return this.createSingleProblemPattern(value); problemMatcher.ts ×8
962 > } else { problemMatcher.ts ×4
963 > this.error(localize('ProblemPatternParser.problemPattern.missingRegExp', 'The problem pattern is missing a regular expression.')); problemMatcher.ts ×2
964 > return null;
965 > }
968 > private createSingleProblemPattern(value: Config.ICheckedProblemPattern): IProblemPattern | null {
969 > const result = this.doCreateSingleProblemPattern(value, true); problemMatcher.ts ×8
970 > if (result === undefined) {
971 return null;
972 > } else if (result.kind === undefined) { problemMatcher.ts ×8
973 > result.kind = ProblemLocationKind.Location; problemMatcher.ts ×1
974 > }
975 > return this.validateProblemPattern([result]) ? result : null; problemMatcher.ts ×8
976 > }
978 > private createNamedMultiLineProblemPattern(value: Config.INamedMultiLineCheckedProblemPattern): INamedMultiLineProblemPattern | null {
979 const validPatterns = this.createMultiLineProblemPattern(value.patterns);
980 if (!validPatterns) {
981 return null;
982 }
983 const result = {
984 name: value.name,
985 label: value.label ? value.label : value.name,
986 patterns: validPatterns
987 };
988 return result;
989 }
991 > private createMultiLineProblemPattern(values: Config.MultiLineCheckedProblemPattern): MultiLineProblemPattern | null {
992 > const result: MultiLineProblemPattern = []; problemMatcher.ts ×5
993 > for (let i = 0; i < values.length; i++) {
994 > const pattern = this.doCreateSingleProblemPattern(values[i], false); problemMatcher.ts ×4
995 > if (pattern === undefined) {
996 return null;
997 }
998 > if (i < values.length - 1) { problemMatcher.ts ×4
999 > if (!Types.isUndefined(pattern.loop) && pattern.loop) { problemMatcher.ts ×2
1000 > pattern.loop = false; problemMatcher.ts ×2
1001 > this.error(localize('ProblemPatternParser.loopProperty.notLast', 'The loop property is only supported on the last line matcher.'));
1002 > }
1004 > result.push(pattern); problemMatcher.ts ×4
1005 > }
1006 > if (!result || result.length === 0) { problemMatcher.ts ×5
1007 > this.error(localize('ProblemPatternParser.problemPattern.emptyPattern', 'The problem pattern is invalid. It must contain at least one pattern.')); problemMatcher.ts ×1
1008 > return null;
1009 > }
1010 > if (result[0].kind === undefined) { problemMatcher.ts ×4
1011 > result[0].kind = ProblemLocationKind.Location; problemMatcher.ts ×1
1012 > }
1013 > return this.validateProblemPattern(result) ? result : null; problemMatcher.ts ×5
1014 > }
1016 > private doCreateSingleProblemPattern(value: Config.ICheckedProblemPattern, setDefaults: boolean): IProblemPattern | undefined {
1017 > const regexp = this.createRegularExpression(value.regexp); problemMatcher.ts ×13
1018 > if (regexp === undefined) {
1019 return undefined;
1020 }
1021 > let result: IProblemPattern = { regexp }; problemMatcher.ts ×13
1022 > if (value.kind) {
1023 > result.kind = ProblemLocationKind.fromString(value.kind); problemMatcher.ts ×3
1024 > }
1026 > function copyProperty(result: IProblemPattern, source: Config.IProblemPattern, resultKey: keyof IProblemPattern, sourceKey: keyof Config.IProblemPattern) {
1027 > const value = source[sourceKey];
1028 > if (typeof value === 'number') {
1029 > (result as unknown as Record<string, unknown>)[resultKey] = value; problemMatcher.ts ×1
1030 > }
1032 > copyProperty(result, value, 'file', 'file');
1033 > copyProperty(result, value, 'location', 'location');
1034 > copyProperty(result, value, 'line', 'line');
1035 > copyProperty(result, value, 'character', 'column');
1036 > copyProperty(result, value, 'endLine', 'endLine');
1037 > copyProperty(result, value, 'endCharacter', 'endColumn');
1038 > copyProperty(result, value, 'severity', 'severity');
1039 > copyProperty(result, value, 'code', 'code');
1040 > copyProperty(result, value, 'message', 'message');
1041 > if (value.loop === true || value.loop === false) {
1042 > result.loop = value.loop; problemMatcher.ts ×2
1043 > }
1044 > if (setDefaults) { problemMatcher.ts ×13
1045 > if (result.location || result.kind === ProblemLocationKind.File) { problemMatcher.ts ×8
1046 > const defaultValue: Partial<IProblemPattern> = { problemMatcher.ts ×1
1047 > file: 1,
1048 > message: 0
1049 > };
1050 > result = Objects.mixin(result, defaultValue, false);
1051 > } else { problemMatcher.ts ×8
1052 > const defaultValue: Partial<IProblemPattern> = { problemMatcher.ts ×1
1053 > file: 1,
1054 > line: 2,
1055 > character: 3,
1056 > message: 0
1057 > };
1058 > result = Objects.mixin(result, defaultValue, false);
1059 > }
1061 > return result; problemMatcher.ts ×13
1062 > }
1064 > private validateProblemPattern(values: IProblemPattern[]): boolean {
1065 > if (!values || values.length === 0) { problemMatcher.ts ×13
1066 this.error(localize('ProblemPatternParser.problemPattern.emptyPattern', 'The problem pattern is invalid. It must contain at least one pattern.'));
1067 return false;
1068 }
1069 > let file: boolean = false, message: boolean = false, location: boolean = false, line: boolean = false; problemMatcher.ts ×13
1070 > const locationKind = (values[0].kind === undefined) ? ProblemLocationKind.Location : values[0].kind;
1071 >
1072 > values.forEach((pattern, i) => {
1073 > if (i !== 0 && pattern.kind) {
1074 this.error(localize('ProblemPatternParser.problemPattern.kindProperty.notFirst', 'The problem pattern is invalid. The kind property must be provided only in the first element'));
1075 }
1076 > file = file || !Types.isUndefined(pattern.file); problemMatcher.ts ×13
1077 > message = message || !Types.isUndefined(pattern.message);
1078 > location = location || !Types.isUndefined(pattern.location);
1079 > line = line || !Types.isUndefined(pattern.line);
1080 > });
1081 > if (!(file && message)) {
1082 > this.error(localize('ProblemPatternParser.problemPattern.missingProperty', 'The problem pattern is invalid. It must have at least have a file and a message.')); problemMatcher.ts ×1
1083 > return false;
1084 > }
1085 > if (locationKind === ProblemLocationKind.Location && !(location || line)) { problemMatcher.ts ×13
1086 > this.error(localize('ProblemPatternParser.problemPattern.missingLocation', 'The problem pattern is invalid. It must either have kind: "file" or have a line or location match group.')); problemMatcher.ts ×1
1087 > return false;
1088 > }
1089 > return true; problemMatcher.ts ×1
1092 > private createRegularExpression(value: string): RegExp | undefined {
1093 > let result: RegExp | undefined; problemMatcher.ts ×13
1094 > try {
1095 > result = new RegExp(value);
1096 > } catch (err) {
1097 this.error(localize('ProblemPatternParser.invalidRegexp', 'Error: The string {0} is not a valid regular expression.\n', value));
1098 }
1099 > return result; problemMatcher.ts ×13
1100 > }
1102 >
1103 > export class ExtensionRegistryReporter implements IProblemReporter {
1104 > constructor(private _collector: ExtensionMessageCollector, private _validationStatus: ValidationStatus = new ValidationStatus()) {
1105 }
1107 > public info(message: string): void {
1108 this._validationStatus.state = ValidationState.Info;
1109 this._collector.info(message);
1110 }
1112 > public warn(message: string): void {
1113 this._validationStatus.state = ValidationState.Warning;
1114 this._collector.warn(message);
1115 }
1117 > public error(message: string): void {
1118 this._validationStatus.state = ValidationState.Error;
1119 this._collector.error(message);
1120 }
1122 > public fatal(message: string): void {
1123 this._validationStatus.state = ValidationState.Fatal;
1124 this._collector.error(message);
1125 }
1127 > public get status(): ValidationStatus {
1128 return this._validationStatus;
1129 }
1131 >
1132 > export namespace Schemas {
1133 >
1134 > export const ProblemPattern: IJSONSchema = {
1135 > default: {
1136 > regexp: '^([^\\\\s].*)\\\\((\\\\d+,\\\\d+)\\\\):\\\\s*(.*)$',
1137 > file: 1,
1138 > location: 2,
1139 > message: 3
1140 > },
1141 > type: 'object',
1142 > additionalProperties: false,
1143 > properties: {
1144 > regexp: {
1145 > type: 'string',
1146 > description: localize('ProblemPatternSchema.regexp', 'The regular expression to find an error, warning or info in the output.')
1147 > },
1148 > kind: {
1149 > type: 'string',
1150 > description: localize('ProblemPatternSchema.kind', 'whether the pattern matches a location (file and line) or only a file.')
1151 > },
1152 > file: {
1153 > type: 'integer',
1154 > description: localize('ProblemPatternSchema.file', 'The match group index of the filename. If omitted 1 is used.')
1155 > },
1156 > location: {
1157 > type: 'integer',
1158 > description: localize('ProblemPatternSchema.location', 'The match group index of the problem\'s location. Valid location patterns are: (line), (line,column) and (startLine,startColumn,endLine,endColumn). If omitted (line,column) is assumed.')
1159 > },
1160 > line: {
1161 > type: 'integer',
1162 > description: localize('ProblemPatternSchema.line', 'The match group index of the problem\'s line. Defaults to 2')
1163 > },
1164 > column: {
1165 > type: 'integer',
1166 > description: localize('ProblemPatternSchema.column', 'The match group index of the problem\'s line character. Defaults to 3')
1167 > },
1168 > endLine: {
1169 > type: 'integer',
1170 > description: localize('ProblemPatternSchema.endLine', 'The match group index of the problem\'s end line. Defaults to undefined')
1171 > },
1172 > endColumn: {
1173 > type: 'integer',
1174 > description: localize('ProblemPatternSchema.endColumn', 'The match group index of the problem\'s end line character. Defaults to undefined')
1175 > },
1176 > severity: {
1177 > type: 'integer',
1178 > description: localize('ProblemPatternSchema.severity', 'The match group index of the problem\'s severity. Defaults to undefined')
1179 > },
1180 > code: {
1181 > type: 'integer',
1182 > description: localize('ProblemPatternSchema.code', 'The match group index of the problem\'s code. Defaults to undefined')
1183 > },
1184 > message: {
1185 > type: 'integer',
1186 > description: localize('ProblemPatternSchema.message', 'The match group index of the message. If omitted it defaults to 4 if location is specified. Otherwise it defaults to 5.')
1187 > },
1188 > loop: {
1189 > type: 'boolean',
1190 > description: localize('ProblemPatternSchema.loop', 'In a multi line matcher loop indicated whether this pattern is executed in a loop as long as it matches. Can only specified on a last pattern in a multi line pattern.')
1191 > }
1192 > }
1193 > };
1194 >
1195 > export const NamedProblemPattern: IJSONSchema = Objects.deepClone(ProblemPattern);
1196 > NamedProblemPattern.properties = Objects.deepClone(NamedProblemPattern.properties) || {};
1197 > NamedProblemPattern.properties['name'] = {
1198 > type: 'string',
1199 > description: localize('NamedProblemPatternSchema.name', 'The name of the problem pattern.')
1200 > };
1201 >
1202 > export const MultiLineProblemPattern: IJSONSchema = {
1203 > type: 'array',
1204 > items: ProblemPattern
1205 > };
1206 >
1207 > export const NamedMultiLineProblemPattern: IJSONSchema = {
1208 > type: 'object',
1209 > additionalProperties: false,
1210 > properties: {
1211 > name: {
1212 > type: 'string',
1213 > description: localize('NamedMultiLineProblemPatternSchema.name', 'The name of the problem multi line problem pattern.')
1214 > },
1215 > patterns: {
1216 > type: 'array',
1217 > description: localize('NamedMultiLineProblemPatternSchema.patterns', 'The actual patterns.'),
1218 > items: ProblemPattern
1219 > }
1220 > }
1221 > };
1222 >
1223 > export const WatchingPattern: IJSONSchema = {
1224 > type: 'object',
1225 > additionalProperties: false,
1226 > properties: {
1227 > regexp: {
1228 > type: 'string',
1229 > description: localize('WatchingPatternSchema.regexp', 'The regular expression to detect the begin or end of a background task.')
1230 > },
1231 > file: {
1232 > type: 'integer',
1233 > description: localize('WatchingPatternSchema.file', 'The match group index of the filename. Can be omitted.')
1234 > },
1235 > }
1236 > };
1237 >
1238 > export const PatternType: IJSONSchema = {
1239 > anyOf: [
1240 > {
1241 > type: 'string',
1242 > description: localize('PatternTypeSchema.name', 'The name of a contributed or predefined pattern')
1243 > },
1244 > Schemas.ProblemPattern,
1245 > Schemas.MultiLineProblemPattern
1246 > ],
1247 > description: localize('PatternTypeSchema.description', 'A problem pattern or the name of a contributed or predefined problem pattern. Can be omitted if base is specified.')
1248 > };
1249 >
1250 > export const ProblemMatcher: IJSONSchema = {
1251 > type: 'object',
1252 > additionalProperties: false,
1253 > properties: {
1254 > base: {
1255 > type: 'string',
1256 > description: localize('ProblemMatcherSchema.base', 'The name of a base problem matcher to use.')
1257 > },
1258 > owner: {
1259 > type: 'string',
1260 > description: localize('ProblemMatcherSchema.owner', 'The owner of the problem inside Code. Can be omitted if base is specified. Defaults to \'external\' if omitted and base is not specified.')
1261 > },
1262 > source: {
1263 > type: 'string',
1264 > description: localize('ProblemMatcherSchema.source', 'A human-readable string describing the source of this diagnostic, e.g. \'typescript\' or \'super lint\'.')
1265 > },
1266 > severity: {
1267 > type: 'string',
1268 > enum: ['error', 'warning', 'info'],
1269 > description: localize('ProblemMatcherSchema.severity', 'The default severity for captures problems. Is used if the pattern doesn\'t define a match group for severity.')
1270 > },
1271 > applyTo: {
1272 > type: 'string',
1273 > enum: ['allDocuments', 'openDocuments', 'closedDocuments'],
1274 > description: localize('ProblemMatcherSchema.applyTo', 'Controls if a problem reported on a text document is applied only to open, closed or all documents.')
1275 > },
1276 > pattern: PatternType,
1277 > fileLocation: {
1278 > oneOf: [
1279 > {
1280 > type: 'string',
1281 > enum: ['absolute', 'relative', 'autoDetect', 'search']
1282 > },
1283 > {
1284 > type: 'array',
1285 > prefixItems: [
1286 > {
1287 > type: 'string',
1288 > enum: ['absolute', 'relative', 'autoDetect', 'search']
1289 > },
1290 > ],
1291 > minItems: 1,
1292 > maxItems: 1,
1293 > additionalItems: false
1294 > },
1295 > {
1296 > type: 'array',
1297 > prefixItems: [
1298 > { type: 'string', enum: ['relative', 'autoDetect'] },
1299 > { type: 'string' },
1300 > ],
1301 > minItems: 2,
1302 > maxItems: 2,
1303 > additionalItems: false,
1304 > examples: [
1305 > ['relative', '${workspaceFolder}'],
1306 > ['autoDetect', '${workspaceFolder}'],
1307 > ]
1308 > },
1309 > {
1310 > type: 'array',
1311 > prefixItems: [
1312 > { type: 'string', enum: ['search'] },
1313 > {
1314 > type: 'object',
1315 > properties: {
1316 > 'include': {
1317 > oneOf: [
1318 > { type: 'string' },
1319 > { type: 'array', items: { type: 'string' } }
1320 > ]
1321 > },
1322 > 'exclude': {
1323 > oneOf: [
1324 > { type: 'string' },
1325 > { type: 'array', items: { type: 'string' } }
1326 > ]
1327 > },
1328 > },
1329 > required: ['include']
1330 > }
1331 > ],
1332 > minItems: 2,
1333 > maxItems: 2,
1334 > additionalItems: false,
1335 > examples: [
1336 > ['search', { 'include': ['${workspaceFolder}'] }],
1337 > ['search', { 'include': ['${workspaceFolder}'], 'exclude': [] }]
1338 > ],
1339 > }
1340 > ],
1341 > description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted. A relative fileLocation may be an array, where the second element of the array is the path of the relative file location. The search fileLocation mode, performs a deep (and, possibly, heavy) file system search within the directories specified by the include/exclude properties of the second element (or the current workspace directory if not specified).')
1342 > },
1343 > background: {
1344 > type: 'object',
1345 > additionalProperties: false,
1346 > description: localize('ProblemMatcherSchema.background', 'Patterns to track the begin and end of a matcher active on a background task.'),
1347 > properties: {
1348 > activeOnStart: {
1349 > type: 'boolean',
1350 > description: localize('ProblemMatcherSchema.background.activeOnStart', 'If set to true the background monitor starts in active mode. This is the same as outputting a line that matches beginsPattern when the task starts.')
1351 > },
1352 > beginsPattern: {
1353 > oneOf: [
1354 > {
1355 > type: 'string'
1356 > },
1357 > Schemas.WatchingPattern
1358 > ],
1359 > description: localize('ProblemMatcherSchema.background.beginsPattern', 'If matched in the output the start of a background task is signaled.')
1360 > },
1361 > endsPattern: {
1362 > oneOf: [
1363 > {
1364 > type: 'string'
1365 > },
1366 > Schemas.WatchingPattern
1367 > ],
1368 > description: localize('ProblemMatcherSchema.background.endsPattern', 'If matched in the output the end of a background task is signaled.')
1369 > }
1370 > }
1371 > },
1372 > watching: {
1373 > type: 'object',
1374 > additionalProperties: false,
1375 > deprecationMessage: localize('ProblemMatcherSchema.watching.deprecated', 'The watching property is deprecated. Use background instead.'),
1376 > description: localize('ProblemMatcherSchema.watching', 'Patterns to track the begin and end of a watching matcher.'),
1377 > properties: {
1378 > activeOnStart: {
1379 > type: 'boolean',
1380 > description: localize('ProblemMatcherSchema.watching.activeOnStart', 'If set to true the watcher starts in active mode. This is the same as outputting a line that matches beginsPattern when the task starts.')
1381 > },
1382 > beginsPattern: {
1383 > oneOf: [
1384 > {
1385 > type: 'string'
1386 > },
1387 > Schemas.WatchingPattern
1388 > ],
1389 > description: localize('ProblemMatcherSchema.watching.beginsPattern', 'If matched in the output the start of a watching task is signaled.')
1390 > },
1391 > endsPattern: {
1392 > oneOf: [
1393 > {
1394 > type: 'string'
1395 > },
1396 > Schemas.WatchingPattern
1397 > ],
1398 > description: localize('ProblemMatcherSchema.watching.endsPattern', 'If matched in the output the end of a watching task is signaled.')
1399 > }
1400 > }
1401 > }
1402 > }
1403 > };
1404 >
1405 > export const LegacyProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher);
1406 > LegacyProblemMatcher.properties = Objects.deepClone(LegacyProblemMatcher.properties) || {};
1407 > LegacyProblemMatcher.properties['watchedTaskBeginsRegExp'] = {
1408 > type: 'string',
1409 > deprecationMessage: localize('LegacyProblemMatcherSchema.watchedBegin.deprecated', 'This property is deprecated. Use the watching property instead.'),
1410 > description: localize('LegacyProblemMatcherSchema.watchedBegin', 'A regular expression signaling that a watched tasks begins executing triggered through file watching.')
1411 > };
1412 > LegacyProblemMatcher.properties['watchedTaskEndsRegExp'] = {
1413 > type: 'string',
1414 > deprecationMessage: localize('LegacyProblemMatcherSchema.watchedEnd.deprecated', 'This property is deprecated. Use the watching property instead.'),
1415 > description: localize('LegacyProblemMatcherSchema.watchedEnd', 'A regular expression signaling that a watched tasks ends executing.')
1416 > };
1417 >
1418 > export const NamedProblemMatcher: IJSONSchema = Objects.deepClone(ProblemMatcher);
1419 > NamedProblemMatcher.properties = Objects.deepClone(NamedProblemMatcher.properties) || {};
1420 > NamedProblemMatcher.properties.name = {
1421 > type: 'string',
1422 > description: localize('NamedProblemMatcherSchema.name', 'The name of the problem matcher used to refer to it.')
1423 > };
1424 > NamedProblemMatcher.properties.label = {
1425 > type: 'string',
1426 > description: localize('NamedProblemMatcherSchema.label', 'A human readable label of the problem matcher.')
1427 > };
1428 > }
1429 >
1430 > const problemPatternExtPoint = ExtensionsRegistry.registerExtensionPoint<Config.NamedProblemPatterns>({
1431 > extensionPoint: 'problemPatterns',
1432 > jsonSchema: {
1433 > description: localize('ProblemPatternExtPoint', 'Contributes problem patterns'),
1434 > type: 'array',
1435 > items: {
1436 > anyOf: [
1437 > Schemas.NamedProblemPattern,
1438 > Schemas.NamedMultiLineProblemPattern
1439 > ]
1440 > }
1441 > }
1442 > });
1443 >
1444 > export interface IProblemPatternRegistry {
1445 > onReady(): Promise<void>;
1446 >
1447 > get(key: string): IProblemPattern | MultiLineProblemPattern;
1448 > }
1449 >
1450 > class ProblemPatternRegistryImpl implements IProblemPatternRegistry {
1451 >
1452 > private patterns: IStringDictionary<Types.SingleOrMany<IProblemPattern>>;
1453 > private readyPromise: Promise<void>;
1454 >
1455 > constructor() {
1456 > this.patterns = Object.create(null);
1457 > this.fillDefaults();
1458 > this.readyPromise = new Promise<void>((resolve, reject) => {
1459 > problemPatternExtPoint.setHandler((extensions, delta) => {
1460 // We get all statically know extension during startup in one batch
1461 try {
1462 delta.removed.forEach(extension => {
1463 const problemPatterns = extension.value as Config.NamedProblemPatterns;
1464 for (const pattern of problemPatterns) {
1465 if (this.patterns[pattern.name]) {
1466 delete this.patterns[pattern.name];
1467 }
1468 }
1469 });
1470 delta.added.forEach(extension => {
1471 const problemPatterns = extension.value as Config.NamedProblemPatterns;
1472 const parser = new ProblemPatternParser(new ExtensionRegistryReporter(extension.collector));
1473 for (const pattern of problemPatterns) {
1474 if (Config.NamedMultiLineCheckedProblemPattern.is(pattern)) {
1475 const result = parser.parse(pattern);
1476 if (parser.problemReporter.status.state < ValidationState.Error) {
1477 this.add(result.name, result.patterns);
1478 } else {
1479 extension.collector.error(localize('ProblemPatternRegistry.error', 'Invalid problem pattern. The pattern will be ignored.'));
1480 extension.collector.error(JSON.stringify(pattern, undefined, 4));
1481 }
1482 }
1483 else if (Config.NamedProblemPattern.is(pattern)) {
1484 const result = parser.parse(pattern);
1485 if (parser.problemReporter.status.state < ValidationState.Error) {
1486 this.add(pattern.name, result);
1487 } else {
1488 extension.collector.error(localize('ProblemPatternRegistry.error', 'Invalid problem pattern. The pattern will be ignored.'));
1489 extension.collector.error(JSON.stringify(pattern, undefined, 4));
1490 }
1491 }
1492 parser.reset();
1493 }
1494 });
1495 } catch (error) {
1496 // Do nothing
1497 }
1498 resolve(undefined);
1500 > });
1501 > }
1502 >
1503 > public onReady(): Promise<void> {
1504 return this.readyPromise;
1505 }
1507 > public add(key: string, value: Types.SingleOrMany<IProblemPattern>): void {
1508 > this.patterns[key] = value;
1509 > }
1510 >
1511 > public get(key: string): Types.SingleOrMany<IProblemPattern> {
1512 > return this.patterns[key];
1513 > }
1514 >
1515 > private fillDefaults(): void {
1516 > this.add('msCompile', {
1517 > regexp: /^\s*(?:\s*\d+>)?(\S.*?)(?:\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\))?\s*:\s+(?:(\S+)\s+)?((?:fatal +)?error|warning|info)\s+(\w+\d+)?\s*:\s*(.*)$/,
1518 > kind: ProblemLocationKind.Location,
1519 > file: 1,
1520 > location: 2,
1521 > severity: 4,
1522 > code: 5,
1523 > message: 6
1524 > });
1525 > this.add('gulp-tsc', {
1526 > regexp: /^([^\s].*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(\d+)\s+(.*)$/,
1527 > kind: ProblemLocationKind.Location,
1528 > file: 1,
1529 > location: 2,
1530 > code: 3,
1531 > message: 4
1532 > });
1533 > this.add('cpp', {
1534 > regexp: /^(\S.*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(C\d+)\s*:\s*(.*)$/,
1535 > kind: ProblemLocationKind.Location,
1536 > file: 1,
1537 > location: 2,
1538 > severity: 3,
1539 > code: 4,
1540 > message: 5
1541 > });
1542 > this.add('csc', {
1543 > regexp: /^(\S.*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(CS\d+)\s*:\s*(.*)$/,
1544 > kind: ProblemLocationKind.Location,
1545 > file: 1,
1546 > location: 2,
1547 > severity: 3,
1548 > code: 4,
1549 > message: 5
1550 > });
1551 > this.add('vb', {
1552 > regexp: /^(\S.*)\((\d+|\d+,\d+|\d+,\d+,\d+,\d+)\):\s+(error|warning|info)\s+(BC\d+)\s*:\s*(.*)$/,
1553 > kind: ProblemLocationKind.Location,
1554 > file: 1,
1555 > location: 2,
1556 > severity: 3,
1557 > code: 4,
1558 > message: 5
1559 > });
1560 > this.add('lessCompile', {
1561 > regexp: /^\s*(.*) in file (.*) line no. (\d+)$/,
1562 > kind: ProblemLocationKind.Location,
1563 > message: 1,
1564 > file: 2,
1565 > line: 3
1566 > });
1567 > this.add('jshint', {
1568 > regexp: /^(.*):\s+line\s+(\d+),\s+col\s+(\d+),\s(.+?)(?:\s+\((\w)(\d+)\))?$/,
1569 > kind: ProblemLocationKind.Location,
1570 > file: 1,
1571 > line: 2,
1572 > character: 3,
1573 > message: 4,
1574 > severity: 5,
1575 > code: 6
1576 > });
1577 > this.add('jshint-stylish', [
1578 > {
1579 > regexp: /^(.+)$/,
1580 > kind: ProblemLocationKind.Location,
1581 > file: 1
1582 > },
1583 > {
1584 > regexp: /^\s+line\s+(\d+)\s+col\s+(\d+)\s+(.+?)(?:\s+\((\w)(\d+)\))?$/,
1585 > line: 1,
1586 > character: 2,
1587 > message: 3,
1588 > severity: 4,
1589 > code: 5,
1590 > loop: true
1591 > }
1592 > ]);
1593 > this.add('eslint-compact', {
1594 > regexp: /^(.+):\sline\s(\d+),\scol\s(\d+),\s(Error|Warning|Info)\s-\s(.+)\s\((.+)\)$/,
1595 > file: 1,
1596 > kind: ProblemLocationKind.Location,
1597 > line: 2,
1598 > character: 3,
1599 > severity: 4,
1600 > message: 5,
1601 > code: 6
1602 > });
1603 > this.add('eslint-stylish', [
1604 > {
1605 > regexp: /^((?:[a-zA-Z]:)*[./\\]+.*?)$/,
1606 > kind: ProblemLocationKind.Location,
1607 > file: 1
1608 > },
1609 > {
1610 > regexp: /^\s+(\d+):(\d+)\s+(error|warning|info)\s+(.+?)(?:\s\s+(.*))?$/,
1611 > line: 1,
1612 > character: 2,
1613 > severity: 3,
1614 > message: 4,
1615 > code: 5,
1616 > loop: true
1617 > }
1618 > ]);
1619 > this.add('go', {
1620 > regexp: /^([^:]*: )?((.:)?[^:]*):(\d+)(:(\d+))?: (.*)$/,
1621 > kind: ProblemLocationKind.Location,
1622 > file: 2,
1623 > line: 4,
1624 > character: 6,
1625 > message: 7
1626 > });
1627 > }
1628 > }
1629 >
1630 > export const ProblemPatternRegistry: IProblemPatternRegistry = new ProblemPatternRegistryImpl();
1631 >
1632 > export class ProblemMatcherParser extends Parser {
1633 >
1634 > constructor(logger: IProblemReporter) {
1635 > super(logger); problemMatcher.ts ×30
1636 > }
1638 > public parse(json: Config.ProblemMatcher): ProblemMatcher | undefined {
1639 > const result = this.createProblemMatcher(json); problemMatcher.ts ×30
1640 > if (!this.checkProblemMatcherValid(json, result)) {
1641 return undefined;
1642 }
1643 > this.addWatchingMatcher(json, result); problemMatcher.ts ×30
1644 >
1645 > return result;
1646 > }
1648 > private checkProblemMatcherValid(externalProblemMatcher: Config.ProblemMatcher, problemMatcher: ProblemMatcher | null): problemMatcher is ProblemMatcher {
1649 > if (!problemMatcher) { problemMatcher.ts ×30
1650 this.error(localize('ProblemMatcherParser.noProblemMatcher', 'Error: the description can\'t be converted into a problem matcher:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4)));
1651 return false;
1652 }
1653 > if (!problemMatcher.pattern) { problemMatcher.ts ×30
1654 this.error(localize('ProblemMatcherParser.noProblemPattern', 'Error: the description doesn\'t define a valid problem pattern:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4)));
1655 return false;
1656 }
1657 > if (!problemMatcher.owner) { problemMatcher.ts ×30
1658 this.error(localize('ProblemMatcherParser.noOwner', 'Error: the description doesn\'t define an owner:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4)));
1659 return false;
1660 }
1661 > if (Types.isUndefined(problemMatcher.fileLocation)) { problemMatcher.ts ×30
1662 this.error(localize('ProblemMatcherParser.noFileLocation', 'Error: the description doesn\'t define a file location:\n{0}\n', JSON.stringify(externalProblemMatcher, null, 4)));
1663 return false;
1664 }
1665 > return true; problemMatcher.ts ×30
1666 > }
1668 > private createProblemMatcher(description: Config.ProblemMatcher): ProblemMatcher | null {
1669 > let result: ProblemMatcher | null = null; problemMatcher.ts ×30
1670 >
1671 > const owner = Types.isString(description.owner) ? description.owner : UUID.generateUuid();
1672 > const source = Types.isString(description.source) ? description.source : undefined;
1673 > let applyTo = Types.isString(description.applyTo) ? ApplyToKind.fromString(description.applyTo) : ApplyToKind.allDocuments;
1674 > if (!applyTo) {
1675 > applyTo = ApplyToKind.allDocuments; problemMatcher.ts ×2
1676 > }
1677 > let fileLocation: FileLocationKind | undefined = undefined; problemMatcher.ts ×30
1678 > let filePrefix: string | Config.SearchFileLocationArgs | undefined = undefined;
1679 >
1680 > let kind: FileLocationKind | undefined;
1681 > if (Types.isUndefined(description.fileLocation)) {
1682 > fileLocation = FileLocationKind.Relative; problemMatcher.ts ×1
1683 > filePrefix = '${workspaceFolder}';
1684 > } else if (Types.isString(description.fileLocation)) { problemMatcher.ts ×30
1685 > kind = FileLocationKind.fromString(<string>description.fileLocation); problemMatcher.ts ×9
1686 > if (kind) {
1687 > fileLocation = kind;
1688 > if ((kind === FileLocationKind.Relative) || (kind === FileLocationKind.AutoDetect)) {
1689 filePrefix = '${workspaceFolder}';
1690 > } else if (kind === FileLocationKind.Search) { problemMatcher.ts ×9
1691 filePrefix = { include: ['${workspaceFolder}'] };
1692 }
1694 > } else if (Types.isStringArray(description.fileLocation)) { problemMatcher.ts ×4
1695 > const values = <string[]>description.fileLocation; problemMatcher.ts ×3
1696 > if (values.length > 0) {
1697 > kind = FileLocationKind.fromString(values[0]);
1698 > if (values.length === 1 && kind === FileLocationKind.Absolute) {
1699 fileLocation = kind;
1700 > } else if (values.length === 2 && (kind === FileLocationKind.Relative || kind === FileLocationKind.AutoDetect) && values[1]) { problemMatcher.ts ×3
1701 > fileLocation = kind;
1702 > filePrefix = values[1];
1703 > }
1704 > }
1705 > } else if (Array.isArray(description.fileLocation)) {
1706 const kind = FileLocationKind.fromString(description.fileLocation[0]);
1707 if (kind === FileLocationKind.Search) {
1708 fileLocation = FileLocationKind.Search;
1709 filePrefix = description.fileLocation[1] ?? { include: ['${workspaceFolder}'] };
1710 }
1711 }
1713 > const pattern = description.pattern ? this.createProblemPattern(description.pattern) : undefined;
1714 >
1715 > let severity = description.severity ? Severity.fromValue(description.severity) : undefined;
1716 > if (severity === Severity.Ignore) {
1717 this.info(localize('ProblemMatcherParser.unknownSeverity', 'Info: unknown severity {0}. Valid values are error, warning and info.\n', description.severity));
1718 severity = Severity.Error;
1719 }
1721 > if (Types.isString(description.base)) {
1722 const variableName = <string>description.base;
1723 if (variableName.length > 1 && variableName[0] === '$') {
1724 const base = ProblemMatcherRegistry.get(variableName.substring(1));
1725 if (base) {
1726 result = Objects.deepClone(base);
1727 if (description.owner !== undefined && owner !== undefined) {
1728 result.owner = owner;
1729 }
1730 if (description.source !== undefined && source !== undefined) {
1731 result.source = source;
1732 }
1733 if (description.fileLocation !== undefined && fileLocation !== undefined) {
1734 result.fileLocation = fileLocation;
1735 result.filePrefix = filePrefix;
1736 }
1737 if (description.pattern !== undefined && pattern !== undefined && pattern !== null) {
1738 result.pattern = pattern;
1739 }
1740 if (description.severity !== undefined && severity !== undefined) {
1741 result.severity = severity;
1742 }
1743 if (description.applyTo !== undefined && applyTo !== undefined) {
1744 result.applyTo = applyTo;
1745 }
1746 }
1747 }
1748 > } else if (fileLocation && pattern) { problemMatcher.ts ×30
1749 > result = {
1750 > owner: owner,
1751 > applyTo: applyTo,
1752 > fileLocation: fileLocation,
1753 > pattern: pattern,
1754 > };
1755 > if (source) {
1756 result.source = source;
1757 }
1758 > if (filePrefix) { problemMatcher.ts ×30
1759 > result.filePrefix = filePrefix; problemMatcher.ts ×2
1760 > }
1761 > if (severity) { problemMatcher.ts ×30
1762 > result.severity = severity; problemMatcher.ts ×9
1763 > }
1765 > if (Config.isNamedProblemMatcher(description)) {
1766 (result as INamedProblemMatcher).name = description.name;
1767 (result as INamedProblemMatcher).label = Types.isString(description.label) ? description.label : description.name;
1768 }
1769 > return result; problemMatcher.ts ×30
1770 > }
1772 > private createProblemPattern(value: string | Config.IProblemPattern | Config.MultiLineProblemPattern): Types.SingleOrMany<IProblemPattern> | null {
1773 > if (Types.isString(value)) { problemMatcher.ts ×30
1774 const variableName: string = <string>value;
1775 if (variableName.length > 1 && variableName[0] === '$') {
1776 const result = ProblemPatternRegistry.get(variableName.substring(1));
1777 if (!result) {
1778 this.error(localize('ProblemMatcherParser.noDefinedPatter', 'Error: the pattern with the identifier {0} doesn\'t exist.', variableName));
1779 }
1780 return result;
1781 } else {
1782 if (variableName.length === 0) {
1783 this.error(localize('ProblemMatcherParser.noIdentifier', 'Error: the pattern property refers to an empty identifier.'));
1784 } else {
1785 this.error(localize('ProblemMatcherParser.noValidIdentifier', 'Error: the pattern property {0} is not a valid pattern variable name.', variableName));
1786 }
1787 }
1788 > } else if (value) { problemMatcher.ts ×30
1789 > const problemPatternParser = new ProblemPatternParser(this.problemReporter);
1790 > if (Array.isArray(value)) {
1791 return problemPatternParser.parse(value);
1792 > } else { problemMatcher.ts ×30
1793 > return problemPatternParser.parse(value);
1794 > }
1795 > }
1796 return null;
1799 > private addWatchingMatcher(external: Config.ProblemMatcher, internal: ProblemMatcher): void {
1800 > const oldBegins = this.createRegularExpression(external.watchedTaskBeginsRegExp); problemMatcher.ts ×30
1801 > const oldEnds = this.createRegularExpression(external.watchedTaskEndsRegExp);
1802 > if (oldBegins && oldEnds) {
1803 internal.watching = {
1804 activeOnStart: false,
1805 beginsPattern: { regexp: oldBegins },
1806 endsPattern: { regexp: oldEnds }
1807 };
1808 return;
1809 }
1810 > const backgroundMonitor = external.background || external.watching; problemMatcher.ts ×30
1811 > if (Types.isUndefinedOrNull(backgroundMonitor)) {
1812 > return;
1813 > }
1814 const begins: IWatchingPattern | null = this.createWatchingPattern(backgroundMonitor.beginsPattern);
1815 const ends: IWatchingPattern | null = this.createWatchingPattern(backgroundMonitor.endsPattern);
1816 > if (begins && ends) { problemMatcher.ts ×30
1817 internal.watching = {
1818 activeOnStart: Types.isBoolean(backgroundMonitor.activeOnStart) ? backgroundMonitor.activeOnStart : false,
1819 beginsPattern: begins,
1820 endsPattern: ends
1821 };
1822 return;
1823 }
1824 > if (begins || ends) { problemMatcher.ts ×30
1825 this.error(localize('ProblemMatcherParser.problemPattern.watchingMatcher', 'A problem matcher must define both a begin pattern and an end pattern for watching.'));
1826 }
1829 > private createWatchingPattern(external: string | Config.IWatchingPattern | undefined): IWatchingPattern | null {
1830 if (Types.isUndefinedOrNull(external)) {
1831 return null;
1832 }
1833 let regexp: RegExp | null;
1834 let file: number | undefined;
1835 if (Types.isString(external)) {
1836 regexp = this.createRegularExpression(external);
1837 } else {
1838 regexp = this.createRegularExpression(external.regexp);
1839 if (Types.isNumber(external.file)) {
1840 file = external.file;
1841 }
1842 }
1843 if (!regexp) {
1844 return null;
1845 }
1846 return file ? { regexp, file } : { regexp, file: 1 };
1847 }
1849 > private createRegularExpression(value: string | undefined): RegExp | null {
1850 > let result: RegExp | null = null; problemMatcher.ts ×30
1851 > if (!value) {
1852 > return result;
1853 > }
1854 try {
1855 result = new RegExp(value);
1856 } catch (err) {
1857 this.error(localize('ProblemMatcherParser.invalidRegexp', 'Error: The string {0} is not a valid regular expression.\n', value));
1858 }
1859 return result;
1862 >
1863 > const problemMatchersExtPoint = ExtensionsRegistry.registerExtensionPoint<Config.INamedProblemMatcher[]>({
1864 > extensionPoint: 'problemMatchers',
1865 > deps: [problemPatternExtPoint],
1866 > jsonSchema: {
1867 > description: localize('ProblemMatcherExtPoint', 'Contributes problem matchers'),
1868 > type: 'array',
1869 > items: Schemas.NamedProblemMatcher
1870 > }
1871 > });
1872 >
1873 > export interface IProblemMatcherRegistry {
1874 > onReady(): Promise<void>;
1875 > get(name: string): INamedProblemMatcher;
1876 > keys(): string[];
1877 > readonly onMatcherChanged: Event<void>;
1878 > }
1879 >
1880 > class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {
1881 >
1882 > private matchers: IStringDictionary<INamedProblemMatcher>;
1883 > private readyPromise: Promise<void>;
1884 > private readonly _onMatchersChanged: Emitter<void> = new Emitter<void>();
1885 > public readonly onMatcherChanged: Event<void> = this._onMatchersChanged.event;
1886 >
1887 >
1888 > constructor() {
1889 > this.matchers = Object.create(null);
1890 > this.fillDefaults();
1891 > this.readyPromise = new Promise<void>((resolve, reject) => {
1892 > problemMatchersExtPoint.setHandler((extensions, delta) => {
1893 try {
1894 delta.removed.forEach(extension => {
1895 const problemMatchers = extension.value;
1896 for (const matcher of problemMatchers) {
1897 if (this.matchers[matcher.name]) {
1898 delete this.matchers[matcher.name];
1899 }
1900 }
1901 });
1902 delta.added.forEach(extension => {
1903 const problemMatchers = extension.value;
1904 const parser = new ProblemMatcherParser(new ExtensionRegistryReporter(extension.collector));
1905 for (const matcher of problemMatchers) {
1906 const result = parser.parse(matcher);
1907 if (result && isNamedProblemMatcher(result)) {
1908 this.add(result);
1909 }
1910 }
1911 });
1912 if ((delta.removed.length > 0) || (delta.added.length > 0)) {
1913 this._onMatchersChanged.fire();
1914 }
1915 } catch (error) {
1916 }
1917 const matcher = this.get('tsc-watch');
1918 if (matcher) {
1919 (matcher as unknown as Record<string, unknown>).tscWatch = true;
1920 }
1921 resolve(undefined);
1923 > });
1924 > }
1925 >
1926 > public onReady(): Promise<void> {
1927 ProblemPatternRegistry.onReady();
1928 return this.readyPromise;
1929 }
1931 > public add(matcher: INamedProblemMatcher): void {
1932 > this.matchers[matcher.name] = matcher;
1933 > }
1934 >
1935 > public get(name: string): INamedProblemMatcher {
1936 > return this.matchers[name]; taskConfiguration.ts ×2
1937 > }
1939 > public keys(): string[] {
1940 return Object.keys(this.matchers);
1941 }
1943 > private fillDefaults(): void {
1944 > this.add({
1945 > name: 'msCompile',
1946 > label: localize('msCompile', 'Microsoft compiler problems'),
1947 > owner: 'msCompile',
1948 > source: 'cpp',
1949 > applyTo: ApplyToKind.allDocuments,
1950 > fileLocation: FileLocationKind.Absolute,
1951 > pattern: ProblemPatternRegistry.get('msCompile')
1952 > });
1953 >
1954 > this.add({
1955 > name: 'lessCompile',
1956 > label: localize('lessCompile', 'Less problems'),
1957 > deprecated: true,
1958 > owner: 'lessCompile',
1959 > source: 'less',
1960 > applyTo: ApplyToKind.allDocuments,
1961 > fileLocation: FileLocationKind.Absolute,
1962 > pattern: ProblemPatternRegistry.get('lessCompile'),
1963 > severity: Severity.Error
1964 > });
1965 >
1966 > this.add({
1967 > name: 'gulp-tsc',
1968 > label: localize('gulp-tsc', 'Gulp TSC Problems'),
1969 > owner: 'typescript',
1970 > source: 'ts',
1971 > applyTo: ApplyToKind.closedDocuments,
1972 > fileLocation: FileLocationKind.Relative,
1973 > filePrefix: '${workspaceFolder}',
1974 > pattern: ProblemPatternRegistry.get('gulp-tsc')
1975 > });
1976 >
1977 > this.add({
1978 > name: 'jshint',
1979 > label: localize('jshint', 'JSHint problems'),
1980 > owner: 'jshint',
1981 > source: 'jshint',
1982 > applyTo: ApplyToKind.allDocuments,
1983 > fileLocation: FileLocationKind.Absolute,
1984 > pattern: ProblemPatternRegistry.get('jshint')
1985 > });
1986 >
1987 > this.add({
1988 > name: 'jshint-stylish',
1989 > label: localize('jshint-stylish', 'JSHint stylish problems'),
1990 > owner: 'jshint',
1991 > source: 'jshint',
1992 > applyTo: ApplyToKind.allDocuments,
1993 > fileLocation: FileLocationKind.Absolute,
1994 > pattern: ProblemPatternRegistry.get('jshint-stylish')
1995 > });
1996 >
1997 > this.add({
1998 > name: 'eslint-compact',
1999 > label: localize('eslint-compact', 'ESLint compact problems'),
2000 > owner: 'eslint',
2001 > source: 'eslint',
2002 > applyTo: ApplyToKind.allDocuments,
2003 > fileLocation: FileLocationKind.Absolute,
2004 > filePrefix: '${workspaceFolder}',
2005 > pattern: ProblemPatternRegistry.get('eslint-compact')
2006 > });
2007 >
2008 > this.add({
2009 > name: 'eslint-stylish',
2010 > label: localize('eslint-stylish', 'ESLint stylish problems'),
2011 > owner: 'eslint',
2012 > source: 'eslint',
2013 > applyTo: ApplyToKind.allDocuments,
2014 > fileLocation: FileLocationKind.Absolute,
2015 > pattern: ProblemPatternRegistry.get('eslint-stylish')
2016 > });
2017 >
2018 > this.add({
2019 > name: 'go',
2020 > label: localize('go', 'Go problems'),
2021 > owner: 'go',
2022 > source: 'go',
2023 > applyTo: ApplyToKind.allDocuments,
2024 > fileLocation: FileLocationKind.Relative,
2025 > filePrefix: '${workspaceFolder}',
2026 > pattern: ProblemPatternRegistry.get('go')
2027 > });
2028 > }
2029 > }
2030 >
2031 > export const ProblemMatcherRegistry: IProblemMatcherRegistry = new ProblemMatcherRegistryImpl();