commandsHandler.ts 20.1 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

6 7 8
import * as nls from 'vs/nls';
import * as arrays from 'vs/base/common/arrays';
import * as types from 'vs/base/common/types';
9
import { Language } from 'vs/base/common/platform';
10 11 12
import { Action } from 'vs/base/common/actions';
import { Mode, IEntryRunContext, IAutoFocus, IModel, IQuickNavigateConfiguration } from 'vs/base/parts/quickopen/common/quickOpen';
import { QuickOpenEntryGroup, IHighlight, QuickOpenModel, QuickOpenEntry } from 'vs/base/parts/quickopen/browser/quickOpenModel';
B
Benjamin Pasero 已提交
13
import { IMenuService, MenuId, MenuItemAction } from 'vs/platform/actions/common/actions';
J
Joao Moreno 已提交
14
import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey';
15
import { QuickOpenHandler, IWorkbenchQuickOpenConfiguration } from 'vs/workbench/browser/quickopen';
B
Benjamin Pasero 已提交
16
import { IEditorAction } from 'vs/editor/common/editorCommon';
J
Johannes Rieken 已提交
17
import { matchesWords, matchesPrefix, matchesContiguousSubString, or } from 'vs/base/common/filters';
18
import { IInstantiationService, ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
J
Johannes Rieken 已提交
19 20
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding';
J
Johannes Rieken 已提交
21
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
22
import { registerEditorAction, EditorAction } from 'vs/editor/browser/editorExtensions';
B
Benjamin Pasero 已提交
23
import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage';
D
Dirk Baeumer 已提交
24
import { LRUCache } from 'vs/base/common/map';
B
Benjamin Pasero 已提交
25 26
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
import { ResolvedKeybinding } from 'vs/base/common/keyCodes';
27
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
28
import { isPromiseCanceledError } from 'vs/base/common/errors';
29
import { ICodeEditor } from 'vs/editor/browser/editorBrowser';
30
import { INotificationService } from 'vs/platform/notification/common/notification';
31
import { CancellationToken } from 'vs/base/common/cancellation';
B
Benjamin Pasero 已提交
32
import { IExtensionService } from 'vs/workbench/services/extensions/common/extensions';
B
Benjamin Pasero 已提交
33
import { Disposable } from 'vs/base/common/lifecycle';
34
import { timeout } from 'vs/base/common/async';
E
Erich Gamma 已提交
35

36
export const ALL_COMMANDS_PREFIX = '>';
E
Erich Gamma 已提交
37

38
let lastCommandPaletteInput: string;
D
Dirk Baeumer 已提交
39
let commandHistory: LRUCache<string, number>;
40 41
let commandCounter = 1;

D
Dirk Baeumer 已提交
42 43 44 45 46
interface ISerializedCommandHistory {
	usesLRU?: boolean;
	entries: { key: string; value: number }[];
}

47
function resolveCommandHistory(configurationService: IConfigurationService): number {
48
	const config = <IWorkbenchQuickOpenConfiguration>configurationService.getValue();
49 50 51 52 53 54 55 56 57

	let commandHistory = config.workbench && config.workbench.commandPalette && config.workbench.commandPalette.history;
	if (typeof commandHistory !== 'number') {
		commandHistory = CommandsHistory.DEFAULT_COMMANDS_HISTORY_LENGTH;
	}

	return commandHistory;
}

B
Benjamin Pasero 已提交
58
class CommandsHistory extends Disposable {
59

B
Benjamin Pasero 已提交
60
	static readonly DEFAULT_COMMANDS_HISTORY_LENGTH = 50;
61 62 63 64 65 66 67

	private static readonly PREF_KEY_CACHE = 'commandPalette.mru.cache';
	private static readonly PREF_KEY_COUNTER = 'commandPalette.mru.counter';

	private commandHistoryLength: number;

	constructor(
68 69
		@IStorageService private readonly storageService: IStorageService,
		@IConfigurationService private readonly configurationService: IConfigurationService
70
	) {
B
Benjamin Pasero 已提交
71 72
		super();

73 74 75 76 77 78
		this.updateConfiguration();
		this.load();

		this.registerListeners();
	}

B
Benjamin Pasero 已提交
79 80 81 82 83
	private registerListeners(): void {
		this._register(this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration()));
		this._register(this.storageService.onWillSaveState(() => this.saveState()));
	}

84 85 86 87
	private updateConfiguration(): void {
		this.commandHistoryLength = resolveCommandHistory(this.configurationService);

		if (commandHistory) {
D
Dirk Baeumer 已提交
88
			commandHistory.limit = this.commandHistoryLength;
89 90 91 92
		}
	}

	private load(): void {
93
		const raw = this.storageService.get(CommandsHistory.PREF_KEY_CACHE, StorageScope.GLOBAL);
94
		let serializedCache: ISerializedCommandHistory | undefined;
95 96
		if (raw) {
			try {
D
Dirk Baeumer 已提交
97
				serializedCache = JSON.parse(raw);
98 99 100 101 102
			} catch (error) {
				// invalid data
			}
		}

D
Dirk Baeumer 已提交
103 104 105 106 107 108 109 110 111 112
		commandHistory = new LRUCache<string, number>(this.commandHistoryLength, 1);
		if (serializedCache) {
			let entries: { key: string; value: number }[];
			if (serializedCache.usesLRU) {
				entries = serializedCache.entries;
			} else {
				entries = serializedCache.entries.sort((a, b) => a.value - b.value);
			}
			entries.forEach(entry => commandHistory.set(entry.key, entry.value));
		}
113

114
		commandCounter = this.storageService.getNumber(CommandsHistory.PREF_KEY_COUNTER, StorageScope.GLOBAL, commandCounter);
115 116
	}

B
Benjamin Pasero 已提交
117 118 119 120
	push(commandId: string): void {
		commandHistory.set(commandId, commandCounter++); // set counter to command
	}

121
	peek(commandId: string): number | undefined {
B
Benjamin Pasero 已提交
122
		return commandHistory.peek(commandId);
123 124
	}

B
Benjamin Pasero 已提交
125
	private saveState(): void {
126
		const serializedCache: ISerializedCommandHistory = { usesLRU: true, entries: [] };
D
Dirk Baeumer 已提交
127
		commandHistory.forEach((value, key) => serializedCache.entries.push({ key, value }));
B
Benjamin Pasero 已提交
128

B
Benjamin Pasero 已提交
129 130
		this.storageService.store(CommandsHistory.PREF_KEY_CACHE, JSON.stringify(serializedCache), StorageScope.GLOBAL);
		this.storageService.store(CommandsHistory.PREF_KEY_COUNTER, commandCounter, StorageScope.GLOBAL);
131 132 133 134
	}
}

