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

'use strict';

8
import * as nls from 'vs/nls';
9 10 11 12
import { KeyMod, KeyChord, KeyCode } from 'vs/base/common/keyCodes';
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IPartService } from 'vs/workbench/services/part/common/partService';
13
import { IWindowsService, IWindowService } from 'vs/platform/windows/common/windows';
14
import { List } from 'vs/base/browser/ui/list/listWidget';
15
import * as errors from 'vs/base/common/errors';
16 17 18
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import URI from 'vs/base/common/uri';
19
import { IEditorOptions, Position as EditorPosition } from 'vs/platform/editor/common/editor';
20
import { WorkbenchListFocusContextKey, IListService, WorkbenchListSupportsMultiSelectContextKey, ListWidget } from 'vs/platform/list/browser/listService';
J
Joao Moreno 已提交
21
import { PagedList } from 'vs/base/browser/ui/list/listPaging';
J
Joao Moreno 已提交
22
import { range } from 'vs/base/common/arrays';
J
Joao Moreno 已提交
23
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
24
import { ITree } from 'vs/base/parts/tree/browser/tree';
B
Benjamin Pasero 已提交
25
import { InEditorZenModeContext, NoEditorsVisibleContext, SingleEditorGroupsContext } from 'vs/workbench/common/editor';
26 27 28

// --- List Commands

29 30 31 32 33 34 35 36 37 38
function ensureDOMFocus(widget: ListWidget): void {
	// it can happen that one of the commands is executed while
	// DOM focus is within another focusable control within the
	// list/tree item. therefor we should ensure that the
	// list/tree has DOM focus again after the command ran.
	if (widget && !widget.isDOMFocused()) {
		widget.domFocus();
	}
}

39 40
export function registerCommands(): void {

41 42 43 44
	function focusDown(accessor: ServicesAccessor, arg2?: number): void {
		const focused = accessor.get(IListService).lastFocusedList;
		const count = typeof arg2 === 'number' ? arg2 : 1;

45 46 47
		// Ensure DOM Focus
		ensureDOMFocus(focused);

48 49 50 51 52
		// List
		if (focused instanceof List || focused instanceof PagedList) {
			const list = focused;

			list.focusNext(count);
I
isidor 已提交
53 54 55 56
			const listFocus = list.getFocus();
			if (listFocus.length) {
				list.reveal(listFocus[0]);
			}
57 58 59 60 61 62 63 64 65 66 67
		}

		// Tree
		else if (focused) {
			const tree = focused;

			tree.focusNext(count, { origin: 'keyboard' });
			tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
		}
	}

68
	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
69
		id: 'list.focusDown',
70
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
71
		when: WorkbenchListFocusContextKey,
72 73 74 75 76
		primary: KeyCode.DownArrow,
		mac: {
			primary: KeyCode.DownArrow,
			secondary: [KeyMod.WinCtrl | KeyCode.KEY_N]
		},
77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113
		handler: (accessor, arg2) => focusDown(accessor, arg2)
	});

	function expandMultiSelection(focused: List<any> | PagedList<any> | ITree, previousFocus: any): void {

		// List
		if (focused instanceof List || focused instanceof PagedList) {
			const list = focused;

			const focus = list.getFocus() ? list.getFocus()[0] : void 0;
			const selection = list.getSelection();
			if (selection && selection.indexOf(focus) >= 0) {
				list.setSelection(selection.filter(s => s !== previousFocus));
			} else {
				list.setSelection(selection.concat(focus));
			}
		}

		// Tree
		else if (focused) {
			const tree = focused;

			const focus = tree.getFocus();
			const selection = tree.getSelection();
			if (selection && selection.indexOf(focus) >= 0) {
				tree.setSelection(selection.filter(s => s !== previousFocus));
			} else {
				tree.setSelection(selection.concat(focus));
			}
		}
	}

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.expandSelectionDown',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: WorkbenchListFocusContextKey,
		primary: KeyMod.Shift | KeyCode.DownArrow,
114
		handler: (accessor, arg2) => {
J
Joao Moreno 已提交
115
			const focused = accessor.get(IListService).lastFocusedList;
116 117

			// List
J
Joao Moreno 已提交
118
			if (focused instanceof List || focused instanceof PagedList) {
119 120
				const list = focused;

121 122 123 124 125 126
				// Focus down first
				const previousFocus = list.getFocus() ? list.getFocus()[0] : void 0;
				focusDown(accessor, arg2);

				// Then adjust selection
				expandMultiSelection(focused, previousFocus);
127 128 129 130 131 132
			}

			// Tree
			else if (focused) {
				const tree = focused;

133 134 135 136 137 138
				// Focus down first
				const previousFocus = tree.getFocus();
				focusDown(accessor, arg2);

				// Then adjust selection
				expandMultiSelection(focused, previousFocus);
139 140 141 142
			}
		}
	});

