From a6f5ffcdc544763db00a0a1ac4a36aa4d9fdbf84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Igor=20T=C3=A1mara?= Date: Wed, 8 Oct 2025 23:49:59 -0500 Subject: [PATCH] hx: add tests for some Helix mode movements --- build.zig | 1 + src/tui/mode/helix.zig | 4 +- test/tests_helix.zig | 157 +++++++++++++++++++++++++++++++---------- 3 files changed, 122 insertions(+), 40 deletions(-) diff --git a/build.zig b/build.zig index 8415849..a93af85 100644 --- a/build.zig +++ b/build.zig @@ -733,6 +733,7 @@ pub fn build_exe( tests.root_module.addImport("Buffer", Buffer_mod); tests.root_module.addImport("color", color_mod); tests.root_module.addImport("tui", tui_mod); + tests.root_module.addImport("command", command_mod); // b.installArtifact(tests); const test_run_cmd = b.addRunArtifact(tests); diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index c0f3a6b..19f05a9 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -454,7 +454,7 @@ const cmds_ = struct { pub const paste_after_meta: Meta = .{ .description = "Paste from clipboard after selection" }; }; -fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { +pub fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try Editor.move_cursor_left(root, cursor, metrics); // Consume " " @@ -479,7 +479,7 @@ fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buff fn move_noop(_: Buffer.Root, _: *Cursor, _: Buffer.Metrics) error{Stop}!void {} -fn move_cursor_word_right_end_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { +pub fn move_cursor_word_right_end_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try Editor.move_cursor_right(root, cursor, metrics); Editor.move_cursor_right_until(root, cursor, Editor.is_word_boundary_right_vim, metrics); try cursor.move_right(root, metrics); diff --git a/test/tests_helix.zig b/test/tests_helix.zig index aafa12e..ca09a08 100644 --- a/test/tests_helix.zig +++ b/test/tests_helix.zig @@ -1,12 +1,47 @@ const std = @import("std"); const Buffer = @import("Buffer"); const Cursor = @import("Buffer").Cursor; +const Result = @import("command").Result; const helix = @import("tui").exports.mode.helix; +const Editor = @import("tui").exports.editor.Editor; -const ArrayList = std.ArrayList; const a = std.testing.allocator; +fn apply_movements(movements: []const u8, root: Buffer.Root, cursor: *Cursor, the_metrics: Buffer.Metrics, row: usize, col: usize) Result { + for (movements) |move| { + switch (move) { + 'W' => { + try helix.move_cursor_long_word_right(root, cursor, the_metrics); + }, + 'B' => { + try helix.move_cursor_long_word_left(root, cursor, the_metrics); + }, + 'E' => { + try helix.move_cursor_long_word_right_end(root, cursor, the_metrics); + }, + 'w' => { + try Editor.move_cursor_word_right_vim(root, cursor, the_metrics); + }, + 'b' => { + try helix.move_cursor_word_left_helix(root, cursor, the_metrics); + }, + 'e' => { + try helix.move_cursor_word_right_end_helix(root, cursor, the_metrics); + }, + else => {}, + } + } + try std.testing.expectEqual(col, cursor.col); + try std.testing.expectEqual(row, cursor.row); +} + +const MoveExpected = struct { + moves: []const u8, + row: usize, + col: usize, +}; + fn metrics() Buffer.Metrics { return .{ .ctx = undefined, @@ -29,49 +64,95 @@ fn metrics() Buffer.Metrics { .tab_width = 8, }; } +const doc: []const u8 = + \\gawk '{print length($0) }' testflowhelixwbe.txt | tr '\n' ' ' + \\a small $% Test.here, with.things()to demo + \\ with surrounding.space a bb AA a small and long + \\ + \\ + \\nospace. + \\ try std.testing.expectEqual(Buffer.Cursor{ .row = 0, .col = 0 }, buffer.root.byte_offset_to_line_and_col(0, test_metrics(), eol_mode)); + \\ + \\ + \\ $$%. []{{}. dart de + \\da +; +//60 44 54 0 2 8 138 0 0 22 2 0 -fn the_pos(buffer: Buffer, pos: u8) Cursor { - return buffer.root.byte_offset_to_line_and_col(pos, metrics(), .lf); -} +var eol_mode: Buffer.EolMode = .lf; +var sanitized: bool = false; +var the_cursor = Cursor{ .row = 1, .col = 1, .target = 0 }; -test "word_movement" { - const W = helix.move_cursor_long_word_right; - const B = helix.move_cursor_long_word_left; - const E = helix.move_cursor_long_word_right_end; - const doc: []const u8 = - \\a small $% Test.here, with.things()to demo - \\ with surrounding.space a bb AA a small and long - \\ - \\ - \\nospace. - \\ try std.testing.expectEqual(Buffer.Cursor{ .row = 0, .col = 0 }, buffer.root.byte_offset_to_line_and_col(0, test_metrics(), eol_mode)); - \\ - \\ - \\ $$%. []{{}. dart de - \\da - ; +// To run a specific test +// zig build test -Dtest-filter=word_movement - //44 55 0 8 0 - // TODO: test selections. Parity with Helix - - var eol_mode: Buffer.EolMode = .lf; - var sanitized: bool = false; +test "words_movement" { const buffer = try Buffer.create(a); defer buffer.deinit(); buffer.update(try buffer.load_from_string(doc, &eol_mode, &sanitized)); const root: Buffer.Root = buffer.root; - var c = Cursor{ .row = 0, .col = 0, .target = 0 }; - const t = the_pos; + the_cursor.col = 1; + the_cursor.row = 0; - try std.testing.expectEqual(Buffer.Cursor{ .row = 0, .col = 0 }, buffer.root.byte_offset_to_line_and_col(0, metrics(), eol_mode)); - try std.testing.expectEqual(try buffer.root.line_width(0, metrics()), 44); - try std.testing.expectEqual(try buffer.root.line_width(1, metrics()), 55); - try E(root, &c, metrics()); - try std.testing.expectEqual(c, t(buffer.*, 1)); - try B(root, &c, metrics()); - try std.testing.expectEqual(c, t(buffer.*, 0)); - try W(root, &c, metrics()); - try std.testing.expectEqual(c, t(buffer.*, 2)); - try B(root, &c, metrics()); - try std.testing.expectEqual(c, t(buffer.*, 1)); + const movements: [12]MoveExpected = .{ + .{ .moves = "b", .row = 0, .col = 0 }, + .{ .moves = "w", .row = 0, .col = 5 }, + .{ .moves = "b", .row = 0, .col = 1 }, + // TODO: Review the following line, an Stop is raising + // .{ .moves = "bb", .row = 0, .col = 0 }, + .{ .moves = "ww", .row = 0, .col = 7 }, + .{ .moves = "bb", .row = 0, .col = 1 }, + .{ .moves = "www", .row = 0, .col = 13 }, + .{ .moves = "bbb", .row = 0, .col = 1 }, + .{ .moves = "wwww", .row = 0, .col = 19 }, + .{ .moves = "bbbb", .row = 0, .col = 1 }, + .{ .moves = "wb", .row = 0, .col = 1 }, + .{ .moves = "e", .row = 0, .col = 4 }, + .{ .moves = "b", .row = 0, .col = 1 }, + // TODO: b has a bug when at the end of the view, it's + // not getting back. + // + // TODO: Another bug detected is when there are multiple + // lines, b is not able to get to the first non + // newline. + }; + + for (movements) |move| { + try apply_movements(move.moves, root, &the_cursor, metrics(), move.row, move.col); + } +} + +test "long_words_movement" { + const buffer = try Buffer.create(a); + defer buffer.deinit(); + buffer.update(try buffer.load_from_string(doc, &eol_mode, &sanitized)); + const root: Buffer.Root = buffer.root; + the_cursor.col = 1; + the_cursor.row = 0; + + const movements: [12]MoveExpected = .{ + .{ .moves = "B", .row = 0, .col = 0 }, + .{ .moves = "W", .row = 0, .col = 5 }, + .{ .moves = "B", .row = 0, .col = 1 }, + // TODO: Review the following line, an Stop is raising + // .{ .moves = "BB", .row = 0, .col = 0 }, + .{ .moves = "WW", .row = 0, .col = 13 }, + .{ .moves = "BB", .row = 0, .col = 1 }, + .{ .moves = "WWW", .row = 0, .col = 24 }, + .{ .moves = "BBB", .row = 0, .col = 1 }, + .{ .moves = "WWWW", .row = 0, .col = 27 }, + .{ .moves = "BBBB", .row = 0, .col = 1 }, + // TODO: + // WWWWW should report 48, is reporting 49, when changing modes + // the others report 48. This is an specific hx mode + // .{ .moves = "WWWWW", .row = 0, .col = 48 }, + // Same bugs detected in b are in B + .{ .moves = "WB", .row = 0, .col = 1 }, + .{ .moves = "E", .row = 0, .col = 4 }, + .{ .moves = "B", .row = 0, .col = 1 }, + }; + + for (movements) |move| { + try apply_movements(move.moves, root, &the_cursor, metrics(), move.row, move.col); + } }