editorCommands.ts 17.9 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 10
import { ServicesAccessor } from 'vs/platform/instantiation/common/instantiation';
import { KeybindingsRegistry } from 'vs/platform/keybinding/common/keybindingsRegistry';
import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService';
I
isidor 已提交
11
import { ActiveEditorMoveArguments, ActiveEditorMovePositioning, ActiveEditorMovePositioningBy, EditorCommands, TextCompareEditorVisible, IEditorContext, EditorInput } from 'vs/workbench/common/editor';
J
Johannes Rieken 已提交
12
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
13
import { IEditor, Position, POSITIONS, Direction, IEditorInput } from 'vs/platform/editor/common/editor';
14
import { EditorContextKeys } from 'vs/editor/common/editorContextKeys';
15
import { TextDiffEditor } from 'vs/workbench/browser/parts/editor/textDiffEditor';
J
Johannes Rieken 已提交
16
import { EditorStacksModel } from 'vs/workbench/common/editor/editorStacksModel';
I
isidor 已提交
17
import { KeyMod, KeyCode, KeyChord } from 'vs/base/common/keyCodes';
18
import { TPromise } from 'vs/base/common/winjs.base';
19
import URI from 'vs/base/common/uri';
B
Benjamin Pasero 已提交
20
import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen';
21
import { IDiffEditorOptions } from 'vs/editor/common/config/editorOptions';
I
isidor 已提交
22 23
import { IListService } from 'vs/platform/list/browser/listService';
import { List } from 'vs/base/browser/ui/list/listWidget';
I
isidor 已提交
24
import { distinct } from 'vs/base/common/arrays';
25

I
isidor 已提交
26 27
export const CLOSE_UNMODIFIED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
28
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
I
isidor 已提交
29 30
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
31
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
B
Benjamin Pasero 已提交
32
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';
33
export const TOGGLE_DIFF_INLINE_MODE = 'toggle.diff.editorMode';
B
Benjamin Pasero 已提交
34 35 36 37 38

export const NAVIGATE_IN_GROUP_ONE_PREFIX = 'edt one ';
export const NAVIGATE_IN_GROUP_TWO_PREFIX = 'edt two ';
export const NAVIGATE_IN_GROUP_THREE_PREFIX = 'edt three ';
export const NAVIGATE_ALL_EDITORS_GROUP_PREFIX = 'edt ';
39

40
export function setup(): void {
41 42
	registerActiveEditorMoveCommand();
	registerDiffEditorCommands();
43
	registerOpenEditorAtIndexCommands();
44
	registerEditorCommands();
S
Sandeep Somavarapu 已提交
45 46
}

B
Benjamin Pasero 已提交
47
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
S
Sandeep Somavarapu 已提交
48 49 50 51
	if (!types.isObject(arg)) {
		return false;
	}

52
	const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
S
Sandeep Somavarapu 已提交
53 54 55 56 57 58 59 60 61

	if (!types.isString(activeEditorMoveArg.to)) {
		return false;
	}

	if (!types.isUndefined(activeEditorMoveArg.by) && !types.isString(activeEditorMoveArg.by)) {
		return false;
	}

62
	if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
S
Sandeep Somavarapu 已提交
63 64 65 66 67 68
		return false;
	}

	return true;
};

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

89
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
I
isidor 已提交
90
	const showTabs = accessor.get(IEditorGroupService).getTabOptions().showTabs;
S
Sandeep Somavarapu 已提交
91
	args.to = args.to || ActiveEditorMovePositioning.RIGHT;
I
isidor 已提交
92
	args.by = showTabs ? args.by || ActiveEditorMovePositioningBy.TAB : ActiveEditorMovePositioningBy.GROUP;
93
	args.value = types.isUndefined(args.value) ? 1 : args.value;
S
Sandeep Somavarapu 已提交
94

95
	const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
