diff --git a/src/vs/code/electron-main/menus.ts b/src/vs/code/electron-main/menus.ts index 74cb9d3e79a2b5341ef22dad731189a94731b81d..da6948e8a521b25cf617d7736b063bfa33836056 100644 --- a/src/vs/code/electron-main/menus.ts +++ b/src/vs/code/electron-main/menus.ts @@ -393,7 +393,7 @@ export class VSCodeMenu { } private setOpenRecentMenu(openRecentMenu: Electron.Menu): void { - openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedFile', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed File"), 'workbench.files.action.reopenClosedFile')); + openRecentMenu.append(this.createMenuItem(nls.localize({ key: 'miReopenClosedEditor', comment: ['&& denotes a mnemonic'] }, "&&Reopen Closed Editor"), 'workbench.action.reopenClosedEditor')); let recentList = this.getOpenedPathsList(); diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 2d6bcac5b878a8c7ec2da818b774907a07d1c077..9eb9db1b6ae114518247a94b2bd82ffbf210538e 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -34,7 +34,7 @@ import {KeyMod, KeyCode} from 'vs/base/common/keyCodes'; import {EditorStacksModel} from 'vs/workbench/common/editor/editorStacksModel'; import {CloseEditorsInGroupAction, CloseEditorsInOtherGroupsAction, CloseAllEditorsAction, MoveGroupLeftAction, MoveGroupRightAction, SplitEditorAction, PinEditorAction, UnpinEditorAction, CloseOtherEditorsInGroupAction, OpenToSideAction, NavigateBetweenGroupsAction, FocusFirstGroupAction, FocusSecondGroupAction, FocusThirdGroupAction, EvenGroupWidthsAction, MaximizeGroupAction, MinimizeOtherGroupsAction, FocusPreviousGroup, FocusNextGroup, - toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction + toEditorQuickOpenEntry, CloseLeftEditorsInGroupAction, CloseRightEditorsInGroupAction, OpenNextEditor, OpenPreviousEditor, NavigateBackwardsAction, NavigateForwardAction, ReopenClosedEditorAction } from 'vs/workbench/browser/parts/editor/editorActions'; // Register String Editor @@ -256,6 +256,7 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousEditor, Op primary: KeyMod.CtrlCmd | KeyCode.PageUp, mac: { primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.LeftArrow } }), 'View: Open Previous Editor', category); +registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedEditorAction, ReopenClosedEditorAction.ID, ReopenClosedEditorAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'View: Reopen Closed Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(PinEditorAction, PinEditorAction.ID, PinEditorAction.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.Enter) }), 'View: Pin Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(UnpinEditorAction, UnpinEditorAction.ID, UnpinEditorAction.LABEL), 'View: Unpin Editor', category); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllEditorsAction, CloseAllEditorsAction.ID, CloseAllEditorsAction.LABEL), 'View: Close All Editors', category); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index ec36ef0a1053f168b226ce4972d8c83f0405e31a..cb9c18f9c6aedf8443e07ad2c13102a469081682 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -9,7 +9,7 @@ import nls = require('vs/nls'); import types = require('vs/base/common/types'); import {Action} from 'vs/base/common/actions'; import {BaseEditor} from 'vs/workbench/browser/parts/editor/baseEditor'; -import {EditorInput, getUntitledOrFileResource, TextEditorOptions} from 'vs/workbench/common/editor'; +import {EditorInput, getUntitledOrFileResource, TextEditorOptions, EditorOptions} from 'vs/workbench/common/editor'; import {QuickOpenEntryGroup} from 'vs/base/parts/quickopen/browser/quickOpenModel'; import {EditorQuickOpenEntry, EditorQuickOpenEntryGroup, IEditorQuickOpenEntry} from 'vs/workbench/browser/quickopen'; import {IWorkbenchEditorService, GroupArrangement} from 'vs/workbench/services/editor/common/editorService'; @@ -808,3 +808,34 @@ export class NavigateBackwardsAction extends Action { return TPromise.as(null); } } + +export class ReopenClosedEditorAction extends Action { + + public static ID = 'workbench.action.reopenClosedEditor'; + public static LABEL = nls.localize('reopenClosedEditor', "Reopen Closed Editor"); + + constructor( + id: string, + label: string, + @IPartService private partService: IPartService, + @IWorkbenchEditorService private editorService: IWorkbenchEditorService + ) { + super(id, label); + } + + public run(): TPromise { + const stacks = this.editorService.getStacksModel(); + + // Find an editor that was closed and is currently not opened in the group + let lastClosedEditor = stacks.popLastClosedEditor(); + while (lastClosedEditor && stacks.activeGroup && stacks.activeGroup.indexOf(lastClosedEditor) >= 0) { + lastClosedEditor = stacks.popLastClosedEditor(); + } + + if (lastClosedEditor) { + this.editorService.openEditor(lastClosedEditor, EditorOptions.create({ pinned: true })); + } + + return TPromise.as(false); + } +} \ No newline at end of file diff --git a/src/vs/workbench/common/editor/editorStacksModel.ts b/src/vs/workbench/common/editor/editorStacksModel.ts index f696d7caebc944797b8558d41d836176686c5b3f..32c39ebfb6aa3bc66bef962f2f6c06e50af96449 100644 --- a/src/vs/workbench/common/editor/editorStacksModel.ts +++ b/src/vs/workbench/common/editor/editorStacksModel.ts @@ -26,7 +26,7 @@ export interface IEditorGroup { onEditorActivated: Event; onEditorOpened: Event; - onEditorClosed: Event; + onEditorClosed: Event; onEditorMoved: Event; onEditorPinned: Event; onEditorUnpinned: Event; @@ -41,6 +41,11 @@ export interface IEditorGroup { isPinned(editor: EditorInput): boolean; } +export interface IGroupEvent { + editor: EditorInput; + pinned: boolean; +} + export interface IEditorStacksModel { onGroupOpened: Event; @@ -60,6 +65,8 @@ export interface IEditorStacksModel { next(): IEditorIdentifier; previous(): IEditorIdentifier; + popLastClosedEditor(): EditorInput; + toString(): string; } @@ -110,7 +117,7 @@ export class EditorGroup implements IEditorGroup { private _onEditorActivated: Emitter; private _onEditorOpened: Emitter; - private _onEditorClosed: Emitter; + private _onEditorClosed: Emitter; private _onEditorMoved: Emitter; private _onEditorPinned: Emitter; private _onEditorUnpinned: Emitter; @@ -134,7 +141,7 @@ export class EditorGroup implements IEditorGroup { this._onEditorActivated = new Emitter(); this._onEditorOpened = new Emitter(); - this._onEditorClosed = new Emitter(); + this._onEditorClosed = new Emitter(); this._onEditorMoved = new Emitter(); this._onEditorPinned = new Emitter(); this._onEditorUnpinned = new Emitter(); @@ -167,7 +174,7 @@ export class EditorGroup implements IEditorGroup { return this._onEditorOpened.event; } - public get onEditorClosed(): Event { + public get onEditorClosed(): Event { return this._onEditorClosed.event; } @@ -313,8 +320,10 @@ export class EditorGroup implements IEditorGroup { } // Preview Editor closed + let pinned = true; if (this.matches(this.preview, editor)) { this.preview = null; + pinned = false; } // Close it @@ -324,7 +333,7 @@ export class EditorGroup implements IEditorGroup { this.splice(index, true); // Event - this.fireEvent(this._onEditorClosed, editor); + this.fireEvent(this._onEditorClosed, { editor, pinned }); } public closeEditors(except: EditorInput, direction?: Direction): void { @@ -434,9 +443,9 @@ export class EditorGroup implements IEditorGroup { return !this.matches(this.preview, editor); } - private fireEvent(emitter: Emitter, editor: EditorInput): void { - emitter.fire(editor); - this._onEditorChanged.fire(editor); + private fireEvent(emitter: Emitter, arg2: EditorInput | IGroupEvent): void { + emitter.fire(arg2); + this._onEditorChanged.fire(arg2 instanceof EditorInput ? arg2 : arg2.editor); } private splice(index: number, del: boolean, editor?: EditorInput): void { @@ -505,7 +514,7 @@ export class EditorGroup implements IEditorGroup { } public serialize(): ISerializedEditorGroup { - let registry = (Registry.as(Extensions.Editors)); + const registry = Registry.as(Extensions.Editors); // Serialize all editor inputs so that we can store them. // Editors that cannot be serialized need to be ignored @@ -539,7 +548,7 @@ export class EditorGroup implements IEditorGroup { } private deserialize(data: ISerializedEditorGroup): void { - let registry = (Registry.as(Extensions.Editors)); + const registry = Registry.as(Extensions.Editors); this._label = data.label; this.editors = data.editors.map(e => registry.getEditorInputFactory(e.id).deserialize(this.instantiationService, e.value)); @@ -556,6 +565,7 @@ export class EditorGroup implements IEditorGroup { interface ISerializedEditorStacksModel { groups: ISerializedEditorGroup[]; active: number; + lastClosed: ISerializedEditorInput[]; } export class EditorStacksModel implements IEditorStacksModel { @@ -569,6 +579,8 @@ export class EditorStacksModel implements IEditorStacksModel { private _activeGroup: EditorGroup; private groupToIdentifier: { [id: number]: EditorGroup }; + private recentlyClosedEditors: ISerializedEditorInput[]; + private _onGroupOpened: Emitter; private _onGroupClosed: Emitter; private _onGroupMoved: Emitter; @@ -587,6 +599,8 @@ export class EditorStacksModel implements IEditorStacksModel { this._groups = []; this.groupToIdentifier = Object.create(null); + this.recentlyClosedEditors = []; + this._onGroupOpened = new Emitter(); this._onGroupClosed = new Emitter(); this._onGroupActivated = new Emitter(); @@ -851,7 +865,8 @@ export class EditorStacksModel implements IEditorStacksModel { return { groups: serializableGroups, - active: serializableActiveIndex + active: serializableActiveIndex, + lastClosed: this.recentlyClosedEditors }; } @@ -888,6 +903,7 @@ export class EditorStacksModel implements IEditorStacksModel { this._groups = serialized.groups.map(s => this.doCreateGroup(s)); this._activeGroup = this._groups[serialized.active]; this._groups.forEach(g => this.groupToIdentifier[g.id] = g); + this.recentlyClosedEditors = serialized.lastClosed || []; } } @@ -928,15 +944,45 @@ export class EditorStacksModel implements IEditorStacksModel { // Funnel editor changes in the group through our event aggregator const l1 = group.onEditorChanged(e => this._onModelChanged.fire(group)); - const l2 = this.onGroupClosed(g => { + const l2 = group.onEditorClosed(e => this.onEditorClosed(e)); + const l3 = this.onGroupClosed(g => { if (g === group) { - dispose(l1, l2); + dispose(l1, l2, l3); } }); return group; } + public popLastClosedEditor(): EditorInput { + const registry = Registry.as(Extensions.Editors); + + let serializedEditor = this.recentlyClosedEditors.pop(); + if (serializedEditor) { + return registry.getEditorInputFactory(serializedEditor.id).deserialize(this.instantiationService, serializedEditor.value); + } + + return null; + } + + private onEditorClosed(event: IGroupEvent): void { + if (!event.pinned) { + return; // we only care about pinned editors + } + + const editor = event.editor; + const registry = Registry.as(Extensions.Editors); + + let factory = registry.getEditorInputFactory(editor.getId()); + if (factory) { + let value = factory.serialize(editor); + if (typeof value === 'string') { + this.recentlyClosedEditors.push({ id: editor.getId(), value }); + this.recentlyClosedEditors = this.recentlyClosedEditors.slice(0, 10); // upper bound of recently closed + } + } + } + private onShutdown(): void { this.save(); diff --git a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts index 18d9a1d5843afae8b435fdf49293a0a144a19f99..f365c5b82276fdce45e439820708af013f0c70a2 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.contribution.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.contribution.ts @@ -10,7 +10,7 @@ import {Action, IAction} from 'vs/base/common/actions'; import {ActionItem, BaseActionItem, Separator} from 'vs/base/browser/ui/actionbar/actionbar'; import {Scope, IActionBarRegistry, Extensions as ActionBarExtensions, ActionBarContributor} from 'vs/workbench/browser/actionBarRegistry'; import {IEditorInputActionContext, IEditorInputAction, EditorInputActionContributor} from 'vs/workbench/browser/parts/editor/baseEditor'; -import {FocusOpenEditorsView, FocusFilesExplorer, OpenPreviousWorkingFile, OpenNextWorkingFile, CloseAllFilesAction, CloseFileAction, CloseOtherFilesAction, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, keybindingForAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, ReopenClosedFileAction, OpenToSideAction, ShowActiveFileInExplorer} from 'vs/workbench/parts/files/browser/fileActions'; +import {FocusOpenEditorsView, FocusFilesExplorer, OpenPreviousWorkingFile, OpenNextWorkingFile, CloseAllFilesAction, CloseFileAction, CloseOtherFilesAction, GlobalCompareResourcesAction, GlobalNewFileAction, GlobalNewFolderAction, RevertFileAction, SaveFilesAction, SaveAllAction, SaveFileAction, keybindingForAction, MoveFileToTrashAction, TriggerRenameFileAction, PasteFileAction, CopyFileAction, SelectResourceForCompareAction, CompareResourcesAction, NewFolderAction, NewFileAction, OpenToSideAction, ShowActiveFileInExplorer} from 'vs/workbench/parts/files/browser/fileActions'; import {RevertLocalChangesAction, AcceptLocalChangesAction, ConflictResolutionDiffEditorInput} from 'vs/workbench/parts/files/browser/saveErrorHandler'; import {SyncActionDescriptor} from 'vs/platform/actions/common/actions'; import {IWorkbenchActionRegistry, Extensions as ActionExtensions} from 'vs/workbench/common/actionRegistry'; @@ -172,7 +172,6 @@ registry.registerWorkbenchAction(new SyncActionDescriptor(GlobalCompareResources registry.registerWorkbenchAction(new SyncActionDescriptor(CloseFileAction, CloseFileAction.ID, CloseFileAction.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_W) }), 'Files: Close File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseOtherFilesAction, CloseOtherFilesAction.ID, CloseOtherFilesAction.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_W) }), 'Files: Close Other Files', category); registry.registerWorkbenchAction(new SyncActionDescriptor(CloseAllFilesAction, CloseAllFilesAction.ID, CloseAllFilesAction.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyMod.CtrlCmd | KeyCode.KEY_W) }), 'Files: Close All Files', category); -registry.registerWorkbenchAction(new SyncActionDescriptor(ReopenClosedFileAction, ReopenClosedFileAction.ID, ReopenClosedFileAction.LABEL, { primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.KEY_T }), 'Reopen Closed File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenNextWorkingFile, OpenNextWorkingFile.ID, OpenNextWorkingFile.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.DownArrow) }), 'Files: Open Next Working File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(OpenPreviousWorkingFile, OpenPreviousWorkingFile.ID, OpenPreviousWorkingFile.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.UpArrow) }), 'Files: Open Previous Working File', category); registry.registerWorkbenchAction(new SyncActionDescriptor(FocusOpenEditorsView, FocusOpenEditorsView.ID, FocusOpenEditorsView.LABEL, { primary: KeyMod.chord(KeyMod.CtrlCmd | KeyCode.KEY_K, KeyCode.KEY_E) }), 'Files: Focus on Working Files', category); diff --git a/src/vs/workbench/parts/files/browser/fileActions.ts b/src/vs/workbench/parts/files/browser/fileActions.ts index b06db7546a86969b87f8ae08c950fdab76fa3b11..0935b428aeaa721e5efbddd1c48493a05fff5584 100644 --- a/src/vs/workbench/parts/files/browser/fileActions.ts +++ b/src/vs/workbench/parts/files/browser/fileActions.ts @@ -25,7 +25,7 @@ import {MessageType, IInputValidator} from 'vs/base/browser/ui/inputbox/inputBox import {ITree, IHighlightEvent} from 'vs/base/parts/tree/browser/tree'; import {dispose, IDisposable} from 'vs/base/common/lifecycle'; import {EventType as WorkbenchEventType, EditorEvent} from 'vs/workbench/common/events'; -import {LocalFileChangeEvent, VIEWLET_ID, ITextFileService, TextFileChangeEvent, ITextFileOperationResult, IWorkingFileModelChangeEvent, IWorkingFileEntry, IWorkingFilesModel, EventType as FileEventType} from 'vs/workbench/parts/files/common/files'; +import {LocalFileChangeEvent, VIEWLET_ID, ITextFileService, TextFileChangeEvent, ITextFileOperationResult, IWorkingFileModelChangeEvent, EventType as FileEventType} from 'vs/workbench/parts/files/common/files'; import {IFileService, IFileStat, IImportResult} from 'vs/platform/files/common/files'; import {DiffEditorInput, toDiffLabel} from 'vs/workbench/common/editor/diffEditorInput'; import {asFileEditorInput, getUntitledOrFileResource, TextEditorOptions, EditorOptions, UntitledEditorInput, ConfirmResult} from 'vs/workbench/common/editor'; @@ -41,7 +41,6 @@ import {IUntitledEditorService} from 'vs/workbench/services/untitled/common/unti import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService'; import {IQuickOpenService} from 'vs/workbench/services/quickopen/common/quickOpenService'; import {IViewletService} from 'vs/workbench/services/viewlet/common/viewletService'; -import {IPartService} from 'vs/workbench/services/part/common/partService'; import {IStorageService} from 'vs/platform/storage/common/storage'; import {Position, IEditor} from 'vs/platform/editor/common/editor'; import {IEventService} from 'vs/platform/event/common/event'; @@ -1741,51 +1740,6 @@ export class RevertFileAction extends Action { } } -export class ReopenClosedFileAction extends Action { - - public static ID = 'workbench.files.action.reopenClosedFile'; - public static LABEL = nls.localize('reopenClosedFile', "Reopen Closed File"); - - constructor( - id: string, - label: string, - @IPartService private partService: IPartService, - @IWorkbenchEditorService private editorService: IWorkbenchEditorService, - @IViewletService private viewletService: IViewletService, - @ITextFileService private textFileService: ITextFileService, - @IFileService private fileService: IFileService - ) { - super(id, label); - } - - public run(): TPromise { - let workingFilesModel: IWorkingFilesModel = this.textFileService.getWorkingFilesModel(); - let entry: IWorkingFileEntry = workingFilesModel.popLastClosedEntry(); - - if (entry === null) { - return TPromise.as(true); - } - - // If the current resource is the recently closed resource, run action again - let activeResource = getUntitledOrFileResource(this.editorService.getActiveEditorInput()); - if (activeResource && activeResource.path === entry.resource.path) { - return this.run(); - } - - return this.fileService.existsFile(entry.resource).then((exists) => { - if (!exists) { - return this.run(); // repeat in case the last closed file got deleted meanwhile - } - - // Make it a working file again - workingFilesModel.addEntry(entry.resource); - - // Open in editor - return this.editorService.openEditor(entry); - }); - } -} - export abstract class BaseCloseWorkingFileAction extends Action { protected model: WorkingFilesModel; private elements: URI[]; diff --git a/src/vs/workbench/parts/files/browser/files.contribution.ts b/src/vs/workbench/parts/files/browser/files.contribution.ts index 40398da21d66b32e1ba767b9dcd77f10380c76a2..f62877508973eeccf2ff7ac0dc69ada67b6b75f0 100644 --- a/src/vs/workbench/parts/files/browser/files.contribution.ts +++ b/src/vs/workbench/parts/files/browser/files.contribution.ts @@ -123,7 +123,6 @@ let descriptor = new AsyncDescriptor('vs/workbench/parts/files interface ISerializedFileInput { resource: string; - mime: string; } // Register Editor Input Factory @@ -134,8 +133,7 @@ class FileEditorInputFactory implements IEditorInputFactory { public serialize(editorInput: EditorInput): string { let fileEditorInput = editorInput; let fileInput: ISerializedFileInput = { - resource: fileEditorInput.getResource().toString(), - mime: fileEditorInput.getMime() + resource: fileEditorInput.getResource().toString() }; return JSON.stringify(fileInput); @@ -144,7 +142,7 @@ class FileEditorInputFactory implements IEditorInputFactory { public deserialize(instantiationService: IInstantiationService, serializedEditorInput: string): EditorInput { let fileInput: ISerializedFileInput = JSON.parse(serializedEditorInput); - return instantiationService.createInstance(FileEditorInput, URI.parse(fileInput.resource), fileInput.mime, void 0); + return instantiationService.createInstance(FileEditorInput, URI.parse(fileInput.resource), void 0, void 0); } } diff --git a/src/vs/workbench/test/common/editor/editorStacksModel.test.ts b/src/vs/workbench/test/common/editor/editorStacksModel.test.ts index f2ef1499482a9b80262a12df14327cade64ae3b8..6190d70295b30ebc8177073ba9b9ce10a18931e7 100644 --- a/src/vs/workbench/test/common/editor/editorStacksModel.test.ts +++ b/src/vs/workbench/test/common/editor/editorStacksModel.test.ts @@ -76,7 +76,7 @@ function groupListener(group: IEditorGroup): GroupEvents { }; group.onEditorOpened(e => groupEvents.opened.push(e)); - group.onEditorClosed(e => groupEvents.closed.push(e)); + group.onEditorClosed(e => groupEvents.closed.push(e.editor)); group.onEditorActivated(e => groupEvents.activated.push(e)); group.onEditorPinned(e => groupEvents.pinned.push(e)); group.onEditorUnpinned(e => groupEvents.unpinned.push(e)); @@ -94,7 +94,7 @@ class TestEditorInput extends EditorInput { public resolve() { return null; } public matches(other: TestEditorInput): boolean { - return this.id === other.id && other instanceof TestEditorInput; + return other && this.id === other.id && other instanceof TestEditorInput; } } @@ -106,7 +106,7 @@ class NonSerializableTestEditorInput extends EditorInput { public resolve() { return null; } public matches(other: TestEditorInput): boolean { - return this.id === other.id && other instanceof NonSerializableTestEditorInput; + return other && this.id === other.id && other instanceof NonSerializableTestEditorInput; } } @@ -262,7 +262,7 @@ suite('Editor Stacks Model', () => { test('Groups - Event Aggregation', function () { const model = create(); - let groupEvents:IEditorGroup[] = []; + let groupEvents: IEditorGroup[] = []; let count = groupEvents.length; model.onModelChanged(group => { groupEvents.push(group); @@ -529,11 +529,59 @@ suite('Editor Stacks Model', () => { assert.equal(input3, group.getEditors()[2]); const input4 = input(); - group.openEditor(input4, { pinned: false, active: true}); // this should cause the preview editor to move after input3 + group.openEditor(input4, { pinned: false, active: true }); // this should cause the preview editor to move after input3 assert.equal(input4, group.getEditors()[2]); }); + test('Stack - Multiple Editors - Recently Closed Tracking', function () { + let services = new ServiceCollection(); + + services.set(IStorageService, new TestStorageService()); + services.set(IWorkspaceContextService, new TestContextService()); + const lifecycle = new TestLifecycleService(); + services.set(ILifecycleService, lifecycle); + + let inst = new InstantiationService(services); + + (Registry.as(EditorExtensions.Editors)).setInstantiationService(inst); + + let model: EditorStacksModel = inst.createInstance(EditorStacksModel); + + const group1 = model.openGroup('group1'); + const group2 = model.openGroup('group2'); + + const input1 = input(); + const input2 = input(); + const input3 = input(); + + group1.openEditor(input1, { pinned: false, active: true }); + group1.openEditor(input2, { pinned: true, active: true }); + group1.openEditor(input3, { pinned: true, active: true }); + + const input4 = input(); + const input5 = input(); + + group2.openEditor(input4, { pinned: true, active: true }); + group2.openEditor(input5, { pinned: true, active: true }); + + assert.ok(!model.popLastClosedEditor()); + + group1.closeEditor(input1); + assert.ok(!model.popLastClosedEditor()); // preview editors are not recorded + + group1.closeEditor(input3); + + assert.ok(input3.matches(model.popLastClosedEditor())); + + group2.closeAllEditors(); + + assert.ok(input5.matches(model.popLastClosedEditor())); + assert.ok(input4.matches(model.popLastClosedEditor())); + + assert.ok(!model.popLastClosedEditor()); + }); + test('Stack - Multiple Editors - Pinned and Active (DEFAULT_OPEN_EDITOR_DIRECTION = Direction.LEFT)', function () { setOpenEditorDirection(Direction.LEFT);