keybindingsEditor.ts 35.7 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/keybindingsEditor';
import { localize } from 'vs/nls';
import { TPromise } from 'vs/base/common/winjs.base';
import { Delayer } from 'vs/base/common/async';
import * as DOM from 'vs/base/browser/dom';
11
import { OS } from 'vs/base/common/platform';
B
Benjamin Pasero 已提交
12
import { dispose } from 'vs/base/common/lifecycle';
S
Sandeep Somavarapu 已提交
13
import { Checkbox } from 'vs/base/browser/ui/checkbox/checkbox';
14
import { HighlightedLabel } from 'vs/base/browser/ui/highlightedlabel/highlightedLabel';
15
import { KeybindingLabel } from 'vs/base/browser/ui/keybindingLabel/keybindingLabel';
16
import { IAction } from 'vs/base/common/actions';
17
import { ActionBar, Separator } from 'vs/base/browser/ui/actionbar/actionbar';
18
import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor';
S
Sandeep Somavarapu 已提交
19
import { EditorOptions } from 'vs/workbench/common/editor';
20
import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry';
21
import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService';
S
Sandeep Somavarapu 已提交
22
import { KeybindingsEditorModel, IKeybindingItemEntry, IListEntry, KEYBINDING_ENTRY_TEMPLATE_ID, KEYBINDING_HEADER_TEMPLATE_ID } from 'vs/workbench/services/preferences/common/keybindingsEditorModel';
23
import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation';
24
import { IKeybindingService, IUserFriendlyKeybinding } from 'vs/platform/keybinding/common/keybinding';
25 26
import { SearchWidget } from 'vs/workbench/parts/preferences/browser/preferencesWidgets';
import { DefineKeybindingWidget } from 'vs/workbench/parts/preferences/browser/keybindingWidgets';
S
Sandeep Somavarapu 已提交
27
import {
S
Sandeep Somavarapu 已提交
28
	IKeybindingsEditor, CONTEXT_KEYBINDING_FOCUS, CONTEXT_KEYBINDINGS_EDITOR, CONTEXT_KEYBINDINGS_SEARCH_FOCUS, KEYBINDINGS_EDITOR_COMMAND_REMOVE, KEYBINDINGS_EDITOR_COMMAND_COPY,
29 30
	KEYBINDINGS_EDITOR_COMMAND_RESET, KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, KEYBINDINGS_EDITOR_COMMAND_DEFINE, KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR, KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS,
	KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS
S
Sandeep Somavarapu 已提交
31
} from 'vs/workbench/parts/preferences/common/preferences';
32 33 34
import { IContextMenuService } from 'vs/platform/contextview/browser/contextView';
import { IKeybindingEditingService } from 'vs/workbench/services/keybinding/common/keybindingEditing';
import { List } from 'vs/base/browser/ui/list/listWidget';
S
Sandeep Somavarapu 已提交
35
import { IDelegate, IRenderer, IListContextMenuEvent, IListEvent } from 'vs/base/browser/ui/list/list';
B
Benjamin Pasero 已提交
36
import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService';
S
Sandeep Somavarapu 已提交
37
import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey';
S
Sandeep Somavarapu 已提交
38
import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent';
39
import { KeyCode, ResolvedKeybinding } from 'vs/base/common/keyCodes';
40
import { listHighlightForeground } from 'vs/platform/theme/common/colorRegistry';
S
Sandeep Somavarapu 已提交
41
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
42
import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions';
43
import { WorkbenchList } from 'vs/platform/list/browser/listService';
44
import { INotificationService } from 'vs/platform/notification/common/notification';
S
Sandeep Somavarapu 已提交
45 46
import { IPreferencesService } from 'vs/workbench/services/preferences/common/preferences';
import { KeybindingsEditorInput } from 'vs/workbench/services/preferences/common/preferencesEditorInput';
47 48 49 50 51

let $ = DOM.$;

export class KeybindingsEditor extends BaseEditor implements IKeybindingsEditor {

M
Matt Bierner 已提交
52
	public static readonly ID: string = 'workbench.editor.keybindings';
S
Sandeep Somavarapu 已提交
53 54 55