96 97 98 99 100 101 102
	if (activeEditor) {
		switch (args.by) {
			case ActiveEditorMovePositioningBy.TAB:
				return moveActiveTab(args, activeEditor, accessor);
			case ActiveEditorMovePositioningBy.GROUP:
				return moveActiveEditorToGroup(args, activeEditor, accessor);
		}
S
Sandeep Somavarapu 已提交
103 104 105
	}
}

106
function moveActiveTab(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
107
	const editorGroupsService: IEditorGroupService = accessor.get(IEditorGroupService);
S
Sandeep Somavarapu 已提交
108
	const editorGroup = editorGroupsService.getStacksModel().groupAt(activeEditor.position);
109
	let index = editorGroup.indexOf(activeEditor.input);
S
Sandeep Somavarapu 已提交
110 111 112 113 114 115 116 117
	switch (args.to) {
		case ActiveEditorMovePositioning.FIRST:
			index = 0;
			break;
		case ActiveEditorMovePositioning.LAST:
			index = editorGroup.count - 1;
			break;
		case ActiveEditorMovePositioning.LEFT:
118
			index = index - args.value;
S
Sandeep Somavarapu 已提交
119 120
			break;
		case ActiveEditorMovePositioning.RIGHT:
121
			index = index + args.value;
S
Sandeep Somavarapu 已提交
122 123
			break;
		case ActiveEditorMovePositioning.CENTER:
124
			index = Math.round(editorGroup.count / 2) - 1;
S
Sandeep Somavarapu 已提交
125 126
			break;
		case ActiveEditorMovePositioning.POSITION:
127
			index = args.value - 1;
S
Sandeep Somavarapu 已提交
128 129
			break;
	}
130

S
Sandeep Somavarapu 已提交
131
	index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
132
	editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
S
Sandeep Somavarapu 已提交
133 134
}

135
function moveActiveEditorToGroup(args: ActiveEditorMoveArguments, activeEditor: IEditor, accessor: ServicesAccessor): void {
136
	let newPosition = activeEditor.position;
S
Sandeep Somavarapu 已提交
137 138
	switch (args.to) {
		case ActiveEditorMovePositioning.LEFT:
S
Sandeep Somavarapu 已提交
139 140 141 142 143 144
			newPosition = newPosition - 1;
			break;
		case ActiveEditorMovePositioning.RIGHT:
			newPosition = newPosition + 1;
			break;
		case ActiveEditorMovePositioning.FIRST:
145
			newPosition = Position.ONE;
S
Sandeep Somavarapu 已提交
146 147
			break;
		case ActiveEditorMovePositioning.LAST:
148
			newPosition = Position.THREE;
S
Sandeep Somavarapu 已提交
149 150
			break;
		case ActiveEditorMovePositioning.CENTER:
151
			newPosition = Position.TWO;
S
Sandeep Somavarapu 已提交
152 153
			break;
		case ActiveEditorMovePositioning.POSITION:
154
			newPosition = args.value - 1;
S
Sandeep Somavarapu 已提交
155 156
			break;
	}
157

S
Sandeep Somavarapu 已提交
158 159
	newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
	accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188
}

function registerDiffEditorCommands(): void {
	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.nextChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: TextCompareEditorVisible,
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, true)
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: 'workbench.action.compareEditor.previousChange',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: TextCompareEditorVisible,
		primary: null,
		handler: accessor => navigateInDiffEditor(accessor, false)
	});

	function navigateInDiffEditor(accessor: ServicesAccessor, next: boolean): void {
		let editorService = accessor.get(IWorkbenchEditorService);
		const candidates = [editorService.getActiveEditor(), ...editorService.getVisibleEditors()].filter(e => e instanceof TextDiffEditor);

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

	KeybindingsRegistry.registerCommandAndKeybindingRule({
189 190
		id: TOGGLE_DIFF_INLINE_MODE,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
191
		when: void 0,
192 193 194
		primary: void 0,
		handler: (accessor) => {
			const editorService = accessor.get(IWorkbenchEditorService);
195

196 197 198 199 200 201 202 203 204
			const editor = editorService.getActiveEditor();
			if (editor instanceof TextDiffEditor) {
				const control = editor.getControl();
				const isInlineMode = !control.renderSideBySide;
				control.updateOptions(<IDiffEditorOptions>{
					renderSideBySide: isInlineMode
				});
			}
		}
205
	});
206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233
}

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 => {
				const editorService = accessor.get(IWorkbenchEditorService);
				const editorGroupService = accessor.get(IEditorGroupService);

				const active = editorService.getActiveEditor();
				if (active) {
					const group = editorGroupService.getStacksModel().groupAt(active.position);
					const editor = group.getEditor(editorIndex);

					if (editor) {
						return editorService.openEditor(editor);
					}
				}
234 235

				return void 0;
236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252
			}
		});
	}

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

		return void 0;
