diff options
Diffstat (limited to 'src/normal.zig')
| -rw-r--r-- | src/normal.zig | 39 |
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) { |
