diff options
Diffstat (limited to 'src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts')
-rw-r--r-- | src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts | 150 |
1 files changed, 118 insertions, 32 deletions
diff --git a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts index 93cbe879116..c123a941880 100644 --- a/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts +++ b/src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts @@ -57,7 +57,7 @@ import { TaskSettingId, TasksSchemaProperties } from 'vs/workbench/contrib/tasks/common/tasks'; -import { ITaskService, ITaskProvider, IProblemMatcherRunOptions, ICustomizationProperties, ITaskFilter, IWorkspaceFolderTaskResult, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext } from 'vs/workbench/contrib/tasks/common/taskService'; +import { ITaskService, ITaskProvider, IProblemMatcherRunOptions, ICustomizationProperties, ITaskFilter, IWorkspaceFolderTaskResult, CustomExecutionSupportedContext, ShellExecutionSupportedContext, ProcessExecutionSupportedContext, TaskCommandsRegistered } from 'vs/workbench/contrib/tasks/common/taskService'; import { getTemplates as getTaskTemplates } from 'vs/workbench/contrib/tasks/common/taskTemplates'; import * as TaskConfig from '../common/taskConfiguration'; @@ -78,7 +78,7 @@ import { ITextEditorSelection, TextEditorSelectionRevealType } from 'vs/platform import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences'; import { CancellationToken, CancellationTokenSource } from 'vs/base/common/cancellation'; import { IViewsService, IViewDescriptorService } from 'vs/workbench/common/views'; -import { isWorkspaceFolder, ITaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG, configureTaskIcon } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; +import { isWorkspaceFolder, ITaskQuickPickEntry, QUICKOPEN_DETAIL_CONFIG, TaskQuickPick, QUICKOPEN_SKIP_CONFIG, configureTaskIcon, ITaskTwoLevelQuickPickEntry } from 'vs/workbench/contrib/tasks/browser/taskQuickPick'; import { ILogService } from 'vs/platform/log/common/log'; import { once } from 'vs/base/common/functional'; import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService'; @@ -200,6 +200,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer private static _nextHandle: number = 0; + private _tasksReconnected: boolean = false; private _schemaVersion: JsonSchemaVersion | undefined; private _executionEngine: ExecutionEngine | undefined; private _workspaceFolders: IWorkspaceFolder[] | undefined; @@ -292,7 +293,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer })); this._taskRunningState = TASK_RUNNING_STATE.bindTo(_contextKeyService); this._onDidStateChange = this._register(new Emitter()); - this._registerCommands(); + this._registerCommands().then(() => { + TaskCommandsRegistered.bindTo(this._contextKeyService).set(true); + }); this._configurationResolverService.contributeVariable('defaultBuildTask', async (): Promise<string | undefined> => { let tasks = await this._getTasksForGroup(TaskGroup.Build); if (tasks.length > 0) { @@ -337,6 +340,27 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer processContext.set(process && !isVirtual); } this._onDidRegisterSupportedExecutions.fire(); + if (this._jsonTasksSupported && !this._tasksReconnected) { + this._reconnectTasks(); + } + } + + private async _reconnectTasks(): Promise<void> { + const recentlyUsedTasks = await this.readRecentTasks(); + if (!recentlyUsedTasks.length) { + return; + } + for (const task of recentlyUsedTasks) { + if (ConfiguringTask.is(task)) { + const resolved = await this.tryResolveTask(task); + if (resolved) { + this.run(resolved, undefined, TaskRunSource.Reconnect); + } + } else { + this.run(task, undefined, TaskRunSource.Reconnect); + } + } + this._tasksReconnected = true; } public get onDidStateChange(): Event<ITaskEvent> { @@ -347,7 +371,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return this.inTerminal(); } - private _registerCommands(): void { + private async _registerCommands(): Promise<void> { CommandsRegistry.registerCommand({ id: 'workbench.action.tasks.runTask', handler: async (accessor, arg) => { @@ -359,8 +383,30 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer description: 'Run Task', args: [{ name: 'args', + isOptional: true, + description: nls.localize('runTask.arg', "Filters the tasks shown in the quickpick"), schema: { - 'type': 'string', + anyOf: [ + { + type: 'string', + description: nls.localize('runTask.label', "The task's label or a term to filter by") + }, + { + type: 'object', + properties: { + type: { + type: 'string', + description: nls.localize('runTask.type', "The contributed task type"), + enum: Array.from(this._providerTypes.values()).map(provider => provider) + }, + taskName: { + type: 'string', + description: nls.localize('runTask.taskName', "The task's label or a term to filter by"), + enum: await this.tasks().then((tasks) => tasks.map(t => t._label)) + } + } + } + ] } }] } @@ -383,7 +429,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer this._runTerminateCommand(arg); } }); - CommandsRegistry.registerCommand('workbench.action.tasks.showLog', () => { if (!this._canRunCommand()) { return; @@ -639,7 +684,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } } - return result; } @@ -895,7 +939,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer map.get(folder).push(task); } } - for (const entry of recentlyUsedTasks.entries()) { const key = entry[0]; const task = JSON.parse(entry[1]); @@ -904,6 +947,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } const readTasksMap: Map<string, (Task | ConfiguringTask)> = new Map(); + async function readTasks(that: AbstractTaskService, map: Map<string, any>, isWorkspaceFile: boolean) { for (const key of map.keys()) { const custom: CustomTask[] = []; @@ -932,7 +976,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } await readTasks(this, folderToTasksMap, false); await readTasks(this, workspaceToTaskMap, true); - for (const key of recentlyUsedTasks.keys()) { if (readTasksMap.has(key)) { tasks.push(readTasksMap.get(key)!); @@ -1727,8 +1770,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer ? await this.getTask(taskFolder, taskIdentifier) : task) ?? task; } await ProblemMatcherRegistry.onReady(); - const executeResult = this._getTaskSystem().run(taskToRun, resolver); - return this._handleExecuteResult(executeResult, runSource); + const executeResult = runSource === TaskRunSource.Reconnect ? this._getTaskSystem().reconnect(taskToRun, resolver) : this._getTaskSystem().run(taskToRun, resolver); + if (executeResult) { + return this._handleExecuteResult(executeResult, runSource); + } + return { exitCode: 0 }; } private async _handleExecuteResult(executeResult: ITaskExecuteResult, runSource?: TaskRunSource): Promise<ITaskSummary> { @@ -1759,6 +1805,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer throw new TaskError(Severity.Warning, nls.localize('TaskSystem.active', 'There is already a task running. Terminate it first before executing another task.'), TaskErrors.RunningTask); } } + this._setRecentlyUsedTask(executeResult.task); return executeResult.promise; } @@ -2127,7 +2174,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } private get _jsonTasksSupported(): boolean { - return !!ShellExecutionSupportedContext.getValue(this._contextKeyService) && !!ProcessExecutionSupportedContext.getValue(this._contextKeyService); + return ShellExecutionSupportedContext.getValue(this._contextKeyService) === true && ProcessExecutionSupportedContext.getValue(this._contextKeyService) === true; } private _computeWorkspaceFolderTasks(workspaceFolder: IWorkspaceFolder, runSource: TaskRunSource = TaskRunSource.User): Promise<IWorkspaceFolderTaskResult> { @@ -2521,11 +2568,11 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return entries; } - private async _showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry) { - return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, this._themeService, placeHolder, defaultEntry); + private async _showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry, filter?: string) { + return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, this._themeService, placeHolder, defaultEntry, filter); } - private async _showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: ITaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, additionalEntries?: ITaskQuickPickEntry[]): Promise<ITaskQuickPickEntry | undefined | null> { + private async _showQuickPick(tasks: Promise<Task[]> | Task[], placeHolder: string, defaultEntry?: ITaskQuickPickEntry, group: boolean = false, sort: boolean = false, selectedEntry?: ITaskQuickPickEntry, additionalEntries?: ITaskQuickPickEntry[], filter?: string): Promise<ITaskQuickPickEntry | undefined | null> { const tokenSource = new CancellationTokenSource(); const cancellationToken: CancellationToken = tokenSource.token; const createEntries = new Promise<QuickPickInput<ITaskQuickPickEntry>[]>((resolve) => { @@ -2564,7 +2611,6 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const picker: IQuickPick<ITaskQuickPickEntry> = this._quickInputService.createQuickPick(); picker.placeholder = placeHolder; picker.matchOnDescription = true; - picker.onDidTriggerItemButton(context => { const task = context.item.task; this._quickInputService.cancel(); @@ -2580,7 +2626,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer picker.items = entries; }); picker.show(); - + if (filter) { + picker.value = filter; + } return new Promise<ITaskQuickPickEntry | undefined | null>(resolve => { this._register(picker.onDidAccept(async () => { let selection = picker.selectedItems ? picker.selectedItems[0] : undefined; @@ -2654,12 +2702,20 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer })) === true; } - private _runTaskCommand(arg?: any): void { + private async _runTaskCommand(filter?: { type?: string; taskName?: string } | string): Promise<void> { if (!this._canRunCommand()) { return; } - const identifier = this._getTaskIdentifier(arg); - if (identifier !== undefined) { + + let typeFilter: boolean = false; + if (filter && typeof filter !== 'string') { + // name takes precedence + typeFilter = !filter?.taskName && !!filter?.type; + filter = filter?.taskName || filter?.type; + } + + const taskIdentifier: KeyedTaskIdentifier | undefined | string = this._getTaskIdentifier(filter); + if (taskIdentifier) { this._getGroupedTasks().then(async (grouped) => { const resolver = this._createResolver(grouped); const folderURIs: (URI | string)[] = this._contextService.getWorkspace().folders.map(folder => folder.uri); @@ -2668,7 +2724,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } folderURIs.push(USER_TASKS_GROUP_KEY); for (const uri of folderURIs) { - const task = await resolver.resolve(uri, identifier); + const task = await resolver.resolve(uri, taskIdentifier); if (task) { this.run(task).then(undefined, reason => { // eat the error, it has already been surfaced to the user and we don't care about it here @@ -2676,7 +2732,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return; } } - this._doRunTaskCommand(grouped.all()); + this._doRunTaskCommand(grouped.all(), typeof taskIdentifier === 'string' ? taskIdentifier : undefined, typeFilter); }, () => { this._doRunTaskCommand(); }); @@ -2716,7 +2772,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer return { tasks, grouped }; } - private _doRunTaskCommand(tasks?: Task[]): void { + private _doRunTaskCommand(tasks?: Task[], filter?: string, typeFilter?: boolean): void { const pickThen = (task: Task | undefined | null) => { if (task === undefined) { return; @@ -2732,28 +2788,58 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer const placeholder = nls.localize('TaskService.pickRunTask', 'Select the task to run'); - this._showIgnoredFoldersMessage().then(() => { + this._showIgnoredFoldersMessage().then(async () => { if (this._configurationService.getValue(USE_SLOW_PICKER)) { let taskResult: { tasks: Promise<Task[]>; grouped: Promise<TaskMap> } | undefined = undefined; if (!tasks) { taskResult = this._tasksAndGroupedTasks(); } + if (filter && typeFilter) { + const picker: IQuickPick<ITaskTwoLevelQuickPickEntry> = this._quickInputService.createQuickPick(); + picker.placeholder = nls.localize('TaskService.pickRunTask', 'Select the task to run'); + picker.matchOnDescription = true; + picker.ignoreFocusOut = false; + const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService); + const result = await taskQuickPick.doPickerSecondLevel(picker, filter); + if (result?.task) { + pickThen(result.task as Task); + taskQuickPick.dispose(); + } + return; + } this._showQuickPick(tasks ? tasks : taskResult!.tasks, placeholder, { label: '$(plus) ' + nls.localize('TaskService.noEntryToRun', 'Configure a Task'), task: null }, - true). + true, false, undefined, undefined, typeof filter === 'string' ? filter : undefined). then((entry) => { return pickThen(entry ? entry.task : undefined); }); } else { - this._showTwoLevelQuickPick(placeholder, - { - label: '$(plus) ' + nls.localize('TaskService.noEntryToRun', 'Configure a Task'), - task: null - }). - then(pickThen); + if (filter && typeFilter) { + const picker: IQuickPick<ITaskTwoLevelQuickPickEntry> = this._quickInputService.createQuickPick(); + picker.placeholder = nls.localize('TaskService.pickRunTask', 'Select the task to run'); + picker.matchOnDescription = true; + picker.ignoreFocusOut = false; + const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService); + const result = await taskQuickPick.doPickerSecondLevel(picker, filter); + if (result?.task) { + pickThen(result.task as Task); + picker.dispose(); + taskQuickPick.dispose(); + return; + } else { + return; + } + } else { + this._showTwoLevelQuickPick(placeholder, + { + label: '$(plus) ' + nls.localize('TaskService.noEntryToRun', 'Configure a Task'), + task: null + }, typeof filter === 'string' ? filter : undefined). + then(pickThen); + } } }); } @@ -3055,7 +3141,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer } } - private _getTaskIdentifier(arg?: any): string | KeyedTaskIdentifier | undefined { + private _getTaskIdentifier(arg?: string | ITaskIdentifier): string | KeyedTaskIdentifier | undefined { let result: string | KeyedTaskIdentifier | undefined = undefined; if (Types.isString(arg)) { result = arg; |