Fixes #29332: Improve workflow to assign a problem matcher to a build task.

This commit is contained in:
Dirk Baeumer
2017-06-23 17:03:25 +02:00
parent 333d658102
commit 4d645781c8
10 changed files with 151 additions and 48 deletions

View File

@@ -6,9 +6,11 @@
import nls = require('vs/nls');
import { TPromise } from 'vs/base/common/winjs.base';
import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher';
import { Task, TaskGroup } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
@@ -25,12 +27,14 @@ class TaskEntry extends base.TaskEntry {
return false;
}
let task = this._task;
this.taskService.run(task);
if (task.command.presentation.focus) {
this.quickOpenService.close();
return false;
if (task.problemMatchers === void 0 || task.problemMatchers.length === 0) {
this.attachProblemMatcher(task).then((task) => {
this.doRun(task);
});
return true;
} else {
return this.doRun(task);
}
return true;
}
}
@@ -47,7 +51,7 @@ export class QuickOpenHandler extends base.QuickOpenHandler {
}
protected getTasks(): TPromise<Task[]> {
return this.taskService.getTasksForGroup(TaskGroup.Build);
return ProblemMatcherRegistry.onReady().then(() => this.taskService.getTasksForGroup(TaskGroup.Build));
}
protected createEntry(task: Task, highlights: Model.IHighlight[]): base.TaskEntry {

View File

@@ -9,16 +9,22 @@ import Filters = require('vs/base/common/filters');
import { TPromise } from 'vs/base/common/winjs.base';
import { Action, IAction } from 'vs/base/common/actions';
import { IStringDictionary } from 'vs/base/common/collections';
import * as Objects from 'vs/base/common/objects';
import Quickopen = require('vs/workbench/browser/quickopen');
import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { IQuickOpenService, IPickOpenEntry } from 'vs/platform/quickOpen/common/quickOpen';
import { ProblemMatcherRegistry, NamedProblemMatcher } from 'vs/platform/markers/common/problemMatcher';
import { Task, TaskSourceKind } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import { ActionBarContributor, ContributableActionProvider } from 'vs/workbench/browser/actions';
interface ProblemMatcherPickEntry extends IPickOpenEntry {
matcher: NamedProblemMatcher;
}
export class TaskEntry extends Model.QuickOpenEntry {
constructor(protected taskService: ITaskService, protected quickOpenService: IQuickOpenService, protected _task: Task, highlights: Model.IHighlight[] = []) {
@@ -36,6 +42,44 @@ export class TaskEntry extends Model.QuickOpenEntry {
public get task(): Task {
return this._task;
}
protected attachProblemMatcher(task: Task): TPromise<Task> {
let entries: ProblemMatcherPickEntry[] = [];
for (let key of ProblemMatcherRegistry.keys()) {
let matcher = ProblemMatcherRegistry.get(key);
if (matcher.name === matcher.label) {
entries.push({ label: matcher.name, matcher: matcher });
} else {
entries.push({ label: nls.localize('entries', '{0} [${1}]', matcher.label, matcher.name), matcher: matcher });
}
}
if (entries.length > 0) {
entries.push({ label: 'Continue without scanning the build output', separator: { border: true }, matcher: undefined });
return this.quickOpenService.pick(entries, {
placeHolder: nls.localize('selectProblemMatcher', 'Select for which kind of errors and warnings to scan the build output')
}).then((selected) => {
if (selected && selected.matcher) {
let newTask = Objects.deepClone(task);
let matcherReference = `$${selected.matcher.name}`;
newTask.problemMatchers = [matcherReference];
this.taskService.customize(task, { problemMatcher: [matcherReference] }, true);
return newTask;
} else {
return task;
}
});
}
return TPromise.as(task);
}
protected doRun(task: Task): boolean {
this.taskService.run(task);
if (task.command.presentation.focus) {
this.quickOpenService.close();
return false;
}
return true;
}
}
export class TaskGroupEntry extends Model.QuickOpenEntryGroup {
@@ -147,7 +191,7 @@ class CustomizeTaskAction extends Action {
}
public run(context: any): TPromise<any> {
return this.taskService.customize(this.task, true).then(() => {
return this.taskService.customize(this.task, undefined, true).then(() => {
this.quickOpenService.close();
});
}

View File

@@ -10,7 +10,7 @@ import QuickOpen = require('vs/base/parts/quickopen/common/quickOpen');
import Model = require('vs/base/parts/quickopen/browser/quickOpenModel');
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
import { Task } from 'vs/workbench/parts/tasks/common/tasks';
import { Task, TaskGroup } from 'vs/workbench/parts/tasks/common/tasks';
import { ITaskService } from 'vs/workbench/parts/tasks/common/taskService';
import { IExtensionService } from 'vs/platform/extensions/common/extensions';
@@ -27,12 +27,12 @@ class TaskEntry extends base.TaskEntry {
return false;
}
let task = this._task;
this.taskService.run(task);
if (task.command.presentation.focus) {
this.quickOpenService.close();
return false;
if (task.group === TaskGroup.Build && ((task.problemMatchers === void 0) || task.problemMatchers.length === 0)) {
this.attachProblemMatcher(task).then(task => this.doRun(task));
return true;
} else {
return this.doRun(task);
}
return true;
}
}

View File

@@ -45,7 +45,7 @@ export interface ITaskService extends IEventEmitter {
getTasksForGroup(group: string): TPromise<Task[]>;
getRecentlyUsedTasks(): LinkedMap<string, string>;
customize(task: Task, openConfig?: boolean): TPromise<void>;
customize(task: Task, properties?: { problemMatcher: string | string[] }, openConfig?: boolean): TPromise<void>;
registerTaskProvider(handle: number, taskProvider: ITaskProvider): void;
unregisterTaskProvider(handle: number): boolean;

View File

@@ -362,6 +362,10 @@ export enum ExecutionEngine {
Terminal = 2
}
export namespace ExecutionEngine {
export const _default: ExecutionEngine = ExecutionEngine.Terminal;
}
export enum JsonSchemaVersion {
V0_1_0 = 1,
V2_0_0 = 2

View File

@@ -610,7 +610,7 @@ class TaskService extends EventEmitter implements ITaskService {
? ExecutionEngine.Terminal
: this._taskSystem instanceof ProcessTaskSystem
? ExecutionEngine.Process
: undefined;
: ExecutionEngine._default;
if (currentExecutionEngine !== this.getExecutionEngine()) {
this.messageService.show(Severity.Info, nls.localize('TaskSystem.noHotSwap', 'Changing the task execution engine requires restarting VS Code. The change is ignored.'));
}
@@ -820,7 +820,7 @@ class TaskService extends EventEmitter implements ITaskService {
return { configured, detected };
}
public customize(task: Task, openConfig: boolean = false): TPromise<void> {
public customize(task: Task, properties?: { problemMatcher: string | string[] }, openConfig?: boolean): TPromise<void> {
if (!ContributedTask.is(task)) {
return TPromise.as<void>(undefined);
}
@@ -836,9 +836,19 @@ class TaskService extends EventEmitter implements ITaskService {
delete identifier['_key'];
Object.keys(identifier).forEach(key => customizes[key] = identifier[key]);
if (task.problemMatchers === void 0 || task.problemMatchers.length === 0) {
customizes.problemMatcher = [];
if (properties) {
for (let property of Object.getOwnPropertyNames(properties)) {
let value = properties[property];
if (value !== void 0 && value !== null) {
customizes[property] = value;
}
}
} else {
if (task.problemMatchers === void 0 || task.problemMatchers.length === 0) {
customizes.problemMatcher = [];
}
}
if (!fileConfig) {
fileConfig = {
version: '2.0.0',
@@ -901,6 +911,8 @@ class TaskService extends EventEmitter implements ITaskService {
return undefined;
}
// We can only have extension tasks if we are in version 2.0.0. Then we can even run
// multiple build tasks.
if (extensionTasks.length === 1) {
return { task: extensionTasks[0], resolver };
} else {
@@ -1152,7 +1164,7 @@ class TaskService extends EventEmitter implements ITaskService {
if (hasParseErrors) {
return TPromise.as({ set: undefined, hasErrors: true, configurations: undefined });
}
let engine = TaskConfig.ExecutionEngine._default;
let engine = ExecutionEngine._default;
if (config) {
engine = TaskConfig.ExecutionEngine.from(config);
if (engine === ExecutionEngine.Process) {
@@ -1229,7 +1241,7 @@ class TaskService extends EventEmitter implements ITaskService {
private getExecutionEngine(): ExecutionEngine {
let { config } = this.getConfiguration();
if (!config) {
return ExecutionEngine.Terminal;
return ExecutionEngine._default;
}
return TaskConfig.ExecutionEngine.from(config);
}
@@ -1429,9 +1441,8 @@ class TaskService extends EventEmitter implements ITaskService {
return;
}
this.getTasksForGroup(TaskGroup.Build).then((tasks) => {
let { configured, detected } = this.splitTasks(tasks);
let total = configured.length + detected.length;
if (total === 0) {
if (tasks.length === 0) {
// Show no build task message.
return;
}
this.quickOpenService.show('build task ');

View File

@@ -1473,8 +1473,6 @@ namespace Globals {
export namespace ExecutionEngine {
export const _default: Tasks.ExecutionEngine = Tasks.ExecutionEngine.Process;
export function from(config: ExternalTaskRunnerConfiguration): Tasks.ExecutionEngine {
let runner = config.runner || config._runner;
let result: Tasks.ExecutionEngine;
@@ -1497,12 +1495,11 @@ export namespace ExecutionEngine {
throw new Error('Shouldn\'t happen.');
}
}
}
export namespace JsonSchemaVersion {
export const _default: Tasks.JsonSchemaVersion = Tasks.JsonSchemaVersion.V0_1_0;
const _default: Tasks.JsonSchemaVersion = Tasks.JsonSchemaVersion.V2_0_0;
export function from(config: ExternalTaskRunnerConfiguration): Tasks.JsonSchemaVersion {
let version = config.version;