Skip to content

Commit c541056

Browse files
committed
Problem matcher file location auto detection
Fixes microsoft#449
1 parent df9b668 commit c541056

5 files changed

Lines changed: 87 additions & 58 deletions

File tree

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

Lines changed: 19 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
1313
import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToKind, WatchingPattern, getResource } from 'vs/workbench/contrib/tasks/common/problemMatcher';
1414
import { IMarkerService, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
1515
import { generateUuid } from 'vs/base/common/uuid';
16+
import { IFileService } from 'vs/platform/files/common/files';
1617

1718
export const enum ProblemCollectorEventKind {
1819
BackgroundProcessingBegins = 'backgroundProcessingBegins',
@@ -30,7 +31,7 @@ namespace ProblemCollectorEvent {
3031
}
3132

3233
export interface IProblemMatcher {
33-
processLine(line: string): void;
34+
processLine(line: string): Promise<void>;
3435
}
3536

3637
export class AbstractProblemCollector implements IDisposable {
@@ -55,10 +56,10 @@ export class AbstractProblemCollector implements IDisposable {
5556

5657
protected _onDidStateChange: Emitter<ProblemCollectorEvent>;
5758

58-
constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService) {
59+
constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService, fileService?: IFileService) {
5960
this.matchers = Object.create(null);
6061
this.bufferLength = 1;
61-
problemMatchers.map(elem => createLineMatcher(elem)).forEach((matcher) => {
62+
problemMatchers.map(elem => createLineMatcher(elem, fileService)).forEach((matcher) => {
6263
let length = matcher.matchLength;
6364
if (length > this.bufferLength) {
6465
this.bufferLength = length;
@@ -143,14 +144,14 @@ export class AbstractProblemCollector implements IDisposable {
143144
return result;
144145
}
145146

146-
protected shouldApplyMatch(result: ProblemMatch): boolean {
147+
protected async shouldApplyMatch(result: ProblemMatch): Promise<boolean> {
147148
switch (result.description.applyTo) {
148149
case ApplyToKind.allDocuments:
149150
return true;
150151
case ApplyToKind.openDocuments:
151-
return !!this.openModels[result.resource.toString()];
152+
return !!this.openModels[(await result.resource).toString()];
152153
case ApplyToKind.closedDocuments:
153-
return !this.openModels[result.resource.toString()];
154+
return !this.openModels[(await result.resource).toString()];
154155
default:
155156
return true;
156157
}
@@ -333,8 +334,8 @@ export class StartStopProblemCollector extends AbstractProblemCollector implemen
333334
private currentOwner: string;
334335
private currentResource: string;
335336

336-
constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, _strategy: ProblemHandlingStrategy = ProblemHandlingStrategy.Clean) {
337-
super(problemMatchers, markerService, modelService);
337+
constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, _strategy: ProblemHandlingStrategy = ProblemHandlingStrategy.Clean, fileService?: IFileService) {
338+
super(problemMatchers, markerService, modelService, fileService);
338339
let ownerSet: { [key: string]: boolean; } = Object.create(null);
339340
problemMatchers.forEach(description => ownerSet[description.owner] = true);
340341
this.owners = Object.keys(ownerSet);
@@ -343,17 +344,17 @@ export class StartStopProblemCollector extends AbstractProblemCollector implemen
343344
});
344345
}
345346

346-
public processLine(line: string): void {
347+
public async processLine(line: string): Promise<void> {
347348
let markerMatch = this.tryFindMarker(line);
348349
if (!markerMatch) {
349350
return;
350351
}
351352

352353
let owner = markerMatch.description.owner;
353-
let resource = markerMatch.resource;
354+
let resource = await markerMatch.resource;
354355
let resourceAsString = resource.toString();
355356
this.removeResourceToClean(owner, resourceAsString);
356-
let shouldApplyMatch = this.shouldApplyMatch(markerMatch);
357+
let shouldApplyMatch = await this.shouldApplyMatch(markerMatch);
357358
if (shouldApplyMatch) {
358359
this.recordMarker(markerMatch.marker, owner, resourceAsString);
359360
if (this.currentOwner !== owner || this.currentResource !== resourceAsString) {
@@ -386,8 +387,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
386387
private currentOwner: string | null;
387388
private currentResource: string | null;
388389

389-
constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService) {
390-
super(problemMatchers, markerService, modelService);
390+
constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, fileService?: IFileService) {
391+
super(problemMatchers, markerService, modelService, fileService);
391392
this.problemMatchers = problemMatchers;
392393
this.resetCurrentResource();
393394
this.backgroundPatterns = [];
@@ -415,19 +416,19 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
415416
}
416417
}
417418

418-
public processLine(line: string): void {
419+
public async processLine(line: string): Promise<void> {
419420
if (this.tryBegin(line) || this.tryFinish(line)) {
420421
return;
421422
}
422423
let markerMatch = this.tryFindMarker(line);
423424
if (!markerMatch) {
424425
return;
425426
}
426-
let resource = markerMatch.resource;
427+
let resource = await markerMatch.resource;
427428
let owner = markerMatch.description.owner;
428429
let resourceAsString = resource.toString();
429430
this.removeResourceToClean(owner, resourceAsString);
430-
let shouldApplyMatch = this.shouldApplyMatch(markerMatch);
431+
let shouldApplyMatch = await this.shouldApplyMatch(markerMatch);
431432
if (shouldApplyMatch) {
432433
this.recordMarker(markerMatch.marker, owner, resourceAsString);
433434
if (this.currentOwner !== owner || this.currentResource !== resourceAsString) {
@@ -442,7 +443,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
442443
this.reportMarkersForCurrentResource();
443444
}
444445

445-
private tryBegin(line: string): boolean {
446+
private async tryBegin(line: string): Promise<boolean> {
446447
let result = false;
447448
for (const background of this.backgroundPatterns) {
448449
let matches = background.begin.regexp.exec(line);
@@ -459,7 +460,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
459460
let file = matches[background.begin.file!];
460461
if (file) {
461462
let resource = getResource(file, background.matcher);
462-
this.recordResourceToClean(owner, resource);
463+
this.recordResourceToClean(owner, await resource);
463464
} else {
464465
this.recordResourcesToClean(owner);
465466
}

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

Lines changed: 42 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -21,11 +21,13 @@ import { IStringDictionary } from 'vs/base/common/collections';
2121
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
2222
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
2323
import { Event, Emitter } from 'vs/base/common/event';
24+
import { IFileService, IFileStat } from 'vs/platform/files/common/files';
2425

2526
export enum FileLocationKind {
26-
Auto,
27+
Default,
2728
Relative,
28-
Absolute
29+
Absolute,
30+
AutoDetect
2931
}
3032

3133
export module FileLocationKind {
@@ -35,6 +37,8 @@ export module FileLocationKind {
3537
return FileLocationKind.Absolute;
3638
} else if (value === 'relative') {
3739
return FileLocationKind.Relative;
40+
} else if (value === 'autodetect') {
41+
return FileLocationKind.AutoDetect;
3842
} else {
3943
return undefined;
4044
}
@@ -172,7 +176,7 @@ interface ProblemData {
172176
}
173177

174178
export interface ProblemMatch {
175-
resource: URI;
179+
resource: Promise<URI>;
176180
marker: IMarkerData;
177181
description: ProblemMatcher;
178182
}
@@ -182,13 +186,32 @@ export interface HandleResult {
182186
continue: boolean;
183187
}
184188

185-
export function getResource(filename: string, matcher: ProblemMatcher): URI {
189+
190+
export async function getResource(filename: string, matcher: ProblemMatcher, fileService?: IFileService): Promise<URI> {
186191
let kind = matcher.fileLocation;
187192
let fullPath: string | undefined;
188193
if (kind === FileLocationKind.Absolute) {
189194
fullPath = filename;
190195
} else if ((kind === FileLocationKind.Relative) && matcher.filePrefix) {
191196
fullPath = join(matcher.filePrefix, filename);
197+
} else if (kind === FileLocationKind.AutoDetect) {
198+
const matcherClone = Objects.deepClone(matcher);
199+
matcherClone.fileLocation = FileLocationKind.Relative;
200+
if (fileService) {
201+
const relative = await getResource(filename, matcherClone);
202+
let stat: IFileStat | undefined = undefined;
203+
try {
204+
stat = await fileService.resolve(relative);
205+
} catch (ex) {
206+
// Do nothing, we just need to catch file resolution errors.
207+
}
208+
if (stat) {
209+
return relative;
210+
}
211+
}
212+
213+
matcherClone.fileLocation = FileLocationKind.Absolute;
214+
return getResource(filename, matcherClone);
192215
}
193216
if (fullPath === undefined) {
194217
throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.');
@@ -210,22 +233,24 @@ export interface ILineMatcher {
210233
handle(lines: string[], start?: number): HandleResult;
211234
}
212235

213-
export function createLineMatcher(matcher: ProblemMatcher): ILineMatcher {
236+
export function createLineMatcher(matcher: ProblemMatcher, fileService?: IFileService): ILineMatcher {
214237
let pattern = matcher.pattern;
215238
if (Types.isArray(pattern)) {
216-
return new MultiLineMatcher(matcher);
239+
return new MultiLineMatcher(matcher, fileService);
217240
} else {
218-
return new SingleLineMatcher(matcher);
241+
return new SingleLineMatcher(matcher, fileService);
219242
}
220243
}
221244

222245
const endOfLine: string = Platform.OS === Platform.OperatingSystem.Windows ? '\r\n' : '\n';
223246

224247
abstract class AbstractLineMatcher implements ILineMatcher {
225248
private matcher: ProblemMatcher;
249+
private fileService?: IFileService;
226250

227-
constructor(matcher: ProblemMatcher) {
251+
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
228252
this.matcher = matcher;
253+
this.fileService = fileService;
229254
}
230255

231256
public handle(lines: string[], start: number = 0): HandleResult {
@@ -312,8 +337,8 @@ abstract class AbstractLineMatcher implements ILineMatcher {
312337
return undefined;
313338
}
314339

315-
protected getResource(filename: string): URI {
316-
return getResource(filename, this.matcher);
340+
protected getResource(filename: string): Promise<URI> {
341+
return getResource(filename, this.matcher, this.fileService);
317342
}
318343

319344
private getLocation(data: ProblemData): Location | null {
@@ -389,8 +414,8 @@ class SingleLineMatcher extends AbstractLineMatcher {
389414

390415
private pattern: ProblemPattern;
391416

392-
constructor(matcher: ProblemMatcher) {
393-
super(matcher);
417+
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
418+
super(matcher, fileService);
394419
this.pattern = <ProblemPattern>matcher.pattern;
395420
}
396421

@@ -425,8 +450,8 @@ class MultiLineMatcher extends AbstractLineMatcher {
425450
private patterns: ProblemPattern[];
426451
private data: ProblemData | null;
427452

428-
constructor(matcher: ProblemMatcher) {
429-
super(matcher);
453+
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
454+
super(matcher, fileService);
430455
this.patterns = <ProblemPattern[]>matcher.pattern;
431456
}
432457

@@ -1345,7 +1370,7 @@ export class ProblemMatcherParser extends Parser {
13451370
kind = FileLocationKind.fromString(<string>description.fileLocation);
13461371
if (kind) {
13471372
fileLocation = kind;
1348-
if (kind === FileLocationKind.Relative) {
1373+
if ((kind === FileLocationKind.Relative) || (kind === FileLocationKind.AutoDetect)) {
13491374
filePrefix = '${workspaceFolder}';
13501375
}
13511376
}
@@ -1355,7 +1380,7 @@ export class ProblemMatcherParser extends Parser {
13551380
kind = FileLocationKind.fromString(values[0]);
13561381
if (values.length === 1 && kind === FileLocationKind.Absolute) {
13571382
fileLocation = kind;
1358-
} else if (values.length === 2 && kind === FileLocationKind.Relative && values[1]) {
1383+
} else if (values.length === 2 && (kind === FileLocationKind.Relative || kind === FileLocationKind.AutoDetect) && values[1]) {
13591384
fileLocation = kind;
13601385
filePrefix = values[1];
13611386
}
@@ -1573,7 +1598,7 @@ export namespace Schemas {
15731598
oneOf: [
15741599
{
15751600
type: 'string',
1576-
enum: ['absolute', 'relative']
1601+
enum: ['absolute', 'relative', 'autoDetect']
15771602
},
15781603
{
15791604
type: 'array',

src/vs/workbench/contrib/tasks/electron-browser/task.contribution.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1297,7 +1297,7 @@ class TaskService extends Disposable implements ITaskService {
12971297
this.terminalService, this.outputService, this.panelService, this.markerService,
12981298
this.modelService, this.configurationResolverService, this.telemetryService,
12991299
this.contextService, this.environmentService,
1300-
TaskService.OutputChannelId,
1300+
TaskService.OutputChannelId, this.fileService,
13011301
(workspaceFolder: IWorkspaceFolder) => {
13021302
if (!workspaceFolder) {
13031303
return undefined;

src/vs/workbench/contrib/tasks/electron-browser/terminalTaskSystem.ts

Lines changed: 14 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
1717
import { isUNC } from 'vs/base/common/extpath';
1818

1919
import { win32 } from 'vs/base/node/processes';
20-
20+
import { IFileService } from 'vs/platform/files/common/files';
2121
import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
2222
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
2323
import { IModelService } from 'vs/editor/common/services/modelService';
@@ -29,7 +29,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
2929
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
3030
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
3131
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
32-
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/contrib/tasks/common/problemCollectors';
32+
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors';
3333
import {
3434
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
3535
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope, RevealProblemKind
@@ -171,6 +171,7 @@ export class TerminalTaskSystem implements ITaskSystem {
171171
private contextService: IWorkspaceContextService,
172172
private environmentService: IWorkbenchEnvironmentService,
173173
private outputChannelId: string,
174+
private fileService: IFileService,
174175
taskSystemInfoResolver: TaskSystemInfoResovler,
175176
) {
176177

@@ -509,7 +510,7 @@ export class TerminalTaskSystem implements ITaskSystem {
509510
if (task.configurationProperties.isBackground) {
510511
promise = new Promise<ITaskSummary>((resolve, reject) => {
511512
const problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers);
512-
let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService);
513+
let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService, this.fileService);
513514
let toDispose: IDisposable[] | undefined = [];
514515
let eventCounter: number = 0;
515516
toDispose.push(watchingProblemMatcher.onDidStateChange((event) => {
@@ -554,13 +555,14 @@ export class TerminalTaskSystem implements ITaskSystem {
554555
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id));
555556
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
556557
const onData = terminal.onLineData((line) => {
557-
watchingProblemMatcher.processLine(line);
558-
if (!delayer) {
559-
delayer = new Async.Delayer(3000);
560-
}
561-
delayer.trigger(() => {
562-
watchingProblemMatcher.forceDelivery();
563-
delayer = undefined;
558+
return watchingProblemMatcher.processLine(line).then(() => {
559+
if (!delayer) {
560+
delayer = new Async.Delayer(3000);
561+
}
562+
delayer.trigger(() => {
563+
watchingProblemMatcher.forceDelivery();
564+
delayer = undefined;
565+
});
564566
});
565567
});
566568
const onExit = terminal.onExit((exitCode) => {
@@ -630,10 +632,10 @@ export class TerminalTaskSystem implements ITaskSystem {
630632
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id));
631633
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
632634
let problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers);
633-
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService);
635+
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService);
634636
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
635637
const onData = terminal.onLineData((line) => {
636-
startStopProblemMatcher.processLine(line);
638+
return startStopProblemMatcher.processLine(line);
637639
});
638640
const onExit = terminal.onExit((exitCode) => {
639641
onData.dispose();

0 commit comments

Comments
 (0)