editorCommands.ts 27.4 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
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
9
import { KeybindingsRegistry, KeybindingWeight } 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';
H
Hao Hu 已提交
25
import { CommandsRegistry, ICommandHandler } from 'vs/platform/commands/common/commands';
26
import { MenuRegistry, MenuId } from 'vs/platform/actions/common/actions';
27

28
export const CLOSE_SAVED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
I
isidor 已提交
29
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
30
export const CLOSE_EDITORS_AND_GROUP_COMMAND_ID = 'workbench.action.closeEditorsAndGroup';
31
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
I
isidor 已提交
32
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
B
Benjamin Pasero 已提交
33
export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeGroup';
I
isidor 已提交
34
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
35 36

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

B
Benjamin Pasero 已提交
42 43 44 45
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';
46

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

H
Hao Hu 已提交
50 51
export const OPEN_EDITOR_AT_INDEX_COMMAND_ID = 'workbench.action.openEditorAtIndex';

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

B
Benjamin Pasero 已提交
58
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
S
Sandeep Somavarapu 已提交
59 60 61 62
	if (!types.isObject(arg)) {
		return false;
	}

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

67
	if (!types.isUndefined(arg.by) && !types.isString(arg.by)) {
S
Sandeep Somavarapu 已提交
68 69 70
		return false;
	}

71
	if (!types.isUndefined(arg.value) && !types.isNumber(arg.value)) {
S
Sandeep Somavarapu 已提交
72 73 74 75 76 77
		return false;
	}

	return true;
};

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

98 99 100 101
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 已提交
102

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

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

138
	index = index < 0 ? 0 : index >= group.count ? group.count - 1 : index;
139
	group.moveEditor(control.input, group, { index });
S
Sandeep Somavarapu 已提交
140 141
}

142
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, control: IEditor, accessor: ServicesAccessor): void {
143
	const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
144
	const configurationService = accessor.get(IConfigurationService);
145 146

	const sourceGroup = control.group;
147
	let targetGroup: IEditorGroup;
148

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

197 198
	if (targetGroup) {
		sourceGroup.moveEditor(control.input, targetGroup);
B
Benjamin Pasero 已提交
199
		targetGroup.focus();
200
	}
201 202
}

203
function registerEditorGroupsLayoutCommand(): void {
204 205 206 207
	CommandsRegistry.registerCommand(LAYOUT_EDITOR_GROUPS_COMMAND_ID, (accessor: ServicesAccessor, args: EditorGroupLayout) => {
		if (!args || typeof args !== 'object') {
			return;
		}
208

209 210 211
		const editorGroupService = accessor.get(IEditorGroupsService);
		editorGroupService.applyLayout(args);
	});
212 213
}

214 215
export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
	const target = editorGroupService.activeGroup;
216
	editorGroupService.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE).forEach(group => {
217 218 219 220 221 222
		if (group === target) {
			return; // keep target
		}

		editorGroupService.mergeGroup(group, target);
	});
223 224
}

225 226 227
function registerDiffEditorCommands(): void {
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.nextChange',
228
		weight: KeybindingWeight.WorkbenchContrib,
229
		when: TextCompareEditorVisibleContext,
230
		primary: KeyMod.Alt | KeyCode.F5,
231 232 233 234 235
		handler: accessor => navigateInDiffEditor(accessor, true)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.previousChange',
236
		weight: KeybindingWeight.WorkbenchContrib,
237
		when: TextCompareEditorVisibleContext,
238
		primary: KeyMod.Alt | KeyMod.Shift | KeyCode.F5,
239 240 241 242
		handler: accessor => navigateInDiffEditor(accessor, false)
	});

	function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
243
		const editorService = accessor.get(IEditorService);
244
		const candidates = [editorService.activeControl, ...editorService.visibleControls].filter(e => e instanceof TextDiffEditor);
245 246 247 248 249 250 251

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

	KeybindingsRegistry.registerCommandAndKeybindingRule({
252
		id: TOGGLE_DIFF_INLINE_MODE,
253
		weight: KeybindingWeight.WorkbenchContrib,
254
		when: void 0,
255
		primary: void 0,
256
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
257
			const editorGroupService = accessor.get(IEditorGroupsService);
258

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

	MenuRegistry.appendMenuItem(MenuId.CommandPalette, {
		command: {
			id: TOGGLE_DIFF_INLINE_MODE,
			title: nls.localize('toggleInlineView', "Compare: Toggle Inline View")
		},
		when: ContextKeyExpr.has('textCompareEditorActive')
	});
277 278 279
}

function registerOpenEditorAtIndexCommands(): void {
H
Hao Hu 已提交
280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295
	const openEditorAtIndex: ICommandHandler = (accessor: ServicesAccessor, editorIndex: number): void => {
		const editorService = accessor.get(IEditorService);
		const activeControl = editorService.activeControl;
		if (activeControl) {
			const editor = activeControl.group.getEditor(editorIndex);
			if (editor) {
				editorService.openEditor(editor);
			}
		}
	};

	// This command takes in the editor index number to open as an argument
	CommandsRegistry.registerCommand({
		id: OPEN_EDITOR_AT_INDEX_COMMAND_ID,
		handler: openEditorAtIndex
	});
296 297 298 299 300 301 302

	// 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({
H
Hao Hu 已提交
303
			id: OPEN_EDITOR_AT_INDEX_COMMAND_ID + visibleIndex,
304
			weight: KeybindingWeight.WorkbenchContrib,
305 306 307
			when: void 0,
			primary: KeyMod.Alt | toKeyCode(visibleIndex),
			mac: { primary: KeyMod.WinCtrl | toKeyCode(visibleIndex) },
H
Hao Hu 已提交
308
			handler: accessor => openEditorAtIndex(accessor, editorIndex)
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324
		});
	}

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

		return void 0;