export class ShowAllCommandsAction extends Action {
E
Erich Gamma 已提交
135

B
Benjamin Pasero 已提交
136 137
	static readonly ID = 'workbench.action.showCommands';
	static readonly LABEL = nls.localize('showTriggerActions', "Show All Commands");
138

139 140 141
	constructor(
		id: string,
		label: string,
142 143
		@IQuickOpenService private readonly quickOpenService: IQuickOpenService,
		@IConfigurationService private readonly configurationService: IConfigurationService
144 145 146 147
	) {
		super(id, label);
	}

148
	run(): Promise<void> {
149
		const config = <IWorkbenchQuickOpenConfiguration>this.configurationService.getValue();
150 151 152 153 154 155 156 157
		const restoreInput = config.workbench && config.workbench.commandPalette && config.workbench.commandPalette.preserveInput === true;

		// Show with last command palette input if any and configured
		let value = ALL_COMMANDS_PREFIX;
		if (restoreInput && lastCommandPaletteInput) {
			value = `${value}${lastCommandPaletteInput}`;
		}

R
Rob Lourens 已提交
158
		this.quickOpenService.show(value, { inputSelection: lastCommandPaletteInput ? { start: 1 /* after prefix */, end: value.length } : undefined });
159

R
Rob Lourens 已提交
160
		return Promise.resolve(undefined);
161 162 163 164 165
	}
}

export class ClearCommandHistoryAction extends Action {

B
Benjamin Pasero 已提交
166 167
	static readonly ID = 'workbench.action.clearCommandHistory';
	static readonly LABEL = nls.localize('clearCommandHistory', "Clear Command History");
168 169 170 171

	constructor(
		id: string,
		label: string,
172
		@IConfigurationService private readonly configurationService: IConfigurationService
173 174 175 176
	) {
		super(id, label);
	}

177
	run(): Promise<void> {
178 179
		const commandHistoryLength = resolveCommandHistory(this.configurationService);
		if (commandHistoryLength > 0) {
D
Dirk Baeumer 已提交
180
			commandHistory = new LRUCache<string, number>(commandHistoryLength);
181 182 183
			commandCounter = 1;
		}

R
Rob Lourens 已提交
184
		return Promise.resolve(undefined);
E
Erich Gamma 已提交
185 186 187
	}
}

