editorCommands.ts 24.0 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 } 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 KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
B
Benjamin Pasero 已提交
36
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
37
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
B
Benjamin Pasero 已提交
38

39 40 41 42 43
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 已提交
44
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
B
Benjamin Pasero 已提交
45
export const NAVIGATE_IN_ACTIVE_GROUP_PREFIX = 'edt active ';
46

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

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

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

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

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

	return true;
};

73
function registerActiveEditorMoveCommand(): void {
A
Alex Dima 已提交
74
	KeybindingsRegistry.registerCommandAndKeybindingRule({
75
		id: MOVE_ACTIVE_EDITOR_COMMAND_ID,
S
Sandeep Somavarapu 已提交
76
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
77
		when: EditorContextKeys.editorTextFocus,
S
Sandeep Somavarapu 已提交
78
		primary: null,
79
		handler: (accessor, args: any) => moveActiveEditor(args, accessor),
S
Sandeep Somavarapu 已提交
80
		description: {
81
			description: nls.localize('editorCommand.activeEditorMove.description', "Move the active editor by tabs or groups"),
S
Sandeep Somavarapu 已提交
82 83 84
			args: [
				{
					name: nls.localize('editorCommand.activeEditorMove.arg.name', "Active editor move argument"),
85
					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 已提交
86 87 88 89 90 91 92
					constraint: isActiveEditorMoveArg
				}
			]
		}
	});
}

93 94 95 96
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 已提交
97

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

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

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

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

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

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

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

function registerDiffEditorCommands(): void {
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.nextChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
199
		when: TextCompareEditorVisibleContext,
200 201 202 203 204 205 206
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, true)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.previousChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
207
		when: TextCompareEditorVisibleContext,
208 209 210 211 212
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, false)
	});

	function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
213
		const editorService = accessor.get(IEditorService);
214
		const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
215 216 217 218 219 220 221

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

	KeybindingsRegistry.registerCommandAndKeybindingRule({
222 223
		id: TOGGLE_DIFF_INLINE_MODE,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
224
		when: void 0,
225
		primary: void 0,
B
Benjamin Pasero 已提交
226
		handler: (accessor, resource, context: IEditorCommandsContext) => {
227
			const editorGroupService = accessor.get(IEditorGroupsService);
228

I
isidor 已提交
229
			const { control } = resolveCommandsContext(editorGroupService, context);
230 231 232 233
			if (control instanceof TextDiffEditor) {
				const widget = control.getControl();
				const isInlineMode = !widget.renderSideBySide;
				widget.updateOptions(<IDiffEditorOptions>{
234 235 236 237
					renderSideBySide: isInlineMode
				});
			}
		}
238
	});
239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254
}

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

257 258
				const activeControl = editorService.activeControl;
				if (activeControl) {
259
					const editor = activeControl.group.getEditor(editorIndex);
260
					if (editor) {
B
Benjamin Pasero 已提交
261
						return editorService.openEditor(editor).then(() => void 0);
262 263
					}
				}
264 265

				return void 0;
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282
			}
		});
	}

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

		return void 0;
285
	}
286 287
}

B
Benjamin Pasero 已提交
288 289 290 291 292 293 294 295 296 297 298 299
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 => {
300
				const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318
				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 已提交
319
				const newGroup = editorGroupService.addGroup(lastGroup, direction);
B
Benjamin Pasero 已提交
320 321 322

				// 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 已提交
323
				if (lastGroup.activeEditor && (lastGroup.activeEditor as EditorInput).supportsSplitEditor()) {
B
Benjamin Pasero 已提交
324 325
					lastGroup.copyEditor(lastGroup.activeEditor, newGroup);
				}
B
Benjamin Pasero 已提交
326 327 328

				// Focus
				newGroup.focus();
B
Benjamin Pasero 已提交
329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
			}
		});
	}

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

362 363 364 365 366 367 368 369 370 371 372 373 374 375 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
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() {
403

I
isidor 已提交
404
	KeybindingsRegistry.registerCommandAndKeybindingRule({
405
		id: CLOSE_SAVED_EDITORS_COMMAND_ID,
I
isidor 已提交
406
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
407
		when: void 0,
I
isidor 已提交
408
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
I
isidor 已提交
409
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
410
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
411 412
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
			if (contexts.length === 0 && editorGroupService.activeGroup) {
413
				contexts.push({ groupId: editorGroupService.activeGroup.id }); // If command is triggered from the command palette use the active group
I
isidor 已提交
414
			}
415

I
isidor 已提交
416 417 418
			return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
				editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
			));
419 420 421
		}
	});

