From 8f1b989c36b5448e12c9f68378fc44de6e5802b1 Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Fri, 13 Oct 2017 17:43:00 +0200 Subject: [PATCH] move editor service into common --- .../browser/parts/editor/editorPart.ts | 2 +- .../browser/parts/editor/tabsTitleControl.ts | 3 +- .../browser/parts/editor/textDiffEditor.ts | 3 +- .../workbench/electron-browser/workbench.ts | 3 +- .../parts/files/browser/explorerViewlet.ts | 3 +- .../services/editor/browser/editorService.ts | 365 ------------------ .../services/editor/common/editorService.ts | 359 ++++++++++++++++- .../editor/test/browser/editorService.test.ts | 2 +- 8 files changed, 363 insertions(+), 377 deletions(-) delete mode 100644 src/vs/workbench/services/editor/browser/editorService.ts diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 25fcf62c102..15ce8ce3eb0 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -26,7 +26,7 @@ import { EditorGroupsControl, Rochade, IEditorGroupsControl, ProgressState } fro import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { IEditorGroupService, GroupOrientation, GroupArrangement, IEditorTabOptions, IMoveOptions } from 'vs/workbench/services/group/common/groupService'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; -import { IEditorPart } from 'vs/workbench/services/editor/browser/editorService'; +import { IEditorPart } from 'vs/workbench/services/editor/common/editorService'; import { IPartService } from 'vs/workbench/services/part/common/partService'; import { Position, POSITIONS, Direction, IEditor } from 'vs/platform/editor/common/editor'; import { IStorageService } from 'vs/platform/storage/common/storage'; diff --git a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts index 3a12c2226cf..e598d3383e3 100644 --- a/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/tabsTitleControl.ts @@ -20,7 +20,7 @@ import { StandardKeyboardEvent } from 'vs/base/browser/keyboardEvent'; import { KeyCode } from 'vs/base/common/keyCodes'; import { ResourceLabel } from 'vs/workbench/browser/labels'; import { ActionBar } from 'vs/base/browser/ui/actionbar/actionbar'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IContextMenuService } from 'vs/platform/contextview/browser/contextView'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IMessageService } from 'vs/platform/message/common/message'; @@ -37,7 +37,6 @@ import { ScrollableElement } from 'vs/base/browser/ui/scrollbar/scrollableElemen import { ScrollbarVisibility } from 'vs/base/common/scrollable'; import { extractResources } from 'vs/base/browser/dnd'; import { getOrSet } from 'vs/base/common/map'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IThemeService, registerThemingParticipant, ITheme, ICssStyleCollector } from 'vs/platform/theme/common/themeService'; import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_ACTIVE_FOREGROUND, TAB_INACTIVE_FOREGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND, TAB_UNFOCUSED_INACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER } from 'vs/workbench/common/theme'; diff --git a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts index 7143b5a9fa2..641e62414b7 100644 --- a/src/vs/workbench/browser/parts/editor/textDiffEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textDiffEditor.ts @@ -22,14 +22,13 @@ import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { DiffNavigator } from 'vs/editor/browser/widget/diffNavigator'; import { DiffEditorWidget } from 'vs/editor/browser/widget/diffEditorWidget'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { FileOperationError, FileOperationResult } from 'vs/platform/files/common/files'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IStorageService } from 'vs/platform/storage/common/storage'; import { ITextResourceConfigurationService } from 'vs/editor/common/services/resourceConfiguration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/platform/theme/common/themeService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IModeService } from 'vs/editor/common/services/modeService'; diff --git a/src/vs/workbench/electron-browser/workbench.ts b/src/vs/workbench/electron-browser/workbench.ts index f6bdc8d057f..c88d5d05d54 100644 --- a/src/vs/workbench/electron-browser/workbench.ts +++ b/src/vs/workbench/electron-browser/workbench.ts @@ -39,7 +39,6 @@ import { IActionBarRegistry, Extensions as ActionBarExtensions } from 'vs/workbe import { PanelRegistry, Extensions as PanelExtensions } from 'vs/workbench/browser/panel'; import { QuickOpenController } from 'vs/workbench/browser/parts/quickopen/quickOpenController'; import { getServices } from 'vs/platform/instantiation/common/extensions'; -import { WorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { Position, Parts, IPartService, ILayoutOptions } from 'vs/workbench/services/part/common/partService'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; @@ -66,7 +65,7 @@ import { ConfigurationResolverService } from 'vs/workbench/services/configuratio import { IPanelService } from 'vs/workbench/services/panel/common/panelService'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { WorkbenchMessageService } from 'vs/workbench/services/message/browser/messageService'; -import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, IResourceInputType, WorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IQuickOpenService } from 'vs/platform/quickOpen/common/quickOpen'; import { IClipboardService } from 'vs/platform/clipboard/common/clipboardService'; import { ClipboardService } from 'vs/platform/clipboard/electron-browser/clipboardService'; diff --git a/src/vs/workbench/parts/files/browser/explorerViewlet.ts b/src/vs/workbench/parts/files/browser/explorerViewlet.ts index 44cac3254e1..74da9b16cd4 100644 --- a/src/vs/workbench/parts/files/browser/explorerViewlet.ts +++ b/src/vs/workbench/parts/files/browser/explorerViewlet.ts @@ -24,11 +24,10 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IExtensionService } from 'vs/platform/extensions/common/extensions'; import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/browser/editorService'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { EditorInput, EditorOptions } from 'vs/workbench/common/editor'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; +import { IWorkbenchEditorService, DelegatingWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEditorGroupService } from 'vs/workbench/services/group/common/groupService'; import { IContextKeyService, IContextKey } from 'vs/platform/contextkey/common/contextkey'; import { IThemeService } from 'vs/platform/theme/common/themeService'; diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts deleted file mode 100644 index 2cabe8935ca..00000000000 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ /dev/null @@ -1,365 +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 { TPromise } from 'vs/base/common/winjs.base'; -import URI from 'vs/base/common/uri'; -import network = require('vs/base/common/network'); -import { Registry } from 'vs/platform/registry/common/platform'; -import { basename, dirname } from 'vs/base/common/paths'; -import { EditorInput, EditorOptions, TextEditorOptions, Extensions as EditorExtensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; -import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; -import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; -import { IWorkbenchEditorService, IResourceInputType } from 'vs/workbench/services/editor/common/editorService'; -import { IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; -import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; -import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; -import nls = require('vs/nls'); -import { getPathLabel } from 'vs/base/common/labels'; -import { ResourceMap } from 'vs/base/common/map'; -import { once } from 'vs/base/common/event'; -import { IEnvironmentService } from 'vs/platform/environment/common/environment'; -import { IFileService } from 'vs/platform/files/common/files'; - -export interface IEditorPart { - openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; - openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; - openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; - replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; - closeEditor(position: Position, input: IEditorInput): TPromise; - closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; - closeAllEditors(except?: Position): TPromise; - getActiveEditor(): IEditor; - getVisibleEditors(): IEditor[]; - getActiveEditorInput(): IEditorInput; -} - -type ICachedEditorInput = ResourceEditorInput | IFileEditorInput; - -export class WorkbenchEditorService implements IWorkbenchEditorService { - - public _serviceBrand: any; - - private static CACHE: ResourceMap = new ResourceMap(); - - private editorPart: IEditorPart | IWorkbenchEditorService; - private fileInputFactory: IFileInputFactory; - - constructor( - editorPart: IEditorPart | IWorkbenchEditorService, - @IUntitledEditorService private untitledEditorService: IUntitledEditorService, - @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, - @IInstantiationService private instantiationService: IInstantiationService, - @IEnvironmentService private environmentService: IEnvironmentService, - @IFileService private fileService: IFileService - ) { - this.editorPart = editorPart; - this.fileInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileInputFactory(); - } - - public getActiveEditor(): IEditor { - return this.editorPart.getActiveEditor(); - } - - public getActiveEditorInput(): IEditorInput { - return this.editorPart.getActiveEditorInput(); - } - - public getVisibleEditors(): IEditor[] { - return this.editorPart.getVisibleEditors(); - } - - public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { - if (!input) { - return false; - } - - return this.getVisibleEditors().some(editor => { - if (!editor.input) { - return false; - } - - if (input.matches(editor.input)) { - return true; - } - - if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { - const sideBySideInput = editor.input; - return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); - } - - return false; - }); - } - - public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; - public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; - public openEditor(input: IResourceInputType, position?: Position): TPromise; - public openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; - public openEditor(input: any, arg2?: any, arg3?: any): TPromise { - if (!input) { - return TPromise.as(null); - } - - // Workbench Input Support - if (input instanceof EditorInput) { - return this.doOpenEditor(input, this.toOptions(arg2), arg3); - } - - // Support opening foreign resources (such as a http link that points outside of the workbench) - const resourceInput = input; - if (resourceInput.resource instanceof URI) { - const schema = resourceInput.resource.scheme; - if (schema === network.Schemas.http || schema === network.Schemas.https) { - window.open(resourceInput.resource.toString(true)); - - return TPromise.as(null); - } - } - - // Untyped Text Editor Support (required for code that uses this service below workbench level) - const textInput = input; - const typedInput = this.createInput(textInput); - if (typedInput) { - return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2); - } - - return TPromise.as(null); - } - - private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions { - if (!options || options instanceof EditorOptions) { - return options as EditorOptions; - } - - const textOptions: ITextEditorOptions = options; - if (!!textOptions.selection) { - return TextEditorOptions.create(options); - } - - return EditorOptions.create(options); - } - - /** - * Allow subclasses to implement their own behavior for opening editor (see below). - */ - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { - return this.editorPart.openEditor(input, options, arg3); - } - - public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; - public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; - public openEditors(editors: any[]): TPromise { - const inputs = editors.map(editor => this.createInput(editor.input)); - const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { - const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input); - - return { - input, - options, - position: editors[index].position - }; - }); - - return this.editorPart.openEditors(typedInputs); - } - - public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; - public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; - public replaceEditors(editors: any[], position?: Position): TPromise { - const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace)); - const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith)); - const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { - const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith); - - return { - toReplace: toReplaceInputs[index], - replaceWith: replaceWithInputs[index], - options - }; - }); - - return this.editorPart.replaceEditors(typedReplacements, position); - } - - public closeEditor(position: Position, input: IEditorInput): TPromise { - return this.doCloseEditor(position, input); - } - - protected doCloseEditor(position: Position, input: IEditorInput): TPromise { - return this.editorPart.closeEditor(position, input); - } - - public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { - return this.editorPart.closeEditors(position, filter); - } - - public closeAllEditors(except?: Position): TPromise { - return this.editorPart.closeAllEditors(except); - } - - public createInput(input: IEditorInput): EditorInput; - public createInput(input: IResourceInputType): EditorInput; - public createInput(input: any): IEditorInput { - - // Workbench Input Support - if (input instanceof EditorInput) { - return input; - } - - // Side by Side Support - const resourceSideBySideInput = input; - if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { - const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource }); - const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource }); - - return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput); - } - - // Diff Editor Support - const resourceDiffInput = input; - if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { - const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }); - const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }); - const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService); - - return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); - } - - // Untitled file support - const untitledInput = input; - if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UNTITLED_SCHEMA)) { - return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); - } - - const resourceInput = input; - - // Files support - if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { - return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding); - } - - // Any other resource - else if (resourceInput.resource instanceof URI) { - const label = resourceInput.label || basename(resourceInput.resource.fsPath); - let description: string; - if (typeof resourceInput.description === 'string') { - description = resourceInput.description; - } else if (resourceInput.resource.scheme === network.Schemas.file) { - description = dirname(resourceInput.resource.fsPath); - } - - return this.createOrGet(resourceInput.resource, this.instantiationService, label, description); - } - - return null; - } - - private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput { - if (WorkbenchEditorService.CACHE.has(resource)) { - const input = WorkbenchEditorService.CACHE.get(resource); - if (input instanceof ResourceEditorInput) { - input.setName(label); - input.setDescription(description); - } else { - input.setPreferredEncoding(encoding); - } - - return input; - } - - let input: ICachedEditorInput; - if (resource.scheme === network.Schemas.file || this.fileService.canHandleResource && this.fileService.canHandleResource(resource)) { - input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); - } else { - input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); - } - - WorkbenchEditorService.CACHE.set(resource, input); - once(input.onDispose)(() => { - WorkbenchEditorService.CACHE.delete(resource); - }); - - return input; - } - - private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceContextService, environment: IEnvironmentService): string { - const leftName = getPathLabel(res1.fsPath, context, environment); - const rightName = getPathLabel(res2.fsPath, context, environment); - - return nls.localize('compareLabels', "{0} ↔ {1}", leftName, rightName); - } -} - -export interface IEditorOpenHandler { - (input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - (input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; -} - -export interface IEditorCloseHandler { - (position: Position, input: IEditorInput): TPromise; -} - -/** - * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior - * of openEditor() and closeEditor() by providing a handler. - * - * This gives clients a chance to override the behavior of openEditor() and closeEditor(). - */ -export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { - private editorOpenHandler: IEditorOpenHandler; - private editorCloseHandler: IEditorCloseHandler; - - constructor( - @IUntitledEditorService untitledEditorService: IUntitledEditorService, - @IInstantiationService instantiationService: IInstantiationService, - @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, - @IWorkbenchEditorService editorService: IWorkbenchEditorService, - @IEnvironmentService environmentService: IEnvironmentService, - @IFileService fileService: IFileService - ) { - super( - editorService, - untitledEditorService, - workspaceContextService, - instantiationService, - environmentService, - fileService - ); - } - - public setEditorOpenHandler(handler: IEditorOpenHandler): void { - this.editorOpenHandler = handler; - } - - public setEditorCloseHandler(handler: IEditorCloseHandler): void { - this.editorCloseHandler = handler; - } - - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; - protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { - const handleOpen = this.editorOpenHandler ? this.editorOpenHandler(input, options, arg3) : TPromise.as(void 0); - - return handleOpen.then(editor => { - if (editor) { - return TPromise.as(editor); - } - - return super.doOpenEditor(input, options, arg3); - }); - } - - protected doCloseEditor(position: Position, input: IEditorInput): TPromise { - const handleClose = this.editorCloseHandler ? this.editorCloseHandler(position, input) : TPromise.as(void 0); - - return handleClose.then(() => { - return super.doCloseEditor(position, input); - }); - } -} diff --git a/src/vs/workbench/services/editor/common/editorService.ts b/src/vs/workbench/services/editor/common/editorService.ts index 05f2ae36cc2..8100a8cd551 100644 --- a/src/vs/workbench/services/editor/common/editorService.ts +++ b/src/vs/workbench/services/editor/common/editorService.ts @@ -6,8 +6,23 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import { createDecorator, ServiceIdentifier } from 'vs/platform/instantiation/common/instantiation'; +import { createDecorator, ServiceIdentifier, IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { IEditorService, IEditor, IEditorInput, IEditorOptions, ITextEditorOptions, Position, Direction, IResourceInput, IResourceDiffInput, IResourceSideBySideInput, IUntitledResourceInput } from 'vs/platform/editor/common/editor'; +import URI from 'vs/base/common/uri'; +import network = require('vs/base/common/network'); +import { Registry } from 'vs/platform/registry/common/platform'; +import { basename, dirname } from 'vs/base/common/paths'; +import { EditorInput, EditorOptions, TextEditorOptions, Extensions as EditorExtensions, SideBySideEditorInput, IFileEditorInput, IFileInputFactory, IEditorInputFactoryRegistry } from 'vs/workbench/common/editor'; +import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; +import { IUntitledEditorService, UNTITLED_SCHEMA } from 'vs/workbench/services/untitled/common/untitledEditorService'; +import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; +import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; +import nls = require('vs/nls'); +import { getPathLabel } from 'vs/base/common/labels'; +import { ResourceMap } from 'vs/base/common/map'; +import { once } from 'vs/base/common/event'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { IFileService } from 'vs/platform/files/common/files'; export const IWorkbenchEditorService = createDecorator('editorService'); @@ -91,4 +106,344 @@ export interface IWorkbenchEditorService extends IEditorService { * Allows to resolve an untyped input to a workbench typed instanceof editor input */ createInput(input: IResourceInputType): IEditorInput; -} \ No newline at end of file +} + +export interface IEditorPart { + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, sideBySide?: boolean): TPromise; + openEditor(input?: IEditorInput, options?: IEditorOptions | ITextEditorOptions, position?: Position): TPromise; + openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions | ITextEditorOptions }[]): TPromise; + replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions | ITextEditorOptions }[], position?: Position): TPromise; + closeEditor(position: Position, input: IEditorInput): TPromise; + closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise; + closeAllEditors(except?: Position): TPromise; + getActiveEditor(): IEditor; + getVisibleEditors(): IEditor[]; + getActiveEditorInput(): IEditorInput; +} + +type ICachedEditorInput = ResourceEditorInput | IFileEditorInput; + +export class WorkbenchEditorService implements IWorkbenchEditorService { + + public _serviceBrand: any; + + private static CACHE: ResourceMap = new ResourceMap(); + + private editorPart: IEditorPart | IWorkbenchEditorService; + private fileInputFactory: IFileInputFactory; + + constructor( + editorPart: IEditorPart | IWorkbenchEditorService, + @IUntitledEditorService private untitledEditorService: IUntitledEditorService, + @IWorkspaceContextService private workspaceContextService: IWorkspaceContextService, + @IInstantiationService private instantiationService: IInstantiationService, + @IEnvironmentService private environmentService: IEnvironmentService, + @IFileService private fileService: IFileService + ) { + this.editorPart = editorPart; + this.fileInputFactory = Registry.as(EditorExtensions.EditorInputFactories).getFileInputFactory(); + } + + public getActiveEditor(): IEditor { + return this.editorPart.getActiveEditor(); + } + + public getActiveEditorInput(): IEditorInput { + return this.editorPart.getActiveEditorInput(); + } + + public getVisibleEditors(): IEditor[] { + return this.editorPart.getVisibleEditors(); + } + + public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { + if (!input) { + return false; + } + + return this.getVisibleEditors().some(editor => { + if (!editor.input) { + return false; + } + + if (input.matches(editor.input)) { + return true; + } + + if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { + const sideBySideInput = editor.input; + return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); + } + + return false; + }); + } + + public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; + public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; + public openEditor(input: IResourceInputType, position?: Position): TPromise; + public openEditor(input: IResourceInputType, sideBySide?: boolean): TPromise; + public openEditor(input: any, arg2?: any, arg3?: any): TPromise { + if (!input) { + return TPromise.as(null); + } + + // Workbench Input Support + if (input instanceof EditorInput) { + return this.doOpenEditor(input, this.toOptions(arg2), arg3); + } + + // Support opening foreign resources (such as a http link that points outside of the workbench) + const resourceInput = input; + if (resourceInput.resource instanceof URI) { + const schema = resourceInput.resource.scheme; + if (schema === network.Schemas.http || schema === network.Schemas.https) { + window.open(resourceInput.resource.toString(true)); + + return TPromise.as(null); + } + } + + // Untyped Text Editor Support (required for code that uses this service below workbench level) + const textInput = input; + const typedInput = this.createInput(textInput); + if (typedInput) { + return this.doOpenEditor(typedInput, TextEditorOptions.from(textInput), arg2); + } + + return TPromise.as(null); + } + + private toOptions(options?: IEditorOptions | EditorOptions): EditorOptions { + if (!options || options instanceof EditorOptions) { + return options as EditorOptions; + } + + const textOptions: ITextEditorOptions = options; + if (!!textOptions.selection) { + return TextEditorOptions.create(options); + } + + return EditorOptions.create(options); + } + + /** + * Allow subclasses to implement their own behavior for opening editor (see below). + */ + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + return this.editorPart.openEditor(input, options, arg3); + } + + public openEditors(editors: { input: IResourceInputType, position: Position }[]): TPromise; + public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; + public openEditors(editors: any[]): TPromise { + const inputs = editors.map(editor => this.createInput(editor.input)); + const typedInputs: { input: IEditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { + const options = editors[index].input instanceof EditorInput ? this.toOptions(editors[index].options) : TextEditorOptions.from(editors[index].input); + + return { + input, + options, + position: editors[index].position + }; + }); + + return this.editorPart.openEditors(typedInputs); + } + + public replaceEditors(editors: { toReplace: IResourceInputType, replaceWith: IResourceInputType }[], position?: Position): TPromise; + public replaceEditors(editors: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: IEditorOptions }[], position?: Position): TPromise; + public replaceEditors(editors: any[], position?: Position): TPromise { + const toReplaceInputs = editors.map(editor => this.createInput(editor.toReplace)); + const replaceWithInputs = editors.map(editor => this.createInput(editor.replaceWith)); + const typedReplacements: { toReplace: IEditorInput, replaceWith: IEditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { + const options = editor.toReplace instanceof EditorInput ? this.toOptions(editor.options) : TextEditorOptions.from(editor.replaceWith); + + return { + toReplace: toReplaceInputs[index], + replaceWith: replaceWithInputs[index], + options + }; + }); + + return this.editorPart.replaceEditors(typedReplacements, position); + } + + public closeEditor(position: Position, input: IEditorInput): TPromise { + return this.doCloseEditor(position, input); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + return this.editorPart.closeEditor(position, input); + } + + public closeEditors(position: Position, filter?: { except?: IEditorInput, direction?: Direction, unmodifiedOnly?: boolean }): TPromise { + return this.editorPart.closeEditors(position, filter); + } + + public closeAllEditors(except?: Position): TPromise { + return this.editorPart.closeAllEditors(except); + } + + public createInput(input: IEditorInput): EditorInput; + public createInput(input: IResourceInputType): EditorInput; + public createInput(input: any): IEditorInput { + + // Workbench Input Support + if (input instanceof EditorInput) { + return input; + } + + // Side by Side Support + const resourceSideBySideInput = input; + if (resourceSideBySideInput.masterResource && resourceSideBySideInput.detailResource) { + const masterInput = this.createInput({ resource: resourceSideBySideInput.masterResource }); + const detailInput = this.createInput({ resource: resourceSideBySideInput.detailResource }); + + return new SideBySideEditorInput(resourceSideBySideInput.label || masterInput.getName(), typeof resourceSideBySideInput.description === 'string' ? resourceSideBySideInput.description : masterInput.getDescription(), detailInput, masterInput); + } + + // Diff Editor Support + const resourceDiffInput = input; + if (resourceDiffInput.leftResource && resourceDiffInput.rightResource) { + const leftInput = this.createInput({ resource: resourceDiffInput.leftResource }); + const rightInput = this.createInput({ resource: resourceDiffInput.rightResource }); + const label = resourceDiffInput.label || this.toDiffLabel(resourceDiffInput.leftResource, resourceDiffInput.rightResource, this.workspaceContextService, this.environmentService); + + return new DiffEditorInput(label, resourceDiffInput.description, leftInput, rightInput); + } + + // Untitled file support + const untitledInput = input; + if (!untitledInput.resource || typeof untitledInput.filePath === 'string' || (untitledInput.resource instanceof URI && untitledInput.resource.scheme === UNTITLED_SCHEMA)) { + return this.untitledEditorService.createOrGet(untitledInput.filePath ? URI.file(untitledInput.filePath) : untitledInput.resource, untitledInput.language, untitledInput.contents, untitledInput.encoding); + } + + const resourceInput = input; + + // Files support + if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { + return this.createOrGet(resourceInput.resource, this.instantiationService, resourceInput.label, resourceInput.description, resourceInput.encoding); + } + + // Any other resource + else if (resourceInput.resource instanceof URI) { + const label = resourceInput.label || basename(resourceInput.resource.fsPath); + let description: string; + if (typeof resourceInput.description === 'string') { + description = resourceInput.description; + } else if (resourceInput.resource.scheme === network.Schemas.file) { + description = dirname(resourceInput.resource.fsPath); + } + + return this.createOrGet(resourceInput.resource, this.instantiationService, label, description); + } + + return null; + } + + private createOrGet(resource: URI, instantiationService: IInstantiationService, label: string, description: string, encoding?: string): ICachedEditorInput { + if (WorkbenchEditorService.CACHE.has(resource)) { + const input = WorkbenchEditorService.CACHE.get(resource); + if (input instanceof ResourceEditorInput) { + input.setName(label); + input.setDescription(description); + } else { + input.setPreferredEncoding(encoding); + } + + return input; + } + + let input: ICachedEditorInput; + if (resource.scheme === network.Schemas.file || this.fileService.canHandleResource && this.fileService.canHandleResource(resource)) { + input = this.fileInputFactory.createFileInput(resource, encoding, instantiationService); + } else { + input = instantiationService.createInstance(ResourceEditorInput, label, description, resource); + } + + WorkbenchEditorService.CACHE.set(resource, input); + once(input.onDispose)(() => { + WorkbenchEditorService.CACHE.delete(resource); + }); + + return input; + } + + private toDiffLabel(res1: URI, res2: URI, context: IWorkspaceContextService, environment: IEnvironmentService): string { + const leftName = getPathLabel(res1.fsPath, context, environment); + const rightName = getPathLabel(res2.fsPath, context, environment); + + return nls.localize('compareLabels', "{0} ↔ {1}", leftName, rightName); + } +} + +export interface IEditorOpenHandler { + (input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + (input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; +} + +export interface IEditorCloseHandler { + (position: Position, input: IEditorInput): TPromise; +} + +/** + * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior + * of openEditor() and closeEditor() by providing a handler. + * + * This gives clients a chance to override the behavior of openEditor() and closeEditor(). + */ +export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { + private editorOpenHandler: IEditorOpenHandler; + private editorCloseHandler: IEditorCloseHandler; + + constructor( + @IUntitledEditorService untitledEditorService: IUntitledEditorService, + @IInstantiationService instantiationService: IInstantiationService, + @IWorkspaceContextService workspaceContextService: IWorkspaceContextService, + @IWorkbenchEditorService editorService: IWorkbenchEditorService, + @IEnvironmentService environmentService: IEnvironmentService, + @IFileService fileService: IFileService + ) { + super( + editorService, + untitledEditorService, + workspaceContextService, + instantiationService, + environmentService, + fileService + ); + } + + public setEditorOpenHandler(handler: IEditorOpenHandler): void { + this.editorOpenHandler = handler; + } + + public setEditorCloseHandler(handler: IEditorCloseHandler): void { + this.editorCloseHandler = handler; + } + + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, position?: Position): TPromise; + protected doOpenEditor(input: IEditorInput, options?: EditorOptions, arg3?: any): TPromise { + const handleOpen = this.editorOpenHandler ? this.editorOpenHandler(input, options, arg3) : TPromise.as(void 0); + + return handleOpen.then(editor => { + if (editor) { + return TPromise.as(editor); + } + + return super.doOpenEditor(input, options, arg3); + }); + } + + protected doCloseEditor(position: Position, input: IEditorInput): TPromise { + const handleClose = this.editorCloseHandler ? this.editorCloseHandler(position, input) : TPromise.as(void 0); + + return handleClose.then(() => { + return super.doCloseEditor(position, input); + }); + } +} diff --git a/src/vs/workbench/services/editor/test/browser/editorService.test.ts b/src/vs/workbench/services/editor/test/browser/editorService.test.ts index c6ce1376dc1..dabe18285d6 100644 --- a/src/vs/workbench/services/editor/test/browser/editorService.test.ts +++ b/src/vs/workbench/services/editor/test/browser/editorService.test.ts @@ -14,7 +14,7 @@ import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; import { EditorInput, EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; import { workbenchInstantiationService } from 'vs/workbench/test/workbenchTestServices'; -import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/browser/editorService'; +import { DelegatingWorkbenchEditorService, WorkbenchEditorService, IEditorPart } from 'vs/workbench/services/editor/common/editorService'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; import { TestThemeService } from 'vs/platform/theme/test/common/testThemeService'; -- GitLab