editorCommands.ts 15.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

I
isidor 已提交
22 23
export const CLOSE_UNMODIFIED_EDITORS_COMMAND_ID = 'workbench.action.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeEditorsInGroup';
24
export const CLOSE_EDITORS_TO_THE_RIGHT_COMMAND_ID = 'workbench.action.closeEditorsToTheRight';
I
isidor 已提交
25 26
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.action.closeActiveEditor';
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.action.closeOtherEditors';
27
export const KEEP_EDITOR_COMMAND_ID = 'workbench.action.keepEditor';
B
Benjamin Pasero 已提交
28 29 30 31 32 33
export const SHOW_EDITORS_IN_GROUP = 'workbench.action.showEditorsInGroup';

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 ';
34

35
export function setup(): void {
36 37
	registerActiveEditorMoveCommand();
	registerDiffEditorCommands();
38
	registerOpenEditorAtIndexCommands();
39
	registerEditorCommands();
S
Sandeep Somavarapu 已提交
40 41
}

B
Benjamin Pasero 已提交
42
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
S
Sandeep Somavarapu 已提交
43 44 45 46
	if (!types.isObject(arg)) {
		return false;
	}

47
	const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
S
Sandeep Somavarapu 已提交
48 49 50 51 52 53 54 55 56

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

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

57
	if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
S
Sandeep Somavarapu 已提交
58 59 60 61 62 63
		return false;
	}

	return true;
};

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

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

90
	const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
91 92 93 94 95 96 97
	if (activeEditor) {
		switch (args.by) {
			case ActiveEditorMovePositioningBy.TAB:
				return moveActiveTab(args, activeEditor, accessor);
			case ActiveEditorMovePositioningBy.GROUP:
				return moveActiveEditorToGroup(args, activeEditor, accessor);
		}
S
Sandeep Somavarapu 已提交
98 99 100
	}
}

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

S
Sandeep Somavarapu 已提交
126
	index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
127
	editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
S
Sandeep Somavarapu 已提交
128 129
}

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

S
Sandeep Somavarapu 已提交
153 154
	newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
	accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
155 156 157 158 159 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({
		id: '_workbench.printStacksModel',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
		handler(accessor: ServicesAccessor) {
			console.log(`${accessor.get(IEditorGroupService).getStacksModel().toString()}\n\n`);
		},
189 190
		when: void 0,
		primary: void 0
191 192 193 194 195 196 197 198
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: '_workbench.validateStacksModel',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
		handler(accessor: ServicesAccessor) {
			(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
		},
199 200
		when: void 0,
		primary: void 0
201
	});
202 203 204 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
}

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);
					}
				}
230 231

				return void 0;
232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248
			}
		});
	}

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

		return void 0;
251
	}
252 253
}

254
function registerEditorCommands() {
255

I
isidor 已提交
256
	KeybindingsRegistry.registerCommandAndKeybindingRule({
257
		id: CLOSE_UNMODIFIED_EDITORS_COMMAND_ID,
I
isidor 已提交
258
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
259
		when: void 0,
I
isidor 已提交
260
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_U),
261
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
262 263 264
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);

265
			const { position } = positionAndInput(editorGroupService, editorService, editorContext);
266 267 268 269 270 271 272 273 274

			if (typeof position === 'number') {
				return editorService.closeEditors(position, { unmodifiedOnly: true });
			}

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

I
isidor 已提交
275
	KeybindingsRegistry.registerCommandAndKeybindingRule({
276
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
277
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(),
278
		when: void 0,
I
isidor 已提交
279
		primary: KeyChord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W),
280
		handler: (accessor, resource: URI, editorContext: IEditorContext) => {
281 282 283
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);

284
			const { position } = positionAndInput(editorGroupService, editorService, editorContext);
285 286 287 288 289 290 291 292 293

			if (typeof position === 'number') {
				return editorService.closeEditors(position);
			}

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

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

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

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

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

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

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

			return TPromise.as(false);
329 330 331
		}
	});

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

342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359
			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 已提交
360

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

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

367 368 369 370 371 372 373 374 375 376 377 378 379 380 381
			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 已提交
382
			if (typeof position === 'number' && input) {
383
				return editorGroupService.pinEditor(position, input);
384 385
			}

I
isidor 已提交
386
			return TPromise.as(false);
387 388
		}
	});
B
Benjamin Pasero 已提交
389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417

	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;
			if (groupCount <= 1 || !context) {
				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);
		}
	});
418
}
419 420 421 422 423 424 425 426 427 428 429 430 431 432

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