188 189 190 191
class CommandPaletteEditorAction extends EditorAction {

	constructor() {
		super({
192
			id: ShowAllCommandsAction.ID,
193 194 195 196
			label: nls.localize('showCommands.label', "Command Palette..."),
			alias: 'Command Palette',
			precondition: null,
			menuOpts: {
B
Benjamin Pasero 已提交
197
				group: 'z_commands',
B
Benjamin Pasero 已提交
198
				order: 1
199
			}
200 201 202
		});
	}

J
Johannes Rieken 已提交
203
	run(accessor: ServicesAccessor, editor: ICodeEditor): Promise<void> {
204 205 206 207 208
		const quickOpenService = accessor.get(IQuickOpenService);

		// Show with prefix
		quickOpenService.show(ALL_COMMANDS_PREFIX);

R
Rob Lourens 已提交
209
		return Promise.resolve(undefined);
210 211 212
	}
}

213
abstract class BaseCommandEntry extends QuickOpenEntryGroup {
214
	private description: string;
215
	private alias: string;
216
	private labelLowercase: string;
217
	private readonly keybindingAriaLabel?: string;
E
Erich Gamma 已提交
218 219

	constructor(
220 221 222
		private commandId: string,
		private keybinding: ResolvedKeybinding,
		private label: string,
223
		alias: string,
224
		highlights: { label: IHighlight[], alias?: IHighlight[] },
225
		private onBeforeRun: (commandId: string) => void,
226
		@INotificationService private readonly notificationService: INotificationService,
A
Alex Dima 已提交
227
		@ITelemetryService protected telemetryService: ITelemetryService
E
Erich Gamma 已提交
228 229 230
	) {
		super();

231
		this.labelLowercase = this.label.toLowerCase();
232
		this.keybindingAriaLabel = keybinding ? keybinding.getAriaLabel() || undefined : undefined;
233

234
		if (this.label !== alias) {
235 236
			this.alias = alias;
		} else {
237
			highlights.alias = undefined;
238 239
		}

240
		this.setHighlights(highlights.label, undefined, highlights.alias);
E
Erich Gamma 已提交
241 242
	}

B
Benjamin Pasero 已提交
243
	getCommandId(): string {
B
Benjamin Pasero 已提交
244 245 246
		return this.commandId;
	}

B
Benjamin Pasero 已提交
247
	getLabel(): string {
248 249 250
		return this.label;
	}

B
Benjamin Pasero 已提交
251
	getSortLabel(): string {
252 253 254
		return this.labelLowercase;
	}

B
Benjamin Pasero 已提交
255
	getDescription(): string {
256 257 258
		return this.description;
	}

B
Benjamin Pasero 已提交
259
	setDescription(description: string): void {
260 261 262
		this.description = description;
	}

B
Benjamin Pasero 已提交
263
	getKeybinding(): ResolvedKeybinding {
264 265 266
		return this.keybinding;
	}

B
Benjamin Pasero 已提交
267
	getDetail(): string {
268
		return this.alias;
E
Erich Gamma 已提交
269 270
	}

B
Benjamin Pasero 已提交
271
	getAriaLabel(): string {
272 273
		if (this.keybindingAriaLabel) {
			return nls.localize('entryAriaLabelWithKey', "{0}, {1}, commands", this.getLabel(), this.keybindingAriaLabel);
274 275
		}

276 277 278
		return nls.localize('entryAriaLabel', "{0}, commands", this.getLabel());
	}

B
Benjamin Pasero 已提交
279
	run(mode: Mode, context: IEntryRunContext): boolean {
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294
		if (mode === Mode.OPEN) {
			this.runAction(this.getAction());

			return true;
		}

		return false;
	}

	protected abstract getAction(): Action | IEditorAction;

	protected runAction(action: Action | IEditorAction): void {

		// Indicate onBeforeRun
		this.onBeforeRun(this.commandId);
E
Erich Gamma 已提交
295 296

		// Use a timeout to give the quick open widget a chance to close itself first
297
		setTimeout(() => {
298
			if (action && (!(action instanceof Action) || action.enabled)) {
E
Erich Gamma 已提交
299
				try {
K
kieferrm 已提交
300
					/* __GDPR__
K
kieferrm 已提交
301 302 303 304 305
						"workbenchActionExecuted" : {
							"id" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" },
							"from": { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
						}
					*/
E
Erich Gamma 已提交
306
					this.telemetryService.publicLog('workbenchActionExecuted', { id: action.id, from: 'quick open' });
307
					(action.run() || Promise.resolve()).then(() => {
308 309 310
						if (action instanceof Action) {
							action.dispose();
						}
311
					}, err => this.onError(err));
E
Erich Gamma 已提交
312 313 314 315
				} catch (error) {
					this.onError(error);
				}
			} else {
316
				this.notificationService.info(nls.localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel()));
E
Erich Gamma 已提交
317
			}
318
		}, 50);
E
Erich Gamma 已提交
319
	}
