From f084c2a1e4e764a5a153fda89452f528e50d8b2e Mon Sep 17 00:00:00 2001 From: Dirk Baeumer Date: Thu, 18 May 2017 21:52:00 +0200 Subject: [PATCH] Fixes #23435: Proposed Task Api Feedback --- .../platform/markers/common/problemMatcher.ts | 54 +- src/vs/vscode.d.ts | 520 ++++++++++++++++++ src/vs/vscode.proposed.d.ts | 515 ----------------- src/vs/workbench/api/node/extHostTask.ts | 7 +- src/vs/workbench/api/node/extHostTypes.ts | 114 ++-- src/vs/workbench/parts/tasks/common/tasks.ts | 2 +- .../electron-browser/task.contribution.ts | 22 +- .../electron-browser/terminalTaskSystem.ts | 24 +- .../parts/tasks/node/processTaskSystem.ts | 22 +- .../tasks/test/node/configuration.test.ts | 37 +- 10 files changed, 688 insertions(+), 629 deletions(-) diff --git a/src/vs/platform/markers/common/problemMatcher.ts b/src/vs/platform/markers/common/problemMatcher.ts index b3120929f9c80..b42f0f2a90667 100644 --- a/src/vs/platform/markers/common/problemMatcher.ts +++ b/src/vs/platform/markers/common/problemMatcher.ts @@ -552,7 +552,7 @@ export namespace Config { /** * A description to track the start and end of a watching task. */ - export interface WatchingMatcher { + export interface BackgroundMonitor { /** * If set to true the watcher is in active mode when the task @@ -648,7 +648,11 @@ export namespace Config { */ watchedTaskEndsRegExp?: string; - watching?: WatchingMatcher; + /** + * @deprecated Use background instead. + */ + watching?: BackgroundMonitor; + background?: BackgroundMonitor; } export type ProblemMatcherType = string | ProblemMatcher | (string | ProblemMatcher)[]; @@ -1264,15 +1268,15 @@ export class ProblemMatcherParser extends Parser { }; return; } - if (Types.isUndefinedOrNull(external.watching)) { + let backgroundMonitor = external.background || external.watching; + if (Types.isUndefinedOrNull(backgroundMonitor)) { return; } - let watching = external.watching; - let begins: WatchingPattern = this.createWatchingPattern(watching.beginsPattern); - let ends: WatchingPattern = this.createWatchingPattern(watching.endsPattern); + let begins: WatchingPattern = this.createWatchingPattern(backgroundMonitor.beginsPattern); + let ends: WatchingPattern = this.createWatchingPattern(backgroundMonitor.endsPattern); if (begins && ends) { internal.watching = { - activeOnStart: Types.isBoolean(watching.activeOnStart) ? watching.activeOnStart : false, + activeOnStart: Types.isBoolean(backgroundMonitor.activeOnStart) ? backgroundMonitor.activeOnStart : false, beginsPattern: begins, endsPattern: ends }; @@ -1325,7 +1329,7 @@ export namespace Schemas { properties: { regexp: { type: 'string', - description: localize('WatchingPatternSchema.regexp', 'The regular expression to detect the begin or end of a watching task.') + description: localize('WatchingPatternSchema.regexp', 'The regular expression to detect the begin or end of a background task.') }, file: { type: 'integer', @@ -1385,9 +1389,40 @@ export namespace Schemas { ], description: localize('ProblemMatcherSchema.fileLocation', 'Defines how file names reported in a problem pattern should be interpreted.') }, + background: { + type: 'object', + additionalProperties: false, + description: localize('ProblemMatcherSchema.background', 'Patterns to track the begin and end of a matcher active on a background task.'), + properties: { + activeOnStart: { + type: 'boolean', + description: localize('ProblemMatcherSchema.background.activeOnStart', 'If set to true the background monitor is in active mode when the task starts. This is equals of issuing a line that matches the beginPattern') + }, + beginsPattern: { + oneOf: [ + { + type: 'string' + }, + Schemas.WatchingPattern + ], + description: localize('ProblemMatcherSchema.background.beginsPattern', 'If matched in the output the start of a background task is signaled.') + }, + endsPattern: { + oneOf: [ + { + type: 'string' + }, + Schemas.WatchingPattern + ], + description: localize('ProblemMatcherSchema.background.endsPattern', 'If matched in the output the end of a background task is signaled.') + } + } + }, watching: { type: 'object', additionalProperties: false, + deprecationMessage: localize('ProblemMatcherSchema.watching.deprecated', 'The watching property is deprecated. Use background instead.'), + description: localize('ProblemMatcherSchema.watching', 'Patterns to track the begin and end of a watching matcher.'), properties: { activeOnStart: { type: 'boolean', @@ -1411,8 +1446,7 @@ export namespace Schemas { ], description: localize('ProblemMatcherSchema.watching.endsPattern', 'If matched in the output the end of a watching task is signaled.') } - }, - description: localize('ProblemMatcherSchema.watching', 'Patterns to track the begin and end of a watching pattern.') + } } } }; diff --git a/src/vs/vscode.d.ts b/src/vs/vscode.d.ts index 3faf70275df3d..190042afeeaf8 100644 --- a/src/vs/vscode.d.ts +++ b/src/vs/vscode.d.ts @@ -3489,6 +3489,526 @@ declare module 'vscode' { update(key: string, value: any): Thenable; } + /** + * Defines a problem pattern + */ + export interface ProblemPattern { + + /** + * The regular expression to find a problem in the console output of an + * executed task. + */ + regexp: RegExp; + + /** + * The match group index of the filename. + * + * Defaults to 1 if omitted. + */ + file?: number; + + /** + * The match group index of the problems's location. Valid location + * patterns are: (line), (line,column) and (startLine,startColumn,endLine,endColumn). + * If omitted the line and colum properties are used. + */ + location?: number; + + /** + * The match group index of the problem's line in the source file. + * + * Defaults to 2 if omitted. + */ + line?: number; + + /** + * The match group index of the problem's character in the source file. + * + * Defaults to 3 if omitted. + */ + character?: number; + + /** + * The match group index of the problem's end line in the source file. + * + * Defaults to undefined. No end line is captured. + */ + endLine?: number; + + /** + * The match group index of the problem's end character in the source file. + * + * Defaults to undefined. No end column is captured. + */ + endCharacter?: number; + + /** + * The match group index of the problem's severity. + * + * Defaults to undefined. In this case the problem matcher's severity + * is used. + */ + severity?: number; + + /** + * The match group index of the problems's code. + * + * Defaults to undefined. No code is captured. + */ + code?: number; + + /** + * The match group index of the message. If omitted it defaults + * to 4 if location is specified. Otherwise it defaults to 5. + */ + message?: number; + + /** + * Specifies if the last pattern in a multi line problem matcher should + * loop as long as it does match a line consequently. Only valid on the + * last problem pattern in a multi line problem matcher. + */ + loop?: boolean; + } + + /** + * A multi line problem pattern. + */ + export type MultiLineProblemPattern = ProblemPattern[]; + + /** + * The way how the file location is interpreted + */ + export enum FileLocationKind { + /** + * VS Code should decide based on whether the file path found in the + * output is absolute or relative. A relative file path will be treated + * relative to the workspace root. + */ + Auto = 1, + + /** + * Always treat the file path relative. + */ + Relative = 2, + + /** + * Always treat the file path absolute. + */ + Absolute = 3 + } + + /** + * Controls to which kind of documents problems are applied. + */ + export enum ApplyToKind { + /** + * Problems are applied to all documents. + */ + AllDocuments = 1, + + /** + * Problems are applied to open documents only. + */ + OpenDocuments = 2, + + + /** + * Problems are applied to closed documents only. + */ + ClosedDocuments = 3 + } + + + /** + * A background monitor pattern + */ + export interface BackgroundPattern { + /** + * The actual regular expression + */ + regexp: RegExp; + + /** + * The match group index of the filename. If provided the expression + * is matched for that file only. + */ + file?: number; + } + + /** + * A description to control the activity of a problem matcher + * watching a background task. + */ + export interface BackgroundMonitor { + /** + * If set to true the monitor is in active mode when the task + * starts. This is equals of issuing a line that matches the + * beginPattern. + */ + activeOnStart?: boolean; + + /** + * If matched in the output the start of a background activity is signaled. + */ + beginsPattern: RegExp | BackgroundPattern; + + /** + * If matched in the output the end of a background activity is signaled. + */ + endsPattern: RegExp | BackgroundPattern; + } + + /** + * Defines a problem matcher + */ + export interface ProblemMatcher { + /** + * The owner of a problem. Defaults to a generated id + * if omitted. + */ + owner?: string; + + /** + * The type of documents problems detected by this matcher + * apply to. Default to `ApplyToKind.AllDocuments` if omitted. + */ + applyTo?: ApplyToKind; + + /** + * How a file location recognized by a matcher should be interpreted. If omitted the file location + * if `FileLocationKind.Auto`. + */ + fileLocation?: FileLocationKind | string; + + /** + * The actual pattern used by the problem matcher. + */ + pattern: ProblemPattern | MultiLineProblemPattern; + + /** + * The default severity of a detected problem in the output. Used + * if the `ProblemPattern` doesn't define a severity match group. + */ + severity?: DiagnosticSeverity; + + /** + * A background monitor for tasks that are running in the background. + */ + backgound?: BackgroundMonitor; + } + + /** + * Controls the behaviour of the terminal's visibility. + */ + export enum RevealKind { + /** + * Always brings the terminal to front if the task is executed. + */ + Always = 1, + + /** + * Only brings the terminal to front if a problem is detected executing the task + * (e.g. the task couldn't be started because). + */ + Silent = 2, + + /** + * The terminal never comes to front when the task is executed. + */ + Never = 3 + } + + /** + * Controls terminal specific behaviour. + */ + export interface TerminalBehaviour { + /** + * Controls whether the terminal executing a task is brought to front or not. + * Defaults to `RevealKind.Always`. + */ + reveal?: RevealKind; + + /** + * Controls whether the command is echoed in the terminal or not. + */ + echo?: boolean; + } + + export interface ProcessOptions { + /** + * The current working directory of the executed program or shell. + * If omitted VSCode's current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed program or shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } + + export namespace TaskGroup { + /** + * The clean task group + */ + export const Clean: 'clean'; + /** + * The build task group + */ + export const Build: 'build'; + /** + * The rebuild all task group + */ + export const RebuildAll: 'rebuildAll'; + /** + * The test task group + */ + export const Test: 'test'; + } + + /** + * The supported task groups. + */ + export type TaskGroup = 'clean' | 'build' | 'rebuildAll' | 'test'; + + /** + * The ProblemMatchers type definition. + */ + export type ProblemMatchers = string | ProblemMatcher | (string | ProblemMatcher)[]; + + /** + * A task that starts an external process. + */ + export class ProcessTask { + + /** + * Creates a process task. + * + * @param name the task's name. Is presented in the user interface. + * @param process the process to start. + * @param problemMatchers the problem matchers to use. + */ + constructor(name: string, process: string, problemMatchers?: ProblemMatchers); + + /** + * Creates a process task. + * + * @param name the task's name. Is presented in the user interface. + * @param process the process to start. + * @param args arguments to be passed to the process. + * @param problemMatchers the problem matchers to use. + */ + constructor(name: string, process: string, args: string[], problemMatchers?: ProblemMatchers); + + /** + * Creates a process task. + * + * @param name the task's name. Is presented in the user interface. + * @param process the process to start. + * @param args arguments to be passed to the process. + * @param options additional options for the started process. + * @param problemMatchers the problem matchers to use. + */ + constructor(name: string, process: string, args: string[], options: ProcessOptions, problemMatchers?: ProblemMatchers); + + /** + * The task's name + */ + readonly name: string; + + /** + * The task's identifier. If omitted the name is + * used as an identifier. + */ + identifier: string; + + /** + * Whether the task is a background task or not. + */ + isBackground: boolean; + + /** + * The process to be executed. + */ + readonly process: string; + + /** + * The arguments passed to the process. Defaults to an empty array. + */ + args: string[]; + + /** + * The task group this tasks belongs to. Defaults to undefined meaning + * that the task doesn't belong to any special group. + */ + group?: TaskGroup; + + /** + * The process options used when the process is executed. + * Defaults to an empty object literal. + */ + options: ProcessOptions; + + /** + * The terminal options. Defaults to an empty object literal. + */ + terminal: TerminalBehaviour; + + /** + * The problem matchers attached to the task. Defaults to an empty + * array. + */ + problemMatchers: (string | ProblemMatcher)[]; + } + + export type ShellOptions = { + /** + * The shell executable. + */ + executable: string; + + /** + * The arguments to be passed to the shell executable used to run the task. + */ + shellArgs?: string[]; + + /** + * The current working directory of the executed shell. + * If omitted VSCode's current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } | { + /** + * The current working directory of the executed shell. + * If omitted VSCode's current workspace root is used. + */ + cwd: string; + + /** + * The additional environment of the executed shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env?: { [key: string]: string }; + } | { + /** + * The current working directory of the executed shell. + * If omitted VSCode's current workspace root is used. + */ + cwd?: string; + + /** + * The additional environment of the executed shell. If omitted + * the parent process' environment is used. If provided it is merged with + * the parent process' environment. + */ + env: { [key: string]: string }; + }; + + /** + * A task that executes a shell command. + */ + export class ShellTask { + + /** + * Creates a shell task. + * + * @param name the task's name. Is presented in the user interface. + * @param commandLine the command line to execute. + * @param problemMatchers the problem matchers to use. + */ + constructor(name: string, commandLine: string, problemMatchers?: ProblemMatchers); + + /** + * Creates a shell task. + * + * @param name the task's name. Is presented in the user interface. + * @param commandLine the command line to execute. + * @param options additional options used when creating the shell. + * @param problemMatchers the problem matchers to use. + */ + constructor(name: string, commandLine: string, options: ShellOptions, problemMatchers?: ProblemMatchers); + + /** + * The task's name + */ + readonly name: string; + + /** + * The task's identifier. If omitted the name is + * used as an identifier. + */ + identifier: string; + + /** + * Whether the task is a background task or not. + */ + isBackground: boolean; + + /** + * The command line to execute. + */ + readonly commandLine: string; + + /** + * The task group this tasks belongs to. Defaults to undefined meaning + * that the task doesn't belong to any special group. + */ + group?: TaskGroup; + + /** + * The shell options used when the shell is executed. Defaults to an + * empty object literal. + */ + options: ShellOptions; + + /** + * The terminal options. Defaults to an empty object literal. + */ + terminal: TerminalBehaviour; + + /** + * The problem matchers attached to the task. Defaults to an empty + * array. + */ + problemMatchers: (string | ProblemMatcher)[]; + } + + export type Task = ProcessTask | ShellTask; + + /** + * A task provider allows to add tasks to the task service. + * A task provider is registerd via #workspace.registerTaskProvider. + */ + export interface TaskProvider { + /** + * Provides additional tasks. + * @param token A cancellation token. + * @return a #TaskSet + */ + provideTasks(token: CancellationToken): ProviderResult; + } + + export namespace workspace { + /** + * Register a task provider. + * + * @param provider A task provider. + * @return A [disposable](#Disposable) that unregisters this provider when being disposed. + */ + export function registerTaskProvider(provider: TaskProvider): Disposable; + } + /** * Namespace describing the environment the editor runs in. */ diff --git a/src/vs/vscode.proposed.d.ts b/src/vs/vscode.proposed.d.ts index 60d118eefc67f..5e6265c2d2077 100644 --- a/src/vs/vscode.proposed.d.ts +++ b/src/vs/vscode.proposed.d.ts @@ -7,521 +7,6 @@ declare module 'vscode' { - /** - * Defines a problem pattern - */ - export interface ProblemPattern { - - /** - * The regular expression to find a problem in the console output of an - * executed task. - */ - regexp: RegExp; - - /** - * The match group index of the filename. - * - * Defaults to 1 if omitted. - */ - file?: number; - - /** - * The match group index of the problems's location. Valid location - * patterns are: (line), (line,column) and (startLine,startColumn,endLine,endColumn). - * If omitted the line and colum properties are used. - */ - location?: number; - - /** - * The match group index of the problem's line in the source file. - * - * Defaults to 2 if omitted. - */ - line?: number; - - /** - * The match group index of the problem's character in the source file. - * - * Defaults to 3 if omitted. - */ - character?: number; - - /** - * The match group index of the problem's end line in the source file. - * - * Defaults to undefined. No end line is captured. - */ - endLine?: number; - - /** - * The match group index of the problem's end character in the source file. - * - * Defaults to undefined. No end column is captured. - */ - endCharacter?: number; - - /** - * The match group index of the problem's severity. - * - * Defaults to undefined. In this case the problem matcher's severity - * is used. - */ - severity?: number; - - /** - * The match group index of the problems's code. - * - * Defaults to undefined. No code is captured. - */ - code?: number; - - /** - * The match group index of the message. If omitted it defaults - * to 4 if location is specified. Otherwise it defaults to 5. - */ - message?: number; - - /** - * Specifies if the last pattern in a multi line problem matcher should - * loop as long as it does match a line consequently. Only valid on the - * last problem pattern in a multi line problem matcher. - */ - loop?: boolean; - } - - /** - * A multi line problem pattern. - */ - export type MultiLineProblemPattern = ProblemPattern[]; - - /** - * The way how the file location is interpreted - */ - export enum FileLocationKind { - /** - * VS Code should decide based on whether the file path found in the - * output is absolute or relative. A relative file path will be treated - * relative to the workspace root. - */ - Auto = 1, - - /** - * Always treat the file path relative. - */ - Relative = 2, - - /** - * Always treat the file path absolute. - */ - Absolute = 3 - } - - /** - * Controls to which kind of documents problems are applied. - */ - export enum ApplyToKind { - /** - * Problems are applied to all documents. - */ - AllDocuments = 1, - /** - * Problems are applied to open documents only. - */ - OpenDocuments = 2, - - /** - * Problems are applied to closed documents only. - */ - ClosedDocuments = 3 - } - - - /** - * A background monitor pattern - */ - export interface BackgroundPattern { - /** - * The actual regular expression - */ - regexp: RegExp; - - /** - * The match group index of the filename. If provided the expression - * is matched for that file only. - */ - file?: number; - } - - /** - * A description to control the activity of a problem matcher - * watching a background task. - */ - export interface BackgroundMonitor { - /** - * If set to true the monitor is in active mode when the task - * starts. This is equals of issuing a line that matches the - * beginPattern. - */ - activeOnStart?: boolean; - - /** - * If matched in the output the start of a background activity is signaled. - */ - beginsPattern: RegExp | BackgroundPattern; - - /** - * If matched in the output the end of a background activity is signaled. - */ - endsPattern: RegExp | BackgroundPattern; - } - - /** - * Defines a problem matcher - */ - export interface ProblemMatcher { - /** - * The owner of a problem. Defaults to a generated id - * if omitted. - */ - owner?: string; - - /** - * The type of documents problems detected by this matcher - * apply to. Default to `ApplyToKind.AllDocuments` if omitted. - */ - applyTo?: ApplyToKind; - - /** - * How a file location recognize by a matcher should be interpreted. If omitted the file location - * if `FileLocationKind.Auto`. - */ - fileLocation?: FileLocationKind | string; - - /** - * The actual pattern used by the problem matcher. - */ - pattern: ProblemPattern | MultiLineProblemPattern; - - /** - * The default severity of a detected problem in the output. Used - * if the `ProblemPattern` doesn't define a severity match group. - */ - severity?: DiagnosticSeverity; - - /** - * A background monitor for tasks that are running in the background. - */ - backgound?: BackgroundMonitor; - } - - /** - * Controls the behaviour of the terminal's visibility. - */ - export enum RevealKind { - /** - * Always brings the terminal to front if the task is executed. - */ - Always = 1, - - /** - * Only brings the terminal to front if a problem is detected executing the task - * (e.g. the task couldn't be started because). - */ - Silent = 2, - - /** - * The terminal never comes to front when the task is executed. - */ - Never = 3 - } - - /** - * Controls terminal specific behaviour. - */ - export interface TerminalBehaviour { - /** - * Controls whether the terminal executing a task is brought to front or not. - * Defaults to `RevealKind.Always`. - */ - reveal?: RevealKind; - - /** - * Controls whether the command is echoed in the terminal or not. - */ - echo?: boolean; - } - - - export interface ProcessOptions { - /** - * The current working directory of the executed program or shell. - * If omitted VSCode's current workspace root is used. - */ - cwd?: string; - - /** - * The additional environment of the executed program or shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - } - - export namespace TaskGroup { - /** - * The clean task group - */ - export const Clean: 'clean'; - /** - * The build task group - */ - export const Build: 'build'; - /** - * The rebuild all task group - */ - export const RebuildAll: 'rebuildAll'; - /** - * The test task group - */ - export const Test: 'test'; - } - - /** - * The supported task groups. - */ - export type TaskGroup = 'clean' | 'build' | 'rebuildAll' | 'test'; - - /** - * A task that starts an external process. - */ - export class ProcessTask { - - /** - * Creates a process task. - * - * @param name the task's name. Is presented in the user interface. - * @param process the process to start. - * @param problemMatchers the problem matchers to use. - */ - constructor(name: string, process: string, ...problemMatchers: ProblemMatcher[]); - - /** - * Creates a process task. - * - * @param name the task's name. Is presented in the user interface. - * @param process the process to start. - * @param args arguments to be passed to the process. - * @param problemMatchers the problem matchers to use. - */ - constructor(name: string, process: string, args: string[], ...problemMatchers: ProblemMatcher[]); - - /** - * Creates a process task. - * - * @param name the task's name. Is presented in the user interface. - * @param process the process to start. - * @param args arguments to be passed to the process. - * @param options additional options for the started process. - * @param problemMatchers the problem matchers to use. - */ - constructor(name: string, process: string, args: string[], options: ProcessOptions, ...problemMatchers: ProblemMatcher[]); - - /** - * The task's name - */ - readonly name: string; - - /** - * The task's identifier. If omitted the name is - * used as an identifier. - */ - identifier: string; - - /** - * Whether the task is a background task or not. - */ - isBackground: boolean; - - /** - * The process to be executed. - */ - readonly process: string; - - /** - * The arguments passed to the process. Defaults to an empty array. - */ - args: string[]; - - /** - * The task group this tasks belongs to. Defaults to undefined meaning - * that the task doesn't belong to any special group. - */ - group?: TaskGroup; - - /** - * The process options used when the process is executed. - * Defaults to an empty object literal. - */ - options: ProcessOptions; - - /** - * The terminal options. Defaults to an empty object literal. - */ - terminal: TerminalBehaviour; - - /** - * The problem matchers attached to the task. Defaults to an empty - * array. - */ - problemMatchers: ProblemMatcher[]; - } - - export type ShellOptions = { - /** - * The shell executable. - */ - executable: string; - - /** - * The arguments to be passed to the shell executable used to run the task. - */ - shellArgs?: string[]; - - /** - * The current working directory of the executed shell. - * If omitted VSCode's current workspace root is used. - */ - cwd?: string; - - /** - * The additional environment of the executed shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - } | { - /** - * The current working directory of the executed shell. - * If omitted VSCode's current workspace root is used. - */ - cwd: string; - - /** - * The additional environment of the executed shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env?: { [key: string]: string }; - } | { - /** - * The current working directory of the executed shell. - * If omitted VSCode's current workspace root is used. - */ - cwd?: string; - - /** - * The additional environment of the executed shell. If omitted - * the parent process' environment is used. If provided it is merged with - * the parent process' environment. - */ - env: { [key: string]: string }; - }; - - /** - * A task that executes a shell command. - */ - export class ShellTask { - - /** - * Creates a shell task. - * - * @param name the task's name. Is presented in the user interface. - * @param commandLine the command line to execute. - * @param problemMatchers the problem matchers to use. - */ - constructor(name: string, commandLine: string, ...problemMatchers: ProblemMatcher[]); - - /** - * Creates a shell task. - * - * @param name the task's name. Is presented in the user interface. - * @param commandLine the command line to execute. - * @param options additional options used when creating the shell. - * @param problemMatchers the problem matchers to use. - */ - constructor(name: string, commandLine: string, options: ShellOptions, ...problemMatchers: ProblemMatcher[]); - - /** - * The task's name - */ - readonly name: string; - - /** - * The task's identifier. If omitted the name is - * used as an identifier. - */ - identifier: string; - - /** - * Whether the task is a background task or not. - */ - isBackground: boolean; - - /** - * The command line to execute. - */ - readonly commandLine: string; - - /** - * The task group this tasks belongs to. Defaults to undefined meaning - * that the task doesn't belong to any special group. - */ - group?: TaskGroup; - - /** - * The shell options used when the shell is executed. Defaults to an - * empty object literal. - */ - options: ShellOptions; - - /** - * The terminal options. Defaults to an empty object literal. - */ - terminal: TerminalBehaviour; - - /** - * The problem matchers attached to the task. Defaults to an empty - * array. - */ - problemMatchers: ProblemMatcher[]; - } - - export type Task = ProcessTask | ShellTask; - - /** - * A task provider allows to add tasks to the task service. - * A task provider is registerd via #workspace.registerTaskProvider. - */ - export interface TaskProvider { - /** - * Provides additional tasks. - * @param token A cancellation token. - * @return a #TaskSet - */ - provideTasks(token: CancellationToken): ProviderResult; - } - - export namespace workspace { - /** - * Register a task provider. - * - * @param provider A task provider. - * @return A [disposable](#Disposable) that unregisters this provider when being disposed. - */ - export function registerTaskProvider(provider: TaskProvider): Disposable; - - } - export namespace window { export function sampleFunction(): Thenable; diff --git a/src/vs/workbench/api/node/extHostTask.ts b/src/vs/workbench/api/node/extHostTask.ts index 67bcbec00c9e2..c6bdc5a4d9645 100644 --- a/src/vs/workbench/api/node/extHostTask.ts +++ b/src/vs/workbench/api/node/extHostTask.ts @@ -159,13 +159,13 @@ namespace WathingMatcher { } namespace ProblemMatcher { - export function from(values: vscode.ProblemMatcher[]): Problems.ProblemMatcher[] { + export function from(values: (string | vscode.ProblemMatcher)[]): (string | Problems.ProblemMatcher)[] { if (values === void 0 || values === null) { return undefined; } - let result: Problems.ProblemMatcher[]; + let result: (string | Problems.ProblemMatcher)[]; for (let value of values) { - let converted = fromSingle(value); + let converted = typeof value === 'string' ? value : fromSingle(value); if (converted) { result.push(converted); } @@ -186,7 +186,6 @@ namespace ProblemMatcher { filePrefix: location.prefix, pattern: ProblemPattern.from(problemMatcher.pattern), severity: fromDiagnosticSeverity(problemMatcher.severity), - }; return result; } diff --git a/src/vs/workbench/api/node/extHostTypes.ts b/src/vs/workbench/api/node/extHostTypes.ts index d8279811f68e7..e52b04efaa450 100644 --- a/src/vs/workbench/api/node/extHostTypes.ts +++ b/src/vs/workbench/api/node/extHostTypes.ts @@ -1018,12 +1018,12 @@ export enum RevealKind { export class BaseTask { private _name: string; - private _problemMatchers: vscode.ProblemMatcher[]; + private _problemMatchers: (string | vscode.ProblemMatcher)[]; private _identifier: string; private _isBackground: boolean; private _terminal: vscode.TerminalBehaviour; - constructor(name: string, problemMatchers: vscode.ProblemMatcher[]) { + constructor(name: string, problemMatchers: (string | vscode.ProblemMatcher)[]) { if (typeof name !== 'string') { throw illegalArgument('name'); } @@ -1074,11 +1074,11 @@ export class BaseTask { this._terminal = value; } - get problemMatchers(): vscode.ProblemMatcher[] { + get problemMatchers(): (string | vscode.ProblemMatcher)[] { return this._problemMatchers; } - set problemMatchers(value: vscode.ProblemMatcher[]) { + set problemMatchers(value: (string | vscode.ProblemMatcher)[]) { if (!Array.isArray(value)) { value = []; } @@ -1093,6 +1093,12 @@ namespace ProblemMatcher { } } +namespace ShellOptions { + export function is(value: any): value is vscode.ShellOptions { + return value && ((typeof value.executable === 'string') || (typeof value.cwd === 'string') || !!value.env); + } +} + export namespace TaskGroup { /** * The clean task group @@ -1126,40 +1132,35 @@ export class ProcessTask extends BaseTask { private _group: vscode.TaskGroup; private _options: vscode.ProcessOptions; - private static parseArguments(restArgs: any[]): { args: string[]; options: vscode.ProcessOptions; problemMatchers: vscode.ProblemMatcher[] } { - let args: string[] = []; - let options: vscode.ProcessOptions = undefined; - let problemMatchers: vscode.ProblemMatcher[] = []; - if (!restArgs || restArgs.length === 0) { - return { args, options, problemMatchers }; - } - let current: any = restArgs[0]; - if (Array.isArray(current)) { - args = current; - restArgs.shift(); - current = restArgs[0]; - } - if (ProblemMatcher.is(current)) { - problemMatchers = restArgs; - } else if (current) { - options = current; - restArgs.shift(); - if (restArgs.length > 0) { - problemMatchers = restArgs; - } - } - return { args, options, problemMatchers }; - } - - constructor(name: string, process: string, ...problemMatchers: vscode.ProblemMatcher[]); - constructor(name: string, process: string, args: string[], ...problemMatchers: vscode.ProblemMatcher[]); - constructor(name: string, process: string, args: string[], options: vscode.ProcessOptions, ...problemMatchers: vscode.ProblemMatcher[]); - constructor(name: string, process: string, ...rest: any[]) { + constructor(name: string, process: string, args?: string[], problemMatchers?: vscode.ProblemMatchers); + constructor(name: string, process: string, args: string[] | undefined, options: vscode.ProcessOptions, problemMatchers?: vscode.ProblemMatchers); + constructor(name: string, process: string, arg3?: string[], arg4?: vscode.ProcessOptions | vscode.ProblemMatchers, arg5?: vscode.ProblemMatchers) { if (typeof process !== 'string') { throw illegalArgument('process'); } - let { args, options, problemMatchers } = ProcessTask.parseArguments(rest); - super(name, problemMatchers); + let args: string[]; + let options: vscode.ProcessOptions; + let problemMatchers: vscode.ProblemMatchers; + + args = arg3 || []; + if (arg4) { + if (Array.isArray(arg4) || typeof arg4 === 'string' || ProblemMatcher.is(arg4)) { + problemMatchers = arg4; + } else { + options = arg4; + } + } + if (arg5 && !problemMatchers) { + problemMatchers = arg5; + } + let pm: (string | vscode.ProblemMatcher)[]; + if (problemMatchers && (typeof problemMatchers === 'string' || ProblemMatcher.is(problemMatchers))) { + pm = [problemMatchers]; + } else if (Array.isArray(problemMatchers)) { + pm = problemMatchers; + } + pm = pm || []; + super(name, pm); this._process = process; this._args = args; this._options = options || Object.create(null); @@ -1203,39 +1204,32 @@ export class ProcessTask extends BaseTask { } } -export class ShellTask extends BaseTask { +export class ShellTask extends BaseTask implements vscode.ShellTask { private _commandLine: string; private _group: vscode.TaskGroup; private _options: vscode.ShellOptions; - private static parseArguments(restArgs: any[]): { options: vscode.ShellOptions; problemMatchers: vscode.ProblemMatcher[] } { - let options: vscode.ShellOptions = undefined; - let problemMatchers: vscode.ProblemMatcher[] = []; - if (!restArgs || restArgs.length === 0) { - return { options, problemMatchers }; - } - let current: any = restArgs[0]; - if (current && !ProblemMatcher.is(current)) { - options = current; - restArgs.shift(); - current = restArgs[0]; - } - if (ProblemMatcher.is(current)) { - problemMatchers = restArgs; - } - return { options, problemMatchers }; - } - - constructor(name: string, commandLine: string, ...problemMatchers: vscode.ProblemMatcher[]); - constructor(name: string, commandLine: string, options: vscode.ShellOptions, ...problemMatchers: vscode.ProblemMatcher[]); - constructor(name: string, commandLine: string, ...rest: any[]) { + constructor(name: string, commandLine: string, problemMatchers?: vscode.ProblemMatchers); + constructor(name: string, commandLine: string, options: vscode.ShellOptions, problemMatchers?: vscode.ProblemMatchers); + constructor(name: string, commandLine: string, optionsOrProblemMatchers?: vscode.ShellOptions | vscode.ProblemMatchers, problemMatchers?: vscode.ProblemMatchers) { if (typeof commandLine !== 'string') { throw illegalArgument('commandLine'); } - let { options, problemMatchers } = ShellTask.parseArguments(rest); - - super(name, problemMatchers); + let options: vscode.ShellOptions = undefined; + let pm: (string | vscode.ProblemMatcher)[]; + if (ShellOptions.is(optionsOrProblemMatchers)) { + options = optionsOrProblemMatchers; + } else { + problemMatchers = optionsOrProblemMatchers; + } + if (problemMatchers && (typeof problemMatchers === 'string' || ProblemMatcher.is(problemMatchers))) { + pm = [problemMatchers]; + } else if (Array.isArray(problemMatchers)) { + pm = problemMatchers; + } + pm = pm || []; + super(name, pm); this._commandLine = commandLine; this._options = options || Object.create(null); } diff --git a/src/vs/workbench/parts/tasks/common/tasks.ts b/src/vs/workbench/parts/tasks/common/tasks.ts index 446ae9ca08c6d..78ba0fc9fa69b 100644 --- a/src/vs/workbench/parts/tasks/common/tasks.ts +++ b/src/vs/workbench/parts/tasks/common/tasks.ts @@ -175,7 +175,7 @@ export interface Task { /** * The problem watchers to use for this task */ - problemMatchers?: ProblemMatcher[]; + problemMatchers?: (string | ProblemMatcher)[]; } export enum ExecutionEngine { diff --git a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts index a779eaf04c5e9..6dd5972868d71 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/task.contribution.ts @@ -791,17 +791,19 @@ class TaskService extends EventEmitter implements ITaskService { } private executeTask(task: Task, resolver: ITaskResolver): TPromise { - return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved - let executeResult = this.getTaskSystem().run(task, resolver); - if (executeResult.kind === TaskExecuteKind.Active) { - let active = executeResult.active; - if (active.same && active.background) { - this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame', 'The task is already active and in watch mode. To terminate the task use `F1 > terminate task`')); - } else { - throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); + return ProblemMatcherRegistry.onReady().then(() => { + return this.textFileService.saveAll().then((value) => { // make sure all dirty files are saved + let executeResult = this.getTaskSystem().run(task, resolver); + if (executeResult.kind === TaskExecuteKind.Active) { + let active = executeResult.active; + if (active.same && active.background) { + this.messageService.show(Severity.Info, nls.localize('TaskSystem.activeSame', 'The task is already active and in watch mode. To terminate the task use `F1 > terminate task`')); + } else { + throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); + } } - } - return executeResult.promise; + return executeResult.promise; + }); }); } diff --git a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts index 4e0d42260d10c..00b2950731fc9 100644 --- a/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/electron-browser/terminalTaskSystem.ts @@ -24,7 +24,8 @@ import * as TPath from 'vs/base/common/paths'; import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ProblemMatcher /*, ProblemPattern, getResource */ } from 'vs/platform/markers/common/problemMatcher'; +import { ProblemMatcher, ProblemMatcherRegistry /*, ProblemPattern, getResource */ } from 'vs/platform/markers/common/problemMatcher'; + import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; @@ -553,15 +554,22 @@ export class TerminalTaskSystem extends EventEmitter implements ITaskSystem { return value.map(s => this.resolveVariable(s)); } - private resolveMatchers(values: T[]): T[] { - if (values === void 0 || values === null) { + private resolveMatchers(values: (string | ProblemMatcher)[]): ProblemMatcher[] { + if (values === void 0 || values === null || values.length === 0) { return []; } - if (values.length === 0) { - return values; - } - let result: T[] = []; - values.forEach((matcher) => { + let result: ProblemMatcher[] = []; + values.forEach((value) => { + let matcher: ProblemMatcher; + if (Types.isString(value)) { + matcher = ProblemMatcherRegistry.get(value); + } else { + matcher = value; + } + if (!matcher) { + this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + return; + } if (!matcher.filePrefix) { result.push(matcher); } else { diff --git a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts index 513a82606f9b3..9cc26f22205dd 100644 --- a/src/vs/workbench/parts/tasks/node/processTaskSystem.ts +++ b/src/vs/workbench/parts/tasks/node/processTaskSystem.ts @@ -22,7 +22,7 @@ import { IConfigurationResolverService } from 'vs/workbench/services/configurati import { IMarkerService } from 'vs/platform/markers/common/markers'; import { IModelService } from 'vs/editor/common/services/modelService'; -import { ProblemMatcher } from 'vs/platform/markers/common/problemMatcher'; +import { ProblemMatcher, ProblemMatcherRegistry } from 'vs/platform/markers/common/problemMatcher'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { StartStopProblemCollector, WatchingProblemCollector, ProblemCollectorEvents } from 'vs/workbench/parts/tasks/common/problemCollectors'; @@ -335,12 +335,22 @@ export class ProcessTaskSystem extends EventEmitter implements ITaskSystem { return value.map(s => this.resolveVariable(s)); } - private resolveMatchers(values: T[]): T[] { - if (values.length === 0) { - return values; + private resolveMatchers(values: (string | ProblemMatcher)[]): ProblemMatcher[] { + if (values === void 0 || values === null || values.length === 0) { + return []; } - let result: T[] = []; - values.forEach((matcher) => { + let result: ProblemMatcher[] = []; + values.forEach((value) => { + let matcher: ProblemMatcher; + if (Types.isString(value)) { + matcher = ProblemMatcherRegistry.get(value); + } else { + matcher = value; + } + if (!matcher) { + this.outputChannel.append(nls.localize('unkownProblemMatcher', 'Problem matcher {0} can\'t be resolved. The matcher will be ignored')); + return; + } if (!matcher.filePrefix) { result.push(matcher); } else { diff --git a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts index fd07989830bf1..fb53a216bd3ae 100644 --- a/src/vs/workbench/parts/tasks/test/node/configuration.test.ts +++ b/src/vs/workbench/parts/tasks/test/node/configuration.test.ts @@ -432,22 +432,29 @@ function assertCommandConfiguration(actual: Tasks.CommandConfiguration, expected } } -function assertProblemMatcher(actual: ProblemMatcher, expected: ProblemMatcher) { - if (expected.owner === ProblemMatcherBuilder.DEFAULT_UUID) { - try { - UUID.parse(actual.owner); - } catch (err) { - assert.fail(actual.owner, 'Owner must be a UUID'); - } - } else { - assert.strictEqual(actual.owner, expected.owner); +function assertProblemMatcher(actual: string | ProblemMatcher, expected: string | ProblemMatcher) { + assert.strictEqual(typeof actual, typeof expected); + if (typeof actual === 'string' && typeof expected === 'string') { + assert.strictEqual(actual, expected, 'Problem matcher references are different'); + return; } - assert.strictEqual(actual.applyTo, expected.applyTo); - assert.strictEqual(actual.severity, expected.severity); - assert.strictEqual(actual.fileLocation, expected.fileLocation); - assert.strictEqual(actual.filePrefix, expected.filePrefix); - if (actual.pattern && expected.pattern) { - assertProblemPatterns(actual.pattern, expected.pattern); + if (typeof actual !== 'string' && typeof expected !== 'string') { + if (expected.owner === ProblemMatcherBuilder.DEFAULT_UUID) { + try { + UUID.parse(actual.owner); + } catch (err) { + assert.fail(actual.owner, 'Owner must be a UUID'); + } + } else { + assert.strictEqual(actual.owner, expected.owner); + } + assert.strictEqual(actual.applyTo, expected.applyTo); + assert.strictEqual(actual.severity, expected.severity); + assert.strictEqual(actual.fileLocation, expected.fileLocation); + assert.strictEqual(actual.filePrefix, expected.filePrefix); + if (actual.pattern && expected.pattern) { + assertProblemPatterns(actual.pattern, expected.pattern); + } } }