Skip to content

Commit 455118a

Browse files
committed
Change how recently used tasks are stored
so that a real task can be generated from the data stored. This enables us to fetch recently used tasks before all providers run. Part of microsoft#90087
1 parent bed9944 commit 455118a

3 files changed

Lines changed: 98 additions & 34 deletions

File tree

src/vs/workbench/contrib/tasks/browser/abstractTaskService.ts

Lines changed: 96 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -201,6 +201,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
201201

202202
// private static autoDetectTelemetryName: string = 'taskServer.autoDetect';
203203
private static readonly RecentlyUsedTasks_Key = 'workbench.tasks.recentlyUsedTasks';
204+
private static readonly RecentlyUsedTasks_KeyV2 = 'workbench.tasks.recentlyUsedTasks2';
204205
private static readonly IgnoreTask010DonotShowAgain_key = 'workbench.tasks.ignoreTask010Shown';
205206

206207
private static CustomizationTelemetryEventName: string = 'taskService.customize';
@@ -226,6 +227,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
226227

227228
protected _taskSystem?: ITaskSystem;
228229
protected _taskSystemListener?: IDisposable;
230+
private _recentlyUsedTasksV1: LRUCache<string, string> | undefined;
229231
private _recentlyUsedTasks: LRUCache<string, string> | undefined;
230232

231233
protected _taskRunningState: IContextKey<boolean>;
@@ -647,20 +649,43 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
647649
return Promise.resolve(this._taskSystem.getBusyTasks());
648650
}
649651