	private keybindingsEditorModel: KeybindingsEditorModel;

S
Sandeep Somavarapu 已提交
56
	private headerContainer: HTMLElement;
57
	private searchWidget: SearchWidget;
S
Sandeep Somavarapu 已提交
58

59
	private overlayContainer: HTMLElement;
S
Sandeep Somavarapu 已提交
60
	private defineKeybindingWidget: DefineKeybindingWidget;
61

S
Sandeep Somavarapu 已提交
62
	private keybindingsListContainer: HTMLElement;
S
Sandeep Somavarapu 已提交
63
	private unAssignedKeybindingItemToRevealAndFocus: IKeybindingItemEntry;
64
	private listEntries: IListEntry[];
S
Sandeep Somavarapu 已提交
65
	private keybindingsList: List<IListEntry>;
66

67
	private dimension: DOM.Dimension;
S
Sandeep Somavarapu 已提交
68
	private delayedFiltering: Delayer<void>;
69 70
	private latestEmptyFilters: string[] = [];
	private delayedFilterLogging: Delayer<void>;
S
Sandeep Somavarapu 已提交
71 72
	private keybindingsEditorContextKey: IContextKey<boolean>;
	private keybindingFocusContextKey: IContextKey<boolean>;
S
Sandeep Somavarapu 已提交
73
	private searchFocusContextKey: IContextKey<boolean>;
S
Sandeep Somavarapu 已提交
74
	private sortByPrecedence: Checkbox;
S
Sandeep Somavarapu 已提交
75

76 77 78 79 80
	constructor(
		@ITelemetryService telemetryService: ITelemetryService,
		@IThemeService themeService: IThemeService,
		@IKeybindingService private keybindingsService: IKeybindingService,
		@IContextMenuService private contextMenuService: IContextMenuService,
S
Sandeep Somavarapu 已提交
81
		@IPreferencesService private preferencesService: IPreferencesService,
82
		@IKeybindingEditingService private keybindingEditingService: IKeybindingEditingService,
S
Sandeep Somavarapu 已提交
83
		@IContextKeyService private contextKeyService: IContextKeyService,
84
		@INotificationService private notificationService: INotificationService,
85
		@IClipboardService private clipboardService: IClipboardService,
S
Sandeep Somavarapu 已提交
86 87
		@IInstantiationService private instantiationService: IInstantiationService,
		@IWorkbenchEditorService private editorService: IWorkbenchEditorService
88 89 90 91
	) {
		super(KeybindingsEditor.ID, telemetryService, themeService);
		this.delayedFiltering = new Delayer<void>(300);
		this._register(keybindingsService.onDidUpdateKeybindings(() => this.render()));
S
Sandeep Somavarapu 已提交
92 93

		this.keybindingsEditorContextKey = CONTEXT_KEYBINDINGS_EDITOR.bindTo(this.contextKeyService);
S
Sandeep Somavarapu 已提交
94
		this.searchFocusContextKey = CONTEXT_KEYBINDINGS_SEARCH_FOCUS.bindTo(this.contextKeyService);
S
Sandeep Somavarapu 已提交
95
		this.keybindingFocusContextKey = CONTEXT_KEYBINDING_FOCUS.bindTo(this.contextKeyService);
96
		this.delayedFilterLogging = new Delayer<void>(1000);
97 98
	}

99 100
	createEditor(parent: HTMLElement): void {
		const keybindingsEditorElement = DOM.append(parent, $('div', { class: 'keybindings-editor' }));
101 102 103 104

		this.createOverlayContainer(keybindingsEditorElement);
		this.createHeader(keybindingsEditorElement);
		this.createBody(keybindingsEditorElement);
S
Sandeep Somavarapu 已提交
105

106
		const focusTracker = this._register(DOM.trackFocus(parent));
107 108
		this._register(focusTracker.onDidFocus(() => this.keybindingsEditorContextKey.set(true)));
		this._register(focusTracker.onDidBlur(() => this.keybindingsEditorContextKey.reset()));
109 110
	}

B
Benjamin Pasero 已提交
111
	setInput(input: KeybindingsEditorInput, options: EditorOptions): TPromise<void> {
112 113 114 115
		const oldInput = this.input;
		return super.setInput(input)
			.then(() => {
				if (!input.matches(oldInput)) {
B
Benjamin Pasero 已提交
116
					this.render(options && options.preserveFocus);
117 118 119 120 121 122
				}
			});
	}

	clearInput(): void {
		super.clearInput();
S
Sandeep Somavarapu 已提交
123 124
		this.keybindingsEditorContextKey.reset();
		this.keybindingFocusContextKey.reset();
125 126
	}

127
	layout(dimension: DOM.Dimension): void {
128 129 130 131 132 133
		this.dimension = dimension;
		this.searchWidget.layout(dimension);

		this.overlayContainer.style.width = dimension.width + 'px';
		this.overlayContainer.style.height = dimension.height + 'px';
		this.defineKeybindingWidget.layout(this.dimension);
S
Sandeep Somavarapu 已提交
134

S
Sandeep Somavarapu 已提交
135
		this.layoutKebindingsList();
136 137 138
	}

	focus(): void {
139 140
		const activeKeybindingEntry = this.activeKeybindingEntry;
		if (activeKeybindingEntry) {
S
Sandeep Somavarapu 已提交
141
			this.selectEntry(activeKeybindingEntry);
142 143 144
		} else {
			this.searchWidget.focus();
		}
145 146
	}

S
Sandeep Somavarapu 已提交
147 148 149 150 151
	get activeKeybindingEntry(): IKeybindingItemEntry {
		const focusedElement = this.keybindingsList.getFocusedElements()[0];
		return focusedElement && focusedElement.templateId === KEYBINDING_ENTRY_TEMPLATE_ID ? <IKeybindingItemEntry>focusedElement : null;
	}

152
	getSecondaryActions(): IAction[] {
S
Shobhit Chittora 已提交
153 154 155 156 157 158
		return <IAction[]>[
			<IAction>{
				label: localize('showDefaultKeybindings', "Show Default Keybindings"),
				enabled: true,
				id: KEYBINDINGS_EDITOR_SHOW_DEFAULT_KEYBINDINGS,
				run: (): TPromise<any> => {
S
Sandeep Somavarapu 已提交
159
					this.searchWidget.setValue('@source:default');
S
Shobhit Chittora 已提交
160 161 162 163 164 165 166 167
					return TPromise.as(null);
				}
			},
			<IAction>{
				label: localize('showUserKeybindings', "Show User Keybindings"),
				enabled: true,
				id: KEYBINDINGS_EDITOR_SHOW_USER_KEYBINDINGS,
				run: (): TPromise<any> => {
S
Sandeep Somavarapu 已提交
168
					this.searchWidget.setValue('@source:user');
S
Shobhit Chittora 已提交
169 170 171 172
					return TPromise.as(null);
				}
			}
		];
173 174
	}

S
Sandeep Somavarapu 已提交
175
	defineKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
S
Sandeep Somavarapu 已提交
176
		this.selectEntry(keybindingEntry);
S
Sandeep Somavarapu 已提交
177
		this.showOverlayContainer();
S
Sandeep Somavarapu 已提交
178 179
		return this.defineKeybindingWidget.define().then(key => {
			if (key) {
S
Sandeep Somavarapu 已提交
180 181
				const currentKey = keybindingEntry.keybindingItem.keybinding ? keybindingEntry.keybindingItem.keybinding.getUserSettingsLabel() : '';
				if (currentKey !== key) {
S
Sandeep Somavarapu 已提交
182 183 184 185 186 187 188 189
					this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_DEFINE, keybindingEntry.keybindingItem.command, key);
					return this.keybindingEditingService.editKeybinding(key, keybindingEntry.keybindingItem.keybindingItem)
						.then(() => {
							if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
								this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
							}
						});
				}
S
Sandeep Somavarapu 已提交
190 191 192
			}
			return null;
		}).then(() => {
S
Sandeep Somavarapu 已提交
193
			this.hideOverlayContainer();
S
Sandeep Somavarapu 已提交
194
			this.selectEntry(keybindingEntry);
S
Sandeep Somavarapu 已提交
195 196 197
		}, error => {
			this.hideOverlayContainer();
			this.onKeybindingEditingError(error);
S
Sandeep Somavarapu 已提交
198
			this.selectEntry(keybindingEntry);
S
Sandeep Somavarapu 已提交
199
			return error;
S
Sandeep Somavarapu 已提交
200 201 202
		});
	}

