Skip to content

Commit 4d64578

Browse files
committed
Fixes microsoft#29332: Improve workflow to assign a problem matcher to a build task.
1 parent 333d658 commit 4d64578

10 files changed

Lines changed: 151 additions & 48 deletions

File tree

extensions/typescript/package.json

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@
484484
"problemMatchers": [
485485
{
486486
"name": "tsc",
487+
"label": "%typescript.problemMatchers.tsc.label%",
487488
"owner": "typescript",
488489
"applyTo": "closedDocuments",
489490
"fileLocation": [
@@ -494,6 +495,7 @@
494495
},
495496
{
496497
"name": "tsc-watch",
498+
"label": "%typescript.problemMatchers.tscWatch.label%",
497499
"owner": "typescript",
498500
"applyTo": "closedDocuments",
499501
"fileLocation": [

extensions/typescript/package.nls.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,5 +39,7 @@
3939
"typescript.npm": "Specifies the path to the NPM executable used for Automatic Type Acquisition. Requires TypeScript >= 2.3.4.",
4040
"typescript.check.npmIsInstalled": "Check if NPM is installed for Automatic Type Acquisition.",
4141
"javascript.nameSuggestions": "Enable/disable including unique names from the file in JavaScript suggestion lists.",
42-
"typescript.tsc.autoDetect": "Controls whether auto detection of tsc tasks is on or off."
42+
"typescript.tsc.autoDetect": "Controls whether auto detection of tsc tasks is on or off.",
43+
"typescript.problemMatchers.tsc.label": "TypeScript problems",
44+
"typescript.problemMatchers.tscWatch.label": "TypeScript problems (watch mode)"
4345
}

src/vs/platform/markers/common/problemMatcher.ts

Lines changed: 58 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -115,10 +115,12 @@ export interface ProblemMatcher {
115115

116116
export interface NamedProblemMatcher extends ProblemMatcher {
117117
name: string;
118+
label: string;
118119
}
119120

120121
export interface NamedMultiLineProblemPattern {
121122
name: string;
123+
label: string;
122124
patterns: MultiLineProblemPattern;
123125
}
124126

@@ -495,6 +497,11 @@ export namespace Config {
495497
* The name of the problem pattern.
496498
*/
497499
name: string;
500+
501+
/**
502+
* A human readable label
503+
*/
504+
label?: string;
498505
}
499506

500507
export namespace NamedProblemPattern {
@@ -518,6 +525,11 @@ export namespace Config {
518525
*/
519526
name: string;
520527

528+
/**
529+
* A human readable label
530+
*/
531+
label?: string;
532+
521533
/**
522534
* The actual patterns
523535
*/
@@ -663,6 +675,11 @@ export namespace Config {
663675
* problem matchter from within a task.
664676
*/
665677
name?: string;
678+
679+
/**
680+
* A human reable label.
681+
*/
682+
label?: string;
666683
}
667684

668685
export function isNamedProblemMatcher(value: ProblemMatcher): value is NamedProblemMatcher {
@@ -704,6 +721,7 @@ class ProblemPatternParser extends Parser {
704721
private createNamedMultiLineProblemPattern(value: Config.NamedMultiLineProblemPattern): NamedMultiLineProblemPattern {
705722
let result = {
706723
name: value.name,
724+
label: value.label ? value.label : value.name,
707725
patterns: this.createMultiLineProblemPattern(value.patterns)
708726
};
709727
return result.patterns ? result : null;
@@ -1229,7 +1247,8 @@ export class ProblemMatcherParser extends Parser {
12291247
}
12301248
}
12311249
if (Config.isNamedProblemMatcher(description)) {
1232-
(<NamedProblemMatcher>result).name = description.name;
1250+
(result as NamedProblemMatcher).name = description.name;
1251+
(result as NamedProblemMatcher).label = Types.isString(description.label) ? description.label : description.name;
12331252
}
12341253
return result;
12351254
}
@@ -1466,9 +1485,13 @@ export namespace Schemas {
14661485

14671486
export const NamedProblemMatcher: IJSONSchema = Objects.clone(ProblemMatcher);
14681487
NamedProblemMatcher.properties = Objects.clone(NamedProblemMatcher.properties);
1469-
NamedProblemMatcher.properties['name'] = {
1488+
NamedProblemMatcher.properties.name = {
14701489
type: 'string',
1471-
description: localize('NamedProblemMatcherSchema.name', 'The name of the problem matcher.')
1490+
description: localize('NamedProblemMatcherSchema.name', 'The name of the problem matcher used to refer to it.')
1491+
};
1492+
NamedProblemMatcher.properties.label = {
1493+
type: 'string',
1494+
description: localize('NamedProblemMatcherSchema.label', 'A human readable label of the problem matcher.')
14721495
};
14731496
}
14741497

@@ -1481,14 +1504,14 @@ let problemMatchersExtPoint = ExtensionsRegistry.registerExtensionPoint<Config.N
14811504
export interface IProblemMatcherRegistry {
14821505
onReady(): TPromise<void>;
14831506
exists(name: string): boolean;
1484-
get(name: string): ProblemMatcher;
1485-
values(): ProblemMatcher[];
1507+
get(name: string): NamedProblemMatcher;
1508+
values(): NamedProblemMatcher[];
14861509
keys(): string[];
14871510
}
14881511

14891512
class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {
14901513

1491-
private matchers: IStringDictionary<ProblemMatcher>;
1514+
private matchers: IStringDictionary<NamedProblemMatcher>;
14921515
private readyPromise: TPromise<void>;
14931516

14941517
constructor() {
@@ -1503,7 +1526,7 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {
15031526
for (let matcher of problemMatchers) {
15041527
let result = parser.parse(matcher);
15051528
if (result && isNamedProblemMatcher(result)) {
1506-
this.add(result.name, result);
1529+
this.add(result);
15071530
}
15081531
}
15091532
});
@@ -1522,11 +1545,11 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {
15221545
return this.readyPromise;
15231546
}
15241547

1525-
public add(name: string, matcher: ProblemMatcher): void {
1526-
this.matchers[name] = matcher;
1548+
public add(matcher: NamedProblemMatcher): void {
1549+
this.matchers[matcher.name] = matcher;
15271550
}
15281551

1529-
public get(name: string): ProblemMatcher {
1552+
public get(name: string): NamedProblemMatcher {
15301553
return this.matchers[name];
15311554
}
15321555

@@ -1542,64 +1565,80 @@ class ProblemMatcherRegistryImpl implements IProblemMatcherRegistry {
15421565
return Object.keys(this.matchers);
15431566
}
15441567

1545-
public values(): ProblemMatcher[] {
1568+
public values(): NamedProblemMatcher[] {
15461569
return Object.keys(this.matchers).map(key => this.matchers[key]);
15471570
}
15481571

15491572
private fillDefaults(): void {
1550-
this.add('msCompile', {
1573+
this.add({
1574+
name: 'msCompile',
1575+
label: localize('msCompile', 'Microsoft compiler problems'),
15511576
owner: 'msCompile',
15521577
applyTo: ApplyToKind.allDocuments,
15531578
fileLocation: FileLocationKind.Absolute,
15541579
pattern: ProblemPatternRegistry.get('msCompile')
15551580
});
15561581

1557-
this.add('lessCompile', {
1582+
this.add({
1583+
name: 'lessCompile',
1584+
label: localize('lessCompile', 'Less problems'),
15581585
owner: 'lessCompile',
15591586
applyTo: ApplyToKind.allDocuments,
15601587
fileLocation: FileLocationKind.Absolute,
15611588
pattern: ProblemPatternRegistry.get('lessCompile'),
15621589
severity: Severity.Error
15631590
});
15641591

1565-
this.add('gulp-tsc', {
1592+
this.add({
1593+
name: 'gulp-tsc',
1594+
label: localize('gulp-tsc', 'Gulp TSC Problems'),
15661595
owner: 'typescript',
15671596
applyTo: ApplyToKind.closedDocuments,
15681597
fileLocation: FileLocationKind.Relative,
15691598
filePrefix: '${cwd}',
15701599
pattern: ProblemPatternRegistry.get('gulp-tsc')
15711600
});
15721601

1573-
this.add('jshint', {
1602+
this.add({
1603+
name: 'jshint',
1604+
label: localize('jshint', 'JSHint problems'),
15741605
owner: 'jshint',
15751606
applyTo: ApplyToKind.allDocuments,
15761607
fileLocation: FileLocationKind.Absolute,
15771608
pattern: ProblemPatternRegistry.get('jshint')
15781609
});
15791610

1580-
this.add('jshint-stylish', {
1611+
this.add({
1612+
name: 'jshint-stylish',
1613+
label: localize('jshint-stylish', 'JSHint stylish problems'),
15811614
owner: 'jshint',
15821615
applyTo: ApplyToKind.allDocuments,
15831616
fileLocation: FileLocationKind.Absolute,
15841617
pattern: ProblemPatternRegistry.get('jshint-stylish')
15851618
});
15861619

1587-
this.add('eslint-compact', {
1620+
this.add({
1621+
name: 'eslint-compact',
1622+
label: localize('eslint-compact', 'ESLint compact problems'),
15881623
owner: 'eslint',
15891624
applyTo: ApplyToKind.allDocuments,
15901625
fileLocation: FileLocationKind.Relative,
15911626
filePrefix: '${cwd}',
15921627
pattern: ProblemPatternRegistry.get('eslint-compact')
15931628
});
15941629

1595-
this.add('eslint-stylish', {
1630+
this.add({
1631+
name: 'eslint-stylish',
1632+
label: localize('eslint-stylish', 'ESLint stylish problems'),
15961633
owner: 'eslint',
15971634
applyTo: ApplyToKind.allDocuments,
15981635
fileLocation: FileLocationKind.Absolute,
15991636
pattern: ProblemPatternRegistry.get('eslint-stylish')
16001637
});
16011638

1602-
this.add('go', {
1639+
this.add({
1640+
name: 'go',
1641+
label: localize('go', 'Go problems'),
16031642
owner: 'go',
16041643
applyTo: ApplyToKind.allDocuments,
16051644
fileLocation: FileLocationKind.Relative,

src/vs/workbench/parts/tasks/browser/buildQuickOpen.ts

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,11 @@
66

77
import nls = require('vs/nls');
88
import { TPromise } from 'vs/base/common/winjs.base';
9+
910
import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
1011
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
1112
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
13+
import { ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher';
1214

1315
import { Task, TaskGroup } from 'vs/workbench/parts/tasks/common/tasks';
1416
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
@@ -25,12 +27,14 @@ class TaskEntry extends base.TaskEntry {
2527
return false;
2628
}
2729
let task = this._task;
28-
this.taskService.run(task);
29-
if (task.command.presentation.focus) {
30-
this.quickOpenService.close();
31-
return false;
30+
if (task.problemMatchers === void 0 || task.problemMatchers.length === 0) {
31+
this.attachProblemMatcher(task).then((task) => {
32+
this.doRun(task);
33+
});
34+
return true;
35+
} else {
36+
return this.doRun(task);
3237
}
33-
return true;
3438
}
3539
}
3640

@@ -47,7 +51,7 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
4751
}
4852

4953
protected getTasks(): TPromise<Task[]> {
50-
return this.taskService.getTasksForGroup(TaskGroup.Build);
54+
return ProblemMatcherRegistry.onReady().then(() => this.taskService.getTasksForGroup(TaskGroup.Build));
5155
}
5256

5357
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {

src/vs/workbench/parts/tasks/browser/quickOpen.ts

Lines changed: 46 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,22 @@ import Filters = require('vs/base/common/filters');
99
import { TPromise } from 'vs/base/common/winjs.base';
1010
import { Action, IAction } from 'vs/base/common/actions';
1111
import { IStringDictionary } from 'vs/base/common/collections';
12+
import * as Objects from 'vs/base/common/objects';
1213

1314
import Quickopen = require('vs/workbench/browser/quickopen');
1415
import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
1516
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
16-
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
17+
import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
18+
import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
1719

1820
import { Task, TaskSourceKind } from 'vs/workbench/parts/tasks/common/tasks';
1921
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
2022
import { ActionBarContributor, ContributableActionProvider } from 'vs/workbench/browser/actions';
2123

24+
interface ProblemMatcherPickEntry extends IPickOpenEntry {
25+
matcher: NamedProblemMatcher;
26+
}
27+
2228
export class TaskEntry extends Model.QuickOpenEntry {
2329

2430
constructor(protected taskService: ITaskService, protected quickOpenService: IQuickOpenService, protected _task: Task, highlights: Model.IHighlight[] = []) {
@@ -36,6 +42,44 @@ export class TaskEntry extends Model.QuickOpenEntry {
3642
public get task(): Task {
3743
return this._task;
3844
}
45+
46+
protected attachProblemMatcher(task: Task): TPromise<Task> {
47+
let entries: ProblemMatcherPickEntry[] = [];
48+
for (let key of ProblemMatcherRegistry.keys()) {
49+
let matcher = ProblemMatcherRegistry.get(key);
50+
if (matcher.name === matcher.label) {
51+
entries.push({ label: matcher.name, matcher: matcher });
52+
} else {
53+
entries.push({ label: nls.localize('entries', '{0} [${1}]', matcher.label, matcher.name), matcher: matcher });
54+
}
55+
}
56+
if (entries.length > 0) {
57+
entries.push({ label: 'Continue without scanning the build output', separator: { border: true }, matcher: undefined });
58+
return this.quickOpenService.pick(entries, {
59+
placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the build output')
60+
}).then((selected) => {
61+
if (selected && selected.matcher) {
62+
let newTask = Objects.deepClone(task);
63+
let matcherReference = `$${selected.matcher.name}`;
64+
newTask.problemMatchers = [matcherReference];
65+
this.taskService.customize(task, { problemMatcher: [matcherReference] }, true);
66+
return newTask;
67+
} else {
68+
return task;
69+
}
70+
});
71+
}
72+
return TPromise.as(task);
73+
}
74+
75+
protected doRun(task: Task): boolean {
76+
this.taskService.run(task);
77+
if (task.command.presentation.focus) {
78+
this.quickOpenService.close();
79+
return false;
80+
}
81+
return true;
82+
}
3983
}
4084

4185
export class TaskGroupEntry extends Model.QuickOpenEntryGroup {
@@ -147,7 +191,7 @@ class CustomizeTaskAction extends Action {
147191
}
148192

149193
public run(context: any): TPromise<any> {
150-
return this.taskService.customize(this.task, true).then(() => {
194+
return this.taskService.customize(this.task, undefined, true).then(() => {
151195
this.quickOpenService.close();
152196
});
153197
}

src/vs/workbench/parts/tasks/browser/taskQuickOpen.ts

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
1010
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
1111
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
1212

13-
import { Task } from 'vs/workbench/parts/tasks/common/tasks';
13+
import { Task, TaskGroup } from 'vs/workbench/parts/tasks/common/tasks';
1414
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
1515
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
1616

@@ -27,12 +27,12 @@ class TaskEntry extends base.TaskEntry {
2727
return false;
2828
}
2929
let task = this._task;
30-
this.taskService.run(task);
31-
if (task.command.presentation.focus) {
32-
this.quickOpenService.close();
33-
return false;
30+
if (task.group === TaskGroup.Build && ((task.problemMatchers === void 0) || task.problemMatchers.length === 0)) {
31+
this.attachProblemMatcher(task).then(task => this.doRun(task));
32+
return true;
33+
} else {
34+
return this.doRun(task);
3435
}
35-
return true;
3636
}
3737
}
3838

src/vs/workbench/parts/tasks/common/taskService.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ export interface ITaskService extends IEventEmitter {
4545
getTasksForGroup(group: string): TPromise<Task[]>;
4646
getRecentlyUsedTasks(): LinkedMap<string, string>;
4747

48-
customize(task: Task, openConfig?: boolean): TPromise<void>;
48+
customize(task: Task, properties?: { problemMatcher: string | string[] }, openConfig?: boolean): TPromise<void>;
4949

5050
registerTaskProvider(handle: number, taskProvider: ITaskProvider): void;
5151
unregisterTaskProvider(handle: number): boolean;

0 commit comments

Comments
 (0)