Skip to content

Commit

Permalink
Problem matcher file location auto detection
Browse files Browse the repository at this point in the history
Fixes #449
  • Loading branch information
alexr00 committed Jun 4, 2019
1 parent df9b668 commit c541056
Show file tree
Hide file tree
Showing 5 changed files with 87 additions and 58 deletions.
37 changes: 19 additions & 18 deletions src/vs/workbench/contrib/tasks/common/problemCollectors.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { IModelService } from 'vs/editor/common/services/modelService';
import { ILineMatcher, createLineMatcher, ProblemMatcher, ProblemMatch, ApplyToKind, WatchingPattern, getResource } from 'vs/workbench/contrib/tasks/common/problemMatcher';
import { IMarkerService, IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { generateUuid } from 'vs/base/common/uuid';
import { IFileService } from 'vs/platform/files/common/files';

export const enum ProblemCollectorEventKind {
BackgroundProcessingBegins = 'backgroundProcessingBegins',
Expand All @@ -30,7 +31,7 @@ namespace ProblemCollectorEvent {
}

export interface IProblemMatcher {
processLine(line: string): void;
processLine(line: string): Promise<void>;
}

export class AbstractProblemCollector implements IDisposable {
Expand All @@ -55,10 +56,10 @@ export class AbstractProblemCollector implements IDisposable {

protected _onDidStateChange: Emitter<ProblemCollectorEvent>;

constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService) {
constructor(problemMatchers: ProblemMatcher[], protected markerService: IMarkerService, private modelService: IModelService, fileService?: IFileService) {
this.matchers = Object.create(null);
this.bufferLength = 1;
problemMatchers.map(elem => createLineMatcher(elem)).forEach((matcher) => {
problemMatchers.map(elem => createLineMatcher(elem, fileService)).forEach((matcher) => {
let length = matcher.matchLength;
if (length > this.bufferLength) {
this.bufferLength = length;
Expand Down Expand Up @@ -143,14 +144,14 @@ export class AbstractProblemCollector implements IDisposable {
return result;
}

protected shouldApplyMatch(result: ProblemMatch): boolean {
protected async shouldApplyMatch(result: ProblemMatch): Promise<boolean> {
switch (result.description.applyTo) {
case ApplyToKind.allDocuments:
return true;
case ApplyToKind.openDocuments:
return !!this.openModels[result.resource.toString()];
return !!this.openModels[(await result.resource).toString()];
case ApplyToKind.closedDocuments:
return !this.openModels[result.resource.toString()];
return !this.openModels[(await result.resource).toString()];
default:
return true;
}
Expand Down Expand Up @@ -333,8 +334,8 @@ export class StartStopProblemCollector extends AbstractProblemCollector implemen
private currentOwner: string;
private currentResource: string;

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

public processLine(line: string): void {
public async processLine(line: string): Promise<void> {
let markerMatch = this.tryFindMarker(line);
if (!markerMatch) {
return;
}

let owner = markerMatch.description.owner;
let resource = markerMatch.resource;
let resource = await markerMatch.resource;
let resourceAsString = resource.toString();
this.removeResourceToClean(owner, resourceAsString);
let shouldApplyMatch = this.shouldApplyMatch(markerMatch);
let shouldApplyMatch = await this.shouldApplyMatch(markerMatch);
if (shouldApplyMatch) {
this.recordMarker(markerMatch.marker, owner, resourceAsString);
if (this.currentOwner !== owner || this.currentResource !== resourceAsString) {
Expand Down Expand Up @@ -386,8 +387,8 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
private currentOwner: string | null;
private currentResource: string | null;

constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService) {
super(problemMatchers, markerService, modelService);
constructor(problemMatchers: ProblemMatcher[], markerService: IMarkerService, modelService: IModelService, fileService?: IFileService) {
super(problemMatchers, markerService, modelService, fileService);
this.problemMatchers = problemMatchers;
this.resetCurrentResource();
this.backgroundPatterns = [];
Expand Down Expand Up @@ -415,19 +416,19 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
}
}

public processLine(line: string): void {
public async processLine(line: string): Promise<void> {
if (this.tryBegin(line) || this.tryFinish(line)) {
return;
}
let markerMatch = this.tryFindMarker(line);
if (!markerMatch) {
return;
}
let resource = markerMatch.resource;
let resource = await markerMatch.resource;
let owner = markerMatch.description.owner;
let resourceAsString = resource.toString();
this.removeResourceToClean(owner, resourceAsString);
let shouldApplyMatch = this.shouldApplyMatch(markerMatch);
let shouldApplyMatch = await this.shouldApplyMatch(markerMatch);
if (shouldApplyMatch) {
this.recordMarker(markerMatch.marker, owner, resourceAsString);
if (this.currentOwner !== owner || this.currentResource !== resourceAsString) {
Expand All @@ -442,7 +443,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
this.reportMarkersForCurrentResource();
}

private tryBegin(line: string): boolean {
private async tryBegin(line: string): Promise<boolean> {
let result = false;
for (const background of this.backgroundPatterns) {
let matches = background.begin.regexp.exec(line);
Expand All @@ -459,7 +460,7 @@ export class WatchingProblemCollector extends AbstractProblemCollector implement
let file = matches[background.begin.file!];
if (file) {
let resource = getResource(file, background.matcher);
this.recordResourceToClean(owner, resource);
this.recordResourceToClean(owner, await resource);
} else {
this.recordResourcesToClean(owner);
}
Expand Down
59 changes: 42 additions & 17 deletions src/vs/workbench/contrib/tasks/common/problemMatcher.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,13 @@ import { IStringDictionary } from 'vs/base/common/collections';
import { IMarkerData, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { ExtensionsRegistry, ExtensionMessageCollector } from 'vs/workbench/services/extensions/common/extensionsRegistry';
import { Event, Emitter } from 'vs/base/common/event';
import { IFileService, IFileStat } from 'vs/platform/files/common/files';

export enum FileLocationKind {
Auto,
Default,
Relative,
Absolute
Absolute,
AutoDetect
}

export module FileLocationKind {
Expand All @@ -35,6 +37,8 @@ export module FileLocationKind {
return FileLocationKind.Absolute;
} else if (value === 'relative') {
return FileLocationKind.Relative;
} else if (value === 'autodetect') {
return FileLocationKind.AutoDetect;
} else {
return undefined;
}
Expand Down Expand Up @@ -172,7 +176,7 @@ interface ProblemData {
}

export interface ProblemMatch {
resource: URI;
resource: Promise<URI>;
marker: IMarkerData;
description: ProblemMatcher;
}
Expand All @@ -182,13 +186,32 @@ export interface HandleResult {
continue: boolean;
}

export function getResource(filename: string, matcher: ProblemMatcher): URI {

export async function getResource(filename: string, matcher: ProblemMatcher, fileService?: IFileService): Promise<URI> {
let kind = matcher.fileLocation;
let fullPath: string | undefined;
if (kind === FileLocationKind.Absolute) {
fullPath = filename;
} else if ((kind === FileLocationKind.Relative) && matcher.filePrefix) {
fullPath = join(matcher.filePrefix, filename);
} else if (kind === FileLocationKind.AutoDetect) {
const matcherClone = Objects.deepClone(matcher);
matcherClone.fileLocation = FileLocationKind.Relative;
if (fileService) {
const relative = await getResource(filename, matcherClone);
let stat: IFileStat | undefined = undefined;
try {
stat = await fileService.resolve(relative);
} catch (ex) {
// Do nothing, we just need to catch file resolution errors.
}
if (stat) {
return relative;
}
}

matcherClone.fileLocation = FileLocationKind.Absolute;
return getResource(filename, matcherClone);
}
if (fullPath === undefined) {
throw new Error('FileLocationKind is not actionable. Does the matcher have a filePrefix? This should never happen.');
Expand All @@ -210,22 +233,24 @@ export interface ILineMatcher {
handle(lines: string[], start?: number): HandleResult;
}

export function createLineMatcher(matcher: ProblemMatcher): ILineMatcher {
export function createLineMatcher(matcher: ProblemMatcher, fileService?: IFileService): ILineMatcher {
let pattern = matcher.pattern;
if (Types.isArray(pattern)) {
return new MultiLineMatcher(matcher);
return new MultiLineMatcher(matcher, fileService);
} else {
return new SingleLineMatcher(matcher);
return new SingleLineMatcher(matcher, fileService);
}
}

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

abstract class AbstractLineMatcher implements ILineMatcher {
private matcher: ProblemMatcher;
private fileService?: IFileService;

constructor(matcher: ProblemMatcher) {
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
this.matcher = matcher;
this.fileService = fileService;
}

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

protected getResource(filename: string): URI {
return getResource(filename, this.matcher);
protected getResource(filename: string): Promise<URI> {
return getResource(filename, this.matcher, this.fileService);
}

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

private pattern: ProblemPattern;

constructor(matcher: ProblemMatcher) {
super(matcher);
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
super(matcher, fileService);
this.pattern = <ProblemPattern>matcher.pattern;
}

Expand Down Expand Up @@ -425,8 +450,8 @@ class MultiLineMatcher extends AbstractLineMatcher {
private patterns: ProblemPattern[];
private data: ProblemData | null;

constructor(matcher: ProblemMatcher) {
super(matcher);
constructor(matcher: ProblemMatcher, fileService?: IFileService) {
super(matcher, fileService);
this.patterns = <ProblemPattern[]>matcher.pattern;
}

Expand Down Expand Up @@ -1345,7 +1370,7 @@ export class ProblemMatcherParser extends Parser {
kind = FileLocationKind.fromString(<string>description.fileLocation);
if (kind) {
fileLocation = kind;
if (kind === FileLocationKind.Relative) {
if ((kind === FileLocationKind.Relative) || (kind === FileLocationKind.AutoDetect)) {
filePrefix = '${workspaceFolder}';
}
}
Expand All @@ -1355,7 +1380,7 @@ export class ProblemMatcherParser extends Parser {
kind = FileLocationKind.fromString(values[0]);
if (values.length === 1 && kind === FileLocationKind.Absolute) {
fileLocation = kind;
} else if (values.length === 2 && kind === FileLocationKind.Relative && values[1]) {
} else if (values.length === 2 && (kind === FileLocationKind.Relative || kind === FileLocationKind.AutoDetect) && values[1]) {
fileLocation = kind;
filePrefix = values[1];
}
Expand Down Expand Up @@ -1573,7 +1598,7 @@ export namespace Schemas {
oneOf: [
{
type: 'string',
enum: ['absolute', 'relative']
enum: ['absolute', 'relative', 'autoDetect']
},
{
type: 'array',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -1297,7 +1297,7 @@ class TaskService extends Disposable implements ITaskService {
this.terminalService, this.outputService, this.panelService, this.markerService,
this.modelService, this.configurationResolverService, this.telemetryService,
this.contextService, this.environmentService,
TaskService.OutputChannelId,
TaskService.OutputChannelId, this.fileService,
(workspaceFolder: IWorkspaceFolder) => {
if (!workspaceFolder) {
return undefined;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import { IDisposable, dispose } from 'vs/base/common/lifecycle';
import { isUNC } from 'vs/base/common/extpath';

import { win32 } from 'vs/base/node/processes';

import { IFileService } from 'vs/platform/files/common/files';
import { IMarkerService, MarkerSeverity } from 'vs/platform/markers/common/markers';
import { IWorkspaceContextService, WorkbenchState, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import { IModelService } from 'vs/editor/common/services/modelService';
Expand All @@ -29,7 +29,7 @@ import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IConfigurationResolverService } from 'vs/workbench/services/configurationResolver/common/configurationResolver';
import { ITerminalService, ITerminalInstance, IShellLaunchConfig } from 'vs/workbench/contrib/terminal/common/terminal';
import { IOutputService } from 'vs/workbench/contrib/output/common/output';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEventKind, ProblemHandlingStrategy } from 'vs/workbench/contrib/tasks/common/problemCollectors';
import {
Task, CustomTask, ContributedTask, RevealKind, CommandOptions, ShellConfiguration, RuntimeType, PanelKind,
TaskEvent, TaskEventKind, ShellQuotingOptions, ShellQuoting, CommandString, CommandConfiguration, ExtensionTaskSource, TaskScope, RevealProblemKind
Expand Down Expand Up @@ -171,6 +171,7 @@ export class TerminalTaskSystem implements ITaskSystem {
private contextService: IWorkspaceContextService,
private environmentService: IWorkbenchEnvironmentService,
private outputChannelId: string,
private fileService: IFileService,
taskSystemInfoResolver: TaskSystemInfoResovler,
) {

Expand Down Expand Up @@ -509,7 +510,7 @@ export class TerminalTaskSystem implements ITaskSystem {
if (task.configurationProperties.isBackground) {
promise = new Promise<ITaskSummary>((resolve, reject) => {
const problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers);
let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService);
let watchingProblemMatcher = new WatchingProblemCollector(problemMatchers, this.markerService, this.modelService, this.fileService);
let toDispose: IDisposable[] | undefined = [];
let eventCounter: number = 0;
toDispose.push(watchingProblemMatcher.onDidStateChange((event) => {
Expand Down Expand Up @@ -554,13 +555,14 @@ export class TerminalTaskSystem implements ITaskSystem {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id));
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
const onData = terminal.onLineData((line) => {
watchingProblemMatcher.processLine(line);
if (!delayer) {
delayer = new Async.Delayer(3000);
}
delayer.trigger(() => {
watchingProblemMatcher.forceDelivery();
delayer = undefined;
return watchingProblemMatcher.processLine(line).then(() => {
if (!delayer) {
delayer = new Async.Delayer(3000);
}
delayer.trigger(() => {
watchingProblemMatcher.forceDelivery();
delayer = undefined;
});
});
});
const onExit = terminal.onExit((exitCode) => {
Expand Down Expand Up @@ -630,10 +632,10 @@ export class TerminalTaskSystem implements ITaskSystem {
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Start, task, terminal.id));
this._onDidStateChange.fire(TaskEvent.create(TaskEventKind.Active, task));
let problemMatchers = this.resolveMatchers(resolver, task.configurationProperties.problemMatchers);
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService);
let startStopProblemMatcher = new StartStopProblemCollector(problemMatchers, this.markerService, this.modelService, ProblemHandlingStrategy.Clean, this.fileService);
const registeredLinkMatchers = this.registerLinkMatchers(terminal, problemMatchers);
const onData = terminal.onLineData((line) => {
startStopProblemMatcher.processLine(line);
return startStopProblemMatcher.processLine(line);
});
const onExit = terminal.onExit((exitCode) => {
onData.dispose();
Expand Down
Loading

0 comments on commit c541056

Please sign in to comment.