652+
public getRecentlyUsedTasksV1(): LRUCache<string, string> {
653+
if (this._recentlyUsedTasksV1) {
654+
return this._recentlyUsedTasksV1;
655+
}
656+
const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
657+
this._recentlyUsedTasksV1 = new LRUCache<string, string>(quickOpenHistoryLimit);
658+
659+
let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
660+
if (storageValue) {
661+
try {
662+
let values: string[] = JSON.parse(storageValue);
663+
if (Array.isArray(values)) {
664+
for (let value of values) {
665+
this._recentlyUsedTasksV1.set(value, value);
666+
}
667+
}
668+
} catch (error) {
669+
// Ignore. We use the empty result
670+
}
671+
}
672+
return this._recentlyUsedTasksV1;
673+
}
674+
650675
public getRecentlyUsedTasks(): LRUCache<string, string> {
651676
if (this._recentlyUsedTasks) {
652677
return this._recentlyUsedTasks;
653678
}
654679
const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
655680
this._recentlyUsedTasks = new LRUCache<string, string>(quickOpenHistoryLimit);
656681

657-
let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
682+
let storageValue = this.storageService.get(AbstractTaskService.RecentlyUsedTasks_KeyV2, StorageScope.WORKSPACE);
658683
if (storageValue) {
659684
try {
660-
let values: string[] = JSON.parse(storageValue);
685+
let values: [string, string][] = JSON.parse(storageValue);
661686
if (Array.isArray(values)) {
662687
for (let value of values) {
663-
this._recentlyUsedTasks.set(value, value);
688+
this._recentlyUsedTasks.set(value[0], value[1]);
664689
}
665690
}
666691
} catch (error) {
@@ -677,25 +702,28 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
677702
}
678703
}
679704

680-
private setRecentlyUsedTask(key: string): void {
681-
this.getRecentlyUsedTasks().set(key, key);
682-
this.saveRecentlyUsedTasks();
705+
private setRecentlyUsedTask(task: Task): void {
706+
const key = task.getRecentlyUsedKey();
707+
if (!InMemoryTask.is(task) && key) {
708+
this.getRecentlyUsedTasks().set(key, JSON.stringify(this.createCustomizableTask(task)));
709+
this.saveRecentlyUsedTasks();
710+
}
683711
}
684712

685713
private saveRecentlyUsedTasks(): void {
686-
if (!this._taskSystem || !this._recentlyUsedTasks) {
714+
if (!this._recentlyUsedTasks) {
687715
return;
688716
}
689717
const quickOpenHistoryLimit = this.configurationService.getValue<number>(QUICKOPEN_HISTORY_LIMIT_CONFIG);
690718
// setting history limit to 0 means no LRU sorting
691719
if (quickOpenHistoryLimit === 0) {
692720
return;
693721
}
694-
let values = this._recentlyUsedTasks.values();
722+
let values = this._recentlyUsedTasks.toJSON();
695723
if (values.length > quickOpenHistoryLimit) {
696724
values = values.slice(0, quickOpenHistoryLimit);
697725
}
698-
this.storageService.store(AbstractTaskService.RecentlyUsedTasks_Key, JSON.stringify(values), StorageScope.WORKSPACE);
726+
this.storageService.store(AbstractTaskService.RecentlyUsedTasks_KeyV2, JSON.stringify(values), StorageScope.WORKSPACE);
699727
}
700728

701729
private openDocumentation(): void {
@@ -999,23 +1027,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
9991027
});
10001028
}
10011029

1002-
public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise<void> {
1003-
const workspaceFolder = task.getWorkspaceFolder();
1004-
if (!workspaceFolder) {
1005-
return Promise.resolve(undefined);
1006-
}
1007-
let configuration = this.getConfiguration(workspaceFolder, task._source.kind);
1008-
if (configuration.hasParseErrors) {
1009-
this.notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.'));
1010-
return Promise.resolve<void>(undefined);
1011-
}
1012-
1013-
let fileConfig = configuration.config;
1014-
let index: number | undefined;
1030+
private createCustomizableTask(task: ContributedTask | CustomTask): TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined {
10151031
let toCustomize: TaskConfig.CustomTask | TaskConfig.ConfiguringTask | undefined;
10161032
let taskConfig = CustomTask.is(task) ? task._source.config : undefined;
10171033
if (taskConfig && taskConfig.element) {
1018-
index = taskConfig.index;
10191034
toCustomize = { ...(taskConfig.element) };
10201035
} else if (ContributedTask.is(task)) {
10211036
toCustomize = {
@@ -1031,19 +1046,38 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
10311046
}
10321047
}
10331048
if (!toCustomize) {
1049+
return undefined;
1050+
}
1051+
if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) {
1052+
toCustomize.problemMatcher = [];
1053+
}
1054+
return toCustomize;
1055+
}
1056+
1057+
public customize(task: ContributedTask | CustomTask, properties?: CustomizationProperties, openConfig?: boolean): Promise<void> {
1058+
const workspaceFolder = task.getWorkspaceFolder();
1059+
if (!workspaceFolder) {
10341060
return Promise.resolve(undefined);
10351061
}
1062+
let configuration = this.getConfiguration(workspaceFolder, task._source.kind);
1063+
if (configuration.hasParseErrors) {
1064+
this.notificationService.warn(nls.localize('customizeParseErrors', 'The current task configuration has errors. Please fix the errors first before customizing a task.'));
1065+
return Promise.resolve<void>(undefined);
1066+
}
1067+
1068+
let fileConfig = configuration.config;
1069+
const toCustomize = this.createCustomizableTask(task);
1070+
if (!toCustomize) {
1071+
return Promise.resolve(undefined);
1072+
}
1073+
const index: number | undefined = CustomTask.is(task) ? task._source.config.index : undefined;
10361074
if (properties) {
10371075
for (let property of Object.getOwnPropertyNames(properties)) {
10381076
let value = (<any>properties)[property];
10391077
if (value !== undefined && value !== null) {
10401078
(<any>toCustomize)[property] = value;
10411079
}
10421080
}
1043-
} else {
1044-
if (toCustomize.problemMatcher === undefined && task.configurationProperties.problemMatchers === undefined || (task.configurationProperties.problemMatchers && task.configurationProperties.problemMatchers.length === 0)) {
1045-
toCustomize.problemMatcher = [];
1046-
}
10471081
}
10481082

10491083
let promise: Promise<void> | undefined;
@@ -1299,10 +1333,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
12991333
this.showOutput();
13001334
}
13011335

1302-
let key = executeResult.task.getRecentlyUsedKey();
1303-
if (key) {
1304-
this.setRecentlyUsedTask(key);
1305-
}
1336+
this.setRecentlyUsedTask(executeResult.task);
13061337
if (executeResult.kind === TaskExecuteKind.Active) {
13071338
let active = executeResult.active;
13081339
if (active && active.same) {
@@ -1402,6 +1433,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
14021433
}
14031434

14041435
private getGroupedTasks(type?: string): Promise<TaskMap> {
1436+
const needsRecentTasksMigration = this.needsRecentTasksMigration();
14051437
return Promise.all([this.extensionService.activateByEvent('onCommand:workbench.action.tasks.runTask'), this.extensionService.whenInstalledExtensionsRegistered()]).then(() => {
14061438
let validTypes: IStringDictionary<boolean> = Object.create(null);
14071439
TaskDefinitionRegistry.all().forEach(definition => validTypes[definition.taskType] = true);
@@ -1558,7 +1590,10 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
15581590
});
15591591

15601592
await Promise.all(customTasksPromises);
1561-
1593+
if (needsRecentTasksMigration) {
1594+
// At this point we have all the tasks and can migrate the recently used tasks.
1595+
this.migrateRecentTasks(result.all());
1596+
}
15621597
return result;
15631598
}, () => {
15641599
// If we can't read the tasks.json file provide at least the contributed tasks
@@ -1989,7 +2024,7 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
19892024
return this.configurationService.getValue<boolean>(QUICKOPEN_DETAIL_CONFIG);
19902025
}
19912026

1992-
private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry): TaskQuickPickEntry[] {
2027+
private createTaskQuickPickEntries(tasks: Task[], group: boolean = false, sort: boolean = false, selectedEntry?: TaskQuickPickEntry, includeRecents: boolean = true): TaskQuickPickEntry[] {
19932028
let count: { [key: string]: number; } = {};
19942029
if (tasks === undefined || tasks === null || tasks.length === 0) {
19952030
return [];
@@ -2054,7 +2089,9 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
20542089
}
20552090
}
20562091
const sorter = this.createSorter();
2057-
fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks'));
2092+
if (includeRecents) {
2093+
fillEntries(entries, recent, nls.localize('recentlyUsed', 'recently used tasks'));
2094+
}
20582095
configured = configured.sort((a, b) => sorter.compare(a, b));
20592096
fillEntries(entries, configured, nls.localize('configured', 'configured tasks'));
20602097
detected = detected.sort((a, b) => sorter.compare(a, b));
@@ -2173,6 +2210,31 @@ export abstract class AbstractTaskService extends Disposable implements ITaskSer
21732210
});
21742211
}
21752212

