diff --git a/CHANGES_CURRENT.md b/CHANGES_CURRENT.md index 68f332c358..4f7c4d349c 100644 --- a/CHANGES_CURRENT.md +++ b/CHANGES_CURRENT.md @@ -38,6 +38,7 @@ - #3732 - Input: Fix remapped keys executing in wrong order (fixes #3729) - #3747 - Layout: Implement Control+W, C binding (related #1721) - #3746 - Extension: Fix edit application in trailing spaces plugin +- #3755 - Vim: Fix extra 'editor tab' with `:tabnew`/`:tabedit` (fixes #3150) ### Performance diff --git a/integration_test/RegressionLayoutTabSingleEditor.re b/integration_test/RegressionLayoutTabSingleEditor.re new file mode 100644 index 0000000000..18f1cdb5ba --- /dev/null +++ b/integration_test/RegressionLayoutTabSingleEditor.re @@ -0,0 +1,51 @@ +// Regression test for #3150: +// When running ':tabnew'/':tabedit' on a buffer, only a single +// editor tab should be visible. + +open Oni_Core; +open Oni_Model; +open Oni_IntegrationTestLib; +module Editor = Feature_Editor.Editor; + +runTest(~name="RegressionLayoutTabSingleEditor", ({wait, _}) => { + wait(~name="Wait for split to be created 1", (state: State.t) => { + let splitCount = + state.layout |> Feature_Layout.visibleEditors |> List.length; + splitCount == 1; + }); + + // Wait for initial buffer + wait(~name="Initial buffer", state => { + let maybeFilePath = + state |> Selectors.getActiveBuffer |> Option.map(Buffer.getFilePath); + + maybeFilePath != None; + }); + + wait( + ~name="Prior to new tab, there should be a single layout", + (state: State.t) => { + let layoutCount = state.layout |> Feature_Layout.layouts |> List.length; + + layoutCount == 1; + }); + + /* :vsp with no arguments should create a second split w/ same buffer */ + ignore(Vim.command("tabnew"): (Vim.Context.t, list(Vim.Effect.t))); + + wait(~name="Wait for group to be created", (state: State.t) => { + let layoutCount = state.layout |> Feature_Layout.layouts |> List.length; + + layoutCount == 2; + }); + + wait( + ~name="There should only be a single editor in the new tab", + (state: State.t) => { + let group = state.layout |> Feature_Layout.activeGroup; + + let editorCount = group |> Feature_Layout.Group.allEditors |> List.length; + + editorCount == 1; + }); +}); diff --git a/integration_test/dune b/integration_test/dune index 1ae88e31b0..4dbb56293b 100644 --- a/integration_test/dune +++ b/integration_test/dune @@ -22,7 +22,9 @@ Regression2988SwitchEditorTest Regression3084CommandLoopTest Regression3323DiagnosticsTest RegressionCommandLineNoCompletionTest RegressionFontFallbackTest RegressionFileModifiedIndicationTest - RegressionNonExistentDirectory RegressionVspEmptyInitialBufferTest + RegressionNonExistentDirectory + RegressionLayoutTabSingleEditor + RegressionVspEmptyInitialBufferTest RegressionVspEmptyExistingBufferTest SCMGitTest SyntaxHighlightPhpTest SyntaxHighlightTextMateTest SyntaxHighlightTreesitterTest AddRemoveSplitTest TerminalSetPidTitleTest TerminalConfigurationTest @@ -58,6 +60,7 @@ Regression3084CommandLoopTest.exe RegressionCommandLineNoCompletionTest.exe RegressionFileModifiedIndicationTest.exe RegressionFontFallbackTest.exe + RegressionLayoutTabSingleEditor.exe RegressionVspEmptyInitialBufferTest.exe RegressionNonExistentDirectory.exe RegressionVspEmptyExistingBufferTest.exe SCMGitTest.exe SearchShowClearHighlightsTest.exe SyntaxHighlightTextMateTest.exe diff --git a/src/Feature/Layout/Feature_Layout.re b/src/Feature/Layout/Feature_Layout.re index fb1ca80e91..662ad54dd6 100644 --- a/src/Feature/Layout/Feature_Layout.re +++ b/src/Feature/Layout/Feature_Layout.re @@ -571,7 +571,9 @@ let update = (~focus, model, msg) => { | Command(ResetSizes) => (resetWeights(model), Nothing) - | Command(AddLayout) => (addLayoutTab(model), Nothing) + | Command(AddLayout) => + let editor = Feature_Editor.Editor.copy(activeEditor(model)); + (addLayoutTab(~editor, model), Nothing); | Command(PreviousLayout) => ( { diff --git a/src/Feature/Layout/Feature_Layout.rei b/src/Feature/Layout/Feature_Layout.rei index 9ac69e5a96..1de421c9e8 100644 --- a/src/Feature/Layout/Feature_Layout.rei +++ b/src/Feature/Layout/Feature_Layout.rei @@ -20,12 +20,21 @@ module Group: { let allEditors: t => list(Editor.t); }; +module LayoutTab: { + type t; + + let groups: t => list(Group.t); +}; + type model; let activeGroup: model => Group.t; let activeLayoutGroups: model => list(Group.t); let setActiveGroup: (Group.id, model) => model; +let layouts: model => list(LayoutTab.t); +let activeLayout: model => LayoutTab.t; + let initial: list(Editor.t) => model; let visibleEditors: model => list(Editor.t); @@ -47,7 +56,7 @@ let activeGroupEditors: model => list(Editor.t); let openEditor: (~config: Config.resolver, Editor.t, model) => model; let closeBuffer: (~force: bool, Vim.Types.buffer, model) => option(model); -let addLayoutTab: model => model; +let addLayoutTab: (~editor: Editor.t, model) => model; let gotoLayoutTab: (int, model) => model; let previousLayoutTab: (~count: int=?, model) => model; let nextLayoutTab: (~count: int=?, model) => model; diff --git a/src/Feature/Layout/Model.re b/src/Feature/Layout/Model.re index 1a770c70cb..4b3afd823f 100644 --- a/src/Feature/Layout/Model.re +++ b/src/Feature/Layout/Model.re @@ -174,6 +174,12 @@ type layout = { activeGroupId: int, }; +module LayoutTab = { + type t = layout; + + let groups = ({groups, _}: layout) => groups; +}; + let activeTree = layout => switch (layout.uncommittedTree) { | `Resizing(tree) @@ -199,6 +205,8 @@ let initial = editors => { {layouts: [initialLayout], activeLayoutIndex: 0}; }; +let layouts = ({layouts, _}) => layouts; + let groups = ({groups, _}) => groups; let groupById = (id, layout) => @@ -471,9 +479,8 @@ let closeBuffer = (~force, buffer, model) => { }; }; -let addLayoutTab = model => { - let newEditor = activeEditor(model) |> Editor.copy; - let newGroup = Group.create([newEditor]); +let addLayoutTab = (~editor, model) => { + let newGroup = Group.create([editor]); let newLayout = { tree: Layout.singleton(newGroup.id), diff --git a/src/Store/Features.re b/src/Store/Features.re index a9a92d7e86..aadfe8fc3c 100644 --- a/src/Store/Features.re +++ b/src/Store/Features.re @@ -1529,7 +1529,8 @@ let update = ) | SplitDirection.Vertical({shouldReuse}) => Feature_Layout.split(~shouldReuse, ~editor, `Vertical, state.layout) - | SplitDirection.NewTab => Feature_Layout.addLayoutTab(state.layout) + | SplitDirection.NewTab => + Feature_Layout.addLayoutTab(~editor, state.layout) }; let editor' =