editorCommands.ts 26.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 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_AND_GROUP_COMMAND_ID = 'workbench.action.closeEditorsAndGroup';
30
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
I
isidor 已提交
31
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
B
Benjamin Pasero 已提交
32
export const CLOSE_EDITOR_GROUP_COMMAND_ID = 'workbench.action.closeGroup';
I
isidor 已提交
33
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
34 35

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

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

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

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

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

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

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

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

	return true;
};

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

95 96 97 98
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 已提交
99

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

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

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

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

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

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

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

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

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

208 209
export function mergeAllGroups(editorGroupService: IEditorGroupsService): void {
	const target = editorGroupService.activeGroup;
210 211 212 213 214 215 216
	editorGroupService.groups.forEach(group => {
		if (group === target) {
			return; // keep target
		}

		editorGroupService.mergeGroup(group, target);
	});
217 218
}

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

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

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

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

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

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

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

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

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

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

		return void 0;
309
	}
310 311
}

B
Benjamin Pasero 已提交
312 313 314
function registerFocusEditorGroupAtIndexCommands(): void {

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

B
Benjamin Pasero 已提交
343 344
				// Focus
				newGroup.focus();
B
Benjamin Pasero 已提交
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 376 377
			}
		});
	}

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

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 410 411
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 }) => {
412 413
		CommandsRegistry.registerCommand(id, function (accessor, resourceOrContext: URI | IEditorCommandsContext, context?: IEditorCommandsContext) {
			splitEditor(accessor.get(IEditorGroupsService), direction, getCommandsContext(resourceOrContext, context));
414 415 416 417 418
		});
	});
}

function registerCloseEditorCommands() {
419

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

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

I
isidor 已提交
438
	KeybindingsRegistry.registerCommandAndKeybindingRule({
439
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
440
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
441
		when: void 0,
I
isidor 已提交
442
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
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);
446
			const distinctGroupIds = distinct(contexts.map(c => c.groupId));
447

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

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

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

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

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

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

498
			editorGroupService.removeGroup(group);
499
		}
500 501
	});

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

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

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

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

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

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

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

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

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

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

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

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

579 580 581
			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 已提交
582
			}
B
Benjamin Pasero 已提交
583

B
Benjamin Pasero 已提交
584
			return quickOpenService.show(NAVIGATE_IN_ACTIVE_GROUP_PREFIX);
B
Benjamin Pasero 已提交
585 586
		}
	});
587 588 589 590 591 592 593 594 595 596 597 598 599 600 601

	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;
	});
602
}
603

604 605 606 607 608 609 610 611 612
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 已提交
613 614 615 616 617
	if (context && typeof context.groupId === 'number') {
		return context;
	}

	return void 0;
618 619
}

620
function resolveCommandsContext(editorGroupService: IEditorGroupsService, context?: IEditorCommandsContext): { group: IEditorGroup, editor: IEditorInput, control: IEditor } {
621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636

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

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

B
Benjamin Pasero 已提交
639 640
	// First check for a focused list to return the selected items from
	const list = listService.lastFocusedList;
I
isidor 已提交
641
	if (list instanceof List && list.isDOMFocused()) {
B
Benjamin Pasero 已提交
642 643 644 645 646 647 648
		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 已提交
649

B
Benjamin Pasero 已提交
650 651 652 653
		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 }
654 655

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

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

663
			return [focus];
I
isidor 已提交
664 665 666
		}
	}

B
Benjamin Pasero 已提交
667
	// Otherwise go with passed in context
I
isidor 已提交
668 669
	return !!editorContext ? [editorContext] : [];
}
670

B
Benjamin Pasero 已提交
671 672 673 674 675 676 677 678 679 680 681 682
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';
}

683 684
export function setup(): void {
	registerActiveEditorMoveCommand();
685
	registerEditorGroupsLayoutCommand();
686 687
	registerDiffEditorCommands();
	registerOpenEditorAtIndexCommands();
688
	registerCloseEditorCommands();
B
Benjamin Pasero 已提交
689
	registerFocusEditorGroupAtIndexCommands();
690
	registerSplitEditorCommands();
691
}