From ce4d9f4e265bea47c8950e673580d03148747ebd Mon Sep 17 00:00:00 2001 From: Alex Dima Date: Tue, 6 Sep 2016 15:49:21 +0200 Subject: [PATCH] Fixes Microsoft/monaco-editor#167: introduce StandaloneCommandService --- .../browser/standalone/simpleServices.ts | 44 +++++++-- .../standalone/standaloneCodeEditor.ts | 8 +- .../browser/standalone/standaloneEditor.ts | 29 ++---- .../browser/standalone/standaloneServices.ts | 90 ++++++++++--------- .../browser/standalone/simpleServices.test.ts | 54 +++++++++++ .../commands/common/commandService.ts | 8 +- .../browser/keybindingServiceImpl.ts | 10 +-- 7 files changed, 154 insertions(+), 89 deletions(-) create mode 100644 src/vs/editor/test/browser/standalone/simpleServices.test.ts diff --git a/src/vs/editor/browser/standalone/simpleServices.ts b/src/vs/editor/browser/standalone/simpleServices.ts index a7cd30e3c4e..57ab0735e66 100644 --- a/src/vs/editor/browser/standalone/simpleServices.ts +++ b/src/vs/editor/browser/standalone/simpleServices.ts @@ -12,8 +12,8 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, getConfigurationValue} from 'vs/platform/configuration/common/configuration'; import {IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, ITextEditorModel, Position} from 'vs/platform/editor/common/editor'; import {AbstractExtensionService, ActivatedExtension} from 'vs/platform/extensions/common/abstractExtensionService'; -import {IExtensionDescription} from 'vs/platform/extensions/common/extensions'; -import {ICommandService, ICommandHandler} from 'vs/platform/commands/common/commands'; +import {IExtensionDescription, IExtensionService} from 'vs/platform/extensions/common/extensions'; +import {ICommandService, ICommand, ICommandHandler} from 'vs/platform/commands/common/commands'; import {KeybindingService} from 'vs/platform/keybinding/browser/keybindingServiceImpl'; import {IOSupport} from 'vs/platform/keybinding/common/keybindingResolver'; import {IKeybindingItem} from 'vs/platform/keybinding/common/keybinding'; @@ -24,6 +24,8 @@ import {ICodeEditor, IDiffEditor} from 'vs/editor/browser/editorBrowser'; import {Selection} from 'vs/editor/common/core/selection'; import Event, {Emitter} from 'vs/base/common/event'; import {getDefaultValues as getDefaultConfiguration} from 'vs/platform/configuration/common/model'; +import {CommandService} from 'vs/platform/commands/common/commandService'; +import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; export class SimpleEditor implements IEditor { @@ -198,11 +200,32 @@ export class SimpleMessageService implements IMessageService { } } +export class StandaloneCommandService extends CommandService { + + private _dynamicCommands: { [id: string]: ICommand; }; + + constructor( + instantiationService: IInstantiationService, + extensionService: IExtensionService + ) { + super(instantiationService, extensionService); + + this._dynamicCommands = Object.create(null); + } + + public addCommand(id:string, command:ICommand): void { + this._dynamicCommands[id] = command; + } + + protected _getCommand(id:string): ICommand { + return super._getCommand(id) || this._dynamicCommands[id]; + } +} + export class StandaloneKeybindingService extends KeybindingService { private static LAST_GENERATED_ID = 0; private _dynamicKeybindings: IKeybindingItem[]; - private _dynamicCommands: { [id: string]: ICommandHandler }; constructor( contextKeyService: IContextKeyService, @@ -213,7 +236,6 @@ export class StandaloneKeybindingService extends KeybindingService { super(contextKeyService, commandService, messageService); this._dynamicKeybindings = []; - this._dynamicCommands = Object.create(null); this._beginListening(domNode); } @@ -230,7 +252,15 @@ export class StandaloneKeybindingService extends KeybindingService { weight1: 1000, weight2: 0 }); - this._dynamicCommands[commandId] = handler; + + let commandService = this._commandService; + if (commandService instanceof StandaloneCommandService) { + commandService.addCommand(commandId, { + handler: handler + }); + } else { + throw new Error('Unknown command service!'); + } this.updateResolver(); return commandId; } @@ -238,10 +268,6 @@ export class StandaloneKeybindingService extends KeybindingService { protected _getExtraKeybindings(isFirstTime:boolean): IKeybindingItem[] { return this._dynamicKeybindings; } - - protected _getCommandHandler(commandId:string): ICommandHandler { - return super._getCommandHandler(commandId) || this._dynamicCommands[commandId]; - } } export class SimpleExtensionService extends AbstractExtensionService { diff --git a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts index 9c6ed0740a1..7b7df49ea99 100644 --- a/src/vs/editor/browser/standalone/standaloneCodeEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneCodeEditor.ts @@ -65,7 +65,7 @@ export class StandaloneEditor extends CodeEditor implements IStandaloneCodeEdito constructor( domElement:HTMLElement, options:IEditorConstructionOptions, - toDispose: IDisposable[], + toDispose: IDisposable, @IInstantiationService instantiationService: IInstantiationService, @ICodeEditorService codeEditorService: ICodeEditorService, @ICommandService commandService: ICommandService, @@ -81,7 +81,7 @@ export class StandaloneEditor extends CodeEditor implements IStandaloneCodeEdito } this._contextViewService = contextViewService; - this._toDispose2 = toDispose; + this._toDispose2 = [toDispose]; let model: IModel = null; if (typeof options.model === 'undefined') { @@ -169,7 +169,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon constructor( domElement:HTMLElement, options:IDiffEditorConstructionOptions, - toDispose: IDisposable[], + toDispose: IDisposable, @IInstantiationService instantiationService: IInstantiationService, @IContextKeyService contextKeyService: IContextKeyService, @IKeybindingService keybindingService: IKeybindingService, @@ -184,7 +184,7 @@ export class StandaloneDiffEditor extends DiffEditorWidget implements IStandalon this._contextViewService = contextViewService; - this._toDispose2 = toDispose; + this._toDispose2 = [toDispose]; this._contextViewService.setContainer(this._containerDomElement); } diff --git a/src/vs/editor/browser/standalone/standaloneEditor.ts b/src/vs/editor/browser/standalone/standaloneEditor.ts index a5334880b00..06c9447b36f 100644 --- a/src/vs/editor/browser/standalone/standaloneEditor.ts +++ b/src/vs/editor/browser/standalone/standaloneEditor.ts @@ -10,13 +10,10 @@ import {ContentWidgetPositionPreference, OverlayWidgetPositionPreference} from ' import {ShallowCancelThenPromise} from 'vs/base/common/async'; import {StandaloneEditor, IStandaloneCodeEditor, StandaloneDiffEditor, IStandaloneDiffEditor, startup, IEditorConstructionOptions, IDiffEditorConstructionOptions} from 'vs/editor/browser/standalone/standaloneCodeEditor'; import {ScrollbarVisibility} from 'vs/base/common/scrollable'; -import {IEditorOverrideServices, ensureDynamicPlatformServices, ensureStaticPlatformServices} from 'vs/editor/browser/standalone/standaloneServices'; +import {IEditorOverrideServices, DynamicStandaloneServices, ensureStaticPlatformServices} from 'vs/editor/browser/standalone/standaloneServices'; import {IDisposable} from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import {TPromise} from 'vs/base/common/winjs.base'; -import {createDecorator} from 'vs/platform/instantiation/common/instantiation'; -import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection'; -import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService'; import {OpenerService} from 'vs/platform/opener/browser/openerService'; import {IModel} from 'vs/editor/common/editorCommon'; import {IModelService} from 'vs/editor/common/services/modelService'; @@ -66,7 +63,7 @@ export function create(domElement:HTMLElement, options?:IEditorConstructionOptio } var t = prepareServices(domElement, services); - var result = t.ctx.instantiationService.createInstance(StandaloneEditor, domElement, options, t.toDispose); + var result = t.services.instantiationService.createInstance(StandaloneEditor, domElement, options, t); if (editorService) { editorService.setEditor(result); @@ -91,7 +88,7 @@ export function createDiffEditor(domElement:HTMLElement, options?:IDiffEditorCon } var t = prepareServices(domElement, services); - var result = t.ctx.instantiationService.createInstance(StandaloneDiffEditor, domElement, options, t.toDispose); + var result = t.services.instantiationService.createInstance(StandaloneDiffEditor, domElement, options, t); if (editorService) { editorService.setEditor(result); @@ -117,24 +114,8 @@ export function createDiffNavigator(diffEditor:IStandaloneDiffEditor, opts?:IDif return new DiffNavigator(diffEditor, opts); } -function prepareServices(domElement: HTMLElement, services: IEditorOverrideServices): { ctx: IEditorOverrideServices; toDispose: IDisposable[]; } { - services = ensureStaticPlatformServices(services); - var toDispose = ensureDynamicPlatformServices(domElement, services); - - var collection = new ServiceCollection(); - for (var legacyServiceId in services) { - if (services.hasOwnProperty(legacyServiceId)) { - let id = createDecorator(legacyServiceId); - let service = services[legacyServiceId]; - collection.set(id, service); - } - } - services.instantiationService = new InstantiationService(collection); - - return { - ctx: services, - toDispose: toDispose - }; +function prepareServices(domElement: HTMLElement, services: IEditorOverrideServices): DynamicStandaloneServices { + return new DynamicStandaloneServices(domElement, ensureStaticPlatformServices(services)); } function doCreateModel(value:string, mode:TPromise, uri?:URI): IModel { diff --git a/src/vs/editor/browser/standalone/standaloneServices.ts b/src/vs/editor/browser/standalone/standaloneServices.ts index 5408ac69bbd..9c1bb4e2c2b 100644 --- a/src/vs/editor/browser/standalone/standaloneServices.ts +++ b/src/vs/editor/browser/standalone/standaloneServices.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ 'use strict'; -import {IDisposable} from 'vs/base/common/lifecycle'; +import {Disposable} from 'vs/base/common/lifecycle'; import URI from 'vs/base/common/uri'; import {IConfigurationService} from 'vs/platform/configuration/common/configuration'; import {ContextMenuService} from 'vs/platform/contextview/browser/contextMenuService'; @@ -14,11 +14,10 @@ import {IEditorService} from 'vs/platform/editor/common/editor'; import {IEventService} from 'vs/platform/event/common/event'; import {EventService} from 'vs/platform/event/common/eventService'; import {IExtensionService} from 'vs/platform/extensions/common/extensions'; -import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; +import {createDecorator, IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService'; import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection'; import {ICommandService} from 'vs/platform/commands/common/commands'; -import {CommandService} from 'vs/platform/commands/common/commandService'; import {IOpenerService} from 'vs/platform/opener/common/opener'; import {IKeybindingService} from 'vs/platform/keybinding/common/keybinding'; import {IContextKeyService} from 'vs/platform/contextkey/common/contextkey'; @@ -37,7 +36,7 @@ import {MainThreadModeServiceImpl} from 'vs/editor/common/services/modeServiceIm import {IModelService} from 'vs/editor/common/services/modelService'; import {ModelServiceImpl} from 'vs/editor/common/services/modelServiceImpl'; import {CodeEditorServiceImpl} from 'vs/editor/browser/services/codeEditorServiceImpl'; -import {SimpleConfigurationService, SimpleMessageService, SimpleExtensionService, StandaloneKeybindingService} from 'vs/editor/browser/standalone/simpleServices'; +import {SimpleConfigurationService, SimpleMessageService, SimpleExtensionService, StandaloneKeybindingService, StandaloneCommandService} from 'vs/editor/browser/standalone/simpleServices'; import {ContextKeyService} from 'vs/platform/contextkey/browser/contextKeyService'; import {IMenuService} from 'vs/platform/actions/common/actions'; import {MenuService} from 'vs/platform/actions/common/menuService'; @@ -150,7 +149,6 @@ export interface IStaticServices { modeService: IModeService; extensionService: IExtensionService; markerService: IMarkerService; - menuService: IMenuService; contextService: IWorkspaceContextService; messageService: IMessageService; telemetryService: ITelemetryService; @@ -159,7 +157,6 @@ export interface IStaticServices { editorWorkerService: IEditorWorkerService; eventService: IEventService; storageService: IStorageService; - commandService: ICommandService; instantiationService: IInstantiationService; } @@ -191,39 +188,54 @@ export function ensureStaticPlatformServices(services: IEditorOverrideServices): return services; } -export function ensureDynamicPlatformServices(domElement:HTMLElement, services: IEditorOverrideServices): IDisposable[] { - let r:IDisposable[] = []; +export class DynamicStandaloneServices extends Disposable { - let contextKeyService:IContextKeyService; - if (typeof services.contextKeyService === 'undefined') { - contextKeyService = new ContextKeyService(services.configurationService); - r.push(contextKeyService); - services.contextKeyService = contextKeyService; - } else { - contextKeyService = services.contextKeyService; - } - if (typeof services.keybindingService === 'undefined') { - let keybindingService = new StandaloneKeybindingService(contextKeyService, services.commandService, services.messageService, domElement); - r.push(keybindingService); - services.keybindingService = keybindingService; - } + public services: IEditorOverrideServices; - let contextViewService:IEditorContextViewService; - if (typeof services.contextViewService === 'undefined') { - contextViewService = new ContextViewService(domElement, services.telemetryService, services.messageService); - r.push(contextViewService); - services.contextViewService = contextViewService; - } else { - contextViewService = services.contextViewService; - } + constructor(domElement:HTMLElement, _services: IEditorOverrideServices) { + super(); - if (typeof services.contextMenuService === 'undefined') { - let contextMenuService = new ContextMenuService(domElement, services.telemetryService, services.messageService, contextViewService); - r.push(contextMenuService); - services.contextMenuService = contextMenuService; - } + let services: IEditorOverrideServices = {}; + for (var serviceId in _services) { + services[serviceId] = _services[serviceId]; + } - return r; + const serviceCollection = new ServiceCollection(); + services.instantiationService = new InstantiationService(serviceCollection); + + if (typeof services.contextKeyService === 'undefined') { + services.contextKeyService = this._register(new ContextKeyService(services.configurationService)); + } + + if (typeof services.commandService === 'undefined') { + services.commandService = new StandaloneCommandService(services.instantiationService, services.extensionService); + } + + if (typeof services.keybindingService === 'undefined') { + services.keybindingService = this._register(new StandaloneKeybindingService(services.contextKeyService, services.commandService, services.messageService, domElement)); + } + + if (typeof services.contextViewService === 'undefined') { + services.contextViewService = this._register(new ContextViewService(domElement, services.telemetryService, services.messageService)); + } + + if (typeof services.contextMenuService === 'undefined') { + services.contextMenuService = this._register(new ContextMenuService(domElement, services.telemetryService, services.messageService, services.contextViewService)); + } + + if (typeof services.menuService === 'undefined') { + services.menuService = new MenuService(services.extensionService, services.commandService); + } + + for (let serviceId in services) { + if (services.hasOwnProperty(serviceId)) { + let service = services[serviceId]; + serviceCollection.set(createDecorator(serviceId), service); + } + } + + this.services = services; + } } // The static services represents a map of services that once 1 editor has been created must be used for all subsequent editors @@ -257,9 +269,6 @@ export function getOrCreateStaticServices(services?: IEditorOverrideServices): I let extensionService = services.extensionService || new SimpleExtensionService(); serviceCollection.set(IExtensionService, extensionService); - let commandService = services.commandService || new CommandService(instantiationService, extensionService); - serviceCollection.set(ICommandService, commandService); - let markerService = services.markerService || new MarkerService(); serviceCollection.set(IMarkerService, markerService); @@ -278,17 +287,12 @@ export function getOrCreateStaticServices(services?: IEditorOverrideServices): I let codeEditorService = services.codeEditorService || new CodeEditorServiceImpl(); serviceCollection.set(ICodeEditorService, codeEditorService); - let menuService = services.menuService || new MenuService(extensionService, commandService); - serviceCollection.set(IMenuService, menuService); - staticServices = { configurationService: configurationService, extensionService: extensionService, - commandService: commandService, compatWorkerService: compatWorkerService, modeService: modeService, markerService: markerService, - menuService: menuService, contextService: contextService, telemetryService: telemetryService, messageService: messageService, diff --git a/src/vs/editor/test/browser/standalone/simpleServices.test.ts b/src/vs/editor/test/browser/standalone/simpleServices.test.ts new file mode 100644 index 00000000000..27303a890e2 --- /dev/null +++ b/src/vs/editor/test/browser/standalone/simpleServices.test.ts @@ -0,0 +1,54 @@ +/*--------------------------------------------------------------------------------------------- + * 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 * as assert from 'assert'; +import {ContextKeyService} from 'vs/platform/contextkey/browser/contextKeyService'; +import {SimpleConfigurationService, SimpleMessageService, SimpleExtensionService, StandaloneKeybindingService, StandaloneCommandService} from 'vs/editor/browser/standalone/simpleServices'; +import {InstantiationService} from 'vs/platform/instantiation/common/instantiationService'; +import {ServiceCollection} from 'vs/platform/instantiation/common/serviceCollection'; +import {KeyCode} from 'vs/base/common/keyCodes'; +import {IKeyboardEvent} from 'vs/base/browser/keyboardEvent'; + +suite('StandaloneKeybindingService', () => { + + class TestStandaloneKeybindingService extends StandaloneKeybindingService { + public dispatch(e: IKeyboardEvent): void { + super._dispatch(e); + } + } + + test('issue Microsoft/monaco-editor#167', () => { + + let serviceCollection = new ServiceCollection(); + const instantiationService = new InstantiationService(serviceCollection, true); + + let configurationService = new SimpleConfigurationService(); + + let contextKeyService = new ContextKeyService(configurationService); + + let extensionService = new SimpleExtensionService(); + + let commandService = new StandaloneCommandService(instantiationService, extensionService); + + let messageService = new SimpleMessageService(); + + let domElement = document.createElement('div'); + + let keybindingService = new TestStandaloneKeybindingService(contextKeyService, commandService, messageService, domElement); + + let commandInvoked = false; + keybindingService.addDynamicKeybinding(KeyCode.F9, () => { + commandInvoked = true; + }, null); + + keybindingService.dispatch({ + asKeybinding: () => KeyCode.F9, + preventDefault: () => {} + }); + + assert.ok(commandInvoked, 'command invoked'); + }); +}); diff --git a/src/vs/platform/commands/common/commandService.ts b/src/vs/platform/commands/common/commandService.ts index b6f0f43df9a..c71310be8b6 100644 --- a/src/vs/platform/commands/common/commandService.ts +++ b/src/vs/platform/commands/common/commandService.ts @@ -6,7 +6,7 @@ import {TPromise} from 'vs/base/common/winjs.base'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; -import {ICommandService, CommandsRegistry} from 'vs/platform/commands/common/commands'; +import {ICommandService, ICommand, CommandsRegistry} from 'vs/platform/commands/common/commands'; import {IExtensionService} from 'vs/platform/extensions/common/extensions'; export class CommandService implements ICommandService { @@ -24,7 +24,7 @@ export class CommandService implements ICommandService { return this._extensionService.activateByEvent(`onCommand:${id}`).then(_ => { - const command = CommandsRegistry.getCommand(id); + const command = this._getCommand(id); if (!command) { return TPromise.wrapError(new Error(`command '${id}' not found`)); } @@ -37,4 +37,8 @@ export class CommandService implements ICommandService { } }); } + + protected _getCommand(id:string): ICommand { + return CommandsRegistry.getCommand(id); + } } diff --git a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts index 1923b3da74f..f79d4b4220c 100644 --- a/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts +++ b/src/vs/platform/keybinding/browser/keybindingServiceImpl.ts @@ -13,7 +13,7 @@ import Severity from 'vs/base/common/severity'; import {isFalsyOrEmpty} from 'vs/base/common/arrays'; import * as dom from 'vs/base/browser/dom'; import {IKeyboardEvent, StandardKeyboardEvent} from 'vs/base/browser/keyboardEvent'; -import {ICommandService, CommandsRegistry, ICommandHandler, ICommandHandlerDescription} from 'vs/platform/commands/common/commands'; +import {ICommandService, CommandsRegistry, ICommandHandlerDescription} from 'vs/platform/commands/common/commands'; import {KeybindingResolver} from 'vs/platform/keybinding/common/keybindingResolver'; import {IKeybindingItem, IKeybindingService} from 'vs/platform/keybinding/common/keybinding'; import {IContextKeyService} from 'vs/platform/contextkey/common/contextkey'; @@ -32,7 +32,7 @@ export abstract class KeybindingService implements IKeybindingService { private _currentChordStatusMessage: IDisposable; private _contextKeyService: IContextKeyService; - private _commandService: ICommandService; + protected _commandService: ICommandService; private _statusService: IStatusbarService; private _messageService: IMessageService; @@ -132,11 +132,7 @@ export abstract class KeybindingService implements IKeybindingService { return '// ' + nls.localize('unboundCommands', "Here are other available commands: ") + '\n// - ' + pretty; } - protected _getCommandHandler(commandId: string): ICommandHandler { - return CommandsRegistry.getCommand(commandId).handler; - } - - private _dispatch(e: IKeyboardEvent): void { + protected _dispatch(e: IKeyboardEvent): void { let isModifierKey = (e.keyCode === KeyCode.Ctrl || e.keyCode === KeyCode.Shift || e.keyCode === KeyCode.Alt || e.keyCode === KeyCode.Meta); if (isModifierKey) { return; -- GitLab