From e87e16547a17864b2a0cd574c1aa42d44125bc6c Mon Sep 17 00:00:00 2001 From: Gabriel Schneider Date: Mon, 28 Sep 2026 16:35:27 -0300 Subject: Alt-o, Alt-i and the other tree-sitter keys walk the file's syntax tree helix's expand/shrink, previous/next sibling, all siblings, all children and parent-node end/start (Alt-o/up, Alt-i/down, Alt-p/left, Alt-right, Alt-a, Alt-I, Alt-e, Alt-b; Alt-n stays the new terminal) walk a whole-file parse the file keeps (File.node_tree), made on the first such key after an edit and reused until the next, never per key. syntax.walkNodes follows helix's TreeCursor over every node, anonymous ones included, and Alt-i goes back to what Alt-o grew from while the selection still holds it. The harness runs without grammars, so the cases prove the plain-text no-op; a test replays eleven JSON walks whose results were taken from the installed hx. Co-Authored-By: Claude Opus 5.5 --- src/modal.zig | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) (limited to 'src/modal.zig') diff --git a/src/modal.zig b/src/modal.zig index f5f3081e..ae008f60 100644 --- a/src/modal.zig +++ b/src/modal.zig @@ -100,6 +100,14 @@ pub const Normal = struct { keep_regex, remove_regex, ensure_forward, + expand_node, + shrink_node, + next_sibling, + prev_sibling, + all_siblings, + all_children, + parent_node_end, + parent_node_start, delete, delete_noyank, @@ -213,6 +221,8 @@ pub const Normal = struct { } }; + /// helix's tree-sitter selection keys: where a range goes in the tree + pub const NodeWalk = enum { expand, shrink, next_sibling, prev_sibling, all_siblings, all_children, parent_end, parent_start }; /// what the s/S/K/Alt-K prompt does with its pattern's matches pub const RegexMode = enum { select, split, keep, remove }; pub const Scope = enum { once, per_selection }; @@ -331,6 +341,7 @@ pub const Normal = struct { search_step: Direction, macro_record, macro_replay: u32, + node: NodeWalk, repeat_insert: u32, pub fn scope(value: Action) Scope { @@ -345,6 +356,7 @@ pub const Normal = struct { .search_step, .macro_record, .macro_replay, + .node, .repeat_insert, => .once, .edit => |edit| switch (edit.kind) { @@ -588,6 +600,14 @@ pub const Normal = struct { if (key.has(.keep_regex)) return resultAction(.{ .select_regex = .keep }); if (key.has(.remove_regex)) return resultAction(.{ .select_regex = .remove }); if (key.has(.ensure_forward)) return resultAction(.{ .multi = .{ .kind = .ensure_forward, .count = count } }); + if (key.has(.expand_node)) return resultAction(.{ .node = .expand }); + if (key.has(.shrink_node)) return resultAction(.{ .node = .shrink }); + if (key.has(.next_sibling)) return resultAction(.{ .node = .next_sibling }); + if (key.has(.prev_sibling)) return resultAction(.{ .node = .prev_sibling }); + if (key.has(.all_siblings)) return resultAction(.{ .node = .all_siblings }); + if (key.has(.all_children)) return resultAction(.{ .node = .all_children }); + if (key.has(.parent_node_end)) return resultAction(.{ .node = .parent_end }); + if (key.has(.parent_node_start)) return resultAction(.{ .node = .parent_start }); if (key.has(.delete)) return resultAction(.{ .edit = .{ .kind = .delete, .count = count } }); if (key.has(.delete_noyank)) return resultAction(.{ .edit = .{ .kind = .delete_noyank, .count = count } }); -- cgit v1.3