diff --git a/src/vs/workbench/browser/parts/editor/textEditor.ts b/src/vs/workbench/browser/parts/editor/textEditor.ts index d0f89123f6a41..18033e72569cd 100644 --- a/src/vs/workbench/browser/parts/editor/textEditor.ts +++ b/src/vs/workbench/browser/parts/editor/textEditor.ts @@ -61,7 +61,7 @@ export abstract class BaseTextEditor extends BaseEditor { @IWorkbenchThemeService protected themeService: IWorkbenchThemeService, @IModeService private modeService: IModeService, @ITextFileService private textFileService: ITextFileService, - @IEditorGroupService private editorGroupService: IEditorGroupService + @IEditorGroupService protected editorGroupService: IEditorGroupService ) { super(id, telemetryService, themeService); diff --git a/src/vs/workbench/common/editor.ts b/src/vs/workbench/common/editor.ts index c89cfe4455b96..c4013c04cad7c 100644 --- a/src/vs/workbench/common/editor.ts +++ b/src/vs/workbench/common/editor.ts @@ -844,6 +844,8 @@ export interface IStacksModelChangeEvent { export interface IEditorStacksModel { onModelChanged: Event; + + onWillCloseEditor: Event; onEditorClosed: Event; groups: IEditorGroup[]; diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index 00e7d3722c29c..a81bd7a2fb1b7 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -703,6 +703,7 @@ export class EditorStacksModel implements IEditorStacksModel { private _onEditorDirty: Emitter; private _onEditorLabelChange: Emitter; private _onEditorOpened: Emitter; + private _onWillCloseEditor: Emitter; private _onEditorClosed: Emitter; private _onModelChanged: Emitter; @@ -728,9 +729,10 @@ export class EditorStacksModel implements IEditorStacksModel { this._onEditorDirty = new Emitter(); this._onEditorLabelChange = new Emitter(); this._onEditorOpened = new Emitter(); + this._onWillCloseEditor = new Emitter(); this._onEditorClosed = new Emitter(); - this.toDispose.push(this._onGroupOpened, this._onGroupClosed, this._onGroupActivated, this._onGroupDeactivated, this._onGroupMoved, this._onGroupRenamed, this._onModelChanged, this._onEditorDisposed, this._onEditorDirty, this._onEditorLabelChange, this._onEditorClosed); + this.toDispose.push(this._onGroupOpened, this._onGroupClosed, this._onGroupActivated, this._onGroupDeactivated, this._onGroupMoved, this._onGroupRenamed, this._onModelChanged, this._onEditorDisposed, this._onEditorDirty, this._onEditorLabelChange, this._onEditorClosed, this._onWillCloseEditor); this.registerListeners(); } @@ -783,6 +785,10 @@ export class EditorStacksModel implements IEditorStacksModel { return this._onEditorOpened.event; } + public get onWillCloseEditor(): Event { + return this._onWillCloseEditor.event; + } + public get onEditorClosed(): Event { return this._onEditorClosed.event; } @@ -1155,6 +1161,7 @@ export class EditorStacksModel implements IEditorStacksModel { unbind.push(group.onEditorStateChanged(editor => this._onModelChanged.fire({ group, editor }))); unbind.push(group.onEditorOpened(editor => this._onEditorOpened.fire({ editor, group }))); unbind.push(group.onEditorClosed(event => { + this._onWillCloseEditor.fire({ editor: event.editor, group }); this.handleOnEditorClosed(event); this._onEditorClosed.fire(event); })); diff --git a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts index ec7f682e99c50..c624ee237af74 100644 --- a/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts +++ b/src/vs/workbench/parts/files/browser/editors/textFileEditor.ts @@ -14,7 +14,7 @@ import { Action } from 'vs/base/common/actions'; import { VIEWLET_ID, TEXT_FILE_EDITOR_ID } from 'vs/workbench/parts/files/common/files'; import { ITextFileEditorModel, ITextFileService } from 'vs/workbench/services/textfile/common/textfiles'; import { BaseTextEditor } from 'vs/workbench/browser/parts/editor/textEditor'; -import { EditorOptions, TextEditorOptions } from 'vs/workbench/common/editor'; +import { EditorOptions, TextEditorOptions, IEditorIdentifier } from 'vs/workbench/common/editor'; import { BinaryEditorModel } from 'vs/workbench/common/editor/binaryEditorModel'; import { FileEditorInput } from 'vs/workbench/parts/files/common/editors/fileEditorInput'; import { ExplorerViewlet } from 'vs/workbench/parts/files/browser/explorerViewlet'; @@ -58,6 +58,9 @@ export class TextFileEditor extends BaseTextEditor { // Clear view state for deleted files this.toUnbind.push(this.fileService.onFileChanges(e => this.onFilesChanged(e))); + + // React to editors closing to preserve view state + this.toUnbind.push(editorGroupService.getStacksModel().onWillCloseEditor(e => this.onWillCloseEditor(e))); } private onFilesChanged(e: FileChangesEvent): void { @@ -67,6 +70,12 @@ export class TextFileEditor extends BaseTextEditor { } } + private onWillCloseEditor(e: IEditorIdentifier): void { + if (e.editor === this.input && this.position === this.editorGroupService.getStacksModel().positionOfGroup(e.group)) { + this.doSaveTextEditorViewState(this.input); + } + } + public getTitle(): string { return this.input ? this.input.getName() : nls.localize('textFileEditor', "Text File Editor"); } @@ -104,9 +113,7 @@ export class TextFileEditor extends BaseTextEditor { } // Remember view settings if input changes - if (oldInput) { - this.saveTextEditorViewState(oldInput.getResource().toString()); - } + this.doSaveTextEditorViewState(oldInput); // Different Input (Reload) return input.resolve(true).then(resolvedModel => { @@ -224,9 +231,7 @@ export class TextFileEditor extends BaseTextEditor { public clearInput(): void { // Keep editor view state in settings to restore when coming back - if (this.input) { - this.saveTextEditorViewState(this.input.getResource().toString()); - } + this.doSaveTextEditorViewState(this.input); // Clear Model this.getControl().setModel(null); @@ -238,11 +243,15 @@ export class TextFileEditor extends BaseTextEditor { public shutdown(): void { // Save View State - if (this.input) { - this.saveTextEditorViewState(this.input.getResource().toString()); - } + this.doSaveTextEditorViewState(this.input); // Call Super super.shutdown(); } + + private doSaveTextEditorViewState(input: FileEditorInput): void { + if (input && !input.isDisposed()) { + this.saveTextEditorViewState(input.getResource().toString()); + } + } } \ No newline at end of file diff --git a/src/vs/workbench/test/browser/editorStacksModel.test.ts b/src/vs/workbench/test/browser/editorStacksModel.test.ts index 9cc08d0d96303..0593fc06d5410 100644 --- a/src/vs/workbench/test/browser/editorStacksModel.test.ts +++ b/src/vs/workbench/test/browser/editorStacksModel.test.ts @@ -7,7 +7,7 @@ import * as assert from 'assert'; import { EditorStacksModel, EditorGroup, GroupEvent } from 'vs/workbench/common/editor/editorStacksModel'; -import { EditorInput, IFileEditorInput, IEditorIdentifier, IEditorGroup, IStacksModelChangeEvent, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory } from 'vs/workbench/common/editor'; +import { EditorInput, IFileEditorInput, IEditorIdentifier, IEditorGroup, IStacksModelChangeEvent, IEditorRegistry, Extensions as EditorExtensions, IEditorInputFactory, IGroupEvent } from 'vs/workbench/common/editor'; import URI from 'vs/base/common/uri'; import { TestStorageService, TestLifecycleService, TestContextService } from 'vs/workbench/test/workbenchTestServices'; import { TestConfigurationService } from 'vs/platform/configuration/test/common/testConfigurationService'; @@ -47,6 +47,8 @@ interface ModelEvents { renamed: IEditorGroup[]; disposed: IEditorIdentifier[]; changed: IStacksModelChangeEvent[]; + editorClosed: IGroupEvent[]; + editorWillClose: IEditorIdentifier[]; } interface GroupEvents { @@ -66,7 +68,9 @@ function modelListener(model: EditorStacksModel): ModelEvents { moved: [], renamed: [], disposed: [], - changed: [] + changed: [], + editorClosed: [], + editorWillClose: [] }; model.onGroupOpened(g => modelEvents.opened.push(g)); @@ -76,6 +80,8 @@ function modelListener(model: EditorStacksModel): ModelEvents { model.onGroupRenamed(g => modelEvents.renamed.push(g)); model.onEditorDisposed(e => modelEvents.disposed.push(e)); model.onModelChanged(e => modelEvents.changed.push(e)); + model.onWillCloseEditor(e => modelEvents.editorWillClose.push(e)); + model.onEditorClosed(e => modelEvents.editorClosed.push(e)); return modelEvents; } @@ -1671,6 +1677,7 @@ suite('Editor Stacks Model', () => { test('Stack - Multiple Editors - Editor Disposed on Close', function () { const model = create(); + const events = modelListener(model); const group1 = model.openGroup('group1'); const group2 = model.openGroup('group2'); @@ -1687,6 +1694,13 @@ suite('Editor Stacks Model', () => { group1.closeEditor(input3); + assert.equal(events.editorClosed.length, 1); + assert.equal(events.editorClosed[0].editor, input3); + + assert.equal(events.editorWillClose.length, 1); + assert.equal(events.editorWillClose[0].editor, input3); + assert.equal(events.editorWillClose[0].group, group1); + assert.equal(input3.isDisposed(), true); group2.openEditor(input2, { pinned: true, active: true }); @@ -1695,18 +1709,38 @@ suite('Editor Stacks Model', () => { group1.closeEditor(input2); + assert.equal(events.editorClosed.length, 2); + assert.equal(events.editorClosed[1].editor, input2); + + assert.equal(events.editorWillClose.length, 2); + assert.equal(events.editorWillClose[1].editor, input2); + assert.equal(events.editorWillClose[1].group, group1); + assert.equal(input2.isDisposed(), false); group2.closeEditor(input2); + assert.equal(events.editorClosed.length, 3); + assert.equal(events.editorClosed[2].editor, input2); + + assert.equal(events.editorWillClose.length, 3); + assert.equal(events.editorWillClose[2].editor, input2); + assert.equal(events.editorWillClose[2].group, group2); + assert.equal(input2.isDisposed(), true); group1.closeAllEditors(); + assert.equal(events.editorClosed.length, 5); + assert.equal(events.editorWillClose.length, 5); + assert.equal(input4.isDisposed(), false); model.closeGroups(); + assert.equal(events.editorClosed.length, 7); + assert.equal(events.editorWillClose.length, 7); + assert.equal(input4.isDisposed(), true); });