summaryrefslogtreecommitdiff
path: root/src/pardes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-10-01 19:20:19 -0300
committerGabriel Schneider <[email protected]>2026-10-01 19:57:27 -0300
commit2868516c215a96d9e1e933ded7ac5744ec16c368 (patch)
treef4283b58044fc0b2155f1a0526a87357b0ca62cc /src/pardes.zig
parent5f41a03fdcf0eaa3c1917e0f94243878c481cf8b (diff)
downloadpardes-2868516c215a96d9e1e933ded7ac5744ec16c368.tar.gz
pardes-2868516c215a96d9e1e933ded7ac5744ec16c368.zip
Tab N sets a tab's width in cells, 1 through 16, in every pane, drawn and counted (wrapping, clicks, the cursor); bare Tab says it; the ctl read and a Dump keep it
A session setting, not acme's per window: the width is read by File's pure width functions (~130 callers that are given no pane), so it is one value, config.tab_now, that Runtime.apply sets and a new session resets. A file:L:C column counts runes, as compilers do, and never depended on it. Co-Authored-By: Claude Opus 5.5 <[email protected]>
Diffstat (limited to 'src/pardes.zig')
-rw-r--r--src/pardes.zig43
1 files changed, 43 insertions, 0 deletions
diff --git a/src/pardes.zig b/src/pardes.zig
index 36a37293..af2b80a2 100644
--- a/src/pardes.zig
+++ b/src/pardes.zig
@@ -3149,6 +3149,47 @@ test "Unicode display cells map back to body and tag byte cursors" {
try std.testing.expectEqualStrings(before, tagline.curTail(pane));
}
+test "Tab N draws every pane's tab N cells wide and clicks count past it; bare Tab says the width; the ctl read and a Dump keep it" {
+ const gpa = std.testing.allocator;
+ const p = try Pardes.init(gpa, .{ .cols = 60, .rows = 12 });
+ defer p.deinit();
+ // The width is the process's: the next test starts from the default.
+ defer config.tab_now = config.tab_width;
+ const pane = try p.setTestFile("\tx = 1\n");
+ try std.testing.expect(p.executeBuiltinLine(0, "Tab 8"));
+ try std.testing.expectEqual(@as(u16, 8), config.tab_now);
+ var frame = std.heap.ArenaAllocator.init(gpa);
+ defer frame.deinit();
+ const surface = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const text_x = rect.x + config.GUTTER + config.PREFIX_W;
+ const body_y = rect.y + BOX_H;
+ try std.testing.expectEqualStrings("x", surface.at(text_x + 8, body_y).grapheme());
+ // A click on the x is on its byte, past the tab.
+ p.update(.{ .mouse = .{ .button = config.select_button, .kind = .press, .col = text_x + 8, .row = body_y } });
+ p.update(.{ .mouse = .{ .button = config.select_button, .kind = .release, .col = text_x + 8, .row = body_y } });
+ try std.testing.expectEqual(@as(i32, 1), pane.body.cur_col);
+ // Bare, it says the width.
+ try std.testing.expect(p.executeBuiltinLine(0, "Tab"));
+ try std.testing.expectEqualStrings("Tab 8", pane.msg[0..pane.msg_len]);
+ // Out of range: refused, the width kept.
+ try std.testing.expect(p.executeBuiltinLine(0, "Tab 0"));
+ try std.testing.expectEqual(@as(u16, 8), config.tab_now);
+ // The ctl read's tab column, and a Dump's settings.
+ const ctl_text = pardes_ctlfs_ctlText(p, pane);
+ try std.testing.expect(std.mem.indexOf(u8, ctl_text, " 8 ") != null);
+ try dump.dumpState(p);
+ const restored = try dump.restore(p, p.dump_out.?, "/tmp/pardes.dump.zon");
+ defer restored.deinit();
+ try std.testing.expectEqual(@as(u16, 8), restored.settings.tab);
+ try std.testing.expectEqual(@as(u16, 8), config.tab_now);
+}
+
+fn pardes_ctlfs_ctlText(p: *Pardes, pane: *Pane) []const u8 {
+ const th = @import("ninep/testing.zig");
+ return th.rd(p, ctlfs.Node.of(pane.serial, .ctl), 0, 4096).bytes;
+}
+
test "tabbed file aligns syntax cursor and mouse at the nearest insertion gap" {
const gpa = std.testing.allocator;
const p = try Pardes.init(gpa, .{ .cols = 60, .rows = 12 });
@@ -4618,6 +4659,8 @@ pub const Pardes = struct {
}
pub fn init(gpa: std.mem.Allocator, opts: Options) !*Pardes {
+ // A new session draws a tab at the default until `Tab` says otherwise.
+ config.tab_now = config.tab_width;
const p = try create(gpa, opts);
errdefer p.deinit();
p.boot = Boot.of(opts);