editorCommands.ts 26.5 KB
Newer Older
S
Sandeep Somavarapu 已提交
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.
 *--------------------------------------------------------------------------------------------*/

import * as nls from 'vs/nls';
import * as types from 'vs/base/common/types';
J
Johannes Rieken 已提交
8 9
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
10
import { TextCompareEditorVisibleContext, EditorInput, IEditorIdentifier, IEditorCommandsContext, ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext, CloseDirection, IEditor, IEditorInput } from 'vs/workbench/common/editor';
11
import { IEditorService } from 'vs/workbench/services/editor/common/editorService';
12
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
13
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
I
isidor 已提交
14
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
15
import { TPromise } from 'vs/base/common/winjs.base';
16
import URI from 'vs/base/common/uri';
B
Benjamin Pasero 已提交
17
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
18
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
I
isidor 已提交
19 20
import { IListService } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
I
isidor 已提交
21
import { distinct } from 'vs/base/common/arrays';
22
import { IEditorGroupsService, IEditorGroup, GroupDirection, GroupLocation, GroupsOrder, preferredGroupDirection, GroupOrientation } from 'vs/workbench/services/group/common/editorGroupsService';
23
import { ContextKeyExpr } from 'vs/platform/contextkey/common/contextkey';
B
Benjamin Pasero 已提交
24
import { IConfigurationService } from 'vs/platform/configuration/common/configuration';
25
import { CommandsRegistry } from 'vs/platform/commands/common/commands';
26

27
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
I
isidor 已提交
28
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
29
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
I
isidor 已提交
30
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
31
export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeEditorGroup';
I
isidor 已提交
32
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
33 34

export const MOVE_ACTIVE_EDITOR_COMMAND_ID = 'moveActiveEditor';
35
export const LAYOUT_EDITOR_GROUPS_COMMAND_ID = 'layoutEditorGroups';
36
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
B
Benjamin Pasero 已提交
37
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
38
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
B
Benjamin Pasero 已提交
39

40 41 42 43 44
export const SPLIT_EDITOR_UP = 'splitEditor.up';
export const SPLIT_EDITOR_DOWN = 'splitEditor.down';
export const SPLIT_EDITOR_LEFT = 'splitEditor.left';
export const SPLIT_EDITOR_RIGHT = 'splitEditor.right';

B
Benjamin Pasero 已提交
45
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
B
Benjamin Pasero 已提交
46
export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
47

48
export interface ActiveEditorMoveArguments {
49
	to?: 'first' | 'last' | 'left' | 'right' | 'up' | 'down' | 'center' | 'position' | 'previous' | 'next';
50 51
	by?: 'tab' | 'group';
	value?: number;
S
Sandeep Somavarapu 已提交
52 53
}

B
Benjamin Pasero 已提交
54
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
S
Sandeep Somavarapu 已提交
55 56 57 58
	if (!types.isObject(arg)) {
		return false;
	}

59
	if (!types.isString(arg.to)) {
S
Sandeep Somavarapu 已提交
60 61 62
		return false;
	}

63
	if (!types.isUndefined(arg.by) && !types.isString(arg.by)) {
S
Sandeep Somavarapu 已提交
64 65 66
		return false;
	}

67
	if (!types.isUndefined(arg.value) && !types.isNumber(arg.value)) {
S
Sandeep Somavarapu 已提交
68 69 70 71 72 73
		return false;
	}

	return true;
};

74
function registerActiveEditorMoveCommand(): void {
A
Alex Dima 已提交
75
	KeybindingsRegistry.registerCommandAndKeybindingRule({
76
		id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
S
Sandeep Somavarapu 已提交
77
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
78
		when: EditorContextKeys.editorTextFocus,
S
Sandeep Somavarapu 已提交
79
		primary: null,
80
		handler: (accessor, args: any) => moveActiveEditor(args, accessor),
S
Sandeep Somavarapu 已提交
81
		description: {
82
			description: nls.localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"),
S
Sandeep Somavarapu 已提交
83 84 85
			args: [
				{
					name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
86
					description: nls.localize('editorCommand.activeEditorMove.arg.description', "Argument Properties:\n\t* 'to': String value providing where to move.\n\t* 'by': String value providing the unit for move (by tab or by group).\n\t* 'value': Number value providing how many positions or an absolute position to move."),
S
Sandeep Somavarapu 已提交
87 88 89 90 91 92 93
					constraint: isActiveEditorMoveArg
				}
			]
		}
	});
}