S
Sandeep Somavarapu 已提交
203
	removeKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
S
Sandeep Somavarapu 已提交
204
		this.selectEntry(keybindingEntry);
S
Sandeep Somavarapu 已提交
205
		if (keybindingEntry.keybindingItem.keybinding) { // This should be a pre-condition
206
			this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_REMOVE, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding);
S
Sandeep Somavarapu 已提交
207
			return this.keybindingEditingService.removeKeybinding(keybindingEntry.keybindingItem.keybindingItem)
S
Sandeep Somavarapu 已提交
208
				.then(() => this.focus(),
M
Matt Bierner 已提交
209 210 211 212
					error => {
						this.onKeybindingEditingError(error);
						this.selectEntry(keybindingEntry);
					});
S
Sandeep Somavarapu 已提交
213 214 215 216
		}
		return TPromise.as(null);
	}

217
	resetKeybinding(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
S
Sandeep Somavarapu 已提交
218
		this.selectEntry(keybindingEntry);
219
		this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_RESET, keybindingEntry.keybindingItem.command, keybindingEntry.keybindingItem.keybinding);
S
Sandeep Somavarapu 已提交
220 221 222 223 224 225 226
		return this.keybindingEditingService.resetKeybinding(keybindingEntry.keybindingItem.keybindingItem)
			.then(() => {
				if (!keybindingEntry.keybindingItem.keybinding) { // reveal only if keybinding was added to unassinged. Because the entry will be placed in different position after rendering
					this.unAssignedKeybindingItemToRevealAndFocus = keybindingEntry;
				}
				this.selectEntry(keybindingEntry);
			},
M
Matt Bierner 已提交
227 228 229 230
				error => {
					this.onKeybindingEditingError(error);
					this.selectEntry(keybindingEntry);
				});
231 232
	}

233
	copyKeybinding(keybinding: IKeybindingItemEntry): TPromise<any> {
S
Sandeep Somavarapu 已提交
234
		this.selectEntry(keybinding);
235
		this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding);
236
		const userFriendlyKeybinding: IUserFriendlyKeybinding = {
A
Alexander 已提交
237 238
			key: keybinding.keybindingItem.keybinding ? keybinding.keybindingItem.keybinding.getUserSettingsLabel() : '',
			command: keybinding.keybindingItem.command
239 240
		};
		if (keybinding.keybindingItem.when) {
241
			userFriendlyKeybinding.when = keybinding.keybindingItem.when;
242 243 244 245 246
		}
		this.clipboardService.writeText(JSON.stringify(userFriendlyKeybinding, null, '  '));
		return TPromise.as(null);
	}

247
	copyKeybindingCommand(keybinding: IKeybindingItemEntry): TPromise<any> {
D
Digized 已提交
248 249
		this.selectEntry(keybinding);
		this.reportKeybindingAction(KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND, keybinding.keybindingItem.command, keybinding.keybindingItem.keybinding);
250 251 252 253
		this.clipboardService.writeText(keybinding.keybindingItem.command);
		return TPromise.as(null);
	}

S
Sandeep Somavarapu 已提交
254 255 256 257
	search(filter: string): void {
		this.searchWidget.focus();
	}

S
Sandeep Somavarapu 已提交
258 259 260 261
	clearSearchResults(): void {
		this.searchWidget.clear();
	}

S
Sandeep Somavarapu 已提交
262
	showSimilarKeybindings(keybindingEntry: IKeybindingItemEntry): TPromise<any> {
S
Sandeep Somavarapu 已提交
263 264 265 266 267 268 269
		const value = `"${keybindingEntry.keybindingItem.keybinding.getAriaLabel()}"`;
		if (value !== this.searchWidget.getValue()) {
			this.searchWidget.setValue(value);
		}
		return TPromise.as(null);
	}

270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286
	showOverlayConflicts(keybinding: [ResolvedKeybinding, ResolvedKeybinding]): void {
		const [firstPart, chordPart] = keybinding;
		let searchLabel = firstPart.getAriaLabel();
		if (chordPart !== null) {
			searchLabel += ' ' + chordPart.getAriaLabel();
		}
		const keybindingsEntries: IKeybindingItemEntry[] = this.keybindingsEditorModel.fetch(searchLabel, this.sortByPrecedence.checked);
		let conflictsList: IKeybindingItemEntry[] = [];
		for (let keybindingEntry of keybindingsEntries) {
			let binding = keybindingEntry.keybindingItem.keybinding;
			if (binding !== null && binding.getAriaLabel() === searchLabel) {
				conflictsList.push(keybindingEntry);
			}
		}
		this.defineKeybindingWidget.printConflicts(conflictsList.length);
	}

287 288 289 290 291
	private createOverlayContainer(parent: HTMLElement): void {
		this.overlayContainer = DOM.append(parent, $('.overlay-container'));
		this.overlayContainer.style.position = 'absolute';
		this.overlayContainer.style.zIndex = '10';
		this.defineKeybindingWidget = this._register(this.instantiationService.createInstance(DefineKeybindingWidget, this.overlayContainer));
292
		this._register(this.defineKeybindingWidget.onDidChange(keybinding => this.showOverlayConflicts(keybinding)));
S
Sandeep Somavarapu 已提交
293 294 295
		this.hideOverlayContainer();
	}

S
Sandeep Somavarapu 已提交
296 297 298 299
	private showOverlayContainer() {
		this.overlayContainer.style.display = 'block';
	}