143 144 145 146
	function focusUp(accessor: ServicesAccessor, arg2?: number): void {
		const focused = accessor.get(IListService).lastFocusedList;
		const count = typeof arg2 === 'number' ? arg2 : 1;

147 148 149
		// Ensure DOM Focus
		ensureDOMFocus(focused);

150 151 152 153 154
		// List
		if (focused instanceof List || focused instanceof PagedList) {
			const list = focused;

			list.focusPrevious(count);
I
isidor 已提交
155 156 157 158
			const listFocus = list.getFocus();
			if (listFocus.length) {
				list.reveal(listFocus[0]);
			}
159 160 161 162 163 164 165 166 167 168 169
		}

		// Tree
		else if (focused) {
			const tree = focused;

			tree.focusPrevious(count, { origin: 'keyboard' });
			tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
		}
	}

170
	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
171
		id: 'list.focusUp',
172
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
173
		when: WorkbenchListFocusContextKey,
174 175 176 177 178
		primary: KeyCode.UpArrow,
		mac: {
			primary: KeyCode.UpArrow,
			secondary: [KeyMod.WinCtrl | KeyCode.KEY_P]
		},
179 180 181 182 183 184 185 186
		handler: (accessor, arg2) => focusUp(accessor, arg2)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.expandSelectionUp',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: WorkbenchListFocusContextKey,
		primary: KeyMod.Shift | KeyCode.UpArrow,
187
		handler: (accessor, arg2) => {
J
Joao Moreno 已提交
188
			const focused = accessor.get(IListService).lastFocusedList;
189 190

			// List
J
Joao Moreno 已提交
191
			if (focused instanceof List || focused instanceof PagedList) {
192 193
				const list = focused;

194 195 196 197 198 199
				// Focus up first
				const previousFocus = list.getFocus() ? list.getFocus()[0] : void 0;
				focusUp(accessor, arg2);

				// Then adjust selection
				expandMultiSelection(focused, previousFocus);
200 201 202 203 204 205
			}

			// Tree
			else if (focused) {
				const tree = focused;

206 207 208 209 210 211
				// Focus up first
				const previousFocus = tree.getFocus();
				focusUp(accessor, arg2);

				// Then adjust selection
				expandMultiSelection(focused, previousFocus);
212 213 214 215 216
			}
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
217
		id: 'list.collapse',
218
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
219
		when: WorkbenchListFocusContextKey,
220 221 222 223 224 225
		primary: KeyCode.LeftArrow,
		mac: {
			primary: KeyCode.LeftArrow,
			secondary: [KeyMod.CtrlCmd | KeyCode.UpArrow]
		},
		handler: (accessor) => {
J
Joao Moreno 已提交
226
			const focused = accessor.get(IListService).lastFocusedList;
227 228

			// Tree only
J
Joao Moreno 已提交
229
			if (focused && !(focused instanceof List || focused instanceof PagedList)) {
230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246
				const tree = focused;
				const focus = tree.getFocus();

				tree.collapse(focus).then(didCollapse => {
					if (focus && !didCollapse) {
						tree.focusParent({ origin: 'keyboard' });

						return tree.reveal(tree.getFocus());
					}

					return void 0;
				}).done(null, errors.onUnexpectedError);
			}
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
247
		id: 'list.expand',
248
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
249
		when: WorkbenchListFocusContextKey,
250 251
		primary: KeyCode.RightArrow,
		handler: (accessor) => {
J
Joao Moreno 已提交
252
			const focused = accessor.get(IListService).lastFocusedList;
253 254

			// Tree only
J
Joao Moreno 已提交
255
			if (focused && !(focused instanceof List || focused instanceof PagedList)) {
256 257 258 259 260 261 262 263 264 265 266 267 268 269 270
				const tree = focused;
				const focus = tree.getFocus();

				tree.expand(focus).then(didExpand => {
					if (focus && !didExpand) {
						tree.focusFirstChild({ origin: 'keyboard' });

						return tree.reveal(tree.getFocus());
					}

					return void 0;
				}).done(null, errors.onUnexpectedError);
			}
		}
	});
271 272

	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
273
		id: 'list.focusPageUp',
274
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
275
		when: WorkbenchListFocusContextKey,