320 321 322 323 324 325

	private onError(error?: Error): void {
		if (isPromiseCanceledError(error)) {
			return;
		}

326
		this.notificationService.error(error || nls.localize('canNotRun', "Command '{0}' resulted in an error.", this.label));
327
	}
E
Erich Gamma 已提交
328 329 330 331 332
}

class EditorActionCommandEntry extends BaseCommandEntry {

	constructor(
B
Benjamin Pasero 已提交
333
		commandId: string,
334
		keybinding: ResolvedKeybinding,
335 336
		label: string,
		meta: string,
337 338 339
		highlights: { label: IHighlight[], alias: IHighlight[] },
		private action: IEditorAction,
		onBeforeRun: (commandId: string) => void,
340
		@INotificationService notificationService: INotificationService,
E
Erich Gamma 已提交
341 342
		@ITelemetryService telemetryService: ITelemetryService
	) {
343
		super(commandId, keybinding, label, meta, highlights, onBeforeRun, notificationService, telemetryService);
E
Erich Gamma 已提交
344 345
	}

346 347
	protected getAction(): Action | IEditorAction {
		return this.action;
E
Erich Gamma 已提交
348 349 350 351 352 353
	}
}

class ActionCommandEntry extends BaseCommandEntry {

	constructor(
B
Benjamin Pasero 已提交
354
		commandId: string,
355
		keybinding: ResolvedKeybinding,
356
		label: string,
357
		alias: string,
358 359 360
		highlights: { label: IHighlight[], alias: IHighlight[] },
		private action: Action,
		onBeforeRun: (commandId: string) => void,
361
		@INotificationService notificationService: INotificationService,
E
Erich Gamma 已提交
362 363
		@ITelemetryService telemetryService: ITelemetryService
	) {
364
		super(commandId, keybinding, label, alias, highlights, onBeforeRun, notificationService, telemetryService);
E
Erich Gamma 已提交
365 366
	}

367 368
	protected getAction(): Action | IEditorAction {
		return this.action;
E
Erich Gamma 已提交
369 370 371
	}
}

372 373
const wordFilter = or(matchesPrefix, matchesWords, matchesContiguousSubString);

E
Erich Gamma 已提交
374
export class CommandsHandler extends QuickOpenHandler {
375

B
Benjamin Pasero 已提交
376
	static readonly ID = 'workbench.picker.commands';
377

378 379
	private commandHistoryEnabled: boolean;
	private commandsHistory: CommandsHistory;
380
	private extensionsRegistered: boolean;
E
Erich Gamma 已提交
381 382

