diff --git a/src/keybind/builtin/helix.json b/src/keybind/builtin/helix.json index 2035724..728d827 100644 --- a/src/keybind/builtin/helix.json +++ b/src/keybind/builtin/helix.json @@ -62,7 +62,7 @@ ["shift+`", "switch_case"], ["shift+t", "till_prev_char"], - ["shift+f", "move_to_char", false], + ["shift+f", "move_to_char", "left"], ["shift+w", "move_next_long_word_start"], ["shift+b", "move_prev_long_word_start"], ["shift+e", "move_next_long_word_end"], @@ -107,7 +107,7 @@ ["l", "move_right"], ["t", "find_till_char"], - ["f", "move_to_char", true], + ["f", "move_to_char", "right"], ["`", "to_lower"], @@ -146,8 +146,8 @@ ["a", ["move_right"], ["enter_mode", "insert"]], ["o", ["smart_insert_line_after"], ["enter_mode", "insert"]], - ["d", "cut"], - ["c", ["cut"], ["enter_mode", "insert"]], + ["d", "cut_forward_internal_inclusive"], + ["c", ["cut_forward_internal_inclusive"], ["enter_mode", "insert"]], ["s", "select_regex"], [";", "collapse_selections"], @@ -204,13 +204,13 @@ ["page_up", "move_scroll_page_up"], ["page_down", "move_scroll_page_down"], - ["space shift+f", "file_picker_in_current_directory"], + ["space shift+f", "find_file"], ["space shift+s", "workspace_symbol_picker"], ["space shift+d", "workspace_diagnostics_picker"], ["space shift+p", "system_paste"], ["space shift+r", "replace_selections_with_clipboard"], ["space shift+/", "open_command_palette"], - ["space f", "file_picker"], + ["space f", "find_file"], ["space b", "buffer_picker"], ["space j", "jumplist_picker"], ["space s", "symbol_picker"], diff --git a/src/tui/editor.zig b/src/tui/editor.zig index a5db341..ada0f81 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -103,7 +103,7 @@ pub const CurSel = struct { }; } - fn enable_selection_normal(self: *Self) *Selection { + pub fn enable_selection_normal(self: *Self) *Selection { return if (self.selection) |*sel| sel else cod: { @@ -493,7 +493,7 @@ pub const Editor = struct { Widget.need_render(); } - fn buf_for_update(self: *Self) !*const Buffer { + pub fn buf_for_update(self: *Self) !*const Buffer { if (!self.pause_undo) { self.cursels_saved.clearAndFree(); self.cursels_saved = try self.cursels.clone(); @@ -501,7 +501,7 @@ pub const Editor = struct { return self.buffer orelse error.Stop; } - fn buf_root(self: *const Self) !Buffer.Root { + pub fn buf_root(self: *const Self) !Buffer.Root { return if (self.buffer) |p| p.root else error.Stop; } @@ -709,7 +709,7 @@ pub const Editor = struct { return meta.toOwnedSlice(); } - fn update_buf(self: *Self, root: Buffer.Root) !void { + pub fn update_buf(self: *Self, root: Buffer.Root) !void { const b = self.buffer orelse return error.Stop; return self.update_buf_and_eol_mode(root, b.file_eol_mode, b.file_utf8_sanitized); } @@ -1649,7 +1649,7 @@ pub const Editor = struct { self.view.col = dest.col; } - inline fn clamp(self: *Self) void { + pub inline fn clamp(self: *Self) void { self.clamp_abs(false); } @@ -1775,7 +1775,7 @@ pub const Editor = struct { cursel.check_selection(root, metrics); } - fn with_selections_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { + pub fn with_selections_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| with_selection_const(root, move, cursel, self.metrics) catch { @@ -1921,7 +1921,7 @@ pub const Editor = struct { const cursel_operator = *const fn (root: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root; const cursel_operator_mut = *const fn (self: *Self, root: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root; - fn is_not_word_char(c: []const u8) bool { + pub fn is_not_word_char(c: []const u8) bool { if (c.len == 0) return true; return switch (c[0]) { ' ' => true, @@ -1962,7 +1962,7 @@ pub const Editor = struct { return cursor.test_at(root, is_word_char, metrics); } - fn is_non_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { + pub fn is_non_word_char_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { return cursor.test_at(root, is_not_word_char, metrics); } @@ -1986,7 +1986,7 @@ pub const Editor = struct { return is_whitespace(c) or c[0] == '\n'; } - fn is_whitespace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { + pub fn is_whitespace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { return cursor.test_at(root, is_whitespace, metrics); } @@ -1994,7 +1994,7 @@ pub const Editor = struct { return !cursor.test_at(root, is_whitespace_or_eol, metrics); } - fn is_word_boundary_left_vim(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { + pub fn is_word_boundary_left_vim(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { if (is_whitespace_at_cursor(root, cursor, metrics)) return false; var next = cursor.*; next.move_left(root, metrics) catch return true; @@ -2087,11 +2087,11 @@ pub const Editor = struct { return false; } - fn move_cursor_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { + pub fn move_cursor_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try cursor.move_left(root, metrics); } - fn move_cursor_left_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { + pub fn move_cursor_left_until(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { while (!pred(root, cursor, metrics)) move_cursor_left(root, cursor, metrics) catch return; } @@ -2101,7 +2101,7 @@ pub const Editor = struct { move_cursor_left(root, cursor, metrics) catch return; } - fn move_cursor_begin(_: Buffer.Root, cursor: *Cursor, _: Buffer.Metrics) !void { + pub fn move_cursor_begin(_: Buffer.Root, cursor: *Cursor, _: Buffer.Metrics) !void { cursor.move_begin(); } @@ -2124,7 +2124,7 @@ pub const Editor = struct { move_cursor_right(root, cursor, metrics) catch return; } - fn move_cursor_end(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { + pub fn move_cursor_end(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { cursor.move_end(root, metrics); } @@ -2413,7 +2413,7 @@ pub const Editor = struct { } } - fn set_clipboard_internal(self: *Self, text: []const u8) void { + pub fn set_clipboard_internal(self: *Self, text: []const u8) void { if (self.clipboard) |old| self.allocator.free(old); self.clipboard = text; @@ -2476,7 +2476,7 @@ pub const Editor = struct { return root_; } - fn cut_to(self: *Self, move: cursor_operator_const, root_: Buffer.Root) !struct { []const u8, Buffer.Root } { + pub fn cut_to(self: *Self, move: cursor_operator_const, root_: Buffer.Root) !struct { []const u8, Buffer.Root } { var all_stop = true; var root = root_; diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index db27f14..85e15e2 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -5,6 +5,9 @@ const command = @import("command"); const cmd = command.executeName; const tui = @import("../tui.zig"); +const Editor = @import("../editor.zig").Editor; +const Buffer = @import("Buffer"); +const Cursor = Buffer.Cursor; var commands: Commands = undefined; @@ -72,4 +75,89 @@ const cmds_ = struct { }, sel); } pub const save_selection_meta: Meta = .{ .description = "Save current selection to location history" }; + + pub fn extend_line_below(_: *void, _: Ctx) Result { + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + + const root = try ed.buf_root(); + for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { + const sel = cursel.enable_selection_normal(); + sel.normalize(); + + try Editor.move_cursor_begin(root, &sel.begin, ed.metrics); + try Editor.move_cursor_end(root, &sel.end, ed.metrics); + cursel.cursor = sel.end; + try cursel.selection.?.end.move_right(root, ed.metrics); + try cursel.cursor.move_right(root, ed.metrics); + }; + + ed.clamp(); + } + pub const extend_line_below_meta: Meta = .{ .description = "Select current line, if already selected, extend to next line" }; + + pub fn move_next_word_start(_: *void, _: Ctx) Result { + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + const root = try ed.buf_root(); + + for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { + cursel.selection = null; + }; + + ed.with_selections_const(root, Editor.move_cursor_word_right_vim) catch {}; + ed.clamp(); + } + + pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start" }; + + pub fn move_prev_word_start(_: *void, _: Ctx) Result { + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + const root = try ed.buf_root(); + + for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { + cursel.selection = null; + }; + + ed.with_selections_const(root, move_cursor_word_left_helix) catch {}; + ed.clamp(); + } + pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start" }; + + pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result { + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + const b = try ed.buf_for_update(); + const text, const root = try ed.cut_to(move_noop, b.root); + ed.set_clipboard_internal(text); + try ed.update_buf(root); + ed.clamp(); + } + pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" }; }; + +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 " " + while (Editor.is_whitespace_at_cursor(root, cursor, metrics)) { + try Editor.move_cursor_left(root, cursor, metrics); + } + + var next = cursor.*; + next.move_left(root, metrics) catch return; + var next_next = next; + next_next.move_left(root, metrics) catch return; + + const cur = next.test_at(root, Editor.is_not_word_char, metrics); + const nxt = next_next.test_at(root, Editor.is_not_word_char, metrics); + if (cur != nxt) { + try Editor.move_cursor_left(root, cursor, metrics); + return; + } else { + try move_cursor_word_left_helix(root, cursor, metrics); + } +} + +fn move_noop(_: Buffer.Root, _: *Cursor, _: Buffer.Metrics) error{Stop}!void {}