276 277
		primary: KeyCode.PageUp,
		handler: (accessor) => {
J
Joao Moreno 已提交
278
			const focused = accessor.get(IListService).lastFocusedList;
279

280 281 282
			// Ensure DOM Focus
			ensureDOMFocus(focused);

283
			// List
J
Joao Moreno 已提交
284
			if (focused instanceof List || focused instanceof PagedList) {
285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301
				const list = focused;

				list.focusPreviousPage();
				list.reveal(list.getFocus()[0]);
			}

			// Tree
			else if (focused) {
				const tree = focused;

				tree.focusPreviousPage({ origin: 'keyboard' });
				tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
			}
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
302
		id: 'list.focusPageDown',
303
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
304
		when: WorkbenchListFocusContextKey,
305 306
		primary: KeyCode.PageDown,
		handler: (accessor) => {
J
Joao Moreno 已提交
307
			const focused = accessor.get(IListService).lastFocusedList;
308

309 310 311
			// Ensure DOM Focus
			ensureDOMFocus(focused);

312
			// List
J
Joao Moreno 已提交
313
			if (focused instanceof List || focused instanceof PagedList) {
314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330
				const list = focused;

				list.focusNextPage();
				list.reveal(list.getFocus()[0]);
			}

			// Tree
			else if (focused) {
				const tree = focused;

				tree.focusNextPage({ origin: 'keyboard' });
				tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
			}
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
331
		id: 'list.focusFirst',
332
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
333
		when: WorkbenchListFocusContextKey,
334
		primary: KeyCode.Home,
335 336
		handler: accessor => listFocusFirst(accessor)
	});
337

338 339 340
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.focusFirstChild',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
341
		when: WorkbenchListFocusContextKey,
342 343 344
		primary: null,
		handler: accessor => listFocusFirst(accessor, { fromFocused: true })
	});
B
Benjamin Pasero 已提交
345

346
	function listFocusFirst(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
J
Joao Moreno 已提交
347
		const focused = accessor.get(IListService).lastFocusedList;
B
Benjamin Pasero 已提交
348

349 350 351
		// Ensure DOM Focus
		ensureDOMFocus(focused);

352
		// List
J
Joao Moreno 已提交
353
		if (focused instanceof List || focused instanceof PagedList) {
354
			const list = focused;
355

356 357
			list.setFocus([0]);
			list.reveal(0);
358
		}
359 360 361 362 363 364 365 366 367

		// Tree
		else if (focused) {
			const tree = focused;

			tree.focusFirst({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0);
			tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
		}
	}
368 369

	KeybindingsRegistry.registerCommandAndKeybindingRule({
B
Benjamin Pasero 已提交
370
		id: 'list.focusLast',
371
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
372
		when: WorkbenchListFocusContextKey,
373
		primary: KeyCode.End,
374 375
		handler: accessor => listFocusLast(accessor)
	});
376

377 378 379
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.focusLastChild',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
380
		when: WorkbenchListFocusContextKey,
381 382 383
		primary: null,
		handler: accessor => listFocusLast(accessor, { fromFocused: true })
	});
B
Benjamin Pasero 已提交
384

385
	function listFocusLast(accessor: ServicesAccessor, options?: { fromFocused: boolean }): void {
J
Joao Moreno 已提交
386
		const focused = accessor.get(IListService).lastFocusedList;
B
Benjamin Pasero 已提交
387

388 389 390
		// Ensure DOM Focus
		ensureDOMFocus(focused);

391
		// List
J
Joao Moreno 已提交
392
		if (focused instanceof List || focused instanceof PagedList) {
393
			const list = focused;
394

395 396
			list.setFocus([list.length - 1]);
			list.reveal(list.length - 1);
397
		}
398 399 400 401 402 403 404 405 406

		// Tree
		else if (focused) {
			const tree = focused;

			tree.focusLast({ origin: 'keyboard' }, options && options.fromFocused ? tree.getFocus() : void 0);
			tree.reveal(tree.getFocus()).done(null, errors.onUnexpectedError);
		}
	}
407 408 409 410

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.select',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
411
		when: WorkbenchListFocusContextKey,
412
		primary: KeyCode.Enter,
413 414
		mac: {
			primary: KeyCode.Enter,
415
			secondary: [KeyMod.CtrlCmd | KeyCode.DownArrow]
416
		},
417
		handler: (accessor) => {
J
Joao Moreno 已提交
418
			const focused = accessor.get(IListService).lastFocusedList;
419 420

			// List
J
Joao Moreno 已提交
421
			if (focused instanceof List || focused instanceof PagedList) {
422
				const list = focused;
J
Joao Moreno 已提交
423
				list.setSelection(list.getFocus());
J
Joao Moreno 已提交
424
				list.open(list.getFocus());
425 426 427 428 429 430 431 432 433 434 435 436 437 438
			}

			// Tree
			else if (focused) {
				const tree = focused;
				const focus = tree.getFocus();

				if (focus) {
					tree.setSelection([focus], { origin: 'keyboard' });
				}
			}
		}
	});