94 95 96 97
function moveActiveEditor(args: ActiveEditorMoveArguments = Object.create(null), accessor: ServicesAccessor): void {
	args.to = args.to || 'right';
	args.by = args.by || 'tab';
	args.value = typeof args.value === 'number' ? args.value : 1;
S
Sandeep Somavarapu 已提交
98

99
	const activeControl = accessor.get(IEditorService).activeControl;
100
	if (activeControl) {
101
		switch (args.by) {
102 103 104 105
			case 'tab':
				return moveActiveTab(args, activeControl, accessor);
			case 'group':
				return moveActiveEditorToGroup(args, activeControl, accessor);
106
		}
S
Sandeep Somavarapu 已提交
107 108 109
	}
}

110 111 112
function moveActiveTab(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
	const group = control.group;
	let index = group.getIndexOfEditor(control.input);
S
Sandeep Somavarapu 已提交
113
	switch (args.to) {
114
		case 'first':
S
Sandeep Somavarapu 已提交
115 116
			index = 0;
			break;
117
		case 'last':
118
			index = group.count - 1;
S
Sandeep Somavarapu 已提交
119
			break;
120
		case 'left':
121
			index = index - args.value;
S
Sandeep Somavarapu 已提交
122
			break;
123
		case 'right':
124
			index = index + args.value;
S
Sandeep Somavarapu 已提交
125
			break;
126
		case 'center':
127
			index = Math.round(group.count / 2) - 1;
S
Sandeep Somavarapu 已提交
128
			break;
129
		case 'position':
130
			index = args.value - 1;
S
Sandeep Somavarapu 已提交
131 132
			break;
	}
133

134
	index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
135
	group.moveEditor(control.input, group, { index });
S
Sandeep Somavarapu 已提交
136 137
}

138
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
139
	const editorGroupService = accessor.get(IEditorGroupsService);
140 141 142

	const groups = editorGroupService.groups;
	const sourceGroup = control.group;
143
	let targetGroup: IEditorGroup;
144

S
Sandeep Somavarapu 已提交
145
	switch (args.to) {
146
		case 'left':
147 148
			targetGroup = editorGroupService.findGroup({ direction: GroupDirection.LEFT }, sourceGroup);
			if (!targetGroup) {
B
Benjamin Pasero 已提交
149
				targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.LEFT);
150
			}
151 152
			break;
		case 'right':
153 154
			targetGroup = editorGroupService.findGroup({ direction: GroupDirection.RIGHT }, sourceGroup);
			if (!targetGroup) {
B
Benjamin Pasero 已提交
155
				targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.RIGHT);
156
			}
S
Sandeep Somavarapu 已提交
157
			break;
158
		case 'up':
159 160
			targetGroup = editorGroupService.findGroup({ direction: GroupDirection.UP }, sourceGroup);
			if (!targetGroup) {
B
Benjamin Pasero 已提交
161
				targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.UP);
162
			}
S
Sandeep Somavarapu 已提交
163
			break;
164
		case 'down':
165 166
			targetGroup = editorGroupService.findGroup({ direction: GroupDirection.DOWN }, sourceGroup);
			if (!targetGroup) {
B
Benjamin Pasero 已提交
167
				targetGroup = editorGroupService.addGroup(sourceGroup, GroupDirection.DOWN);
168
			}
S
Sandeep Somavarapu 已提交
169
			break;
170
		case 'first':
171
			targetGroup = editorGroupService.findGroup({ location: GroupLocation.FIRST }, sourceGroup);
