@@ -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 ) ;
0 commit comments