editorCommonExtensions.ts 9.0 KB
Newer Older
E
Erich Gamma 已提交
1 2 3 4 5 6
/*---------------------------------------------------------------------------------------------
 *  Copyright (c) Microsoft Corporation. All rights reserved.
 *  Licensed under the MIT License. See License.txt in the project root for license information.
 *--------------------------------------------------------------------------------------------*/
'use strict';

A
Alex Dima 已提交
7
import {illegalArgument, onUnexpectedError} from 'vs/base/common/errors';
8
import URI from 'vs/base/common/uri';
A
Alex Dima 已提交
9
import {TPromise} from 'vs/base/common/winjs.base';
10
import {ServicesAccessor, IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
11 12
import {IKeybindings, KbExpr} from 'vs/platform/keybinding/common/keybinding';
import {ICommandHandler} from 'vs/platform/commands/common/commands';
A
Alex Dima 已提交
13 14
import {ICommandDescriptor, KeybindingsRegistry} from 'vs/platform/keybinding/common/keybindingsRegistry';
import {Registry} from 'vs/platform/platform';
E
Erich Gamma 已提交
15
import {ITelemetryService} from 'vs/platform/telemetry/common/telemetry';
A
Alex Dima 已提交
16 17 18
import {findFocusedEditor, getActiveEditor, withCodeEditorFromCommandHandler} from 'vs/editor/common/config/config';
import {Position} from 'vs/editor/common/core/position';
import * as editorCommon from 'vs/editor/common/editorCommon';
19
import {IModelService} from 'vs/editor/common/services/modelService';
20
import {MenuId, MenuRegistry} from 'vs/platform/actions/common/actions';
E
Erich Gamma 已提交
21

A
Alex Dima 已提交
22 23
const EditorKbExpr = editorCommon.EditorKbExpr;

24 25
export type ServicesAccessor = ServicesAccessor;

E
Erich Gamma 已提交
26
// --- Keybinding extensions to make it more concise to express keybindings conditions
27
export interface IEditorCommandMenuOptions {
28 29
	kbExpr: KbExpr;
	menu?: MenuId;
30
	group?: string;
31
	order?: number;
32
}
E
Erich Gamma 已提交
33 34 35 36

// --- Editor Actions

export interface IEditorCommandHandler {
A
Alex Dima 已提交
37
	(accessor:ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void;
E
Erich Gamma 已提交
38 39 40 41
}

export module CommonEditorRegistry {

A
Alex Dima 已提交
42 43
	export function registerEditorAction(desc:EditorAction) {
		(<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).registerEditorAction(desc);
44 45
	}

A
Alex Dima 已提交
46 47
	export function getEditorActions(): EditorAction[] {
		return (<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).getEditorActions();
48 49
	}

E
Erich Gamma 已提交
50
	// --- Editor Contributions
A
Alex Dima 已提交
51
	export function registerEditorContribution(ctor:editorCommon.ICommonEditorContributionCtor): void {
A
Alex Dima 已提交
52
		(<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).registerEditorContribution(ctor);
E
Erich Gamma 已提交
53
	}
A
Alex Dima 已提交
54
	export function getEditorContributions(): editorCommon.ICommonEditorContributionDescriptor[] {
A
Alex Dima 已提交
55
		return (<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).getEditorContributions();
E
Erich Gamma 已提交
56 57 58 59 60 61 62 63 64 65 66 67
	}

	// --- Editor Commands
	export function commandWeight(importance: number = 0): number {
		return KeybindingsRegistry.WEIGHT.editorContrib(importance);
	}

	export function registerEditorCommand(commandId: string, weight: number, keybinding:IKeybindings, needsTextFocus: boolean, needsKey: string, handler: IEditorCommandHandler): void {
		var commandDesc: ICommandDescriptor = {
			id: commandId,
			handler: createCommandHandler(commandId, handler),
			weight: weight,
68
			when: whenRule(needsTextFocus, needsKey),
E
Erich Gamma 已提交
69 70 71 72 73 74 75 76 77
			primary: keybinding.primary,
			secondary: keybinding.secondary,
			win: keybinding.win,
			linux: keybinding.linux,
			mac: keybinding.mac,
		};

		KeybindingsRegistry.registerCommandDesc(commandDesc);
	}
78 79 80 81

	export function registerLanguageCommand(id: string, handler: (accessor: ServicesAccessor, args: { [n: string]: any }) => any) {
		KeybindingsRegistry.registerCommandDesc({
			id,
82 83
			handler(accessor, args: any) {
				return handler(accessor, args || {});
84 85 86
			},
			weight: KeybindingsRegistry.WEIGHT.editorContrib(),
			primary: undefined,
87
			when: undefined,
88 89 90
		});
	}

A
Alex Dima 已提交
91
	export function registerDefaultLanguageCommand(id: string, handler: (model: editorCommon.IModel, position: Position, args: { [n: string]: any }) => any) {
92 93 94
		registerLanguageCommand(id, function(accessor, args) {

			const {resource, position} = args;
95
			if (!(resource instanceof URI) || !Position.isIPosition(position)) {
96 97 98 99 100 101 102 103
				throw illegalArgument();
			}

			const model = accessor.get(IModelService).getModel(resource);
			if (!model) {
				throw illegalArgument();
			}

104 105 106
			const editorPosition = Position.lift(position);

			return handler(model, editorPosition, args);
107 108
		});
	}
E
Erich Gamma 已提交
109 110
}

A
Alex Dima 已提交
111 112
class SimpleEditorContributionDescriptor implements editorCommon.ICommonEditorContributionDescriptor {
	private _ctor:editorCommon.ICommonEditorContributionCtor;
E
Erich Gamma 已提交
113

A
Alex Dima 已提交
114
	constructor(ctor:editorCommon.ICommonEditorContributionCtor) {
E
Erich Gamma 已提交
115 116 117
		this._ctor = ctor;
	}

A
Alex Dima 已提交
118
	public createInstance(instantiationService: IInstantiationService, editor:editorCommon.ICommonCodeEditor): editorCommon.IEditorContribution {
E
Erich Gamma 已提交
119 120 121 122 123 124 125 126 127 128 129
		return instantiationService.createInstance(this._ctor, editor);
	}
}

// Editor extension points
var Extensions = {
	EditorCommonContributions: 'editor.commonContributions'
};

class EditorContributionRegistry {

A
Alex Dima 已提交
130
	private editorContributions: editorCommon.ICommonEditorContributionDescriptor[];
A
Alex Dima 已提交
131
	private editorActions: EditorAction[];
E
Erich Gamma 已提交
132 133 134

	constructor() {
		this.editorContributions = [];
135
		this.editorActions = [];
E
Erich Gamma 已提交
136 137
	}

A
Alex Dima 已提交
138
	public registerEditorContribution(ctor:editorCommon.ICommonEditorContributionCtor): void {
E
Erich Gamma 已提交
139 140 141
		this.editorContributions.push(new SimpleEditorContributionDescriptor(ctor));
	}

A
Alex Dima 已提交
142
	public registerEditorAction(action:EditorAction) {
143 144

		if (action.menuOpts) {
A
Alex Dima 已提交
145
			MenuRegistry.appendMenuItem(action.menuOpts.menu || MenuId.EditorContext, {
146 147 148 149
				command: {
					id: action.id,
					title: action.label
				},
A
Alex Dima 已提交
150 151 152
				when: action.menuOpts.kbExpr,
				group: action.menuOpts.group,
				order: action.menuOpts.order
153 154 155 156 157 158 159 160 161 162 163
			});
		}

		if (!action.kbOpts) {
			action.kbOpts = {
				primary: 0
			};
		}

		let commandDesc: ICommandDescriptor = {
			id: action.id,
A
Alex Dima 已提交
164
			handler: action.kbOpts.commandHandler || triggerEditorActionGlobal.bind(null, action.id),
165 166 167 168 169 170 171 172 173 174 175 176 177 178
			weight: KeybindingsRegistry.WEIGHT.editorContrib(),
			when: action.kbOpts.kbExpr,
			primary: action.kbOpts.primary,
			secondary: action.kbOpts.secondary,
			win: action.kbOpts.win,
			linux: action.kbOpts.linux,
			mac: action.kbOpts.mac,
		};

		KeybindingsRegistry.registerCommandDesc(commandDesc);

		this.editorActions.push(action);
	}

A
Alex Dima 已提交
179
	public getEditorContributions(): editorCommon.ICommonEditorContributionDescriptor[] {
E
Erich Gamma 已提交
180 181
		return this.editorContributions.slice(0);
	}
182

A
Alex Dima 已提交
183
	public getEditorActions(): EditorAction[] {
184 185
		return this.editorActions.slice(0);
	}
E
Erich Gamma 已提交
186 187 188 189 190
}
Registry.add(Extensions.EditorCommonContributions, new EditorContributionRegistry());

function triggerEditorActionGlobal(actionId: string, accessor: ServicesAccessor, args: any): void {
	// TODO: this is not necessarily keyboard
191
	var focusedEditor = findFocusedEditor(actionId, accessor, false);
E
Erich Gamma 已提交
192 193 194 195 196
	if (focusedEditor) {
		focusedEditor.trigger('keyboard', actionId, args);
		return;
	}

A
Alex Dima 已提交
197
	var activeEditor = getActiveEditor(accessor);
E
Erich Gamma 已提交
198 199 200 201
	if (activeEditor) {
		var action = activeEditor.getAction(actionId);
		if (action) {
			accessor.get(ITelemetryService).publicLog('editorActionInvoked', {name: action.label} );
202
			action.run().done(null, onUnexpectedError);
E
Erich Gamma 已提交
203 204 205 206 207
		}
		return;
	}
}

208
function whenRule(needsTextFocus: boolean, needsKey: string): KbExpr {
209

A
Alex Dima 已提交
210
	let base = (needsTextFocus ? EditorKbExpr.TextFocus : EditorKbExpr.Focus);
211 212

	if (needsKey) {
213
		return KbExpr.and(base, KbExpr.has(needsKey));
E
Erich Gamma 已提交
214
	}
215

216
	return base;
E
Erich Gamma 已提交
217 218 219 220
}

function createCommandHandler(commandId: string, handler: IEditorCommandHandler): ICommandHandler {
	return (accessor, args) => {
221
		withCodeEditorFromCommandHandler(commandId, accessor, (editor) => {
222
			handler(accessor, editor, args||{});
E
Erich Gamma 已提交
223 224
		});
	};
225
}
226 227 228

export interface IEditorActionKeybindingOptions2 extends IKeybindings {
	kbExpr?: KbExpr;
A
Alex Dima 已提交
229
	commandHandler?: ICommandHandler;
230 231
}

A
Alex Dima 已提交
232
export abstract class EditorAction {
233 234 235 236 237 238 239 240 241

	private _needsWritableEditor: boolean;

	public id: string;
	public label: string;
	public alias: string;
	public kbOpts: IEditorActionKeybindingOptions2;
	public menuOpts: IEditorCommandMenuOptions;

242
	constructor(id:string, label:string, alias:string, needsWritableEditor:boolean) {
243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264
		this.id = id;
		this.label = label;
		this.alias = alias;
		this._needsWritableEditor = needsWritableEditor;
		this.kbOpts = null;
		this.menuOpts = null;
	}

	public enabled(accessor:ServicesAccessor, editor:editorCommon.ICommonCodeEditor): boolean {
		if (this._needsWritableEditor) {
			return !editor.getConfiguration().readOnly;
		}
		return true;
	}

	public supported(accessor:ServicesAccessor, editor:editorCommon.ICommonCodeEditor): boolean {
		if (this._needsWritableEditor) {
			return !editor.getConfiguration().readOnly;
		}
		return true;
	}

A
Alex Dima 已提交
265
	public abstract run(accessor:ServicesAccessor, editor:editorCommon.ICommonCodeEditor): void | TPromise<void>;
266 267
}

A
Alex Dima 已提交
268
export abstract class HandlerEditorAction extends EditorAction {
A
Alex Dima 已提交
269 270 271 272 273 274 275 276 277 278 279
	private _handlerId: string;

	constructor(id:string, label:string, alias:string, needsWritableEditor:boolean, handlerId: string) {
		super(id, label, alias, needsWritableEditor);
		this._handlerId = handlerId;
	}

	public run(accessor:ServicesAccessor, editor:editorCommon.ICommonCodeEditor): void {
		editor.trigger(this.id, this._handlerId, null);
	}
}