S
Sandeep Somavarapu 已提交
172
			break;
173
		case 'last':
174 175 176 177 178 179 180
			targetGroup = editorGroupService.findGroup({ location: GroupLocation.LAST }, sourceGroup);
			break;
		case 'previous':
			targetGroup = editorGroupService.findGroup({ location: GroupLocation.PREVIOUS }, sourceGroup);
			break;
		case 'next':
			targetGroup = editorGroupService.findGroup({ location: GroupLocation.NEXT }, sourceGroup);
S
Sandeep Somavarapu 已提交
181
			break;
182 183 184 185 186
		case 'center':
			targetGroup = groups[(groups.length / 2) - 1];
			break;
		case 'position':
			targetGroup = groups[args.value - 1];
S
Sandeep Somavarapu 已提交
187 188
			break;
	}
189

190 191
	if (targetGroup) {
		sourceGroup.moveEditor(control.input, targetGroup);
B
Benjamin Pasero 已提交
192
		targetGroup.focus();
193
	}
194 195
}

196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268
function registerEditorGroupsLayoutCommand(): void {
	CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, applyEditorGroupLayout);
}

export interface GroupLayoutArgument {
	size?: number;
	groups?: Array<GroupLayoutArgument>;
}

export interface EditorGroupLayout {
	orientation: GroupOrientation;
	groups: GroupLayoutArgument[];
}

function applyEditorGroupLayout(accessor: ServicesAccessor, args: EditorGroupLayout): void {
	if (!args || typeof args !== 'object') {
		return;
	}

	const editorGroupService = accessor.get(IEditorGroupsService);

	// Reduce to one editor group
	mergeAllGroups(editorGroupService);

	// Apply orientation
	if (typeof args.orientation === 'number') {
		editorGroupService.setGroupOrientation(args.orientation);
	}

	// Build layout
	function buildLayout(groups: IEditorGroup[], descriptions: GroupLayoutArgument[], direction: GroupDirection): void {
		if (descriptions.length === 0) {
			return; // we need at least one group to layout
		}

		// Add a group for each item in the description
		let totalProportions = 0;
		descriptions.forEach((description, index) => {
			if (index > 0) {
				groups.push(editorGroupService.addGroup(groups[index - 1], direction));
			}

			if (typeof description.size === 'number') {
				totalProportions += description.size;
			}
		});

		// Apply proportions if they are valid (sum() === 1)
		if (totalProportions === 1) {
			const totalSize = groups.map(group => editorGroupService.getSize(group)).reduce(((prev, cur) => prev + cur));
			descriptions.forEach((description, index) => {
				editorGroupService.setSize(groups[index], totalSize * description.size);
			});
		}

		// Continue building layout if description.groups is array-type
		descriptions.forEach((description, index) => {
			if (Array.isArray(description.groups)) {
				buildLayout([groups[index]], description.groups, direction === GroupDirection.RIGHT ? GroupDirection.DOWN : GroupDirection.RIGHT);
			}
		});
	}

	buildLayout([editorGroupService.groups[0]], args.groups, editorGroupService.orientation === GroupOrientation.HORIZONTAL ? GroupDirection.RIGHT : GroupDirection.DOWN);
}

export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
	const firstGroup = editorGroupService.groups[0];
	while (editorGroupService.count > 1) {
		editorGroupService.mergeGroup(editorGroupService.findGroup({ location: GroupLocation.NEXT }, firstGroup), firstGroup);
	}
}

269 270 271 272
function registerDiffEditorCommands(): void {
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.nextChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
273
		when: TextCompareEditorVisibleContext,
274 275 276 277 278 279 280
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, true)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.previousChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
281
		when: TextCompareEditorVisibleContext,
282 283 284 285 286
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, false)
	});

	function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
287
		const editorService = accessor.get(IEditorService);
288
		const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