255
	}
256 257
}

258
function registerEditorCommands() {
259

I
isidor 已提交
260
	KeybindingsRegistry.registerCommandAndKeybindingRule({
261
		id: CLOSE_UNMODIFIED_EDITORS_COMMAND_ID,
I
isidor 已提交
262
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
263
		when: void 0,
I
isidor 已提交
264
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
265
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
266 267
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);
I
isidor 已提交
268 269
			const contexts = getMultiSelectedEditorContexts(editorContext, accessor.get(IListService));
			const positions = contexts.map(context => positionAndInput(editorGroupService, editorService, context).position);
270

I
isidor 已提交
271 272
			return TPromise.join(distinct(positions.filter(p => typeof p === 'number'))
				.map(position => editorService.closeEditors(position, { unmodifiedOnly: true })));
273 274 275
		}
	});

I
isidor 已提交
276
	KeybindingsRegistry.registerCommandAndKeybindingRule({
277
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
278
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
279
		when: void 0,
I
isidor 已提交
280
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
281
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
282 283
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);
I
isidor 已提交
284 285 286
			const contexts = getMultiSelectedEditorContexts(editorContext, accessor.get(IListService));
			const positions = contexts.map(context => positionAndInput(editorGroupService, editorService, context).position);
			const distinctPositions = distinct(positions.filter(p => typeof p === 'number'));
287

I
isidor 已提交
288 289
			if (distinctPositions.length) {
				return editorService.closeEditors(distinctPositions);
290 291 292 293 294 295
			}

			return TPromise.as(false);
		}
	});

I
isidor 已提交
296
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
297
		id: CLOSE_EDITOR_COMMAND_ID,
I
isidor 已提交
298
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
299
		when: void 0,
I
isidor 已提交
300 301
		primary: KeyMod.CtrlCmd | KeyCode.KEY_W,
		win: { primary: KeyMod.CtrlCmd | KeyCode.F4, secondary: [KeyMod.CtrlCmd | KeyCode.KEY_W] },
302
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
I
isidor 已提交
303
			const editorGroupService = accessor.get(IEditorGroupService);
304
			const editorService = accessor.get(IWorkbenchEditorService);
I
isidor 已提交
305

306
			const position = editorContext ? editorGroupService.getStacksModel().positionOfGroup(editorContext.group) : null;
I
isidor 已提交
307 308 309 310 311 312 313

			// Close Active Editor
			if (typeof position !== 'number') {
				const activeEditor = editorService.getActiveEditor();
				if (activeEditor) {
					return editorService.closeEditor(activeEditor.position, activeEditor.input);
				}
314 315
			}

316
			let input = editorContext ? editorContext.editor : null;
I
isidor 已提交
317 318 319 320 321 322
			if (!input) {

				// Get Top Editor at Position
				const visibleEditors = editorService.getVisibleEditors();
				if (visibleEditors[position]) {
					input = visibleEditors[position].input;
323
				}
I
isidor 已提交
324 325 326 327 328 329 330
			}

			if (input) {
				return editorService.closeEditor(position, input);
			}

			return TPromise.as(false);
331 332 333
		}
	});