	constructor(
383 384 385 386 387 388
		@IEditorService private readonly editorService: IEditorService,
		@IInstantiationService private readonly instantiationService: IInstantiationService,
		@IKeybindingService private readonly keybindingService: IKeybindingService,
		@IMenuService private readonly menuService: IMenuService,
		@IConfigurationService private readonly configurationService: IConfigurationService,
		@IExtensionService private readonly extensionService: IExtensionService
E
Erich Gamma 已提交
389 390
	) {
		super();
391 392 393

		this.commandsHistory = this.instantiationService.createInstance(CommandsHistory);

394 395
		this.extensionService.whenInstalledExtensionsRegistered().then(() => this.extensionsRegistered = true);

396
		this.configurationService.onDidChangeConfiguration(e => this.updateConfiguration());
397
		this.updateConfiguration();
E
Erich Gamma 已提交
398 399
	}

400 401
	private updateConfiguration(): void {
		this.commandHistoryEnabled = resolveCommandHistory(this.configurationService) > 0;
E
Erich Gamma 已提交
402 403
	}

J
Johannes Rieken 已提交
404
	getResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel> {
405 406 407
		if (this.extensionsRegistered) {
			return this.doGetResults(searchValue, token);
		}
E
Erich Gamma 已提交
408

409 410 411 412 413 414
		// If extensions are not yet registered, we wait for a little moment to give them
		// a chance to register so that the complete set of commands shows up as result
		// We do not want to delay functionality beyond that time though to keep the commands
		// functional.
		return Promise.race([timeout(800), this.extensionService.whenInstalledExtensionsRegistered().then(() => undefined)]).then(() => this.doGetResults(searchValue, token));
	}
E
Erich Gamma 已提交
415

416 417 418 419
	private doGetResults(searchValue: string, token: CancellationToken): Promise<QuickOpenModel> {
		if (token.isCancellationRequested) {
			return Promise.resolve(new QuickOpenModel([]));
		}
B
Benjamin Pasero 已提交
420

421
		searchValue = searchValue.trim();
E
Erich Gamma 已提交
422

423 424
		// Remember as last command palette input
		lastCommandPaletteInput = searchValue;
E
Erich Gamma 已提交
425

426 427 428 429 430 431
		// Editor Actions
		const activeTextEditorWidget = this.editorService.activeTextEditorWidget;
		let editorActions: IEditorAction[] = [];
		if (activeTextEditorWidget && types.isFunction(activeTextEditorWidget.getSupportedActions)) {
			editorActions = activeTextEditorWidget.getSupportedActions();
		}
432

433
		const editorEntries = this.editorActionsToEntries(editorActions, searchValue);
E
Erich Gamma 已提交
434

435 436 437 438 439
		// Other Actions
		const menu = this.editorService.invokeWithinEditorContext(accessor => this.menuService.createMenu(MenuId.CommandPalette, accessor.get(IContextKeyService)));
		const menuActions = menu.getActions().reduce((r, [, actions]) => [...r, ...actions], <MenuItemAction[]>[]).filter(action => action instanceof MenuItemAction) as MenuItemAction[];
		const commandEntries = this.menuItemActionsToEntries(menuActions, searchValue);
		menu.dispose();
440

441 442
		// Concat
		let entries = [...editorEntries, ...commandEntries];
443

444 445 446 447 448 449 450 451 452 453 454 455 456
		// Remove duplicates
		entries = arrays.distinct(entries, entry => `${entry.getLabel()}${entry.getGroupLabel()}${entry.getCommandId()}`);

		// Handle label clashes
		const commandLabels = new Set<string>();
		entries.forEach(entry => {
			const commandLabel = `${entry.getLabel()}${entry.getGroupLabel()}`;
			if (commandLabels.has(commandLabel)) {
				entry.setDescription(entry.getCommandId());
			} else {
				commandLabels.add(commandLabel);
			}
		});
457

458 459 460 461
		// Sort by MRU order and fallback to name otherwie
		entries = entries.sort((elementA, elementB) => {
			const counterA = this.commandsHistory.peek(elementA.getCommandId());
			const counterB = this.commandsHistory.peek(elementB.getCommandId());
462

463 464 465
			if (counterA && counterB) {
				return counterA > counterB ? -1 : 1; // use more recently used command before older
			}
B
Benjamin Pasero 已提交
466

467 468 469
			if (counterA) {
				return -1; // first command was used, so it wins over the non used one
			}
B
Benjamin Pasero 已提交
470

471 472 473 474 475 476 477
			if (counterB) {
				return 1; // other command was used so it wins over the command
			}

			// both commands were never used, so we sort by name
			return elementA.getSortLabel().localeCompare(elementB.getSortLabel());
		});
478

479 480 481 482 483 484 485 486 487 488 489
		// Introduce group marker border between recently used and others
		// only if we have recently used commands in the result set
		const firstEntry = entries[0];
		if (firstEntry && this.commandsHistory.peek(firstEntry.getCommandId())) {
			firstEntry.setGroupLabel(nls.localize('recentlyUsed', "recently used"));
			for (let i = 1; i < entries.length; i++) {
				const entry = entries[i];
				if (!this.commandsHistory.peek(entry.getCommandId())) {
					entry.setShowBorder(true);
					entry.setGroupLabel(nls.localize('morecCommands', "other commands"));
					break;
490 491
				}
			}
492
		}
E
Erich Gamma 已提交
493

494
		return Promise.resolve(new QuickOpenModel(entries));
E
Erich Gamma 已提交
495 496
	}

A
Alex Dima 已提交
497
	private editorActionsToEntries(actions: IEditorAction[], searchValue: string): EditorActionCommandEntry[] {
B
Benjamin Pasero 已提交
498
		const entries: EditorActionCommandEntry[] = [];
E
Erich Gamma 已提交
499

500
		for (const action of actions) {
B
Benjamin Pasero 已提交
501 502 503
			if (action.id === ShowAllCommandsAction.ID) {
				continue; // avoid duplicates
			}
E
Erich Gamma 已提交
504

B
Benjamin Pasero 已提交
505
			const label = action.label;
506
			if (label) {
507

508
				// Alias for non default languages
509
				const alias = !Language.isDefaultVariant() ? action.alias : null;
B
Benjamin Pasero 已提交
510 511
				const labelHighlights = wordFilter(searchValue, label);
				const aliasHighlights = alias ? wordFilter(searchValue, alias) : null;
512

513
				if (labelHighlights || aliasHighlights) {
514
					entries.push(this.instantiationService.createInstance(EditorActionCommandEntry, action.id, this.keybindingService.lookupKeybinding(action.id), label, alias, { label: labelHighlights, alias: aliasHighlights }, action, (id: string) => this.onBeforeRunCommand(id)));
E
Erich Gamma 已提交
515 516 517 518 519 520 521
				}
			}
		}

		return entries;
	}

522 523 524 525 526 527
	private onBeforeRunCommand(commandId: string): void {

		// Remember in commands history
		this.commandsHistory.push(commandId);
	}

528
	private menuItemActionsToEntries(actions: MenuItemAction[], searchValue: string): ActionCommandEntry[] {
B
Benjamin Pasero 已提交
529
		const entries: ActionCommandEntry[] = [];
E
Erich Gamma 已提交
530 531

		for (let action of actions) {
532 533 534 535 536 537 538
			const title = typeof action.item.title === 'string' ? action.item.title : action.item.title.value;
			let category, label = title;
			if (action.item.category) {
				category = typeof action.item.category === 'string' ? action.item.category : action.item.category.value;
				label = nls.localize('cat.title', "{0}: {1}", category, title);
			}

539 540
			if (label) {
				const labelHighlights = wordFilter(searchValue, label);
541

542
				// Add an 'alias' in original language when running in different locale
543 544
				const aliasTitle = (!Language.isDefaultVariant() && typeof action.item.title !== 'string') ? action.item.title.original : null;
				const aliasCategory = (!Language.isDefaultVariant() && category && action.item.category && typeof action.item.category !== 'string') ? action.item.category.original : null;
J
Johannes Rieken 已提交
545 546 547 548 549 550
				let alias;
				if (aliasTitle && category) {
					alias = aliasCategory ? `${aliasCategory}: ${aliasTitle}` : `${category}: ${aliasTitle}`;
				} else if (aliasTitle) {
					alias = aliasTitle;
				}
551
				const aliasHighlights = alias ? wordFilter(searchValue, alias) : null;
552

553
				if (labelHighlights || aliasHighlights) {
554
					entries.push(this.instantiationService.createInstance(ActionCommandEntry, action.id, this.keybindingService.lookupKeybinding(action.item.id), label, alias, { label: labelHighlights, alias: aliasHighlights }, action, (id: string) => this.onBeforeRunCommand(id)));
555
				}
556
			}
E
Erich Gamma 已提交
557 558 559 560 561
		}

		return entries;
	}

B
Benjamin Pasero 已提交
562
	getAutoFocus(searchValue: string, context: { model: IModel<QuickOpenEntry>, quickNavigateConfiguration?: IQuickNavigateConfiguration }): IAutoFocus {
563
		let autoFocusPrefixMatch: string | undefined = searchValue.trim();
564 565 566

		if (autoFocusPrefixMatch && this.commandHistoryEnabled) {
			const firstEntry = context.model && context.model.entries[0];
D
Dirk Baeumer 已提交
567
			if (firstEntry instanceof BaseCommandEntry && this.commandsHistory.peek(firstEntry.getCommandId())) {
R
Rob Lourens 已提交
568
				autoFocusPrefixMatch = undefined; // keep focus on MRU element if we have history elements
569 570 571
			}
		}

E
Erich Gamma 已提交
572 573
		return {
			autoFocusFirstEntry: true,
574
			autoFocusPrefixMatch
E
Erich Gamma 已提交
575 576 577
		};
	}

B
Benjamin Pasero 已提交
578
	getEmptyLabel(searchString: string): string {
E
Erich Gamma 已提交
579 580
		return nls.localize('noCommandsMatching', "No commands matching");
	}
581
}
582

583
registerEditorAction(CommandPaletteEditorAction);