editorCommands.ts 13.6 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 13
import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService';
import { IEditor, Position, POSITIONS } 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';
17
import { ICommandService, CommandsRegistry } from 'vs/platform/commands/common/commands';
J
Johannes Rieken 已提交
18 19
import { IMessageService, Severity, CloseAction } from 'vs/platform/message/common/message';
import { Action } from 'vs/base/common/actions';
20
import { KeyMod, KeyCode } from 'vs/base/common/keyCodes';
21 22 23 24
import { TPromise } from 'vs/base/common/winjs.base';

export const CLOSE_UNMODIFIED_EDITORS_COMMAND_ID = 'workbench.command.closeUnmodifiedEditors';
export const CLOSE_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.command.closeEditorsInGroup';
I
isidor 已提交
25 26
export const CLOSE_EDITOR_COMMAND_ID = 'workbench.command.closeActiveEditor';
export const CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID = 'workbench.command.closeOtherEditors';
27

28
export function setup(): void {
29 30
	registerActiveEditorMoveCommand();
	registerDiffEditorCommands();
31
	registerOpenEditorAtIndexCommands();
32
	registerEditorCommands();
33
	handleCommandDeprecations();
S
Sandeep Somavarapu 已提交
34 35
}

B
Benjamin Pasero 已提交
36
const isActiveEditorMoveArg = function (arg: ActiveEditorMoveArguments): boolean {
S
Sandeep Somavarapu 已提交
37 38 39 40
	if (!types.isObject(arg)) {
		return false;
	}

41
	const activeEditorMoveArg: ActiveEditorMoveArguments = arg;
S
Sandeep Somavarapu 已提交
42 43 44 45 46 47 48 49 50

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

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

51
	if (!types.isUndefined(activeEditorMoveArg.value) && !types.isNumber(activeEditorMoveArg.value)) {
S
Sandeep Somavarapu 已提交
52 53 54 55 56 57
		return false;
	}

	return true;
};

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

78
function moveActiveEditor(args: ActiveEditorMoveArguments = {}, accessor: ServicesAccessor): void {
I
isidor 已提交
79
	const showTabs = accessor.get(IEditorGroupService).getTabOptions().showTabs;
S
Sandeep Somavarapu 已提交
80
	args.to = args.to || ActiveEditorMovePositioning.RIGHT;
I
isidor 已提交
81
	args.by = showTabs ? args.by || ActiveEditorMovePositioningBy.TAB : ActiveEditorMovePositioningBy.GROUP;
82
	args.value = types.isUndefined(args.value) ? 1 : args.value;
S
Sandeep Somavarapu 已提交
83

84
	const activeEditor = accessor.get(IWorkbenchEditorService).getActiveEditor();
85 86 87 88 89 90 91
	if (activeEditor) {
		switch (args.by) {
			case ActiveEditorMovePositioningBy.TAB:
				return moveActiveTab(args, activeEditor, accessor);
			case ActiveEditorMovePositioningBy.GROUP:
				return moveActiveEditorToGroup(args, activeEditor, accessor);
		}
S
Sandeep Somavarapu 已提交
92 93 94
	}
}

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

S
Sandeep Somavarapu 已提交
120
	index = index < 0 ? 0 : index >= editorGroup.count ? editorGroup.count - 1 : index;
121
	editorGroupsService.moveEditor(activeEditor.input, editorGroup, editorGroup, { index });
S
Sandeep Somavarapu 已提交
122 123
}

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

S
Sandeep Somavarapu 已提交
147 148
	newPosition = POSITIONS.indexOf(newPosition) !== -1 ? newPosition : activeEditor.position;
	accessor.get(IEditorGroupService).moveEditor(activeEditor.input, activeEditor.position, newPosition);