289 290 291 292 293 294 295

		if (candidates.length > 0) {
			next ? (<TextDiffEditor>candidates[0]).getDiffNavigator().next() : (<TextDiffEditor>candidates[0]).getDiffNavigator().previous();
		}
	}

	KeybindingsRegistry.registerCommandAndKeybindingRule({
296 297
		id: TOGGLE_DIFF_INLINE_MODE,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
298
		when: void 0,
299
		primary: void 0,
B
Benjamin Pasero 已提交
300
		handler: (accessor, resource, context: IEditorCommandsContext) => {
301
			const editorGroupService = accessor.get(IEditorGroupsService);
302

I
isidor 已提交
303
			const { control } = resolveCommandsContext(editorGroupService, context);
304 305 306 307
			if (control instanceof TextDiffEditor) {
				const widget = control.getControl();
				const isInlineMode = !widget.renderSideBySide;
				widget.updateOptions(<IDiffEditorOptions>{
308 309 310 311
					renderSideBySide: isInlineMode
				});
			}
		}
312
	});
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
}

function registerOpenEditorAtIndexCommands(): void {

	// Keybindings to focus a specific index in the tab folder if tabs are enabled
	for (let i = 0; i < 9; i++) {
		const editorIndex = i;
		const visibleIndex = i + 1;

		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: 'workbench.action.openEditorAtIndex' + visibleIndex,
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
			when: void 0,
			primary: KeyMod.Alt | toKeyCode(visibleIndex),
			mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
			handler: accessor => {
329
				const editorService = accessor.get(IEditorService);
330

331 332
				const activeControl = editorService.activeControl;
				if (activeControl) {
333
					const editor = activeControl.group.getEditor(editorIndex);
334
					if (editor) {
B
Benjamin Pasero 已提交
335
						return editorService.openEditor(editor).then(() => void 0);
336 337
					}
				}
338 339

				return void 0;
340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356
			}
		});
	}

	function toKeyCode(index: number): KeyCode {
		switch (index) {
			case 0: return KeyCode.KEY_0;
			case 1: return KeyCode.KEY_1;
			case 2: return KeyCode.KEY_2;
			case 3: return KeyCode.KEY_3;
			case 4: return KeyCode.KEY_4;
			case 5: return KeyCode.KEY_5;
			case 6: return KeyCode.KEY_6;
			case 7: return KeyCode.KEY_7;
			case 8: return KeyCode.KEY_8;
			case 9: return KeyCode.KEY_9;
		}
357 358

		return void 0;
359
	}
360 361
}

B
Benjamin Pasero 已提交
362 363 364 365 366 367 368 369 370 371 372 373
function registerFocusEditorGroupAtIndexCommands(): void {

	// Keybindings to focus a specific group (2-8) in the editor area
	for (let i = 1; i < 8; i++) {
		const groupIndex = i;

		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: toCommandId(groupIndex),
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
			when: void 0,
			primary: KeyMod.CtrlCmd | toKeyCode(groupIndex),
			handler: accessor => {
374
				const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392
				const configurationService = accessor.get(IConfigurationService);

				// To keep backwards compatibility (pre-grid), allow to focus a group
				// that does not exist as long as it is the next group after the last
				// opened group. Otherwise we return.
				if (groupIndex > editorGroupService.count) {
					return;
				}

				// Group exists: just focus
				const groups = editorGroupService.getGroups(GroupsOrder.CREATION_TIME);
				if (groups[groupIndex]) {
					return groups[groupIndex].focus();
				}

				// Group does not exist: create new by splitting the active one of the last group
				const direction = preferredGroupDirection(configurationService);
				const lastGroup = editorGroupService.findGroup({ location: GroupLocation.LAST });
B
Benjamin Pasero 已提交
393
				const newGroup = editorGroupService.addGroup(lastGroup, direction);
B
Benjamin Pasero 已提交
394 395 396

				// To keep backwards compatibility (pre-grid) we automatically copy the active editor
				// of the last group over to the new group as long as it supports to be split.
B
Benjamin Pasero 已提交
397
				if (lastGroup.activeEditor && (lastGroup.activeEditor as EditorInput).supportsSplitEditor()) {
B
Benjamin Pasero 已提交
398 399
					lastGroup.copyEditor(lastGroup.activeEditor, newGroup);
				}
B
Benjamin Pasero 已提交
400 401 402

				// Focus
				newGroup.focus();
B
Benjamin Pasero 已提交
403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435
			}
		});
	}

	function toCommandId(index: number): string {
		switch (index) {
			case 1: return 'workbench.action.focusSecondEditorGroup';
			case 2: return 'workbench.action.focusThirdEditorGroup';
			case 3: return 'workbench.action.focusFourthEditorGroup';
			case 4: return 'workbench.action.focusFifthEditorGroup';
			case 5: return 'workbench.action.focusSixthEditorGroup';
			case 6: return 'workbench.action.focusSeventhEditorGroup';
			case 7: return 'workbench.action.focusEighthEditorGroup';
		}

		return void 0;
	}

	function toKeyCode(index: number): KeyCode {
		switch (index) {
			case 1: return KeyCode.KEY_2;
			case 2: return KeyCode.KEY_3;
			case 3: return KeyCode.KEY_4;
			case 4: return KeyCode.KEY_5;
			case 5: return KeyCode.KEY_6;
			case 6: return KeyCode.KEY_7;
			case 7: return KeyCode.KEY_8;
		}

		return void 0;
	}
}