S
Sandeep Somavarapu 已提交
300 301
	private hideOverlayContainer() {
		this.overlayContainer.style.display = 'none';
302 303 304
	}

	private createHeader(parent: HTMLElement): void {
S
Sandeep Somavarapu 已提交
305
		this.headerContainer = DOM.append(parent, $('.keybindings-header'));
S
Sandeep Somavarapu 已提交
306 307 308

		const searchContainer = DOM.append(this.headerContainer, $('.search-container'));
		this.searchWidget = this._register(this.instantiationService.createInstance(SearchWidget, searchContainer, {
309
			ariaLabel: localize('SearchKeybindings.AriaLabel', "Search keybindings"),
S
Sandeep Somavarapu 已提交
310
			placeholder: localize('SearchKeybindings.Placeholder', "Search keybindings"),
S
Sandeep Somavarapu 已提交
311
			focusKey: this.searchFocusContextKey
312
		}));
313
		this._register(this.searchWidget.onDidChange(searchValue => this.delayedFiltering.trigger(() => this.filterKeybindings())));
314

S
Sandeep Somavarapu 已提交
315 316 317 318 319 320 321 322
		this.sortByPrecedence = this._register(new Checkbox({
			actionClassName: 'sort-by-precedence',
			isChecked: false,
			onChange: () => this.renderKeybindingsEntries(false),
			title: localize('sortByPrecedene', "Sort by Precedence")
		}));
		searchContainer.appendChild(this.sortByPrecedence.domNode);

S
Sandeep Somavarapu 已提交
323 324 325 326 327 328 329 330 331
		this.createOpenKeybindingsElement(this.headerContainer);
	}

	private createOpenKeybindingsElement(parent: HTMLElement): void {
		const openKeybindingsContainer = DOM.append(parent, $('.open-keybindings-container'));
		DOM.append(openKeybindingsContainer, $('', null, localize('header-message', "For advanced customizations open and edit")));
		const fileElement = DOM.append(openKeybindingsContainer, $('.file-name', null, localize('keybindings-file-name', "keybindings.json")));
		fileElement.tabIndex = 0;

S
Sandeep Somavarapu 已提交
332
		this._register(DOM.addDisposableListener(fileElement, DOM.EventType.CLICK, () => this.preferencesService.openGlobalKeybindingSettings(true)));
S
Sandeep Somavarapu 已提交
333 334 335 336 337 338 339 340 341 342
		this._register(DOM.addDisposableListener(fileElement, DOM.EventType.KEY_UP, e => {
			let keyboardEvent = new StandardKeyboardEvent(e);
			switch (keyboardEvent.keyCode) {
				case KeyCode.Enter:
					this.preferencesService.openGlobalKeybindingSettings(true);
					keyboardEvent.preventDefault();
					keyboardEvent.stopPropagation();
					return;
			}
		}));
343 344
	}

S
Sandeep Somavarapu 已提交
345
	private createBody(parent: HTMLElement): void {
346 347 348 349 350
		const bodyContainer = DOM.append(parent, $('.keybindings-body'));
		this.createList(bodyContainer);
	}

	private createList(parent: HTMLElement): void {
S
Sandeep Somavarapu 已提交
351
		this.keybindingsListContainer = DOM.append(parent, $('.keybindings-list-container'));
S
Sandeep Somavarapu 已提交
352

353
		this.keybindingsList = this._register(this.instantiationService.createInstance(WorkbenchList, this.keybindingsListContainer, new Delegate(), [new KeybindingHeaderRenderer(), new KeybindingItemRenderer(this, this.keybindingsService)],
354
			{ identityProvider: e => e.id, mouseSupport: true, ariaLabel: localize('keybindingsLabel', "Keybindings") })) as WorkbenchList<IListEntry>;
S
Sandeep Somavarapu 已提交
355 356
		this._register(this.keybindingsList.onContextMenu(e => this.onContextMenu(e)));
		this._register(this.keybindingsList.onFocusChange(e => this.onFocusChange(e)));
357
		this._register(this.keybindingsList.onDidFocus(() => {
S
Sandeep Somavarapu 已提交
358 359
			DOM.addClass(this.keybindingsList.getHTMLElement(), 'focused');
		}));
360
		this._register(this.keybindingsList.onDidBlur(() => {
S
Sandeep Somavarapu 已提交
361 362 363
			DOM.removeClass(this.keybindingsList.getHTMLElement(), 'focused');
			this.keybindingFocusContextKey.reset();
		}));
364 365
		this._register(this.keybindingsList.onMouseDblClick(() => this.defineKeybinding(this.activeKeybindingEntry)));
		this._register(this.keybindingsList.onKeyDown(e => {
S
Sandeep Somavarapu 已提交
366 367 368 369 370 371 372 373 374
			const event = new StandardKeyboardEvent(e);
			if (event.keyCode === KeyCode.Enter) {
				const keybindingEntry = this.activeKeybindingEntry;
				if (keybindingEntry) {
					this.defineKeybinding(this.activeKeybindingEntry);
				}
				e.stopPropagation();
			}
		}));
375 376
	}

B
Benjamin Pasero 已提交
377
	private render(preserveFocus?: boolean): TPromise<any> {
378 379
		if (this.input) {
			return this.input.resolve()
S
Sandeep Somavarapu 已提交
380
				.then((keybindingsModel: KeybindingsEditorModel) => this.keybindingsEditorModel = keybindingsModel)
381
				.then(() => {
382
					const editorActionsLabels: { [id: string]: string; } = EditorExtensionsRegistry.getEditorActions().reduce((editorActions, editorAction) => {
383 384 385 386 387
						editorActions[editorAction.id] = editorAction.label;
						return editorActions;
					}, {});
					return this.keybindingsEditorModel.resolve(editorActionsLabels);
				})
B
Benjamin Pasero 已提交
388
				.then(() => this.renderKeybindingsEntries(false, preserveFocus));
389 390 391 392
		}
		return TPromise.as(null);
	}

393
	private filterKeybindings(): void {
S
Sandeep Somavarapu 已提交
394 395
		this.renderKeybindingsEntries(this.searchWidget.hasFocus());
		this.delayedFilterLogging.trigger(() => this.reportFilteringUsed(this.searchWidget.getValue()));
396 397
	}