149 150 151 152 153 154 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 189 190 191 192 193 194 195 196 197 198 199
}

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`);
		},
		when: undefined,
		primary: undefined
	});

	KeybindingsRegistry.registerCommandAndKeybindingRule({
		id: '_workbench.validateStacksModel',
		weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
		handler(accessor: ServicesAccessor) {
			(<EditorStacksModel>accessor.get(IEditorGroupService).getStacksModel()).validate();
		},
		when: undefined,
		primary: undefined
	});
}

function handleCommandDeprecations(): void {
	const mapDeprecatedCommands = {
200 201
		'workbench.action.files.newFile': 'explorer.newFile',
		'workbench.action.files.newFolder': 'explorer.newFolder'
202 203 204
	};

	Object.keys(mapDeprecatedCommands).forEach(deprecatedCommandId => {
205
		const newCommandId: string = mapDeprecatedCommands[deprecatedCommandId];
206 207 208 209 210 211 212 213 214 215 216 217 218

		KeybindingsRegistry.registerCommandAndKeybindingRule({
			id: deprecatedCommandId,
			weight: KeybindingsRegistry.WEIGHT.workbenchContrib(0),
			handler(accessor: ServicesAccessor) {
				const messageService = accessor.get(IMessageService);
				const commandService = accessor.get(ICommandService);

				messageService.show(Severity.Warning, {
					message: nls.localize('commandDeprecated', "Command **{0}** has been removed. You can use **{1}** instead", deprecatedCommandId, newCommandId),
					actions: [
						new Action('openKeybindings', nls.localize('openKeybindings', "Configure Keyboard Shortcuts"), null, true, () => {
							return commandService.executeCommand('workbench.action.openGlobalKeybindings');
219 220
						}),
						CloseAction
221 222 223 224 225 226 227
					]
				});
			},
			when: undefined,
			primary: undefined
		});
	});
228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255
}

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);
					}
				}
256 257

				return void 0;
258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274
			}
		});
	}

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

		return void 0;
277
	}
278 279
}

280
function registerEditorCommands() {
281

282
	CommandsRegistry.registerCommand({
283
		id: CLOSE_UNMODIFIED_EDITORS_COMMAND_ID,
I
isidor 已提交
284
		handler: (accessor, args: IEditorContext) => {
285 286 287
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);

I
isidor 已提交
288
			let position = args ? editorGroupService.getStacksModel().positionOfGroup(args.group) : null;
289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305

			// If position is not passed in take the position of the active editor.
			if (typeof position !== 'number') {
				const active = editorService.getActiveEditor();
				if (active) {
					position = active.position;
				}
			}

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

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

306
	CommandsRegistry.registerCommand({
307
		id: CLOSE_EDITORS_IN_GROUP_COMMAND_ID,
I
isidor 已提交
308
		handler: (accessor, args: IEditorContext) => {
309 310 311
			const editorGroupService = accessor.get(IEditorGroupService);
			const editorService = accessor.get(IWorkbenchEditorService);

I
isidor 已提交
312
			let position = args ? editorGroupService.getStacksModel().positionOfGroup(args.group) : null;
313 314 315 316 317 318 319 320 321 322 323 324 325 326 327
			if (typeof position !== 'number') {
				const activeEditor = editorService.getActiveEditor();
				if (activeEditor) {
					position = activeEditor.position;
				}
			}

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

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

328
	CommandsRegistry.registerCommand({
I
isidor 已提交
329 330 331
		id: CLOSE_EDITOR_COMMAND_ID,
		handler: (accessor, args: IEditorContext) => {
			const editorGroupService = accessor.get(IEditorGroupService);
332
			const editorService = accessor.get(IWorkbenchEditorService);
I
isidor 已提交
333 334 335 336 337 338 339 340 341

			const position = args ? editorGroupService.getStacksModel().positionOfGroup(args.group) : null;

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

I
isidor 已提交
344 345 346 347 348 349 350
			let input = args ? args.editor : null;
			if (!input) {

				// Get Top Editor at Position
				const visibleEditors = editorService.getVisibleEditors();
				if (visibleEditors[position]) {
					input = visibleEditors[position].input;
351
				}
I
isidor 已提交
352 353 354 355 356 357 358
			}

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

			return TPromise.as(false);
359 360 361
		}
	});

362
	CommandsRegistry.registerCommand({
I
isidor 已提交
363 364 365
		id: CLOSE_OTHER_EDITORS_IN_GROUP_COMMAND_ID,
		handler: (accessor, args: IEditorContext) => {
			const editorGroupService = accessor.get(IEditorGroupService);
366 367
			const editorService = accessor.get(IWorkbenchEditorService);

I
isidor 已提交
368 369 370 371 372 373 374 375
			let position = args ? editorGroupService.getStacksModel().positionOfGroup(args.group) : null;
			let input = args ? args.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;
376 377
			}

I
isidor 已提交
378 379
			if (typeof position === 'number' && input) {
				return editorService.closeEditors(position, { except: input });
380 381
			}

I
isidor 已提交
382
			return TPromise.as(false);
383 384 385
		}
	});
}