Skip to content

Editor splits / multiple groups #60

Closed
2 of 2 issues completed
Closed

Description

@jamescrosswell

Opportunity

You can see one file at a time. Much of the work the vision describes is "navigating code bases, reviewing PRs and trying to understand existing/new code". A lot of that needs two files in view at once: a test next to the code it covers, an interface next to its implementation, a caller next to the method it calls, a doc next to the code it describes. Reviewing a PR (#126, shipped) makes it sharper: you read a diff tab and want the file it calls into beside it. Today you flip tabs with Alt+Tab and hold the other file in your head, or open a second TuiCode in a terminal split. That copy has its own tabs, explorer and unsaved state, and neither knows about the other, so saving in one leaves the other with a stale buffer.

VS Code (Split Editor, Ctrl+\), JetBrains (Split Right) and every terminal editor (Vim, Helix, micro) let you do this. It's one of the first things a VS Code user reaches for, so it fits Familiar. It isn't multiplexing (What it deliberately isn't): it's one editor with one set of buffers, laid out twice.

Options considered

  1. Do nothing: a second TuiCode in a terminal split. It costs nothing and works today. But the two copies don't share buffers, tabs or state, so you can edit one file in both and lose changes. The disk-change notices (Notice when open files change on disk #133) soften that, but only after it has happened.
  2. A read-only peek. A command that shows a second file (or the definition under the cursor) in a pane you can scroll but not edit, like VS Code's Peek. It's cheaper because it's not a real editor. But you can't fix the test while reading the code, and most of what you'd want to peek at needs language-aware navigation (Finding where a symbol is defined or used means a text search full of false hits #63), which we don't have.
  3. Two editor groups; a tab moves between them. (Proposed.) Each group is a normal editor with its own tabs. A file lives in one group at a time, so there's never a second copy of a buffer to reconcile. Test beside code works fully, with editing.
  4. VS Code-style splits: the same file in both groups, any number of groups, stacked or side by side. The most familiar option. But two live views of one buffer need a shared document model under both, and today each tab owns its text, undo history, carets and dirty state. That rebuild is the bulk of the cost, and it serves a less common case (two places in one file).

Option 3 gives the common case, two different files side by side, for a fraction of option 4's cost, and it doesn't rule out option 4 later.

Proposal

The editor area can hold two editor groups side by side. Each group has its own tab strip. Commands act on the focused group: the group you last clicked or moved focus into, drawn with the focused border.

  • Move to other group (mg, Ctrl+\): moves the active tab into the other group, creating it on the right if there's only one. Splitting is just this: with Program.cs and ProgramTests.cs open, Ctrl+\ puts the tests on the right.
  • Focus other group (fo, F6): moves focus to the other group's active tab.
  • Join groups (jg): moves every tab back into one group.
  • Closing a group's last tab closes the group, and the other one takes the full width, as in VS Code. Moving a group's only tab out also unsplits.
  • All three sit under View in the menu bar.

One file, one tab. A file is open in at most one group. If you open a file that's already open in the other group (explorer, Ctrl+O, a Find result, cursor history, the Review tab), focus goes there instead of opening a second copy. New files open in the focused group. mg on a group's only tab, with no split yet, says Open another file to split with in the status bar.

Everything that already follows "the active tab" follows the focused group's active tab: save, close, the find bar, go to line, Ctrl+1…9, next/previous tab, change grammar, the status bar's path, Ln, Col and selected count, document info (di), blame (Ctrl+G B), the Compare commands, cursor history and multi-cursor carets. Explorer rename, move and delete, disk-change notices, and global find/replace over open buffers cover both groups. A diff tab is just a tab, so it can sit in either group.

The split is saved with the folder's workspace state, so reopening the folder restores both groups and their tabs. State files from before this load as one group.

Mockup

120 columns, sidebar showing, focus in the right group:

┌ Explorer │ Find ────┐┌ Editor ───────────────────────────────┐┏ Editor ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┓
│ ▾ src                ││ LineDiff.cs  EditorGutter.cs          │┃ LineDiffTests.cs  EditorGutterTests.cs         ┃
│   ▾ TuiCode.Editor   ││ 12  public static class LineDiff      │┃ 40  [Fact]                                     ┃
│       EditorGroup.cs ││ 13  {                                 │┃ 41  public void Hunks_trims_common_prefix()    ┃
│       LineDiff.cs    ││ 14      public const int MaxEdits = … │┃ 42  {                                          ┃
│ ▾ tests              ││ 15                                    │┃ 43      var hunks = LineDiff.Hunks(["a", "b"], ┃
│     LineDiffTests.cs ││ 16      public static IReadOnlyList<… │┃ 44          ["a", "c"]);                       ┃
└──────────────────────┘└───────────────────────────────────────┘┗━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━┛
 tests/TuiCode.Tests/LineDiffTests.cs  •  C#                                                          Ln 43, Col 9

View menu:

 View
┌──────────────────────────────┐
│ …                            │
│──────────────────────────────│
│ Move to other group  Ctrl+\  │
│ Focus other group        F6  │
│ Join groups                  │
└──────────────────────────────┘

mg with the file as its group's only tab and no split:

 Open another file to split with                                                                      Ln 1, Col 1

Scope

In

  • Two groups, side by side, splitting the width evenly.
  • mg / Ctrl+\, fo / F6, jg, closing an empty group, and a View menu home for each.
  • Every active-tab feature following the focused group. Explorer file operations, disk-change notices and global find covering both groups.
  • One file per tab, across both groups.
  • The split saved and restored with workspace state.

Out (later, or never)

  • The same file in both groups (option 4). Worth its own pitch if moving tabs turns out not to be enough.
  • More than two groups, grids, and stacked (top/bottom) groups.
  • Dragging tabs between groups, or resizing the split with the mouse.
  • Anything diff-specific.

Delivery

One PR, #460. Moving tabs, focus, every active-tab feature, find, cursor history and saving the split all change the same code in WorkbenchHost. Split up, each part would need its own try and review of nearly the same diff. It waits for #451, which reworks how files open.

Decided

  1. Move, not copy. A tab moves between groups; the same file never shows in both. (Stakeholder.)
  2. Side by side only. No automatic stacking at narrow widths. (Stakeholder.)
  3. Keys. Ctrl+\ for mg, F6 for fo, no key for jg. (Stakeholder.)
  4. One PR, not the three slices sketched earlier. None of the later slices needs the first in use before it can be judged. (Lead.)

Original idea

Split out from #16.

Touches focus management; a second EditorGroup and a way to direct Open to a specific one.

Directional, not a spec — needs a real plan when picked up.

Activity

  1. added
    pitchAn a-team pitch: Lead shapes it, reviewer approves it
    on Sep 18, 2026
  2. jamescrosswell commented on Sep 19, 2026

    @jamescrosswell
    ContributorAuthor

    Let's pick up some higher priority issues before tackling this.

  3. jamescrosswell commented on Sep 19, 2026

    @jamescrosswell
    ContributorAuthor

    Got it — parking this behind higher-priority work. The board script won't let the Lead move a pitch out of Pitched, so please move it back to Exploring (or Idea) yourself; while it sits in Pitched it holds one of the three Pitched slots. When there's room I'll promote from Exploring by priority, so a lower priority on this one keeps it behind the others. I've opened mentaldesk/a-team#24 so the Lead can do this itself next time.

  4. a-team-app commented on Oct 1, 2026

    @a-team-app
    Contributor

    Back in Pitched now that there's room. I checked it against today's main. The only change: the View menu (#340) has shipped, so the mockup and proposal no longer say "once #340 lands". Keys Ctrl+\ and F6, and the mnemonics mg, fo and jg, are all still free. The three open questions are unchanged.

  5. jamescrosswell commented on Oct 4, 2026

    @jamescrosswell
    ContributorAuthor
    • Move, not copy. VS Code's Ctrl+\ copies the tab, so the same file can show in both groups. I've proposed moving it, because sharing a buffer between two views is the expensive part. Is two different files side by side enough to start with? I think it is: test beside code is the common case.

    Yes Move.

    • Side by side only? With the sidebar showing, 120 columns leaves each group about 45. At 80 columns it's about 25, too narrow to read code. I'd ship side by side only and hide the sidebar (ts) when it's tight. Or should it stack automatically below some width?

    Yes side by side only. People always have more horizontal than vertical space on a computer.

    • Keys. Ctrl+\ for mg (VS Code's split key) and F6 for fo (VS Code's key to cycle focus between parts). Both are unbound today. Ctrl+\ still needs checking in Windows Terminal and over SSH; if it doesn't arrive, mg and the View menu still work. jg gets no key. OK?

    OK.

  6. a-team-app commented on Oct 4, 2026

    @a-team-app
    Contributor

    Thanks. All three answers folded in: the open questions are now under Assumed (move not copy, side by side only, Ctrl+\ / F6 / no key for jg). Nothing left to ask, so it's ready to approve whenever you are.

  7. a-team-app commented on Oct 8, 2026

    @a-team-app
    Contributor

    Broken down into one task: #460, the whole two-group split in one PR. It waits for #451, which reworks how files open.

    I've folded the three slices sketched in the pitch into one PR, as you asked on #439. The later slices didn't need the first in use before you could judge them. Say if you'd rather have it split.

  8. a-team-app commented on Oct 11, 2026

    @a-team-app
    Contributor

    Tried the split on main after #500: Ctrl+\, F6, Join groups, closing a group's last tab, one-file-one-tab, the View menu, palette, F1 and quitting with edits in both groups all work.

    Two things go wrong after reopening a split folder: focus comes back on the other group, and the right group's files start scrolled one column (amespace). Filed #513 for both; I'll close the pitch once it merges.

  9. a-team-app commented on Oct 12, 2026

    @a-team-app
    Contributor

    Tried it on origin/main (68bffda): opened README.md and AGENTS.md, mg put AGENTS.md in a second group on the right with focus and the status-bar path following it. After quitting and reopening the folder both groups came back with their tabs, and F6 switched focus between them. Matches the pitch, so I'm closing it.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    pitchAn a-team pitch: Lead shapes it, reviewer approves it

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions