diff --git a/src/keybind/builtin/vim.json b/src/keybind/builtin/vim.json index 626cae6..7389845 100644 --- a/src/keybind/builtin/vim.json +++ b/src/keybind/builtin/vim.json @@ -75,8 +75,8 @@ ["yy", ["copy_line_internal_vim"], ["cancel"]], - ["", "move_scroll_half_page_up_vim"], - ["", "move_scroll_half_page_down_vim"], + ["", "move_scroll_half_page_up"], + ["", "move_scroll_half_page_down"], ["zz", "scroll_view_center"], @@ -149,8 +149,8 @@ ["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"], + ["", "move_scroll_half_page_up"], + ["", "move_scroll_half_page_down"], ["zz", "scroll_view_center"], ["", "indent"], @@ -196,8 +196,8 @@ ["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"], + ["", "move_scroll_half_page_up"], + ["", "move_scroll_half_page_down"], ["", "indent"], ["", "unindent"], diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 6f88b77..7a9e7ac 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -1628,7 +1628,7 @@ pub const Editor = struct { return row < sel.begin.row or (row == sel.begin.row and col < sel.begin.col); } - inline fn screen_cursor(self: *const Self, cursor: *const Cursor) ?Cursor { + pub inline fn screen_cursor(self: *const Self, cursor: *const Cursor) ?Cursor { return if (self.view.is_visible(cursor)) .{ .row = cursor.row - self.view.row, .col = cursor.col - self.view.col, @@ -1741,7 +1741,7 @@ pub const Editor = struct { try self.send_editor_cursel_msg("jump_source", self.get_primary()); } - fn send_editor_jump_destination(self: *Self) !void { + pub fn send_editor_jump_destination(self: *Self) !void { try self.send_editor_cursel_msg("jump_destination", self.get_primary()); } @@ -1959,7 +1959,7 @@ pub const Editor = struct { try move(root, &cursel.cursor, view, metrics); } - fn with_cursors_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { + pub fn with_cursors_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| with_cursor_and_view_const(root, move, cursel, view, self.metrics) catch { @@ -2028,7 +2028,7 @@ pub const Editor = struct { cursel.cursor = sel.end; } - fn with_selections_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { + pub fn with_selections_and_view_const(self: *Self, root: Buffer.Root, move: cursor_view_operator_const, view: *const View) error{Stop}!void { var someone_stopped = false; for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| with_selection_and_view_const(root, move, cursel, view, self.metrics) catch { @@ -2346,7 +2346,7 @@ pub const Editor = struct { return false; } - fn is_eol_left(_: Buffer.Root, cursor: *const Cursor, _: Buffer.Metrics) bool { + pub fn is_eol_left(_: Buffer.Root, cursor: *const Cursor, _: Buffer.Metrics) bool { if (cursor.col == 0) return true; return false; @@ -2359,22 +2359,6 @@ pub const Editor = struct { return false; } - fn is_eol_right_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 - 1) - return true; - 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; - } - pub fn move_cursor_left(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { try cursor.move_left(root, metrics); } @@ -2384,7 +2368,7 @@ pub const Editor = struct { move_cursor_left(root, cursor, metrics) catch return; } - fn move_cursor_left_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { + pub fn move_cursor_left_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { if (!pred(root, cursor, metrics)) move_cursor_left(root, cursor, metrics) catch return; } @@ -2407,7 +2391,7 @@ pub const Editor = struct { move_cursor_right(root, cursor, metrics) catch return; } - fn move_cursor_right_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { + pub fn move_cursor_right_unless(root: Buffer.Root, cursor: *Cursor, pred: cursor_predicate, metrics: Buffer.Metrics) void { if (!pred(root, cursor, metrics)) move_cursor_right(root, cursor, metrics) catch return; } @@ -2416,32 +2400,18 @@ 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 { cursor.move_up(root, metrics) catch |e| switch (e) { error.Stop => cursor.move_begin(), }; } - 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); - } - pub fn move_cursor_down(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { cursor.move_down(root, metrics) catch |e| switch (e) { error.Stop => cursor.move_end(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(); } @@ -2458,24 +2428,6 @@ pub const Editor = struct { cursor.move_page_down(root, view, metrics); } - fn move_cursor_half_page_up(root: Buffer.Root, cursor: *Cursor, view: *const View, metrics: Buffer.Metrics) !void { - 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) { @@ -3190,14 +3142,6 @@ pub const Editor = struct { } pub const delete_line_meta: Meta = .{ .description = "Delete current line", .arguments = &.{.integer} }; - pub fn cut_to_end_vim(self: *Self, _: Context) Result { - const b = try self.buf_for_update(); - const root = try self.cut_to(move_cursor_end_vim, b.root); - try self.update_buf(root); - self.clamp(); - } - pub const cut_to_end_vim_meta: Meta = .{ .description = "Cut to end of line (vim)" }; - pub fn join_next_line(self: *Self, ctx: Context) Result { const b = try self.buf_for_update(); try self.with_cursors_const_repeat(b.root, move_cursor_end, ctx); @@ -3248,28 +3192,6 @@ pub const Editor = struct { } pub const move_right_meta: Meta = .{ .description = "Move cursor right", .arguments = &.{.integer} }; - fn move_cursor_left_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { - move_cursor_left_unless(root, cursor, is_eol_left, metrics); - } - - fn move_cursor_right_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { - move_cursor_right_unless(root, cursor, is_eol_right_vim, metrics); - } - - pub fn move_left_vim(self: *Self, ctx: Context) Result { - const root = try self.buf_root(); - self.with_cursors_const_repeat(root, move_cursor_left_vim, ctx) catch {}; - self.clamp(); - } - pub const move_left_vim_meta: Meta = .{ .description = "Move cursor left (vim)", .arguments = &.{.integer} }; - - pub fn move_right_vim(self: *Self, ctx: Context) Result { - const root = try self.buf_root(); - self.with_cursors_const_repeat(root, move_cursor_right_vim, ctx) catch {}; - self.clamp(); - } - pub const move_right_vim_meta: Meta = .{ .description = "Move cursor right (vim)", .arguments = &.{.integer} }; - fn move_cursor_word_begin(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { if (is_non_word_char_at_cursor(root, cursor, metrics)) { move_cursor_left_until(root, cursor, is_word_boundary_right, metrics); @@ -3556,13 +3478,6 @@ pub const Editor = struct { } pub const move_up_meta: Meta = .{ .description = "Move cursor up", .arguments = &.{.integer} }; - pub fn move_up_vim(self: *Self, ctx: Context) Result { - const root = try self.buf_root(); - self.with_cursors_const_repeat(root, move_cursor_up_vim, ctx) catch {}; - self.clamp(); - } - pub const move_up_vim_meta: Meta = .{ .description = "Move cursor up (vim)", .arguments = &.{.integer} }; - pub fn add_cursor_up(self: *Self, ctx: Context) Result { const root = try self.buf_root(); var repeat: usize = 1; @@ -3583,13 +3498,6 @@ pub const Editor = struct { } pub const move_down_meta: Meta = .{ .description = "Move cursor down", .arguments = &.{.integer} }; - pub fn move_down_vim(self: *Self, ctx: Context) Result { - const root = try self.buf_root(); - self.with_cursors_const_repeat(root, move_cursor_down_vim, ctx) catch {}; - self.clamp(); - } - pub const move_down_vim_meta: Meta = .{ .description = "Move cursor down (vim)", .arguments = &.{.integer} }; - pub fn add_cursor_down(self: *Self, ctx: Context) Result { var repeat: usize = 1; _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; @@ -4027,54 +3935,6 @@ pub const Editor = struct { } pub const move_scroll_page_down_meta: Meta = .{ .description = "Move and scroll page down" }; - pub fn move_scroll_half_page_up(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, &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_meta: 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: 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(); - self.with_cursors_and_view_const(root, move_cursor_half_page_down, &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_meta: 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: 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_once(root, smart_move_cursor_begin); @@ -4363,24 +4223,6 @@ pub const Editor = struct { } pub const select_page_down_meta: Meta = .{ .description = "Select page down" }; - pub fn select_half_page_up(self: *Self, _: Context) Result { - try self.send_editor_jump_source(); - const root = try self.buf_root(); - try self.with_selections_and_view_const(root, move_cursor_half_page_up, &self.view); - self.clamp(); - try self.send_editor_jump_destination(); - } - pub const select_half_page_up_meta: Meta = .{ .description = "Select half a page up" }; - - pub fn select_half_page_down(self: *Self, _: Context) Result { - try self.send_editor_jump_source(); - const root = try self.buf_root(); - try self.with_selections_and_view_const(root, move_cursor_half_page_down, &self.view); - self.clamp(); - try self.send_editor_jump_destination(); - } - pub const select_half_page_down_meta: Meta = .{ .description = "Select half a page down" }; - pub fn select_all(self: *Self, _: Context) Result { try self.send_editor_jump_source(); self.cancel_all_selections(); diff --git a/src/tui/mode/helix.zig b/src/tui/mode/helix.zig index 248f2ba..849700d 100644 --- a/src/tui/mode/helix.zig +++ b/src/tui/mode/helix.zig @@ -13,16 +13,19 @@ const CurSel = @import("../editor.zig").CurSel; const Buffer = @import("Buffer"); const Cursor = Buffer.Cursor; const Selection = Buffer.Selection; - +const View = Buffer.View; const char_class = Editor.char_class; const Direction = enum { backwards, forwards }; var commands: Commands = undefined; +const Self = Editor; + pub fn init() !void { - var v: void = {}; - try commands.init(&v); + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + try commands.init(ed); } pub fn deinit() void { @@ -30,60 +33,60 @@ pub fn deinit() void { } const Commands = command.Collection(cmds_); -const Ctx = command.Context; +const Context = command.Context; const Meta = command.Metadata; const Result = command.Result; const cmds_ = struct { - pub const Target = void; + pub const Target = Self; - pub fn w(_: *void, _: Ctx) Result { + pub fn w(_: *Self, _: Context) Result { try cmd("save_file", .{}); } pub const w_meta: Meta = .{ .description = "w (write/save file)" }; - pub fn q(_: *void, _: Ctx) Result { + pub fn q(_: *Self, _: Context) Result { try cmd("quit", .{}); } pub const q_meta: Meta = .{ .description = "q (quit)" }; - pub fn qa(_: *void, _: Ctx) Result { + pub fn qa(_: *Self, _: Context) Result { try cmd("quit", .{}); } pub const qa_meta: Meta = .{ .description = "qa (close all)" }; - pub fn @"q!"(_: *void, _: Ctx) Result { + pub fn @"q!"(_: *Self, _: Context) Result { try cmd("quit_without_saving", .{}); } pub const @"q!_meta": Meta = .{ .description = "q! (quit without saving)" }; - pub fn @"qa!"(_: *void, _: Ctx) Result { + pub fn @"qa!"(_: *Self, _: Context) Result { try cmd("quit_without_saving", .{}); } pub const @"qa!_meta": Meta = .{ .description = "qa! (quit without saving)" }; - pub fn wq(_: *void, _: Ctx) Result { + pub fn wq(_: *Self, _: Context) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); } pub const wq_meta: Meta = .{ .description = "wq (write/save file and quit)" }; - pub fn @"x!"(_: *void, _: Ctx) Result { + pub fn @"x!"(_: *Self, _: Context) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit_without_saving", .{} } })); } pub const @"x!_meta": Meta = .{ .description = "x! (write/save file and exit, ignoring other unsaved changes)" }; - pub fn x(_: *void, _: Ctx) Result { + pub fn x(_: *Self, _: Context) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); } pub const x_meta: Meta = .{ .description = "x (write/save file and quit)" }; - pub fn wa(_: *void, _: Ctx) Result { + pub fn wa(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| bm.save_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); } pub const wa_meta: Meta = .{ .description = "wa (save all)" }; - pub fn xa(_: *void, _: Ctx) Result { + pub fn xa(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| { bm.save_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); try cmd("quit", .{}); @@ -91,7 +94,7 @@ const cmds_ = struct { } pub const xa_meta: Meta = .{ .description = "xa (write all and quit)" }; - pub fn @"xa!"(_: *void, _: Ctx) Result { + pub fn @"xa!"(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| { bm.save_all() catch {}; try cmd("quit_without_saving", .{}); @@ -99,14 +102,14 @@ const cmds_ = struct { } pub const @"xa!_meta": Meta = .{ .description = "xa! (write all and exit, ignoring other unsaved changes)" }; - pub fn wqa(_: *void, _: Ctx) Result { + pub fn wqa(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| bm.save_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); try cmd("quit", .{}); } pub const wqa_meta: Meta = .{ .description = "wqa (write all and quit)" }; - pub fn @"wqa!"(_: *void, _: Ctx) Result { + pub fn @"wqa!"(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| { bm.save_all() catch {}; try cmd("quit_without_saving", .{}); @@ -114,54 +117,54 @@ const cmds_ = struct { } pub const @"wqa!_meta": Meta = .{ .description = "wqa! (write all and exit, ignoring unsaved changes)" }; - pub fn rl(_: *void, _: Ctx) Result { + pub fn rl(_: *Self, _: Context) Result { try cmd("reload_file", .{}); } pub const rl_meta: Meta = .{ .description = "rl (reload current file)" }; - pub fn rla(_: *void, _: Ctx) Result { + pub fn rla(_: *Self, _: Context) Result { if (tui.get_buffer_manager()) |bm| bm.reload_all() catch |e| return tp.exit_error(e, @errorReturnTrace()); } pub const rla_meta: Meta = .{ .description = "rla (reload all files)" }; - pub fn o(_: *void, _: Ctx) Result { + pub fn o(_: *Self, _: Context) Result { try cmd("open_file", .{}); } pub const o_meta: Meta = .{ .description = "o (open file)" }; - pub fn @"wq!"(_: *void, _: Ctx) Result { + pub fn @"wq!"(_: *Self, _: Context) Result { cmd("save_file", .{}) catch {}; try cmd("quit_without_saving", .{}); } pub const @"wq!_meta": Meta = .{ .description = "wq! (write/save file and quit without saving)" }; - pub fn n(_: *void, _: Ctx) Result { + pub fn n(_: *Self, _: Context) Result { try cmd("create_new_file", .{}); } pub const n_meta: Meta = .{ .description = "n (Create new buffer/tab)" }; - pub fn bn(_: *void, _: Ctx) Result { + pub fn bn(_: *Self, _: Context) Result { try cmd("next_tab", .{}); } pub const bn_meta: Meta = .{ .description = "bn (Next buffer/tab)" }; - pub fn bp(_: *void, _: Ctx) Result { + pub fn bp(_: *Self, _: Context) Result { try cmd("previous_tab", .{}); } pub const bp_meta: Meta = .{ .description = "bp (Previous buffer/tab)" }; - pub fn bc(_: *void, _: Ctx) Result { + pub fn bc(_: *Self, _: Context) Result { try cmd("delete_buffer", .{}); } pub const bc_meta: Meta = .{ .description = "bc (Close buffer/tab)" }; - pub fn @"bc!"(_: *void, _: Ctx) Result { + pub fn @"bc!"(_: *Self, _: Context) Result { try cmd("close_file_without_saving", .{}); } pub const @"bc!_meta": Meta = .{ .description = "bc! (Close buffer/tab, ignoring changes)" }; - pub fn @"bco!"(_: *void, _: Ctx) Result { + pub fn @"bco!"(_: *Self, _: Context) Result { const mv = tui.mainview() orelse return; if (tui.get_buffer_manager()) |bm| { if (mv.get_active_buffer()) |buffer| try bm.delete_others(buffer); @@ -169,7 +172,7 @@ const cmds_ = struct { } pub const @"bco!_meta": Meta = .{ .description = "bco! (Close other buffers/tabs, discarding changes)" }; - pub fn bco(_: *void, _: Ctx) Result { + pub fn bco(_: *Self, _: Context) Result { const logger = log.logger("helix-mode"); defer logger.deinit(); const mv = tui.mainview() orelse return; @@ -184,7 +187,49 @@ const cmds_ = struct { } pub const bco_meta: Meta = .{ .description = "bco (Close other buffers/tabs)" }; - pub fn save_selection(_: *void, _: Ctx) Result { + pub fn move_scroll_half_page_up(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, &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_meta: Meta = .{ .description = "Move and scroll half a page up" }; + + 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(); + self.with_cursors_and_view_const(root, move_cursor_half_page_down, &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_meta: Meta = .{ .description = "Move and scroll half a page down" }; + + pub fn select_half_page_up(self: *Self, _: Context) Result { + try self.send_editor_jump_source(); + const root = try self.buf_root(); + try self.with_selections_and_view_const(root, move_cursor_half_page_up, &self.view); + self.clamp(); + try self.send_editor_jump_destination(); + } + pub const select_half_page_up_meta: Meta = .{ .description = "Select half a page up" }; + + pub fn select_half_page_down(self: *Self, _: Context) Result { + try self.send_editor_jump_source(); + const root = try self.buf_root(); + try self.with_selections_and_view_const(root, move_cursor_half_page_down, &self.view); + self.clamp(); + try self.send_editor_jump_destination(); + } + pub const select_half_page_down_meta: Meta = .{ .description = "Select half a page down" }; + + pub fn save_selection(_: *Self, _: Context) Result { const logger = log.logger("helix-mode"); defer logger.deinit(); logger.print("saved location", .{}); @@ -202,7 +247,7 @@ const cmds_ = struct { } pub const save_selection_meta: Meta = .{ .description = "Save current selection to location history" }; - pub fn split_selection_on_newline(_: *void, _: Ctx) Result { + pub fn split_selection_on_newline(_: *Self, _: Context) Result { const ed, const root = get_buf() orelse return; const cursels = try ed.cursels.toOwnedSlice(ed.allocator); defer ed.allocator.free(cursels); @@ -213,14 +258,14 @@ const cmds_ = struct { } pub const split_selection_on_newline_meta: Meta = .{ .description = "Add cursor to each line in selection helix" }; - pub fn match_brackets(_: *void, ctx: Ctx) Result { + pub fn match_brackets(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return; try ed.with_cursels_const_once_arg(root, &match_bracket, ctx); ed.clamp(); } pub const match_brackets_meta: Meta = .{ .description = "Goto matching bracket" }; - pub fn extend_line_below(_: *void, ctx: Ctx) Result { + pub fn extend_line_below(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return; var repeat: usize = 1; _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; @@ -240,73 +285,73 @@ 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 init_helix_select_mode(_: *void, _: Ctx) Result { + pub fn init_helix_select_mode(_: *Self, _: Context) Result { _, const ed = get_context() orelse return; try ed.enable_selection(.{}); } pub const init_helix_select_mode_meta: Meta = .{}; - pub fn move_next_word_start(_: *void, ctx: Ctx) Result { + pub fn move_next_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn extend_next_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn move_next_long_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn extend_next_long_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn move_prev_word_start(_: *Self, ctx: Context) Result { try move_cursels_const_repeat(move_cursor_prev_word_start, ctx); } 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 { + pub fn extend_prev_word_start(_: *Self, ctx: Context) Result { try move_cursels_const_repeat(move_cursor_prev_word_start_extend, ctx); } 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 { + pub fn move_prev_long_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn extend_prev_long_word_start(_: *Self, ctx: Context) Result { 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 { + pub fn move_next_word_end(_: *Self, ctx: Context) Result { 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 { + pub fn extend_next_word_end(_: *Self, ctx: Context) Result { 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 { + pub fn move_next_long_word_end(_: *Self, ctx: Context) Result { 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 { + pub fn extend_next_long_word_end(_: *Self, ctx: Context) Result { 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} }; - pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result { + pub fn cut_forward_internal_inclusive(_: *Self, _: Context) Result { const ed, const b = get_buf_for_update() orelse return; tui.clipboard_start_group(); const root = try ed.cut_to(move_noop, b.root); @@ -315,7 +360,7 @@ const cmds_ = struct { } pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" }; - pub fn select_right_helix(_: *void, ctx: Ctx) Result { + pub fn select_right_helix(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return; var repeat: usize = 1; _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; @@ -340,7 +385,7 @@ const cmds_ = struct { } pub const select_right_helix_meta: Meta = .{ .description = "Select right", .arguments = &.{.integer} }; - pub fn select_left_helix(_: *void, ctx: Ctx) Result { + pub fn select_left_helix(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return; var repeat: usize = 1; _ = ctx.args.match(.{tp.extract(&repeat)}) catch false; @@ -368,47 +413,47 @@ const cmds_ = struct { } pub const select_left_helix_meta: Meta = .{ .description = "Select left", .arguments = &.{.integer} }; - pub fn select_to_char_left_helix(_: *void, ctx: Ctx) Result { + pub fn select_to_char_left_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &select_cursel_to_char_left_helix); } pub const select_to_char_left_helix_meta: Meta = .{ .description = "Select to char left" }; - pub fn select_till_char_left_helix(_: *void, ctx: Ctx) Result { + pub fn select_till_char_left_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &select_cursel_till_char_left_helix); } pub const select_till_char_left_helix_meta: Meta = .{ .description = "Select until char left" }; - pub fn extend_to_char_left_helix(_: *void, ctx: Ctx) Result { + pub fn extend_to_char_left_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &extend_cursel_to_char_left_helix); } pub const extend_to_char_left_helix_meta: Meta = .{ .description = "Extend Selection to char left" }; - pub fn extend_till_char_left_helix(_: *void, ctx: Ctx) Result { + pub fn extend_till_char_left_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &extend_cursel_till_char_left_helix); } pub const extend_till_char_left_helix_meta: Meta = .{ .description = "Extend Selection until char left" }; - pub fn select_till_char_right_helix(_: *void, ctx: Ctx) Result { + pub fn select_till_char_right_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &select_cursel_till_char_right_helix); } pub const select_till_char_right_helix_meta: Meta = .{ .description = "Select until char right" }; - pub fn select_to_char_right_helix(_: *void, ctx: Ctx) Result { + pub fn select_to_char_right_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &select_cursel_to_char_right_helix); } pub const select_to_char_right_helix_meta: Meta = .{ .description = "Select to char right" }; - pub fn extend_till_char_right_helix(_: *void, ctx: Ctx) Result { + pub fn extend_till_char_right_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &extend_cursel_till_char_right_helix); } pub const extend_till_char_right_helix_meta: Meta = .{ .description = "Extend Selection until char right" }; - pub fn extend_to_char_right_helix(_: *void, ctx: Ctx) Result { + pub fn extend_to_char_right_helix(_: *Self, ctx: Context) Result { try to_char_helix(ctx, &extend_cursel_to_char_right_helix); } pub const extend_to_char_right_helix_meta: Meta = .{ .description = "Extend Selection to char right" }; - pub fn select_textobject_inner(_: *void, ctx: Ctx) Result { + pub fn select_textobject_inner(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return error.Stop; var action: []const u8 = ""; @@ -425,7 +470,7 @@ const cmds_ = struct { } pub const select_textobject_inner_meta: Meta = .{ .description = "select inside object helix" }; - pub fn select_textobject_around(_: *void, ctx: Ctx) Result { + pub fn select_textobject_around(_: *Self, ctx: Context) Result { const ed, const root = get_buf() orelse return; var action: []const u8 = ""; @@ -442,7 +487,7 @@ const cmds_ = struct { } pub const select_textobject_around_meta: Meta = .{ .description = "select around object helix" }; - pub fn copy_helix(_: *void, _: Ctx) Result { + pub fn copy_helix(_: *Self, _: Context) Result { const ed, const root = get_buf() orelse return; tui.clipboard_start_group(); @@ -453,22 +498,22 @@ const cmds_ = struct { } pub const copy_helix_meta: Meta = .{ .description = "Copy selection to clipboard (helix)" }; - pub fn paste_after(_: *void, ctx: Ctx) Result { + pub fn paste_after(_: *Self, ctx: Context) Result { try paste_helix(ctx, insert_after); } pub const paste_after_meta: Meta = .{ .description = "Paste from clipboard after selection" }; - pub fn replace_selections_with_clipboard(_: *void, ctx: Ctx) Result { + pub fn replace_selections_with_clipboard(_: *Self, ctx: Context) Result { try paste_helix(ctx, insert_replace_selection); } pub const replace_selections_with_clipboard_meta: Meta = .{ .description = "Replace selection from clipboard" }; - pub fn paste_clipboard_before(_: *void, ctx: Ctx) Result { + pub fn paste_clipboard_before(_: *Self, ctx: Context) Result { try paste_helix(ctx, insert_before); } pub const paste_clipboard_before_meta: Meta = .{ .description = "Paste from clipboard before selection" }; - pub fn replace_with_character_helix(_: *void, ctx: Ctx) Result { + pub fn replace_with_character_helix(_: *Self, ctx: Context) Result { const ed, const b = get_buf_for_update() orelse return; var root = b.root; root = try ed.with_cursels_mut_once_arg(root, replace_cursel_with_character, ed.allocator, ctx); @@ -554,7 +599,7 @@ fn move_cursor_prev_word_start_extend(root: Buffer.Root, cursel: *CurSel, metric try move_cursor_prev_word_start(root, cursel, metrics); } -fn move_cursels_const_repeat(move: Editor.cursel_operator_const, ctx: Ctx) Result { +fn move_cursels_const_repeat(move: Editor.cursel_operator_const, ctx: Context) Result { const ed, const root = get_buf() orelse return; try ed.with_cursels_const_repeat(root, move, ctx); ed.clamp(); @@ -1294,6 +1339,14 @@ fn is_cursel_from_extend_line_below(cursel: CurSel) bool { return false; } +fn move_cursor_half_page_up(root: Buffer.Root, cursor: *Cursor, view: *const View, metrics: Buffer.Metrics) !void { + cursor.move_half_page_up(root, view, 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); +} + const private = @This(); // exports for unittests pub const test_internal = struct { diff --git a/src/tui/mode/vim.zig b/src/tui/mode/vim.zig index 38eacbe..d59b858 100644 --- a/src/tui/mode/vim.zig +++ b/src/tui/mode/vim.zig @@ -2,11 +2,19 @@ const std = @import("std"); 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; +const View = Buffer.View; + var commands: Commands = undefined; +const Self = Editor; pub fn init() !void { - var v: void = {}; - try commands.init(&v); + const mv = tui.mainview() orelse return; + const ed = mv.get_active_editor() orelse return; + try commands.init(ed); } pub fn deinit() void { @@ -14,74 +22,75 @@ pub fn deinit() void { } const Commands = command.Collection(cmds_); -const cmds_ = struct { - pub const Target = void; - const Ctx = command.Context; - const Meta = command.Metadata; - const Result = command.Result; +const Context = command.Context; +const Meta = command.Metadata; +const Result = command.Result; - pub fn w(_: *void, _: Ctx) Result { +const cmds_ = struct { + pub const Target = Self; + + pub fn w(_: *Self, _: Context) Result { try cmd("save_file", .{}); } pub const w_meta: Meta = .{ .description = "w (write file)" }; - pub fn q(_: *void, _: Ctx) Result { + pub fn q(_: *Self, _: Context) Result { try cmd("quit", .{}); } pub const q_meta: Meta = .{ .description = "q (quit)" }; - pub fn @"q!"(_: *void, _: Ctx) Result { + pub fn @"q!"(_: *Self, _: Context) Result { try cmd("quit_without_saving", .{}); } pub const @"q!_meta": Meta = .{ .description = "q! (quit without saving)" }; - pub fn @"qa!"(_: *void, _: Ctx) Result { + pub fn @"qa!"(_: *Self, _: Context) Result { try cmd("quit_without_saving", .{}); } pub const @"qa!_meta": Meta = .{ .description = "qa! (quit without saving anything)" }; - pub fn wq(_: *void, _: Ctx) Result { + pub fn wq(_: *Self, _: Context) Result { try cmd("save_file", command.fmt(.{ "then", .{ "quit", .{} } })); } pub const wq_meta: Meta = .{ .description = "wq (write file and quit)" }; - pub fn @"wq!"(_: *void, _: Ctx) Result { + pub fn @"wq!"(_: *Self, _: Context) Result { cmd("save_file", .{}) catch {}; try cmd("quit_without_saving", .{}); } pub const @"wq!_meta": Meta = .{ .description = "wq! (write file and quit without saving)" }; - pub fn @"e!"(_: *void, _: Ctx) Result { + pub fn @"e!"(_: *Self, _: Context) Result { try cmd("reload_file", .{}); } pub const @"e!_meta": Meta = .{ .description = "e! (force reload current file)" }; - pub fn bd(_: *void, _: Ctx) Result { + pub fn bd(_: *Self, _: Context) Result { try cmd("close_file", .{}); } pub const bd_meta: Meta = .{ .description = "bd (Close file)" }; - pub fn bw(_: *void, _: Ctx) Result { + pub fn bw(_: *Self, _: Context) Result { try cmd("delete_buffer", .{}); } pub const bw_meta: Meta = .{ .description = "bw (Delete buffer)" }; - pub fn bnext(_: *void, _: Ctx) Result { + pub fn bnext(_: *Self, _: Context) Result { try cmd("next_tab", .{}); } pub const bnext_meta: Meta = .{ .description = "bnext (Next buffer/tab)" }; - pub fn bprevious(_: *void, _: Ctx) Result { + pub fn bprevious(_: *Self, _: Context) Result { try cmd("next_tab", .{}); } pub const bprevious_meta: Meta = .{ .description = "bprevious (Previous buffer/tab)" }; - pub fn ls(_: *void, _: Ctx) Result { + pub fn ls(_: *Self, _: Context) Result { try cmd("switch_buffers", .{}); } pub const ls_meta: Meta = .{ .description = "ls (List/switch buffers)" }; - pub fn move_begin_or_add_integer_argument_zero(_: *void, _: Ctx) Result { + pub fn move_begin_or_add_integer_argument_zero(_: *Self, _: Context) Result { return if (@import("keybind").current_integer_argument()) |_| command.executeName("add_integer_argument_digit", command.fmt(.{0})) else @@ -89,7 +98,7 @@ const cmds_ = struct { } pub const move_begin_or_add_integer_argument_zero_meta: Meta = .{ .description = "Move cursor to beginning of line (vim)" }; - pub fn enter_mode_at_next_char(self: *void, ctx: Ctx) Result { + pub fn enter_mode_at_next_char(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -98,7 +107,7 @@ const cmds_ = struct { pub const enter_mode_at_next_char_meta: Meta = .{ .description = "Move forward one char and change mode" }; - pub fn enter_mode_on_newline_down(self: *void, ctx: Ctx) Result { + pub fn enter_mode_on_newline_down(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -107,7 +116,7 @@ const cmds_ = struct { pub const enter_mode_on_newline_down_meta: Meta = .{ .description = "Insert a newline and change mode" }; - pub fn enter_mode_on_newline_up(self: *void, ctx: Ctx) Result { + pub fn enter_mode_on_newline_up(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -115,7 +124,7 @@ const cmds_ = struct { } pub const enter_mode_on_newline_up_meta: Meta = .{ .description = "Insert a newline above the current line and change mode" }; - pub fn enter_mode_at_line_begin(self: *void, ctx: Ctx) Result { + pub fn enter_mode_at_line_begin(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -124,7 +133,7 @@ const cmds_ = struct { pub const enter_mode_at_line_begin_meta: Meta = .{ .description = "Goto line begin and change mode" }; - pub fn enter_mode_at_line_end(self: *void, ctx: Ctx) Result { + pub fn enter_mode_at_line_end(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -132,7 +141,7 @@ const cmds_ = struct { } pub const enter_mode_at_line_end_meta: Meta = .{ .description = "Goto line end and change mode" }; - pub fn copy_line(self: *void, ctx: Ctx) Result { + pub fn copy_line(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO @@ -140,4 +149,112 @@ const cmds_ = struct { } pub const copy_line_meta: Meta = .{ .description = "Copies the current line" }; + + pub fn move_scroll_half_page_up(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, &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_meta: Meta = .{ .description = "Move and scroll half a page up" }; + + 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(); + self.with_cursors_and_view_const(root, move_cursor_half_page_down, &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_meta: Meta = .{ .description = "Move and scroll half a page down" }; + + pub fn move_left_vim(self: *Self, ctx: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const_repeat(root, move_cursor_left_vim, ctx) catch {}; + self.clamp(); + } + pub const move_left_vim_meta: Meta = .{ .description = "Move cursor left (vim)", .arguments = &.{.integer} }; + + pub fn move_right_vim(self: *Self, ctx: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const_repeat(root, move_cursor_right_vim, ctx) catch {}; + self.clamp(); + } + pub const move_right_vim_meta: Meta = .{ .description = "Move cursor right (vim)", .arguments = &.{.integer} }; + + pub fn cut_to_end_vim(self: *Self, _: Context) Result { + const b = try self.buf_for_update(); + const root = try self.cut_to(move_cursor_end_vim, b.root); + try self.update_buf(root); + self.clamp(); + } + pub const cut_to_end_vim_meta: Meta = .{ .description = "Cut to end of line (vim)" }; + + pub fn move_up_vim(self: *Self, ctx: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const_repeat(root, move_cursor_up_vim, ctx) catch {}; + self.clamp(); + } + pub const move_up_vim_meta: Meta = .{ .description = "Move cursor up (vim)", .arguments = &.{.integer} }; + + pub fn move_down_vim(self: *Self, ctx: Context) Result { + const root = try self.buf_root(); + self.with_cursors_const_repeat(root, move_cursor_down_vim, ctx) catch {}; + self.clamp(); + } + pub const move_down_vim_meta: Meta = .{ .description = "Move cursor down (vim)", .arguments = &.{.integer} }; }; + +fn is_eol_right_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 - 1) + return true; + 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_half_page_up(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); + if (is_eol_vim(root, cursor, metrics)) try move_cursor_left_vim(root, cursor, 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_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_end_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) !void { + Editor.move_cursor_right_until(root, cursor, is_eol_vim, metrics); +} + +fn move_cursor_left_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { + Editor.move_cursor_left_unless(root, cursor, Editor.is_eol_left, metrics); +} + +fn move_cursor_right_vim(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { + Editor.move_cursor_right_unless(root, cursor, is_eol_right_vim, metrics); +}