// ***************************************************************************** // Copyright (C) 2017 Ericsson and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** import { inject, injectable, optional } from '@theia/core/shared/inversify'; import { TaskService } from './task-service'; import { TaskInfo, TaskConfiguration, TaskCustomization, TaskScope, TaskConfigurationScope, TaskDefinition } from '../common/task-protocol'; import { TaskDefinitionRegistry } from './task-definition-registry'; import URI from '@theia/core/lib/common/uri'; import { LabelProvider, QuickAccessProvider, QuickAccessRegistry, QuickInputService, QuickPick } from '@theia/core/lib/browser'; import { WorkspaceService } from '@theia/workspace/lib/browser'; import { TerminalService } from '@theia/terminal/lib/browser/base/terminal-service'; import { ALL_TASK_TYPES } from './provided-task-configurations'; import { TaskNameResolver } from './task-name-resolver'; import { TaskSourceResolver } from './task-source-resolver'; import { TaskConfigurationManager } from './task-configuration-manager'; import { filterItems, QuickInputButton, QuickPickItem, QuickPickItemOrSeparator, QuickPicks, QuickPickInput, QuickPickValue } from '@theia/core/lib/browser/quick-input/quick-input-service'; import { CancellationToken, PreferenceService } from '@theia/core/lib/common'; import { nls } from '@theia/core/lib/common/nls'; import { TriggerAction } from '@theia/monaco-editor-core/esm/vs/platform/quickinput/browser/pickerQuickAccess'; export namespace ConfigureTaskAction { export const ID = 'workbench.action.tasks.configureTaskRunner'; export const TEXT = nls.localizeByDefault('Configure Task'); } export type TaskEntry = QuickPickItemOrSeparator | QuickPickValue; export namespace TaskEntry { export function isQuickPickValue(item: QuickPickItemOrSeparator | QuickPickValue): item is QuickPickValue { return 'value' in item && typeof item.value === 'string'; } } export const CHOOSE_TASK = nls.localizeByDefault('Select the task to run'); export const CONFIGURE_A_TASK = nls.localizeByDefault('Configure a Task'); export const NO_TASK_TO_RUN = nls.localize('theia/task/noTaskToRun', 'No task to run found. Configure Tasks...'); export const NO_TASKS_FOUND = nls.localize('theia/task/noTasksFound', 'No tasks found'); export const SHOW_ALL = nls.localizeByDefault('Show All Tasks...'); @injectable() export class QuickOpenTask implements QuickAccessProvider { static readonly PREFIX = 'task '; readonly description: string = nls.localizeByDefault('Run Task'); protected items: Array = []; @inject(TaskService) protected readonly taskService: TaskService; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(QuickAccessRegistry) protected readonly quickAccessRegistry: QuickAccessRegistry; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @inject(TaskNameResolver) protected readonly taskNameResolver: TaskNameResolver; @inject(TaskSourceResolver) protected readonly taskSourceResolver: TaskSourceResolver; @inject(TaskConfigurationManager) protected readonly taskConfigurationManager: TaskConfigurationManager; @inject(PreferenceService) protected readonly preferences: PreferenceService; @inject(LabelProvider) protected readonly labelProvider: LabelProvider; init(): Promise { return this.doInit(this.taskService.startUserAction()); } protected async doInit(token: number): Promise { const recentTasks = this.taskService.recentTasks; const configuredTasks = await this.taskService.getConfiguredTasks(token); const providedTypes = this.taskDefinitionRegistry.getAll(); const { filteredRecentTasks, filteredConfiguredTasks } = this.getFilteredTasks(recentTasks, configuredTasks, []); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; this.items = []; const filteredRecentTasksItems = this.getItems(filteredRecentTasks, nls.localizeByDefault('recently used tasks'), token, isMulti); const filteredConfiguredTasksItems = this.getItems(filteredConfiguredTasks, nls.localizeByDefault('configured tasks'), token, isMulti, { label: `$(plus) ${CONFIGURE_A_TASK}`, execute: () => this.configure() }); const providedTypeItems = this.createProvidedTypeItems(providedTypes); this.items.push( ...filteredRecentTasksItems, ...filteredConfiguredTasksItems, ...providedTypeItems ); if (!this.items.length) { this.items.push(({ label: NO_TASK_TO_RUN, execute: () => this.configure() })); } } protected createProvidedTypeItems(providedTypes: TaskDefinition[]): TaskEntry[] { const result: TaskEntry[] = []; result.push({ type: 'separator', label: nls.localizeByDefault('contributed') }); providedTypes.sort((t1, t2) => t1.taskType.localeCompare(t2.taskType) ); for (const definition of providedTypes) { const type = definition.taskType; result.push(this.toProvidedTaskTypeEntry(type, `$(folder) ${type}`)); } result.push(this.toProvidedTaskTypeEntry(SHOW_ALL, SHOW_ALL)); return result; } protected toProvidedTaskTypeEntry(type: string, label: string): TaskEntry { return { label, value: type, /** * This function is used in the context of a QuickAccessProvider (triggered from the command palette: '?task'). * It triggers a call to QuickOpenTask#getPicks, * the 'execute' function below is called when the user selects an entry for a task type which triggers the display of * the second level quick pick. * * Due to the asynchronous resolution of second-level tasks, there may be a delay in showing the quick input widget. * * NOTE: The widget is not delayed in other contexts e.g. by commands (Run Tasks), see the implementation at QuickOpenTask#open * * To improve the performance, we may consider using a `PickerQuickAccessProvider` instead of a `QuickAccessProvider`, * and support providing 'FastAndSlowPicks'. * * TODO: Consider the introduction and exposure of monaco `PickerQuickAccessProvider` and the corresponding refactoring for this and other * users of QuickAccessProvider. */ execute: () => { this.doSecondLevel(type); } }; } protected onDidTriggerGearIcon(item: QuickPickItem): void { if (item instanceof TaskRunQuickOpenItem) { this.taskService.configure(item.token, item.task); this.quickInputService.hide(); } } async open(): Promise { this.showMultiLevelQuickPick(); } async showMultiLevelQuickPick(skipInit?: boolean): Promise { if (!skipInit) { await this.init(); } const picker: QuickPick = this.quickInputService.createQuickPick(); picker.placeholder = CHOOSE_TASK; picker.matchOnDescription = true; picker.ignoreFocusOut = false; picker.items = this.items; picker.onDidTriggerItemButton(({ item }) => this.onDidTriggerGearIcon(item)); const firstLevelTask = await this.doPickerFirstLevel(picker); if (!!firstLevelTask && TaskEntry.isQuickPickValue(firstLevelTask)) { // A taskType was selected picker.busy = true; await this.doSecondLevel(firstLevelTask.value); } else if (!!firstLevelTask && 'execute' in firstLevelTask && typeof firstLevelTask.execute === 'function') { firstLevelTask.execute(); } picker.dispose(); } protected async doPickerFirstLevel(picker: QuickPick): Promise { picker.show(); const firstLevelPickerResult = await new Promise(resolve => { picker.onDidAccept(async () => { resolve(picker.selectedItems ? picker.selectedItems[0] : undefined); }); }); return firstLevelPickerResult ?? undefined; } protected async doSecondLevel(taskType: string): Promise { // Resolve Second level tasks based on selected TaskType const isMulti = this.workspaceService.isMultiRootWorkspaceOpened; const token = this.taskService.startUserAction(); const providedTasks = taskType === SHOW_ALL ? await this.taskService.getProvidedTasks(token, ALL_TASK_TYPES) : await this.taskService.getProvidedTasks(token, taskType); const providedTasksItems = this.getItems(providedTasks, taskType + ' tasks', token, isMulti); const label = providedTasksItems.length ? nls.localizeByDefault('Go back ↩') : nls.localizeByDefault('No {0} tasks found. Go back ↩', taskType); providedTasksItems.push(({ label, execute: () => this.showMultiLevelQuickPick(true) })); this.quickInputService?.showQuickPick(providedTasksItems, { placeholder: CHOOSE_TASK, onDidTriggerItemButton: ({ item }) => this.onDidTriggerGearIcon(item) }); } attach(): void { this.items = []; const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; this.taskService.getRunningTasks().then(tasks => { if (!tasks.length) { this.items.push({ label: NO_TASKS_FOUND, }); } else { tasks.forEach((task: TaskInfo) => { // can only attach to terminal processes, so only list those if (task.terminalId) { this.items.push(new RunningTaskQuickOpenItem( task, this.taskService, this.taskNameResolver, this.taskSourceResolver, this.taskDefinitionRegistry, this.labelProvider, isMulti, () => this.taskService.attach(task.terminalId!, task) )); } }); } if (this.items.length === 0) { this.items.push(({ label: NO_TASKS_FOUND })); } this.quickInputService?.showQuickPick(this.items, { placeholder: CHOOSE_TASK }); }); } async configure(): Promise { this.quickInputService?.pick(this.resolveItemsToConfigure(), { placeHolder: nls.localizeByDefault('Select a task to configure') }). then(async item => { if (item && 'execute' in item && typeof item.execute === 'function') { item.execute(); } }); } protected async resolveItemsToConfigure(): Promise[]> { const items: Array> = []; const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; const token: number = this.taskService.startUserAction(); const configuredTasks = await this.taskService.getConfiguredTasks(token); const providedTasks = await this.taskService.getProvidedTasks(token, ALL_TASK_TYPES); // check if tasks.json exists. If not, display "Create tasks.json file from template" // If tasks.json exists and empty, display 'Open tasks.json file' const { filteredConfiguredTasks, filteredProvidedTasks } = this.getFilteredTasks([], configuredTasks, providedTasks); const groupedTasks = this.getGroupedTasksByWorkspaceFolder([...filteredConfiguredTasks, ...filteredProvidedTasks]); if (groupedTasks.has(TaskScope.Global.toString())) { const configs = groupedTasks.get(TaskScope.Global.toString())!; this.addConfigurationItems(items, configs, token, isMulti); } if (groupedTasks.has(TaskScope.Workspace.toString())) { const configs = groupedTasks.get(TaskScope.Workspace.toString())!; this.addConfigurationItems(items, configs, token, isMulti); } const rootUris = (await this.workspaceService.roots).map(rootStat => rootStat.resource.toString()); for (const rootFolder of rootUris) { const folderName = new URI(rootFolder).displayName; if (groupedTasks.has(rootFolder)) { const configs = groupedTasks.get(rootFolder.toString())!; this.addConfigurationItems(items, configs, token, isMulti); } else { const { configUri } = this.preferences.resolve('tasks', [], rootFolder); const existTaskConfigFile = !!configUri; items.push(({ label: existTaskConfigFile ? nls.localizeByDefault('Open tasks.json file') : nls.localizeByDefault('Create tasks.json file from template'), execute: () => { setTimeout(() => this.taskConfigurationManager.openConfiguration(rootFolder)); } })); } if (items.length > 0) { items.unshift({ type: 'separator', label: isMulti ? folderName : '' }); } } if (items.length === 0) { items.push(({ label: NO_TASKS_FOUND })); } return items; } private addConfigurationItems(items: QuickPickInput[], configs: TaskConfiguration[], token: number, isMulti: boolean): void { items.push( ...configs.map(taskConfig => { const item = new TaskConfigureQuickOpenItem( token, taskConfig, this.taskService, this.taskNameResolver, this.workspaceService, isMulti ); item['taskDefinitionRegistry'] = this.taskDefinitionRegistry; return item; }).sort((t1, t2) => t1.label.localeCompare(t2.label) ) ); } protected getTaskItems(): QuickPickItem[] { return this.items.filter((item): item is QuickPickItem => item.type !== 'separator' && (item as TaskRunQuickOpenItem).task !== undefined); } async runBuildOrTestTask(buildOrTestType: 'build' | 'test'): Promise { const shouldRunBuildTask = buildOrTestType === 'build'; const token: number = this.taskService.startUserAction(); await this.doInit(token); const taskItems = this.getTaskItems(); if (taskItems.length > 0) { // the item in `this.items` is not 'No tasks found' const buildOrTestTasks = taskItems.filter((t: TaskRunQuickOpenItem) => shouldRunBuildTask ? TaskCustomization.isBuildTask(t.task) : TaskCustomization.isTestTask(t.task) ); if (buildOrTestTasks.length > 0) { // build / test tasks are defined in the workspace const defaultBuildOrTestTasks = buildOrTestTasks.filter((t: TaskRunQuickOpenItem) => shouldRunBuildTask ? TaskCustomization.isDefaultBuildTask(t.task) : TaskCustomization.isDefaultTestTask(t.task) ); if (defaultBuildOrTestTasks.length === 1) { // run the default build / test task const defaultBuildOrTestTask = defaultBuildOrTestTasks[0]; const taskToRun = (defaultBuildOrTestTask as TaskRunQuickOpenItem).task; const scope = taskToRun._scope; if (this.taskDefinitionRegistry && !!this.taskDefinitionRegistry.getDefinition(taskToRun)) { this.taskService.run(token, taskToRun.source, taskToRun.label, scope); } else { this.taskService.run(token, taskToRun._source, taskToRun.label, scope); } return; } // if default build / test task is not found, or there are more than one default, // display the list of build /test tasks to let the user decide which to run this.items = buildOrTestTasks; } else { // no build / test tasks, display an action item to configure the build / test task this.items = [({ label: shouldRunBuildTask ? nls.localizeByDefault('No build task to run found. Configure Build Task...') : nls.localizeByDefault('No test task to run found. Configure Tasks...'), execute: () => { this.doInit(token).then(() => { // update the `tasks.json` file, instead of running the task itself this.items = this.getTaskItems().map((item: TaskRunQuickOpenItem) => new ConfigureBuildOrTestTaskQuickOpenItem( token, item.task, this.taskService, this.workspaceService.isMultiRootWorkspaceOpened, this.taskNameResolver, shouldRunBuildTask, this.taskConfigurationManager, this.taskDefinitionRegistry, this.taskSourceResolver )); this.quickInputService?.showQuickPick(this.items, { placeholder: shouldRunBuildTask ? nls.localizeByDefault('Select the task to be used as the default build task') : nls.localizeByDefault('Select the task to be used as the default test task') }); }); } })]; } } else { // no tasks are currently present, prompt users if they'd like to configure a task. this.items = [{ label: shouldRunBuildTask ? nls.localizeByDefault('No build task to run found. Configure Build Task...') : nls.localizeByDefault('No test task to run found. Configure Tasks...'), execute: () => this.configure() }]; } this.quickInputService?.showQuickPick(this.items, { placeholder: shouldRunBuildTask ? nls.localizeByDefault('Select the build task to run') : nls.localizeByDefault('Select the test task to run'), onDidTriggerItemButton: ({ item }) => this.onDidTriggerGearIcon(item) }); } async getPicks(filter: string, token: CancellationToken): Promise { await this.init(); return filterItems(this.items, filter); } registerQuickAccessProvider(): void { this.quickAccessRegistry.registerQuickAccessProvider({ getInstance: () => this, prefix: QuickOpenTask.PREFIX, placeholder: nls.localizeByDefault('Select the task to run'), helpEntries: [{ description: nls.localizeByDefault('Run Task'), needsEditor: false }] }); } protected getRunningTaskLabel(task: TaskInfo): string { return `Task id: ${task.taskId}, label: ${task.config.label}`; } private getItems(tasks: TaskConfiguration[], groupLabel: string, token: number, isMulti: boolean, defaultTask?: TaskEntry): TaskEntry[] { const items: TaskEntry[] = tasks.map(task => new TaskRunQuickOpenItem(token, task, this.taskService, isMulti, this.taskDefinitionRegistry, this.taskNameResolver, this.taskSourceResolver, this.taskConfigurationManager, [{ iconClass: 'codicon-gear', tooltip: nls.localizeByDefault('Configure Task'), }]) ).sort((t1, t2) => { let result = (t1.description ?? '').localeCompare(t2.description ?? ''); if (result === 0) { result = t1.label.localeCompare(t2.label); } return result; }); if (items.length === 0 && defaultTask) { items.push(defaultTask); } if (items.length > 0) { items.unshift({ type: 'separator', label: groupLabel }); } return items; } private getFilteredTasks(recentTasks: TaskConfiguration[], configuredTasks: TaskConfiguration[], providedTasks: TaskConfiguration[]): { filteredRecentTasks: TaskConfiguration[], filteredConfiguredTasks: TaskConfiguration[], filteredProvidedTasks: TaskConfiguration[] } { const filteredRecentTasks: TaskConfiguration[] = []; recentTasks.forEach(recent => { const originalTaskConfig = [...configuredTasks, ...providedTasks].find(t => this.taskDefinitionRegistry.compareTasks(recent, t)); if (originalTaskConfig) { filteredRecentTasks.push(originalTaskConfig); } }); const filteredProvidedTasks: TaskConfiguration[] = []; providedTasks.forEach(provided => { const exist = [...filteredRecentTasks, ...configuredTasks].some(t => this.taskDefinitionRegistry.compareTasks(provided, t)); if (!exist) { filteredProvidedTasks.push(provided); } }); const filteredConfiguredTasks: TaskConfiguration[] = []; configuredTasks.forEach(configured => { const exist = filteredRecentTasks.some(t => this.taskDefinitionRegistry.compareTasks(configured, t)); if (!exist) { filteredConfiguredTasks.push(configured); } }); return { filteredRecentTasks, filteredConfiguredTasks, filteredProvidedTasks }; } private getGroupedTasksByWorkspaceFolder(tasks: TaskConfiguration[]): Map { const grouped = new Map(); for (const task of tasks) { const scope = task._scope; if (grouped.has(scope.toString())) { grouped.get(scope.toString())!.push(task); } else { grouped.set(scope.toString(), [task]); } } for (const taskConfigs of grouped.values()) { taskConfigs.sort((t1, t2) => t1.label.localeCompare(t2.label)); } return grouped; } } export class TaskRunQuickOpenItem implements QuickPickItem { constructor( readonly token: number, readonly task: TaskConfiguration, protected taskService: TaskService, protected isMulti: boolean, protected readonly taskDefinitionRegistry: TaskDefinitionRegistry, protected readonly taskNameResolver: TaskNameResolver, protected readonly taskSourceResolver: TaskSourceResolver, protected taskConfigurationManager: TaskConfigurationManager, readonly buttons?: Array ) { } get label(): string { return this.taskNameResolver.resolve(this.task); } get description(): string { return renderScope(this.task._scope, this.isMulti); } get detail(): string | undefined { return this.task.detail; } execute(): void { const scope = this.task._scope; if (this.taskDefinitionRegistry && !!this.taskDefinitionRegistry.getDefinition(this.task)) { this.taskService.run(this.token, this.task.source || this.task._source, this.task.label, scope); } else { this.taskService.run(this.token, this.task._source, this.task.label, scope); } } trigger(): TriggerAction { this.taskService.configure(this.token, this.task); return TriggerAction.CLOSE_PICKER; } } export class ConfigureBuildOrTestTaskQuickOpenItem extends TaskRunQuickOpenItem { constructor( token: number, task: TaskConfiguration, taskService: TaskService, isMulti: boolean, taskNameResolver: TaskNameResolver, protected readonly isBuildTask: boolean, taskConfigurationManager: TaskConfigurationManager, taskDefinitionRegistry: TaskDefinitionRegistry, taskSourceResolver: TaskSourceResolver ) { super(token, task, taskService, isMulti, taskDefinitionRegistry, taskNameResolver, taskSourceResolver, taskConfigurationManager); } override execute(): void { this.taskService.updateTaskConfiguration(this.token, this.task, { group: { kind: this.isBuildTask ? 'build' : 'test', isDefault: true } }) .then(() => { if (this.task._scope) { this.taskConfigurationManager.openConfiguration(this.task._scope); } }); } } function renderScope(scope: TaskConfigurationScope, isMulti: boolean): string { if (typeof scope === 'string') { if (isMulti) { return new URI(scope).displayName; } else { return ''; } } else { return TaskScope[scope]; } } export class TaskConfigureQuickOpenItem implements QuickPickItem { protected taskDefinitionRegistry: TaskDefinitionRegistry; constructor( protected readonly token: number, protected readonly task: TaskConfiguration, protected readonly taskService: TaskService, protected readonly taskNameResolver: TaskNameResolver, protected readonly workspaceService: WorkspaceService, protected readonly isMulti: boolean ) { const stat = this.workspaceService.workspace; this.isMulti = stat ? !stat.isDirectory : false; } get label(): string { return this.taskNameResolver.resolve(this.task); } get description(): string { return renderScope(this.task._scope, this.isMulti); } accept(): void { this.execute(); } execute(): void { this.taskService.configure(this.token, this.task); } } @injectable() export class TaskTerminateQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @inject(TaskNameResolver) protected readonly taskNameResolver: TaskNameResolver; @inject(TaskSourceResolver) protected readonly taskSourceResolver: TaskSourceResolver; @inject(TaskService) protected readonly taskService: TaskService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; async getItems(): Promise> { const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { items.push(({ label: nls.localizeByDefault('No task is currently running'), })); } else { runningTasks.forEach((task: TaskInfo) => { items.push(new RunningTaskQuickOpenItem( task, this.taskService, this.taskNameResolver, this.taskSourceResolver, this.taskDefinitionRegistry, this.labelProvider, isMulti, () => this.taskService.kill(task.taskId) )); }); if (runningTasks.length > 1) { items.push(({ label: nls.localizeByDefault('All Running Tasks'), execute: () => { runningTasks.forEach((t: TaskInfo) => { this.taskService.kill(t.taskId); }); } })); } } return items; } async open(): Promise { const items = await this.getItems(); this.quickInputService?.showQuickPick(items, { placeholder: nls.localizeByDefault('Select a task to terminate') }); } } @injectable() export class TaskRunningQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @inject(TaskNameResolver) protected readonly taskNameResolver: TaskNameResolver; @inject(TaskSourceResolver) protected readonly taskSourceResolver: TaskSourceResolver; @inject(TaskService) protected readonly taskService: TaskService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; @inject(TerminalService) protected readonly terminalService: TerminalService; async getItems(): Promise> { const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { items.push(({ label: nls.localizeByDefault('No task is currently running'), })); } else { runningTasks.forEach((task: TaskInfo) => { items.push(new RunningTaskQuickOpenItem( task, this.taskService, this.taskNameResolver, this.taskSourceResolver, this.taskDefinitionRegistry, this.labelProvider, isMulti, () => { if (task.terminalId) { const terminal = this.terminalService.getByTerminalId(task.terminalId); if (terminal) { this.terminalService.open(terminal); } } } )); }); } return items; } async open(): Promise { const items = await this.getItems(); this.quickInputService?.showQuickPick(items, { placeholder: nls.localizeByDefault('Select the task to show its output') }); } } export class RunningTaskQuickOpenItem implements QuickPickItem { constructor( protected readonly taskInfo: TaskInfo, protected readonly taskService: TaskService, protected readonly taskNameResolver: TaskNameResolver, protected readonly taskSourceResolver: TaskSourceResolver, protected readonly taskDefinitionRegistry: TaskDefinitionRegistry, protected readonly labelProvider: LabelProvider, protected readonly isMulti: boolean, public readonly execute: () => void, ) { } get label(): string { return this.taskNameResolver.resolve(this.taskInfo.config); } get description(): string { return renderScope(this.taskInfo.config._scope, this.isMulti); } get detail(): string | undefined { return this.taskInfo.config.detail; } } @injectable() export class TaskRestartRunningQuickOpen { @inject(LabelProvider) protected readonly labelProvider: LabelProvider; @inject(QuickInputService) @optional() protected readonly quickInputService: QuickInputService; @inject(TaskDefinitionRegistry) protected readonly taskDefinitionRegistry: TaskDefinitionRegistry; @inject(TaskNameResolver) protected readonly taskNameResolver: TaskNameResolver; @inject(TaskSourceResolver) protected readonly taskSourceResolver: TaskSourceResolver; @inject(TaskService) protected readonly taskService: TaskService; @inject(WorkspaceService) protected readonly workspaceService: WorkspaceService; async getItems(): Promise> { const items: Array = []; const runningTasks: TaskInfo[] = await this.taskService.getRunningTasks(); const isMulti: boolean = this.workspaceService.isMultiRootWorkspaceOpened; if (runningTasks.length <= 0) { items.push({ label: nls.localizeByDefault('No task to restart') }); } else { runningTasks.forEach((task: TaskInfo) => { items.push(new RunningTaskQuickOpenItem( task, this.taskService, this.taskNameResolver, this.taskSourceResolver, this.taskDefinitionRegistry, this.labelProvider, isMulti, () => this.taskService.restartTask(task) )); }); } return items; } async open(): Promise { const items = await this.getItems(); this.quickInputService?.showQuickPick(items, { placeholder: nls.localizeByDefault('Select the task to restart') }); } }