detached e0bc7dc

test(core): cover cascade move-column-right across bands and a real lone float

commit
e0bc7dc73316befb0a8419bc052806ce0fb637de
author
brice · · 2026-10-12 12:48 UTC
parent
9c3cc20
signature
GPG VerifiedCD88F9E564E0102646FB6077A1A07C7C7C3E349D · arch-gpg Verified for the committer email
4 files · +17 -12
M docs/config.md +1 -1
@@ -267,7 +267,7 @@ In a row, the auto columns share the cells the set ones leave; when no cell is l
267267 | `maximize-column` | Toggle full usable width for the focused column, preserving gaps and its saved width; in fixed overflow, other columns are hidden until focus moves or it is toggled off. On a window that made itself fullscreen (e.g. a Wine app at monitor size), it first returns the window to its column |
268268 | `cycle-column-width` | Grow the focused column by one cell, taking it from the widest other column of its row (see [Column widths](#column-widths)). When no column has a cell to give, every column of the row goes back to equal shares. In scroll overflow, the column grows to `max-columns` cells, then back to auto. A lone column or a full row cannot grow: it goes back to equal shares if its cells were set, and otherwise nothing changes. A spiral never changes |
269269 | `focus-column-left/right` | Focus the neighbor column; at the edge, the neighbor monitor. In cascade overflow the edge is the band's, and past it with no monitor the previous or next workspace. In fixed overflow, the neighbor on screen. From a fullscreen window, the neighbor column comes first and fullscreen ends; at the edge it stays |
270-| `focus-window-up/down` | Focus in the column; past the edge, the next workspace (in cascade overflow, the adjacent band, stopping at the first and last). In fixed overflow, the column on screen above or below comes first. Up at the top brings back a covering float put behind the columns. From a fullscreen window, a window above or below comes first and fullscreen ends |
270+| `focus-window-up/down` | Focus in the column; past the edge, the previous or next workspace (in cascade overflow, the adjacent band, stopping at the first and last). In fixed overflow, the column on screen above or below comes first. Up at the top brings back a covering float put behind the columns. From a fullscreen window, a window above or below comes first and fullscreen ends |
271271 | `move-column-left/right` | Move the focused column; at the edge, to the neighbor monitor, focus following. In cascade overflow the edge is the band's; with no monitor there, the column crosses into the adjacent band. Fixed overflow follows column order, not the screen |
272272 | `move-window-up/down` | Move the focused window up or down in its column; focus follows it. In cascade overflow, past the column, the column moves to the adjacent band, where `focus-window-up/down` goes |
273273 | `set-column-width <+N\|-N>` | Widen or narrow the focused column by N cells (1-16), never below one cell and without cycling back to equal shares. In `cascade` and `fixed` overflow the cells come from, or go to, the other columns of the row; in `scroll` overflow the column grows on its own, up to `max-columns` cells |
@@ -267,7 +267,7 @@ In a row, the auto columns share the cells the set ones leave; when no cell is l
267 | `maximize-column` | Toggle full usable width for the focused column, preserving gaps and its saved width; in fixed overflow, other columns are hidden until focus moves or it is toggled off. On a window that made itself fullscreen (e.g. a Wine app at monitor size), it first returns the window to its column |267 | `maximize-column` | Toggle full usable width for the focused column, preserving gaps and its saved width; in fixed overflow, other columns are hidden until focus moves or it is toggled off. On a window that made itself fullscreen (e.g. a Wine app at monitor size), it first returns the window to its column |
268 | `cycle-column-width` | Grow the focused column by one cell, taking it from the widest other column of its row (see [Column widths](#column-widths)). When no column has a cell to give, every column of the row goes back to equal shares. In scroll overflow, the column grows to `max-columns` cells, then back to auto. A lone column or a full row cannot grow: it goes back to equal shares if its cells were set, and otherwise nothing changes. A spiral never changes |268 | `cycle-column-width` | Grow the focused column by one cell, taking it from the widest other column of its row (see [Column widths](#column-widths)). When no column has a cell to give, every column of the row goes back to equal shares. In scroll overflow, the column grows to `max-columns` cells, then back to auto. A lone column or a full row cannot grow: it goes back to equal shares if its cells were set, and otherwise nothing changes. A spiral never changes |
269 | `focus-column-left/right` | Focus the neighbor column; at the edge, the neighbor monitor. In cascade overflow the edge is the band's, and past it with no monitor the previous or next workspace. In fixed overflow, the neighbor on screen. From a fullscreen window, the neighbor column comes first and fullscreen ends; at the edge it stays |269 | `focus-column-left/right` | Focus the neighbor column; at the edge, the neighbor monitor. In cascade overflow the edge is the band's, and past it with no monitor the previous or next workspace. In fixed overflow, the neighbor on screen. From a fullscreen window, the neighbor column comes first and fullscreen ends; at the edge it stays |
270-| `focus-window-up/down` | Focus in the column; past the edge, the next workspace (in cascade overflow, the adjacent band, stopping at the first and last). In fixed overflow, the column on screen above or below comes first. Up at the top brings back a covering float put behind the columns. From a fullscreen window, a window above or below comes first and fullscreen ends |270+| `focus-window-up/down` | Focus in the column; past the edge, the previous or next workspace (in cascade overflow, the adjacent band, stopping at the first and last). In fixed overflow, the column on screen above or below comes first. Up at the top brings back a covering float put behind the columns. From a fullscreen window, a window above or below comes first and fullscreen ends |
271 | `move-column-left/right` | Move the focused column; at the edge, to the neighbor monitor, focus following. In cascade overflow the edge is the band's; with no monitor there, the column crosses into the adjacent band. Fixed overflow follows column order, not the screen |271 | `move-column-left/right` | Move the focused column; at the edge, to the neighbor monitor, focus following. In cascade overflow the edge is the band's; with no monitor there, the column crosses into the adjacent band. Fixed overflow follows column order, not the screen |
272 | `move-window-up/down` | Move the focused window up or down in its column; focus follows it. In cascade overflow, past the column, the column moves to the adjacent band, where `focus-window-up/down` goes |272 | `move-window-up/down` | Move the focused window up or down in its column; focus follows it. In cascade overflow, past the column, the column moves to the adjacent band, where `focus-window-up/down` goes |
273 | `set-column-width <+N\|-N>` | Widen or narrow the focused column by N cells (1-16), never below one cell and without cycling back to equal shares. In `cascade` and `fixed` overflow the cells come from, or go to, the other columns of the row; in `scroll` overflow the column grows on its own, up to `max-columns` cells |273 | `set-column-width <+N\|-N>` | Widen or narrow the focused column by N cells (1-16), never below one cell and without cycling back to equal shares. In `cascade` and `fixed` overflow the cells come from, or go to, the other columns of the row; in `scroll` overflow the column grows on its own, up to `max-columns` cells |
M internal/core/action.go +3 -2
@@ -197,8 +197,9 @@ type Effect struct {
197197 }
198198
199199 // applyAction runs a bind action. Monitor actions change the focused
200-// screen; column focus past the edge column moves to the neighbor screen;
201-// everything else applies to the focused screen's monitor.
200+// screen; column focus past the edge column moves to the neighbor screen,
201+// else along the layout's workspace axis; everything else applies to the
202+// focused screen's monitor.
202203 func (c *Core) applyAction(a Action) Effect {
203204 if e, ok := c.cur().mon.overviewAction(a); ok {
204205 return e
@@ -197,8 +197,9 @@ type Effect struct {
197 }197 }
198 198
199 // applyAction runs a bind action. Monitor actions change the focused199 // applyAction runs a bind action. Monitor actions change the focused
200-// screen; column focus past the edge column moves to the neighbor screen;200+// screen; column focus past the edge column moves to the neighbor screen,
201-// everything else applies to the focused screen's monitor.201+// else along the layout's workspace axis; everything else applies to the
202+// focused screen's monitor.
202 func (c *Core) applyAction(a Action) Effect {203 func (c *Core) applyAction(a Action) Effect {
203 if e, ok := c.cur().mon.overviewAction(a); ok {204 if e, ok := c.cur().mon.overviewAction(a); ok {
204 return e205 return e
M internal/core/cascade_moves_test.go +5 -1
@@ -104,7 +104,7 @@ func TestCascadeInsideBandStaysOnMonitor(t *testing.T) {
104104 // band: at the band edge they step the workspace (the first has none before
105105 // it; the last is the trailing empty one). up/down cross bands, and
106106 // move-column still crosses in column order.
107-func TestCascadeBandEdgeWithoutMonitorCrossesBands(t *testing.T) {
107+func TestCascadeBandEdgeWithoutMonitorFocusStepsMoveCrosses(t *testing.T) {
108108 c, w := cascadeMovesCore(t, 5, 3, false)
109109 m := c.cur().mon
110110 w.FocusID(4)
@@ -123,6 +123,10 @@ func TestCascadeBandEdgeWithoutMonitorCrossesBands(t *testing.T) {
123123 c.applyAction(ActionMoveColumnLeft)
124124 wantColumns(t, w, 1, 2, 4, 3, 5)
125125 wantFocus(t, c, "M", 4)
126+ w.FocusID(4)
127+ c.applyAction(ActionMoveColumnRight)
128+ wantColumns(t, w, 1, 2, 3, 4, 5)
129+ wantFocus(t, c, "M", 4)
126130 }
127131
128132 // With one column per band, every column is at both edges of its band.
@@ -104,7 +104,7 @@ func TestCascadeInsideBandStaysOnMonitor(t *testing.T) {
104 // band: at the band edge they step the workspace (the first has none before104 // band: at the band edge they step the workspace (the first has none before
105 // it; the last is the trailing empty one). up/down cross bands, and105 // it; the last is the trailing empty one). up/down cross bands, and
106 // move-column still crosses in column order.106 // move-column still crosses in column order.
107-func TestCascadeBandEdgeWithoutMonitorCrossesBands(t *testing.T) {107+func TestCascadeBandEdgeWithoutMonitorFocusStepsMoveCrosses(t *testing.T) {
108 c, w := cascadeMovesCore(t, 5, 3, false)108 c, w := cascadeMovesCore(t, 5, 3, false)
109 m := c.cur().mon109 m := c.cur().mon
110 w.FocusID(4)110 w.FocusID(4)
@@ -123,6 +123,10 @@ func TestCascadeBandEdgeWithoutMonitorCrossesBands(t *testing.T) {
123 c.applyAction(ActionMoveColumnLeft)123 c.applyAction(ActionMoveColumnLeft)
124 wantColumns(t, w, 1, 2, 4, 3, 5)124 wantColumns(t, w, 1, 2, 4, 3, 5)
125 wantFocus(t, c, "M", 4)125 wantFocus(t, c, "M", 4)
126+ w.FocusID(4)
127+ c.applyAction(ActionMoveColumnRight)
128+ wantColumns(t, w, 1, 2, 3, 4, 5)
129+ wantFocus(t, c, "M", 4)
126 }130 }
127 131
128 // With one column per band, every column is at both edges of its band.132 // With one column per band, every column is at both edges of its band.
M internal/core/workspace_axis_test.go +8 -8
@@ -155,17 +155,17 @@ func TestCascadeLoneFloatAndEmptyWorkspaceStepWorkspace(t *testing.T) {
155155 c.applyAction(ActionFocusColumnLeft)
156156 wantActive(t, m, 2)
157157
158- c, m = axisCore(t, false, OverflowCascade, 3)
159- m.Focus(0)
160- m.Current().AddFloating(9, 100, 100)
158+ // A lone native float on its own workspace, past the trailing one.
161159 for _, tc := range []struct {
162160 a Action
163161 want int
164- }{{ActionFocusColumnLeft, 0}, {ActionFocusColumnRight, 1}} {
165- m.Focus(0)
166- w := m.Current()
167- w.Columns = nil // a lone float: no columns under it
168- w.floatFocus = true
162+ }{{ActionFocusColumnLeft, 2}, {ActionFocusColumnRight, 4}} {
163+ c, m = axisCore(t, false, OverflowCascade, 3)
164+ m.Focus(3)
165+ m.AddFloating(9, 100, 100)
166+ if w := m.Current(); len(w.Columns) != 0 || !w.floatFocus {
167+ t.Fatalf("columns %d floatFocus %v, want a lone focused float", len(w.Columns), w.floatFocus)
168+ }
169169 c.applyAction(tc.a)
170170 wantActive(t, m, tc.want)
171171 }
@@ -155,17 +155,17 @@ func TestCascadeLoneFloatAndEmptyWorkspaceStepWorkspace(t *testing.T) {
155 c.applyAction(ActionFocusColumnLeft)155 c.applyAction(ActionFocusColumnLeft)
156 wantActive(t, m, 2)156 wantActive(t, m, 2)
157 157
158- c, m = axisCore(t, false, OverflowCascade, 3)158+ // A lone native float on its own workspace, past the trailing one.
159- m.Focus(0)
160- m.Current().AddFloating(9, 100, 100)
161 for _, tc := range []struct {159 for _, tc := range []struct {
162 a Action160 a Action
163 want int161 want int
164- }{{ActionFocusColumnLeft, 0}, {ActionFocusColumnRight, 1}} {162+ }{{ActionFocusColumnLeft, 2}, {ActionFocusColumnRight, 4}} {
165- m.Focus(0)163+ c, m = axisCore(t, false, OverflowCascade, 3)
166- w := m.Current()164+ m.Focus(3)
167- w.Columns = nil // a lone float: no columns under it165+ m.AddFloating(9, 100, 100)
168- w.floatFocus = true166+ if w := m.Current(); len(w.Columns) != 0 || !w.floatFocus {
167+ t.Fatalf("columns %d floatFocus %v, want a lone focused float", len(w.Columns), w.floatFocus)
168+ }
169 c.applyAction(tc.a)169 c.applyAction(tc.a)
170 wantActive(t, m, tc.want)170 wantActive(t, m, tc.want)
171 }171 }

Keyboard shortcuts

Single-key shortcuts are on. ctrl k and esc work whatever this says.

ctrl k :
go to a section, branch, tag, file or repository
?
this list
j k
move the row cursor down / up
enter
open the row under the cursor; on Files, a directory opens or closes in place
↑ ↓ ← → home end
in the file tree (focus on a row): previous / next row, close / open a directory or go to its parent / first entry, first / last row. They never open a file
l h
on Files: open a directory or a file / close a directory or go up to its parent, then to the parent page. Elsewhere: into the side panel (on a merge request, its files) / back to the content; there j k move between its controls
g g G
first / last row
/
filter the list; on Files, find a file anywhere in the repository (esc clears, enter opens the best match)
b
switch branch or tag
g s g f g c g b g m g i g r g a
tabs: summary, files, commits, branches & tags, merge requests, issues, releases, actions (when available)
g h
home
w
wide mode: use the whole width of the screen, or not (remembered)
y
copy a permalink pinned to the commit
d u x
diff: split / unified / collapse or expand all
e E
diff: the whole file in view / every file, with the lines the hunks hide
n
merge request: next note in the diff
esc
close a dialog, leave a field

Branches 23

Tags 10

↑↓ moveenter openesc close