editorCommands.ts 25.6 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, preferredSideBySideGroupDirection, EditorGroupLayout } 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

B
Benjamin Pasero 已提交
40 41 42 43
export const SPLIT_EDITOR_UP = 'workbench.action.splitEditorUp';
export const SPLIT_EDITOR_DOWN = 'workbench.action.splitEditorDown';
export const SPLIT_EDITOR_LEFT = 'workbench.action.splitEditorLeft';
export const SPLIT_EDITOR_RIGHT = 'workbench.action.splitEditorRight';
44

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
function registerEditorGroupsLayoutCommand(): void {
197 198 199 200
	CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
		if (!args || typeof args !== 'object') {
			return;
		}
201

202 203 204
		const editorGroupService = accessor.get(IEditorGroupsService);
		editorGroupService.applyLayout(args);
	});
205 206
}

207 208 209 210 211 212 213 214
export function mergeAllGroups(editorGroupService: IEditorGroupsService, target = editorGroupService.groups[0]): void {
	editorGroupService.groups.forEach(group => {
		if (group === target) {
			return; // keep target
		}

		editorGroupService.mergeGroup(group, target);
	});
215 216
}

217 218 219 220
function registerDiffEditorCommands(): void {
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.nextChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
221
		when: TextCompareEditorVisibleContext,
222 223 224 225 226 227 228
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, true)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.previousChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
229
		when: TextCompareEditorVisibleContext,
230 231 232 233 234
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, false)
	});

	function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
235
		const editorService = accessor.get(IEditorService);
236
		const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
237 238 239 240 241 242 243

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

	KeybindingsRegistry.registerCommandAndKeybindingRule({
244 245
		id: TOGGLE_DIFF_INLINE_MODE,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
246
		when: void 0,
247
		primary: void 0,
248
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
249
			const editorGroupService = accessor.get(IEditorGroupsService);
250

251
			const { control } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
252 253 254 255
			if (control instanceof TextDiffEditor) {
				const widget = control.getControl();
				const isInlineMode = !widget.renderSideBySide;
				widget.updateOptions(<IDiffEditorOptions>{
256 257 258 259
					renderSideBySide: isInlineMode
				});
			}
		}
260
	});
261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276
}

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 => {
277
				const editorService = accessor.get(IEditorService);
278

279 280
				const activeControl = editorService.activeControl;
				if (activeControl) {
281
					const editor = activeControl.group.getEditor(editorIndex);
282
					if (editor) {
B
Benjamin Pasero 已提交
283
						return editorService.openEditor(editor).then(() => void 0);
284 285
					}
				}
286 287

				return void 0;
288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304
			}
		});
	}

	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;
		}
305 306

		return void 0;
307
	}
308 309
}

B
Benjamin Pasero 已提交
310 311 312
function registerFocusEditorGroupAtIndexCommands(): void {

	// Keybindings to focus a specific group (2-8) in the editor area
313
	for (let groupIndex = 1; groupIndex < 8; groupIndex++) {
B
Benjamin Pasero 已提交
314 315 316 317 318 319
		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: toCommandId(groupIndex),
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
			when: void 0,
			primary: KeyMod.CtrlCmd | toKeyCode(groupIndex),
			handler: accessor => {
320
				const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336
				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
337
				const direction = preferredSideBySideGroupDirection(configurationService);
B
Benjamin Pasero 已提交
338
				const lastGroup = editorGroupService.findGroup({ location: GroupLocation.LAST });
B
Benjamin Pasero 已提交
339
				const newGroup = editorGroupService.addGroup(lastGroup, direction);
B
Benjamin Pasero 已提交
340

B
Benjamin Pasero 已提交
341 342
				// Focus
				newGroup.focus();
B
Benjamin Pasero 已提交
343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375
			}
		});
	}

	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;
	}
}

376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409
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 }) => {
410 411
		CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) {
			splitEditor(accessor.get(IEditorGroupsService), direction, getCommandsContext(resourceOrContext, context));
412 413 414 415 416
		});
	});
}

function registerCloseEditorCommands() {
417

I
isidor 已提交
418
	KeybindingsRegistry.registerCommandAndKeybindingRule({
419
		id: CLOSE_SAVED_EDITORS_COMMAND_ID,
I
isidor 已提交
420
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
421
		when: void 0,
I
isidor 已提交
422
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
423
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
424
			const editorGroupService = accessor.get(IEditorGroupsService);
425
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
I
isidor 已提交
426
			if (contexts.length === 0 && editorGroupService.activeGroup) {
427
				contexts.push({ groupId: editorGroupService.activeGroup.id }); // If command is triggered from the command palette use the active group
I
isidor 已提交
428
			}
429

I
isidor 已提交
430 431 432
			return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
				editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
			));
433 434 435
		}
	});

I
isidor 已提交
436
	KeybindingsRegistry.registerCommandAndKeybindingRule({
437
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
438
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
439
		when: void 0,
I
isidor 已提交
440
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
441
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
442
			const editorGroupService = accessor.get(IEditorGroupsService);
443
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
444
			const distinctGroupIds = distinct(contexts.map(c => c.groupId));
445

I
isidor 已提交
446 447
			if (distinctGroupIds.length === 0) {
				distinctGroupIds.push(editorGroupService.activeGroup.id);
448 449
			}

I
isidor 已提交
450 451 452
			return TPromise.join(distinctGroupIds.map(groupId =>
				editorGroupService.getGroup(groupId).closeAllEditors()
			));
453 454 455
		}
	});