J
Joao Moreno 已提交
439 440 441
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.selectAll',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
442
		when: ContextKeyExpr.and(WorkbenchListFocusContextKey, WorkbenchListSupportsMultiSelectContextKey),
J
Joao Moreno 已提交
443 444 445 446 447 448 449 450 451 452 453 454
		primary: KeyMod.CtrlCmd | KeyCode.KEY_A,
		handler: (accessor) => {
			const focused = accessor.get(IListService).lastFocusedList;

			// List
			if (focused instanceof List || focused instanceof PagedList) {
				const list = focused;
				list.setSelection(range(list.length));
			}
		}
	});

455 456 457
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.toggleExpand',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
458
		when: WorkbenchListFocusContextKey,
459 460
		primary: KeyCode.Space,
		handler: (accessor) => {
J
Joao Moreno 已提交
461
			const focused = accessor.get(IListService).lastFocusedList;
462 463

			// Tree only
J
Joao Moreno 已提交
464
			if (focused && !(focused instanceof List || focused instanceof PagedList)) {
465 466 467 468 469 470 471 472 473 474 475 476 477
				const tree = focused;
				const focus = tree.getFocus();

				if (focus) {
					tree.toggleExpansion(focus);
				}
			}
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'list.clear',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
J
Joao Moreno 已提交
478
		when: WorkbenchListFocusContextKey,
479 480
		primary: KeyCode.Escape,
		handler: (accessor) => {
J
Joao Moreno 已提交
481
			const focused = accessor.get(IListService).lastFocusedList;
482 483

			// Tree only
J
Joao Moreno 已提交
484
			if (focused && !(focused instanceof List || focused instanceof PagedList)) {
485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500
				const tree = focused;

				if (tree.getSelection().length) {
					tree.clearSelection({ origin: 'keyboard' });

					return void 0;
				}

				if (tree.getFocus()) {
					tree.clearFocus({ origin: 'keyboard' });

					return void 0;
				}
			}
		}
	});
501 502 503 504 505 506

	// --- commands

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.closeWindow', // close the window when the last editor is closed by reusing the same keybinding
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
B
Benjamin Pasero 已提交
507
		when: ContextKeyExpr.and(NoEditorsVisibleContext, SingleEditorGroupsContext),
508 509
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		handler: accessor => {
510 511
			const windowService = accessor.get(IWindowService);
			windowService.closeWindow();
512 513 514 515 516
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.exitZenMode',
517
		weight: KeybindingsRegistry.WEIGHT.editorContrib(-1000),
518 519 520 521
		handler(accessor: ServicesAccessor, configurationOrName: any) {
			const partService = accessor.get(IPartService);
			partService.toggleZenMode();
		},
522
		when: InEditorZenModeContext,
523 524 525 526 527 528 529 530 531 532 533 534 535 536 537
		primary: KeyChord(KeyCode.Escape, KeyCode.Escape)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.quit',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		handler(accessor: ServicesAccessor) {
			const windowsService = accessor.get(IWindowsService);
			windowsService.quit();
		},
		when: void 0,
		primary: KeyMod.CtrlCmd | KeyCode.KEY_Q,
		win: { primary: void 0 }
	});

538
	CommandsRegistry.registerCommand('_workbench.diff', function (accessor: ServicesAccessor, args: [URI, URI, string, string, IEditorOptions, EditorPosition]) {
539
		const editorService = accessor.get(IWorkbenchEditorService);
540 541 542 543
		let [leftResource, rightResource, label, description, options, position] = args;

		if (!options || typeof options !== 'object') {
			options = {
544
				preserveFocus: false
545
			};
546
		}
547 548 549 550 551

		if (!label) {
			label = nls.localize('diffLeftRightLabel', "{0} ⟷ {1}", leftResource.toString(true), rightResource.toString(true));
		}

552
		return editorService.openEditor({ leftResource, rightResource, label, description, options }, position).then(() => {
553 554 555 556
			return void 0;
		});
	});

557
	CommandsRegistry.registerCommand('_workbench.open', function (accessor: ServicesAccessor, args: [URI, IEditorOptions, EditorPosition]) {
558
		const editorService = accessor.get(IWorkbenchEditorService);
559
		const [resource, options, column] = args;
560

561
		return editorService.openEditor({ resource, options }, column).then(() => {
562 563 564
			return void 0;
		});
	});
565 566 567 568 569 570 571 572

	CommandsRegistry.registerCommand('_workbench.removeFromRecentlyOpened', function (accessor: ServicesAccessor, path: string) {
		const windowsService = accessor.get(IWindowsService);

		return windowsService.removeFromRecentlyOpened([path]).then(() => {
			return void 0;
		});
	});
573
}