327
	}
328 329
}

B
Benjamin Pasero 已提交
330 331 332
function registerFocusEditorGroupAtIndexCommands(): void {

	// Keybindings to focus a specific group (2-8) in the editor area
333
	for (let groupIndex = 1; groupIndex < 8; groupIndex++) {
B
Benjamin Pasero 已提交
334 335
		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: toCommandId(groupIndex),
336
			weight: KeybindingWeight.WorkbenchContrib,
B
Benjamin Pasero 已提交
337 338 339
			when: void 0,
			primary: KeyMod.CtrlCmd | toKeyCode(groupIndex),
			handler: accessor => {
340
				const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
341 342 343 344 345 346 347 348 349 350
				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
B
Benjamin Pasero 已提交
351
				const groups = editorGroupService.getGroups(GroupsOrder.GRID_APPEARANCE);
B
Benjamin Pasero 已提交
352 353 354 355 356
				if (groups[groupIndex]) {
					return groups[groupIndex].focus();
				}

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

B
Benjamin Pasero 已提交
361 362
				// Focus
				newGroup.focus();
B
Benjamin Pasero 已提交
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
			}
		});
	}

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

396 397 398 399 400 401 402 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
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 }) => {
430 431
		CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) {
			splitEditor(accessor.get(IEditorGroupsService), direction, getCommandsContext(resourceOrContext, context));
432 433 434 435 436
		});
	});
}

function registerCloseEditorCommands() {
437

I
isidor 已提交
438
	KeybindingsRegistry.registerCommandAndKeybindingRule({
439
		id: CLOSE_SAVED_EDITORS_COMMAND_ID,
440
		weight: KeybindingWeight.WorkbenchContrib,
441
		when: void 0,
I
isidor 已提交
442
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
443
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
444
			const editorGroupService = accessor.get(IEditorGroupsService);
445
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
B
Benjamin Pasero 已提交
446 447 448 449

			const activeGroup = editorGroupService.activeGroup;
			if (contexts.length === 0) {
				contexts.push({ groupId: activeGroup.id }); // active group as fallback
I
isidor 已提交
450
			}
451

I
isidor 已提交
452 453 454
			return TPromise.join(distinct(contexts.map(c => c.groupId)).map(groupId =>
				editorGroupService.getGroup(groupId).closeEditors({ savedOnly: true })
			));
455 456 457
		}
	});

I
isidor 已提交
458
	KeybindingsRegistry.registerCommandAndKeybindingRule({
459
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
460
		weight: KeybindingWeight.WorkbenchContrib,
461
		when: void 0,
I
isidor 已提交
462
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
463
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
464
			const editorGroupService = accessor.get(IEditorGroupsService);
465
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
466
			const distinctGroupIds = distinct(contexts.map(c => c.groupId));
467

I
isidor 已提交
468 469
			if (distinctGroupIds.length === 0) {
				distinctGroupIds.push(editorGroupService.activeGroup.id);
470 471
			}

I
isidor 已提交
472 473 474
			return TPromise.join(distinctGroupIds.map(groupId =>
				editorGroupService.getGroup(groupId).closeAllEditors()
			));
475 476 477
		}
	});

I
isidor 已提交
478
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
479
		id: CLOSE_EDITOR_COMMAND_ID,
480
		weight: KeybindingWeight.WorkbenchContrib,
481
		when: void 0,
I
isidor 已提交
482 483
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
484
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
485
			const editorGroupService = accessor.get(IEditorGroupsService);
486
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
B
Benjamin Pasero 已提交
487

I
isidor 已提交
488
			const activeGroup = editorGroupService.activeGroup;
B
Benjamin Pasero 已提交
489 490
			if (contexts.length === 0 && activeGroup.activeEditor) {
				contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });  // active editor as fallback
491
			}
492

I
isidor 已提交
493
			const groupIds = distinct(contexts.map(context => context.groupId));
B
Benjamin Pasero 已提交
494

I
isidor 已提交
495 496
			return TPromise.join(groupIds.map(groupId => {
				const group = editorGroupService.getGroup(groupId);
B
Benjamin Pasero 已提交
497 498 499 500
				const editors = contexts
					.filter(context => context.groupId === groupId)
					.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);

I
isidor 已提交
501 502
				return group.closeEditors(editors);
			}));
503
		}
504 505 506 507
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITOR_GROUP_COMMAND_ID,
508
		weight: KeybindingWeight.WorkbenchContrib,
