Skip to content

Commit

Permalink
editors - reuse existing side by side editor when identical input ope…
Browse files Browse the repository at this point in the history
…ned (#36700)
  • Loading branch information
bpasero committed Sep 8, 2021
1 parent cc767f0 commit fb60d95
Show file tree
Hide file tree
Showing 3 changed files with 169 additions and 42 deletions.
12 changes: 6 additions & 6 deletions src/vs/workbench/browser/parts/editor/editorGroupView.ts
Expand Up @@ -4,7 +4,7 @@
*--------------------------------------------------------------------------------------------*/

import 'vs/css!./media/editorgroupview';
import { EditorGroupModel, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroupModel, isSerializedEditorGroupModel } from 'vs/workbench/common/editor/editorGroupModel';
import { EditorGroupModel, IEditorOpenOptions, EditorCloseEvent, ISerializedEditorGroupModel, isSerializedEditorGroupModel, SideBySideMatchingStrategy } from 'vs/workbench/common/editor/editorGroupModel';
import { GroupIdentifier, CloseDirection, IEditorCloseEvent, ActiveEditorDirtyContext, IEditorPane, EditorGroupEditorsCountContext, SaveReason, IEditorPartOptionsChangeEvent, EditorsOrder, IVisibleEditorPane, ActiveEditorStickyContext, ActiveEditorPinnedContext, EditorResourceAccessor, IEditorMoveEvent, EditorInputCapabilities, IEditorOpenEvent, IUntypedEditorInput, DEFAULT_EDITOR_ASSOCIATION, ActiveEditorGroupLockedContext, IEditorInput } from 'vs/workbench/common/editor';
import { EditorInput } from 'vs/workbench/common/editor/editorInput';
import { SideBySideEditorInput } from 'vs/workbench/common/editor/sideBySideEditorInput';
Expand Down Expand Up @@ -613,8 +613,8 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
private canDispose(editor: IEditorInput): boolean {
for (const groupView of this.accessor.groups) {
if (groupView instanceof EditorGroupView && groupView.model.contains(editor, {
strictEquals: true, // only if this input is not shared across editor groups
supportSideBySide: true // include side by side editor primary & secondary
strictEquals: true, // only if this input is not shared across editor groups
supportSideBySide: SideBySideMatchingStrategy.ANY_SIDE // include any side of an opened side by side editor
})) {
return false;
}
Expand Down Expand Up @@ -1031,19 +1031,19 @@ export class EditorGroupView extends Themable implements IEditorGroupView {
let lock = false;

// By `typeId`
if (this.accessor.partOptions.experimentalAutoLockGroups?.has(editor.typeId)) {
if (this.accessor.partOptions.experimentalAutoLockGroups?.has(openedEditor.typeId)) {
lock = true;
}

// By `editorId`
else if (editor.editorId && this.accessor.partOptions.experimentalAutoLockGroups?.has(editor.editorId)) {
else if (openedEditor.editorId && this.accessor.partOptions.experimentalAutoLockGroups?.has(openedEditor.editorId)) {
lock = true;
}

// By `viewType` (TODO@bpasero remove this hack once editors have adopted `editorId`)
// See https://github.com/microsoft/vscode/issues/131692
else {
const editorViewType = (editor as { viewType?: string }).viewType;
const editorViewType = (openedEditor as { viewType?: string }).viewType;
if (editorViewType && this.accessor.partOptions.experimentalAutoLockGroups?.has(editorViewType)) {
lock = true;
}
Expand Down
77 changes: 62 additions & 15 deletions src/vs/workbench/common/editor/editorGroupModel.ts
Expand Up @@ -61,6 +61,37 @@ export function isSerializedEditorGroupModel(group?: unknown): group is ISeriali
return !!(candidate && typeof candidate === 'object' && Array.isArray(candidate.editors) && Array.isArray(candidate.mru));
}

export enum SideBySideMatchingStrategy {

/**
* Consider an editor to match a side by side
* editor if any of the two sides match.
*/
ANY_SIDE = 1,

/**
* Consider an editor to match a side by side
* editor if both sides match.
*/
BOTH_SIDES
}

export interface IMatchOptions {

/**
* Whether to support side by side editors when
* considering a match.
*/
supportSideBySide?: SideBySideMatchingStrategy;

/**
* Only consider an editor to match when the
* `candidate === editor` but not when
* `candidate.matches(editor)`.
*/
strictEquals?: boolean;
}

export class EditorGroupModel extends Disposable {

private static IDS = 0;
Expand Down Expand Up @@ -189,7 +220,13 @@ export class EditorGroupModel extends Disposable {
const makePinned = options?.pinned || options?.sticky;
const makeActive = options?.active || !this.activeEditor || (!makePinned && this.matches(this.preview, this.activeEditor));

const existingEditorAndIndex = this.findEditor(candidate);
const existingEditorAndIndex = this.findEditor(candidate, {
// Allow to match on a side-by-side editor when same
// editor is opened on both sides. In that case we
// do not want to open a new editor but reuse that one.
// For: https://github.com/microsoft/vscode/issues/36700
supportSideBySide: SideBySideMatchingStrategy.BOTH_SIDES
});

// New editor
if (!existingEditorAndIndex) {
Expand Down Expand Up @@ -687,54 +724,64 @@ export class EditorGroupModel extends Disposable {
}
}

indexOf(candidate: IEditorInput | null, editors = this.editors): number {
indexOf(candidate: IEditorInput | null, editors = this.editors, options?: IMatchOptions): number {
if (!candidate) {
return -1;
}

for (let i = 0; i < editors.length; i++) {
if (this.matches(editors[i], candidate)) {
if (this.matches(editors[i], candidate, options)) {
return i;
}
}

return -1;
}

private findEditor(candidate: EditorInput | null): [EditorInput, number /* index */] | undefined {
const index = this.indexOf(candidate, this.editors);
private findEditor(candidate: EditorInput | null, options?: IMatchOptions): [EditorInput, number /* index */] | undefined {
const index = this.indexOf(candidate, this.editors, options);
if (index === -1) {
return undefined;
}

return [this.editors[index], index];
}

contains(candidate: EditorInput | IUntypedEditorInput, options?: { supportSideBySide?: boolean, strictEquals?: boolean }): boolean {
contains(candidate: EditorInput | IUntypedEditorInput, options?: IMatchOptions): boolean {
for (const editor of this.editors) {
if (this.matches(editor, candidate, options?.strictEquals)) {
if (this.matches(editor, candidate, options)) {
return true;
}

if (options?.supportSideBySide && editor instanceof SideBySideEditorInput) {
if (this.matches(editor.primary, candidate, options?.strictEquals) || this.matches(editor.secondary, candidate, options?.strictEquals)) {
return true;
}
}
}

return false;
}

private matches(editor: IEditorInput | null, candidate: IEditorInput | IUntypedEditorInput | null, strictEquals?: boolean): boolean {
private matches(editor: IEditorInput | null, candidate: IEditorInput | IUntypedEditorInput | null, options?: IMatchOptions): boolean {
if (!editor || !candidate) {
return false;
}

if (strictEquals) {
if (options?.strictEquals) {
return editor === candidate;
}

if (options?.supportSideBySide && editor instanceof SideBySideEditorInput && !(candidate instanceof SideBySideEditorInput)) {
switch (options.supportSideBySide) {
case SideBySideMatchingStrategy.ANY_SIDE:
if (this.matches(editor.primary, candidate, options) || this.matches(editor.secondary, candidate, options)) {
return true;
}
break;
case SideBySideMatchingStrategy.BOTH_SIDES:
if (this.matches(editor.primary, candidate, options) && this.matches(editor.secondary, candidate, options)) {
return true;
}
break;
}

}

return editor.matches(candidate);
}

Expand Down

0 comments on commit fb60d95

Please sign in to comment.