From 3f6ad1bc30570348297ae4aa6d1fd676de229a09 Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Thu, 24 Sep 2026 10:06:08 -0300 Subject: Ask from the keyboard which neighbour a closed pane's rows go to Del takes a side: `Del k` gives the closed pane's rows to the nearest expanded pane above it, `Del j` to the one below, each falling back to the other side when it has none. DelAbove and DelBelow are those two lines, with no path of their own under SPC. A bare Del started from a key -- SPC d, Enter on the tag word, a row run from an output buffer -- on a pane with expanded panes both above and below asks instead of guessing. The question is a prompt like Save's or a search's (Pane.Prompt.del_side), so it is painted on the pane's notice band by the same path, and the next key answers it before any mode sees it: k or Up, j or Down, anything else keeps the pane, as does a click. Only a key press sets Pardes.can_ask, so a click, a 9P ctl or event write, a startup line, a restore and a shell exiting all close the pane at once, the rows going where layout.absorbVWeight has always sent them. A collapsed pane is not asked about (it has only a tag row to give), and collapsed neighbours are passed over (layout.expandedNeighbor, which Collapse now uses too). removePane and absorbVWeight take the recipient; every other caller passes null. Three scripts that closed a middle pane with SPC d answer k, which is where the rows went before, and their goldens are unchanged. delask.snap covers the question, Esc, j, a clicked DelBelow and a clicked Del. Co-Authored-By: Claude Opus 5.5 (1M context) --- src/layout.zig | 29 +++++++++++++++++------------ 1 file changed, 17 insertions(+), 12 deletions(-) (limited to 'src/layout.zig') diff --git a/src/layout.zig b/src/layout.zig index 3637f30e..116a64b8 100644 --- a/src/layout.zig +++ b/src/layout.zig @@ -995,7 +995,7 @@ pub fn splitColumn(p: *Pardes, source_id: usize, id: usize, before: bool) bool { if (needs_rebase) old_weight *= 2; if (id == source_id) { if (p.col_n[source_col] <= 1) return false; - absorbVWeight(p, id); + absorbVWeight(p, id, null); removePane(p, id); } else if (findPane(p, id) != null) return false; @@ -1033,13 +1033,16 @@ pub fn snapColWeights(p: *Pardes, c: usize) void { } } -pub fn absorbVWeight(p: *Pardes, id: usize) void { +/// Hand a closing pane's rows to `to`. With none named they go up: past any +/// results listing to the nearest pane above that is not one, or to the +/// pane below when the closing one is first in its column. +pub fn absorbVWeight(p: *Pardes, id: usize, to: ?usize) void { const f = findPane(p, id) orelse return; if (p.col_n[f.col] <= 1) return; snapColWeights(p, f.col); - var sib = if (f.idx > 0) p.col_panes[f.col][f.idx - 1] else p.col_panes[f.col][f.idx + 1]; + var sib = to orelse if (f.idx > 0) p.col_panes[f.col][f.idx - 1] else p.col_panes[f.col][f.idx + 1]; var k = f.idx; - while (k > 0) : (k -= 1) { + while (to == null and k > 0) : (k -= 1) { sib = p.col_panes[f.col][k - 1]; if (p.panes[sib]) |pp| if (if (pp.file) |ff| panes.Output.fileTraits(ff.output).doc else true) break; } @@ -1119,7 +1122,7 @@ pub fn toggleCollapse(p: *Pardes, id: usize) void { // Convert only visible panes to row weights. The toggle then changes one // pair, rather than letting the column redistribute the released space. snapColWeights(p, at.col); - const neighbor = collapseNeighbor(p, at.col, at.idx); + const neighbor = expandedNeighbor(p, at.col, at.idx, .above) orelse expandedNeighbor(p, at.col, at.idx, .below); if (was_collapsed) { if (neighbor) |other| { const wanted: u16 = @intFromFloat(@min(@as(f32, @floatFromInt(std.math.maxInt(u16))), @max(1, @round(remembered_height)))); @@ -1139,15 +1142,17 @@ pub fn toggleCollapse(p: *Pardes, id: usize) void { compute(p); } -fn collapseNeighbor(p: *Pardes, col: usize, index: usize) ?usize { - var above = index; - while (above > 0) { - above -= 1; - const id = p.col_panes[col][above]; +/// The nearest pane on one side of `index` in its column that is not folded +/// to its tag: the one Collapse lends rows to and Del k / Del j gives them +/// to. A folded pane only keeps its weight for later, so rows handed to it +/// would spread over the whole column instead. +pub fn expandedNeighbor(p: *const Pardes, col: usize, index: usize, side: enum { above, below }) ?usize { + var k = index; + while (if (side == .above) k > 0 else k + 1 < p.col_n[col]) { + k = if (side == .above) k - 1 else k + 1; + const id = p.col_panes[col][k]; if (p.panes[id]) |pane| if (!pane.collapsed) return id; } - for (p.col_panes[col][index + 1 .. p.col_n[col]]) |id| - if (p.panes[id]) |pane| if (!pane.collapsed) return id; return null; } -- cgit v1.3