B
Benjamin Pasero 已提交
398
	private renderKeybindingsEntries(reset: boolean, preserveFocus?: boolean): void {
S
Sandeep Somavarapu 已提交
399
		if (this.keybindingsEditorModel) {
S
Sandeep Somavarapu 已提交
400 401
			const filter = this.searchWidget.getValue();
			const keybindingsEntries: IKeybindingItemEntry[] = this.keybindingsEditorModel.fetch(filter, this.sortByPrecedence.checked);
402 403 404
			if (keybindingsEntries.length === 0) {
				this.latestEmptyFilters.push(filter);
			}
S
Sandeep Somavarapu 已提交
405 406 407 408
			const currentSelectedIndex = this.keybindingsList.getSelection()[0];
			this.listEntries = [{ id: 'keybinding-header-entry', templateId: KEYBINDING_HEADER_TEMPLATE_ID }, ...keybindingsEntries];
			this.keybindingsList.splice(0, this.keybindingsList.length, this.listEntries);
			this.layoutKebindingsList();
409

410 411 412 413 414 415 416 417 418 419 420 421 422
			if (reset) {
				this.keybindingsList.setSelection([]);
				this.keybindingsList.setFocus([]);
			} else {
				if (this.unAssignedKeybindingItemToRevealAndFocus) {
					const index = this.getNewIndexOfUnassignedKeybinding(this.unAssignedKeybindingItemToRevealAndFocus);
					if (index !== -1) {
						this.keybindingsList.reveal(index, 0.2);
						this.selectEntry(index);
					}
					this.unAssignedKeybindingItemToRevealAndFocus = null;
				} else if (currentSelectedIndex !== -1 && currentSelectedIndex < this.listEntries.length) {
					this.selectEntry(currentSelectedIndex);
B
Benjamin Pasero 已提交
423
				} else if (this.editorService.getActiveEditor() === this && !preserveFocus) {
S
Sandeep Somavarapu 已提交
424
					this.focus();
S
Sandeep Somavarapu 已提交
425
				}
S
Sandeep Somavarapu 已提交
426
			}
427 428 429
		}
	}

S
Sandeep Somavarapu 已提交
430 431 432 433 434 435
	private layoutKebindingsList(): void {
		const listHeight = this.dimension.height - (DOM.getDomNodePagePosition(this.headerContainer).height + 12 /*padding*/);
		this.keybindingsListContainer.style.height = `${listHeight}px`;
		this.keybindingsList.layout(listHeight);
	}

S
Sandeep Somavarapu 已提交
436 437 438 439 440 441 442
	private getIndexOf(listEntry: IListEntry): number {
		const index = this.listEntries.indexOf(listEntry);
		if (index === -1) {
			for (let i = 0; i < this.listEntries.length; i++) {
				if (this.listEntries[i].id === listEntry.id) {
					return i;
				}
443
			}
S
Sandeep Somavarapu 已提交
444 445 446 447 448 449 450
		}
		return index;
	}

	private getNewIndexOfUnassignedKeybinding(unassignedKeybinding: IKeybindingItemEntry): number {
		for (let index = 0; index < this.listEntries.length; index++) {
			const entry = this.listEntries[index];
451
			if (entry.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
S
Sandeep Somavarapu 已提交
452 453 454
				const keybindingItemEntry = (<IKeybindingItemEntry>entry);
				if (keybindingItemEntry.keybindingItem.command === unassignedKeybinding.keybindingItem.command) {
					return index;
455 456 457
				}
			}
		}
S
Sandeep Somavarapu 已提交
458 459 460
		return -1;
	}

S
Sandeep Somavarapu 已提交
461 462
	private selectEntry(keybindingItemEntry: IKeybindingItemEntry | number): void {
		const index = typeof keybindingItemEntry === 'number' ? keybindingItemEntry : this.getIndexOf(keybindingItemEntry);
463 464 465
		if (index !== -1) {
			this.keybindingsList.getHTMLElement().focus();
			this.keybindingsList.setFocus([index]);
S
Sandeep Somavarapu 已提交
466
			this.keybindingsList.setSelection([index]);
467
		}
468 469
	}

S
Sandeep Somavarapu 已提交
470 471 472 473
	focusKeybindings(): void {
		this.keybindingsList.getHTMLElement().focus();
		const currentFocusIndices = this.keybindingsList.getFocus();
		this.keybindingsList.setFocus([currentFocusIndices.length ? currentFocusIndices[0] : 0]);
S
Sandeep Somavarapu 已提交
474 475
	}

S
Sandeep Somavarapu 已提交
476 477
	private onContextMenu(e: IListContextMenuEvent<IListEntry>): void {
		if (e.element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
S
Sandeep Somavarapu 已提交
478
			this.selectEntry(<IKeybindingItemEntry>e.element);
S
Sandeep Somavarapu 已提交
479 480
			this.contextMenuService.showContextMenu({
				getAnchor: () => e.anchor,
481 482
				getActions: () => TPromise.as([
					this.createCopyAction(<IKeybindingItemEntry>e.element),
483
					this.createCopyCommandAction(<IKeybindingItemEntry>e.element),
484 485 486
					new Separator(),
					this.createDefineAction(<IKeybindingItemEntry>e.element),
					this.createRemoveAction(<IKeybindingItemEntry>e.element),
S
Sandeep Somavarapu 已提交
487 488
					this.createResetAction(<IKeybindingItemEntry>e.element),
					new Separator(),
489
					this.createShowConflictsAction(<IKeybindingItemEntry>e.element)])
S
Sandeep Somavarapu 已提交
490
			});
491 492 493
		}
	}

S
Sandeep Somavarapu 已提交
494
	private onFocusChange(e: IListEvent<IListEntry>): void {
S
Sandeep Somavarapu 已提交
495 496 497 498 499 500
		this.keybindingFocusContextKey.reset();
		const element = e.elements[0];
		if (!element) {
			return;
		}
		if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) {
S
Sandeep Somavarapu 已提交
501
			this.keybindingsList.focusNext();
S
Sandeep Somavarapu 已提交
502 503 504 505
			return;
		}
		if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
			this.keybindingFocusContextKey.set(true);
506 507 508
		}
	}

509 510 511 512 513 514 515 516 517
	private createDefineAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
		return <IAction>{
			label: keybindingItemEntry.keybindingItem.keybinding ? localize('changeLabel', "Change Keybinding") : localize('addLabel', "Add Keybinding"),
			enabled: true,
			id: KEYBINDINGS_EDITOR_COMMAND_DEFINE,
			run: () => this.defineKeybinding(keybindingItemEntry)
		};
	}

S
Sandeep Somavarapu 已提交
518
	private createRemoveAction(keybindingItem: IKeybindingItemEntry): IAction {
519 520
		return <IAction>{
			label: localize('removeLabel', "Remove Keybinding"),
S
Sandeep Somavarapu 已提交
521 522 523
			enabled: !!keybindingItem.keybindingItem.keybinding,
			id: KEYBINDINGS_EDITOR_COMMAND_REMOVE,
			run: () => this.removeKeybinding(keybindingItem)
524 525
		};
	}
