Clean up editor action post adoption

This commit is contained in:
Alex Dima 2016-08-04 15:34:39 +02:00
parent 952405e9df
commit f64059d2f6
12 changed files with 119 additions and 616 deletions

View file

@ -29,7 +29,7 @@ import {View} from 'vs/editor/browser/view/viewImpl';
import {Disposable, IDisposable} from 'vs/base/common/lifecycle';
import Event, {Emitter} from 'vs/base/common/event';
import {IKeyboardEvent} from 'vs/base/browser/keyboardEvent';
import {NewEditorAction} from 'vs/editor/common/editorAction';
import {InternalEditorAction} from 'vs/editor/common/editorAction';
export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.ICodeEditor {
@ -98,20 +98,19 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.
this.contentWidgets = {};
this.overlayWidgets = {};
var contributionDescriptors = [].concat(EditorBrowserRegistry.getEditorContributions()).concat(CommonEditorRegistry.getEditorContributions());
for (var i = 0, len = contributionDescriptors.length; i < len; i++) {
let contributionDescriptors = [].concat(EditorBrowserRegistry.getEditorContributions()).concat(CommonEditorRegistry.getEditorContributions());
for (let i = 0, len = contributionDescriptors.length; i < len; i++) {
try {
var contribution = contributionDescriptors[i].createInstance(this._instantiationService, this);
this.contributions[contribution.getId()] = contribution;
let contribution = contributionDescriptors[i].createInstance(this._instantiationService, this);
this._contributions[contribution.getId()] = contribution;
} catch (err) {
console.error('Could not instantiate contribution ' + contribution.getId());
onUnexpectedError(err);
}
}
CommonEditorRegistry.getEditorActions().forEach((action) => {
let contribution = new NewEditorAction(action, this, this._instantiationService);
this.contributions[contribution.getId()] = contribution;
let internalAction = new InternalEditorAction(action, this, this._instantiationService);
this._actions[internalAction.id] = internalAction;
});
}
@ -239,15 +238,18 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.
return null;
}
let contributionsState: {[key:string]:any} = {};
for (let id in this.contributions) {
let contribution = this.contributions[id];
let keys = Object.keys(this._contributions);
for (let i = 0, len = keys.length; i < len; i++) {
let id = keys[i];
let contribution = this._contributions[id];
if (typeof contribution.saveViewState === 'function') {
contributionsState[id] = contribution.saveViewState();
}
}
var cursorState = this.cursor.saveState();
var viewState = this._view.saveState();
let cursorState = this.cursor.saveState();
let viewState = this._view.saveState();
return {
cursorState: cursorState,
viewState: viewState,
@ -272,8 +274,10 @@ export class CodeEditorWidget extends CommonCodeEditor implements editorBrowser.
this._view.restoreState(codeEditorState.viewState);
let contributionsState = s.contributionsState || {};
for (let id in this.contributions) {
let contribution = this.contributions[id];
let keys = Object.keys(this._contributions);
for (let i = 0, len = keys.length; i < len; i++) {
let id = keys[i];
let contribution = this._contributions[id];
if (typeof contribution.restoreViewState === 'function') {
contribution.restoreViewState(contributionsState[id]);
}

View file

@ -6,7 +6,6 @@
'use strict';
import 'vs/css!./media/diffEditor';
import {IAction} from 'vs/base/common/actions';
import {RunOnceScheduler} from 'vs/base/common/async';
import {EventEmitter, EmitterEvent} from 'vs/base/common/eventEmitter';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
@ -597,15 +596,15 @@ export class DiffEditorWidget extends EventEmitter implements editorBrowser.IDif
this.modifiedEditor.addAction(descriptor);
}
public getActions(): IAction[] {
public getActions(): editorCommon.IEditorAction[] {
return this.modifiedEditor.getActions();
}
public getSupportedActions(): IAction[] {
public getSupportedActions(): editorCommon.IEditorAction[] {
return this.modifiedEditor.getSupportedActions();
}
public getAction(id:string): IAction {
public getAction(id:string): editorCommon.IEditorAction {
return this.modifiedEditor.getAction(id);
}

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IAction, isAction} from 'vs/base/common/actions';
import {onUnexpectedError} from 'vs/base/common/errors';
import {EventEmitter, IEventEmitter} from 'vs/base/common/eventEmitter';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
@ -24,7 +23,7 @@ import {EditorState} from 'vs/editor/common/core/editorState';
import {Position} from 'vs/editor/common/core/position';
import {Range} from 'vs/editor/common/core/range';
import {Selection} from 'vs/editor/common/core/selection';
import {EditorAction, DynamicEditorAction} from 'vs/editor/common/editorAction';
import {DynamicEditorAction} from 'vs/editor/common/editorAction';
import * as editorCommon from 'vs/editor/common/editorCommon';
import {ICodeEditorService} from 'vs/editor/common/services/codeEditorService';
import {CharacterHardWrappingLineMapperFactory} from 'vs/editor/common/viewModel/characterHardWrappingLineMapper';
@ -92,7 +91,8 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
_telemetryService:ITelemetryService;
protected contributions:{ [key:string]:editorCommon.IEditorContribution; };
protected _contributions:{ [key:string]:editorCommon.IEditorContribution; };
protected _actions:{ [key:string]:editorCommon.IEditorAction; };
// --- Members logically associated to a model
protected model:editorCommon.IModel;
@ -180,9 +180,8 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
this._attachModel(null);
// Create editor contributions
this.contributions = {};
this._contributions = {};
this._actions = {};
timerEvent.stop();
@ -207,13 +206,15 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
this._codeEditorService.removeCodeEditor(this);
this._lifetimeDispose = dispose(this._lifetimeDispose);
let keys = Object.keys(this.contributions);
let keys = Object.keys(this._contributions);
for (let i = 0, len = keys.length; i < len; i++) {
let contributionId = keys[i];
this.contributions[contributionId].dispose();
this._contributions[contributionId].dispose();
}
this._contributions = {};
this.contributions = {};
// editor actions don't need to be disposed
this._actions = {};
this._postDetachModelCleanup(this._detachModel());
this._configuration.dispose();
@ -545,7 +546,7 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
public abstract hasWidgetFocus(): boolean;
public getContribution(id: string): editorCommon.IEditorContribution {
return this.contributions[id] || null;
return this._contributions[id] || null;
}
public addAction(descriptor:editorCommon.IActionDescriptor): void {
@ -556,42 +557,31 @@ export abstract class CommonCodeEditor extends EventEmitter implements editorCom
) {
throw new Error('Invalid action descriptor, `id`, `label` and `run` are required properties!');
}
var action = this._instantiationService.createInstance(DynamicEditorAction, descriptor, this);
this.contributions[action.getId()] = action;
let action = new DynamicEditorAction(descriptor, this);
this._actions[action.id] = action;
}
public getActions(): IAction[] {
let result: IAction[] = [];
public getActions(): editorCommon.IEditorAction[] {
let result: editorCommon.IEditorAction[] = [];
let keys = Object.keys(this.contributions);
let keys = Object.keys(this._actions);
for (let i = 0, len = keys.length; i < len; i++) {
let id = keys[i];
let contribution = <any>this.contributions[id];
// contribution instanceof IAction
if (isAction(contribution)) {
result.push(<IAction>contribution);
}
result.push(this._actions[id]);
}
return result;
}
public getSupportedActions(): IAction[] {
public getSupportedActions(): editorCommon.IEditorAction[] {
let result = this.getActions();
result = result.filter(action => (<EditorAction>action).isSupported());
result = result.filter(action => action.isSupported());
return result;
}
public getAction(id:string): IAction {
var contribution = <any>this.contributions[id];
if (contribution) {
// contribution instanceof IAction
if (isAction(contribution)) {
return <IAction>contribution;
}
}
return null;
public getAction(id:string): editorCommon.IEditorAction {
return this._actions[id] || null;
}
public trigger(source:string, handlerId:string, payload:any): void {

View file

@ -4,225 +4,74 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {Action} from 'vs/base/common/actions';
import * as strings from 'vs/base/common/strings';
import {TPromise} from 'vs/base/common/winjs.base';
import {Behaviour, IEnablementState, createActionEnablement} from 'vs/editor/common/editorActionEnablement';
import {IActionDescriptor, IActionEnablement, ICommonCodeEditor, IEditorActionDescriptorData, IEditorContribution} from 'vs/editor/common/editorCommon';
import {ILineContext} from 'vs/editor/common/modes';
import {IActionDescriptor, ICommonCodeEditor, IEditorAction} from 'vs/editor/common/editorCommon';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {EditorAction2} from 'vs/editor/common/editorCommonExtensions';
var defaultBehaviour = Behaviour.TextFocus | Behaviour.Writeable | Behaviour.UpdateOnModelChange;
export abstract class AbstractInternalEditorAction {
export class EditorAction extends Action implements IEditorContribution {
public id: string;
public label: string;
public alias: string;
protected _editor: ICommonCodeEditor;
public editor:ICommonCodeEditor;
private _supportsReadonly:boolean;
private _descriptor:IEditorActionDescriptorData;
private _enablementState:IEnablementState;
constructor(descriptor:IEditorActionDescriptorData, editor:ICommonCodeEditor, condition:Behaviour = defaultBehaviour) {
super(descriptor.id);
this.editor = editor;
this._descriptor = descriptor;
this.label = descriptor.label || '';
this._enablementState = createActionEnablement(editor, condition, this);
this._supportsReadonly = !(condition & Behaviour.Writeable);
}
public getId(): string {
return this.id;
}
public dispose(): void {
this._enablementState.dispose();
super.dispose();
}
public getDescriptor(): IEditorActionDescriptorData {
return this._descriptor;
}
// ---- enablement state mangament --------------------------------------------------------
public get enabled():boolean {
return this._enablementState.value();
}
public set enabled(value:boolean) {
// call reset?
var e:any = new Error();
console.log('setting EditorAction.enabled is UNCOOL. Use resetEnablementState and getEnablementState');
console.log(e.stack);
}
public resetEnablementState():void {
this._enablementState.reset();
}
/**
* Returns {{true}} in case this action works
* with the current mode. To be overwritten
* in subclasses.
*/
public isSupported():boolean {
if (!this._supportsReadonly) {
if (this.editor.getConfiguration().readOnly) {
return false; // action requires a writeable model
}
var model = this.editor.getModel();
if (model && model.hasEditableRange()) {
return false; // editable ranges are an indicator for mostly readonly models
}
}
return true;
}
/**
* Returns the enablement state of this action. This
* method is being called in the process of {{updateEnablementState}}
* and overwriters should call super (this method).
*/
public getEnablementState(): boolean {
return true;
}
public getAlias(): string {
return this._descriptor.alias;
constructor(id:string, label:string, alias:string, editor:ICommonCodeEditor) {
this.id = id;
this.label = label;
this.alias = alias;
}
}
export class NewEditorAction extends EditorAction {
export class InternalEditorAction extends AbstractInternalEditorAction implements IEditorAction {
private _actual: EditorAction2;
private _instantiationService:IInstantiationService;
constructor(actual:EditorAction2, editor:ICommonCodeEditor, instantiationService:IInstantiationService) {
super({ id: actual.id, label: actual.label, alias: actual.alias }, editor, 0);
super(actual.id, actual.label, actual.alias, editor);
this._actual = actual;
this._instantiationService = instantiationService;
}
public get enabled():boolean {
return this._instantiationService.invokeFunction((accessor) => {
return this._actual.enabled(accessor, this.editor);
return this._actual.enabled(accessor, this._editor);
});
}
public isSupported():boolean {
return this._instantiationService.invokeFunction((accessor) => {
return this._actual.supported(accessor, this.editor);
return this._actual.supported(accessor, this._editor);
});
}
public run(): TPromise<void> {
return this._instantiationService.invokeFunction((accessor) => {
this._actual.run(accessor, this.editor);
return TPromise.as(void 0);
return TPromise.as(this._actual.run(accessor, this._editor));
});
}
}
export class DynamicEditorAction extends EditorAction {
private static _transformBehaviour(behaviour:IActionEnablement): Behaviour {
var r = 0;
if (behaviour.textFocus) {
// Allowed to set text focus only if not appearing in the context menu
r |= Behaviour.TextFocus;
}
if (behaviour.widgetFocus) {
r |= Behaviour.WidgetFocus;
}
if (behaviour.writeableEditor) {
r |= Behaviour.Writeable;
}
if (typeof behaviour.tokensAtPosition !== 'undefined') {
r |= Behaviour.UpdateOnCursorPositionChange;
}
if (typeof behaviour.wordAtPosition !== 'undefined') {
r |= Behaviour.UpdateOnCursorPositionChange;
}
return r;
}
export class DynamicEditorAction extends AbstractInternalEditorAction implements IEditorAction {
private _run: (editor:ICommonCodeEditor)=>void;
private _tokensAtPosition:string[];
private _wordAtPosition:boolean;
constructor(descriptor:IActionDescriptor, editor:ICommonCodeEditor) {
var enablement: IActionEnablement = descriptor.enablement || {};
super({
id: descriptor.id,
label: descriptor.label
}, editor, DynamicEditorAction._transformBehaviour(enablement));
super(descriptor.id, descriptor.label, descriptor.label, editor);
this._run = descriptor.run;
}
this._tokensAtPosition = enablement.tokensAtPosition;
this._wordAtPosition = enablement.wordAtPosition;
public get enabled():boolean {
return true;
}
public isSupported():boolean {
return true;
}
public run(): TPromise<void> {
return TPromise.as(this._run(this.editor));
}
public getEnablementState():boolean {
return this._getEnablementOnTokens() && this._getEnablementOnWord();
}
private _getEnablementOnTokens(): boolean {
if (!this._tokensAtPosition) {
return true;
}
var model = this.editor.getModel(),
position = this.editor.getSelection().getStartPosition(),
lineContext = model.getLineContext(position.lineNumber),
offset = position.column - 1;
return isToken(lineContext, offset, this._tokensAtPosition);
}
private _getEnablementOnWord(): boolean {
if (!this._wordAtPosition) {
return true;
}
var model = this.editor.getModel(),
position = this.editor.getSelection().getStartPosition(),
wordAtPosition = model.getWordAtPosition(position);
return (!!wordAtPosition);
return TPromise.as(this._run(this._editor));
}
}
function isToken(context:ILineContext, offset:number, types:string[]): boolean {
if (context.getLineContent().length <= offset) {
return false;
}
var tokenIdx = context.findIndexOfOffset(offset);
var type = context.getTokenType(tokenIdx);
for (var i = 0, len = types.length; i < len; i++) {
if (types[i] === '') {
if (type === '') {
return true;
}
} else {
if (strings.startsWith(type, types[i])) {
return true;
}
}
}
return false;
}

View file

@ -1,197 +0,0 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IDisposable, dispose} from 'vs/base/common/lifecycle';
import {ICommonCodeEditor} from 'vs/editor/common/editorCommon';
export enum Behaviour {
TextFocus = 1 << 0,
WidgetFocus = 1 << 1,
Writeable = 1 << 2,
UpdateOnModelChange = 1 << 3,
UpdateOnCursorPositionChange = 1 << 6
}
export interface IEditorAction {
isSupported(): boolean;
getEnablementState(): boolean;
}
export function createActionEnablement(editor: ICommonCodeEditor, condition:Behaviour, action:IEditorAction): IEnablementState {
return new CompositeEnablementState([new InternalEnablementState(condition, editor), new DescentEnablementState(condition, editor, action)]);
}
/**
* Used to signal that something enabled
*/
export interface IEnablementState extends IDisposable {
value(): boolean;
reset(): void;
}
/**
* A composite that acts like a logical AND on
* enablement states
*/
class CompositeEnablementState implements IEnablementState {
constructor(private _delegates:IEnablementState[]) {
// empty
}
public value():boolean {
return this._delegates.every(d => d.value());
}
public reset():void {
this._delegates.forEach(d => {
if(d instanceof CachingEnablementState) {
(<CachingEnablementState> d).reset();
}
});
}
public dispose():void {
this._delegates.forEach(d => d.dispose());
}
}
/**
* A enablement state that caches its result until
* reset is called.
*/
class CachingEnablementState implements IEnablementState {
private _value:boolean;
constructor() {
this._value = null;
}
public reset():void {
this._value = null;
}
public dispose():void {
//
}
public value():boolean {
if (this._value === null) {
this._value = this._computeValue();
}
return this._value;
}
public _computeValue():boolean {
return false;
}
}
/**
* An enablement state that checks behaviours of the
* editor action that can be check inside the action,
* for instance: widget focus, text focus, readonly-ness
*/
class InternalEnablementState extends CachingEnablementState {
public hasTextFocus:boolean;
public hasWidgetFocus:boolean;
public isReadOnly:boolean;
private _callOnDispose:IDisposable[];
constructor(private _behaviour:Behaviour, private editor:ICommonCodeEditor) {
super();
this.hasTextFocus = false;
this.hasWidgetFocus = false;
this.isReadOnly = false;
this._callOnDispose = [];
if (this._behaviour & Behaviour.TextFocus) {
this._callOnDispose.push(this.editor.onDidFocusEditorText(() => this._updateTextFocus(true)));
this._callOnDispose.push(this.editor.onDidBlurEditorText(() => this._updateTextFocus(false)));
}
if (this._behaviour & Behaviour.WidgetFocus) {
this._callOnDispose.push(this.editor.onDidFocusEditor(() => this._updateWidgetFocus(true)));
this._callOnDispose.push(this.editor.onDidBlurEditor(() => this._updateWidgetFocus(false)));
}
if (this._behaviour & Behaviour.Writeable) {
this._callOnDispose.push(this.editor.onDidChangeConfiguration((e) => this._update()));
}
}
private _updateTextFocus(hasTextFocus:boolean):void {
this.hasTextFocus = hasTextFocus;
this.reset();
}
private _updateWidgetFocus(hasWidgetFocus:boolean):void {
this.hasWidgetFocus = hasWidgetFocus;
this.reset();
}
private _update():void {
this.isReadOnly = this.editor.getConfiguration().readOnly;
this.reset();
}
public dispose():void {
super.dispose();
dispose(this._callOnDispose);
}
public _computeValue():boolean {
if(this._behaviour & Behaviour.TextFocus && !this.hasTextFocus) {
return false;
}
if(this._behaviour & Behaviour.WidgetFocus && !this.hasWidgetFocus) {
return false;
}
if(this._behaviour & Behaviour.Writeable && this.isReadOnly) {
return false;
}
return true;
}
}
/**
* An enablement state that makes uses of the
* {{isSupported}} and {{getEnablementState}}
* functions that are supposed to be overwritten.
*/
class DescentEnablementState extends CachingEnablementState {
private _callOnDispose:IDisposable[] = [];
constructor(behaviour:Behaviour, private editor:ICommonCodeEditor, private _action:IEditorAction) {
super();
if (behaviour & Behaviour.UpdateOnModelChange) {
this._callOnDispose.push(this.editor.onDidChangeModel(() => this.reset()));
this._callOnDispose.push(this.editor.onDidChangeModelMode(() => this.reset()));
this._callOnDispose.push(this.editor.onDidChangeModelModeSupport(() => this.reset()));
}
if (behaviour & Behaviour.UpdateOnCursorPositionChange) {
this._callOnDispose.push(this.editor.onDidChangeCursorPosition(() => this.reset()));
}
}
public _computeValue():boolean {
if(!this.editor.getModel()) {
return false;
}
if(!this._action.isSupported()) {
return false;
}
if(!this._action.getEnablementState()) {
return false;
}
return true;
}
}

View file

@ -4,7 +4,6 @@
*--------------------------------------------------------------------------------------------*/
'use strict';
import {IAction} from 'vs/base/common/actions';
import {IEventEmitter, BulkListenerCallback} from 'vs/base/common/eventEmitter';
import {MarkedString} from 'vs/base/common/htmlContent';
import * as types from 'vs/base/common/types';
@ -2881,37 +2880,6 @@ export interface IDimension {
width:number;
height:number;
}
/**
* Conditions describing action enablement
*/
export interface IActionEnablement {
/**
* The action is enabled only if text in the editor is focused (e.g. blinking cursor).
* Warning: This condition will be disabled if the action is marked to be displayed in the context menu
* Defaults to false.
*/
textFocus?: boolean;
/**
* The action is enabled only if the editor or its widgets have focus (e.g. focus is in find widget).
* Defaults to false.
*/
widgetFocus?: boolean;
/**
* The action is enabled only if the editor is not in read only mode.
* Defaults to false.
*/
writeableEditor?: boolean;
/**
* The action is enabled only if the cursor position is over tokens of a certain kind.
* Defaults to no tokens required.
*/
tokensAtPosition?: string[];
/**
* The action is enabled only if the cursor position is over a word (i.e. not whitespace).
* Defaults to false.
*/
wordAtPosition?: boolean;
}
/**
* A (serializable) state of the cursors.
@ -3440,11 +3408,6 @@ export interface IActionDescriptor {
* The keybinding rule.
*/
keybindingContext?: string;
/**
* A set of enablement conditions.
*/
enablement?: IActionEnablement;
/**
* Method that will be executed when the action is triggered.
* @param editor The editor instance is passed in as a convinience
@ -3483,6 +3446,15 @@ export interface ICommonEditorContributionDescriptor {
createInstance(instantiationService:IInstantiationService, editor:ICommonCodeEditor): IEditorContribution;
}
export interface IEditorAction {
id: string;
label: string;
alias: string;
enabled: boolean;
isSupported():boolean;
run(): TPromise<void>;
}
/**
* An editor.
*/
@ -3585,12 +3557,12 @@ export interface IEditor {
/**
* Returns all actions associated with this editor.
*/
getActions(): IAction[];
getActions(): IEditorAction[];
/**
* Returns all actions associated with this editor.
*/
getSupportedActions(): IAction[];
getSupportedActions(): IEditorAction[];
/**
* Saves current view state of the editor in a serializable object.
@ -3985,7 +3957,7 @@ export interface ICommonCodeEditor extends IEditor {
* @id Unique identifier of the contribution.
* @return The action or null if action not found.
*/
getAction(id: string): IAction;
getAction(id: string): IEditorAction;
/**
* Execute a command on the editor.

View file

@ -23,19 +23,6 @@ import {MenuId, MenuRegistry} from 'vs/platform/actions/common/actions';
export type ServicesAccessor = ServicesAccessor;
// --- Keybinding extensions to make it more concise to express keybindings conditions
export enum ContextKey {
None = 0,
EditorTextFocus = 1,
EditorFocus = 2
}
export interface IEditorActionKeybindingOptions extends IKeybindings {
handler?: ICommandHandler;
context: ContextKey;
kbExpr?: KbExpr;
}
export interface IEditorCommandKeybindingOptions extends IKeybindings {
context: ContextKey;
}
export interface IEditorCommandMenuOptions {
kbExpr: KbExpr;
menu?: MenuId;
@ -44,26 +31,6 @@ export interface IEditorCommandMenuOptions {
}
// --- Editor Actions
export class EditorActionDescriptor {
public ctor: editorCommon.IEditorActionContributionCtor;
public id: string;
public label: string;
public alias: string;
public kbOpts: IEditorActionKeybindingOptions;
public menuOpts: IEditorCommandMenuOptions;
constructor(ctor: editorCommon.IEditorActionContributionCtor, id: string, label: string,
kbOpts: IEditorActionKeybindingOptions = defaultEditorActionKeybindingOptions,
alias?: string
) {
this.ctor = ctor;
this.id = id;
this.label = label;
this.alias = alias;
this.kbOpts = kbOpts;
}
}
export interface IEditorCommandHandler {
(accessor:ServicesAccessor, editor: editorCommon.ICommonCodeEditor, args: any): void;
@ -71,10 +38,6 @@ export interface IEditorCommandHandler {
export module CommonEditorRegistry {
export function registerEditorAction(desc:EditorActionDescriptor) {
(<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).registerEditorAction(desc);
}
export function registerEditorAction2(desc:EditorAction2) {
(<EditorContributionRegistry>Registry.as(Extensions.EditorCommonContributions)).registerEditorAction2(desc);
}
@ -229,58 +192,6 @@ class EditorContributionRegistry {
this.editorActions.push(action);
}
public registerEditorAction(desc:EditorActionDescriptor): void {
let handler = desc.kbOpts.handler;
if (!handler) {
// here
if (desc.kbOpts.context === ContextKey.EditorTextFocus || desc.kbOpts.context === ContextKey.EditorFocus) {
handler = triggerEditorAction.bind(null, desc.id);
} else {
handler = triggerEditorActionGlobal.bind(null, desc.id);
}
}
let when: KbExpr = null;
if (typeof desc.kbOpts.kbExpr === 'undefined') {
// here
if (desc.kbOpts.context === ContextKey.EditorTextFocus) {
when = KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS);
} else if (desc.kbOpts.context === ContextKey.EditorFocus) {
when = KbExpr.has(editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS);
}
} else {
when = desc.kbOpts.kbExpr;
}
if (desc.menuOpts) {
let {menu, kbExpr, group, order} = desc.menuOpts;
MenuRegistry.appendMenuItem(menu || MenuId.EditorContext, {
command: {
id: desc.id,
title: desc.label
},
when: kbExpr,
group,
order
});
}
let commandDesc: ICommandDescriptor = {
id: desc.id,
handler: handler,
weight: KeybindingsRegistry.WEIGHT.editorContrib(),
when: when,
primary: desc.kbOpts.primary,
secondary: desc.kbOpts.secondary,
win: desc.kbOpts.win,
linux: desc.kbOpts.linux,
mac: desc.kbOpts.mac,
};
KeybindingsRegistry.registerCommandDesc(commandDesc);
this.editorContributions.push(new InternalEditorActionDescriptor(desc.ctor, desc.id, desc.label, desc.alias));
}
public getEditorContributions2(): editorCommon.ICommonEditorContributionDescriptor[] {
return this.editorContributions.slice(0);
}
@ -291,12 +202,6 @@ class EditorContributionRegistry {
}
Registry.add(Extensions.EditorCommonContributions, new EditorContributionRegistry());
function triggerEditorAction(actionId: string, accessor: ServicesAccessor, args: any): void {
withCodeEditorFromCommandHandler(actionId, accessor, (editor) => {
editor.trigger('keyboard', actionId, args);
});
}
function triggerEditorActionGlobal(actionId: string, accessor: ServicesAccessor, args: any): void {
// TODO: this is not necessarily keyboard
var focusedEditor = findFocusedEditor(actionId, accessor, false);
@ -316,8 +221,6 @@ function triggerEditorActionGlobal(actionId: string, accessor: ServicesAccessor,
}
}
export var defaultEditorActionKeybindingOptions:IEditorActionKeybindingOptions = { primary: null, context: ContextKey.EditorTextFocus };
function whenRule(needsTextFocus: boolean, needsKey: string): KbExpr {
let base = KbExpr.has(needsTextFocus ? editorCommon.KEYBINDING_CONTEXT_EDITOR_TEXT_FOCUS : editorCommon.KEYBINDING_CONTEXT_EDITOR_FOCUS);

View file

@ -5,14 +5,13 @@
'use strict';
import * as nls from 'vs/nls';
import {IAction} from 'vs/base/common/actions';
import {onUnexpectedError} from 'vs/base/common/errors';
import {matchesFuzzy} from 'vs/base/common/filters';
import {TPromise} from 'vs/base/common/winjs.base';
import {IContext, IHighlight, QuickOpenEntryGroup, QuickOpenModel} from 'vs/base/parts/quickopen/browser/quickOpenModel';
import {IAutoFocus, Mode} from 'vs/base/parts/quickopen/common/quickOpen';
import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding';
import {ICommonCodeEditor, IEditor} from 'vs/editor/common/editorCommon';
import {IEditorAction, ICommonCodeEditor, IEditor} from 'vs/editor/common/editorCommon';
import {BaseEditorQuickOpenAction} from './editorQuickOpen';
import {EditorKbExpr, ServicesAccessor} from 'vs/editor/common/editorCommonExtensions';
import {KeyCode, KeyMod} from 'vs/base/common/keyCodes';
@ -20,10 +19,10 @@ import * as browser from 'vs/base/browser/browser';
export class EditorActionCommandEntry extends QuickOpenEntryGroup {
private key: string;
private action: IAction;
private action: IEditorAction;
private editor: IEditor;
constructor(key: string, highlights: IHighlight[], action: IAction, editor: IEditor) {
constructor(key: string, highlights: IHighlight[], action: IEditorAction, editor: IEditor) {
super();
this.key = key;
@ -115,7 +114,7 @@ export class QuickCommandAction extends BaseEditorQuickOpenAction {
}
private _editorActionsToEntries(keybindingService:IKeybindingService, editor:ICommonCodeEditor, searchValue: string): EditorActionCommandEntry[] {
let actions: IAction[] = editor.getSupportedActions();
let actions: IEditorAction[] = editor.getSupportedActions();
let entries: EditorActionCommandEntry[] = [];
for (let i = 0; i < actions.length; i++) {

View file

@ -60,7 +60,7 @@ export class MockCodeEditor extends CommonCodeEditor {
public registerAndInstantiateContribution<T extends editorCommon.IEditorContribution>(ctor:any): T {
let r = <T>this._instantiationService.createInstance(ctor, this);
this.contributions[r.getId()] = r;
this._contributions[r.getId()] = r;
return r;
}
}

51
src/vs/monaco.d.ts vendored
View file

@ -2570,38 +2570,6 @@ declare module monaco.editor {
height: number;
}
/**
* Conditions describing action enablement
*/
export interface IActionEnablement {
/**
* The action is enabled only if text in the editor is focused (e.g. blinking cursor).
* Warning: This condition will be disabled if the action is marked to be displayed in the context menu
* Defaults to false.
*/
textFocus?: boolean;
/**
* The action is enabled only if the editor or its widgets have focus (e.g. focus is in find widget).
* Defaults to false.
*/
widgetFocus?: boolean;
/**
* The action is enabled only if the editor is not in read only mode.
* Defaults to false.
*/
writeableEditor?: boolean;
/**
* The action is enabled only if the cursor position is over tokens of a certain kind.
* Defaults to no tokens required.
*/
tokensAtPosition?: string[];
/**
* The action is enabled only if the cursor position is over a word (i.e. not whitespace).
* Defaults to false.
*/
wordAtPosition?: boolean;
}
/**
* A (serializable) state of the cursors.
*/
@ -2808,10 +2776,6 @@ declare module monaco.editor {
* The keybinding rule.
*/
keybindingContext?: string;
/**
* A set of enablement conditions.
*/
enablement?: IActionEnablement;
/**
* Method that will be executed when the action is triggered.
* @param editor The editor instance is passed in as a convinience
@ -2819,6 +2783,15 @@ declare module monaco.editor {
run: (editor: ICommonCodeEditor) => Promise<void>;
}
export interface IEditorAction {
id: string;
label: string;
alias: string;
enabled: boolean;
isSupported(): boolean;
run(): Promise<void>;
}
/**
* An editor.
*/
@ -2888,11 +2861,11 @@ declare module monaco.editor {
/**
* Returns all actions associated with this editor.
*/
getActions(): IAction[];
getActions(): IEditorAction[];
/**
* Returns all actions associated with this editor.
*/
getSupportedActions(): IAction[];
getSupportedActions(): IEditorAction[];
/**
* Saves current view state of the editor in a serializable object.
*/
@ -3128,7 +3101,7 @@ declare module monaco.editor {
* @id Unique identifier of the contribution.
* @return The action or null if action not found.
*/
getAction(id: string): IAction;
getAction(id: string): IEditorAction;
/**
* Execute a command on the editor.
* @param source The source of the call.

View file

@ -16,14 +16,13 @@ import uri from 'vs/base/common/uri';
import errors = require('vs/base/common/errors');
import {IStatusbarItem} from 'vs/workbench/browser/parts/statusbar/statusbar';
import {Action} from 'vs/base/common/actions';
import {EditorAction} from 'vs/editor/common/editorAction';
import {language, LANGUAGE_DEFAULT} from 'vs/base/common/platform';
import {IMode} from 'vs/editor/common/modes';
import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput';
import {IFileEditorInput, EncodingMode, IEncodingSupport, asFileEditorInput, getUntitledOrFileResource} from 'vs/workbench/common/editor';
import {IDisposable, combinedDisposable, dispose} from 'vs/base/common/lifecycle';
import {IMessageService, Severity} from 'vs/platform/message/common/message';
import {ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon';
import {IEditorAction, ICommonCodeEditor, IModelContentChangedEvent, IModelOptionsChangedEvent, IModelModeChangedEvent, ICursorPositionChangedEvent} from 'vs/editor/common/editorCommon';
import {OpenGlobalSettingsAction} from 'vs/workbench/browser/actions/openSettings';
import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser';
import {TrimTrailingWhitespaceAction} from 'vs/editor/contrib/linesOperations/common/linesOperations';
@ -766,11 +765,11 @@ class ChangeIndentationAction extends Action {
control.getAction(IndentationToSpacesAction.ID),
control.getAction(IndentationToTabsAction.ID),
control.getAction(TrimTrailingWhitespaceAction.ID)
].map((a: EditorAction) => {
].map((a: IEditorAction) => {
return {
id: a.id,
label: a.label,
detail: (language === LANGUAGE_DEFAULT) ? null : a.getAlias(),
detail: (language === LANGUAGE_DEFAULT) ? null : a.alias,
run: () => a.run()
};
});

View file

@ -19,8 +19,8 @@ import {SyncActionDescriptor, ExecuteCommandAction, IMenuService} from 'vs/platf
import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry';
import {Registry} from 'vs/platform/platform';
import {QuickOpenHandler, QuickOpenAction} from 'vs/workbench/browser/quickopen';
import {IEditorAction} from 'vs/editor/common/editorCommon';
import {matchesWords, matchesPrefix, matchesContiguousSubString, or} from 'vs/base/common/filters';
import {EditorAction} from 'vs/editor/common/editorAction';
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation';
import {IMessageService, Severity, IMessageWithAction} from 'vs/platform/message/common/message';
@ -57,7 +57,7 @@ class BaseCommandEntry extends QuickOpenEntryGroup {
labelHighlights: IHighlight[],
aliasHighlights: IHighlight[],
@IMessageService protected messageService: IMessageService,
@ITelemetryService private telemetryService: ITelemetryService
@ITelemetryService protected telemetryService: ITelemetryService
) {
super();
@ -164,7 +164,7 @@ class CommandEntry extends BaseCommandEntry {
}
class EditorActionCommandEntry extends BaseCommandEntry {
private action: IAction;
private action: IEditorAction;
constructor(
keyLabel: string,
@ -173,7 +173,7 @@ class EditorActionCommandEntry extends BaseCommandEntry {
meta: string,
labelHighlights: IHighlight[],
aliasHighlights: IHighlight[],
action: IAction,
action: IEditorAction,
@IWorkbenchEditorService private editorService: IWorkbenchEditorService,
@IMessageService messageService: IMessageService,
@ITelemetryService telemetryService: ITelemetryService
@ -185,7 +185,19 @@ class EditorActionCommandEntry extends BaseCommandEntry {
public run(mode: Mode, context: IEntryRunContext): boolean {
if (mode === Mode.OPEN) {
this.runAction(this.action);
// Use a timeout to give the quick open widget a chance to close itself first
TPromise.timeout(50).done(() => {
if (this.action && this.action.enabled) {
try {
this.telemetryService.publicLog('workbenchActionExecuted', { id: this.action.id, from: 'quick open' });
(this.action.run() || TPromise.as(null)).done(null, (err) => this.onError(err));
} catch (error) {
this.onError(error);
}
} else {
this.messageService.show(Severity.Info, nls.localize('actionNotEnabled', "Command '{0}' is not enabled in the current context.", this.getLabel()));
}
}, (err) => this.onError(err));
return true;
}
@ -255,7 +267,7 @@ export class CommandsHandler extends QuickOpenHandler {
let activeEditor = this.editorService.getActiveEditor();
let activeEditorControl = <any>(activeEditor ? activeEditor.getControl() : null);
let editorActions: EditorAction[] = [];
let editorActions: IEditorAction[] = [];
if (activeEditorControl && types.isFunction(activeEditorControl.getSupportedActions)) {
editorActions = activeEditorControl.getSupportedActions();
}
@ -313,7 +325,7 @@ export class CommandsHandler extends QuickOpenHandler {
return entries;
}
private editorActionsToEntries(actions: EditorAction[], searchValue: string): EditorActionCommandEntry[] {
private editorActionsToEntries(actions: IEditorAction[], searchValue: string): EditorActionCommandEntry[] {
let entries: EditorActionCommandEntry[] = [];
for (let i = 0; i < actions.length; i++) {
@ -327,7 +339,7 @@ export class CommandsHandler extends QuickOpenHandler {
if (label) {
// Alias for non default languages
let alias = (language !== LANGUAGE_DEFAULT) ? action.getAlias() : null;
let alias = (language !== LANGUAGE_DEFAULT) ? action.alias : null;
let labelHighlights = wordFilter(searchValue, label);
let aliasHighlights = alias ? wordFilter(searchValue, alias) : null;
if (labelHighlights || aliasHighlights) {