From b5dfdbe00f1bf40d079555f5848a51dbcbbe6a6e Mon Sep 17 00:00:00 2001 From: Benjamin Pasero Date: Wed, 14 Apr 2021 08:36:17 +0200 Subject: [PATCH] editors - some :lipstick: renames for editor group model --- .../workbench/browser/parts/editor/editor.ts | 7 +- .../browser/parts/editor/editorGroupView.ts | 152 +++++++++--------- .../browser/parts/editor/editorPart.ts | 8 +- .../{editorGroup.ts => editorGroupModel.ts} | 42 ++--- .../editor/common/editorGroupsService.ts | 5 + ...roups.test.ts => editorGroupModel.test.ts} | 110 ++++++------- .../test/browser/workbenchTestServices.ts | 2 - 7 files changed, 163 insertions(+), 163 deletions(-) rename src/vs/workbench/common/editor/{editorGroup.ts => editorGroupModel.ts} (95%) rename src/vs/workbench/test/browser/parts/editor/{editorGroups.test.ts => editorGroupModel.test.ts} (95%) diff --git a/src/vs/workbench/browser/parts/editor/editor.ts b/src/vs/workbench/browser/parts/editor/editor.ts index 08a708c4cce..6984779b870 100644 --- a/src/vs/workbench/browser/parts/editor/editor.ts +++ b/src/vs/workbench/browser/parts/editor/editor.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorInput, IEditorMoveEvent } from 'vs/workbench/common/editor'; +import { GroupIdentifier, IWorkbenchEditorConfiguration, EditorOptions, TextEditorOptions, IEditorInput, IEditorIdentifier, IEditorCloseEvent, IEditorPartOptions, IEditorPartOptionsChangeEvent, EditorInput } from 'vs/workbench/common/editor'; import { IEditorGroup, GroupDirection, IAddGroupOptions, IMergeGroupOptions, GroupsOrder, GroupsArrangement } from 'vs/workbench/services/editor/common/editorGroupsService'; import { IDisposable } from 'vs/base/common/lifecycle'; import { Dimension } from 'vs/base/browser/dom'; @@ -104,11 +104,9 @@ export interface IEditorGroupTitleHeight { export interface IEditorGroupView extends IDisposable, ISerializableView, IEditorGroup { readonly onDidFocus: Event; + readonly onDidOpenEditorFail: Event; - readonly onWillMoveEditor: Event; - readonly onWillCloseEditor: Event; readonly onDidCloseEditor: Event; - readonly onWillDispose: Event; /** * A promise that resolves when the group has been restored. @@ -120,7 +118,6 @@ export interface IEditorGroupView extends IDisposable, ISerializableView, IEdito readonly titleHeight: IEditorGroupTitleHeight; - readonly isEmpty: boolean; readonly isMinimized: boolean; readonly disposed: boolean; diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 60356d2abcd..fb118890b85 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import 'vs/css!./media/editorgroupview'; -import { EditorGroup, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; +import { EditorGroupModel, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroupModel, isSerializedEditorGroupModel } from 'vs/workbench/common/editor/editorGroupModel'; import { EditorInput, EditorOptions, GroupIdentifier, SideBySideEditorInput, CloseDirection, IEditorCloseEvent, ActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, ActiveEditorStickyContext, ActiveEditorPinnedContext, EditorResourceAccessor, IEditorMoveEvent } from 'vs/workbench/common/editor'; import { Event, Emitter, Relay } from 'vs/base/common/event'; import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; @@ -60,7 +60,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return instantiationService.createInstance(EditorGroupView, accessor, null, index); } - static createFromSerialized(serialized: ISerializedEditorGroup, accessor: IEditorGroupsAccessor, index: number, instantiationService: IInstantiationService): IEditorGroupView { + static createFromSerialized(serialized: ISerializedEditorGroupModel, accessor: IEditorGroupsAccessor, index: number, instantiationService: IInstantiationService): IEditorGroupView { return instantiationService.createInstance(EditorGroupView, accessor, serialized, index); } @@ -100,7 +100,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#endregion - private readonly _group: EditorGroup; + private readonly model: EditorGroupModel; private active: boolean | undefined; private dimension: Dimension | undefined; @@ -125,7 +125,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { constructor( private accessor: IEditorGroupsAccessor, - from: IEditorGroupView | ISerializedEditorGroup | null, + from: IEditorGroupView | ISerializedEditorGroupModel | null, private _index: number, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @@ -144,11 +144,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { super(themeService); if (from instanceof EditorGroupView) { - this._group = this._register(from._group.clone()); - } else if (isSerializedEditorGroup(from)) { - this._group = this._register(instantiationService.createInstance(EditorGroup, from)); + this.model = this._register(from.model.clone()); + } else if (isSerializedEditorGroupModel(from)) { + this.model = this._register(instantiationService.createInstance(EditorGroupModel, from)); } else { - this._group = this._register(instantiationService.createInstance(EditorGroup, undefined)); + this.model = this._register(instantiationService.createInstance(EditorGroupModel, undefined)); } //#region create() @@ -238,7 +238,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const observeActiveEditor = () => { activeEditorListener.clear(); - const activeEditor = this._group.activeEditor; + const activeEditor = this.model.activeEditor; if (activeEditor) { groupActiveEditorDirtyContext.set(activeEditor.isDirty() && !activeEditor.isSaving()); activeEditorListener.value = activeEditor.onDidChangeDirty(() => { @@ -258,13 +258,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { observeActiveEditor(); break; case GroupChangeKind.EDITOR_PIN: - if (e.editor && e.editor === this._group.activeEditor) { - groupActiveEditorPinnedContext.set(this._group.isPinned(this._group.activeEditor)); + if (e.editor && e.editor === this.model.activeEditor) { + groupActiveEditorPinnedContext.set(this.model.isPinned(this.model.activeEditor)); } break; case GroupChangeKind.EDITOR_STICKY: - if (e.editor && e.editor === this._group.activeEditor) { - groupActiveEditorStickyContext.set(this._group.isSticky(this._group.activeEditor)); + if (e.editor && e.editor === this.model.activeEditor) { + groupActiveEditorStickyContext.set(this.model.isSticky(this.model.activeEditor)); } break; } @@ -305,7 +305,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.element.appendChild(toolbarContainer); // Toolbar - const groupId = this._group.id; + const groupId = this.model.id; const containerToolbar = this._register(new ActionBar(toolbarContainer, { ariaLabel: localize('ariaLabelGroupActions', "Editor group actions"), actionRunner: this._register(new class extends ActionRunner { async override run(action: IAction) { @@ -447,8 +447,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return this.titleAreaControl; } - private restoreEditors(from: IEditorGroupView | ISerializedEditorGroup | null): Promise | undefined { - if (this._group.count === 0) { + private restoreEditors(from: IEditorGroupView | ISerializedEditorGroupModel | null): Promise | undefined { + if (this.model.count === 0) { return; // nothing to show } @@ -460,13 +460,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { options = new EditorOptions(); } - const activeEditor = this._group.activeEditor; + const activeEditor = this.model.activeEditor; if (!activeEditor) { return; } - options.pinned = this._group.isPinned(activeEditor); // preserve pinned state - options.sticky = this._group.isSticky(activeEditor); // preserve sticky state + options.pinned = this.model.isPinned(activeEditor); // preserve pinned state + options.sticky = this.model.isSticky(activeEditor); // preserve sticky state options.preserveFocus = true; // handle focus after editor is opened const activeElement = document.activeElement; @@ -490,13 +490,13 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private registerListeners(): void { // Model Events - this._register(this._group.onDidChangeEditorPinned(editor => this.onDidChangeEditorPinned(editor))); - this._register(this._group.onDidChangeEditorSticky(editor => this.onDidChangeEditorSticky(editor))); - this._register(this._group.onDidOpenEditor(editor => this.onDidOpenEditor(editor))); - this._register(this._group.onDidCloseEditor(editor => this.handleOnDidCloseEditor(editor))); - this._register(this._group.onWillDisposeEditor(editor => this.onWillDisposeEditor(editor))); - this._register(this._group.onDidChangeEditorDirty(editor => this.onDidChangeEditorDirty(editor))); - this._register(this._group.onDidEditorLabelChange(editor => this.onDidEditorLabelChange(editor))); + this._register(this.model.onDidChangeEditorPinned(editor => this.onDidChangeEditorPinned(editor))); + this._register(this.model.onDidChangeEditorSticky(editor => this.onDidChangeEditorSticky(editor))); + this._register(this.model.onDidOpenEditor(editor => this.onDidOpenEditor(editor))); + this._register(this.model.onDidCloseEditor(editor => this.handleOnDidCloseEditor(editor))); + this._register(this.model.onWillDisposeEditor(editor => this.onWillDisposeEditor(editor))); + this._register(this.model.onDidChangeEditorDirty(editor => this.onDidChangeEditorDirty(editor))); + this._register(this.model.onDidEditorLabelChange(editor => this.onDidEditorLabelChange(editor))); // Option Changes this._register(this.accessor.onDidChangeEditorPartOptions(e => this.onDidChangeEditorPartOptions(e))); @@ -608,9 +608,9 @@ export class EditorGroupView extends Themable implements IEditorGroupView { let activeEditor: EditorInput | undefined; const inactiveEditors: EditorInput[] = []; for (const editor of editors) { - if (this._group.isActive(editor)) { + if (this.model.isActive(editor)) { activeEditor = editor; - } else if (this._group.contains(editor)) { + } else if (this.model.contains(editor)) { inactiveEditors.push(editor); } } @@ -641,8 +641,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.relayout(); // Ensure to show active editor if any - if (this._group.activeEditor) { - this.titleAreaControl.openEditor(this._group.activeEditor); + if (this.model.activeEditor) { + this.titleAreaControl.openEditor(this.model.activeEditor); } } @@ -656,8 +656,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Pin preview editor once user disables preview if (event.oldPartOptions.enablePreview && !event.newPartOptions.enablePreview) { - if (this._group.previewEditor) { - this.pinEditor(this._group.previewEditor); + if (this.model.previewEditor) { + this.pinEditor(this.model.previewEditor); } } } @@ -711,7 +711,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } get isEmpty(): boolean { - return this._group.count === 0; + return this.model.count === 0; } get titleHeight(): IEditorGroupTitleHeight { @@ -757,19 +757,19 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region basics() get id(): GroupIdentifier { - return this._group.id; + return this.model.id; } get editors(): EditorInput[] { - return this._group.getEditors(EditorsOrder.SEQUENTIAL); + return this.model.getEditors(EditorsOrder.SEQUENTIAL); } get count(): number { - return this._group.count; + return this.model.count; } get stickyCount(): number { - return this._group.stickyCount; + return this.model.stickyCount; } get activeEditorPane(): IVisibleEditorPane | undefined { @@ -777,43 +777,43 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } get activeEditor(): EditorInput | null { - return this._group.activeEditor; + return this.model.activeEditor; } get previewEditor(): EditorInput | null { - return this._group.previewEditor; + return this.model.previewEditor; } isPinned(editor: EditorInput): boolean { - return this._group.isPinned(editor); + return this.model.isPinned(editor); } isSticky(editorOrIndex: EditorInput | number): boolean { - return this._group.isSticky(editorOrIndex); + return this.model.isSticky(editorOrIndex); } isActive(editor: EditorInput): boolean { - return this._group.isActive(editor); + return this.model.isActive(editor); } contains(candidate: EditorInput, options?: { supportSideBySide?: boolean, strictEquals?: boolean }): boolean { - return this._group.contains(candidate, options); + return this.model.contains(candidate, options); } getEditors(order: EditorsOrder, options?: { excludeSticky?: boolean }): EditorInput[] { - return this._group.getEditors(order, options); + return this.model.getEditors(order, options); } getEditorByIndex(index: number): EditorInput | undefined { - return this._group.getEditorByIndex(index); + return this.model.getEditorByIndex(index); } getIndexOfEditor(editor: EditorInput): number { - return this._group.indexOf(editor); + return this.model.indexOf(editor); } isOpened(editor: EditorInput): boolean { - return this._group.contains(editor); + return this.model.contains(editor); } focus(): void { @@ -830,10 +830,10 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } pinEditor(candidate: EditorInput | undefined = this.activeEditor || undefined): void { - if (candidate && !this._group.isPinned(candidate)) { + if (candidate && !this.model.isPinned(candidate)) { // Update model - const editor = this._group.pin(candidate); + const editor = this.model.pin(candidate); // Forward to title control if (editor) { @@ -851,11 +851,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } private doStickEditor(candidate: EditorInput | undefined, sticky: boolean): void { - if (candidate && this._group.isSticky(candidate) !== sticky) { + if (candidate && this.model.isSticky(candidate) !== sticky) { const oldIndexOfEditor = this.getIndexOfEditor(candidate); // Update model - const editor = sticky ? this._group.stick(candidate) : this._group.unstick(candidate); + const editor = sticky ? this.model.stick(candidate) : this.model.unstick(candidate); if (!editor) { return; } @@ -906,19 +906,19 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Determine options const openEditorOptions: IEditorOpenOptions = { index: options ? options.index : undefined, - pinned: options?.sticky || !this.accessor.partOptions.enablePreview || editor.isDirty() || (options?.pinned ?? typeof options?.index === 'number' /* unless specified, prefer to pin when opening with index */) || (typeof options?.index === 'number' && this._group.isSticky(options.index)), - sticky: options?.sticky || (typeof options?.index === 'number' && this._group.isSticky(options.index)), - active: this._group.count === 0 || !options || !options.inactive + pinned: options?.sticky || !this.accessor.partOptions.enablePreview || editor.isDirty() || (options?.pinned ?? typeof options?.index === 'number' /* unless specified, prefer to pin when opening with index */) || (typeof options?.index === 'number' && this.model.isSticky(options.index)), + sticky: options?.sticky || (typeof options?.index === 'number' && this.model.isSticky(options.index)), + active: this.model.count === 0 || !options || !options.inactive }; - if (options?.sticky && typeof options?.index === 'number' && !this._group.isSticky(options.index)) { + if (options?.sticky && typeof options?.index === 'number' && !this.model.isSticky(options.index)) { // Special case: we are to open an editor sticky but at an index that is not sticky // In that case we prefer to open the editor at the index but not sticky. This enables // to drag a sticky editor to an index that is not sticky to unstick it. openEditorOptions.sticky = false; } - if (!openEditorOptions.active && !openEditorOptions.pinned && this._group.activeEditor && !this._group.isPinned(this._group.activeEditor)) { + if (!openEditorOptions.active && !openEditorOptions.pinned && this.model.activeEditor && !this.model.isPinned(this.model.activeEditor)) { // Special case: we are to open an editor inactive and not pinned, but the current active // editor is also not pinned, which means it will get replaced with this one. As such, // the editor can only be active. @@ -951,7 +951,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // out that the editor is already opened at a different index. This // ensures the right set of events are fired to the outside. if (typeof openEditorOptions.index === 'number') { - const indexOfEditor = this._group.indexOf(editor); + const indexOfEditor = this.model.indexOf(editor); if (indexOfEditor !== -1 && indexOfEditor !== openEditorOptions.index) { this.doMoveEditorInsideGroup(editor, openEditorOptions); } @@ -960,7 +960,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update model and make sure to continue to use the editor we get from // the model. It is possible that the editor was already opened and we // want to ensure that we use the existing instance in that case. - const { editor: openedEditor, isNew } = this._group.openEditor(editor, openEditorOptions); + const { editor: openedEditor, isNew } = this.model.openEditor(editor, openEditorOptions); // Show editor const showEditorResult = this.doShowEditor(openedEditor, { active: !!openEditorOptions.active, isNew }, options); @@ -1149,7 +1149,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return; // do nothing if we move into same group without index } - const currentIndex = this._group.indexOf(candidate); + const currentIndex = this.model.indexOf(candidate); if (currentIndex === -1 || currentIndex === moveToIndex) { return; // do nothing if editor unknown in model or is already at the given index } @@ -1157,14 +1157,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update model and make sure to continue to use the editor we get from // the model. It is possible that the editor was already opened and we // want to ensure that we use the existing instance in that case. - const editor = this._group.getEditorByIndex(currentIndex); + const editor = this.model.getEditorByIndex(currentIndex); if (!editor) { return; } // Update model - this._group.moveEditor(editor, moveToIndex); - this._group.pin(editor); + this.model.moveEditor(editor, moveToIndex); + this.model.pin(editor); // Forward to title area this.titleAreaControl.moveEditor(editor, currentIndex, moveToIndex); @@ -1182,7 +1182,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const options = getActiveTextEditorOptions(this, editor, EditorOptions.create({ ...openOptions, pinned: true, // always pin moved editor - sticky: !keepCopy && this._group.isSticky(editor) // preserve sticky state only if editor is moved (https://github.com/microsoft/vscode/issues/99035) + sticky: !keepCopy && this.model.isSticky(editor) // preserve sticky state only if editor is moved (https://github.com/microsoft/vscode/issues/99035) })); // Indicate will move event @@ -1249,7 +1249,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private doCloseEditor(editor: EditorInput, focusNext = (this.accessor.activeGroup === this), fromError?: boolean): void { // Closing the active editor of the group is a bit more work - if (this._group.isActive(editor)) { + if (this.model.isActive(editor)) { this.doCloseActiveEditor(focusNext, fromError); } @@ -1274,7 +1274,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // event because it became empty, only to then trigger another one when the next // group gets active. const closeEmptyGroup = this.accessor.partOptions.closeEmptyGroups; - if (closeEmptyGroup && this.active && this._group.count === 1) { + if (closeEmptyGroup && this.active && this.model.count === 1) { const mostRecentlyActiveGroups = this.accessor.getGroups(GroupsOrder.MOST_RECENTLY_ACTIVE); const nextActiveGroup = mostRecentlyActiveGroups[1]; // [0] will be the current one, so take [1] if (nextActiveGroup) { @@ -1288,11 +1288,11 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Update model if (editorToClose) { - this._group.closeEditor(editorToClose); + this.model.closeEditor(editorToClose); } // Open next active if there are more to show - const nextActiveEditor = this._group.activeEditor; + const nextActiveEditor = this.model.activeEditor; if (nextActiveEditor) { const preserveFocus = !focusNext; @@ -1356,7 +1356,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private doCloseInactiveEditor(editor: EditorInput) { // Update model - this._group.closeEditor(editor); + this.model.closeEditor(editor); } private async handleDirtyClosing(editors: EditorInput[]): Promise { @@ -1395,7 +1395,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { return false; // editor must be dirty and not saving } - if (editor instanceof SideBySideEditorInput && this._group.contains(editor.primary)) { + if (editor instanceof SideBySideEditorInput && this.model.contains(editor.primary)) { return false; // primary-side of editor is still opened somewhere else } @@ -1524,7 +1524,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { const filter = args; const hasDirection = typeof filter.direction === 'number'; - let editorsToClose = this._group.getEditors(hasDirection ? EditorsOrder.SEQUENTIAL : EditorsOrder.MOST_RECENTLY_ACTIVE, filter); // in MRU order only if direction is not specified + let editorsToClose = this.model.getEditors(hasDirection ? EditorsOrder.SEQUENTIAL : EditorsOrder.MOST_RECENTLY_ACTIVE, filter); // in MRU order only if direction is not specified // Filter: saved or saving only if (filter.savedOnly) { @@ -1534,8 +1534,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Filter: direction (left / right) else if (hasDirection && filter.except) { editorsToClose = (filter.direction === CloseDirection.LEFT) ? - editorsToClose.slice(0, this._group.indexOf(filter.except, editorsToClose)) : - editorsToClose.slice(this._group.indexOf(filter.except, editorsToClose) + 1); + editorsToClose.slice(0, this.model.indexOf(filter.except, editorsToClose)) : + editorsToClose.slice(this.model.indexOf(filter.except, editorsToClose) + 1); } // Filter: except @@ -1587,7 +1587,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } // Check for dirty and veto - const veto = await this.handleDirtyClosing(this._group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE, options)); + const veto = await this.handleDirtyClosing(this.model.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE, options)); if (veto) { return; } @@ -1600,7 +1600,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { // Close all inactive editors first const editorsToClose: EditorInput[] = []; - for (const editor of this._group.getEditors(EditorsOrder.SEQUENTIAL, options)) { + for (const editor of this.model.getEditors(EditorsOrder.SEQUENTIAL, options)) { if (!this.isActive(editor)) { this.doCloseInactiveEditor(editor); } @@ -1759,8 +1759,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } - toJSON(): ISerializedEditorGroup { - return this._group.serialize(); + toJSON(): ISerializedEditorGroupModel { + return this.model.serialize(); } //#endregion diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index b34c9de2853..c3b07182bf3 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -19,7 +19,7 @@ import { EditorGroupView } from 'vs/workbench/browser/parts/editor/editorGroupVi import { IConfigurationService, IConfigurationChangeEvent } from 'vs/platform/configuration/common/configuration'; import { IDisposable, dispose, toDisposable, DisposableStore } from 'vs/base/common/lifecycle'; import { IStorageService, StorageScope, StorageTarget } from 'vs/platform/storage/common/storage'; -import { ISerializedEditorGroup, isSerializedEditorGroup } from 'vs/workbench/common/editor/editorGroup'; +import { ISerializedEditorGroupModel, isSerializedEditorGroupModel } from 'vs/workbench/common/editor/editorGroupModel'; import { EditorDropTarget, IEditorDropTargetDelegate } from 'vs/workbench/browser/parts/editor/editorDropTarget'; import { IEditorDropService } from 'vs/workbench/services/editor/browser/editorDropService'; import { Color } from 'vs/base/common/color'; @@ -513,13 +513,13 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro return this._partOptions.splitSizing === 'split' ? Sizing.Split : Sizing.Distribute; } - private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroup | null): IEditorGroupView { + private doCreateGroupView(from?: IEditorGroupView | ISerializedEditorGroupModel | null): IEditorGroupView { // Create group view let groupView: IEditorGroupView; if (from instanceof EditorGroupView) { groupView = EditorGroupView.createCopy(from, this, this.count, this.instantiationService); - } else if (isSerializedEditorGroup(from)) { + } else if (isSerializedEditorGroupModel(from)) { groupView = EditorGroupView.createFromSerialized(from, this, this.count, this.instantiationService); } else { groupView = EditorGroupView.createNew(this, this.count, this.instantiationService); @@ -1005,7 +1005,7 @@ export class EditorPart extends Part implements IEditorGroupsService, IEditorGro // Create new const groupViews: IEditorGroupView[] = []; const gridWidget = SerializableGrid.deserialize(serializedGrid, { - fromJSON: (serializedEditorGroup: ISerializedEditorGroup | null) => { + fromJSON: (serializedEditorGroup: ISerializedEditorGroupModel | null) => { let groupView: IEditorGroupView; if (reuseGroupViews.length > 0) { groupView = reuseGroupViews.shift()!; diff --git a/src/vs/workbench/common/editor/editorGroup.ts b/src/vs/workbench/common/editor/editorGroupModel.ts similarity index 95% rename from src/vs/workbench/common/editor/editorGroup.ts rename to src/vs/workbench/common/editor/editorGroupModel.ts index e280b7bd907..934646ff9fb 100644 --- a/src/vs/workbench/common/editor/editorGroup.ts +++ b/src/vs/workbench/common/editor/editorGroupModel.ts @@ -44,7 +44,7 @@ export interface ISerializedEditorInput { readonly value: string; } -export interface ISerializedEditorGroup { +export interface ISerializedEditorGroupModel { readonly id: number; readonly editors: ISerializedEditorInput[]; readonly mru: number[]; @@ -52,13 +52,13 @@ export interface ISerializedEditorGroup { sticky?: number; } -export function isSerializedEditorGroup(obj?: unknown): obj is ISerializedEditorGroup { - const group = obj as ISerializedEditorGroup; +export function isSerializedEditorGroupModel(obj?: unknown): obj is ISerializedEditorGroupModel { + const group = obj as ISerializedEditorGroupModel; return !!(obj && typeof obj === 'object' && Array.isArray(group.editors) && Array.isArray(group.mru)); } -export class EditorGroup extends Disposable { +export class EditorGroupModel extends Disposable { private static IDS = 0; @@ -107,16 +107,16 @@ export class EditorGroup extends Disposable { private focusRecentEditorAfterClose: boolean | undefined; constructor( - labelOrSerializedGroup: ISerializedEditorGroup | undefined, + labelOrSerializedGroup: ISerializedEditorGroupModel | undefined, @IInstantiationService private readonly instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); - if (isSerializedEditorGroup(labelOrSerializedGroup)) { + if (isSerializedEditorGroupModel(labelOrSerializedGroup)) { this._id = this.deserialize(labelOrSerializedGroup); } else { - this._id = EditorGroup.IDS++; + this._id = EditorGroupModel.IDS++; } this.onConfigurationUpdated(); @@ -722,25 +722,25 @@ export class EditorGroup extends Disposable { return editor.matches(candidate); } - clone(): EditorGroup { - const group = this.instantiationService.createInstance(EditorGroup, undefined); + clone(): EditorGroupModel { + const clone = this.instantiationService.createInstance(EditorGroupModel, undefined); // Copy over group properties - group.editors = this.editors.slice(0); - group.mru = this.mru.slice(0); - group.preview = this.preview; - group.active = this.active; - group.sticky = this.sticky; + clone.editors = this.editors.slice(0); + clone.mru = this.mru.slice(0); + clone.preview = this.preview; + clone.active = this.active; + clone.sticky = this.sticky; // Ensure to register listeners for each editor - for (const editor of group.editors) { - group.registerEditorListeners(editor); + for (const editor of clone.editors) { + clone.registerEditorListeners(editor); } - return group; + return clone; } - serialize(): ISerializedEditorGroup { + serialize(): ISerializedEditorGroupModel { const registry = Registry.as(Extensions.EditorInputFactories); // Serialize all editor inputs so that we can store them. @@ -794,15 +794,15 @@ export class EditorGroup extends Disposable { }; } - private deserialize(data: ISerializedEditorGroup): number { + private deserialize(data: ISerializedEditorGroupModel): number { const registry = Registry.as(Extensions.EditorInputFactories); if (typeof data.id === 'number') { this._id = data.id; - EditorGroup.IDS = Math.max(data.id + 1, EditorGroup.IDS); // make sure our ID generator is always larger + EditorGroupModel.IDS = Math.max(data.id + 1, EditorGroupModel.IDS); // make sure our ID generator is always larger } else { - this._id = EditorGroup.IDS++; // backwards compatibility + this._id = EditorGroupModel.IDS++; // backwards compatibility } this.editors = coalesce(data.editors.map((e, index) => { diff --git a/src/vs/workbench/services/editor/common/editorGroupsService.ts b/src/vs/workbench/services/editor/common/editorGroupsService.ts index 48aedaed07b..ce6aacbe217 100644 --- a/src/vs/workbench/services/editor/common/editorGroupsService.ts +++ b/src/vs/workbench/services/editor/common/editorGroupsService.ts @@ -458,6 +458,11 @@ export interface IEditorGroup { */ readonly count: number; + /** + * Whether the group has editors or not. + */ + readonly isEmpty: boolean; + /** * The number of sticky editors in this group. */ diff --git a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts similarity index 95% rename from src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts rename to src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts index cb4421f01c8..0e2bed00151 100644 --- a/src/vs/workbench/test/browser/parts/editor/editorGroups.test.ts +++ b/src/vs/workbench/test/browser/parts/editor/editorGroupModel.test.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import * as assert from 'assert'; -import { EditorGroup, ISerializedEditorGroup, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroup'; +import { EditorGroupModel, ISerializedEditorGroupModel, EditorCloseEvent } from 'vs/workbench/common/editor/editorGroupModel'; import { Extensions as EditorExtensions, IEditorInputFactoryRegistry, EditorInput, IFileEditorInput, IEditorInputSerializer, CloseDirection, EditorsOrder } from 'vs/workbench/common/editor'; import { URI } from 'vs/base/common/uri'; import { TestLifecycleService, workbenchInstantiationService } from 'vs/workbench/test/browser/workbenchTestServices'; @@ -23,7 +23,7 @@ import { IStorageService } from 'vs/platform/storage/common/storage'; import { DisposableStore } from 'vs/base/common/lifecycle'; import { TestContextService, TestStorageService } from 'vs/workbench/test/common/workbenchTestServices'; -suite('Workbench editor groups', () => { +suite('Workbench editor group model', () => { function inst(): IInstantiationService { let inst = new TestInstantiationService(); @@ -39,17 +39,17 @@ suite('Workbench editor groups', () => { return inst; } - function createGroup(serialized?: ISerializedEditorGroup): EditorGroup { - return inst().createInstance(EditorGroup, serialized); + function createEditorGroupModel(serialized?: ISerializedEditorGroupModel): EditorGroupModel { + return inst().createInstance(EditorGroupModel, serialized); } - function closeAllEditors(group: EditorGroup): void { + function closeAllEditors(group: EditorGroupModel): void { for (const editor of group.getEditors(EditorsOrder.SEQUENTIAL)) { group.closeEditor(editor, false); } } - function closeEditors(group: EditorGroup, except: EditorInput, direction?: CloseDirection): void { + function closeEditors(group: EditorGroupModel, except: EditorInput, direction?: CloseDirection): void { const index = group.indexOf(except); if (index === -1) { return; // not found @@ -87,7 +87,7 @@ suite('Workbench editor groups', () => { disposed: EditorInput[]; } - function groupListener(group: EditorGroup): GroupEvents { + function groupListener(group: EditorGroupModel): GroupEvents { const groupEvents: GroupEvents = { opened: [], closed: [], @@ -236,7 +236,7 @@ suite('Workbench editor groups', () => { }); test('Clone Group', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input() as TestEditorInput; const input2 = input(); @@ -276,7 +276,7 @@ suite('Workbench editor groups', () => { }); test('contains()', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const instantiationService = workbenchInstantiationService(); const input1 = input(); @@ -367,7 +367,7 @@ suite('Workbench editor groups', () => { test('group serialization', function () { inst().invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -379,7 +379,7 @@ suite('Workbench editor groups', () => { group.openEditor(input2, { pinned: true, active: true }); group.openEditor(input3, { pinned: false, active: true }); - let deserialized = createGroup(group.serialize()); + let deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 3); assert.strictEqual(deserialized.getEditors(EditorsOrder.SEQUENTIAL).length, 3); @@ -392,7 +392,7 @@ suite('Workbench editor groups', () => { // Case 2: inputs cannot be serialized TestEditorInputSerializer.disableSerialize = true; - deserialized = createGroup(group.serialize()); + deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 0); assert.strictEqual(deserialized.getEditors(EditorsOrder.SEQUENTIAL).length, 0); @@ -402,7 +402,7 @@ suite('Workbench editor groups', () => { TestEditorInputSerializer.disableSerialize = false; TestEditorInputSerializer.disableDeserialize = true; - deserialized = createGroup(group.serialize()); + deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 0); assert.strictEqual(deserialized.getEditors(EditorsOrder.SEQUENTIAL).length, 0); @@ -411,7 +411,7 @@ suite('Workbench editor groups', () => { test('group serialization (sticky editor)', function () { inst().invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -426,7 +426,7 @@ suite('Workbench editor groups', () => { group.stick(input2); assert.ok(group.isSticky(input2)); - let deserialized = createGroup(group.serialize()); + let deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 3); @@ -445,7 +445,7 @@ suite('Workbench editor groups', () => { // Case 2: inputs cannot be serialized TestEditorInputSerializer.disableSerialize = true; - deserialized = createGroup(group.serialize()); + deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 0); assert.strictEqual(deserialized.stickyCount, 0); @@ -456,7 +456,7 @@ suite('Workbench editor groups', () => { TestEditorInputSerializer.disableSerialize = false; TestEditorInputSerializer.disableDeserialize = true; - deserialized = createGroup(group.serialize()); + deserialized = createEditorGroupModel(group.serialize()); assert.strictEqual(group.id, deserialized.id); assert.strictEqual(deserialized.count, 0); assert.strictEqual(deserialized.stickyCount, 0); @@ -465,7 +465,7 @@ suite('Workbench editor groups', () => { }); test('One Editor', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); assert.strictEqual(group.count, 0); @@ -576,7 +576,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Pinned and Active', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input('1'); @@ -650,7 +650,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Preview editor moves to the side of the active one', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -679,7 +679,7 @@ suite('Workbench editor groups', () => { inst.stub(IConfigurationService, config); config.setUserConfiguration('workbench', { editor: { openPositioning: 'left' } }); - const group: EditorGroup = inst.createInstance(EditorGroup, undefined); + const group: EditorGroupModel = inst.createInstance(EditorGroupModel, undefined); const events = groupListener(group); @@ -703,7 +703,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Pinned and Not Active', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -735,7 +735,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Preview gets overwritten', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input(); @@ -768,7 +768,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - set active', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input(); @@ -803,7 +803,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - pin and unpin', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input(); @@ -852,7 +852,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - closing picks next from MRU list', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input(); @@ -911,7 +911,7 @@ suite('Workbench editor groups', () => { config.setUserConfiguration('workbench', { editor: { focusRecentEditorAfterClose: false } }); inst.stub(IConfigurationService, config); - const group = inst.createInstance(EditorGroup, undefined); + const group = inst.createInstance(EditorGroupModel, undefined); const events = groupListener(group); const input1 = input(); @@ -959,7 +959,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - move editor', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const events = groupListener(group); const input1 = input(); @@ -1021,8 +1021,8 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - move editor across groups', function () { - const group1 = createGroup(); - const group2 = createGroup(); + const group1 = createEditorGroupModel(); + const group2 = createEditorGroupModel(); const g1_input1 = input(); const g1_input2 = input(); @@ -1043,8 +1043,8 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - move editor across groups (input already exists in group 1)', function () { - const group1 = createGroup(); - const group2 = createGroup(); + const group1 = createEditorGroupModel(); + const group2 = createEditorGroupModel(); const g1_input1 = input(); const g1_input2 = input(); @@ -1067,7 +1067,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Pinned & Non Active', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); group.openEditor(input1); @@ -1098,7 +1098,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Close Others, Close Left, Close Right', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -1153,7 +1153,7 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - real user example', function () { - const group = createGroup(); + const group = createEditorGroupModel(); // [] -> /index.html/ const indexHtml = input('index.html'); @@ -1287,7 +1287,7 @@ suite('Workbench editor groups', () => { inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - let group = createGroup(); + let group = createEditorGroupModel(); const input1 = input(); group.openEditor(input1); @@ -1298,7 +1298,7 @@ suite('Workbench editor groups', () => { assert.strictEqual(group.isActive(input1), true); // Create model again - should load from storage - group = inst.createInstance(EditorGroup, group.serialize()); + group = inst.createInstance(EditorGroupModel, group.serialize()); assert.strictEqual(group.count, 1); assert.strictEqual(group.activeEditor!.matches(input1), true); @@ -1321,7 +1321,7 @@ suite('Workbench editor groups', () => { inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - let group1 = createGroup(); + let group1 = createEditorGroupModel(); const g1_input1 = input(); const g1_input2 = input(); @@ -1331,7 +1331,7 @@ suite('Workbench editor groups', () => { group1.openEditor(g1_input2, { active: true, pinned: false }); group1.openEditor(g1_input3, { active: false, pinned: true }); - let group2 = createGroup(); + let group2 = createEditorGroupModel(); const g2_input1 = input(); const g2_input2 = input(); @@ -1357,8 +1357,8 @@ suite('Workbench editor groups', () => { assert.strictEqual(group2.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[2].matches(g2_input2), true); // Create model again - should load from storage - group1 = inst.createInstance(EditorGroup, group1.serialize()); - group2 = inst.createInstance(EditorGroup, group2.serialize()); + group1 = inst.createInstance(EditorGroupModel, group1.serialize()); + group2 = inst.createInstance(EditorGroupModel, group2.serialize()); assert.strictEqual(group1.count, 3); assert.strictEqual(group2.count, 3); @@ -1391,7 +1391,7 @@ suite('Workbench editor groups', () => { inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - let group = createGroup(); + let group = createEditorGroupModel(); const serializableInput1 = input(); const nonSerializableInput2 = input('3', true); @@ -1410,7 +1410,7 @@ suite('Workbench editor groups', () => { assert.strictEqual(group.getEditors(EditorsOrder.MOST_RECENTLY_ACTIVE)[2].matches(serializableInput1), true); // Create model again - should load from storage - group = inst.createInstance(EditorGroup, group.serialize()); + group = inst.createInstance(EditorGroupModel, group.serialize()); assert.strictEqual(group.count, 2); assert.strictEqual(group.activeEditor!.matches(serializableInput2), true); @@ -1435,7 +1435,7 @@ suite('Workbench editor groups', () => { inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - let group = createGroup(); + let group = createEditorGroupModel(); const serializableInput1 = input(); const nonSerializableInput2 = input('3', true); @@ -1449,7 +1449,7 @@ suite('Workbench editor groups', () => { assert.strictEqual(group.stickyCount, 1); // Create model again - should load from storage - group = inst.createInstance(EditorGroup, group.serialize()); + group = inst.createInstance(EditorGroupModel, group.serialize()); assert.strictEqual(group.count, 2); assert.strictEqual(group.stickyCount, 0); @@ -1470,8 +1470,8 @@ suite('Workbench editor groups', () => { inst.invokeFunction(accessor => Registry.as(EditorExtensions.EditorInputFactories).start(accessor)); - let group1 = createGroup(); - let group2 = createGroup(); + let group1 = createEditorGroupModel(); + let group2 = createEditorGroupModel(); const serializableInput1 = input(); const serializableInput2 = input(); @@ -1483,8 +1483,8 @@ suite('Workbench editor groups', () => { group2.openEditor(nonSerializableInput); // Create model again - should load from storage - group1 = inst.createInstance(EditorGroup, group1.serialize()); - group2 = inst.createInstance(EditorGroup, group2.serialize()); + group1 = inst.createInstance(EditorGroupModel, group1.serialize()); + group2 = inst.createInstance(EditorGroupModel, group2.serialize()); assert.strictEqual(group1.count, 2); assert.strictEqual(group1.getEditors(EditorsOrder.SEQUENTIAL)[0].matches(serializableInput1), true); @@ -1492,8 +1492,8 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Editor Dispose', function () { - const group1 = createGroup(); - const group2 = createGroup(); + const group1 = createEditorGroupModel(); + const group2 = createEditorGroupModel(); const group1Listener = groupListener(group1); const group2Listener = groupListener(group2); @@ -1523,7 +1523,7 @@ suite('Workbench editor groups', () => { }); test('Preview tab does not have a stable position (https://github.com/microsoft/vscode/issues/8245)', function () { - const group1 = createGroup(); + const group1 = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -1538,8 +1538,8 @@ suite('Workbench editor groups', () => { }); test('Multiple Editors - Editor Emits Dirty and Label Changed', function () { - const group1 = createGroup(); - const group2 = createGroup(); + const group1 = createEditorGroupModel(); + const group2 = createEditorGroupModel(); const input1 = input(); const input2 = input(); @@ -1591,7 +1591,7 @@ suite('Workbench editor groups', () => { }); test('Sticky Editors', function () { - const group = createGroup(); + const group = createEditorGroupModel(); const input1 = input(); const input2 = input(); diff --git a/src/vs/workbench/test/browser/workbenchTestServices.ts b/src/vs/workbench/test/browser/workbenchTestServices.ts index d8be96298ee..cd3540a637d 100644 --- a/src/vs/workbench/test/browser/workbenchTestServices.ts +++ b/src/vs/workbench/test/browser/workbenchTestServices.ts @@ -54,7 +54,6 @@ import { IEditorGroupsService, IEditorGroup, GroupsOrder, GroupsArrangement, Gro import { IEditorService, IOpenEditorOverrideHandler, ISaveEditorsOptions, IRevertAllEditorsOptions, IResourceEditorInputType, SIDE_GROUP_TYPE, ACTIVE_GROUP_TYPE, IOpenEditorOverrideEntry } from 'vs/workbench/services/editor/common/editorService'; import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; import { IEditorRegistry, EditorDescriptor, Extensions } from 'vs/workbench/browser/editor'; -import { EditorGroup } from 'vs/workbench/common/editor/editorGroup'; import { Dimension, IDimension } from 'vs/base/browser/dom'; import { ILogService, NullLogService } from 'vs/platform/log/common/log'; import { ILabelService } from 'vs/platform/label/common/label'; @@ -659,7 +658,6 @@ export class TestEditorGroupView implements IEditorGroupView { constructor(public id: number) { } - get group(): EditorGroup { throw new Error('not implemented'); } activeEditorPane!: IVisibleEditorPane; activeEditor!: IEditorInput; previewEditor!: IEditorInput;