diff --git a/src/keybind/builtin/vim.json b/src/keybind/builtin/vim.json index 26b1a7a..a3ba41f 100644 --- a/src/keybind/builtin/vim.json +++ b/src/keybind/builtin/vim.json @@ -20,8 +20,8 @@ ["s", ["cut_forward_internal"], ["enter_mode", "insert"]], ["u", "undo"], - ["j", "move_down"], - ["k", "move_up"], + ["j", "move_down_vim"], + ["k", "move_up_vim"], ["l", "move_right_vim"], ["h", "move_left_vim"], ["", "move_right_vim"], @@ -33,15 +33,20 @@ ["o", ["smart_insert_line_after"], ["enter_mode", "insert"]], ["O", ["smart_insert_line_before"], ["enter_mode", "insert"]], + ["", "indent"], + ["", "unindent"], + ["v", "enter_mode", "visual"], - ["V", ["move_begin"], ["enter_mode", "visual"], ["select_end"]], + ["V", ["enter_mode", "visual line"], ["select_line_vim"]], ["n", "goto_next_match"], ["0", "move_begin"], ["^", "smart_move_begin"], ["$", "move_end"], [":", "open_command_palette"], + ["p", "paste_internal_vim"], + ["P", "paste_internal_vim"], ["gd", "goto_definition"], ["gi", "goto_implementation"], @@ -51,16 +56,25 @@ ["gD", "goto_declaration"], ["G", "move_buffer_end"], - ["d$", "delete_to_end"], + ["d$", "cut_to_end_vim"], ["dw", "cut_word_right_vim"], ["db", "cut_word_left_vim"], ["dd", "cut_internal_vim"], ["\"_dd", "delete_line"], + ["cc", ["cut_internal_vim"], ["enter_mode", "insert"]], + ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]], + ["D", "cut_to_end_vim"], + ["cw", ["cut_word_right_vim"], ["enter_mode", "insert"]], + ["cb", ["cut_word_left_vim"], ["enter_mode", "insert"]], + ["yy", ["copy_line_internal_vim"], ["cancel"]], - ["", "move_scroll_page_up"], - ["", "move_scroll_page_down"], + ["", "move_scroll_half_page_up_vim"], + ["", "move_scroll_half_page_down_vim"], + + ["zz", "scroll_view_center"], + ["u", "undo"], ["", "redo"], ["", "jump_back"], @@ -83,17 +97,77 @@ "cursor": "block", "selection": "normal", "press": [ - ["", "enter_mode", "normal"], + ["", ["cancel"], ["enter_mode", "normal"]], ["k", "select_up"], ["j", "select_down"], ["h", "select_left"], ["l", "select_right"], + ["b", "select_word_left_vim"], + ["w", "select_word_right_vim"], + ["W", "select_word_right"], + ["B", "select_word_left"], + ["e", "select_word_right_end_vim"], + + ["0", "move_begin"], + ["^", "smart_move_begin"], + ["$", "move_end"], + + ["p", ["paste_internal_vim"], ["enter_mode", "normal"]], + ["P", ["paste_internal_vim"], ["enter_mode", "normal"]], + + ["", "move_scroll_half_page_up_vim"], + ["", "move_scroll_half_page_down_vim"], + + ["zz", "scroll_view_center"], + ["", "indent"], + ["", "unindent"], + ["y", ["copy_internal_vim"], ["cancel"], ["enter_mode", "normal"]], - ["x", ["cut_forward_internal"], ["cancel"], ["enter_mode", "normal"]], - ["d", ["cut_forward_internal"], ["cancel"], ["enter_mode", "normal"]], - ["s", ["cut_forward_internal"], ["cancel"], ["enter_mode", "insert"]] + ["x", ["cut_forward_internal"], ["enter_mode", "normal"]], + ["d", ["cut_forward_internal"], ["enter_mode", "normal"]], + ["s", ["cut_forward_internal"], ["enter_mode", "insert"]], + + ["c", ["cut_forward_internal"], ["enter_mode", "insert"]], + ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]], + ["D", "cut_to_end_vim"] + ] + }, + "visual line": { + "syntax": "vim", + "on_match_failure": "ignore", + "name": "VISUAL LINE", + "line_numbers": "relative", + "cursor": "block", + "selection": "normal", + "press": [ + ["", ["cancel"], ["enter_mode", "normal"]], + ["k", "select_up"], + ["j", "select_down"], + + ["0", "move_begin"], + ["^", "smart_move_begin"], + ["$", "move_end"], + + ["p", ["paste_internal_vim"], ["enter_mode", "normal"]], + ["P", ["paste_internal_vim"], ["enter_mode", "normal"]], + + ["", "move_scroll_half_page_up_vim"], + ["", "move_scroll_half_page_down_vim"], + + ["", "indent"], + ["", "unindent"], + + ["y", ["copy_line_internal_vim"], ["cancel"], ["enter_mode", "normal"]], + + ["x", ["cut_internal_vim"], ["enter_mode", "normal"]], + ["d", ["cut_internal_vim"], ["enter_mode", "normal"]], + ["s", ["cut_internal_vim"], ["enter_mode", "insert"]], + + ["c", ["cut_internal_vim"], ["enter_mode", "insert"]], + ["C", ["cut_to_end_vim"], ["enter_mode", "insert"]], + ["D", "cut_to_end_vim"] ] }, "insert": { @@ -102,7 +176,7 @@ "line_numbers": "absolute", "cursor": "beam", "press": [ - ["", "enter_mode", "normal"], + ["", ["move_left_vim"], ["enter_mode", "normal"]], ["", "delete_forward"], ["", "delete_backward"], ["", "smart_insert_line"], diff --git a/src/tui/editor.zig b/src/tui/editor.zig index fd31e39..7e248cd 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -2029,6 +2029,14 @@ pub const Editor = struct { return false; } + fn is_eol_vim(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool { + const line_width = root.line_width(cursor.row, metrics) catch return true; + if (line_width == 0) return true; + if (cursor.col >= line_width) + return true; + return false; + } + fn move_cursor_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try cursor.move_left(root, metrics); } @@ -2070,14 +2078,28 @@ pub const Editor = struct { cursor.move_end(root, metrics); } + fn move_cursor_end_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { + move_cursor_right_until(root, cursor, is_eol_vim, metrics); + } + fn move_cursor_up(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { try cursor.move_up(root, metrics); } + fn move_cursor_up_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { + try cursor.move_up(root, metrics); + if(is_eol_vim(root, cursor, metrics)) try move_cursor_left_vim(root, cursor, metrics); + } + fn move_cursor_down(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { try cursor.move_down(root, metrics); } + fn move_cursor_down_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { + try cursor.move_down(root, metrics); + if(is_eol_vim(root, cursor, metrics)) try move_cursor_left_vim(root, cursor, metrics); + } + fn move_cursor_buffer_begin(_: Buffer.Root, cursor: *Cursor, _: Buffer.Metrics) !void { cursor.move_buffer_begin(); } @@ -2098,10 +2120,20 @@ pub const Editor = struct { cursor.move_half_page_up(root, view, metrics); } + fn move_cursor_half_page_up_vim(root: Buffer.Root, cursor: *Cursor, view: *const View, metrics: Buffer.Metrics) !void { + cursor.move_half_page_up(root, view, metrics); + if(is_eol_vim(root, cursor, metrics)) try move_cursor_left_vim(root, cursor, metrics); + } + fn move_cursor_half_page_down(root: Buffer.Root, cursor: *Cursor, view: *const View, metrics: Buffer.Metrics) !void { cursor.move_half_page_down(root, view, metrics); } + fn move_cursor_half_page_down_vim(root: Buffer.Root, cursor: *Cursor, view: *const View, metrics: Buffer.Metrics) !void { + cursor.move_half_page_down(root, view, metrics); + if(is_eol_vim(root, cursor, metrics)) try move_cursor_left_vim(root, cursor, metrics); + } + pub fn primary_click(self: *Self, y: c_int, x: c_int) !void { const root = self.buf_root() catch return; if (self.fast_scroll) { @@ -2738,6 +2770,15 @@ pub const Editor = struct { } pub const delete_to_end_meta = .{ .description = "Delete to end of line" }; + pub fn cut_to_end_vim(self: *Self, _: Context) Result { + const b = try self.buf_for_update(); + const text, const root = try self.cut_to(move_cursor_end_vim, b.root); + self.set_clipboard_internal(text); + try self.update_buf(root); + self.clamp(); + } + pub const cut_to_end_vim_meta = .{ .description = "Cut to end of line (vim)" }; + pub fn join_next_line(self: *Self, _: Context) Result { const b = try self.buf_for_update(); try self.with_cursors_const(b.root, move_cursor_end); @@ -2938,6 +2979,13 @@ pub const Editor = struct { } pub const move_up_meta = .{ .description = "Move cursor up" }; + pub fn move_up_vim(self: *Self, _: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const(root, move_cursor_up_vim) catch {}; + self.clamp(); + } + pub const move_up_vim_meta = .{ .description = "Move cursor up (vim)" }; + pub fn add_cursor_up(self: *Self, _: Context) Result { try self.push_cursor(); const primary = self.get_primary(); @@ -2954,6 +3002,13 @@ pub const Editor = struct { } pub const move_down_meta = .{ .description = "Move cursor down" }; + pub fn move_down_vim(self: *Self, _: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const(root, move_cursor_down_vim) catch {}; + self.clamp(); + } + pub const move_down_vim_meta = .{ .description = "Move cursor down (vim)" }; + pub fn add_cursor_down(self: *Self, _: Context) Result { try self.push_cursor(); const primary = self.get_primary(); @@ -3304,6 +3359,18 @@ pub const Editor = struct { } pub const move_scroll_half_page_up_meta = .{ .description = "Move and scroll half a page up" }; + pub fn move_scroll_half_page_up_vim(self: *Self, _: Context) Result { + if (self.screen_cursor(&self.get_primary().cursor)) |cursor| { + const root = try self.buf_root(); + self.with_cursors_and_view_const(root, move_cursor_half_page_up_vim, &self.view) catch {}; + const new_cursor_row = self.get_primary().cursor.row; + self.update_scroll_dest_abs(if (cursor.row > new_cursor_row) 0 else new_cursor_row - cursor.row); + } else { + return self.move_half_page_up(.{}); + } + } + pub const move_scroll_half_page_up_vim_meta = .{ .description = "Move and scroll half a page up (vim)" }; + pub fn move_scroll_half_page_down(self: *Self, _: Context) Result { if (self.screen_cursor(&self.get_primary().cursor)) |cursor| { const root = try self.buf_root(); @@ -3316,6 +3383,18 @@ pub const Editor = struct { } pub const move_scroll_half_page_down_meta = .{ .description = "Move and scroll half a page down" }; + pub fn move_scroll_half_page_down_vim(self: *Self, _: Context) Result { + if (self.screen_cursor(&self.get_primary().cursor)) |cursor| { + const root = try self.buf_root(); + self.with_cursors_and_view_const(root, move_cursor_half_page_down_vim, &self.view) catch {}; + const new_cursor_row = self.get_primary().cursor.row; + self.update_scroll_dest_abs(if (cursor.row > new_cursor_row) 0 else new_cursor_row - cursor.row); + } else { + return self.move_half_page_down(.{}); + } + } + pub const move_scroll_half_page_down_vim_meta = .{ .description = "Move and scroll half a page down (vim)" }; + pub fn smart_move_begin(self: *Self, _: Context) Result { const root = try self.buf_root(); try self.with_cursors_const(root, smart_move_cursor_begin); @@ -3396,6 +3475,16 @@ pub const Editor = struct { } pub const cancel_meta = .{ .description = "Cancel current action" }; + pub fn select_line_vim(self: *Self, _: Context) Result { + self.selection_mode = .line; + for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| + try self.select_line_around_cursor(cursel); + self.collapse_cursors(); + + self.clamp(); + } + pub const select_line_vim_meta = .{ .description = "Select the line around the cursor (vim)" }; + pub fn select_up(self: *Self, _: Context) Result { const root = try self.buf_root(); try self.with_selections_const(root, move_cursor_up); @@ -3447,6 +3536,13 @@ pub const Editor = struct { } pub const select_word_left_meta = .{ .description = "Select left by word" }; + pub fn select_word_left_vim(self: *Self, _: Context) Result { + const root = try self.buf_root(); + try self.with_selections_const(root, move_cursor_word_left_vim); + self.clamp(); + } + pub const select_word_left_vim_meta = .{ .description = "Select left by word (vim)" }; + pub fn select_word_right(self: *Self, _: Context) Result { const root = try self.buf_root(); try self.with_selections_const(root, move_cursor_word_right); @@ -3454,6 +3550,20 @@ pub const Editor = struct { } pub const select_word_right_meta = .{ .description = "Select right by word" }; + pub fn select_word_right_vim(self: *Self, _: Context) Result { + const root = try self.buf_root(); + try self.with_selections_const(root, move_cursor_word_right_vim); + self.clamp(); + } + pub const select_word_right_vim_meta = .{ .description = "Select right by word (vim)" }; + + pub fn select_word_right_end_vim(self: *Self, _: Context) Result { + const root = try self.buf_root(); + try self.with_selections_const(root, move_cursor_word_right_end_vim); + self.clamp(); + } + pub const select_word_right_end_vim_meta = .{ .description = "Select right by end of word (vim)" }; + pub fn select_word_begin(self: *Self, _: Context) Result { const root = try self.buf_root(); try self.with_selections_const(root, move_cursor_word_begin); @@ -3590,6 +3700,14 @@ pub const Editor = struct { cursel.cursor = sel.end; } + fn select_line_around_cursor(self: *Self, cursel: *CurSel) !void { + const root = try self.buf_root(); + const sel = try cursel.enable_selection(root, self.metrics); + sel.normalize(); + try move_cursor_begin(root, &sel.begin, self.metrics); + try move_cursor_end(root, &sel.end, self.metrics); + } + fn selection_reverse(_: Buffer.Root, cursel: *CurSel) !void { if (cursel.selection) |*sel| { sel.reverse();