Skip to content

Commit 157bd3e

Browse files
Copilotjruales
andauthored
Add remove button to Command Palette recently used commands (#278276)
* Add remove button for recently used commands in Command Palette Co-authored-by: jruales <[email protected]> * Fix button order and TypeScript compilation errors Co-authored-by: jruales <[email protected]> * Simplify by using isInHistory instead of isRecentlyUsedSection Co-authored-by: jruales <[email protected]> --------- Co-authored-by: copilot-swe-agent[bot] <[email protected]> Co-authored-by: jruales <[email protected]>
1 parent 9243a00 commit 157bd3e

File tree

1 file changed

+44
-8
lines changed

1 file changed

+44
-8
lines changed

src/vs/platform/quickinput/browser/commandsQuickAccess.ts

Lines changed: 44 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,14 @@
55

66
import { WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../base/common/actions.js';
77
import { CancellationToken } from '../../../base/common/cancellation.js';
8+
import { Codicon } from '../../../base/common/codicons.js';
89
import { toErrorMessage } from '../../../base/common/errorMessage.js';
910
import { isCancellationError } from '../../../base/common/errors.js';
1011
import { IMatch, matchesBaseContiguousSubString, matchesWords, or } from '../../../base/common/filters.js';
1112
import { createSingleCallFunction } from '../../../base/common/functional.js';
1213
import { Disposable, DisposableStore, IDisposable } from '../../../base/common/lifecycle.js';
1314
import { LRUCache } from '../../../base/common/map.js';
15+
import { ThemeIcon } from '../../../base/common/themables.js';
1416
import { TfIdfCalculator, normalizeTfIdfScores } from '../../../base/common/tfIdf.js';
1517
import { localize } from '../../../nls.js';
1618
import { ILocalizedString } from '../../action/common/action.js';
@@ -20,9 +22,9 @@ import { IDialogService } from '../../dialogs/common/dialogs.js';
2022
import { IInstantiationService } from '../../instantiation/common/instantiation.js';
2123
import { IKeybindingService } from '../../keybinding/common/keybinding.js';
2224
import { 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';
2426
import { IQuickAccessProviderRunOptions } from '../common/quickAccess.js';
25-
import { IQuickPickSeparator } from '../common/quickInput.js';
27+
import { IKeyMods, IQuickPickSeparator } from '../common/quickInput.js';
2628
import { IStorageService, StorageScope, StorageTarget, WillSaveStateReason } from '../../storage/common/storage.js';
2729
import { ITelemetryService } from '../../telemetry/common/telemetry.js';
2830
import { 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

Comments
 (0)