summaryrefslogtreecommitdiff
path: root/test
diff options
context:
space:
mode:
Diffstat (limited to 'test')
-rw-r--r--test/panes.zig134
1 files changed, 134 insertions, 0 deletions
diff --git a/test/panes.zig b/test/panes.zig
index ab7726e1..ded5b075 100644
--- a/test/panes.zig
+++ b/test/panes.zig
@@ -12,6 +12,139 @@ const image = pardes.image;
const syntax = pardes.syntax;
const filesystem = pardes.filesystem;
+const ClickCursorTests = struct {
+ fn click(p: *Pardes, button: pardes.Mouse.Button, x: u16, y: u16) void {
+ p.update(.{ .mouse = .{ .button = button, .kind = .press, .col = x, .row = y } });
+ p.update(.{ .mouse = .{ .button = button, .kind = .release, .col = x, .row = y } });
+ }
+
+ test "click cursor and insertion agree beyond line and file ends" {
+ for ([_]bool{ false, true }) |already_insert| {
+ for ([_]bool{ false, true }) |bottom_tag| {
+ for ([_]struct { text: []const u8, click_row: u16, row: i32, col: i32, result: []const u8 }{
+ .{ .text = "abc\nxy", .click_row = 0, .row = 0, .col = 3, .result = "abcZ\nxy" },
+ .{ .text = "abc\nxy", .click_row = 5, .row = 1, .col = 2, .result = "abc\nxyZ" },
+ .{ .text = "abc\n", .click_row = 5, .row = 1, .col = 0, .result = "abc\nZ" },
+ .{ .text = "", .click_row = 5, .row = 0, .col = 0, .result = "Z" },
+ }) |case| {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 12 });
+ defer p.deinit();
+ p.settings.tag_bottom = bottom_tag;
+ const pane = try p.setTestFile(case.text);
+ p.presentation.enabled = false;
+ if (already_insert) p.update(.{ .key = .{ .cp = 'i' } });
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const x = rect.x + config.GUTTER + panes.File.gutterWidth(pane);
+ const y = if (bottom_tag) rect.y else rect.y + pardes.BOX_H;
+ click(p, .left, x + 15, y + case.click_row);
+ try std.testing.expectEqual(case.row, pane.cur_row);
+ try std.testing.expectEqual(case.col, pane.cur_col);
+ const clicked = (try p.render(frame.allocator())).cursor orelse return error.MissingClickCursor;
+ try std.testing.expectEqual(x + @as(u16, @intCast(case.col)), clicked.x);
+ try std.testing.expectEqual(y + @as(u16, @intCast(case.row)), clicked.y);
+ if (!already_insert) p.update(.{ .key = .{ .cp = 'i' } });
+ try std.testing.expectEqual(Pane.Mode.insert, pane.mode);
+ const insert = (try p.render(frame.allocator())).cursor orelse return error.MissingInsertCursor;
+ try std.testing.expectEqual(clicked.x, insert.x);
+ try std.testing.expectEqual(clicked.y, insert.y);
+ p.update(.{ .key = .{ .cp = 'Z', .text = "Z" } });
+ try std.testing.expectEqualStrings(case.result, pane.file.?.content);
+ }
+ }
+ }
+ }
+
+ test "click cursor snaps tab and wrapped wide glyph cells before insertion" {
+ for ([_]bool{ false, true }) |already_insert| {
+ for ([_]bool{ false, true }) |wrap| {
+ const source = if (wrap) "012345678901234567890123456789界tail\n" else "\t界tail\n";
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 24, .rows = 12 });
+ defer p.deinit();
+ const pane = try p.setTestFile(source);
+ p.settings.wrap = wrap;
+ p.presentation.enabled = false;
+ if (already_insert) p.update(.{ .key = .{ .cp = 'i' } });
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ const surface = try p.render(frame.allocator());
+ var target: ?struct { x: u16, y: u16 } = null;
+ for (0..surface.rows) |row| for (0..surface.cols) |col| {
+ if (std.mem.eql(u8, surface.at(@intCast(col), @intCast(row)).grapheme(), "界"))
+ target = .{ .x = @intCast(col), .y = @intCast(row) };
+ };
+ const glyph = target orelse return error.MissingWideGlyph;
+ if (wrap) try std.testing.expect(glyph.y > p.rects[0].y + pardes.BOX_H);
+ click(p, .left, glyph.x + 1, glyph.y);
+ const byte = std.mem.indexOf(u8, source, "界").?;
+ try std.testing.expectEqual(@as(i32, @intCast(byte)), pane.cur_col);
+ const clicked = (try p.render(frame.allocator())).cursor orelse return error.MissingClickCursor;
+ try std.testing.expectEqual(glyph.x, clicked.x);
+ try std.testing.expectEqual(glyph.y, clicked.y);
+ if (!already_insert) p.update(.{ .key = .{ .cp = 'i' } });
+ try std.testing.expectEqual(Pane.Mode.insert, pane.mode);
+ const insert = (try p.render(frame.allocator())).cursor orelse return error.MissingInsertCursor;
+ try std.testing.expectEqual(clicked.x, insert.x);
+ try std.testing.expectEqual(clicked.y, insert.y);
+ p.update(.{ .key = .{ .cp = 'Z', .text = "Z" } });
+ const expected = try std.fmt.allocPrint(p.gpa, "{s}Z{s}", .{ source[0..byte], source[byte..] });
+ defer p.gpa.free(expected);
+ try std.testing.expectEqualStrings(expected, pane.file.?.content);
+ }
+ }
+ }
+
+ test "click operands and hover follow the scrolled source selection" {
+ const p = try Pardes.init(std.testing.allocator, .{ .tty_only = true, .cols = 40, .rows = 14 });
+ defer p.deinit();
+ const pane = try p.setTestFile("zero\none\ntwo\nthree\nalpha beta\nfive\ngamma delta\nseven\n" ++ "tail\n" ** 20);
+ p.presentation.enabled = false;
+ p.settings.wrap = false;
+ var frame = std.heap.ArenaAllocator.init(p.gpa);
+ defer frame.deinit();
+ _ = try p.render(frame.allocator());
+ const rect = p.rects[0];
+ const x = rect.x + config.GUTTER + panes.File.gutterWidth(pane);
+ const y = if (p.settings.tag_bottom) rect.y else rect.y + pardes.BOX_H;
+ JumpSelectionTests.drag(p, x + 1, y + 4, x + 3, y + 4);
+ try std.testing.expectEqualStrings("lph", pardes.test_api.heldSelection(p, 0).?);
+ for (0..2) |_| p.update(.{ .mouse = .{ .button = .wheel_down, .kind = .press, .col = x + 2, .row = y + 4 } });
+ try std.testing.expectEqual(@as(usize, 2), pane.file.?.scroll);
+ _ = try p.render(frame.allocator());
+ const event_node = filesystem.Node.of(pane.serial, .event);
+ const opened = filesystem.handle(p, .{ .tag = 0, .op = .open, .node = event_node });
+ try std.testing.expectEqual(.ok, opened.status);
+ defer _ = filesystem.handle(p, .{ .tag = 0, .op = .release, .node = event_node, .handle = opened.handle });
+
+ for ([_]pardes.Mouse.Button{ .middle, .right }) |button| {
+ const events = &p.fs.panes[0].events;
+ while (!events.empty()) events.pop();
+ click(p, button, x + 2, y + 2);
+ const selected = events.peek() orelse return error.MissingSelectionGesture;
+ try std.testing.expect(std.mem.endsWith(u8, selected, " lph\n"));
+ try std.testing.expect(std.mem.startsWith(u8, selected[2..], "20 23 "));
+ while (!events.empty()) events.pop();
+ click(p, button, x + 2, y + 4);
+ const unselected = events.peek() orelse return error.MissingWordGesture;
+ try std.testing.expect(std.mem.endsWith(u8, unselected, " gamma\n"));
+ }
+
+ const delay = config.look_preview_delay_frames orelse return;
+ p.update(.{ .mouse = .{ .button = .none, .kind = .motion, .col = x + 2, .row = y + 2 } });
+ for (0..delay) |_| p.update(.tick);
+ const selected_preview = p.look_hover_preview orelse return error.MissingSelectionHover;
+ try std.testing.expect(selected_preview.sel != null);
+ try std.testing.expect(pane.pointerSelection(selected_preview.sel.?) != null);
+ p.update(.{ .mouse = .{ .button = .none, .kind = .motion, .col = x + 2, .row = y + 4 } });
+ for (0..delay) |_| p.update(.tick);
+ const word_preview = p.look_hover_preview orelse return error.MissingWordHover;
+ try std.testing.expect(word_preview.file_word != null);
+ try std.testing.expectEqual(@as(i32, 6), word_preview.file_word.?.row);
+ }
+};
+
const JumpSelectionTests = struct {
fn drag(p: *Pardes, col0: u16, row0: u16, col1: u16, row1: u16) void {
p.update(.{ .mouse = .{ .button = .left, .kind = .press, .col = col0, .row = row0 } });
@@ -2691,6 +2824,7 @@ const TerminalTests = struct {
};
test {
+ _ = ClickCursorTests;
_ = JumpSelectionTests;
_ = @import("output.zig");
_ = @import("pdf.zig");