526

527 528 529
	private createResetAction(keybindingItem: IKeybindingItemEntry): IAction {
		return <IAction>{
			label: localize('resetLabel', "Reset Keybinding"),
S
Sandeep Somavarapu 已提交
530
			enabled: !keybindingItem.keybindingItem.keybindingItem.isDefault,
531 532 533 534 535
			id: KEYBINDINGS_EDITOR_COMMAND_RESET,
			run: () => this.resetKeybinding(keybindingItem)
		};
	}

S
Sandeep Somavarapu 已提交
536 537
	private createShowConflictsAction(keybindingItem: IKeybindingItemEntry): IAction {
		return <IAction>{
S
Sandeep Somavarapu 已提交
538
			label: localize('showSameKeybindings', "Show Same Keybindings"),
S
Sandeep Somavarapu 已提交
539
			enabled: !!keybindingItem.keybindingItem.keybinding,
S
Sandeep Somavarapu 已提交
540 541
			id: KEYBINDINGS_EDITOR_COMMAND_SHOW_SIMILAR,
			run: () => this.showSimilarKeybindings(keybindingItem)
S
Sandeep Somavarapu 已提交
542 543 544
		};
	}

545 546 547 548 549 550 551 552
	private createCopyAction(keybindingItem: IKeybindingItemEntry): IAction {
		return <IAction>{
			label: localize('copyLabel', "Copy"),
			enabled: true,
			id: KEYBINDINGS_EDITOR_COMMAND_COPY,
			run: () => this.copyKeybinding(keybindingItem)
		};
	}
S
Sandeep Somavarapu 已提交
553

554 555 556 557 558 559 560 561 562
	private createCopyCommandAction(keybinding: IKeybindingItemEntry): IAction {
		return <IAction>{
			label: localize('copyCommandLabel', "Copy Command"),
			enabled: true,
			id: KEYBINDINGS_EDITOR_COMMAND_COPY_COMMAND,
			run: () => this.copyKeybindingCommand(keybinding)
		};
	}

563 564 565 566 567 568 569
	private reportFilteringUsed(filter: string): void {
		if (filter) {
			let data = {
				filter,
				emptyFilters: this.getLatestEmptyFiltersForTelemetry()
			};
			this.latestEmptyFilters = [];
K
kieferrm 已提交
570
			/* __GDPR__
K
kieferrm 已提交
571 572 573 574 575
				"keybindings.filter" : {
					"filter": { "classification": "CustomerContent", "purpose": "FeatureInsight" },
					"emptyFilters" : { "classification": "SystemMetaData", "purpose": "FeatureInsight" }
				}
			*/
576 577 578 579 580 581 582 583 584 585 586 587 588 589
			this.telemetryService.publicLog('keybindings.filter', data);
		}
	}

	/**
	 * Put a rough limit on the size of the telemetry data, since otherwise it could be an unbounded large amount
	 * of data. 8192 is the max size of a property value. This is rough since that probably includes ""s, etc.
	 */
	private getLatestEmptyFiltersForTelemetry(): string[] {
		let cumulativeSize = 0;
		return this.latestEmptyFilters.filter(filterText => (cumulativeSize += filterText.length) <= 8192);
	}

	private reportKeybindingAction(action: string, command: string, keybinding: ResolvedKeybinding | string): void {
K
kieferrm 已提交
590
		// __GDPR__TODO__ Need to move off dynamic event names and properties as they cannot be registered statically
591 592 593
		this.telemetryService.publicLog(action, { command, keybinding: keybinding ? (typeof keybinding === 'string' ? keybinding : keybinding.getUserSettingsLabel()) : '' });
	}

S
Sandeep Somavarapu 已提交
594
	private onKeybindingEditingError(error: any): void {
595
		this.notificationService.error(typeof error === 'string' ? error : localize('error', "Error '{0}' while editing the keybinding. Please open 'keybindings.json' file and check for errors.", `${error}`));
S
Sandeep Somavarapu 已提交
596
	}
597 598
}

S
Sandeep Somavarapu 已提交
599
class Delegate implements IDelegate<IListEntry> {
600

S
Sandeep Somavarapu 已提交
601 602
	getHeight(element: IListEntry) {
		if (element.templateId === KEYBINDING_ENTRY_TEMPLATE_ID) {
603 604 605 606 607 608
			const commandIdMatched = (<IKeybindingItemEntry>element).keybindingItem.commandLabel && (<IKeybindingItemEntry>element).commandIdMatches;
			const commandDefaultLabelMatched = !!(<IKeybindingItemEntry>element).commandDefaultLabelMatches;
			if (commandIdMatched && commandDefaultLabelMatched) {
				return 60;
			}
			if (commandIdMatched || commandDefaultLabelMatched) {
S
Sandeep Somavarapu 已提交
609 610 611 612 613 614 615 616
				return 40;
			}
		}
		if (element.templateId === KEYBINDING_HEADER_TEMPLATE_ID) {
			return 30;
		}
		return 24;
	}
617

S
Sandeep Somavarapu 已提交
618 619
	getTemplateId(element: IListEntry) {
		return element.templateId;
620 621 622 623
	}
}

interface KeybindingItemTemplate {
S
Sandeep Somavarapu 已提交
624
	parent: HTMLElement;
625 626 627 628 629 630 631
	actions: ActionsColumn;
	command: CommandColumn;
	keybinding: KeybindingColumn;
	source: SourceColumn;
	when: WhenColumn;
}

S
Sandeep Somavarapu 已提交
632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659
class KeybindingHeaderRenderer implements IRenderer<IListEntry, any> {

	get templateId(): string { return KEYBINDING_HEADER_TEMPLATE_ID; }

	constructor() { }

	renderTemplate(container: HTMLElement): any {
		DOM.addClass(container, 'keybindings-list-header');
		DOM.append(container,
			$('.header.actions'),
			$('.header.command', null, localize('command', "Command")),
			$('.header.keybinding', null, localize('keybinding', "Keybinding")),
			$('.header.source', null, localize('source', "Source")),
			$('.header.when', null, localize('when', "When")));
		return {};
	}