436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476
export function splitEditor(editorGroupService: IEditorGroupsService, direction: GroupDirection, context?: IEditorCommandsContext): void {
	let sourceGroup: IEditorGroup;
	if (context && typeof context.groupId === 'number') {
		sourceGroup = editorGroupService.getGroup(context.groupId);
	} else {
		sourceGroup = editorGroupService.activeGroup;
	}

	// Add group
	const newGroup = editorGroupService.addGroup(sourceGroup, direction);

	// Split editor (if it can be split)
	let editorToCopy: IEditorInput;
	if (context && typeof context.editorIndex === 'number') {
		editorToCopy = sourceGroup.getEditor(context.editorIndex);
	} else {
		editorToCopy = sourceGroup.activeEditor;
	}

	if (editorToCopy && (editorToCopy as EditorInput).supportsSplitEditor()) {
		sourceGroup.copyEditor(editorToCopy, newGroup);
	}

	// Focus
	newGroup.focus();
}

function registerSplitEditorCommands() {
	[
		{ id: SPLIT_EDITOR_UP, direction: GroupDirection.UP },
		{ id: SPLIT_EDITOR_DOWN, direction: GroupDirection.DOWN },
		{ id: SPLIT_EDITOR_LEFT, direction: GroupDirection.LEFT },
		{ id: SPLIT_EDITOR_RIGHT, direction: GroupDirection.RIGHT }
	].forEach(({ id, direction }) => {
		CommandsRegistry.registerCommand(id, function (accessor, resource: URI | object, context: IEditorCommandsContext) {
			splitEditor(accessor.get(IEditorGroupsService), direction, context);
		});
	});
}

function registerCloseEditorCommands() {
477

I
isidor 已提交
478
	KeybindingsRegistry.registerCommandAndKeybindingRule({
479
		id: CLOSE_SAVED_EDITORS_COMMAND_ID,
I
isidor 已提交
480
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
481
		when: void 0,
I
isidor 已提交
482
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
I
isidor 已提交
483
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
484
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
485 486
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
			if (contexts.length === 0 && editorGroupService.activeGroup) {
487
				contexts.push({ groupId: editorGroupService.activeGroup.id }); // If command is triggered from the command palette use the active group
I
isidor 已提交
488
			}
489

I
isidor 已提交
490 491 492
			return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
				editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
			));
493 494 495
		}
	});