I
isidor 已提交
422
	KeybindingsRegistry.registerCommandAndKeybindingRule({
423
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
424
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
425
		when: void 0,
I
isidor 已提交
426
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
I
isidor 已提交
427
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
428
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
429
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
430
			const distinctGroupIds = distinct(contexts.map(c => c.groupId));
431

I
isidor 已提交
432 433
			if (distinctGroupIds.length === 0) {
				distinctGroupIds.push(editorGroupService.activeGroup.id);
434 435
			}

I
isidor 已提交
436 437 438
			return TPromise.join(distinctGroupIds.map(groupId =>
				editorGroupService.getGroup(groupId).closeAllEditors()
			));
439 440 441
		}
	});

I
isidor 已提交
442
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
443
		id: CLOSE_EDITOR_COMMAND_ID,
I
isidor 已提交
444
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
445
		when: void 0,
I
isidor 已提交
446 447
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
I
isidor 已提交
448
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
449
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
450 451 452 453
			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) });
454
			}
455

I
isidor 已提交
456 457 458 459 460 461
			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);
			}));
462
		}
463 464 465 466 467 468 469 470 471
	});

	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) => {
472
			const editorGroupService = accessor.get(IEditorGroupsService);
473

474
			let group: IEditorGroup;
475 476 477 478 479
			if (context && typeof context.groupId === 'number') {
				group = editorGroupService.getGroup(context.groupId);
			} else {
				group = editorGroupService.activeGroup;
			}
480

481
			editorGroupService.removeGroup(group);
482
		}
483 484
	});

I
isidor 已提交
485
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
486
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
487
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
488 489
		when: void 0,
		primary: void 0,
I
isidor 已提交
490
		mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
I
isidor 已提交
491
		handler: (accessor, resource: URI | object, context: IEditorCommandsContext) => {
492
			const editorGroupService = accessor.get(IEditorGroupsService);
I
isidor 已提交
493
			const contexts = getMultiSelectedEditorContexts(context, accessor.get(IListService), editorGroupService);
494

495 496
			if (contexts.length === 0) {
				// Cover the case when run from command palette
I
isidor 已提交
497 498 499
				const activeGroup = editorGroupService.activeGroup;
				if (activeGroup && activeGroup.activeEditor) {
					contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });
500 501 502 503
				}
			}

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

I
isidor 已提交
505 506 507 508
			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);
509

I
isidor 已提交
510 511
				return group.closeEditors(editorsToClose);
			}));
512 513 514 515 516 517 518 519
		}
	});

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

I
isidor 已提交
523
			const { group, editor } = resolveCommandsContext(editorGroupService, context);
524 525
			if (group && editor) {
				return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
526 527
			}

528 529 530 531 532 533 534 535 536
			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),
537
		handler: (accessor, resource: URI, context: IEditorCommandsContext) => {
538
			const editorGroupService = accessor.get(IEditorGroupsService);
539

I
isidor 已提交
540
			const { group, editor } = resolveCommandsContext(editorGroupService, context);
541 542
			if (group && editor) {
				return group.pinEditor(editor);
543 544
			}

I
isidor 已提交
545
			return TPromise.as(false);
546 547
		}
	});
B
Benjamin Pasero 已提交
548 549 550 551 552 553

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

B
Benjamin Pasero 已提交
558
			if (editorGroupService.count <= 1) {
B
Benjamin Pasero 已提交
559 560 561
				return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
			}

B
Benjamin Pasero 已提交
562 563 564
			if (context && typeof context.groupId === 'number') {
				editorGroupService.activateGroup(editorGroupService.getGroup(context.groupId)); // we need the group to be active
			}
B
Benjamin Pasero 已提交
565

B
Benjamin Pasero 已提交
566
			return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
B
Benjamin Pasero 已提交
567 568
		}
	});
569
}
570

571
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587

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

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

B
Benjamin Pasero 已提交
590 591
	// First check for a focused list to return the selected items from
	const list = listService.lastFocusedList;
I
isidor 已提交
592
	if (list instanceof List && list.isDOMFocused()) {
B
Benjamin Pasero 已提交
593 594 595 596 597 598 599
		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 已提交
600

B
Benjamin Pasero 已提交
601 602 603 604
		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 }
605 606

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

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

614
			return [focus];
I
isidor 已提交
615 616 617
		}
	}

B
Benjamin Pasero 已提交
618
	// Otherwise go with passed in context
I
isidor 已提交
619 620
	return !!editorContext ? [editorContext] : [];
}
621

B
Benjamin Pasero 已提交
622 623 624 625 626 627 628 629 630 631 632 633
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';
}

634 635 636 637
export function setup(): void {
	registerActiveEditorMoveCommand();
	registerDiffEditorCommands();
	registerOpenEditorAtIndexCommands();
638
	registerCloseEditorCommands();
B
Benjamin Pasero 已提交
639
	registerFocusEditorGroupAtIndexCommands();
640
	registerSplitEditorCommands();
641
}