@opensumi/ide-task 2.21.13 → 2.22.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/lib/browser/index.js +2 -2
- package/lib/browser/index.js.map +1 -1
- package/lib/browser/parser.js +4 -4
- package/lib/browser/parser.js.map +1 -1
- package/lib/browser/task-config.d.ts +4 -4
- package/lib/browser/task-config.d.ts.map +1 -1
- package/lib/browser/task-config.js +31 -31
- package/lib/browser/task-config.js.map +1 -1
- package/lib/browser/task-executor.d.ts.map +1 -1
- package/lib/browser/task-executor.js +16 -22
- package/lib/browser/task-executor.js.map +1 -1
- package/lib/browser/task-preferences.contribution.js.map +1 -1
- package/lib/browser/task-preferences.provider.d.ts +2 -2
- package/lib/browser/task-preferences.provider.d.ts.map +1 -1
- package/lib/browser/task-preferences.provider.js +2 -2
- package/lib/browser/task-preferences.provider.js.map +1 -1
- package/lib/browser/task.contribution.js.map +1 -1
- package/lib/browser/task.schema.d.ts +2 -2
- package/lib/browser/task.schema.d.ts.map +1 -1
- package/lib/browser/task.schema.js +5 -1
- package/lib/browser/task.schema.js.map +1 -1
- package/lib/browser/task.service.d.ts +3 -1
- package/lib/browser/task.service.d.ts.map +1 -1
- package/lib/browser/task.service.js +10 -5
- package/lib/browser/task.service.js.map +1 -1
- package/lib/browser/terminal-task-system.js +11 -11
- package/lib/browser/terminal-task-system.js.map +1 -1
- package/lib/common/index.d.ts +1 -1
- package/lib/common/index.d.ts.map +1 -1
- package/lib/common/task.d.ts +4 -4
- package/lib/common/task.d.ts.map +1 -1
- package/lib/common/task.js +7 -7
- package/lib/common/task.js.map +1 -1
- package/package.json +15 -14
- package/src/browser/index.ts +33 -0
- package/src/browser/parser.ts +944 -0
- package/src/browser/problem-collector.ts +71 -0
- package/src/browser/problem-line-matcher.ts +461 -0
- package/src/browser/task-config.ts +2302 -0
- package/src/browser/task-executor.ts +296 -0
- package/src/browser/task-preferences.contribution.ts +9 -0
- package/src/browser/task-preferences.provider.ts +23 -0
- package/src/browser/task-preferences.ts +14 -0
- package/src/browser/task.contribution.ts +70 -0
- package/src/browser/task.schema.ts +368 -0
- package/src/browser/task.service.ts +504 -0
- package/src/browser/terminal-task-system.ts +340 -0
- package/src/common/index.ts +165 -0
- package/src/common/task.ts +1174 -0
- package/src/index.ts +1 -0
|
@@ -0,0 +1,944 @@
|
|
|
1
|
+
/* ---------------------------------------------------------------------------------------------
|
|
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 {
|
|
7
|
+
ProblemMatcher,
|
|
8
|
+
ApplyToKind,
|
|
9
|
+
uuid,
|
|
10
|
+
isString,
|
|
11
|
+
FileLocationKind,
|
|
12
|
+
Severity,
|
|
13
|
+
localize,
|
|
14
|
+
NamedProblemMatcher,
|
|
15
|
+
ProblemPattern,
|
|
16
|
+
WatchingPattern,
|
|
17
|
+
isBoolean,
|
|
18
|
+
isStringArray,
|
|
19
|
+
isUndefined,
|
|
20
|
+
MultiLineProblemPattern,
|
|
21
|
+
ProblemLocationKind,
|
|
22
|
+
NamedProblemPattern,
|
|
23
|
+
NamedMultiLineProblemPattern,
|
|
24
|
+
isArray,
|
|
25
|
+
mixin,
|
|
26
|
+
isUndefinedOrNull,
|
|
27
|
+
isNumber,
|
|
28
|
+
objects,
|
|
29
|
+
} from '@opensumi/ide-core-common';
|
|
30
|
+
|
|
31
|
+
import { IProblemReporter, getProblemPatternFn, getProblemMatcherFn } from './task-config';
|
|
32
|
+
|
|
33
|
+
const { deepClone } = objects;
|
|
34
|
+
|
|
35
|
+
export const enum ValidationState {
|
|
36
|
+
OK = 0,
|
|
37
|
+
Info = 1,
|
|
38
|
+
Warning = 2,
|
|
39
|
+
Error = 3,
|
|
40
|
+
Fatal = 4,
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export class ValidationStatus {
|
|
44
|
+
private _state: ValidationState;
|
|
45
|
+
|
|
46
|
+
constructor() {
|
|
47
|
+
this._state = ValidationState.OK;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
public get state(): ValidationState {
|
|
51
|
+
return this._state;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
public set state(value: ValidationState) {
|
|
55
|
+
if (value > this._state) {
|
|
56
|
+
this._state = value;
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
public isOK(): boolean {
|
|
61
|
+
return this._state === ValidationState.OK;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
public isFatal(): boolean {
|
|
65
|
+
return this._state === ValidationState.Fatal;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface IProblemReporterBase {
|
|
70
|
+
info(message: string): void;
|
|
71
|
+
warn(message: string): void;
|
|
72
|
+
error(message: string): void;
|
|
73
|
+
fatal(message: string): void;
|
|
74
|
+
status: ValidationStatus;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export abstract class Parser {
|
|
78
|
+
private _problemReporter: IProblemReporterBase;
|
|
79
|
+
|
|
80
|
+
constructor(problemReporter: IProblemReporterBase) {
|
|
81
|
+
this._problemReporter = problemReporter;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
public reset(): void {
|
|
85
|
+
this._problemReporter.status.state = ValidationState.OK;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
public get problemReporter(): IProblemReporterBase {
|
|
89
|
+
return this._problemReporter;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
public info(message: string): void {
|
|
93
|
+
this._problemReporter.info(message);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
public warn(message: string): void {
|
|
97
|
+
this._problemReporter.warn(message);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
public error(message: string): void {
|
|
101
|
+
this._problemReporter.error(message);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
public fatal(message: string): void {
|
|
105
|
+
this._problemReporter.fatal(message);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export namespace Config {
|
|
110
|
+
export interface ProblemPattern {
|
|
111
|
+
/**
|
|
112
|
+
* The regular expression to find a problem in the console output of an
|
|
113
|
+
* executed task.
|
|
114
|
+
*/
|
|
115
|
+
regexp?: string;
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Whether the pattern matches a whole file, or a location (file/line)
|
|
119
|
+
*
|
|
120
|
+
* The default is to match for a location. Only valid on the
|
|
121
|
+
* first problem pattern in a multi line problem matcher.
|
|
122
|
+
*/
|
|
123
|
+
kind?: string;
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* The match group index of the filename.
|
|
127
|
+
* If omitted 1 is used.
|
|
128
|
+
*/
|
|
129
|
+
file?: number;
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* The match group index of the problem's location. Valid location
|
|
133
|
+
* patterns are: (line), (line,column) and (startLine,startColumn,endLine,endColumn).
|
|
134
|
+
* If omitted the line and column properties are used.
|
|
135
|
+
*/
|
|
136
|
+
location?: number;
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* The match group index of the problem's line in the source file.
|
|
140
|
+
*
|
|
141
|
+
* Defaults to 2.
|
|
142
|
+
*/
|
|
143
|
+
line?: number;
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* The match group index of the problem's column in the source file.
|
|
147
|
+
*
|
|
148
|
+
* Defaults to 3.
|
|
149
|
+
*/
|
|
150
|
+
column?: number;
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* The match group index of the problem's end line in the source file.
|
|
154
|
+
*
|
|
155
|
+
* Defaults to undefined. No end line is captured.
|
|
156
|
+
*/
|
|
157
|
+
endLine?: number;
|
|
158
|
+
|
|
159
|
+
/**
|
|
160
|
+
* The match group index of the problem's end column in the source file.
|
|
161
|
+
*
|
|
162
|
+
* Defaults to undefined. No end column is captured.
|
|
163
|
+
*/
|
|
164
|
+
endColumn?: number;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* The match group index of the problem's severity.
|
|
168
|
+
*
|
|
169
|
+
* Defaults to undefined. In this case the problem matcher's severity
|
|
170
|
+
* is used.
|
|
171
|
+
*/
|
|
172
|
+
severity?: number;
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* The match group index of the problem's code.
|
|
176
|
+
*
|
|
177
|
+
* Defaults to undefined. No code is captured.
|
|
178
|
+
*/
|
|
179
|
+
code?: number;
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* The match group index of the message. If omitted it defaults
|
|
183
|
+
* to 4 if location is specified. Otherwise it defaults to 5.
|
|
184
|
+
*/
|
|
185
|
+
message?: number;
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* Specifies if the last pattern in a multi line problem matcher should
|
|
189
|
+
* loop as long as it does match a line consequently. Only valid on the
|
|
190
|
+
* last problem pattern in a multi line problem matcher.
|
|
191
|
+
*/
|
|
192
|
+
loop?: boolean;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
export interface CheckedProblemPattern extends ProblemPattern {
|
|
196
|
+
/**
|
|
197
|
+
* The regular expression to find a problem in the console output of an
|
|
198
|
+
* executed task.
|
|
199
|
+
*/
|
|
200
|
+
regexp: string;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export namespace CheckedProblemPattern {
|
|
204
|
+
export function is(value: any): value is CheckedProblemPattern {
|
|
205
|
+
const candidate: ProblemPattern = value as ProblemPattern;
|
|
206
|
+
return candidate && isString(candidate.regexp);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export interface NamedProblemPattern extends ProblemPattern {
|
|
211
|
+
/**
|
|
212
|
+
* The name of the problem pattern.
|
|
213
|
+
*/
|
|
214
|
+
name: string;
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* A human readable label
|
|
218
|
+
*/
|
|
219
|
+
label?: string;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
export namespace NamedProblemPattern {
|
|
223
|
+
export function is(value: any): value is NamedProblemPattern {
|
|
224
|
+
const candidate: NamedProblemPattern = value as NamedProblemPattern;
|
|
225
|
+
return candidate && isString(candidate.name);
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
export interface NamedCheckedProblemPattern extends NamedProblemPattern {
|
|
230
|
+
/**
|
|
231
|
+
* The regular expression to find a problem in the console output of an
|
|
232
|
+
* executed task.
|
|
233
|
+
*/
|
|
234
|
+
regexp: string;
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
export namespace NamedCheckedProblemPattern {
|
|
238
|
+
export function is(value: any): value is NamedCheckedProblemPattern {
|
|
239
|
+
const candidate: NamedProblemPattern = value as NamedProblemPattern;
|
|
240
|
+
return candidate && NamedProblemPattern.is(candidate) && isString(candidate.regexp);
|
|
241
|
+
}
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
export type MultiLineProblemPattern = ProblemPattern[];
|
|
245
|
+
|
|
246
|
+
export namespace MultiLineProblemPattern {
|
|
247
|
+
export function is(value: any): value is MultiLineProblemPattern {
|
|
248
|
+
return value && isArray(value);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
export type MultiLineCheckedProblemPattern = CheckedProblemPattern[];
|
|
253
|
+
|
|
254
|
+
export namespace MultiLineCheckedProblemPattern {
|
|
255
|
+
export function is(value: any): value is MultiLineCheckedProblemPattern {
|
|
256
|
+
if (!MultiLineProblemPattern.is(value)) {
|
|
257
|
+
return false;
|
|
258
|
+
}
|
|
259
|
+
for (const element of value) {
|
|
260
|
+
if (!Config.CheckedProblemPattern.is(element)) {
|
|
261
|
+
return false;
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return true;
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
export interface NamedMultiLineCheckedProblemPattern {
|
|
269
|
+
/**
|
|
270
|
+
* The name of the problem pattern.
|
|
271
|
+
*/
|
|
272
|
+
name: string;
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* A human readable label
|
|
276
|
+
*/
|
|
277
|
+
label?: string;
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* The actual patterns
|
|
281
|
+
*/
|
|
282
|
+
patterns: MultiLineCheckedProblemPattern;
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
export namespace NamedMultiLineCheckedProblemPattern {
|
|
286
|
+
export function is(value: any): value is NamedMultiLineCheckedProblemPattern {
|
|
287
|
+
const candidate = value as NamedMultiLineCheckedProblemPattern;
|
|
288
|
+
return (
|
|
289
|
+
candidate &&
|
|
290
|
+
isString(candidate.name) &&
|
|
291
|
+
isArray(candidate.patterns) &&
|
|
292
|
+
MultiLineCheckedProblemPattern.is(candidate.patterns)
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export type NamedProblemPatterns = (Config.NamedProblemPattern | Config.NamedMultiLineCheckedProblemPattern)[];
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* A watching pattern
|
|
301
|
+
*/
|
|
302
|
+
export interface WatchingPattern {
|
|
303
|
+
/**
|
|
304
|
+
* The actual regular expression
|
|
305
|
+
*/
|
|
306
|
+
regexp?: string;
|
|
307
|
+
|
|
308
|
+
/**
|
|
309
|
+
* The match group index of the filename. If provided the expression
|
|
310
|
+
* is matched for that file only.
|
|
311
|
+
*/
|
|
312
|
+
file?: number;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* A description to track the start and end of a watching task.
|
|
317
|
+
*/
|
|
318
|
+
export interface BackgroundMonitor {
|
|
319
|
+
/**
|
|
320
|
+
* If set to true the watcher is in active mode when the task
|
|
321
|
+
* starts. This is equals of issuing a line that matches the
|
|
322
|
+
* beginsPattern.
|
|
323
|
+
*/
|
|
324
|
+
activeOnStart?: boolean;
|
|
325
|
+
|
|
326
|
+
/**
|
|
327
|
+
* If matched in the output the start of a watching task is signaled.
|
|
328
|
+
*/
|
|
329
|
+
beginsPattern?: string | WatchingPattern;
|
|
330
|
+
|
|
331
|
+
/**
|
|
332
|
+
* If matched in the output the end of a watching task is signaled.
|
|
333
|
+
*/
|
|
334
|
+
endsPattern?: string | WatchingPattern;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
/**
|
|
338
|
+
* A description of a problem matcher that detects problems
|
|
339
|
+
* in build output.
|
|
340
|
+
*/
|
|
341
|
+
export interface ProblemMatcher {
|
|
342
|
+
/**
|
|
343
|
+
* The name of a base problem matcher to use. If specified the
|
|
344
|
+
* base problem matcher will be used as a template and properties
|
|
345
|
+
* specified here will replace properties of the base problem
|
|
346
|
+
* matcher
|
|
347
|
+
*/
|
|
348
|
+
base?: string;
|
|
349
|
+
|
|
350
|
+
/**
|
|
351
|
+
* The owner of the produced VSCode problem. This is typically
|
|
352
|
+
* the identifier of a VSCode language service if the problems are
|
|
353
|
+
* to be merged with the one produced by the language service
|
|
354
|
+
* or a generated internal id. Defaults to the generated internal id.
|
|
355
|
+
*/
|
|
356
|
+
owner?: string;
|
|
357
|
+
|
|
358
|
+
/**
|
|
359
|
+
* A human-readable string describing the source of this problem.
|
|
360
|
+
* E.g. 'typescript' or 'super lint'.
|
|
361
|
+
*/
|
|
362
|
+
source?: string;
|
|
363
|
+
|
|
364
|
+
/**
|
|
365
|
+
* Specifies to which kind of documents the problems found by this
|
|
366
|
+
* matcher are applied. Valid values are:
|
|
367
|
+
*
|
|
368
|
+
* "allDocuments": problems found in all documents are applied.
|
|
369
|
+
* "openDocuments": problems found in documents that are open
|
|
370
|
+
* are applied.
|
|
371
|
+
* "closedDocuments": problems found in closed documents are
|
|
372
|
+
* applied.
|
|
373
|
+
*/
|
|
374
|
+
applyTo?: string | string[];
|
|
375
|
+
|
|
376
|
+
/**
|
|
377
|
+
* The severity of the VSCode problem produced by this problem matcher.
|
|
378
|
+
*
|
|
379
|
+
* Valid values are:
|
|
380
|
+
* "error": to produce errors.
|
|
381
|
+
* "warning": to produce warnings.
|
|
382
|
+
* "info": to produce infos.
|
|
383
|
+
*
|
|
384
|
+
* The value is used if a pattern doesn't specify a severity match group.
|
|
385
|
+
* Defaults to "error" if omitted.
|
|
386
|
+
*/
|
|
387
|
+
severity?: string | Severity;
|
|
388
|
+
|
|
389
|
+
/**
|
|
390
|
+
* Defines how filename reported in a problem pattern
|
|
391
|
+
* should be read. Valid values are:
|
|
392
|
+
* - "absolute": the filename is always treated absolute.
|
|
393
|
+
* - "relative": the filename is always treated relative to
|
|
394
|
+
* the current working directory. This is the default.
|
|
395
|
+
* - ["relative", "path value"]: the filename is always
|
|
396
|
+
* treated relative to the given path value.
|
|
397
|
+
*/
|
|
398
|
+
fileLocation?: string | string[];
|
|
399
|
+
|
|
400
|
+
/**
|
|
401
|
+
* The name of a predefined problem pattern, the inline definintion
|
|
402
|
+
* of a problem pattern or an array of problem patterns to match
|
|
403
|
+
* problems spread over multiple lines.
|
|
404
|
+
*/
|
|
405
|
+
pattern?: string | ProblemPattern | ProblemPattern[];
|
|
406
|
+
|
|
407
|
+
/**
|
|
408
|
+
* A regular expression signaling that a watched tasks begins executing
|
|
409
|
+
* triggered through file watching.
|
|
410
|
+
*/
|
|
411
|
+
watchedTaskBeginsRegExp?: string;
|
|
412
|
+
|
|
413
|
+
/**
|
|
414
|
+
* A regular expression signaling that a watched tasks ends executing.
|
|
415
|
+
*/
|
|
416
|
+
watchedTaskEndsRegExp?: string;
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* @deprecated Use background instead.
|
|
420
|
+
*/
|
|
421
|
+
watching?: BackgroundMonitor;
|
|
422
|
+
background?: BackgroundMonitor;
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export type ProblemMatcherType = string | ProblemMatcher | Array<string | ProblemMatcher>;
|
|
426
|
+
|
|
427
|
+
export interface NamedProblemMatcher extends ProblemMatcher {
|
|
428
|
+
/**
|
|
429
|
+
* This name can be used to refer to the
|
|
430
|
+
* problem matcher from within a task.
|
|
431
|
+
*/
|
|
432
|
+
name: string;
|
|
433
|
+
|
|
434
|
+
/**
|
|
435
|
+
* A human readable label.
|
|
436
|
+
*/
|
|
437
|
+
label?: string;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
export function isNamedProblemMatcher(value?: ProblemMatcher): value is NamedProblemMatcher {
|
|
441
|
+
return isString((value as NamedProblemMatcher).name);
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
export class ProblemPatternParser extends Parser {
|
|
446
|
+
constructor(logger: IProblemReporter) {
|
|
447
|
+
super(logger);
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
public parse(value: Config.ProblemPattern): ProblemPattern;
|
|
451
|
+
public parse(value: Config.MultiLineProblemPattern): MultiLineProblemPattern;
|
|
452
|
+
public parse(value: Config.NamedProblemPattern): NamedProblemPattern;
|
|
453
|
+
public parse(value: Config.NamedMultiLineCheckedProblemPattern): NamedMultiLineProblemPattern;
|
|
454
|
+
public parse(
|
|
455
|
+
value:
|
|
456
|
+
| Config.ProblemPattern
|
|
457
|
+
| Config.MultiLineProblemPattern
|
|
458
|
+
| Config.NamedProblemPattern
|
|
459
|
+
| Config.NamedMultiLineCheckedProblemPattern,
|
|
460
|
+
): any {
|
|
461
|
+
if (Config.NamedMultiLineCheckedProblemPattern.is(value)) {
|
|
462
|
+
return this.createNamedMultiLineProblemPattern(value);
|
|
463
|
+
} else if (Config.MultiLineCheckedProblemPattern.is(value)) {
|
|
464
|
+
return this.createMultiLineProblemPattern(value);
|
|
465
|
+
} else if (Config.NamedCheckedProblemPattern.is(value)) {
|
|
466
|
+
const result = this.createSingleProblemPattern(value) as NamedProblemPattern;
|
|
467
|
+
result.name = value.name;
|
|
468
|
+
return result;
|
|
469
|
+
} else if (Config.CheckedProblemPattern.is(value)) {
|
|
470
|
+
return this.createSingleProblemPattern(value);
|
|
471
|
+
} else {
|
|
472
|
+
this.error(
|
|
473
|
+
localize(
|
|
474
|
+
'ProblemPatternParser.problemPattern.missingRegExp',
|
|
475
|
+
'The problem pattern is missing a regular expression.',
|
|
476
|
+
),
|
|
477
|
+
);
|
|
478
|
+
return null;
|
|
479
|
+
}
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
private createSingleProblemPattern(value: Config.CheckedProblemPattern): ProblemPattern | null {
|
|
483
|
+
const result = this.doCreateSingleProblemPattern(value, true);
|
|
484
|
+
if (result === undefined) {
|
|
485
|
+
return null;
|
|
486
|
+
} else if (result.kind === undefined) {
|
|
487
|
+
result.kind = ProblemLocationKind.Location;
|
|
488
|
+
}
|
|
489
|
+
return this.validateProblemPattern([result]) ? result : null;
|
|
490
|
+
}
|
|
491
|
+
|
|
492
|
+
private createNamedMultiLineProblemPattern(
|
|
493
|
+
value: Config.NamedMultiLineCheckedProblemPattern,
|
|
494
|
+
): NamedMultiLineProblemPattern | null {
|
|
495
|
+
const validPatterns = this.createMultiLineProblemPattern(value.patterns);
|
|
496
|
+
if (!validPatterns) {
|
|
497
|
+
return null;
|
|
498
|
+
}
|
|
499
|
+
const result = {
|
|
500
|
+
name: value.name,
|
|
501
|
+
label: value.label ? value.label : value.name,
|
|
502
|
+
patterns: validPatterns,
|
|
503
|
+
};
|
|
504
|
+
return result;
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
private createMultiLineProblemPattern(values: Config.MultiLineCheckedProblemPattern): MultiLineProblemPattern | null {
|
|
508
|
+
const result: MultiLineProblemPattern = [];
|
|
509
|
+
for (let i = 0; i < values.length; i++) {
|
|
510
|
+
const pattern = this.doCreateSingleProblemPattern(values[i], false);
|
|
511
|
+
if (pattern === undefined) {
|
|
512
|
+
return null;
|
|
513
|
+
}
|
|
514
|
+
if (i < values.length - 1) {
|
|
515
|
+
if (!isUndefined(pattern.loop) && pattern.loop) {
|
|
516
|
+
pattern.loop = false;
|
|
517
|
+
this.error(
|
|
518
|
+
localize(
|
|
519
|
+
'ProblemPatternParser.loopProperty.notLast',
|
|
520
|
+
'The loop property is only supported on the last line matcher.',
|
|
521
|
+
),
|
|
522
|
+
);
|
|
523
|
+
}
|
|
524
|
+
}
|
|
525
|
+
result.push(pattern);
|
|
526
|
+
}
|
|
527
|
+
if (result[0].kind === undefined) {
|
|
528
|
+
result[0].kind = ProblemLocationKind.Location;
|
|
529
|
+
}
|
|
530
|
+
return this.validateProblemPattern(result) ? result : null;
|
|
531
|
+
}
|
|
532
|
+
|
|
533
|
+
private doCreateSingleProblemPattern(
|
|
534
|
+
value: Config.CheckedProblemPattern,
|
|
535
|
+
setDefaults: boolean,
|
|
536
|
+
): ProblemPattern | undefined {
|
|
537
|
+
const regexp = this.createRegularExpression(value.regexp);
|
|
538
|
+
if (regexp === undefined) {
|
|
539
|
+
return undefined;
|
|
540
|
+
}
|
|
541
|
+
let result: ProblemPattern = { regexp };
|
|
542
|
+
if (value.kind) {
|
|
543
|
+
result.kind = ProblemLocationKind.fromString(value.kind);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
function copyProperty(
|
|
547
|
+
result: ProblemPattern,
|
|
548
|
+
source: Config.ProblemPattern,
|
|
549
|
+
resultKey: keyof ProblemPattern,
|
|
550
|
+
sourceKey: keyof Config.ProblemPattern,
|
|
551
|
+
) {
|
|
552
|
+
const value = source[sourceKey];
|
|
553
|
+
if (typeof value === 'number') {
|
|
554
|
+
(result as any)[resultKey] = value;
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
copyProperty(result, value, 'file', 'file');
|
|
558
|
+
copyProperty(result, value, 'location', 'location');
|
|
559
|
+
copyProperty(result, value, 'line', 'line');
|
|
560
|
+
copyProperty(result, value, 'character', 'column');
|
|
561
|
+
copyProperty(result, value, 'endLine', 'endLine');
|
|
562
|
+
copyProperty(result, value, 'endCharacter', 'endColumn');
|
|
563
|
+
copyProperty(result, value, 'severity', 'severity');
|
|
564
|
+
copyProperty(result, value, 'code', 'code');
|
|
565
|
+
copyProperty(result, value, 'message', 'message');
|
|
566
|
+
if (value.loop === true || value.loop === false) {
|
|
567
|
+
result.loop = value.loop;
|
|
568
|
+
}
|
|
569
|
+
if (setDefaults) {
|
|
570
|
+
if (result.location || result.kind === ProblemLocationKind.File) {
|
|
571
|
+
const defaultValue: Partial<ProblemPattern> = {
|
|
572
|
+
file: 1,
|
|
573
|
+
message: 0,
|
|
574
|
+
};
|
|
575
|
+
result = mixin(result, defaultValue, false);
|
|
576
|
+
} else {
|
|
577
|
+
const defaultValue: Partial<ProblemPattern> = {
|
|
578
|
+
file: 1,
|
|
579
|
+
line: 2,
|
|
580
|
+
character: 3,
|
|
581
|
+
message: 0,
|
|
582
|
+
};
|
|
583
|
+
result = mixin(result, defaultValue, false);
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
return result;
|
|
587
|
+
}
|
|
588
|
+
|
|
589
|
+
private validateProblemPattern(values: ProblemPattern[]): boolean {
|
|
590
|
+
// tslint:disable-next-line: one-variable-per-declaration
|
|
591
|
+
let file = false;
|
|
592
|
+
let message = false;
|
|
593
|
+
let location = false;
|
|
594
|
+
let line = false;
|
|
595
|
+
const locationKind = values[0].kind === undefined ? ProblemLocationKind.Location : values[0].kind;
|
|
596
|
+
|
|
597
|
+
values.forEach((pattern, i) => {
|
|
598
|
+
if (i !== 0 && pattern.kind) {
|
|
599
|
+
this.error(
|
|
600
|
+
localize(
|
|
601
|
+
'ProblemPatternParser.problemPattern.kindProperty.notFirst',
|
|
602
|
+
'The problem pattern is invalid. The kind property must be provided only in the first element',
|
|
603
|
+
),
|
|
604
|
+
);
|
|
605
|
+
}
|
|
606
|
+
file = file || !isUndefined(pattern.file);
|
|
607
|
+
message = message || !isUndefined(pattern.message);
|
|
608
|
+
location = location || !isUndefined(pattern.location);
|
|
609
|
+
line = line || !isUndefined(pattern.line);
|
|
610
|
+
});
|
|
611
|
+
if (!(file && message)) {
|
|
612
|
+
this.error(
|
|
613
|
+
localize(
|
|
614
|
+
'ProblemPatternParser.problemPattern.missingProperty',
|
|
615
|
+
'The problem pattern is invalid. It must have at least have a file and a message.',
|
|
616
|
+
),
|
|
617
|
+
);
|
|
618
|
+
return false;
|
|
619
|
+
}
|
|
620
|
+
if (locationKind === ProblemLocationKind.Location && !(location || line)) {
|
|
621
|
+
this.error(
|
|
622
|
+
localize(
|
|
623
|
+
'ProblemPatternParser.problemPattern.missingLocation',
|
|
624
|
+
'The problem pattern is invalid. It must either have kind: "file" or have a line or location match group.',
|
|
625
|
+
),
|
|
626
|
+
);
|
|
627
|
+
return false;
|
|
628
|
+
}
|
|
629
|
+
return true;
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
private createRegularExpression(value: string): RegExp | undefined {
|
|
633
|
+
let result: RegExp | undefined;
|
|
634
|
+
try {
|
|
635
|
+
result = new RegExp(value);
|
|
636
|
+
} catch (err) {
|
|
637
|
+
this.error(
|
|
638
|
+
localize(
|
|
639
|
+
'ProblemPatternParser.invalidRegexp',
|
|
640
|
+
'Error: The string {0} is not a valid regular expression.\n',
|
|
641
|
+
value,
|
|
642
|
+
),
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
return result;
|
|
646
|
+
}
|
|
647
|
+
}
|
|
648
|
+
|
|
649
|
+
export class ProblemMatcherParser extends Parser {
|
|
650
|
+
constructor(logger: IProblemReporter) {
|
|
651
|
+
super(logger);
|
|
652
|
+
}
|
|
653
|
+
|
|
654
|
+
public parse(
|
|
655
|
+
json: Config.ProblemMatcher,
|
|
656
|
+
getProblemPattern: getProblemPatternFn,
|
|
657
|
+
getProblemMatcher: getProblemMatcherFn,
|
|
658
|
+
): ProblemMatcher | undefined {
|
|
659
|
+
const result = this.createProblemMatcher(json, getProblemPattern, getProblemMatcher);
|
|
660
|
+
if (!this.checkProblemMatcherValid(json, result)) {
|
|
661
|
+
return undefined;
|
|
662
|
+
}
|
|
663
|
+
this.addWatchingMatcher(json, result);
|
|
664
|
+
|
|
665
|
+
return result;
|
|
666
|
+
}
|
|
667
|
+
|
|
668
|
+
private checkProblemMatcherValid(
|
|
669
|
+
externalProblemMatcher: Config.ProblemMatcher,
|
|
670
|
+
problemMatcher: ProblemMatcher | null,
|
|
671
|
+
): problemMatcher is ProblemMatcher {
|
|
672
|
+
if (!problemMatcher) {
|
|
673
|
+
this.error(
|
|
674
|
+
localize(
|
|
675
|
+
'ProblemMatcherParser.noProblemMatcher',
|
|
676
|
+
"Error: the description can't be converted into a problem matcher:\n{0}\n",
|
|
677
|
+
JSON.stringify(externalProblemMatcher, null, 4),
|
|
678
|
+
),
|
|
679
|
+
);
|
|
680
|
+
return false;
|
|
681
|
+
}
|
|
682
|
+
if (!problemMatcher.pattern) {
|
|
683
|
+
this.error(
|
|
684
|
+
localize(
|
|
685
|
+
'ProblemMatcherParser.noProblemPattern',
|
|
686
|
+
"Error: the description doesn't define a valid problem pattern:\n{0}\n",
|
|
687
|
+
JSON.stringify(externalProblemMatcher, null, 4),
|
|
688
|
+
),
|
|
689
|
+
);
|
|
690
|
+
return false;
|
|
691
|
+
}
|
|
692
|
+
if (!problemMatcher.owner) {
|
|
693
|
+
this.error(
|
|
694
|
+
localize(
|
|
695
|
+
'ProblemMatcherParser.noOwner',
|
|
696
|
+
"Error: the description doesn't define an owner:\n{0}\n",
|
|
697
|
+
JSON.stringify(externalProblemMatcher, null, 4),
|
|
698
|
+
),
|
|
699
|
+
);
|
|
700
|
+
return false;
|
|
701
|
+
}
|
|
702
|
+
if (isUndefined(problemMatcher.fileLocation)) {
|
|
703
|
+
this.error(
|
|
704
|
+
localize(
|
|
705
|
+
'ProblemMatcherParser.noFileLocation',
|
|
706
|
+
"Error: the description doesn't define a file location:\n{0}\n",
|
|
707
|
+
JSON.stringify(externalProblemMatcher, null, 4),
|
|
708
|
+
),
|
|
709
|
+
);
|
|
710
|
+
return false;
|
|
711
|
+
}
|
|
712
|
+
return true;
|
|
713
|
+
}
|
|
714
|
+
|
|
715
|
+
private createProblemMatcher(
|
|
716
|
+
description: Config.ProblemMatcher,
|
|
717
|
+
getProblemPattern: getProblemPatternFn,
|
|
718
|
+
getProblemMatcher: getProblemMatcherFn,
|
|
719
|
+
): ProblemMatcher | null {
|
|
720
|
+
let result: ProblemMatcher | null = null;
|
|
721
|
+
|
|
722
|
+
const owner = isString(description.owner) ? description.owner : uuid();
|
|
723
|
+
const source = isString(description.source) ? description.source : undefined;
|
|
724
|
+
let applyTo = isString(description.applyTo)
|
|
725
|
+
? ApplyToKind.fromString(description.applyTo)
|
|
726
|
+
: ApplyToKind.allDocuments;
|
|
727
|
+
if (!applyTo) {
|
|
728
|
+
applyTo = ApplyToKind.allDocuments;
|
|
729
|
+
}
|
|
730
|
+
let fileLocation: FileLocationKind | undefined;
|
|
731
|
+
let filePrefix: string | undefined;
|
|
732
|
+
|
|
733
|
+
let kind: FileLocationKind | undefined;
|
|
734
|
+
if (isUndefined(description.fileLocation)) {
|
|
735
|
+
fileLocation = FileLocationKind.Relative;
|
|
736
|
+
filePrefix = '${workspaceFolder}';
|
|
737
|
+
} else if (isString(description.fileLocation)) {
|
|
738
|
+
kind = FileLocationKind.fromString(description.fileLocation as string);
|
|
739
|
+
if (kind) {
|
|
740
|
+
fileLocation = kind;
|
|
741
|
+
if (kind === FileLocationKind.Relative || kind === FileLocationKind.AutoDetect) {
|
|
742
|
+
filePrefix = '${workspaceFolder}';
|
|
743
|
+
}
|
|
744
|
+
}
|
|
745
|
+
} else if (isStringArray(description.fileLocation)) {
|
|
746
|
+
const values = description.fileLocation as string[];
|
|
747
|
+
if (values.length > 0) {
|
|
748
|
+
kind = FileLocationKind.fromString(values[0]);
|
|
749
|
+
if (values.length === 1 && kind === FileLocationKind.Absolute) {
|
|
750
|
+
fileLocation = kind;
|
|
751
|
+
} else if (
|
|
752
|
+
values.length === 2 &&
|
|
753
|
+
(kind === FileLocationKind.Relative || kind === FileLocationKind.AutoDetect) &&
|
|
754
|
+
values[1]
|
|
755
|
+
) {
|
|
756
|
+
fileLocation = kind;
|
|
757
|
+
filePrefix = values[1];
|
|
758
|
+
}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const pattern = description.pattern ? this.createProblemPattern(description.pattern, getProblemPattern) : undefined;
|
|
763
|
+
|
|
764
|
+
let severity = description.severity ? Severity.fromValue(description.severity as string) : undefined;
|
|
765
|
+
if (severity === Severity.Ignore) {
|
|
766
|
+
this.info(
|
|
767
|
+
localize(
|
|
768
|
+
'ProblemMatcherParser.unknownSeverity',
|
|
769
|
+
'Info: unknown severity {0}. Valid values are error, warning and info.\n',
|
|
770
|
+
description.severity as string,
|
|
771
|
+
),
|
|
772
|
+
);
|
|
773
|
+
severity = Severity.Error;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
if (isString(description.base)) {
|
|
777
|
+
const variableName = description.base as string;
|
|
778
|
+
if (variableName.length > 1 && variableName[0] === '$') {
|
|
779
|
+
const base = getProblemMatcher(variableName.substring(1));
|
|
780
|
+
if (base) {
|
|
781
|
+
result = deepClone(base);
|
|
782
|
+
if (description.owner !== undefined && owner !== undefined) {
|
|
783
|
+
result.owner = owner;
|
|
784
|
+
}
|
|
785
|
+
if (description.source !== undefined && source !== undefined) {
|
|
786
|
+
result.source = source;
|
|
787
|
+
}
|
|
788
|
+
if (description.fileLocation !== undefined && fileLocation !== undefined) {
|
|
789
|
+
result.fileLocation = fileLocation;
|
|
790
|
+
result.filePrefix = filePrefix;
|
|
791
|
+
}
|
|
792
|
+
if (description.pattern !== undefined && pattern !== undefined && pattern !== null) {
|
|
793
|
+
result.pattern = pattern;
|
|
794
|
+
}
|
|
795
|
+
if (description.severity !== undefined && severity !== undefined) {
|
|
796
|
+
result.severity = severity;
|
|
797
|
+
}
|
|
798
|
+
if (description.applyTo !== undefined && applyTo !== undefined) {
|
|
799
|
+
result.applyTo = applyTo;
|
|
800
|
+
}
|
|
801
|
+
}
|
|
802
|
+
}
|
|
803
|
+
} else if (fileLocation && pattern) {
|
|
804
|
+
result = {
|
|
805
|
+
owner,
|
|
806
|
+
applyTo,
|
|
807
|
+
fileLocation,
|
|
808
|
+
pattern,
|
|
809
|
+
};
|
|
810
|
+
if (source) {
|
|
811
|
+
result.source = source;
|
|
812
|
+
}
|
|
813
|
+
if (filePrefix) {
|
|
814
|
+
result.filePrefix = filePrefix;
|
|
815
|
+
}
|
|
816
|
+
if (severity) {
|
|
817
|
+
result.severity = severity;
|
|
818
|
+
}
|
|
819
|
+
}
|
|
820
|
+
if (Config.isNamedProblemMatcher(description)) {
|
|
821
|
+
(result as NamedProblemMatcher).name = description.name;
|
|
822
|
+
(result as NamedProblemMatcher).label = isString(description.label) ? description.label : description.name;
|
|
823
|
+
}
|
|
824
|
+
return result;
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
private createProblemPattern(
|
|
828
|
+
value: string | Config.ProblemPattern | Config.MultiLineProblemPattern,
|
|
829
|
+
getProblemPattern: getProblemPatternFn,
|
|
830
|
+
): ProblemPattern | ProblemPattern[] | undefined {
|
|
831
|
+
if (isString(value)) {
|
|
832
|
+
const variableName: string = value as string;
|
|
833
|
+
if (variableName.length > 1 && variableName[0] === '$') {
|
|
834
|
+
const result = getProblemPattern(variableName.substring(1));
|
|
835
|
+
if (!result) {
|
|
836
|
+
this.error(
|
|
837
|
+
localize(
|
|
838
|
+
'ProblemMatcherParser.noDefinedPatter',
|
|
839
|
+
"Error: the pattern with the identifier {0} doesn't exist.",
|
|
840
|
+
variableName,
|
|
841
|
+
),
|
|
842
|
+
);
|
|
843
|
+
}
|
|
844
|
+
return result;
|
|
845
|
+
} else {
|
|
846
|
+
if (variableName.length === 0) {
|
|
847
|
+
this.error(
|
|
848
|
+
localize('ProblemMatcherParser.noIdentifier', 'Error: the pattern property refers to an empty identifier.'),
|
|
849
|
+
);
|
|
850
|
+
} else {
|
|
851
|
+
this.error(
|
|
852
|
+
localize(
|
|
853
|
+
'ProblemMatcherParser.noValidIdentifier',
|
|
854
|
+
'Error: the pattern property {0} is not a valid pattern variable name.',
|
|
855
|
+
variableName,
|
|
856
|
+
),
|
|
857
|
+
);
|
|
858
|
+
}
|
|
859
|
+
}
|
|
860
|
+
} else if (value) {
|
|
861
|
+
const problemPatternParser = new ProblemPatternParser(this.problemReporter);
|
|
862
|
+
if (Array.isArray(value)) {
|
|
863
|
+
return problemPatternParser.parse(value);
|
|
864
|
+
} else {
|
|
865
|
+
return problemPatternParser.parse(value);
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
return undefined;
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
private addWatchingMatcher(external: Config.ProblemMatcher, internal: ProblemMatcher): void {
|
|
872
|
+
const oldBegins = this.createRegularExpression(external.watchedTaskBeginsRegExp);
|
|
873
|
+
const oldEnds = this.createRegularExpression(external.watchedTaskEndsRegExp);
|
|
874
|
+
if (oldBegins && oldEnds) {
|
|
875
|
+
internal.watching = {
|
|
876
|
+
activeOnStart: false,
|
|
877
|
+
beginsPattern: { regexp: oldBegins },
|
|
878
|
+
endsPattern: { regexp: oldEnds },
|
|
879
|
+
};
|
|
880
|
+
return;
|
|
881
|
+
}
|
|
882
|
+
const backgroundMonitor = external.background || external.watching;
|
|
883
|
+
if (isUndefinedOrNull(backgroundMonitor)) {
|
|
884
|
+
return;
|
|
885
|
+
}
|
|
886
|
+
const begins: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.beginsPattern);
|
|
887
|
+
const ends: WatchingPattern | null = this.createWatchingPattern(backgroundMonitor.endsPattern);
|
|
888
|
+
if (begins && ends) {
|
|
889
|
+
internal.watching = {
|
|
890
|
+
activeOnStart: isBoolean(backgroundMonitor.activeOnStart) ? backgroundMonitor.activeOnStart : false,
|
|
891
|
+
beginsPattern: begins,
|
|
892
|
+
endsPattern: ends,
|
|
893
|
+
};
|
|
894
|
+
return;
|
|
895
|
+
}
|
|
896
|
+
if (begins || ends) {
|
|
897
|
+
this.error(
|
|
898
|
+
localize(
|
|
899
|
+
'ProblemMatcherParser.problemPattern.watchingMatcher',
|
|
900
|
+
'A problem matcher must define both a begin pattern and an end pattern for watching.',
|
|
901
|
+
),
|
|
902
|
+
);
|
|
903
|
+
}
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
private createWatchingPattern(external: string | Config.WatchingPattern | undefined): WatchingPattern | null {
|
|
907
|
+
if (isUndefinedOrNull(external)) {
|
|
908
|
+
return null;
|
|
909
|
+
}
|
|
910
|
+
let regexp: RegExp | null;
|
|
911
|
+
let file: number | undefined;
|
|
912
|
+
if (isString(external)) {
|
|
913
|
+
regexp = this.createRegularExpression(external);
|
|
914
|
+
} else {
|
|
915
|
+
regexp = this.createRegularExpression(external.regexp);
|
|
916
|
+
if (isNumber(external.file)) {
|
|
917
|
+
file = external.file;
|
|
918
|
+
}
|
|
919
|
+
}
|
|
920
|
+
if (!regexp) {
|
|
921
|
+
return null;
|
|
922
|
+
}
|
|
923
|
+
return file ? { regexp, file } : { regexp, file: 1 };
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
private createRegularExpression(value: string | undefined): RegExp | null {
|
|
927
|
+
let result: RegExp | null = null;
|
|
928
|
+
if (!value) {
|
|
929
|
+
return result;
|
|
930
|
+
}
|
|
931
|
+
try {
|
|
932
|
+
result = new RegExp(value);
|
|
933
|
+
} catch (err) {
|
|
934
|
+
this.error(
|
|
935
|
+
localize(
|
|
936
|
+
'ProblemMatcherParser.invalidRegexp',
|
|
937
|
+
'Error: The string {0} is not a valid regular expression.\n',
|
|
938
|
+
value,
|
|
939
|
+
),
|
|
940
|
+
);
|
|
941
|
+
}
|
|
942
|
+
return result;
|
|
943
|
+
}
|
|
944
|
+
}
|