I
isidor 已提交
496
	KeybindingsRegistry.registerCommandAndKeybindingRule({
497
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
498
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
499
		when: void 0,
I
isidor 已提交
500
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
I
isidor 已提交
501
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
502
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
503
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
504
			const distinctGroupIds = distinct(contexts.map(c => c.groupId));
505

I
isidor 已提交
506 507
			if (distinctGroupIds.length === 0) {
				distinctGroupIds.push(editorGroupService.activeGroup.id);
508 509
			}

I
isidor 已提交
510 511 512
			return TPromise.join(distinctGroupIds.map(groupId =>
				editorGroupService.getGroup(groupId).closeAllEditors()
			));
513 514 515
		}
	});

I
isidor 已提交
516
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
517
		id: CLOSE_EDITOR_COMMAND_ID,
I
isidor 已提交
518
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
519
		when: void 0,
I
isidor 已提交
520 521
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
I
isidor 已提交
522
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
523
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
524 525 526 527
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
			const activeGroup = editorGroupService.activeGroup;
			if (contexts.length === 0 && activeGroup && activeGroup.activeEditor) {
				contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });
528
			}
529

I
isidor 已提交
530 531 532 533 534 535
			const groupIds = distinct(contexts.map(context => context.groupId));
			return TPromise.join(groupIds.map(groupId => {
				const group = editorGroupService.getGroup(groupId);
				const editors = contexts.filter(c => c.groupId === groupId).map(c => group.getEditor(c.editorIndex));
				return group.closeEditors(editors);
			}));
536
		}
537 538 539 540 541 542 543 544 545
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITOR_GROUP_COMMAND_ID,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext),
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
546
			const editorGroupService = accessor.get(IEditorGroupsService);
547

548
			let group: IEditorGroup;
549 550 551 552 553
			if (context && typeof context.groupId === 'number') {
				group = editorGroupService.getGroup(context.groupId);
			} else {
				group = editorGroupService.activeGroup;
			}
554

555
			editorGroupService.removeGroup(group);
556
		}
557 558
	});

I
isidor 已提交
559
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
560
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
561
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
562 563
		when: void 0,
		primary: void 0,
I
isidor 已提交
564
		mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
I
isidor 已提交
565
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
566
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
567
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
568

569 570
			if (contexts.length === 0) {
				// Cover the case when run from command palette
I
isidor 已提交
571 572 573
				const activeGroup = editorGroupService.activeGroup;
				if (activeGroup && activeGroup.activeEditor) {
					contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });
574 575 576 577
				}
			}

			const groupIds = distinct(contexts.map(context => context.groupId));
578

I
isidor 已提交
579 580 581 582
			return TPromise.join(groupIds.map(groupId => {
				const group = editorGroupService.getGroup(groupId);
				const editors = contexts.filter(c => c.groupId === groupId).map(c => group.getEditor(c.editorIndex));
				const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
583

I
isidor 已提交
584 585
				return group.closeEditors(editorsToClose);
			}));
586 587 588 589 590 591 592 593
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
594
		handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
595
			const editorGroupService = accessor.get(IEditorGroupsService);
596

I
isidor 已提交
597
			const { group, editor } = resolveCommandsContext(editorGroupService, context);
598 599
			if (group && editor) {
				return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
600 601
			}

602 603 604 605 606 607 608 609 610
			return TPromise.as(false);
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: KEEP_EDITOR_COMMAND_ID,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
611
		handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
612
			const editorGroupService = accessor.get(IEditorGroupsService);
613

I
isidor 已提交
614
			const { group, editor } = resolveCommandsContext(editorGroupService, context);
615 616
			if (group && editor) {
				return group.pinEditor(editor);
617 618
			}

I
isidor 已提交
619
			return TPromise.as(false);
620 621
		}
	});
B
Benjamin Pasero 已提交
622 623 624 625 626 627

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: SHOW_EDITORS_IN_GROUP,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
B
Benjamin Pasero 已提交
628
		handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
629
			const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
630 631
			const quickOpenService = accessor.get(IQuickOpenService);

B
Benjamin Pasero 已提交
632
			if (editorGroupService.count <= 1) {
B
Benjamin Pasero 已提交
633 634 635
				return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
			}