	renderElement(entry: IListEntry, index: number, template: any): void {
	}

	disposeTemplate(template: any): void {
	}
}

class KeybindingItemRenderer implements IRenderer<IKeybindingItemEntry, KeybindingItemTemplate> {

	get templateId(): string { return KEYBINDING_ENTRY_TEMPLATE_ID; }

660
	constructor(private keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) { }
S
Sandeep Somavarapu 已提交
661 662 663

	renderTemplate(container: HTMLElement): KeybindingItemTemplate {
		DOM.addClass(container, 'keybinding-item');
S
Sandeep Somavarapu 已提交
664 665 666 667 668 669
		const actions = new ActionsColumn(container, this.keybindingsEditor, this.keybindingsService);
		const command = new CommandColumn(container, this.keybindingsEditor);
		const keybinding = new KeybindingColumn(container, this.keybindingsEditor);
		const source = new SourceColumn(container, this.keybindingsEditor);
		const when = new WhenColumn(container, this.keybindingsEditor);
		container.setAttribute('aria-labelledby', [command.id, keybinding.id, source.id, when.id].join(' '));
S
Sandeep Somavarapu 已提交
670 671
		return {
			parent: container,
S
Sandeep Somavarapu 已提交
672 673 674 675 676
			actions,
			command,
			keybinding,
			source,
			when
S
Sandeep Somavarapu 已提交
677 678 679 680
		};
	}

	renderElement(keybindingEntry: IKeybindingItemEntry, index: number, template: KeybindingItemTemplate): void {
681
		DOM.toggleClass(template.parent, 'odd', index % 2 === 1);
S
Sandeep Somavarapu 已提交
682 683 684 685 686 687 688 689
		template.actions.render(keybindingEntry);
		template.command.render(keybindingEntry);
		template.keybinding.render(keybindingEntry);
		template.source.render(keybindingEntry);
		template.when.render(keybindingEntry);
	}

	disposeTemplate(template: KeybindingItemTemplate): void {
B
Benjamin Pasero 已提交
690
		template.actions.dispose();
S
Sandeep Somavarapu 已提交
691 692 693
	}
}

S
Sandeep Somavarapu 已提交
694 695 696 697 698 699 700
abstract class Column {

	static COUNTER = 0;

	protected element: HTMLElement;
	readonly id: string;

701
	constructor(protected parent: HTMLElement, protected keybindingsEditor: IKeybindingsEditor) {
S
Sandeep Somavarapu 已提交
702 703
		this.element = this.create(parent);
		this.id = this.element.getAttribute('id');
704
	}
S
Sandeep Somavarapu 已提交
705 706

	abstract create(parent: HTMLElement): HTMLElement;
707 708 709 710 711 712
}

class ActionsColumn extends Column {

	private actionBar: ActionBar;

713 714 715 716
	constructor(parent: HTMLElement, keybindingsEditor: IKeybindingsEditor, private keybindingsService: IKeybindingService) {
		super(parent, keybindingsEditor);
	}

S
Sandeep Somavarapu 已提交
717 718
	create(parent: HTMLElement): HTMLElement {
		const actionsContainer = DOM.append(parent, $('.column.actions', { id: 'actions_' + ++Column.COUNTER }));
719
		this.actionBar = new ActionBar(actionsContainer, { animated: false });
S
Sandeep Somavarapu 已提交
720
		return actionsContainer;
721 722 723 724 725 726
	}

	render(keybindingItemEntry: IKeybindingItemEntry): void {
		this.actionBar.clear();
		const actions = [];
		if (keybindingItemEntry.keybindingItem.keybinding) {
S
Sandeep Somavarapu 已提交
727
			actions.push(this.createEditAction(keybindingItemEntry));
728
		} else {
S
Sandeep Somavarapu 已提交
729
			actions.push(this.createAddAction(keybindingItemEntry));
730 731 732 733
		}
		this.actionBar.push(actions, { icon: true });
	}

S
Sandeep Somavarapu 已提交
734
	private createEditAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
735
		const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE);
736 737 738 739
		return <IAction>{
			class: 'edit',
			enabled: true,
			id: 'editKeybinding',
740
			tooltip: keybinding ? localize('editKeybindingLabelWithKey', "Change Keybinding {0}", `(${keybinding.getLabel()})`) : localize('editKeybindingLabel', "Change Keybinding"),
S
Sandeep Somavarapu 已提交
741
			run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry)
742 743 744
		};
	}

S
Sandeep Somavarapu 已提交
745
	private createAddAction(keybindingItemEntry: IKeybindingItemEntry): IAction {
746
		const keybinding = this.keybindingsService.lookupKeybinding(KEYBINDINGS_EDITOR_COMMAND_DEFINE);
747 748 749 750
		return <IAction>{
			class: 'add',
			enabled: true,
			id: 'addKeybinding',
751
			tooltip: keybinding ? localize('addKeybindingLabelWithKey', "Add Keybinding {0}", `(${keybinding.getLabel()})`) : localize('addKeybindingLabel', "Add Keybinding"),
S
Sandeep Somavarapu 已提交
752
			run: () => this.keybindingsEditor.defineKeybinding(keybindingItemEntry)
753 754
		};
	}
B
Benjamin Pasero 已提交
755 756 757 758

	public dispose(): void {
		this.actionBar = dispose(this.actionBar);
	}
759 760 761 762 763 764
}

class CommandColumn extends Column {

	private commandColumn: HTMLElement;

S
Sandeep Somavarapu 已提交
765 766 767
	create(parent: HTMLElement): HTMLElement {
		this.commandColumn = DOM.append(parent, $('.column.command', { id: 'command_' + ++Column.COUNTER }));
		return this.commandColumn;
768 769 770 771 772
	}

