use task icon/color in quick pick (#151990)

This commit is contained in:
Megan Rogge 2022-06-13 17:26:53 -08:00 committed by GitHub
parent 9e2b6e082c
commit 33b1980f6f
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 121 additions and 17 deletions

View file

@ -79,7 +79,7 @@ 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 { ILogService } from 'vs/platform/log/common/log';
import { once } from 'vs/base/common/functional';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { VirtualWorkspaceContext } from 'vs/workbench/common/contextkeys';
import { Schemas } from 'vs/base/common/network';
@ -94,7 +94,7 @@ export namespace ConfigureTaskAction {
export const TEXT = nls.localize('ConfigureTaskRunnerAction.label', "Configure Task");
}
type TaskQuickPickEntryType = (IQuickPickItem & { task: Task }) | (IQuickPickItem & { folder: IWorkspaceFolder }) | (IQuickPickItem & { settingType: string });
export type TaskQuickPickEntryType = (IQuickPickItem & { task: Task }) | (IQuickPickItem & { folder: IWorkspaceFolder }) | (IQuickPickItem & { settingType: string });
class ProblemReporter implements TaskConfig.IProblemReporter {
@ -256,7 +256,8 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
@IViewDescriptorService private readonly _viewDescriptorService: IViewDescriptorService,
@IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService,
@IWorkspaceTrustManagementService private readonly _workspaceTrustManagementService: IWorkspaceTrustManagementService,
@ILogService private readonly _logService: ILogService
@ILogService private readonly _logService: ILogService,
@IThemeService private readonly _themeService: IThemeService
) {
super();
@ -2519,7 +2520,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
}
private async _showTwoLevelQuickPick(placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, placeHolder, defaultEntry);
return TaskQuickPick.show(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService, this._themeService, placeHolder, defaultEntry);
}
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> {
@ -3143,7 +3144,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (this._isTaskEntry(selection)) {
this._configureTask(selection.task);
} else if (this._isSettingEntry(selection)) {
const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._dialogService);
const taskQuickPick = new TaskQuickPick(this, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService);
taskQuickPick.handleSettingOption(selection.settingType);
} else if (selection.folder && (this._contextService.getWorkbenchState() !== WorkbenchState.EMPTY)) {
this._openTaskFile(selection.folder.toResource('.vscode/tasks.json'), TaskSourceKind.Workspace);
@ -3201,7 +3202,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
if (tasks.length > 0) {
tasks = tasks.sort((a, b) => a._label.localeCompare(b._label));
for (const task of tasks) {
entries.push({ label: task._label, task, description: this.getTaskDescription(task), detail: this._showDetail() ? task.configurationProperties.detail : undefined });
const entry = { label: TaskQuickPick.getTaskLabelWithIcon(task), task, description: this.getTaskDescription(task), detail: this._showDetail() ? task.configurationProperties.detail : undefined };
TaskQuickPick.applyColorStyles(task, entry, this._themeService);
entries.push(entry);
if (!ContributedTask.is(task)) {
configuredCount++;
}

View file

@ -9,16 +9,18 @@ import { Task, ContributedTask, CustomTask, ConfiguringTask, TaskSorter, KeyedTa
import { IWorkspace, IWorkspaceFolder } from 'vs/platform/workspace/common/workspace';
import * as Types from 'vs/base/common/types';
import { ITaskService, IWorkspaceFolderTaskResult } from 'vs/workbench/contrib/tasks/common/taskService';
import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton } from 'vs/base/parts/quickinput/common/quickInput';
import { IQuickPickItem, QuickPickInput, IQuickPick, IQuickInputButton, IQuickPickSeparator } from 'vs/base/parts/quickinput/common/quickInput';
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { IQuickInputService } from 'vs/platform/quickinput/common/quickInput';
import { Disposable } from 'vs/base/common/lifecycle';
import { Disposable, dispose, IDisposable } from 'vs/base/common/lifecycle';
import { Event } from 'vs/base/common/event';
import { INotificationService, Severity } from 'vs/platform/notification/common/notification';
import { Codicon } from 'vs/base/common/codicons';
import { ThemeIcon } from 'vs/platform/theme/common/themeService';
import { IThemeService, ThemeIcon } from 'vs/platform/theme/common/themeService';
import { registerIcon } from 'vs/platform/theme/common/iconRegistry';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { getColorClass, getColorStyleElement, getStandardColors } from 'vs/workbench/contrib/terminal/browser/terminalIcon';
import { TaskQuickPickEntryType } from 'vs/workbench/contrib/tasks/browser/abstractTaskService';
export const QUICKOPEN_DETAIL_CONFIG = 'task.quickOpen.detail';
export const QUICKOPEN_SKIP_CONFIG = 'task.quickOpen.skip';
@ -49,6 +51,7 @@ export class TaskQuickPick extends Disposable {
private _configurationService: IConfigurationService,
private _quickInputService: IQuickInputService,
private _notificationService: INotificationService,
private _themeService: IThemeService,
private _dialogService: IDialogService) {
super();
this._sorter = this._taskService.createSorter();
@ -61,7 +64,7 @@ export class TaskQuickPick extends Disposable {
private _guessTaskLabel(task: Task | ConfiguringTask): string {
if (task._label) {
return task._label;
return TaskQuickPick.getTaskLabelWithIcon(task);
}
if (ConfiguringTask.is(task)) {
let label: string = task.configures.type;
@ -74,9 +77,29 @@ export class TaskQuickPick extends Disposable {
return '';
}
public static getTaskLabelWithIcon(task: Task | ConfiguringTask): string {
return task.configurationProperties.icon ? `$(${task.configurationProperties.icon}) ${task._label}` : task.configurationProperties.color ? `$(${Codicon.tools.id}) ${task._label}` : `${task._label}`;
}
public static applyColorStyles(task: Task | ConfiguringTask, entry: TaskQuickPickEntryType | ITaskTwoLevelQuickPickEntry, themeService: IThemeService): void {
if (task.configurationProperties.color) {
const colorTheme = themeService.getColorTheme();
const styleElement = getColorStyleElement(colorTheme);
entry.iconClasses = [getColorClass(task.configurationProperties.color)];
document.body.appendChild(styleElement);
}
}
private _createTaskEntry(task: Task | ConfiguringTask, extraButtons: IQuickInputButton[] = []): ITaskTwoLevelQuickPickEntry {
const customizeIconButton = { iconClass: ThemeIcon.asClassName(Codicon.pencil), tooltip: nls.localize('setIconAndColor', "Choose color and icon") };
const entry: ITaskTwoLevelQuickPickEntry = { label: this._guessTaskLabel(task), description: this._taskService.getTaskDescription(task), task, detail: this._showDetail() ? task.configurationProperties.detail : undefined };
entry.buttons = [{ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") }, ...extraButtons];
entry.buttons = [];
if (CustomTask.is(task)) {
entry.buttons.push(customizeIconButton);
}
entry.buttons.push({ iconClass: ThemeIcon.asClassName(configureTaskIcon), tooltip: nls.localize('configureTask', "Configure Task") });
entry.buttons.push(...extraButtons);
TaskQuickPick.applyColorStyles(task, entry, this._themeService);
return entry;
}
@ -211,6 +234,9 @@ export class TaskQuickPick extends Disposable {
if (indexToRemove >= 0) {
picker.items = [...picker.items.slice(0, indexToRemove), ...picker.items.slice(indexToRemove + 1)];
}
} else if (context.button.iconClass = ThemeIcon.asClassName(Codicon.pencil)) {
await this._setColor(task);
await this._setIcon(task);
} else {
this._quickInputService.cancel();
if (ContributedTask.is(task)) {
@ -265,6 +291,74 @@ export class TaskQuickPick extends Disposable {
return;
}
private async _setColor(task: Task | ConfiguringTask | null | string | undefined): Promise<void> {
if (task === undefined || task === null || typeof task === 'string') {
return;
}
const colorTheme = this._themeService.getColorTheme();
const standardColors: string[] = getStandardColors(colorTheme);
const styleElement = getColorStyleElement(colorTheme);
const items: (IQuickPickItem | IQuickPickSeparator)[] = [];
for (const colorKey of standardColors) {
const colorClass = getColorClass(colorKey);
items.push({
label: `$(${Codicon.circleFilled.id}) ${colorKey.replace('terminal.ansi', '')}`, id: colorKey, description: colorKey, iconClasses: [colorClass]
});
}
items.push({ type: 'separator' });
const showAllColorsItem = { label: 'Reset to default' };
items.push(showAllColorsItem);
document.body.appendChild(styleElement);
const quickPick = this._quickInputService.createQuickPick();
quickPick.items = items;
quickPick.matchOnDescription = true;
quickPick.show();
const disposables: IDisposable[] = [];
const result = await new Promise<IQuickPickItem | undefined>(r => {
disposables.push(quickPick.onDidHide(() => r(undefined)));
disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0])));
});
dispose(disposables);
if (result && task && typeof task !== 'string') {
task.configurationProperties.color = result.id;
}
document.body.removeChild(styleElement);
quickPick.hide();
}
private async _setIcon(task: Task | ConfiguringTask | null | string | undefined): Promise<void> {
if (task === undefined || task === null || typeof task === 'string') {
return;
}
type Item = IQuickPickItem & { icon: ThemeIcon };
const items: Item[] = [];
for (const icon of Codicon.getAll()) {
items.push({ label: `$(${icon.id})`, description: `${icon.id}`, id: icon.id, icon, iconClasses: task.configurationProperties.color ? [getColorClass(task.configurationProperties.color)] : undefined });
}
const quickPick = this._quickInputService.createQuickPick();
quickPick.items = items;
quickPick.matchOnDescription = true;
quickPick.show();
const disposables: IDisposable[] = [];
const result = await new Promise<IQuickPickItem | undefined>(r => {
disposables.push(quickPick.onDidHide(() => r(undefined)));
disposables.push(quickPick.onDidAccept(() => r(quickPick.selectedItems[0])));
});
dispose(disposables);
if (result && task && typeof task !== 'string') {
task.configurationProperties.icon = result.id;
}
if (CustomTask.is(task) && result) {
await this._taskService.customize(task, { icon: result.id, color: task.configurationProperties.color }, false);
}
quickPick.hide();
}
private async _doPickerFirstLevel(picker: IQuickPick<ITaskTwoLevelQuickPickEntry>, taskQuickPickEntries: QuickPickInput<ITaskTwoLevelQuickPickEntry>[]): Promise<Task | ConfiguringTask | string | null | undefined> {
picker.items = taskQuickPickEntries;
const firstLevelPickerResult = await new Promise<ITaskTwoLevelQuickPickEntry | undefined | null>(resolve => {
@ -367,8 +461,8 @@ export class TaskQuickPick extends Disposable {
static async show(taskService: ITaskService, configurationService: IConfigurationService,
quickInputService: IQuickInputService, notificationService: INotificationService,
dialogService: IDialogService, placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
const taskQuickPick = new TaskQuickPick(taskService, configurationService, quickInputService, notificationService, dialogService);
dialogService: IDialogService, themeService: IThemeService, placeHolder: string, defaultEntry?: ITaskQuickPickEntry) {
const taskQuickPick = new TaskQuickPick(taskService, configurationService, quickInputService, notificationService, themeService, dialogService);
return taskQuickPick.show(placeHolder, defaultEntry);
}
}

View file

@ -17,6 +17,7 @@ import { IConfigurationService } from 'vs/platform/configuration/common/configur
import { isString } from 'vs/base/common/types';
import { INotificationService } from 'vs/platform/notification/common/notification';
import { IDialogService } from 'vs/platform/dialogs/common/dialogs';
import { IThemeService } from 'vs/platform/theme/common/themeService';
export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQuickAccessItem> {
@ -28,7 +29,8 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
@IConfigurationService private _configurationService: IConfigurationService,
@IQuickInputService private _quickInputService: IQuickInputService,
@INotificationService private _notificationService: INotificationService,
@IDialogService private _dialogService: IDialogService
@IDialogService private _dialogService: IDialogService,
@IThemeService private _themeService: IThemeService
) {
super(TasksQuickAccessProvider.PREFIX, {
noResultsPick: {
@ -42,7 +44,7 @@ export class TasksQuickAccessProvider extends PickerQuickAccessProvider<IPickerQ
return [];
}
const taskQuickPick = new TaskQuickPick(this._taskService, this._configurationService, this._quickInputService, this._notificationService, this._dialogService);
const taskQuickPick = new TaskQuickPick(this._taskService, this._configurationService, this._quickInputService, this._notificationService, this._themeService, this._dialogService);
const topLevelPicks = await taskQuickPick.getTopLevelEntries();
const taskPicks: Array<IPickerQuickAccessItem | IQuickPickSeparator> = [];

View file

@ -36,6 +36,8 @@ export interface ICustomizationProperties {
group?: string | { kind?: string; isDefault?: boolean };
problemMatcher?: string | string[];
isBackground?: boolean;
color?: string;
icon?: string;
}
export interface ITaskFilter {

View file

@ -43,6 +43,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile
import { IWorkspaceTrustManagementService, IWorkspaceTrustRequestService } from 'vs/platform/workspace/common/workspaceTrust';
import { ITerminalProfileResolverService } from 'vs/workbench/contrib/terminal/common/terminal';
import { IPaneCompositePartService } from 'vs/workbench/services/panecomposite/browser/panecomposite';
import { IThemeService } from 'vs/platform/theme/common/themeService';
interface IWorkspaceFolderConfigurationResult {
workspaceFolder: IWorkspaceFolder;
@ -83,7 +84,8 @@ export class TaskService extends AbstractTaskService {
@IViewDescriptorService viewDescriptorService: IViewDescriptorService,
@IWorkspaceTrustRequestService workspaceTrustRequestService: IWorkspaceTrustRequestService,
@IWorkspaceTrustManagementService workspaceTrustManagementService: IWorkspaceTrustManagementService,
@ILogService logService: ILogService) {
@ILogService logService: ILogService,
@IThemeService themeService: IThemeService) {
super(configurationService,
markerService,
outputService,
@ -115,7 +117,8 @@ export class TaskService extends AbstractTaskService {
viewDescriptorService,
workspaceTrustRequestService,
workspaceTrustManagementService,
logService);
logService,
themeService);
this._register(lifecycleService.onBeforeShutdown(event => event.veto(this.beforeShutdown(), 'veto.tasks')));
}