diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index 4ecaca1..21b3258 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -13,6 +13,8 @@ const Buffer = @import("Buffer"); const Cursor = Buffer.Cursor; const Selection = Buffer.Selection; +const Direction = enum { backwards, forwards }; + var commands: Commands = undefined; pub fn init() !void { @@ -242,62 +244,62 @@ const cmds_ = struct { pub const extend_line_below_meta: Meta = .{ .arguments = &.{.integer}, .description = "Select current line, if already selected, extend to next line" }; pub fn move_next_word_start(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, Editor.move_cursor_word_right_vim); + try move_to_word(ctx, Editor.move_cursor_word_right_vim, .forwards); } pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start", .arguments = &.{.integer} }; pub fn extend_next_word_start(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, Editor.move_cursor_word_right_vim); + try extend_to_word(ctx, Editor.move_cursor_word_right_vim, .forwards); } pub const extend_next_word_start_meta: Meta = .{ .description = "Extend next word start", .arguments = &.{.integer} }; pub fn move_next_long_word_start(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, move_cursor_long_word_right); + try move_to_word(ctx, move_cursor_long_word_right, .forwards); } pub const move_next_long_word_start_meta: Meta = .{ .description = "Move next long word start", .arguments = &.{.integer} }; pub fn extend_next_long_word_start(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, move_cursor_long_word_right); + try extend_to_word(ctx, move_cursor_long_word_right, .forwards); } pub const extend_next_long_word_start_meta: Meta = .{ .description = "Extend next long word start", .arguments = &.{.integer} }; pub fn move_prev_word_start(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, move_cursor_word_left_helix); + try move_to_word(ctx, move_cursor_word_left_helix, .backwards); } pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start", .arguments = &.{.integer} }; pub fn extend_prev_word_start(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, move_cursor_word_left_helix); + try extend_to_word(ctx, move_cursor_word_left_helix, .backwards); } pub const extend_prev_word_start_meta: Meta = .{ .description = "Extend previous word start", .arguments = &.{.integer} }; pub fn move_prev_long_word_start(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, move_cursor_long_word_left); + try move_to_word(ctx, move_cursor_long_word_left, .backwards); } pub const move_prev_long_word_start_meta: Meta = .{ .description = "Move previous long word start", .arguments = &.{.integer} }; pub fn extend_prev_long_word_start(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, move_cursor_long_word_left); + try extend_to_word(ctx, move_cursor_long_word_left, .backwards); } pub const extend_prev_long_word_start_meta: Meta = .{ .description = "Extend previous word start", .arguments = &.{.integer} }; pub fn move_next_word_end(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, move_cursor_word_right_end_helix); + try move_to_word(ctx, move_cursor_word_right_end_helix, .forwards); } pub const move_next_word_end_meta: Meta = .{ .description = "Move next word end", .arguments = &.{.integer} }; pub fn extend_next_word_end(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, move_cursor_word_right_end_helix); + try extend_to_word(ctx, move_cursor_word_right_end_helix, .forwards); } pub const extend_next_word_end_meta: Meta = .{ .description = "Extend next word end", .arguments = &.{.integer} }; pub fn move_next_long_word_end(_: *void, ctx: Ctx) Result { - try move_to_word(ctx, move_cursor_long_word_right_end); + try move_to_word(ctx, move_cursor_long_word_right_end, .forwards); } pub const move_next_long_word_end_meta: Meta = .{ .description = "Move next long word end", .arguments = &.{.integer} }; pub fn extend_next_long_word_end(_: *void, ctx: Ctx) Result { - try extend_to_word(ctx, move_cursor_long_word_right_end); + try extend_to_word(ctx, move_cursor_long_word_right_end, .forwards); } pub const extend_next_long_word_end_meta: Meta = .{ .description = "Extend next long word end", .arguments = &.{.integer} }; @@ -526,7 +528,7 @@ fn match_bracket(root: Buffer.Root, cursel: *CurSel, ctx: command.Context, metri } } -fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const) command.Result { +fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direction: Direction) command.Result { const mv = tui.mainview() orelse return; const ed = mv.get_active_editor() orelse return; const root = try ed.buf_root(); @@ -537,17 +539,44 @@ fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const) comman if (repeat > 1) ed.with_cursors_const_repeat(root, move, command.fmt(.{repeat - 1})) catch {}; for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { - cursel.selection = null; + var sel = Selection.from_cursor(&cursel.cursor); + const cur = sel.begin.test_at(root, is_not_whitespace_or_eol, ed.metrics); + if (direction == .backwards) { + sel.begin.move_left(root, ed.metrics) catch continue; + const prev = sel.begin.test_at(root, Editor.is_not_word_char, ed.metrics); + sel.begin = sel.end; + if (!cur or cur != prev) + sel.begin.move_right(root, ed.metrics) catch continue; + } else { + sel.end.move_right(root, ed.metrics) catch continue; + const next = sel.end.test_at(root, Editor.is_not_word_char, ed.metrics); + if (!cur and cur != next) + sel.begin = sel.end; + } + cursel.cursor = sel.end; + cursel.selection = sel; }; ed.with_selections_const_repeat(root, move, command.fmt(.{1})) catch {}; ed.clamp(); } -fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const) command.Result { +fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direction: Direction) command.Result { const mv = tui.mainview() orelse return; const ed = mv.get_active_editor() orelse return; const root = try ed.buf_root(); + var repeat: usize = 1; + _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; + for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { + if (cursel.selection == null) { + cursel.selection = Selection.from_cursor(cursel.cursor); + } + const sel = &cursel.selection.?; + const pivot = if (sel.is_reversed()) cursel.begin - 1 else cursel.begin; + var i: usize = repeat; + while (i > 0) : (i -= 1) {} + }; + ed.with_selections_const_repeat(root, move, ctx) catch {}; ed.clamp(); } @@ -830,7 +859,6 @@ 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 { 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); } fn move_cursor_to_char_left_beyond_eol(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics, ctx: command.Context) error{Stop}!void {