mirror of
https://github.com/Microsoft/vscode
synced 2024-09-13 21:55:38 +00:00
move recently closed editors to history service
This commit is contained in:
parent
d5f06aec6b
commit
0d5d1d4b1c
|
@ -889,7 +889,7 @@ export class ReopenClosedEditorAction extends Action {
|
|||
constructor(
|
||||
id: string,
|
||||
label: string,
|
||||
@IPartService private partService: IPartService,
|
||||
@IHistoryService private historyService: IHistoryService,
|
||||
@IEditorGroupService private editorGroupService: IEditorGroupService,
|
||||
@IWorkbenchEditorService private editorService: IWorkbenchEditorService
|
||||
) {
|
||||
|
@ -900,9 +900,9 @@ export class ReopenClosedEditorAction extends Action {
|
|||
const stacks = this.editorGroupService.getStacksModel();
|
||||
|
||||
// Find an editor that was closed and is currently not opened in the group
|
||||
let lastClosedEditor = stacks.popLastClosedEditor();
|
||||
let lastClosedEditor = this.historyService.popLastClosedEditor();
|
||||
while (lastClosedEditor && stacks.activeGroup && stacks.activeGroup.indexOf(lastClosedEditor) >= 0) {
|
||||
lastClosedEditor = stacks.popLastClosedEditor();
|
||||
lastClosedEditor = this.historyService.popLastClosedEditor();
|
||||
}
|
||||
|
||||
if (lastClosedEditor) {
|
||||
|
@ -1082,9 +1082,6 @@ export class ClearEditorHistoryAction extends Action {
|
|||
// Editor history
|
||||
this.historyService.clear();
|
||||
|
||||
// Recently closed editors
|
||||
this.editorGroupService.getStacksModel().clearLastClosedEditors();
|
||||
|
||||
return TPromise.as(true);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -451,7 +451,6 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupService
|
|||
// Recover by closing the active editor (if the input is still the active one)
|
||||
if (group.activeEditor === input) {
|
||||
this.doCloseActiveEditor(group);
|
||||
this.stacks.popLastClosedEditor(); // remove from "last closed" history because this input is failing
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -610,6 +610,7 @@ export interface IStacksModelChangeEvent {
|
|||
export interface IEditorStacksModel {
|
||||
|
||||
onModelChanged: Event<IStacksModelChangeEvent>;
|
||||
onEditorClosed: Event<IGroupEvent>;
|
||||
|
||||
groups: IEditorGroup[];
|
||||
activeGroup: IEditorGroup;
|
||||
|
@ -623,9 +624,6 @@ export interface IEditorStacksModel {
|
|||
next(): IEditorIdentifier;
|
||||
previous(): IEditorIdentifier;
|
||||
|
||||
popLastClosedEditor(): IEditorInput;
|
||||
clearLastClosedEditors(): void;
|
||||
|
||||
isOpen(editor: IEditorInput): boolean;
|
||||
isOpen(resource: URI): boolean;
|
||||
|
||||
|
@ -661,6 +659,11 @@ export interface IEditorContext extends IEditorIdentifier {
|
|||
event: any;
|
||||
}
|
||||
|
||||
export interface IGroupEvent {
|
||||
editor: IEditorInput;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export type GroupIdentifier = number;
|
||||
|
||||
export const EditorOpenPositioning = {
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
'use strict';
|
||||
|
||||
import Event, {Emitter} from 'vs/base/common/event';
|
||||
import {EditorInput, getUntitledOrFileResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, GroupIdentifier, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, EditorOpenPositioning} from 'vs/workbench/common/editor';
|
||||
import {EditorInput, getUntitledOrFileResource, IEditorStacksModel, IEditorGroup, IEditorIdentifier, IGroupEvent, GroupIdentifier, IStacksModelChangeEvent, IWorkbenchEditorConfiguration, EditorOpenPositioning} 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';
|
||||
|
@ -22,9 +22,8 @@ import {DiffEditorInput} from 'vs/workbench/common/editor/diffEditorInput';
|
|||
// TODO@Ben currently only files and untitled editors are tracked with their resources in the stacks model
|
||||
// Once the resource is a base concept of all editor inputs, every resource should be tracked for any editor
|
||||
|
||||
export interface GroupEvent {
|
||||
export interface GroupEvent extends IGroupEvent {
|
||||
editor: EditorInput;
|
||||
pinned: boolean;
|
||||
}
|
||||
|
||||
export interface EditorIdentifier extends IEditorIdentifier {
|
||||
|
@ -624,13 +623,11 @@ export class EditorGroup implements IEditorGroup {
|
|||
interface ISerializedEditorStacksModel {
|
||||
groups: ISerializedEditorGroup[];
|
||||
active: number;
|
||||
lastClosed: ISerializedEditorInput[];
|
||||
}
|
||||
|
||||
export class EditorStacksModel implements IEditorStacksModel {
|
||||
|
||||
private static STORAGE_KEY = 'editorStacks.model';
|
||||
private static MAX_RECENTLY_CLOSED_EDITORS = 20;
|
||||
|
||||
private toDispose: IDisposable[];
|
||||
private loaded: boolean;
|
||||
|
@ -639,8 +636,6 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
private _activeGroup: EditorGroup;
|
||||
private groupToIdentifier: { [id: number]: EditorGroup };
|
||||
|
||||
private recentlyClosedEditors: ISerializedEditorInput[];
|
||||
|
||||
private _onGroupOpened: Emitter<EditorGroup>;
|
||||
private _onGroupClosed: Emitter<EditorGroup>;
|
||||
private _onGroupMoved: Emitter<EditorGroup>;
|
||||
|
@ -649,6 +644,7 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
private _onGroupRenamed: Emitter<EditorGroup>;
|
||||
private _onEditorDisposed: Emitter<EditorIdentifier>;
|
||||
private _onEditorDirty: Emitter<EditorIdentifier>;
|
||||
private _onEditorClosed: Emitter<GroupEvent>;
|
||||
private _onModelChanged: Emitter<IStacksModelChangeEvent>;
|
||||
|
||||
constructor(
|
||||
|
@ -662,8 +658,6 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
this._groups = [];
|
||||
this.groupToIdentifier = Object.create(null);
|
||||
|
||||
this.recentlyClosedEditors = [];
|
||||
|
||||
this._onGroupOpened = new Emitter<EditorGroup>();
|
||||
this._onGroupClosed = new Emitter<EditorGroup>();
|
||||
this._onGroupActivated = new Emitter<EditorGroup>();
|
||||
|
@ -673,8 +667,9 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
this._onModelChanged = new Emitter<IStacksModelChangeEvent>();
|
||||
this._onEditorDisposed = new Emitter<EditorIdentifier>();
|
||||
this._onEditorDirty = new Emitter<EditorIdentifier>();
|
||||
this._onEditorClosed = new Emitter<GroupEvent>();
|
||||
|
||||
this.toDispose.push(this._onGroupOpened, this._onGroupClosed, this._onGroupActivated, this._onGroupDeactivated, this._onGroupMoved, this._onGroupRenamed, this._onModelChanged, this._onEditorDisposed, this._onEditorDirty);
|
||||
this.toDispose.push(this._onGroupOpened, this._onGroupClosed, this._onGroupActivated, this._onGroupDeactivated, this._onGroupMoved, this._onGroupRenamed, this._onModelChanged, this._onEditorDisposed, this._onEditorDirty, this._onEditorClosed);
|
||||
|
||||
this.registerListeners();
|
||||
}
|
||||
|
@ -719,6 +714,10 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
return this._onEditorDirty.event;
|
||||
}
|
||||
|
||||
public get onEditorClosed(): Event<GroupEvent> {
|
||||
return this._onEditorClosed.event;
|
||||
}
|
||||
|
||||
public get groups(): EditorGroup[] {
|
||||
this.ensureLoaded();
|
||||
|
||||
|
@ -957,8 +956,7 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
|
||||
return {
|
||||
groups: serializableGroups,
|
||||
active: serializableActiveIndex,
|
||||
lastClosed: this.recentlyClosedEditors
|
||||
active: serializableActiveIndex
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -994,7 +992,6 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
|
||||
this._groups = serialized.groups.map(s => this.doCreateGroup(s));
|
||||
this._activeGroup = this._groups[serialized.active];
|
||||
this.recentlyClosedEditors = serialized.lastClosed || [];
|
||||
} else {
|
||||
this.migrate();
|
||||
}
|
||||
|
@ -1036,7 +1033,6 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
this._groups = [];
|
||||
this._activeGroup = void 0;
|
||||
this.groupToIdentifier = Object.create(null);
|
||||
this.recentlyClosedEditors = [];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1082,7 +1078,10 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
const unbind: IDisposable[] = [];
|
||||
unbind.push(group.onEditorsStructureChanged(editor => this._onModelChanged.fire({ group, editor, structural: true })));
|
||||
unbind.push(group.onEditorStateChanged(editor => this._onModelChanged.fire({ group, editor })));
|
||||
unbind.push(group.onEditorClosed(e => this.onEditorClosed(e)));
|
||||
unbind.push(group.onEditorClosed(event => {
|
||||
this.handleOnEditorClosed(event);
|
||||
this._onEditorClosed.fire(event);
|
||||
}));
|
||||
unbind.push(group.onEditorDisposed(editor => this._onEditorDisposed.fire({ editor, group })));
|
||||
unbind.push(group.onEditorDirty(editor => this._onEditorDirty.fire({ editor, group })));
|
||||
unbind.push(this.onGroupClosed(g => {
|
||||
|
@ -1094,26 +1093,7 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
return group;
|
||||
}
|
||||
|
||||
public popLastClosedEditor(): EditorInput {
|
||||
this.ensureLoaded();
|
||||
|
||||
const registry = Registry.as<IEditorRegistry>(Extensions.Editors);
|
||||
|
||||
let serializedEditor = this.recentlyClosedEditors.pop();
|
||||
if (serializedEditor) {
|
||||
return registry.getEditorInputFactory(serializedEditor.id).deserialize(this.instantiationService, serializedEditor.value);
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
public clearLastClosedEditors(): void {
|
||||
this.ensureLoaded();
|
||||
|
||||
this.recentlyClosedEditors = [];
|
||||
}
|
||||
|
||||
private onEditorClosed(event: GroupEvent): void {
|
||||
private handleOnEditorClosed(event: GroupEvent): void {
|
||||
const editor = event.editor;
|
||||
|
||||
// Close the editor when it is no longer open in any group
|
||||
|
@ -1129,22 +1109,6 @@ export class EditorStacksModel implements IEditorStacksModel {
|
|||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Track closing of pinned editor to support to reopen closed editors
|
||||
if (event.pinned) {
|
||||
const registry = Registry.as<IEditorRegistry>(Extensions.Editors);
|
||||
|
||||
const factory = registry.getEditorInputFactory(editor.getTypeId());
|
||||
if (factory) {
|
||||
let value = factory.serialize(editor);
|
||||
if (typeof value === 'string') {
|
||||
this.recentlyClosedEditors.push({ id: editor.getTypeId(), value });
|
||||
if (this.recentlyClosedEditors.length > EditorStacksModel.MAX_RECENTLY_CLOSED_EDITORS) {
|
||||
this.recentlyClosedEditors = this.recentlyClosedEditors.slice(this.recentlyClosedEditors.length - EditorStacksModel.MAX_RECENTLY_CLOSED_EDITORS); // upper bound of recently closed
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public isOpen(resource: URI): boolean;
|
||||
|
|
|
@ -10,7 +10,7 @@ import platform = require('vs/base/common/platform');
|
|||
import nls = require('vs/nls');
|
||||
import {EventType} from 'vs/base/common/events';
|
||||
import {IEditor as IBaseEditor} from 'vs/platform/editor/common/editor';
|
||||
import {TextEditorOptions, EditorInput} from 'vs/workbench/common/editor';
|
||||
import {TextEditorOptions, EditorInput, IGroupEvent} from 'vs/workbench/common/editor';
|
||||
import {BaseTextEditor} from 'vs/workbench/browser/parts/editor/textEditor';
|
||||
import {IWorkbenchEditorService} from 'vs/workbench/services/editor/common/editorService';
|
||||
import {IHistoryService} from 'vs/workbench/services/history/common/history';
|
||||
|
@ -218,6 +218,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
private static STORAGE_KEY = 'history.entries';
|
||||
private static MAX_HISTORY_ITEMS = 200;
|
||||
private static MAX_STACK_ITEMS = 20;
|
||||
private static MAX_RECENTLY_CLOSED_EDITORS = 20;
|
||||
|
||||
private stack: IStackEntry[];
|
||||
private index: number;
|
||||
|
@ -225,6 +226,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
private currentFileEditorState: EditorState;
|
||||
|
||||
private history: IEditorInput[];
|
||||
private recentlyClosed: IEditorInput[];
|
||||
private loaded: boolean;
|
||||
private registry: IEditorRegistry;
|
||||
|
||||
|
@ -241,6 +243,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
|
||||
this.index = -1;
|
||||
this.stack = [];
|
||||
this.recentlyClosed = [];
|
||||
this.loaded = false;
|
||||
this.registry = Registry.as<IEditorRegistry>(Extensions.Editors);
|
||||
|
||||
|
@ -250,6 +253,35 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
private registerListeners(): void {
|
||||
this.toUnbind.push(this.lifecycleService.onShutdown(() => this.save()));
|
||||
this.toUnbind.push(this.editorGroupService.onEditorOpenFail(editor => this.remove(editor)));
|
||||
this.toUnbind.push(this.editorGroupService.getStacksModel().onEditorClosed(event => this.onEditorClosed(event)));
|
||||
}
|
||||
|
||||
private onEditorClosed(event: IGroupEvent): void {
|
||||
|
||||
// Track closing of pinned editor to support to reopen closed editors
|
||||
if (event.pinned) {
|
||||
const editor = event.editor;
|
||||
|
||||
// Remove all inputs matching and add as last recently closed
|
||||
this.removeFromRecentlyClosed(editor);
|
||||
this.recentlyClosed.push(editor);
|
||||
|
||||
// Bounding
|
||||
if (this.recentlyClosed.length > HistoryService.MAX_RECENTLY_CLOSED_EDITORS) {
|
||||
this.recentlyClosed = this.recentlyClosed.slice(this.recentlyClosed.length - HistoryService.MAX_RECENTLY_CLOSED_EDITORS); // upper bound of recently closed
|
||||
}
|
||||
|
||||
// Restore on dispose
|
||||
editor.addOneTimeDisposableListener(EventType.DISPOSE, () => {
|
||||
this.restoreInRecentlyClosed(editor);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
public popLastClosedEditor(): IEditorInput {
|
||||
this.ensureLoaded();
|
||||
|
||||
return this.recentlyClosed.pop();
|
||||
}
|
||||
|
||||
public forward(): void {
|
||||
|
@ -272,6 +304,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
this.index = -1;
|
||||
this.stack.splice(0);
|
||||
this.history = [];
|
||||
this.recentlyClosed = [];
|
||||
}
|
||||
|
||||
private navigate(): void {
|
||||
|
@ -358,6 +391,7 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
public remove(input: IEditorInput): void {
|
||||
this.removeFromHistory(input);
|
||||
this.removeFromStack(input);
|
||||
setTimeout(() => this.removeFromRecentlyClosed(input)); // race condition with editor close and dispose
|
||||
}
|
||||
|
||||
private removeFromHistory(input: IEditorInput, index?: number): void {
|
||||
|
@ -495,6 +529,26 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
});
|
||||
}
|
||||
|
||||
private restoreInRecentlyClosed(input: IEditorInput): void {
|
||||
let restoredInput: EditorInput;
|
||||
let restored = false;
|
||||
|
||||
this.recentlyClosed.forEach((e, i) => {
|
||||
if (e.matches(input)) {
|
||||
if (!restored) {
|
||||
restoredInput = this.restoreInput(input);
|
||||
restored = true;
|
||||
}
|
||||
|
||||
if (restoredInput) {
|
||||
this.recentlyClosed[i] = restoredInput;
|
||||
} else {
|
||||
this.stack.splice(i, 1);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private restoreInput(input: IEditorInput): EditorInput {
|
||||
if (input instanceof EditorInput) {
|
||||
const factory = this.registry.getEditorInputFactory(input.getTypeId());
|
||||
|
@ -520,6 +574,14 @@ export class HistoryService extends BaseHistoryService implements IHistoryServic
|
|||
});
|
||||
}
|
||||
|
||||
private removeFromRecentlyClosed(input: IEditorInput): void {
|
||||
this.recentlyClosed.forEach((e, i) => {
|
||||
if (e.matches(input)) {
|
||||
this.recentlyClosed.splice(i, 1);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public getHistory(): IEditorInput[] {
|
||||
this.ensureLoaded();
|
||||
|
||||
|
|
|
@ -13,6 +13,11 @@ export interface IHistoryService {
|
|||
|
||||
serviceId: ServiceIdentifier<any>;
|
||||
|
||||
/**
|
||||
* Removes and returns the last closed editor if any.
|
||||
*/
|
||||
popLastClosedEditor(): IEditorInput;
|
||||
|
||||
/**
|
||||
* Navigate forwards in history.
|
||||
*/
|
||||
|
|
|
@ -591,57 +591,6 @@ suite('Editor Stacks Model', () => {
|
|||
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);
|
||||
const config = new TestConfigurationService();
|
||||
config.setUserConfiguration('workbench', { editorOpenPositioning: 'right' });
|
||||
services.set(IConfigurationService, config);
|
||||
|
||||
let inst = new InstantiationService(services);
|
||||
|
||||
(<IEditorRegistry>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 () {
|
||||
let services = new ServiceCollection();
|
||||
services.set(IStorageService, new TestStorageService());
|
||||
|
|
|
@ -66,6 +66,10 @@ export class TestHistoryService implements IHistoryService {
|
|||
|
||||
}
|
||||
|
||||
public popLastClosedEditor(): IEditorInput {
|
||||
return null;
|
||||
}
|
||||
|
||||
public getHistory(): IEditorInput[] {
|
||||
return [];
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue