summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/builtins.zig3
-rw-r--r--src/layout.zig27
-rw-r--r--src/ninep/cols.zig26
3 files changed, 55 insertions, 1 deletions
diff --git a/src/builtins.zig b/src/builtins.zig
index 6688d05f..27a1d36d 100644
--- a/src/builtins.zig
+++ b/src/builtins.zig
@@ -1271,6 +1271,9 @@ pub const Joincol = struct {
// The keyboard's column joins the one to its right; the last has none.
const col = c.p.keyboardColumn(c.p.active) orelse return;
if (col + 1 >= c.p.ncol) return c.p.reportFailure(c.id, "Joincol: no column to the right");
+ // As Newcol is refused: a column that could not give each pane its
+ // tag and 2 rows would leave a layout `size` refuses as it is.
+ if (!layout.joinFits(c.p, col)) return c.p.reportFailure(c.id, "Joincol: no space: the joined column cannot give each pane its tag and 2 rows");
layout.joinCol(c.p, col);
}
};
diff --git a/src/layout.zig b/src/layout.zig
index 4aebcf28..a2211dfd 100644
--- a/src/layout.zig
+++ b/src/layout.zig
@@ -360,13 +360,38 @@ pub fn noteColumn(p: *Pardes, what: []const u8, serial: u32) void {
pub fn joinCol(p: *Pardes, c: usize) void {
if (c + 1 >= p.ncol) return;
const dst = c + 1;
+ joinPanes(p, c, dst);
+ dropColumn(p, c);
+}
+
+/// Whether column `c` joined into the one on its right leaves every pane
+/// its tag and 2 rows (minimaFit), tried in place and undone: nothing is
+/// dropped or logged for a join that does not fit.
+pub fn joinFits(p: *Pardes, c: usize) bool {
+ if (c + 1 >= p.ncol) return true;
+ const dst = c + 1;
+ const n = p.col_n[c];
+ const had = p.col_n[dst];
+ const weights = .{ p.col_weight[c], p.col_weight[dst] };
+ const panes_c = p.col_panes[c];
+ joinPanes(p, c, dst);
+ const fits = minimaFit(p);
+ p.col_n[dst] = had;
+ p.col_n[c] = n;
+ p.col_panes[c] = panes_c;
+ p.col_weight[c] = weights[0];
+ p.col_weight[dst] = weights[1];
+ compute(p);
+ return fits;
+}
+
+fn joinPanes(p: *Pardes, c: usize, dst: usize) void {
for (0..p.col_n[c]) |k| p.col_panes[dst][p.col_n[dst] + k] = p.col_panes[c][k];
p.col_n[dst] += p.col_n[c];
p.col_n[c] = 0;
// The width goes right, where the panes went.
p.col_weight[dst] +|= p.col_weight[c];
p.col_weight[c] = 0;
- dropColumn(p, c);
}
/// Put new pane `id` in column `c` as acme's makenewwindow does
diff --git a/src/ninep/cols.zig b/src/ninep/cols.zig
index a1fe9ecc..7afcfbd8 100644
--- a/src/ninep/cols.zig
+++ b/src/ninep/cols.zig
@@ -232,6 +232,32 @@ test "New written to a column's exec makes its scratch in the session's director
try testing.expectEqualStrings("/project/src/+New", p.panes[p.active].?.file.?.path);
}
+test "Joincol is refused when the joined column cannot give each pane its tag and 2 rows" {
+ const p = try pardes.Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 12 });
+ defer p.deinit();
+ while (p.nextEffect()) |_| {}
+ const ctl = @intFromEnum(tree.TopFile.ctl);
+ const focus = @intFromEnum(tree.TopFile.focus);
+ // Two panes in the first column, then a second column of two.
+ const a = try th.newPane(p);
+ _ = a;
+ try testing.expectEqual(tree.Status.ok, th.wr(p, ctl, "Newcol\n").reply.status);
+ const b = try th.newPane(p);
+ var digits: [16]u8 = undefined;
+ _ = th.wr(p, focus, try std.fmt.bufPrint(&digits, "{d}\n", .{b}));
+ _ = try th.newPane(p);
+ try testing.expectEqual(@as(usize, 2), p.ncol);
+ try testing.expect(p.col_n[0] >= 2 and p.col_n[1] >= 2);
+ // Focus in the left column: joined, four panes want 12 rows of 10.
+ const left = p.panes[p.col_panes[0][0]].?.serial;
+ _ = th.wr(p, focus, try std.fmt.bufPrint(&digits, "{d}\n", .{left}));
+ const r = th.wr(p, ctl, "Joincol\n");
+ try testing.expectEqual(tree.Status.err, r.reply.status);
+ try testing.expect(std.mem.indexOf(u8, r.reply.ename, "tag and 2 rows") != null);
+ try testing.expectEqual(@as(usize, 2), p.ncol);
+ try testing.expect(pardes.layout.minimaFit(p));
+}
+
test "pane/new takes rows from a pane in another column before refusing ENOSPC" {
const p = try pardes.Pardes.init(testing.allocator, .{ .tty_only = true, .cols = 160, .rows = 16 });
defer p.deinit();