I
isidor 已提交
334
	KeybindingsRegistry.registerCommandAndKeybindingRule({
I
isidor 已提交
335
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
336
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
337 338
		when: void 0,
		primary: void 0,
I
isidor 已提交
339
		mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KEY_T },
340
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
I
isidor 已提交
341
			const editorGroupService = accessor.get(IEditorGroupService);
342 343
			const editorService = accessor.get(IWorkbenchEditorService);

344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361
			const { position, input } = positionAndInput(editorGroupService, editorService, editorContext);

			if (typeof position === 'number' && input) {
				return editorService.closeEditors(position, { except: input });
			}

			return TPromise.as(false);
		}
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);
I
isidor 已提交
362

363 364 365 366
			const { position, input } = positionAndInput(editorGroupService, editorService, editorContext);

			if (typeof position === 'number' && input) {
				return editorService.closeEditors(position, { except: input, direction: Direction.RIGHT });
367 368
			}

369 370 371 372 373 374 375 376 377 378 379 380 381 382 383
			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),
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);

			const { position, input } = positionAndInput(editorGroupService, editorService, editorContext);

I
isidor 已提交
384
			if (typeof position === 'number' && input) {
385
				return editorGroupService.pinEditor(position, input);
386 387
			}

I
isidor 已提交
388
			return TPromise.as(false);
389 390
		}
	});
B
Benjamin Pasero 已提交
391 392 393 394 395 396 397 398 399 400 401 402 403

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: SHOW_EDITORS_IN_GROUP,
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
		when: void 0,
		primary: void 0,
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);
			const quickOpenService = accessor.get(IQuickOpenService);

			const stacks = editorGroupService.getStacksModel();
			const groupCount = stacks.groups.length;
404
			if (groupCount <= 1) {
B
Benjamin Pasero 已提交
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419
				return quickOpenService.show(NAVIGATE_ALL_EDITORS_GROUP_PREFIX);
			}

			const { position } = positionAndInput(editorGroupService, editorService, editorContext);

			switch (position) {
				case Position.TWO:
					return quickOpenService.show(NAVIGATE_IN_GROUP_TWO_PREFIX);
				case Position.THREE:
					return quickOpenService.show(NAVIGATE_IN_GROUP_THREE_PREFIX);
			}

			return quickOpenService.show(NAVIGATE_IN_GROUP_ONE_PREFIX);
		}
	});
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: '_workbench.printStacksModel',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
		handler(accessor: ServicesAccessor) {
			console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
		},
		when: void 0,
		primary: void 0
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: '_workbench.validateStacksModel',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
		handler(accessor: ServicesAccessor) {
			(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
		},
		when: void 0,
		primary: void 0
	});
440
}
441 442 443 444 445 446 447 448 449 450 451 452 453 454

function positionAndInput(editorGroupService: IEditorGroupService, editorService: IWorkbenchEditorService, editorContext?: IEditorContext): { position: Position, input: IEditorInput } {
	let position = editorContext ? editorGroupService.getStacksModel().positionOfGroup(editorContext.group) : null;
	let input = editorContext ? editorContext.editor : null;

	// If position or input are not passed in take the position and input of the active editor.
	const active = editorService.getActiveEditor();
	if (active) {
		position = typeof position === 'number' ? position : active.position;
		input = input ? input : <EditorInput>active.input;
	}

	return { position, input };
}
I
isidor 已提交
455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475

export function getMultiSelectedEditorContexts(editorContext: IEditorContext, listService: IListService): IEditorContext[] {
	const list = listService.lastFocusedList;
	// Mapping for open editors view
	const elementToContext = element => element && element.editorGroup && element.editorInput ? { group: element.editorGroup, editor: element.editorInput } : { group: element };

	if (list instanceof List && list.isDOMFocused()) {
		const selection = list.getSelectedElements();
		const focus = list.getFocusedElements();
		// Only respect selection if it contains focused element
		if (focus.length && selection && selection.indexOf(focus[0]) >= 0) {
			return list.getSelectedElements().map(elementToContext);
		}

		if (focus) {
			return focus.map(elementToContext);
		}
	}

	return !!editorContext ? [editorContext] : [];
}