509 510 511
		when: ContextKeyExpr.and(ActiveEditorGroupEmptyContext, MultipleEditorGroupsContext),
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
512
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
513
			const editorGroupService = accessor.get(IEditorGroupsService);
514
			const commandsContext = getCommandsContext(resourceOrContext, context);
515

516
			let group: IEditorGroup;
517 518
			if (commandsContext && typeof commandsContext.groupId === 'number') {
				group = editorGroupService.getGroup(commandsContext.groupId);
519 520 521
			} else {
				group = editorGroupService.activeGroup;
			}
522

523
			editorGroupService.removeGroup(group);
524
		}
525 526
	});

I
isidor 已提交
527
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
528
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
529
		weight: KeybindingWeight.WorkbenchContrib,
530 531
		when: void 0,
		primary: void 0,
I
isidor 已提交
532
		mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
533
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
534
			const editorGroupService = accessor.get(IEditorGroupsService);
535
			const contexts = getMultiSelectedEditorContexts(getCommandsContext(resourceOrContext, context), accessor.get(IListService), editorGroupService);
536

B
Benjamin Pasero 已提交
537 538 539
			const activeGroup = editorGroupService.activeGroup;
			if (contexts.length === 0 && activeGroup.activeEditor) {
				contexts.push({ groupId: activeGroup.id, editorIndex: activeGroup.getIndexOfEditor(activeGroup.activeEditor) });  // active editor as fallback
540 541 542
			}

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

I
isidor 已提交
544 545
			return TPromise.join(groupIds.map(groupId => {
				const group = editorGroupService.getGroup(groupId);
B
Benjamin Pasero 已提交
546 547 548
				const editors = contexts
					.filter(context => context.groupId === groupId)
					.map(context => typeof context.editorIndex === 'number' ? group.getEditor(context.editorIndex) : group.activeEditor);
I
isidor 已提交
549
				const editorsToClose = group.editors.filter(e => editors.indexOf(e) === -1);
550

I
isidor 已提交
551 552
				return group.closeEditors(editorsToClose);
			}));
553 554 555 556 557
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
558
		weight: KeybindingWeight.WorkbenchContrib,
559 560
		when: void 0,
		primary: void 0,
561
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
562
			const editorGroupService = accessor.get(IEditorGroupsService);
563

564
			const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
565 566
			if (group && editor) {
				return group.closeEditors({ direction: CloseDirection.RIGHT, except: editor });
567 568
			}

569 570 571 572 573 574
			return TPromise.as(false);
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: KEEP_EDITOR_COMMAND_ID,
575
		weight: KeybindingWeight.WorkbenchContrib,
576 577
		when: void 0,
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter),
578
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
579
			const editorGroupService = accessor.get(IEditorGroupsService);
580

581
			const { group, editor } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
582 583
			if (group && editor) {
				return group.pinEditor(editor);
584 585
			}

I
isidor 已提交
586
			return TPromise.as(false);
587 588
		}
	});
B
Benjamin Pasero 已提交
589 590 591

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: SHOW_EDITORS_IN_GROUP,
592
		weight: KeybindingWeight.WorkbenchContrib,
B
Benjamin Pasero 已提交
593 594
		when: void 0,
		primary: void 0,
595
		handler: (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
596
			const editorGroupService = accessor.get(IEditorGroupsService);
B
Benjamin Pasero 已提交
597 598
			const quickOpenService = accessor.get(IQuickOpenService);

B
Benjamin Pasero 已提交
599
			if (editorGroupService.count <= 1) {
B
Benjamin Pasero 已提交
600 601 602
				return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
			}

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

B
Benjamin Pasero 已提交
608
			return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
B
Benjamin Pasero 已提交
609 610
		}
	});
611 612 613 614 615 616 617 618 619 620 621 622 623 624 625

	CommandsRegistry.registerCommand(CLOSE_EDITORS_AND_GROUP_COMMAND_ID, (accessor: ServicesAccessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) => {
		const editorGroupService = accessor.get(IEditorGroupsService);

		const { group } = resolveCommandsContext(editorGroupService, getCommandsContext(resourceOrContext, context));
		if (group) {
			return group.closeAllEditors().then(() => {
				if (group.count === 0 && editorGroupService.getGroup(group.id) /* could be gone by now */) {
					editorGroupService.removeGroup(group); // only remove group if it is now empty
				}
			});
		}

		return void 0;
	});
626
}
627

628 629 630 631 632 633 634 635 636
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 已提交
637 638 639 640 641
	if (context && typeof context.groupId === 'number') {
		return context;
	}

	return void 0;
642 643
}

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

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

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

B
Benjamin Pasero 已提交
663 664
	// First check for a focused list to return the selected items from
	const list = listService.lastFocusedList;
I
isidor 已提交
665
	if (list instanceof List && list.isDOMFocused()) {
B
Benjamin Pasero 已提交
666 667 668 669 670 671 672
		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 已提交
673

B
Benjamin Pasero 已提交
674 675 676 677
		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 }
678 679

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

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

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

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

B
Benjamin Pasero 已提交
695 696 697 698 699 700 701 702 703 704 705 706
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';
}

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