diff options
| author | Gabriel Schneider <[email protected]> | 2026-09-29 02:32:33 -0300 |
|---|---|---|
| committer | Gabriel Schneider <[email protected]> | 2026-10-01 00:12:15 -0300 |
| commit | 672b2643583d13feef5d47600e0f6c15f276fba2 (patch) | |
| tree | 3d556d0f30e72c8b2ed7bd4177a802e2a0697992 /src/pardes.zig | |
| parent | 45a66bd1c062a5cf4d63b1df68e050b059cdfa1d (diff) | |
| download | pardes-672b2643583d13feef5d47600e0f6c15f276fba2.tar.gz pardes-672b2643583d13feef5d47600e0f6c15f276fba2.zip | |
No placement leaves a pane shorter than its tag and two rows
Placement could split a pane down to a tag and one row, or give a new one
less. Every placement now checks the pane it made and each pane it shrank
against its tag (wrapped at its width) plus two rows: where they lack the
room, the column's tallest pane is halved instead, and where none can give
it the new pane is refused with "no space for a pane in that column"
(ENOSPC over 9P) and closed at the step's end, once its maker is done with
it. splitBelow and splitParent leave two body rows (a boot layout, split
before any row exists, is as before). Re-recorded by name: lookpanes (panes
one or two rows lower) and splitparent (the 6-row squeeze now refuses);
fs.py closes the scratches and long-named panes it no longer needs, since
its 24-row session filled.
Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/pardes.zig')
| -rw-r--r-- | src/pardes.zig | 58 |
1 files changed, 46 insertions, 12 deletions
diff --git a/src/pardes.zig b/src/pardes.zig index f2791e33..ac6d52cd 100644 --- a/src/pardes.zig +++ b/src/pardes.zig @@ -4271,6 +4271,10 @@ pub const Pardes = struct { /// The column whose tag the running command came from, while it runs: /// the column acme's commands take from their Text's col. exec_column: ?usize = null, + /// Panes made this step that found no room in their column + /// (exec.placeNew): closed at the step's end, once whatever made them + /// is done with them. + unplaced: std.StaticBitSet(MAX_PANES) = .initEmpty(), /// acme's activecol (dat.c:37), by column serial, 0 for none: the /// column last typed in (acme.c:487), clicked in with the select button /// (acme.c:659), dropped into by a grip (acme.c:640), or given a new @@ -6388,7 +6392,26 @@ pub const Pardes = struct { /// Recompute geometry, push grid-size changes to each emulator + pty, fire /// deferred greetings. The mirror of the prototype's loop epilogue. + /// The rows pane `id`'s tag takes at its width, wrapped as `sync` wraps + /// it: what a placement must leave it above its body rows. + pub fn wantedTagRows(p: *Pardes, id: usize) u16 { + const pane = p.panes[id] orelse return BOX_H; + if (pane.collapsed or !pane.tag_expanded) return BOX_H; + if (p.rects[id].w == 0) return pane.tag_rows; // never laid out + var wrap_room: [32 * 1024]u8 = undefined; + var wrap_fba = std.heap.FixedBufferAllocator.init(&wrap_room); + const wa = wrap_fba.allocator(); + const prefix = tagline.prefixIn(wa, pane) catch return pane.tag_rows; + const whole = std.mem.concat(wa, u8, &.{ prefix, tagline.curTail(pane) }) catch return pane.tag_rows; + const shown = tagline.wrapTag(wa, whole, tagline.tagWidth(p, id)) catch return pane.tag_rows; + return @intCast(@max(BOX_H, @min(shown.rows(), MAX_TAG_ROWS))); + } + pub fn sync(p: *Pardes) void { + while (p.unplaced.findFirstSet()) |id| { + p.unplaced.unset(id); + if (p.panes[id] != null) p.removePane(id, null) catch {}; + } tagline.flattenHeaders(p); if (p.header_column != null and p.columnBarHeight() == 0) { tagline.exitHeader(p); @@ -7042,7 +7065,10 @@ test "Del from a key asks which neighbour takes the rows; nothing else asks" { try std.testing.expectEqual(low, p.active); // focus follows the rows // Esc takes the question back and the pane stays; so does any other key, - // and so does a click. + // and so does a click. (Top gets rows back first: split at its cursor, + // it kept its tag and two, too few to give a pane below its own.) + p.panes[top].?.vweight = p.panes[low].?.vweight; + p.sync(); p.newScratchBelow(top); const again = p.active; p.sync(); @@ -7096,17 +7122,6 @@ test "Del from a key asks which neighbour takes the rows; nothing else asks" { try std.testing.expectEqual(ctlfs.Status.ok, th.wr(p, ctl, "answer -\n").reply.status); try std.testing.expect(p.panes[asked] != null and p.panes[asked].?.prompt == .none); try std.testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "answer {d} -\n", .{serial}))); - // A pane closed while its question stands ends the question in the log. - p.newScratchBelow(top); - const closing = p.active; - p.sync(); - const closing_serial = p.panes[closing].?.serial; - p.update(.{ .key = .{ .cp = ' ' } }); - p.update(.{ .key = .{ .cp = 'd' } }); - try std.testing.expectEqual(Pane.Prompt.del_side, p.panes[closing].?.prompt); - try p.removePane(closing, null); - try std.testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "answer {d} -\ndel {d}", .{ closing_serial, closing_serial }))); - p.active = asked; p.update(.{ .key = .{ .cp = ' ' } }); p.update(.{ .key = .{ .cp = 'd' } }); try std.testing.expectEqual(ctlfs.Status.ok, th.wr(p, ctl, "answer k\n").reply.status); @@ -7891,3 +7906,22 @@ test "core animation keeps wall time at 60, 120 and 144 Hz and across a stall" { p.advance(p.stepped_ns + frame); try std.testing.expectEqual(@as(?u64, null), p.nextWake()); } + +test "a pane closed while its question stands ends the question in the log" { + const th = @import("ninep/testing.zig"); + const p = try th.withFile(std.testing.allocator, "x\n"); + defer p.deinit(); + p.settings.placement = .pardes; + p.newScratchBelow(0); + const closing = p.active; + p.newScratchBelow(closing); + p.sync(); + p.active = closing; // a middle pane: Del from a key asks which side + const serial = p.panes[closing].?.serial; + p.update(.{ .key = .{ .cp = ' ' } }); + p.update(.{ .key = .{ .cp = 'd' } }); + try std.testing.expectEqual(Pane.Prompt.del_side, p.panes[closing].?.prompt); + try p.removePane(closing, null); + var want: [64]u8 = undefined; + try std.testing.expect(th.logHas(p, try std.fmt.bufPrint(&want, "answer {d} -\ndel {d}", .{ serial, serial }))); +} |