2213+
private needsRecentTasksMigration(): boolean {
2214+
return (this.getRecentlyUsedTasksV1().size > 0) && (this.getRecentlyUsedTasks().size === 0);
2215+
}
2216+
2217+
public migrateRecentTasks(tasks: Task[]) {
2218+
if (!this.needsRecentTasksMigration()) {
2219+
return;
2220+
}
2221+
let recentlyUsedTasks = this.getRecentlyUsedTasksV1();
2222+
let taskMap: IStringDictionary<Task> = Object.create(null);
2223+
tasks.forEach(task => {
2224+
let key = task.getRecentlyUsedKey();
2225+
if (key) {
2226+
taskMap[key] = task;
2227+
}
2228+
});
2229+
recentlyUsedTasks.keys().reverse().forEach(key => {
2230+
let task = taskMap[key];
2231+
if (task) {
2232+
this.setRecentlyUsedTask(task);
2233+
}
2234+
});
2235+
this.storageService.remove(AbstractTaskService.RecentlyUsedTasks_Key, StorageScope.WORKSPACE);
2236+
}
2237+
21762238
private showIgnoredFoldersMessage(): Promise<void> {
21772239
if (this.ignoredWorkspaceFolders.length === 0 || !this.showIgnoreMessage) {
21782240
return Promise.resolve(undefined);

src/vs/workbench/contrib/tasks/browser/quickOpen.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,7 @@ export abstract class QuickOpenHandler extends Quickopen.QuickOpenHandler {
8585
return Promise.resolve(null);
8686
}
8787
return this.tasks.then((tasks) => {
88+
this.taskService.migrateRecentTasks(tasks);
8889
let entries: Model.QuickOpenEntry[] = [];
8990
if (tasks.length === 0 || token.isCancellationRequested) {
9091
return new Model.QuickOpenModel(entries);

src/vs/workbench/contrib/tasks/common/taskService.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,7 @@ export interface ITaskService {
7676
getTask(workspaceFolder: IWorkspace | IWorkspaceFolder | string, alias: string | TaskIdentifier, compareId?: boolean): Promise<Task | undefined>;
7777
getTasksForGroup(group: string): Promise<Task[]>;
7878
getRecentlyUsedTasks(): LinkedMap<string, string>;
79+
migrateRecentTasks(tasks: Task[]): void;
7980
createSorter(): TaskSorter;
8081

8182
getTaskDescription(task: Task): string | undefined;

0 commit comments

Comments
 (0)