/*--------------------------------------------------------------------------------------------- * 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 { Schemas } from 'vs/base/common/network'; import Severity from 'vs/base/common/severity'; import URI from 'vs/base/common/uri'; import { TPromise } from 'vs/base/common/winjs.base'; import { IConfigurationService, IConfigurationServiceEvent, IConfigurationValue, getConfigurationValue, IConfigurationKeys } from 'vs/platform/configuration/common/configuration'; import { IEditor, IEditorInput, IEditorOptions, IEditorService, IResourceInput, Position } from 'vs/platform/editor/common/editor'; import { ICommandService, ICommand, ICommandEvent, ICommandHandler, CommandsRegistry } from 'vs/platform/commands/common/commands'; import { AbstractKeybindingService } from 'vs/platform/keybinding/common/abstractKeybindingService'; import { USLayoutResolvedKeybinding } from 'vs/platform/keybinding/common/usLayoutResolvedKeybinding'; import { KeybindingResolver } from 'vs/platform/keybinding/common/keybindingResolver'; import { IKeybindingEvent, KeybindingSource, IKeyboardEvent } from 'vs/platform/keybinding/common/keybinding'; import { ContextKeyExpr, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; import { IConfirmation, IMessageService } from 'vs/platform/message/common/message'; import { IWorkspaceContextService, Workspace, IWorkspace } from 'vs/platform/workspace/common/workspace'; import * as editorCommon from 'vs/editor/common/editorCommon'; 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 { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IProgressService, IProgressRunner } from 'vs/platform/progress/common/progress'; import { ITextModelService, ITextModelContentProvider, ITextEditorModel } from 'vs/editor/common/services/resolverService'; import { IDisposable, IReference, ImmortalReference, combinedDisposable } from 'vs/base/common/lifecycle'; import * as dom from 'vs/base/browser/dom'; import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeybindingsRegistry, IKeybindingItem } from 'vs/platform/keybinding/common/keybindingsRegistry'; import { MenuId, IMenu, IMenuService } from 'vs/platform/actions/common/actions'; import { Menu } from 'vs/platform/actions/common/menu'; import { ITelemetryService, ITelemetryExperiments, ITelemetryInfo } from 'vs/platform/telemetry/common/telemetry'; import { ResolvedKeybinding, Keybinding, createKeybinding, SimpleKeybinding } from 'vs/base/common/keyCodes'; import { ResolvedKeybindingItem } from 'vs/platform/keybinding/common/resolvedKeybindingItem'; import { OS } from 'vs/base/common/platform'; import { IRange } from 'vs/editor/common/core/range'; export class SimpleEditor implements IEditor { public input: IEditorInput; public options: IEditorOptions; public position: Position; public _widget: editorCommon.IEditor; constructor(editor: editorCommon.IEditor) { this._widget = editor; } public getId(): string { return 'editor'; } public getControl(): editorCommon.IEditor { return this._widget; } public getSelection(): Selection { return this._widget.getSelection(); } public focus(): void { this._widget.focus(); } public isVisible(): boolean { return true; } public withTypedEditor(codeEditorCallback: (editor: ICodeEditor) => T, diffEditorCallback: (editor: IDiffEditor) => T): T { if (editorCommon.isCommonCodeEditor(this._widget)) { // Single Editor return codeEditorCallback(this._widget); } else { // Diff Editor return diffEditorCallback(this._widget); } } } export class SimpleModel implements ITextEditorModel { private model: editorCommon.IModel; private _onDispose: Emitter; constructor(model: editorCommon.IModel) { this.model = model; this._onDispose = new Emitter(); } public get onDispose(): Event { return this._onDispose.event; } public load(): TPromise { return TPromise.as(this); } public get textEditorModel(): editorCommon.IModel { return this.model; } public dispose(): void { this._onDispose.fire(); } } export interface IOpenEditorDelegate { (url: string): boolean; } export class SimpleEditorService implements IEditorService { public _serviceBrand: any; private editor: SimpleEditor; private openEditorDelegate: IOpenEditorDelegate; constructor() { this.openEditorDelegate = null; } public setEditor(editor: editorCommon.IEditor): void { this.editor = new SimpleEditor(editor); } public setOpenEditorDelegate(openEditorDelegate: IOpenEditorDelegate): void { this.openEditorDelegate = openEditorDelegate; } public openEditor(typedData: IResourceInput, sideBySide?: boolean): TPromise { return TPromise.as(this.editor.withTypedEditor( (editor) => this.doOpenEditor(editor, typedData), (diffEditor) => ( this.doOpenEditor(diffEditor.getOriginalEditor(), typedData) || this.doOpenEditor(diffEditor.getModifiedEditor(), typedData) ) )); } private doOpenEditor(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): IEditor { let model = this.findModel(editor, data); if (!model) { if (data.resource) { if (this.openEditorDelegate) { this.openEditorDelegate(data.resource.toString()); return null; } else { let schema = data.resource.scheme; if (schema === Schemas.http || schema === Schemas.https) { // This is a fully qualified http or https URL window.open(data.resource.toString()); return this.editor; } } } return null; } let selection = data.options.selection; if (selection) { if (typeof selection.endLineNumber === 'number' && typeof selection.endColumn === 'number') { editor.setSelection(selection); editor.revealRangeInCenter(selection); } else { let pos = { lineNumber: selection.startLineNumber, column: selection.startColumn }; editor.setPosition(pos); editor.revealPositionInCenter(pos); } } return this.editor; } private findModel(editor: editorCommon.ICommonCodeEditor, data: IResourceInput): editorCommon.IModel { let model = editor.getModel(); if (model.uri.toString() !== data.resource.toString()) { return null; } return model; } } export class SimpleEditorModelResolverService implements ITextModelService { public _serviceBrand: any; private editor: SimpleEditor; public setEditor(editor: editorCommon.IEditor): void { this.editor = new SimpleEditor(editor); } public createModelReference(resource: URI): TPromise> { let model: editorCommon.IModel; model = this.editor.withTypedEditor( (editor) => this.findModel(editor, resource), (diffEditor) => this.findModel(diffEditor.getOriginalEditor(), resource) || this.findModel(diffEditor.getModifiedEditor(), resource) ); if (!model) { return TPromise.as(new ImmortalReference(null)); } return TPromise.as(new ImmortalReference(new SimpleModel(model))); } public registerTextModelContentProvider(scheme: string, provider: ITextModelContentProvider): IDisposable { return { dispose: function () { /* no op */ } }; } private findModel(editor: editorCommon.ICommonCodeEditor, resource: URI): editorCommon.IModel { let model = editor.getModel(); if (model.uri.toString() !== resource.toString()) { return null; } return model; } } export class SimpleProgressService implements IProgressService { _serviceBrand: any; private static NULL_PROGRESS_RUNNER: IProgressRunner = { done: () => { }, total: () => { }, worked: () => { } }; show(infinite: boolean, delay?: number): IProgressRunner; show(total: number, delay?: number): IProgressRunner; show(): IProgressRunner { return SimpleProgressService.NULL_PROGRESS_RUNNER; } showWhile(promise: TPromise, delay?: number): TPromise { return null; } } export class SimpleMessageService implements IMessageService { public _serviceBrand: any; private static Empty = function () { /* nothing */ }; public show(sev: Severity, message: any): () => void { switch (sev) { case Severity.Error: console.error(message); break; case Severity.Warning: console.warn(message); break; default: console.log(message); break; } return SimpleMessageService.Empty; } public hideAll(): void { // No-op } public confirm(confirmation: IConfirmation): boolean { let messageText = confirmation.message; if (confirmation.detail) { messageText = messageText + '\n\n' + confirmation.detail; } return window.confirm(messageText); } } export class StandaloneCommandService implements ICommandService { _serviceBrand: any; private readonly _instantiationService: IInstantiationService; private _dynamicCommands: { [id: string]: ICommand; }; private _onWillExecuteCommand: Emitter = new Emitter(); public readonly onWillExecuteCommand: Event = this._onWillExecuteCommand.event; constructor(instantiationService: IInstantiationService) { this._instantiationService = instantiationService; this._dynamicCommands = Object.create(null); } public addCommand(id: string, command: ICommand): IDisposable { this._dynamicCommands[id] = command; return { dispose: () => { delete this._dynamicCommands[id]; } }; } public executeCommand(id: string, ...args: any[]): TPromise { const command = (CommandsRegistry.getCommand(id) || this._dynamicCommands[id]); if (!command) { return TPromise.wrapError(new Error(`command '${id}' not found`)); } try { this._onWillExecuteCommand.fire({ commandId: id }); const result = this._instantiationService.invokeFunction.apply(this._instantiationService, [command.handler].concat(args)); return TPromise.as(result); } catch (err) { return TPromise.wrapError(err); } } } export class StandaloneKeybindingService extends AbstractKeybindingService { private _cachedResolver: KeybindingResolver; private _dynamicKeybindings: IKeybindingItem[]; constructor( contextKeyService: IContextKeyService, commandService: ICommandService, messageService: IMessageService, domNode: HTMLElement ) { super(contextKeyService, commandService, messageService); this._cachedResolver = null; this._dynamicKeybindings = []; this.toDispose.push(dom.addDisposableListener(domNode, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { let keyEvent = new StandardKeyboardEvent(e); let shouldPreventDefault = this._dispatch(keyEvent, keyEvent.target); if (shouldPreventDefault) { keyEvent.preventDefault(); } })); } public addDynamicKeybinding(commandId: string, keybinding: number, handler: ICommandHandler, when: ContextKeyExpr): IDisposable { let toDispose: IDisposable[] = []; this._dynamicKeybindings.push({ keybinding: createKeybinding(keybinding, OS), command: commandId, when: when, weight1: 1000, weight2: 0 }); toDispose.push({ dispose: () => { for (let i = 0; i < this._dynamicKeybindings.length; i++) { let kb = this._dynamicKeybindings[i]; if (kb.command === commandId) { this._dynamicKeybindings.splice(i, 1); this.updateResolver({ source: KeybindingSource.Default }); return; } } } }); let commandService = this._commandService; if (commandService instanceof StandaloneCommandService) { toDispose.push(commandService.addCommand(commandId, { handler: handler })); } else { throw new Error('Unknown command service!'); } this.updateResolver({ source: KeybindingSource.Default }); return combinedDisposable(toDispose); } private updateResolver(event: IKeybindingEvent): void { this._cachedResolver = null; this._onDidUpdateKeybindings.fire(event); } protected _getResolver(): KeybindingResolver { if (!this._cachedResolver) { const defaults = this._toNormalizedKeybindingItems(KeybindingsRegistry.getDefaultKeybindings(), true); const overrides = this._toNormalizedKeybindingItems(this._dynamicKeybindings, false); this._cachedResolver = new KeybindingResolver(defaults, overrides); } return this._cachedResolver; } private _toNormalizedKeybindingItems(items: IKeybindingItem[], isDefault: boolean): ResolvedKeybindingItem[] { let result: ResolvedKeybindingItem[] = [], resultLen = 0; for (let i = 0, len = items.length; i < len; i++) { const item = items[i]; const when = (item.when ? item.when.normalize() : null); const keybinding = item.keybinding; if (!keybinding) { // This might be a removal keybinding item in user settings => accept it result[resultLen++] = new ResolvedKeybindingItem(null, item.command, item.commandArgs, when, isDefault); } else { const resolvedKeybindings = this.resolveKeybinding(keybinding); for (let j = 0; j < resolvedKeybindings.length; j++) { result[resultLen++] = new ResolvedKeybindingItem(resolvedKeybindings[j], item.command, item.commandArgs, when, isDefault); } } } return result; } public resolveKeybinding(keybinding: Keybinding): ResolvedKeybinding[] { return [new USLayoutResolvedKeybinding(keybinding, OS)]; } public resolveKeyboardEvent(keyboardEvent: IKeyboardEvent): ResolvedKeybinding { let keybinding = new SimpleKeybinding( keyboardEvent.ctrlKey, keyboardEvent.shiftKey, keyboardEvent.altKey, keyboardEvent.metaKey, keyboardEvent.keyCode ); return new USLayoutResolvedKeybinding(keybinding, OS); } public resolveUserBinding(userBinding: string): ResolvedKeybinding[] { return []; } } export class SimpleConfigurationService implements IConfigurationService { _serviceBrand: any; private _onDidUpdateConfiguration = new Emitter(); public onDidUpdateConfiguration: Event = this._onDidUpdateConfiguration.event; private _config: any; constructor() { this._config = getDefaultConfiguration(); } public getConfiguration(section?: any): T { return this._config; } public reloadConfiguration(section?: string): TPromise { return TPromise.as(this.getConfiguration(section)); } public lookup(key: string): IConfigurationValue { return { value: getConfigurationValue(this.getConfiguration(), key), default: getConfigurationValue(this.getConfiguration(), key), user: getConfigurationValue(this.getConfiguration(), key) }; } public keys(): IConfigurationKeys { return { default: [], user: [] }; } } export class SimpleMenuService implements IMenuService { _serviceBrand: any; private readonly _commandService: ICommandService; constructor(commandService: ICommandService) { this._commandService = commandService; } public createMenu(id: MenuId, contextKeyService: IContextKeyService): IMenu { return new Menu(id, TPromise.as(true), this._commandService, contextKeyService); } } export class StandaloneTelemetryService implements ITelemetryService { _serviceBrand: void; public isOptedIn = false; public publicLog(eventName: string, data?: any): TPromise { return TPromise.as(null); } public getTelemetryInfo(): TPromise { return null; } public getExperiments(): ITelemetryExperiments { return null; } } export class SimpleWorkspaceContextService implements IWorkspaceContextService { public _serviceBrand: any; private readonly _onDidChangeFolders: Emitter = new Emitter(); public readonly onDidChangeFolders: Event = this._onDidChangeFolders.event; private readonly folders: URI[]; constructor(private workspace?: Workspace) { this.folders = workspace ? [workspace.resource] : []; } public getFolders(): URI[] { return this.folders; } public getWorkspace(): IWorkspace { return this.workspace; } public hasWorkspace(): boolean { return !!this.workspace; } public isInsideWorkspace(resource: URI): boolean { return this.workspace ? this.workspace.isInsideWorkspace(resource) : false; } public toWorkspaceRelativePath(resource: URI, toOSPath?: boolean): string { return this.workspace ? this.workspace.toWorkspaceRelativePath(resource, toOSPath) : null; } public toResource(workspaceRelativePath: string): URI { return this.workspace ? this.workspace.toResource(workspaceRelativePath) : null; } }