/*--------------------------------------------------------------------------------------------- * 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 {guessMimeTypes} from 'vs/base/common/mime'; import {Registry} from 'vs/platform/platform'; import {basename, dirname} from 'vs/base/common/paths'; import types = require('vs/base/common/types'); import {IDiffEditor, ICodeEditor} from 'vs/editor/browser/editorBrowser'; import {ICommonCodeEditor, IModel, EditorType, IEditor as ICommonEditor} from 'vs/editor/common/editorCommon'; import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor'; import {EditorInput, EditorOptions, IFileEditorInput, TextEditorOptions, IEditorRegistry, Extensions} from 'vs/workbench/common/editor'; import {ResourceEditorInput} from 'vs/workbench/common/editor/resourceEditorInput'; import {UntitledEditorInput} from 'vs/workbench/common/editor/untitledEditorInput'; import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput'; import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/untitledEditorService'; import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {IEditorInput, IEditorModel, IEditorOptions, Position, Direction, IEditor, IResourceInput, ITextEditorModel} from 'vs/platform/editor/common/editor'; import {IInstantiationService} from 'vs/platform/instantiation/common/instantiation'; import {AsyncDescriptor0} from 'vs/platform/instantiation/common/descriptors'; export interface IEditorPart { openEditor(input?: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; openEditor(input?: EditorInput, options?: EditorOptions, position?: Position): TPromise; openEditors(editors: { input: EditorInput, position: Position, options?: EditorOptions }[]): TPromise; replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[]): TPromise; closeEditor(position: Position, input: IEditorInput): TPromise; closeEditors(position: Position, except?: IEditorInput, direction?: Direction): TPromise; closeAllEditors(except?: Position): TPromise; getActiveEditor(): BaseEditor; getVisibleEditors(): IEditor[]; getActiveEditorInput(): EditorInput; } export class WorkbenchEditorService implements IWorkbenchEditorService { public serviceId = IWorkbenchEditorService; private editorPart: IEditorPart | IWorkbenchEditorService; private fileInputDescriptor: AsyncDescriptor0; constructor( editorPart: IEditorPart | IWorkbenchEditorService, @IUntitledEditorService private untitledEditorService: IUntitledEditorService, @IInstantiationService private instantiationService?: IInstantiationService ) { this.editorPart = editorPart; this.fileInputDescriptor = (Registry.as(Extensions.Editors)).getDefaultFileInput(); } 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, includeDiff: boolean): boolean { if (!input) { return false; } return this.getVisibleEditors().some((editor) => { if (!editor.input) { return false; } if (input.matches(editor.input)) { return true; } if (includeDiff && editor.input instanceof DiffEditorInput) { let diffInput = editor.input; return input.matches(diffInput.modifiedInput) || input.matches(diffInput.originalInput); } return false; }); } public openEditor(input: IEditorInput, options?: IEditorOptions, sideBySide?: boolean): TPromise; public openEditor(input: IEditorInput, options?: IEditorOptions, position?: Position): TPromise; public openEditor(input: IResourceInput, position?: Position): TPromise; public openEditor(input: IResourceInput, 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) { if (arg2 && !(arg2 instanceof EditorOptions)) { arg2 = EditorOptions.create(arg2); } return this.doOpenEditor(input, arg2, arg3); } // Support opening foreign resources (such as a http link that points outside of the workbench) let resourceInput = input; if (resourceInput.resource instanceof URI) { let 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) let textInput = input; return this.createInput(textInput).then((typedFileInput: EditorInput) => { if (typedFileInput) { return this.doOpenEditor(typedFileInput, TextEditorOptions.from(textInput), arg2); } return TPromise.as(null); }); } /** * Allow subclasses to implement their own behavior for opening editor (see below). */ protected doOpenEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; protected doOpenEditor(input: EditorInput, options?: EditorOptions, position?: Position): TPromise; protected doOpenEditor(input: EditorInput, options?: EditorOptions, arg3?: any): TPromise { return this.editorPart.openEditor(input, options, arg3); } public openEditors(editors: { input: IResourceInput, position: Position }[]): TPromise; public openEditors(editors: { input: IEditorInput, position: Position, options?: IEditorOptions }[]): TPromise; public openEditors(editors: any[]): TPromise { return TPromise.join(editors.map(editor => this.createInput(editor.input))).then(inputs => { const typedInputs: { input: EditorInput, position: Position, options?: EditorOptions }[] = inputs.map((input, index) => { let options = editors[index].input instanceof EditorInput ? editors[index].options : TextEditorOptions.from(editors[index].input); if (options && !(options instanceof EditorOptions)) { options = EditorOptions.create(options); } return { input, options, position: editors[index].position }; }); return this.editorPart.openEditors(typedInputs); }); } public replaceEditors(editors: { toReplace: IResourceInput, replaceWith: IResourceInput }[]): TPromise; public replaceEditors(editors: { toReplace: EditorInput, replaceWith: EditorInput, options?: IEditorOptions }[]): TPromise; public replaceEditors(editors: any[]): TPromise { return TPromise.join(editors.map(editor => this.createInput(editor.toReplace))).then(toReplaceInputs => { return TPromise.join(editors.map(editor => this.createInput(editor.replaceWith))).then(replaceWithInputs => { const typedReplacements: { toReplace: EditorInput, replaceWith: EditorInput, options?: EditorOptions }[] = editors.map((editor, index) => { let options = editor.toReplace instanceof EditorInput ? editor.options : TextEditorOptions.from(editor.replaceWith); if (options && !(options instanceof EditorOptions)) { options = EditorOptions.create(options); } return { toReplace: toReplaceInputs[index], replaceWith: replaceWithInputs[index], options }; }); return this.editorPart.replaceEditors(typedReplacements); }); }); } public closeEditor(position: Position, input: IEditorInput): TPromise { return this.editorPart.closeEditor(position, input); } public closeEditors(position: Position, except?: IEditorInput, direction?: Direction): TPromise { return this.editorPart.closeEditors(position, except, direction); } public closeAllEditors(except?: Position): TPromise { return this.editorPart.closeAllEditors(except); } public resolveEditorModel(input: IEditorInput, refresh?: boolean): TPromise; public resolveEditorModel(input: IResourceInput, refresh?: boolean): TPromise; public resolveEditorModel(input: any, refresh?: boolean): TPromise { return this.createInput(input).then((workbenchInput: IEditorInput) => { if (workbenchInput) { // Resolve if applicable if (workbenchInput instanceof EditorInput) { return workbenchInput.resolve(!!refresh); } } return TPromise.as(null); }); } public createInput(input: EditorInput): TPromise; public createInput(input: IResourceInput): TPromise; public createInput(input: any): TPromise { // Workbench Input Support if (input instanceof EditorInput) { return TPromise.as(input); } // Base Text Editor Support for inmemory resources let resourceInput = input; if (resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.inMemory) { // For in-memory resources we only support to resolve the input from the current active editor // because the workbench does not track editor models by in memory URL. This concept is only // being used in the code editor. let activeEditor = this.getActiveEditor(); if (activeEditor) { let control = activeEditor.getControl(); if (types.isFunction(control.getEditorType)) { // Single Editor: If code editor model matches, return input from editor if (control.getEditorType() === EditorType.ICodeEditor) { let codeEditor = control; let model = this.findModel(codeEditor, input); if (model) { return TPromise.as(activeEditor.input); } } // Diff Editor: If left or right code editor model matches, return associated input else if (control.getEditorType() === EditorType.IDiffEditor) { let diffInput = activeEditor.input; let diffCodeEditor = control; let originalModel = this.findModel(diffCodeEditor.getOriginalEditor(), input); if (originalModel) { return TPromise.as(diffInput.originalInput); } let modifiedModel = this.findModel(diffCodeEditor.getModifiedEditor(), input); if (modifiedModel) { return TPromise.as(diffInput.modifiedInput); } } } } } // Untitled file support else if (resourceInput.resource instanceof URI && (resourceInput.resource.scheme === UntitledEditorInput.SCHEMA)) { return TPromise.as(this.untitledEditorService.createOrGet(resourceInput.resource)); } // Base Text Editor Support for file resources else if (this.fileInputDescriptor && resourceInput.resource instanceof URI && resourceInput.resource.scheme === network.Schemas.file) { return this.createFileInput(resourceInput.resource, resourceInput.mime, resourceInput.encoding); } // Treat an URI as ResourceEditorInput else if (resourceInput.resource instanceof URI) { return TPromise.as(this.instantiationService.createInstance(ResourceEditorInput, basename(resourceInput.resource.fsPath), dirname(resourceInput.resource.fsPath), resourceInput.resource)); } return TPromise.as(null); } private createFileInput(resource: URI, mime?: string, encoding?: string): TPromise { return this.instantiationService.createInstance(this.fileInputDescriptor).then((typedFileInput) => { typedFileInput.setResource(resource); typedFileInput.setMime(mime || guessMimeTypes(resource.fsPath).join(', ')); typedFileInput.setPreferredEncoding(encoding); return typedFileInput; }); } private findModel(editor: ICommonCodeEditor, input: IResourceInput): IModel { let model = editor.getModel(); if (!model) { return null; } return model.uri.toString() === input.resource.toString() ? model : null; } } export interface IDelegatingWorkbenchEditorServiceHandler { (input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; (input: EditorInput, options?: EditorOptions, position?: Position): TPromise; } /** * Subclass of workbench editor service that delegates all calls to the provided editor service. Subclasses can choose to override the behavior * of openEditor() by providing a handler. The handler returns a promise that resolves to an editor to indicate if an action has been taken. * If falsify is returned, the service will delegate to editor service for handling the call to openEditor(). * * This gives clients a chance to override the behavior of openEditor() to match their context. */ export class DelegatingWorkbenchEditorService extends WorkbenchEditorService { private handler: IDelegatingWorkbenchEditorServiceHandler; constructor( handler: IDelegatingWorkbenchEditorServiceHandler, @IUntitledEditorService untitledEditorService: IUntitledEditorService, @IInstantiationService instantiationService: IInstantiationService, @IWorkbenchEditorService editorService: IWorkbenchEditorService ) { super( editorService, untitledEditorService, instantiationService ); this.handler = handler; } protected doOpenEditor(input: EditorInput, options?: EditorOptions, sideBySide?: boolean): TPromise; protected doOpenEditor(input: EditorInput, options?: EditorOptions, position?: Position): TPromise; protected doOpenEditor(input: EditorInput, options?: EditorOptions, arg3?: any): TPromise { return this.handler(input, options, arg3).then(editor => { if (editor) { return TPromise.as(editor); } return super.doOpenEditor(input, options, arg3); }); } }