	render(keybindingItemEntry: IKeybindingItemEntry): void {
		DOM.clearNode(this.commandColumn);
		const keybindingItem = keybindingItemEntry.keybindingItem;
773 774 775
		const commandIdMatched = !!(keybindingItem.commandLabel && keybindingItemEntry.commandIdMatches);
		const commandDefaultLabelMatched = !!keybindingItemEntry.commandDefaultLabelMatches;
		DOM.toggleClass(this.commandColumn, 'vertical-align-column', commandIdMatched || commandDefaultLabelMatched);
S
Sandeep Somavarapu 已提交
776
		this.commandColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
S
Sandeep Somavarapu 已提交
777
		let commandLabel: HighlightedLabel;
778
		if (keybindingItem.commandLabel) {
S
Sandeep Somavarapu 已提交
779
			commandLabel = new HighlightedLabel(this.commandColumn);
S
Sandeep Somavarapu 已提交
780 781
			commandLabel.set(keybindingItem.commandLabel, keybindingItemEntry.commandLabelMatches);
		}
782
		if (keybindingItemEntry.commandDefaultLabelMatches) {
S
Sandeep Somavarapu 已提交
783 784
			commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.command-default-label')));
			commandLabel.set(keybindingItem.commandDefaultLabel, keybindingItemEntry.commandDefaultLabelMatches);
785
		}
S
Sandeep Somavarapu 已提交
786
		if (keybindingItemEntry.commandIdMatches || !keybindingItem.commandLabel) {
S
Sandeep Somavarapu 已提交
787 788 789 790
			commandLabel = new HighlightedLabel(DOM.append(this.commandColumn, $('.code')));
			commandLabel.set(keybindingItem.command, keybindingItemEntry.commandIdMatches);
		}
		if (commandLabel) {
S
Sandeep Somavarapu 已提交
791
			commandLabel.element.title = keybindingItem.commandLabel ? localize('title', "{0} ({1})", keybindingItem.commandLabel, keybindingItem.command) : keybindingItem.command;
792 793
		}
	}
S
Sandeep Somavarapu 已提交
794 795 796 797

	private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
		return localize('commandAriaLabel', "Command is {0}.", keybindingItemEntry.keybindingItem.commandLabel ? keybindingItemEntry.keybindingItem.commandLabel : keybindingItemEntry.keybindingItem.command);
	}
798 799 800 801 802 803
}

class KeybindingColumn extends Column {

	private keybindingColumn: HTMLElement;

S
Sandeep Somavarapu 已提交
804 805 806
	create(parent: HTMLElement): HTMLElement {
		this.keybindingColumn = DOM.append(parent, $('.column.keybinding', { id: 'keybinding_' + ++Column.COUNTER }));
		return this.keybindingColumn;
807 808 809 810
	}

	render(keybindingItemEntry: IKeybindingItemEntry): void {
		DOM.clearNode(this.keybindingColumn);
S
Sandeep Somavarapu 已提交
811
		this.keybindingColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
812
		if (keybindingItemEntry.keybindingItem.keybinding) {
813
			new KeybindingLabel(this.keybindingColumn, OS).set(keybindingItemEntry.keybindingItem.keybinding, keybindingItemEntry.keybindingMatches);
814 815
		}
	}
S
Sandeep Somavarapu 已提交
816 817 818 819

	private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
		return keybindingItemEntry.keybindingItem.keybinding ? localize('keybindingAriaLabel', "Keybinding is {0}.", keybindingItemEntry.keybindingItem.keybinding.getAriaLabel()) : localize('noKeybinding', "No Keybinding assigned.");
	}
820 821 822 823 824 825
}

class SourceColumn extends Column {

	private sourceColumn: HTMLElement;

S
Sandeep Somavarapu 已提交
826 827 828
	create(parent: HTMLElement): HTMLElement {
		this.sourceColumn = DOM.append(parent, $('.column.source', { id: 'source_' + ++Column.COUNTER }));
		return this.sourceColumn;
829 830 831
	}

	render(keybindingItemEntry: IKeybindingItemEntry): void {
832
		DOM.clearNode(this.sourceColumn);
S
Sandeep Somavarapu 已提交
833
		this.sourceColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
834
		new HighlightedLabel(this.sourceColumn).set(keybindingItemEntry.keybindingItem.source, keybindingItemEntry.sourceMatches);
835
	}
S
Sandeep Somavarapu 已提交
836 837 838 839

	private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
		return localize('sourceAriaLabel', "Source is {0}.", keybindingItemEntry.keybindingItem.source);
	}
840 841 842 843 844 845
}

class WhenColumn extends Column {

	private whenColumn: HTMLElement;

S
Sandeep Somavarapu 已提交
846
	create(parent: HTMLElement): HTMLElement {
847
		const column = DOM.append(parent, $('.column.when'));
S
Sandeep Somavarapu 已提交
848 849
		this.whenColumn = DOM.append(column, $('div', { id: 'when_' + ++Column.COUNTER }));
		return this.whenColumn;
850 851 852
	}

	render(keybindingItemEntry: IKeybindingItemEntry): void {
853
		DOM.clearNode(this.whenColumn);
S
Sandeep Somavarapu 已提交
854
		this.whenColumn.setAttribute('aria-label', this.getAriaLabel(keybindingItemEntry));
855 856
		DOM.toggleClass(this.whenColumn, 'code', !!keybindingItemEntry.keybindingItem.when);
		DOM.toggleClass(this.whenColumn, 'empty', !keybindingItemEntry.keybindingItem.when);
S
Sandeep Somavarapu 已提交
857
		if (keybindingItemEntry.keybindingItem.when) {
858 859
			new HighlightedLabel(this.whenColumn).set(keybindingItemEntry.keybindingItem.when, keybindingItemEntry.whenMatches);
			this.whenColumn.title = keybindingItemEntry.keybindingItem.when;
S
Sandeep Somavarapu 已提交
860 861 862
		} else {
			this.whenColumn.textContent = '';
		}
863
	}
S
Sandeep Somavarapu 已提交
864 865 866 867

	private getAriaLabel(keybindingItemEntry: IKeybindingItemEntry): string {
		return keybindingItemEntry.keybindingItem.when ? localize('whenAriaLabel', "When is {0}.", keybindingItemEntry.keybindingItem.when) : localize('noWhen', "No when context.");
	}
B
Benjamin Pasero 已提交
868 869 870 871 872 873 874
}

registerThemingParticipant((theme: ITheme, collector: ICssStyleCollector) => {
	const listHighlightForegroundColor = theme.getColor(listHighlightForeground);
	if (listHighlightForegroundColor) {
		collector.addRule(`.keybindings-editor > .keybindings-body > .keybindings-list-container .monaco-list-row > .column .highlight { color: ${listHighlightForegroundColor}; }`);
	}
875
});