55
66import { WorkbenchActionExecutedClassification , WorkbenchActionExecutedEvent } from '../../../base/common/actions.js' ;
77import { CancellationToken } from '../../../base/common/cancellation.js' ;
8+ import { Codicon } from '../../../base/common/codicons.js' ;
89import { toErrorMessage } from '../../../base/common/errorMessage.js' ;
910import { isCancellationError } from '../../../base/common/errors.js' ;
1011import { IMatch , matchesBaseContiguousSubString , matchesWords , or } from '../../../base/common/filters.js' ;
1112import { createSingleCallFunction } from '../../../base/common/functional.js' ;
1213import { Disposable , DisposableStore , IDisposable } from '../../../base/common/lifecycle.js' ;
1314import { LRUCache } from '../../../base/common/map.js' ;
15+ import { ThemeIcon } from '../../../base/common/themables.js' ;
1416import { TfIdfCalculator , normalizeTfIdfScores } from '../../../base/common/tfIdf.js' ;
1517import { localize } from '../../../nls.js' ;
1618import { ILocalizedString } from '../../action/common/action.js' ;
@@ -20,9 +22,9 @@ import { IDialogService } from '../../dialogs/common/dialogs.js';
2022import { IInstantiationService } from '../../instantiation/common/instantiation.js' ;
2123import { IKeybindingService } from '../../keybinding/common/keybinding.js' ;
2224import { ILogService } from '../../log/common/log.js' ;
23- import { FastAndSlowPicks , IPickerQuickAccessItem , IPickerQuickAccessProviderOptions , PickerQuickAccessProvider , Picks } from './pickerQuickAccess.js' ;
25+ import { FastAndSlowPicks , IPickerQuickAccessItem , IPickerQuickAccessProviderOptions , PickerQuickAccessProvider , Picks , TriggerAction } from './pickerQuickAccess.js' ;
2426import { IQuickAccessProviderRunOptions } from '../common/quickAccess.js' ;
25- import { IQuickPickSeparator } from '../common/quickInput.js' ;
27+ import { IKeyMods , IQuickPickSeparator } from '../common/quickInput.js' ;
2628import { IStorageService , StorageScope , StorageTarget , WillSaveStateReason } from '../../storage/common/storage.js' ;
2729import { ITelemetryService } from '../../telemetry/common/telemetry.js' ;
2830import { Categories } from '../../action/common/actionCommonCategories.js' ;
@@ -215,9 +217,10 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
215217 let addCommonlyUsedSeparator = ! ! this . options . suggestedCommandIds ;
216218 for ( let i = 0 ; i < filteredCommandPicks . length ; i ++ ) {
217219 const commandPick = filteredCommandPicks [ i ] ;
220+ const isInHistory = ! ! this . commandsHistory . peek ( commandPick . commandId ) ;
218221
219222 // Separator: recently used
220- if ( i === 0 && this . commandsHistory . peek ( commandPick . commandId ) ) {
223+ if ( i === 0 && isInHistory ) {
221224 commandPicks . push ( { type : 'separator' , label : localize ( 'recentlyUsed' , "recently used" ) } ) ;
222225 addOtherSeparator = true ;
223226 }
@@ -228,20 +231,20 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
228231 }
229232
230233 // Separator: commonly used
231- if ( addCommonlyUsedSeparator && commandPick . tfIdfScore === undefined && ! this . commandsHistory . peek ( commandPick . commandId ) && this . options . suggestedCommandIds ?. has ( commandPick . commandId ) ) {
234+ if ( addCommonlyUsedSeparator && commandPick . tfIdfScore === undefined && ! isInHistory && this . options . suggestedCommandIds ?. has ( commandPick . commandId ) ) {
232235 commandPicks . push ( { type : 'separator' , label : localize ( 'commonlyUsed' , "commonly used" ) } ) ;
233236 addOtherSeparator = true ;
234237 addCommonlyUsedSeparator = false ;
235238 }
236239
237240 // Separator: other commands
238- if ( addOtherSeparator && commandPick . tfIdfScore === undefined && ! this . commandsHistory . peek ( commandPick . commandId ) && ! this . options . suggestedCommandIds ?. has ( commandPick . commandId ) ) {
241+ if ( addOtherSeparator && commandPick . tfIdfScore === undefined && ! isInHistory && ! this . options . suggestedCommandIds ?. has ( commandPick . commandId ) ) {
239242 commandPicks . push ( { type : 'separator' , label : localize ( 'morecCommands' , "other commands" ) } ) ;
240243 addOtherSeparator = false ;
241244 }
242245
243246 // Command
244- commandPicks . push ( this . toCommandPick ( commandPick , runOptions ) ) ;
247+ commandPicks . push ( this . toCommandPick ( commandPick , runOptions , isInHistory ) ) ;
245248 }
246249
247250 if ( ! this . hasAdditionalCommandPicks ( filter , token ) ) {
@@ -267,7 +270,7 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
267270 } ;
268271 }
269272
270- private toCommandPick ( commandPick : ICommandQuickPick | IQuickPickSeparator , runOptions ?: IQuickAccessProviderRunOptions ) : ICommandQuickPick | IQuickPickSeparator {
273+ private toCommandPick ( commandPick : ICommandQuickPick | IQuickPickSeparator , runOptions ?: IQuickAccessProviderRunOptions , isRecentlyUsed : boolean = false ) : ICommandQuickPick | IQuickPickSeparator {
271274 if ( commandPick . type === 'separator' ) {
272275 return commandPick ;
273276 }
@@ -277,11 +280,22 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
277280 localize ( 'commandPickAriaLabelWithKeybinding' , "{0}, {1}" , commandPick . label , keybinding . getAriaLabel ( ) ) :
278281 commandPick . label ;
279282
283+ // Add remove button for recently used items (as the last button, to the right)
284+ const existingButtons = commandPick . buttons || [ ] ;
285+ const buttons = isRecentlyUsed ? [
286+ ...existingButtons ,
287+ {
288+ iconClass : ThemeIcon . asClassName ( Codicon . close ) ,
289+ tooltip : localize ( 'removeFromRecentlyUsed' , "Remove from Recently Used" )
290+ }
291+ ] : commandPick . buttons ;
292+
280293 return {
281294 ...commandPick ,
282295 ariaLabel,
283296 detail : this . options . showAlias && commandPick . commandAlias !== commandPick . label ? commandPick . commandAlias : undefined ,
284297 keybinding,
298+ buttons,
285299 accept : async ( ) => {
286300
287301 // Add to history
@@ -303,7 +317,20 @@ export abstract class AbstractCommandsQuickAccessProvider extends PickerQuickAcc
303317 this . dialogService . error ( localize ( 'canNotRun' , "Command '{0}' resulted in an error" , commandPick . label ) , toErrorMessage ( error ) ) ;
304318 }
305319 }
306- }
320+ } ,
321+ trigger : isRecentlyUsed ? ( buttonIndex : number , keyMods : IKeyMods ) : TriggerAction | Promise < TriggerAction > => {
322+ // The remove button is now the last button
323+ const removeButtonIndex = existingButtons . length ;
324+ if ( buttonIndex === removeButtonIndex ) {
325+ this . commandsHistory . remove ( commandPick . commandId ) ;
326+ return TriggerAction . REMOVE_ITEM ;
327+ }
328+ // Handle other buttons (e.g., configure keybinding button)
329+ if ( commandPick . trigger ) {
330+ return commandPick . trigger ( buttonIndex , keyMods ) ;
331+ }
332+ return TriggerAction . NO_ACTION ;
333+ } : commandPick . trigger
307334 } ;
308335 }
309336
@@ -429,6 +456,15 @@ export class CommandsHistory extends Disposable {
429456 return CommandsHistory . cache ?. peek ( commandId ) ;
430457 }
431458
459+ remove ( commandId : string ) : void {
460+ if ( ! CommandsHistory . cache ) {
461+ return ;
462+ }
463+
464+ CommandsHistory . cache . delete ( commandId ) ;
465+ CommandsHistory . hasChanges = true ;
466+ }
467+
432468 private saveState ( ) : void {
433469 if ( ! CommandsHistory . cache ) {
434470 return ;
0 commit comments