B
Benjamin Pasero 已提交
636 637 638
			if (context && typeof context.groupId === 'number') {
				editorGroupService.activateGroup(editorGroupService.getGroup(context.groupId)); // we need the group to be active
			}
B
Benjamin Pasero 已提交
639

B
Benjamin Pasero 已提交
640
			return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
B
Benjamin Pasero 已提交
641 642
		}
	});
643
}
644

645
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661

	// Resolve from context
	let group = context && typeof context.groupId === 'number' ? editorGroupService.getGroup(context.groupId) : undefined;
	let editor = group && typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : undefined;
	let control = group ? group.activeControl : undefined;

	// Fallback to active group as needed
	if (!group) {
		group = editorGroupService.activeGroup;
		editor = <EditorInput>group.activeEditor;
		control = group.activeControl;
	}

	return { group, editor, control };
}

662
export function getMultiSelectedEditorContexts(editorContext: IEditorCommandsContext, listService: IListService, editorGroupService: IEditorGroupsService): IEditorCommandsContext[] {
B
Benjamin Pasero 已提交
663

B
Benjamin Pasero 已提交
664 665
	// First check for a focused list to return the selected items from
	const list = listService.lastFocusedList;
I
isidor 已提交
666
	if (list instanceof List && list.isDOMFocused()) {
B
Benjamin Pasero 已提交
667 668 669 670 671 672 673
		const elementToContext = (element: IEditorIdentifier | IEditorGroup) => {
			if (isEditorGroup(element)) {
				return { groupId: element.id, editorIndex: void 0 };
			}

			return { groupId: element.groupId, editorIndex: editorGroupService.getGroup(element.groupId).getIndexOfEditor(element.editor) };
		};
I
isidor 已提交
674

B
Benjamin Pasero 已提交
675 676 677 678
		const onlyEditorGroupAndEditor = (e: IEditorIdentifier | IEditorGroup) => isEditorGroup(e) || isEditorIdentifier(e);

		const focusedElements: (IEditorIdentifier | IEditorGroup)[] = list.getFocusedElements().filter(onlyEditorGroupAndEditor);
		const focus = editorContext ? editorContext : focusedElements.length ? focusedElements.map(elementToContext)[0] : void 0; // need to take into account when editor context is { group: group }
679 680

		if (focus) {
B
Benjamin Pasero 已提交
681 682
			const selection: (IEditorIdentifier | IEditorGroup)[] = list.getSelectedElements().filter(onlyEditorGroupAndEditor);

683
			// Only respect selection if it contains focused element
B
Benjamin Pasero 已提交
684
			if (selection && selection.some(s => isEditorGroup(s) ? s.id === focus.groupId : s.groupId === focus.groupId && editorGroupService.getGroup(s.groupId).getIndexOfEditor(s.editor) === focus.editorIndex)) {
685 686
				return selection.map(elementToContext);
			}
I
isidor 已提交
687

688
			return [focus];
I
isidor 已提交
689 690 691
		}
	}

B
Benjamin Pasero 已提交
692
	// Otherwise go with passed in context
I
isidor 已提交
693 694
	return !!editorContext ? [editorContext] : [];
}
695

B
Benjamin Pasero 已提交
696 697 698 699 700 701 702 703 704 705 706 707
function isEditorGroup(thing: any): thing is IEditorGroup {
	const group = thing as IEditorGroup;

	return group && typeof group.id === 'number' && Array.isArray(group.editors);
}

function isEditorIdentifier(thing: any): thing is IEditorIdentifier {
	const identifier = thing as IEditorIdentifier;

	return identifier && typeof identifier.groupId === 'number';
}

708 709
export function setup(): void {
	registerActiveEditorMoveCommand();
710
	registerEditorGroupsLayoutCommand();
711 712
	registerDiffEditorCommands();
	registerOpenEditorAtIndexCommands();
713
	registerCloseEditorCommands();
B
Benjamin Pasero 已提交
714
	registerFocusEditorGroupAtIndexCommands();
715
	registerSplitEditorCommands();
716
}