I
isidor 已提交
456
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
457
		id: CLOSE_EDITOR_COMMAND_ID,
I
isidor 已提交
458
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
459
		when: void 0,
I
isidor 已提交
460 461
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
462
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
463
			const editorGroupService = accessor.get(IEditorGroupsService);
464
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
I
isidor 已提交
465 466 467
			const activeGroup = editorGroupService.activeGroup;
			if (contexts.length === 0 && activeGroup && activeGroup.activeEditor) {
				contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });
468
			}
469

I
isidor 已提交
470 471 472 473 474 475
			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);
			}));
476
		}
477 478 479 480 481 482 483 484
	});

	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] },
485
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
486
			const editorGroupService = accessor.get(IEditorGroupsService);
487
			const commandsContext = getCommandsContext(resourceOrContext, context);
488

489
			let group: IEditorGroup;
490 491
			if (commandsContext && typeof commandsContext.groupId === 'number') {
				group = editorGroupService.getGroup(commandsContext.groupId);
492 493 494
			} else {
				group = editorGroupService.activeGroup;
			}
495

496
			editorGroupService.removeGroup(group);
497
		}
498 499
	});

I
isidor 已提交
500
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
501
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
502
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
503 504
		when: void 0,
		primary: void 0,
I
isidor 已提交
505
		mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
506
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
507
			const editorGroupService = accessor.get(IEditorGroupsService);
508
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
509

510 511
			if (contexts.length === 0) {
				// Cover the case when run from command palette
I
isidor 已提交
512 513 514
				const activeGroup = editorGroupService.activeGroup;
				if (activeGroup && activeGroup.activeEditor) {
					contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });
515 516 517 518
				}
			}

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

I
isidor 已提交
520 521 522 523
			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);
524

I
isidor 已提交
525 526
				return group.closeEditors(editorsToClose);
			}));
527 528 529 530 531 532 533 534
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
535
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
536
			const editorGroupService = accessor.get(IEditorGroupsService);
537

538
			const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
539 540
			if (group && editor) {
				return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
541 542
			}

543 544 545 546 547 548 549 550 551
			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),
552
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
553
			const editorGroupService = accessor.get(IEditorGroupsService);
554

555
			const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
556 557
			if (group && editor) {
				return group.pinEditor(editor);
558 559
			}

I
isidor 已提交
560
			return TPromise.as(false);
561 562
		}
	});
B
Benjamin Pasero 已提交
563 564 565 566 567 568

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: SHOW_EDITORS_IN_GROUP,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
569
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
570
			const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
571 572
			const quickOpenService = accessor.get(IQuickOpenService);

B
Benjamin Pasero 已提交
573
			if (editorGroupService.count <= 1) {
B
Benjamin Pasero 已提交
574 575 576
				return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
			}

577 578 579
			const commandsContext = getCommandsContext(resourceOrContext, context);
			if (commandsContext && typeof commandsContext.groupId === 'number') {
				editorGroupService.activateGroup(editorGroupService.getGroup(commandsContext.groupId)); // we need the group to be active
B
Benjamin Pasero 已提交
580
			}
B
Benjamin Pasero 已提交
581

B
Benjamin Pasero 已提交
582
			return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
B
Benjamin Pasero 已提交
583 584
		}
	});
585
}
586

587 588 589 590 591 592 593 594 595
function getCommandsContext(resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext): IEditorCommandsContext {
	if (URI.isUri(resourceOrContext)) {
		return context;
	}

	if (resourceOrContext && typeof resourceOrContext.groupId === 'number') {
		return resourceOrContext;
	}

I
polish  
isidor 已提交
596 597 598 599 600
	if (context && typeof context.groupId === 'number') {
		return context;
	}

	return void 0;
601 602
}

603
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619

	// 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 };
}

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

B
Benjamin Pasero 已提交
622 623
	// First check for a focused list to return the selected items from
	const list = listService.lastFocusedList;
I
isidor 已提交
624
	if (list instanceof List && list.isDOMFocused()) {
B
Benjamin Pasero 已提交
625 626 627 628 629 630 631
		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 已提交
632

B
Benjamin Pasero 已提交
633 634 635 636
		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 }
637 638

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

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

646
			return [focus];
I
isidor 已提交
647 648 649
		}
	}

B
Benjamin Pasero 已提交
650
	// Otherwise go with passed in context
I
isidor 已提交
651 652
	return !!editorContext ? [editorContext] : [];
}
653

B
Benjamin Pasero 已提交
654 655 656 657 658 659 660 661 662 663 664 665
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';
}

666 667
export function setup(): void {
	registerActiveEditorMoveCommand();
668
	registerEditorGroupsLayoutCommand();
669 670
	registerDiffEditorCommands();
	registerOpenEditorAtIndexCommands();
671
	registerCloseEditorCommands();
B
Benjamin Pasero 已提交
672
	registerFocusEditorGroupAtIndexCommands();
673
	registerSplitEditorCommands();
674
}