const std = @import("std"); const command = @import("command"); const tp = @import("thespian"); 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 { const mv = tui.mainview() orelse return; const ed = mv.get_active_editor() orelse return; try commands.init(ed); } pub fn deinit() void { commands.deinit(); } const Commands = command.Collection(cmds_); const Context = command.Context; const Meta = command.Metadata; const Result = command.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(_: *Self, _: Context) Result { try cmd("quit", .{}); } pub const q_meta: Meta = .{ .description = "q (quit)" }; pub fn @"q!"(_: *Self, _: Context) Result { try cmd("quit_without_saving", .{}); } pub const @"q!_meta": Meta = .{ .description = "q! (quit without saving)" }; 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(_: *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!"(_: *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!"(_: *Self, _: Context) Result { try cmd("reload_file", .{}); } pub const @"e!_meta": Meta = .{ .description = "e! (force reload current file)" }; pub fn bd(_: *Self, _: Context) Result { try cmd("close_file", .{}); } pub const bd_meta: Meta = .{ .description = "bd (Close file)" }; pub fn bw(_: *Self, _: Context) Result { try cmd("delete_buffer", .{}); } pub const bw_meta: Meta = .{ .description = "bw (Delete buffer)" }; pub fn bnext(_: *Self, _: Context) Result { try cmd("next_tab", .{}); } pub const bnext_meta: Meta = .{ .description = "bnext (Next buffer/tab)" }; pub fn bprevious(_: *Self, _: Context) Result { try cmd("next_tab", .{}); } pub const bprevious_meta: Meta = .{ .description = "bprevious (Previous buffer/tab)" }; 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(_: *Self, _: Context) Result { return if (@import("keybind").current_integer_argument()) |_| command.executeName("add_integer_argument_digit", command.fmt(.{0})) else command.executeName("move_begin", .{}); } 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: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } 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: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } pub const enter_mode_on_newline_down_meta: Meta = .{ .description = "Insert a newline and change mode" }; pub fn enter_mode_on_newline_up(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } 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: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } pub const enter_mode_at_line_begin_meta: Meta = .{ .description = "Goto line begin and change mode" }; pub fn enter_mode_at_line_end(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } pub const enter_mode_at_line_end_meta: Meta = .{ .description = "Goto line end and change mode" }; pub fn copy_line(self: *Self, ctx: Context) Result { _ = self; // autofix _ = ctx; // autofix //TODO return undefined; } 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} }; pub fn goto_line_vim(self: *Self, ctx: Context) Result { try self.send_editor_jump_source(); var line: usize = 0; _ = ctx.args.match(.{tp.extract(&line)}) catch false; const root = self.buf_root() catch return; const primary = self.get_primary(); try primary.cursor.move_to(root, @intCast(if (line < 1) 0 else line - 1), primary.cursor.col, self.metrics); self.clamp(); try self.send_editor_jump_destination(); } pub const goto_line_vim_meta: Meta = .{ .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); }