summaryrefslogtreecommitdiff
path: root/test/panes.zig
diff options
context:
space:
mode:
authorGabriel Schneider <[email protected]>2026-09-15 13:39:27 -0300
committerGabriel Schneider <[email protected]>2026-09-15 17:24:42 -0300
commit13d0d107a06d7ac5fb5f8e86c747e54603f51b4e (patch)
tree6d404c6048736a093aacb1827081c9726d5906e0 /test/panes.zig
parent1d19d0ec24b924d1d2ccf2cab25199c6e6865fd7 (diff)
downloadpardes-13d0d107a06d7ac5fb5f8e86c747e54603f51b4e.tar.gz
pardes-13d0d107a06d7ac5fb5f8e86c747e54603f51b4e.zip
Remember tag cursors and reserve uppercase pane navigation
Diffstat (limited to 'test/panes.zig')
-rw-r--r--test/panes.zig179
1 files changed, 179 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig
index a7e59923..a452bcfb 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -12,6 +12,184 @@ const image = pardes.image;
const syntax = pardes.syntax;
const filesystem = pardes.filesystem;
+const TagNavigationTests = struct {
+ fn key(p: *Pardes, cp: u21) void {
+ p.update(.{ .key = .{ .cp = cp } });
+ }
+
+ fn enterNormal(p: *Pardes) *Pane {
+ key(p, ':');
+ return p.panes[p.active].?;
+ }
+
+ test "tag navigation retains each pane cursor and mouse placement overrides memory" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 20 });
+ defer p.deinit();
+ const first = try p.setTestFile("body\n");
+ p.presentation.enabled = false;
+ try std.testing.expect(p.executeBuiltinLine(0, "New"));
+ const second_id = p.active;
+ const second = p.panes[second_id].?;
+ try std.testing.expect(layout.splitColumn(p, second_id, second_id, false));
+ p.update(.tick);
+ p.active = 0;
+ key(p, ':');
+ key(p, Key.home);
+ for (0..3) |_| key(p, Key.right);
+ const first_col = first.tag_col;
+ key(p, Key.escape);
+ p.active = second_id;
+ key(p, ':');
+ key(p, Key.home);
+ for (0..5) |_| key(p, Key.right);
+ const second_col = second.tag_col;
+ key(p, Key.escape);
+ p.active = 0;
+ key(p, ':');
+ try std.testing.expectEqual(first_col, first.tag_col);
+ key(p, Key.escape);
+ p.active = second_id;
+ key(p, ':');
+ try std.testing.expectEqual(second_col, second.tag_col);
+ key(p, Key.escape);
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const y = if (p.settings.tag_bottom) rect.y + rect.h - pardes.BOX_H else rect.y;
+ ClickCursorTests.click(p, .left, rect.x + config.GUTTER + 6, y);
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ try std.testing.expectEqual(@as(u16, 6), first.tag_col);
+ key(p, Key.escape);
+ p.startPrompt(first, .{ .search = config.search_marker });
+ key(p, Key.escape);
+ key(p, ':');
+ try std.testing.expectEqual(@as(u16, 6), first.tag_col);
+ key(p, Key.escape);
+ try std.testing.expectEqualStrings("body\n", first.file.?.content);
+ }
+
+ test "tag navigation clamps remembered columns to renamed graphemes and shorter tags" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 14 });
+ defer p.deinit();
+ const pane = try p.setTestFile("body\n");
+ key(p, ':');
+ key(p, Key.home);
+ for (0..3) |_| key(p, Key.right);
+ key(p, Key.escape);
+ p.gpa.free(pane.file.?.path);
+ pane.file.?.path = try p.gpa.dupe(u8, "/界.zig");
+ key(p, ':');
+ try std.testing.expectEqual(@as(u16, 1), pane.tag_col);
+ key(p, Key.end);
+ key(p, Key.escape);
+ p.gpa.free(pane.file.?.path);
+ pane.file.?.path = try p.gpa.dupe(u8, "/é");
+ pane.tag_tail_len = 0;
+ key(p, ':');
+ try std.testing.expectEqual(@as(u16, 3), pane.tag_col);
+ try std.testing.expectEqualStrings("body\n", pane.file.?.content);
+ }
+
+ test "tag navigation J and K restore cursors in stacked panes" {
+ for ([_]bool{ false, true }) |bottom| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 80, .rows = 24 });
+ defer p.deinit();
+ const first = try p.setTestFile("body\n");
+ p.presentation.enabled = false;
+ p.settings.tag_bottom = bottom;
+ try std.testing.expect(p.executeBuiltinLine(0, "New"));
+ const second_id = p.active;
+ const second = p.panes[second_id].?;
+ p.update(.tick);
+ p.active = 0;
+ _ = enterNormal(p);
+ key(p, Key.home);
+ key(p, 'l');
+ key(p, 'l');
+ key(p, 'J');
+ try std.testing.expectEqual(second_id, p.active);
+ try std.testing.expect(!first.tag_edit and second.tag_edit);
+ key(p, Key.home);
+ for (0..4) |_| key(p, 'l');
+ key(p, 'K');
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ try std.testing.expectEqual(@as(u16, 2), first.tag_col);
+ key(p, 'J');
+ try std.testing.expectEqual(second_id, p.active);
+ try std.testing.expectEqual(@as(u16, 4), second.tag_col);
+ }
+ }
+
+ test "tag navigation uses uppercase pane and header keys while lowercase stays local" {
+ for ([_]bool{ false, true }) |bottom| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 100, .rows = 20 });
+ defer p.deinit();
+ _ = try p.setTestFile("body\n");
+ p.presentation.enabled = false;
+ p.settings.tag_bottom = bottom;
+ p.settings.column_tags = true;
+ try std.testing.expect(p.executeBuiltinLine(0, "New"));
+ const right = p.active;
+ try std.testing.expect(layout.splitColumn(p, right, right, false));
+ p.update(.tick);
+ p.active = 0;
+ const pane = enterNormal(p);
+ key(p, Key.home);
+ key(p, 'l');
+ try std.testing.expectEqual(@as(u16, 1), pane.tag_col);
+ key(p, 'h');
+ key(p, 'k');
+ try std.testing.expectEqual(@as(u16, 0), pane.tag_col);
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ try std.testing.expect(pane.tag_edit and p.topbar_col == null);
+ key(p, 'L');
+ try std.testing.expectEqual(right, p.active);
+ try std.testing.expect(!pane.tag_edit and p.panes[right].?.tag_edit);
+ key(p, 'H');
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ key(p, 'K');
+ try std.testing.expect(p.topbar_col != null and p.header_column != null);
+ try std.testing.expect(!pane.tag_edit);
+ key(p, 'l');
+ const column_cursor = p.topbar_col.?;
+ key(p, 'K');
+ try std.testing.expect(p.topbar_col != null and p.header_column == null);
+ key(p, 'l');
+ key(p, 'l');
+ const workspace_cursor = p.topbar_col.?;
+ key(p, 'J');
+ try std.testing.expect(p.topbar_col != null and p.header_column != null);
+ try std.testing.expectEqual(column_cursor, p.topbar_col.?);
+ key(p, 'K');
+ try std.testing.expectEqual(workspace_cursor, p.topbar_col.?);
+ key(p, 'J');
+ key(p, 'J');
+ try std.testing.expect(p.topbar_col == null and pane.tag_edit);
+ key(p, 'j');
+ try std.testing.expect(!pane.tag_edit and p.topbar_col == null);
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ _ = enterNormal(p);
+ key(p, 'K');
+ key(p, 'j');
+ try std.testing.expect(p.topbar_col == null and !pane.tag_edit);
+ key(p, ':');
+ key(p, 'i');
+ key(p, Key.end);
+ const before = pane.tag_tail_len;
+ try std.testing.expect(pane.tag_edit and pane.mode == .insert);
+ for ("hjklHJKL") |letter| {
+ const text = [_]u8{letter};
+ p.update(.{ .key = .{ .cp = letter, .text = &text } });
+ try std.testing.expect(pane.tag_edit and pane.mode == .insert);
+ }
+ try std.testing.expectEqualStrings("hjklHJKL", pane.tag_tail[before..pane.tag_tail_len]);
+ try std.testing.expect(pane.tag_edit and pane.mode == .insert and p.topbar_col == null);
+ try std.testing.expectEqual(@as(usize, 0), p.active);
+ }
+ }
+};
+
const TreeContextTests = struct {
const source = "const Box = struct {\n" ++
" pub fn work(\n" ++
@@ -3375,6 +3553,7 @@ const TerminalTests = struct {
};
test {
+ _ = TagNavigationTests;
_ = TagNameTintTests;
_ = TreeContextTests;
_ = TtySelectionTests;