summaryrefslogtreecommitdiff
path: root/src/normal.zig
diff options
context:
space:
mode:
Diffstat (limited to 'src/normal.zig')
-rw-r--r--src/normal.zig39
1 files changed, 39 insertions, 0 deletions
diff --git a/src/normal.zig b/src/normal.zig
index f7ba3810..4459ab53 100644
--- a/src/normal.zig
+++ b/src/normal.zig
@@ -245,6 +245,44 @@ pub fn nodeSelect(p: *Pardes, pane: *Pane, t: *Text, walk: modal.Normal.NodeWalk
t.setRanges(text, out[0..m], &.{}, @min(got.pri, m - 1), expl);
}
+/// `gw` (helix goto_word): label the words in view, nearest the cursor first,
+/// with two letters each; the next two keys choose one (`jumpKey`).
+fn startJump(p: *Pardes, pane: *Pane, t: *Text) void {
+ if (t.what != .body) return;
+ const text = edit.flatSurface(p, t) catch return;
+ const cursor = modal.selectionCursor(text, t.primaryRange(text, 0));
+ const top: usize = @intCast(@max(0, pane.scroll()));
+ const start = @min(modal.lineStartOffset(text, top), text.len);
+ const end = @min(modal.lineStartOffset(text, top + pane.rows), text.len);
+ const letters = config.jump_label_alphabet.len;
+ pane.jump.resize(p.gpa, letters * letters) catch return;
+ pane.jump.shrinkRetainingCapacity(modal.jumpWords(text, cursor, start, end, pane.jump.items));
+ pane.jump_first = null;
+}
+
+/// A key while `gw`'s labels are up: a label's first letter, then its
+/// second, which selects that word; anything else takes the labels away.
+pub fn jumpKey(p: *Pardes, pane: *Pane, t: *Text, key: Key) void {
+ const letters = config.jump_label_alphabet;
+ const at = if (key.ctrl or key.alt or key.cp > 0x7f) null else std.mem.indexOfScalar(u8, letters, @intCast(key.cp));
+ const i = at orelse return pane.jump.clearRetainingCapacity();
+ const first = pane.jump_first orelse {
+ if (i * letters.len > pane.jump.items.len) return pane.jump.clearRetainingCapacity();
+ pane.jump_first = @intCast(i);
+ return;
+ };
+ const chosen = first * letters.len + i;
+ defer pane.jump.clearRetainingCapacity();
+ if (chosen >= pane.jump.items.len) return;
+ const r = pane.jump.items[chosen];
+ const text = edit.flatSurface(p, t) catch return;
+ if (!t.select) return t.setRange(text, 0, .{ .anchor = @min(r.anchor, r.head), .head = @max(r.anchor, r.head) }, true);
+ // select mode stretches the primary to the word (helix Movement::Extend)
+ const pr = t.primaryRange(text, 0);
+ const anchor = if (r.anchor < r.head) @min(r.anchor, @min(pr.anchor, pr.head)) else @max(r.anchor, @max(pr.anchor, pr.head));
+ t.setRange(text, 0, .{ .anchor = anchor, .head = r.head }, true);
+}
+
const KeyState = struct {
mode: Text.Mode,
select: bool,
@@ -706,6 +744,7 @@ fn executeNormalAction(p: *Pardes, t: *Text, semantic: modal.Normal.Action) void
if (direction == .forward) @as(i32, 1) else -1,
),
.node => |walk| return nodeSelect(p, pane, t, walk),
+ .goto_word => return startJump(p, pane, t),
.macro_record => {
const m = &p.macro;
if (m.recording == 0) {