diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index fa0ec153cf4..8088b650a1b 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -12,9 +12,10 @@ import { IUntitledEditorService } from 'vs/workbench/services/untitled/common/un import { IEditorQuickOpenEntry, IQuickOpenRegistry, Extensions as QuickOpenExtensions, QuickOpenHandlerDescriptor } from 'vs/workbench/browser/quickopen'; import { StatusbarItemDescriptor, StatusbarAlignment, IStatusbarRegistry, Extensions as StatusExtensions } from 'vs/workbench/browser/parts/statusbar/statusbar'; import { EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory } from 'vs/workbench/common/editor'; +import { EditorInput, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; import { StringEditor } from 'vs/workbench/browser/parts/editor/stringEditor'; +import { SideBySideEditor } from 'vs/workbench/browser/parts/editor/sideBySideEditor'; import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { UntitledEditorInput } from 'vs/workbench/common/editor/untitledEditorInput'; import { ResourceEditorInput } from 'vs/workbench/common/editor/resourceEditorInput'; @@ -80,6 +81,18 @@ Registry.as(EditorExtensions.Editors).registerEditor( ] ); +Registry.as(EditorExtensions.Editors).registerEditor( + new EditorDescriptor( + SideBySideEditor.ID, + nls.localize('sideBySideEditor', "Side by Side Editor"), + 'vs/workbench/browser/parts/editor/sideBySideEditor', + 'SideBySideEditor' + ), + [ + new SyncDescriptor(SideBySideEditorInput) + ] +); + interface ISerializedUntitledEditorInput { resource: string; } diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index 6bc8e5902e7..feb5faa327c 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -8,6 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import nls = require('vs/nls'); import { Action } from 'vs/base/common/actions'; import { mixin } from 'vs/base/common/objects'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; import { EditorInput, getUntitledOrFileResource, TextEditorOptions, EditorOptions, IEditorIdentifier, IEditorContext, ActiveEditorMoveArguments, ActiveEditorMovePositioning, EditorCommands } from 'vs/workbench/common/editor'; import { QuickOpenEntryGroup } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen'; @@ -19,7 +20,6 @@ import { IInstantiationService } from 'vs/platform/instantiation/common/instanti import { IHistoryService } from 'vs/workbench/services/history/common/history'; import { IKeybindingService } from 'vs/platform/keybinding/common/keybinding'; import { IEditorGroupService, GroupArrangement } from 'vs/workbench/services/group/common/groupService'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { ICommandService } from 'vs/platform/commands/common/commands'; export class SplitEditorAction extends Action { @@ -56,9 +56,10 @@ export class SplitEditorAction extends Action { // Options let options: EditorOptions; - if (editorToSplit instanceof BaseTextEditor) { + const codeEditor = getCodeEditor(editorToSplit); + if (codeEditor) { options = new TextEditorOptions(); - (options).fromEditor(editorToSplit.getControl()); + (options).fromEditor(codeEditor); } else { options = new EditorOptions(); } @@ -247,10 +248,11 @@ export abstract class BaseFocusSideGroupAction extends Action { // Options let options: EditorOptions; - if (referenceEditor instanceof BaseTextEditor) { + const codeEditor = getCodeEditor(referenceEditor); + if (codeEditor) { options = new TextEditorOptions(); options.pinned = true; - (options).fromEditor(referenceEditor.getControl()); + (options).fromEditor(codeEditor); } else { options = EditorOptions.create({ pinned: true }); } diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index cd66bfd4c19..022328445cc 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -16,11 +16,12 @@ import strings = require('vs/base/common/strings'); import arrays = require('vs/base/common/arrays'); import types = require('vs/base/common/types'); import errors = require('vs/base/common/errors'); +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; import { toErrorMessage } from 'vs/base/common/errorMessage'; import { Scope as MementoScope } from 'vs/workbench/common/memento'; import { Part } from 'vs/workbench/browser/part'; import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, IEditorDescriptor, TextEditorOptions } from 'vs/workbench/common/editor'; +import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, ConfirmResult, IWorkbenchEditorConfiguration, IEditorDescriptor, TextEditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { SideBySideEditorControl, Rochade, ISideBySideEditorControl, ProgressState } from 'vs/workbench/browser/parts/editor/sideBySideEditorControl'; import { WorkbenchProgressService } from 'vs/workbench/services/progress/browser/progressService'; import { IEditorGroupService, GroupOrientation, GroupArrangement } from 'vs/workbench/services/group/common/groupService'; @@ -29,13 +30,11 @@ import { IEditorPart } from 'vs/workbench/services/editor/browser/editorService' import { IPartService } from 'vs/workbench/services/part/common/partService'; import { Position, POSITIONS, Direction } from 'vs/platform/editor/common/editor'; import { IStorageService } from 'vs/platform/storage/common/storage'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; import { IMessageService, IMessageWithAction, Severity } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IProgressService } from 'vs/platform/progress/common/progress'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { EditorStacksModel, EditorGroup, EditorIdentifier, GroupEvent } from 'vs/workbench/common/editor/editorStacksModel'; import Event, { Emitter } from 'vs/base/common/event'; @@ -666,7 +665,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } private doHandleDirty(identifier: EditorIdentifier, ignoreIfOpenedInOtherGroup?: boolean): TPromise { - if (!identifier || !identifier.editor || !identifier.editor.isDirty() || (ignoreIfOpenedInOtherGroup && this.countEditors(identifier.editor, true /* include diff editors */) > 1 /* allow to close a dirty editor if it is opened in another group */)) { + if (!identifier || !identifier.editor || !identifier.editor.isDirty() || (ignoreIfOpenedInOtherGroup && this.countEditors(identifier.editor) > 1 /* allow to close a dirty editor if it is opened in another group */)) { return TPromise.as(false); // no veto } @@ -685,10 +684,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService } } - private countEditors(editor: EditorInput, includeDiffEditors: boolean): number { + private countEditors(editor: EditorInput): number { const editors = [editor]; - if (includeDiffEditors && editor instanceof DiffEditorInput) { - editors.push(editor.modifiedInput); + if (editor instanceof SideBySideEditorInput) { + editors.push(editor.master); } return editors.reduce((prev, e) => prev += this.stacks.count(e), 0); @@ -796,9 +795,10 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService // for th editor to be a text editor and creating the options accordingly if so let options = EditorOptions.create({ pinned: true, index }); const activeEditor = this.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor && activeEditor.position === this.stacks.positionOfGroup(fromGroup) && input.matches(activeEditor.input)) { + const codeEditor = getCodeEditor(activeEditor); + if (codeEditor && activeEditor.position === this.stacks.positionOfGroup(fromGroup) && input.matches(activeEditor.input)) { options = TextEditorOptions.create({ pinned: true, index }); - (options).fromEditor(activeEditor.getControl()); + (options).fromEditor(codeEditor); } // A move to another group is an open first... diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index fcdf703d901..5b9b5b5c3e6 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -28,7 +28,6 @@ import { IEditorAction, ICommonCodeEditor, IModelContentChangedEvent, IModelOpti import { ICodeEditor, IDiffEditor } from 'vs/editor/browser/editorBrowser'; import { TrimTrailingWhitespaceAction } from 'vs/editor/contrib/linesOperations/common/linesOperations'; import { IndentUsingSpaces, IndentUsingTabs, DetectIndentation, IndentationToSpacesAction, IndentationToTabsAction } from 'vs/editor/contrib/indentation/common/indentation'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { BaseBinaryResourceEditor } from 'vs/workbench/browser/parts/editor/binaryEditor'; import { BinaryResourceDiffEditor } from 'vs/workbench/browser/parts/editor/binaryDiffEditor'; import { IEditor as IBaseEditor } from 'vs/platform/editor/common/editor'; @@ -47,6 +46,7 @@ import { ITextFileService } from 'vs/workbench/services/textfile/common/textfile import { IExtensionGalleryService } from 'vs/platform/extensionManagement/common/extensionManagement'; import { IViewletService } from 'vs/workbench/services/viewlet/browser/viewlet'; import { IExtensionsViewlet, VIEWLET_ID } from 'vs/workbench/parts/extensions/common/extensions'; +import { getCodeEditor as getEditorWidget } from 'vs/editor/common/services/codeEditorService'; function getCodeEditor(editorWidget: IEditor): ICommonCodeEditor { if (editorWidget) { @@ -459,11 +459,8 @@ export class EditorStatus implements IStatusbarItem { } private onEditorsChanged(): void { - let control: IEditor; const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor) { - control = activeEditor.getControl(); - } + let control: IEditor = getEditorWidget(activeEditor); // Update all states this.onSelectionChange(control); @@ -477,8 +474,7 @@ export class EditorStatus implements IStatusbarItem { dispose(this.activeEditorListeners); // Attach new listeners to active editor - if (activeEditor instanceof BaseTextEditor) { - const control = activeEditor.getControl(); + if (control) { // Hook Listener for Selection changes this.activeEditorListeners.push(control.onDidChangeCursorPosition((event: ICursorPositionChangedEvent) => { @@ -623,7 +619,7 @@ export class EditorStatus implements IStatusbarItem { const info: StateDelta = { encoding: null }; // We only support text based editors - if (e instanceof BaseTextEditor) { + if (getEditorWidget(e)) { const encodingSupport: IEncodingSupport = asFileOrUntitledEditorInput(e.input); if (encodingSupport && types.isFunction(encodingSupport.getEncoding)) { const rawEncoding = encodingSupport.getEncoding(); @@ -662,8 +658,12 @@ export class EditorStatus implements IStatusbarItem { } } -function isWritableCodeEditor(e: BaseTextEditor): boolean { - let editorWidget = e.getControl(); +function isWritableCodeEditor(e: IBaseEditor): boolean { + let editorWidget: IEditor = getEditorWidget(e); + if (!editorWidget) { + return false; + } + if (editorWidget.getEditorType() === EditorType.IDiffEditor) { editorWidget = (editorWidget).getModifiedEditor(); } @@ -719,11 +719,11 @@ export class ChangeModeAction extends Action { public run(): TPromise { let activeEditor = this.editorService.getActiveEditor(); - if (!(activeEditor instanceof BaseTextEditor)) { + const editorWidget = getEditorWidget(activeEditor); + if (!editorWidget) { return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } - const editorWidget = (activeEditor).getControl(); const textModel = getTextModel(editorWidget); const fileinput = asFileEditorInput(activeEditor.input, true); @@ -811,8 +811,8 @@ export class ChangeModeAction extends Action { // Change mode for active editor activeEditor = this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor) { - const editorWidget = activeEditor.getControl(); + const editorWidget: IEditor = getEditorWidget(activeEditor); + if (editorWidget) { const models: IModel[] = []; const textModel = getTextModel(editorWidget); @@ -912,14 +912,14 @@ class ChangeIndentationAction extends Action { public run(): TPromise { const activeEditor = this.editorService.getActiveEditor(); - if (!(activeEditor instanceof BaseTextEditor)) { + const control = getEditorWidget(activeEditor); + if (!control) { return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } - if (!isWritableCodeEditor(activeEditor)) { + if (!isWritableCodeEditor(activeEditor)) { return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } - const control = activeEditor.getControl(); const picks = [ control.getAction(IndentUsingSpaces.ID), control.getAction(IndentUsingTabs.ID), @@ -962,15 +962,15 @@ export class ChangeEOLAction extends Action { public run(): TPromise { let activeEditor = this.editorService.getActiveEditor(); - if (!(activeEditor instanceof BaseTextEditor)) { + const editorWidget = getEditorWidget(activeEditor); + if (!editorWidget) { return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } - if (!isWritableCodeEditor(activeEditor)) { + if (!isWritableCodeEditor(activeEditor)) { return this.quickOpenService.pick([{ label: nls.localize('noWritableCodeEditor', "The active code editor is read-only.") }]); } - const editorWidget = (activeEditor).getControl(); const textModel = getTextModel(editorWidget); const EOLOptions: IChangeEOLEntry[] = [ @@ -983,8 +983,8 @@ export class ChangeEOLAction extends Action { return this.quickOpenService.pick(EOLOptions, { placeHolder: nls.localize('pickEndOfLine', "Select End of Line Sequence"), autoFocus: { autoFocusIndex: selectedIndex } }).then(eol => { if (eol) { activeEditor = this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor && isWritableCodeEditor(activeEditor)) { - const editorWidget = activeEditor.getControl(); + const editorWidget = getEditorWidget(activeEditor); + if (editorWidget && isWritableCodeEditor(activeEditor)) { const textModel = getTextModel(editorWidget); textModel.setEOL(eol.eol); } @@ -1010,7 +1010,7 @@ export class ChangeEncodingAction extends Action { public run(): TPromise { let activeEditor = this.editorService.getActiveEditor(); - if (!(activeEditor instanceof BaseTextEditor) || !activeEditor.input) { + if (!getEditorWidget(activeEditor) || !activeEditor.input) { return this.quickOpenService.pick([{ label: nls.localize('noEditor', "No text editor active at this time") }]); } @@ -1033,7 +1033,7 @@ export class ChangeEncodingAction extends Action { if (encodingSupport instanceof UntitledEditorInput) { pickActionPromise = TPromise.as(saveWithEncodingPick); - } else if (!isWritableCodeEditor(activeEditor)) { + } else if (!isWritableCodeEditor(activeEditor)) { pickActionPromise = TPromise.as(reopenWithEncodingPick); } else { pickActionPromise = this.quickOpenService.pick([reopenWithEncodingPick, saveWithEncodingPick], { placeHolder: nls.localize('pickAction', "Select Action"), matchOnDetail: true }); diff --git a/src/vs/workbench/browser/parts/editor/media/sidebysideEditor.css b/src/vs/workbench/browser/parts/editor/media/sidebysideEditor.css new file mode 100644 index 00000000000..dbb603fb30b --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/media/sidebysideEditor.css @@ -0,0 +1,12 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.vs-dark .side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px black; +} + +.side-by-side-editor > .master-editor-container { + box-shadow: -6px 0 5px -5px #DDD; +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts new file mode 100644 index 00000000000..fb65c25e6f1 --- /dev/null +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -0,0 +1,266 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import 'vs/css!./media/sidebysideEditor'; +import { TPromise } from 'vs/base/common/winjs.base'; +import * as strings from 'vs/base/common/strings'; +import { Disposable } from 'vs/base/common/lifecycle'; +import * as DOM from 'vs/base/browser/dom'; +import { Dimension, Builder } from 'vs/base/browser/builder'; +import Event, { Emitter } from 'vs/base/common/event'; + +import { Registry } from 'vs/platform/platform'; +import { IEditorRegistry, Extensions as EditorExtensions, EditorInput, EditorOptions, SideBySideEditorInput } from 'vs/workbench/common/editor'; +import { BaseEditor, EditorDescriptor } from 'vs/workbench/browser/parts/editor/baseEditor'; +import { IEditorControl, Position } from 'vs/platform/editor/common/editor'; +import { Sash, ISashEvent, IVerticalSashLayoutProvider } from 'vs/base/browser/ui/sash/sash'; + +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; + +export class SideBySideEditor extends BaseEditor { + + public static ID: string = 'workbench.editor.sidebysideEditor'; + + private dimension: Dimension; + + private masterEditor: BaseEditor; + private masterEditorContainer: HTMLElement; + + private detailsEditor: BaseEditor; + private detailsEditorContainer: HTMLElement; + + private sash: VSash; + + constructor( + @ITelemetryService telemetryService: ITelemetryService, + @IInstantiationService private instantiationService: IInstantiationService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(SideBySideEditor.ID, telemetryService); + } + + public createEditor(parent: Builder) { + const parentElement = parent.getHTMLElement(); + DOM.addClass(parentElement, 'side-by-side-editor'); + this.createSash(parentElement); + } + + public setInput(newInput: SideBySideEditorInput, options: EditorOptions): TPromise { + const oldInput = this.getInput(); + return super.setInput(newInput, options) + .then(() => this.updateInput(oldInput, newInput, options)); + } + + public setEditorVisible(visible: boolean, position: Position): void { + if (this.masterEditor) { + this.masterEditor.setVisible(visible); + } + if (this.detailsEditor) { + this.detailsEditor.setVisible(visible); + } + super.setEditorVisible(visible, position); + } + + public clearInput() { + this.disposeEditors(); + super.clearInput(); + } + + public focus() { + if (this.masterEditor) { + this.masterEditor.focus(); + } + } + + public layout(dimension: Dimension) { + this.dimension = dimension; + this.sash.setDimenesion(this.dimension); + } + + public getControl(): IEditorControl { + return this.masterEditor.getControl(); + } + + private updateInput(oldInput: SideBySideEditorInput, newInput: SideBySideEditorInput, options: EditorOptions): TPromise { + if (!newInput.matches(oldInput)) { + if (oldInput) { + this.disposeEditors(); + } + this.createEditorContainers(); + return this.setNewInput(newInput, options); + } else { + this.detailsEditor.setInput(newInput.details, new EditorOptions()); + this.masterEditor.setInput(newInput.master, options); + } + } + + private setNewInput(newInput: SideBySideEditorInput, options: EditorOptions): TPromise { + return TPromise.join([ + this._createEditor(newInput.details, this.detailsEditorContainer, new EditorOptions()), //TODO@ben why do you have to provide options + this._createEditor(newInput.master, this.masterEditorContainer, options) + ]).then(result => this.onEditorsCreated(result[0], result[1])); + } + + private _createEditor(editorInput: EditorInput, container: HTMLElement, options: EditorOptions): TPromise { + const descriptor = Registry.as(EditorExtensions.Editors).getEditor(editorInput); + if (!descriptor) { + return TPromise.wrapError(new Error(strings.format('Can not find a registered editor for the input {0}', editorInput))); + } + return this.instantiationService.createInstance(descriptor) + .then((editor: BaseEditor) => { + editor.create(new Builder(container)); + editor.setInput(editorInput, options); + return editor; + }); + } + + private onEditorsCreated(details: BaseEditor, master: BaseEditor) { + this.detailsEditor = details; + this.masterEditor = master; + this.setVisible(this.isVisible()); + this.dolayout(this.sash.getVerticalSashLeft()); + } + + private createEditorContainers() { + const parentElement = this.getContainer().getHTMLElement(); + this.detailsEditorContainer = DOM.append(parentElement, DOM.$('.details-editor-container')); + this.detailsEditorContainer.style.position = 'absolute'; + this.masterEditorContainer = DOM.append(parentElement, DOM.$('.master-editor-container')); + this.masterEditorContainer.style.position = 'absolute'; + } + + private createSash(parentElement: HTMLElement): void { + this.sash = this._register(new VSash(parentElement)); + this._register(this.sash.onPositionChange(position => this.dolayout(position))); + } + + private dolayout(splitPoint: number) { + if (!this.detailsEditor || !this.masterEditor) { + return; + } + const masterEditorWidth = this.dimension.width - splitPoint; + const detailsEditorWidth = this.dimension.width - masterEditorWidth; + + this.detailsEditorContainer.style.width = `${detailsEditorWidth}px`; + this.detailsEditorContainer.style.height = `${this.dimension.height}px`; + this.detailsEditorContainer.style.left = '0px'; + + this.masterEditorContainer.style.width = `${masterEditorWidth}px`; + this.masterEditorContainer.style.height = `${this.dimension.height}px`; + this.masterEditorContainer.style.left = `${splitPoint}px`; + + this.detailsEditor.layout(new Dimension(detailsEditorWidth, this.dimension.height)); + this.masterEditor.layout(new Dimension(masterEditorWidth, this.dimension.height)); + } + + private disposeEditors() { + const parentContainer = this.getContainer().getHTMLElement(); + if (this.detailsEditor) { + this.detailsEditor.dispose(); + this.detailsEditor = null; + } + if (this.masterEditor) { + this.masterEditor.dispose(); + this.detailsEditor = null; + } + if (this.detailsEditorContainer) { + parentContainer.removeChild(this.detailsEditorContainer); + this.detailsEditorContainer = null; + } + if (this.masterEditorContainer) { + parentContainer.removeChild(this.masterEditorContainer); + this.masterEditorContainer = null; + } + } +} + +class VSash extends Disposable implements IVerticalSashLayoutProvider { + + private static MINIMUM_EDITOR_WIDTH = 220; + + private sash: Sash; + private ratio: number; + private startPosition: number; + private position: number; + private dimension: Dimension; + + private _onPositionChange: Emitter = new Emitter(); + public get onPositionChange(): Event { return this._onPositionChange.event; } + + constructor(container: HTMLElement) { + super(); + this.ratio = 0.5; + this.sash = new Sash(container, this); + + this._register(this.sash.addListener2('start', () => this.onSashDragStart())); + this._register(this.sash.addListener2('change', (e: ISashEvent) => this.onSashDrag(e))); + this._register(this.sash.addListener2('end', () => this.onSashDragEnd())); + this._register(this.sash.addListener2('reset', () => this.onSashReset())); + } + + public getVerticalSashTop(): number { + return 0; + } + + public getVerticalSashLeft(): number { + return this.position; + } + + public getVerticalSashHeight(): number { + return this.dimension.height; + } + + public setDimenesion(dimension: Dimension) { + this.dimension = dimension; + this.compute(this.ratio); + } + + private onSashDragStart(): void { + this.startPosition = this.position; + } + + private onSashDrag(e: ISashEvent): void { + this.compute((this.startPosition + (e.currentX - e.startX)) / this.dimension.width); + } + + private compute(ratio: number) { + this.computeSashPosition(ratio); + this.ratio = this.position / this.dimension.width; + this._onPositionChange.fire(this.position); + } + + private onSashDragEnd(): void { + this.sash.layout(); + } + + private onSashReset(): void { + this.ratio = 0.5; + this._onPositionChange.fire(this.position); + this.sash.layout(); + } + + private computeSashPosition(sashRatio: number = this.ratio) { + let contentWidth = this.dimension.width; + let sashPosition = Math.floor((sashRatio || 0.5) * contentWidth); + let midPoint = Math.floor(0.5 * contentWidth); + + if (contentWidth > VSash.MINIMUM_EDITOR_WIDTH * 2) { + if (sashPosition < VSash.MINIMUM_EDITOR_WIDTH) { + sashPosition = VSash.MINIMUM_EDITOR_WIDTH; + } + if (sashPosition > contentWidth - VSash.MINIMUM_EDITOR_WIDTH) { + sashPosition = contentWidth - VSash.MINIMUM_EDITOR_WIDTH; + } + } else { + sashPosition = midPoint; + } + if (this.position !== sashPosition) { + this.position = sashPosition; + this.sash.layout(); + } + } +} \ No newline at end of file diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts index 87009b01e42..881a2d25fe4 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditorControl.ts @@ -21,7 +21,6 @@ import { isMacintosh } from 'vs/base/common/platform'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Position, POSITIONS } from 'vs/platform/editor/common/editor'; import { IEditorGroupService, GroupArrangement, GroupOrientation } from 'vs/workbench/services/group/common/groupService'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -35,6 +34,7 @@ import { NoTabsTitleControl } from 'vs/workbench/browser/parts/editor/noTabsTitl import { IEditorStacksModel, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, IEditorGroup, EditorOptions, TextEditorOptions, IEditorIdentifier } from 'vs/workbench/common/editor'; import { extractResources } from 'vs/base/browser/dnd'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; export enum Rochade { NONE, @@ -939,9 +939,10 @@ export class SideBySideEditorControl implements ISideBySideEditorControl, IVerti // for th editor to be a text editor and creating the options accordingly if so let options = EditorOptions.create({ pinned: true }); const activeEditor = $this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor && activeEditor.position === stacks.positionOfGroup(identifier.group) && identifier.editor.matches(activeEditor.input)) { + const editor = getCodeEditor(activeEditor); + if (editor && activeEditor.position === stacks.positionOfGroup(identifier.group) && identifier.editor.matches(activeEditor.input)) { options = TextEditorOptions.create({ pinned: true }); - (options).fromEditor(activeEditor.getControl()); + (options).fromEditor(editor); } return options; diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index 08870420bc3..f4f22bbb0f6 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -24,7 +24,6 @@ import { IMessageService } from 'vs/platform/message/common/message'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IThemeService } from 'vs/workbench/services/themes/common/themeService'; -import { Selection } from 'vs/editor/common/core/selection'; /** * The base class of editors that leverage the text editor for the editing experience. This class is only intended to @@ -176,10 +175,6 @@ export abstract class BaseTextEditor extends BaseEditor { return this.editorControl; } - public getSelection(): Selection { - return this.editorControl.getSelection(); - } - public dispose(): void { // Destroy Editor Control diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index b8464593761..408e2fea0b0 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -5,10 +5,11 @@ 'use strict'; import { TPromise } from 'vs/base/common/winjs.base'; -import Event, { Emitter } from 'vs/base/common/event'; +import Event, { Emitter, once } from 'vs/base/common/event'; import * as objects from 'vs/base/common/objects'; import types = require('vs/base/common/types'); import URI from 'vs/base/common/uri'; +import { IDisposable, dispose } from 'vs/base/common/lifecycle'; import { IEditor, ICommonCodeEditor, IEditorViewState, IEditorOptions as ICodeEditorOptions, IModel } from 'vs/editor/common/editorCommon'; import { IEditorInput, IEditorModel, IEditorOptions, ITextEditorOptions, IResourceInput, Position } from 'vs/platform/editor/common/editor'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -358,47 +359,114 @@ export abstract class UntitledEditorInput extends EditorInput implements IEncodi } /** - * The base class of editor inputs that have an original and modified side. + * Side by side editor inputs that have a master and details side. */ -export abstract class BaseDiffEditorInput extends EditorInput { - private _originalInput: EditorInput; - private _modifiedInput: EditorInput; +export class SideBySideEditorInput extends EditorInput { - constructor(originalInput: EditorInput, modifiedInput: EditorInput) { + private _toUnbind: IDisposable[]; + + constructor(private name: string, private description: string, private _details: EditorInput, private _master: EditorInput, protected forceOpenAsBinary?: boolean) { super(); - - this._originalInput = originalInput; - this._modifiedInput = modifiedInput; + this._toUnbind = []; + this.registerListeners(); } - public get originalInput(): EditorInput { - return this._originalInput; + get master(): EditorInput { + return this._master; } - public get modifiedInput(): EditorInput { - return this._modifiedInput; + get details(): EditorInput { + return this._details; } public isDirty(): boolean { - return this._modifiedInput.isDirty(); + return this.master.isDirty(); } public confirmSave(): ConfirmResult { - return this._modifiedInput.confirmSave(); + return this.master.confirmSave(); } public save(): TPromise { - return this._modifiedInput.save(); + return this.master.save(); } public revert(): TPromise { - return this._modifiedInput.revert(); + return this.master.revert(); } public getTelemetryDescriptor(): { [key: string]: any; } { - const descriptor = this._modifiedInput.getTelemetryDescriptor(); + const descriptor = this.master.getTelemetryDescriptor(); return objects.assign(descriptor, super.getTelemetryDescriptor()); } + + private registerListeners(): void { + + // When the details or master input gets disposed, dispose this diff editor input + const onceDetailsDisposed = once(this.details.onDispose); + this._toUnbind.push(onceDetailsDisposed(() => { + if (!this.isDisposed()) { + this.dispose(); + } + })); + + const onceMasterDisposed = once(this.master.onDispose); + this._toUnbind.push(onceMasterDisposed(() => { + if (!this.isDisposed()) { + this.dispose(); + } + })); + + // Reemit some events from the master side to the outside + this._toUnbind.push(this.master.onDidChangeDirty(() => this._onDidChangeDirty.fire())); + this._toUnbind.push(this.master.onDidChangeLabel(() => this._onDidChangeLabel.fire())); + } + + public get toUnbind() { + return this._toUnbind; + } + + public resolve(refresh?: boolean): TPromise { + return TPromise.as(null); + } + + getTypeId(): string { + return 'workbench.editorinputs.sidebysideEditorInput'; + } + + public getName(): string { + return this.name; + } + + public getDescription(): string { + return this.description; + } + + public supportsSplitEditor(): boolean { + return false; + } + + public matches(otherInput: any): boolean { + if (super.matches(otherInput) === true) { + return true; + } + + if (otherInput) { + if (!(otherInput instanceof SideBySideEditorInput)) { + return false; + } + + const otherDiffInput = otherInput; + return this.details.matches(otherDiffInput.details) && this.master.matches(otherDiffInput.master); + } + + return false; + } + + public dispose(): void { + this._toUnbind = dispose(this._toUnbind); + super.dispose(); + } } export interface ITextEditorModel extends IEditorModel { @@ -785,14 +853,14 @@ export function getOutOfWorkspaceEditorResources(editorGroupService: IEditorGrou /** * Returns the object as IFileEditorInput only if it matches the signature. */ -export function asFileEditorInput(obj: any, supportDiff?: boolean): IFileEditorInput { +export function asFileEditorInput(obj: any, supportSideBySide?: boolean): IFileEditorInput { if (!obj) { return null; } - // Check for diff if we are asked to - if (supportDiff && obj instanceof BaseDiffEditorInput) { - obj = (obj).modifiedInput; + // Check for side by side if we are asked to + if (supportSideBySide && obj instanceof SideBySideEditorInput) { + obj = (obj).master; } const i = obj; diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index f42f61d3c86..c29d5be7dc6 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -6,77 +6,33 @@ import nls = require('vs/nls'); import { TPromise } from 'vs/base/common/winjs.base'; -import { once } from 'vs/base/common/event'; import URI from 'vs/base/common/uri'; import { getPathLabel, IWorkspaceProvider } from 'vs/base/common/labels'; -import { EditorModel, EditorInput, BaseDiffEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; +import { EditorModel, EditorInput, SideBySideEditorInput, TEXT_DIFF_EDITOR_ID, BINARY_DIFF_EDITOR_ID } from 'vs/workbench/common/editor'; import { BaseTextEditorModel } from 'vs/workbench/common/editor/textEditorModel'; import { DiffEditorModel } from 'vs/workbench/common/editor/diffEditorModel'; import { TextDiffEditorModel } from 'vs/workbench/common/editor/textDiffEditorModel'; -import { IDisposable, dispose } from 'vs/base/common/lifecycle'; /** * The base editor input for the diff editor. It is made up of two editor inputs, the original version * and the modified version. */ -export class DiffEditorInput extends BaseDiffEditorInput { +export class DiffEditorInput extends SideBySideEditorInput { public static ID = 'workbench.editors.diffEditorInput'; - private _toUnbind: IDisposable[]; - private name: string; - private description: string; private cachedModel: DiffEditorModel; - private forceOpenAsBinary: boolean; - - constructor(name: string, description: string, originalInput: EditorInput, modifiedInput: EditorInput, forceOpenAsBinary?: boolean) { - super(originalInput, modifiedInput); - - this.name = name; - this.description = description; - this.forceOpenAsBinary = forceOpenAsBinary; - - this._toUnbind = []; - - this.registerListeners(); - } - - private registerListeners(): void { - - // When the original or modified input gets disposed, dispose this diff editor input - const onceOriginalDisposed = once(this.originalInput.onDispose); - this._toUnbind.push(onceOriginalDisposed(() => { - if (!this.isDisposed()) { - this.dispose(); - } - })); - - const onceModifiedDisposed = once(this.modifiedInput.onDispose); - this._toUnbind.push(onceModifiedDisposed(() => { - if (!this.isDisposed()) { - this.dispose(); - } - })); - - // Reemit some events from the modified side to the outside - this._toUnbind.push(this.modifiedInput.onDidChangeDirty(() => this._onDidChangeDirty.fire())); - this._toUnbind.push(this.modifiedInput.onDidChangeLabel(() => this._onDidChangeLabel.fire())); - } - - public get toUnbind() { - return this._toUnbind; - } public getTypeId(): string { return DiffEditorInput.ID; } - public getName(): string { - return this.name; + get originalInput(): EditorInput { + return this.details; } - public getDescription(): string { - return this.description; + get modifiedInput(): EditorInput { + return this.master; } public resolve(refresh?: boolean): TPromise { @@ -130,30 +86,7 @@ export class DiffEditorInput extends BaseDiffEditorInput { }); } - public supportsSplitEditor(): boolean { - return false; - } - - public matches(otherInput: any): boolean { - if (super.matches(otherInput) === true) { - return true; - } - - if (otherInput) { - if (!(otherInput instanceof DiffEditorInput)) { - return false; - } - - const otherDiffInput = otherInput; - return this.originalInput.matches(otherDiffInput.originalInput) && this.modifiedInput.matches(otherDiffInput.modifiedInput); - } - - return false; - } - public dispose(): void { - this._toUnbind = dispose(this._toUnbind); - // Free the diff editor model but do not propagate the dispose() call to the two inputs // We never created the two inputs (original and modified) so we can not dispose // them without sideeffects. @@ -161,8 +94,6 @@ export class DiffEditorInput extends BaseDiffEditorInput { this.cachedModel.dispose(); this.cachedModel = null; } - - super.dispose(); } } diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 7fb0b31970a..82d2eeb2721 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -6,7 +6,7 @@ 'use strict'; import Event, { Emitter, once } from 'vs/base/common/event'; -import { IEditorRegistry, Extensions, EditorInput, getResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, IGroupEvent, GroupIdentifier, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, EditorOpenPositioning } from 'vs/workbench/common/editor'; +import { IEditorRegistry, Extensions, EditorInput, getResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, IGroupEvent, GroupIdentifier, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, EditorOpenPositioning, SideBySideEditorInput } from 'vs/workbench/common/editor'; import URI from 'vs/base/common/uri'; import { IStorageService, StorageScope } from 'vs/platform/storage/common/storage'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -16,7 +16,6 @@ import { dispose, IDisposable } from 'vs/base/common/lifecycle'; import { Registry } from 'vs/platform/platform'; import { Position, Direction } from 'vs/platform/editor/common/editor'; import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; export interface GroupEvent extends IGroupEvent { editor: EditorInput; @@ -1135,9 +1134,9 @@ export class EditorStacksModel implements IEditorStacksModel { if (!this.isOpen(editor)) { editor.close(); - // Also take care of diff editor inputs that wrap around 2 editors - if (editor instanceof DiffEditorInput) { - [editor.originalInput, editor.modifiedInput].forEach(editor => { + // Also take care of side by side editor inputs that wrap around 2 editors + if (editor instanceof SideBySideEditorInput) { + [editor.master, editor.details].forEach(editor => { if (!this.isOpen(editor)) { editor.close(); } diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index 2463664c3d2..10003bec050 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -18,7 +18,6 @@ import strings = require('vs/base/common/strings'); import { Event, EventType as CommonEventType } from 'vs/base/common/events'; import severity from 'vs/base/common/severity'; import diagnostics = require('vs/base/common/diagnostics'); -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { Action, IAction } from 'vs/base/common/actions'; import { MessageType, IInputValidator } from 'vs/base/browser/ui/inputbox/inputBox'; import { ITree, IHighlightEvent } from 'vs/base/parts/tree/browser/tree'; @@ -50,6 +49,7 @@ import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace import { KeyMod, KeyCode } from 'vs/base/common/keyCodes'; import { Keybinding } from 'vs/base/common/keybinding'; import { Selection } from 'vs/editor/common/core/selection'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; export interface IEditableData { action: IAction; @@ -1430,10 +1430,11 @@ export abstract class BaseSaveFileAction extends BaseActionWithErrorReporting { let selectionOfSource: Selection; const activeEditor = this.editorService.getActiveEditor(); - if (activeEditor instanceof BaseTextEditor) { + const editor = getCodeEditor(activeEditor); + if (editor) { const activeResource = getUntitledOrFileResource(activeEditor.input, true); if (activeResource && activeResource.toString() === source.toString()) { - selectionOfSource = activeEditor.getSelection(); + selectionOfSource = editor.getSelection(); } } diff --git a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts index 36c16325fa8..5f113639f9f 100644 --- a/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts +++ b/src/vs/workbench/parts/files/common/editors/fileEditorTracker.ts @@ -8,10 +8,9 @@ import { IWorkbenchContribution } from 'vs/workbench/common/contributions'; import errors = require('vs/base/common/errors'); import URI from 'vs/base/common/uri'; import paths = require('vs/base/common/paths'); -import { DiffEditorInput } from 'vs/workbench/common/editor/diffEditorInput'; import { IEditor } from 'vs/editor/common/editorCommon'; import { IEditor as IBaseEditor } from 'vs/platform/editor/common/editor'; -import { EditorInput, IEditorStacksModel } from 'vs/workbench/common/editor'; +import { EditorInput, IEditorStacksModel, SideBySideEditorInput } from 'vs/workbench/common/editor'; import { BINARY_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { LocalFileChangeEvent, ITextFileService, ModelState } from 'vs/workbench/services/textfile/common/textfiles'; import { FileChangeType, FileChangesEvent, EventType as CommonFileEventType } from 'vs/platform/files/common/files'; @@ -127,16 +126,16 @@ export class FileEditorTracker implements IWorkbenchContribution { group.getEditors().forEach(input => { if (input instanceof FileEditorInput) { inputs.push(input); - } else if (input instanceof DiffEditorInput) { - const originalInput = input.originalInput; - const modifiedInput = input.modifiedInput; + } else if (input instanceof SideBySideEditorInput) { + const master = input.master; + const details = input.details; - if (originalInput instanceof FileEditorInput) { - inputs.push(originalInput); + if (master instanceof FileEditorInput) { + inputs.push(master); } - if (modifiedInput instanceof FileEditorInput) { - inputs.push(modifiedInput); + if (details instanceof FileEditorInput) { + inputs.push(details); } } }); @@ -174,8 +173,8 @@ export class FileEditorTracker implements IWorkbenchContribution { const editors = this.editorService.getVisibleEditors(); editors.forEach(editor => { let input = editor.input; - if (input instanceof DiffEditorInput) { - input = this.getMatchingFileEditorInputFromDiff(input, e); + if (input instanceof SideBySideEditorInput) { + input = this.getMatchingFileEditorInputFromSideBySide(input, e); } // File Editor Input @@ -231,25 +230,25 @@ export class FileEditorTracker implements IWorkbenchContribution { return false; } - // Support diff editor input too - if (input instanceof DiffEditorInput) { - input = (input).modifiedInput; + // Support side by side editor input too + if (input instanceof SideBySideEditorInput) { + input = (input).master; } return input instanceof FileEditorInput && input.getResource().toString() === resource.toString(); } - private getMatchingFileEditorInputFromDiff(input: DiffEditorInput, e: FileChangesEvent): FileEditorInput { + private getMatchingFileEditorInputFromSideBySide(input: SideBySideEditorInput, e: FileChangesEvent): FileEditorInput { - // First try modifiedInput - const modifiedInput = input.modifiedInput; - const res = this.getMatchingFileEditorInputFromInput(modifiedInput, e); + // First try master + const master = input.master; + const res = this.getMatchingFileEditorInputFromInput(master, e); if (res) { return res; } - // Second try originalInput - return this.getMatchingFileEditorInputFromInput(input.originalInput, e); + // Second try details + return this.getMatchingFileEditorInputFromInput(input.details, e); } private getMatchingFileEditorInputFromInput(input: EditorInput, e: FileChangesEvent): FileEditorInput { diff --git a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts index e64a0cbf64b..943a95785ff 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesEditor.ts @@ -150,6 +150,7 @@ export class DefaultPreferencesEditor extends BaseEditor { options.folding = false; options.renderWhitespace = 'none'; options.wrappingColumn = 0; + options.overviewRulerLanes = 0; } return options; } diff --git a/src/vs/workbench/parts/preferences/browser/preferencesService.ts b/src/vs/workbench/parts/preferences/browser/preferencesService.ts index bdd0e78647e..672890bd90d 100644 --- a/src/vs/workbench/parts/preferences/browser/preferencesService.ts +++ b/src/vs/workbench/parts/preferences/browser/preferencesService.ts @@ -11,7 +11,7 @@ import { LinkedMap as Map } from 'vs/base/common/map'; import * as labels from 'vs/base/common/labels'; import { Disposable } from 'vs/base/common/lifecycle'; import { parseTree, findNodeAtLocation } from 'vs/base/common/json'; -import { asFileEditorInput, EditorInput } from 'vs/workbench/common/editor'; +import { asFileEditorInput, SideBySideEditorInput, EditorInput } from 'vs/workbench/common/editor'; import { StringEditorInput } from 'vs/workbench/common/editor/stringEditorInput'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IWorkspaceContextService } from 'vs/platform/workspace/common/workspace'; @@ -49,7 +49,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic _serviceBrand: any; - private configurationTarget: ConfigurationTarget = null; private defaultEditorModels: Map; constructor( @@ -69,12 +68,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic ) { super(); this.defaultEditorModels = new Map(); - this._register(this.editorGroupService.onEditorsChanged(() => { - const configurationTarget = this.getConfigurationTargetForCurrentActiveEditor(); - if (configurationTarget !== null) { - this.configurationTarget = configurationTarget; - } - })); } createDefaultSettingsModel(): TPromise { @@ -143,38 +136,25 @@ export class PreferencesService extends Disposable implements IPreferencesServic }); } - private openEditableSettings(configurationTarget: ConfigurationTarget, showVisibleEditor: boolean = false): TPromise { + private openEditableSettings(configurationTarget: ConfigurationTarget): TPromise { const emptySettingsContents = this.getEmptyEditableSettingsContent(configurationTarget); const settingsResource = this.getEditableSettingsURI(configurationTarget); - if (showVisibleEditor) { - if (this.isEditorFor(this.editorService.getActiveEditor(), configurationTarget)) { - return TPromise.wrap(this.editorService.getActiveEditor()); - } - const [editableSettingsEditor] = this.editorService.getVisibleEditors().filter(editor => this.isEditorFor(editor, configurationTarget)); - if (editableSettingsEditor) { - return TPromise.wrap(editableSettingsEditor); - } - } return this.createIfNotExists(settingsResource, emptySettingsContents).then(() => this.editorService.openEditor({ resource: settingsResource, options: { pinned: true } })); } - public copyConfiguration(configurationValue: IConfigurationValue, configurationTarget: ConfigurationTarget = null): void { + public copyConfiguration(configurationValue: IConfigurationValue): void { + const configurationTarget = this.getConfigurationTargetForCurrentActiveEditor(); if (configurationTarget !== null) { - this.configurationTarget = configurationTarget; - } - if (this.configurationTarget !== null) { this.telemetryService.publicLog('defaultSettingsActions.copySetting', { userConfigurationKeys: [configurationValue.key] }); - this.openEditableSettings(this.configurationTarget, true).then(editor => { - const editorControl = editor.getControl(); - this.configurationEditingService.writeConfiguration(this.configurationTarget, configurationValue, { writeToBuffer: true, autoSave: true }) - .then(() => { - editorControl.focus(); - editorControl.setSelection(this.getSelectionRange(configurationValue.key, editorControl.getModel())); - }, error => this.messageService.show(Severity.Error, error)); - }); + const editorControl = this.editorService.getActiveEditor().getControl(); + this.configurationEditingService.writeConfiguration(configurationTarget, configurationValue, { writeToBuffer: true, autoSave: true }) + .then(() => { + editorControl.focus(); + editorControl.setSelection(this.getSelectionRange(configurationValue.key, editorControl.getModel())); + }, error => this.messageService.show(Severity.Error, error)); } } @@ -187,11 +167,6 @@ export class PreferencesService extends Disposable implements IPreferencesServic return TPromise.wrap(null); } - private isEditorFor(editor: IEditor, configurationTarget: ConfigurationTarget): boolean { - const fileEditorInput = editor ? asFileEditorInput(editor.input) : null; - return !!fileEditorInput && fileEditorInput.getResource().fsPath === this.getEditableSettingsURI(configurationTarget).fsPath; - } - private getEmptyEditableSettingsContent(configurationTarget: ConfigurationTarget): string { switch (configurationTarget) { case ConfigurationTarget.USER: @@ -246,19 +221,12 @@ export class PreferencesService extends Disposable implements IPreferencesServic return this.openEditableSettings(configurationTarget).then(() => null); } - private openTwoEditors(leftHandDefaultInput: EditorInput, editableResource: URI, defaultEditableContents: string): TPromise { + private openTwoEditors(leftHandDefaultInput: EditorInput, editableResource: URI, defaultEditableContents: string): TPromise { // Create as needed and open in editor return this.createIfNotExists(editableResource, defaultEditableContents).then(() => { return this.editorService.createInput({ resource: editableResource }).then(typedRightHandEditableInput => { - const editors = [ - { input: leftHandDefaultInput, position: Position.ONE, options: { pinned: true } }, - { input: typedRightHandEditableInput, position: Position.TWO, options: { pinned: true } } - ]; - - return this.editorService.openEditors(editors).then(result => { - this.editorGroupService.focusGroup(Position.TWO); - return result; - }); + const sideBySideInput = new SideBySideEditorInput(typedRightHandEditableInput.getName(), typedRightHandEditableInput.getDescription(), leftHandDefaultInput, typedRightHandEditableInput); + return this.editorService.openEditor(sideBySideInput); }); }); } @@ -278,7 +246,7 @@ export class PreferencesService extends Disposable implements IPreferencesServic private getConfigurationTargetForCurrentActiveEditor(): ConfigurationTarget { const activeEditor = this.editorService.getActiveEditor(); if (activeEditor) { - const editorInput = asFileEditorInput(activeEditor.input); + const editorInput = asFileEditorInput(activeEditor.input, true); if (editorInput) { return this.getConfigurationTarget(editorInput.getResource()); } diff --git a/src/vs/workbench/parts/preferences/common/preferences.ts b/src/vs/workbench/parts/preferences/common/preferences.ts index b560b02dec5..f46009e5549 100644 --- a/src/vs/workbench/parts/preferences/common/preferences.ts +++ b/src/vs/workbench/parts/preferences/common/preferences.ts @@ -8,7 +8,7 @@ import { TPromise } from 'vs/base/common/winjs.base'; import { LinkedMap as Map } from 'vs/base/common/map'; import { IRange } from 'vs/editor/common/editorCommon'; import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; -import { IConfigurationValue, ConfigurationTarget } from 'vs/workbench/services/configuration/common/configurationEditing'; +import { IConfigurationValue } from 'vs/workbench/services/configuration/common/configurationEditing'; import { RawContextKey } from 'vs/platform/contextkey/common/contextkey'; export interface ISettingsGroup { @@ -68,7 +68,7 @@ export interface IPreferencesService { openWorkspaceSettings(): TPromise; openGlobalKeybindingSettings(): TPromise; - copyConfiguration(configurationValue: IConfigurationValue, configurationTarget?: ConfigurationTarget): void; + copyConfiguration(configurationValue: IConfigurationValue): void; } export const CONTEXT_DEFAULT_SETTINGS_EDITOR = new RawContextKey('defaultSettingsEditor', false); diff --git a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts index 816f39c9bfa..8c421c897e9 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoLineHandler.ts @@ -12,11 +12,11 @@ import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/comm import { QuickOpenModel } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { KeyMod } from 'vs/base/common/keyCodes'; import { QuickOpenHandler, EditorQuickOpenEntry, QuickOpenAction } from 'vs/workbench/browser/quickopen'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IRange, IEditorViewState, ITextModel, IDiffEditorModel } from 'vs/editor/common/editorCommon'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickOpenService'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; export const GOTO_LINE_PREFIX = ':'; @@ -188,7 +188,7 @@ export class GotoLineHandler extends QuickOpenHandler { } public canRun(): boolean | string { - let canRun = this.editorService.getActiveEditor() instanceof BaseTextEditor; + let canRun = getCodeEditor(this.editorService.getActiveEditor()) !== null; return canRun ? true : nls.localize('cannotRunGotoLine', "Open a text file first to go to a line"); } diff --git a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts index 03723ac122c..3be447ebe8b 100644 --- a/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts +++ b/src/vs/workbench/parts/quickopen/browser/gotoSymbolHandler.ts @@ -14,7 +14,6 @@ import strings = require('vs/base/common/strings'); import { IEntryRunContext, Mode, IAutoFocus } from 'vs/base/parts/quickopen/common/quickOpen'; import { QuickOpenModel, IHighlight } from 'vs/base/parts/quickopen/browser/quickOpenModel'; import { QuickOpenHandler, EditorQuickOpenEntryGroup, QuickOpenAction } from 'vs/workbench/browser/quickopen'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import filters = require('vs/base/common/filters'); import { KeyMod } from 'vs/base/common/keyCodes'; import { IEditor, IModelDecorationsChangeAccessor, OverviewRulerLane, IModelDeltaDecoration, IRange, IModel, ITokenizedModel, IDiffEditorModel, IEditorViewState } from 'vs/editor/common/editorCommon'; @@ -23,6 +22,7 @@ import { IQuickOpenService } from 'vs/workbench/services/quickopen/common/quickO import { Position, IEditorInput, ITextEditorOptions } from 'vs/platform/editor/common/editor'; import { getDocumentSymbols } from 'vs/editor/contrib/quickOpen/common/quickOpen'; import { DocumentSymbolProviderRegistry, SymbolInformation, SymbolKind } from 'vs/editor/common/modes'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; export const GOTO_SYMBOL_PREFIX = '@'; export const SCOPE_PREFIX = ':'; @@ -413,9 +413,8 @@ export class GotoSymbolHandler extends QuickOpenHandler { public canRun(): boolean | string { let canRun = false; - let editor = this.editorService.getActiveEditor(); - if (editor instanceof BaseTextEditor) { - let editorControl = editor.getControl(); + const editorControl: IEditor = getCodeEditor(this.editorService.getActiveEditor()); + if (editorControl) { let model = editorControl.getModel(); if (model && (model).modified && (model).original) { model = (model).modified; // Support for diff editor models @@ -426,7 +425,7 @@ export class GotoSymbolHandler extends QuickOpenHandler { } } - return canRun ? true : editor instanceof BaseTextEditor ? nls.localize('cannotRunGotoSymbolInFile', "No symbol information for the file") : nls.localize('cannotRunGotoSymbol', "Open a text file first to go to a symbol"); + return canRun ? true : editorControl !== null ? nls.localize('cannotRunGotoSymbolInFile', "No symbol information for the file") : nls.localize('cannotRunGotoSymbol', "Open a text file first to go to a symbol"); } public getAutoFocus(searchValue: string): IAutoFocus { @@ -470,9 +469,8 @@ export class GotoSymbolHandler extends QuickOpenHandler { } private doGetActiveOutline(): TPromise { - let editor = this.editorService.getActiveEditor(); - if (editor instanceof BaseTextEditor) { - let editorControl = editor.getControl(); + const editorControl: IEditor = getCodeEditor(this.editorService.getActiveEditor()); + if (editorControl) { let model = editorControl.getModel(); if (model && (model).modified && (model).original) { model = (model).modified; // Support for diff editor models diff --git a/src/vs/workbench/services/editor/browser/editorService.ts b/src/vs/workbench/services/editor/browser/editorService.ts index 0f4c79ef8aa..18c397f5c6f 100644 --- a/src/vs/workbench/services/editor/browser/editorService.ts +++ b/src/vs/workbench/services/editor/browser/editorService.ts @@ -10,10 +10,9 @@ import network = require('vs/base/common/network'); import { Registry } from 'vs/platform/platform'; import { basename, dirname } from 'vs/base/common/paths'; import { BaseEditor } from 'vs/workbench/browser/parts/editor/baseEditor'; -import { EditorInput, EditorOptions, IFileEditorInput, TextEditorOptions, IEditorRegistry, Extensions } from 'vs/workbench/common/editor'; +import { EditorInput, EditorOptions, IFileEditorInput, TextEditorOptions, IEditorRegistry, Extensions, SideBySideEditorInput } 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, IEditorOptions, ITextEditorOptions, Position, Direction, IEditor, IResourceInput } from 'vs/platform/editor/common/editor'; @@ -61,7 +60,7 @@ export class WorkbenchEditorService implements IWorkbenchEditorService { return this.editorPart.getVisibleEditors(); } - public isVisible(input: IEditorInput, includeDiff: boolean): boolean { + public isVisible(input: IEditorInput, includeSideBySide: boolean): boolean { if (!input) { return false; } @@ -75,9 +74,9 @@ export class WorkbenchEditorService implements IWorkbenchEditorService { return true; } - if (includeDiff && editor.input instanceof DiffEditorInput) { - const diffInput = editor.input; - return input.matches(diffInput.modifiedInput) || input.matches(diffInput.originalInput); + if (includeSideBySide && editor.input instanceof SideBySideEditorInput) { + const sideBySideInput = editor.input; + return input.matches(sideBySideInput.master) || input.matches(sideBySideInput.details); } return false; diff --git a/src/vs/workbench/services/history/browser/history.ts b/src/vs/workbench/services/history/browser/history.ts index 5ba65a02a22..fdc9461bca2 100644 --- a/src/vs/workbench/services/history/browser/history.ts +++ b/src/vs/workbench/services/history/browser/history.ts @@ -12,9 +12,9 @@ import nls = require('vs/nls'); import labels = require('vs/base/common/labels'); import URI from 'vs/base/common/uri'; import product from 'vs/platform/product'; +import * as editorCommon from 'vs/editor/common/editorCommon'; import { IEditor as IBaseEditor, IEditorInput, ITextEditorOptions, IResourceInput } from 'vs/platform/editor/common/editor'; import { EditorInput, IGroupEvent, IEditorRegistry, Extensions, asFileEditorInput, IEditorGroup } from 'vs/workbench/common/editor'; -import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; import { IWorkbenchEditorService } from 'vs/workbench/services/editor/common/editorService'; import { IEventService } from 'vs/platform/event/common/event'; import { IHistoryService } from 'vs/workbench/services/history/common/history'; @@ -33,6 +33,7 @@ import { IEnvironmentService } from 'vs/platform/environment/common/environment' import { IIntegrityService } from 'vs/platform/integrity/common/integrity'; import { ITitleService } from 'vs/workbench/services/title/common/titleService'; import { IWindowService } from 'vs/platform/windows/common/windows'; +import { getCodeEditor } from 'vs/editor/common/services/codeEditorService'; /** * Stores the selection & view state of an editor and allows to compare it to other selection states. @@ -150,8 +151,8 @@ export abstract class BaseHistoryService { } // Apply listener for selection changes if this is a text editor - if (activeEditor instanceof BaseTextEditor) { - const control = activeEditor.getControl(); + const control = getCodeEditor(activeEditor); + if (control) { this.activeEditorListeners.push(control.onDidChangeCursorPosition(event => { this.handleEditorSelectionChangeEvent(activeEditor); })); @@ -467,8 +468,9 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic return; // while we open an editor due to a navigation, we do not want to update our stack } - if (editor instanceof BaseTextEditor && editor.input) { - this.handleTextEditorEvent(editor); + const control = getCodeEditor(editor); + if (control && editor.input) { + this.handleTextEditorEvent(editor, control); return; } @@ -480,14 +482,14 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic } } - private handleTextEditorEvent(editor: BaseTextEditor): void { - const stateCandidate = new EditorState(editor.input, editor.getSelection()); + private handleTextEditorEvent(editor: IBaseEditor, editorControl: editorCommon.IEditor): void { + const stateCandidate = new EditorState(editor.input, editorControl.getSelection()); if (!this.currentFileEditorState || this.currentFileEditorState.justifiesNewPushState(stateCandidate)) { this.currentFileEditorState = stateCandidate; let options: ITextEditorOptions; - const selection = editor.getSelection(); + const selection = editorControl.getSelection(); if (selection) { options = { selection: { startLineNumber: selection.startLineNumber, startColumn: selection.startColumn }