Compare commits

...

9 commits

11 changed files with 400 additions and 244 deletions

View file

@ -150,13 +150,8 @@ pub fn execute(id: ID, ctx: Context) tp.result {
} }
pub fn get_id(name: []const u8) ?ID { pub fn get_id(name: []const u8) ?ID {
for (commands.items) |cmd| { var id: ?ID = null;
if (cmd) |p| return get_id_cache(name, &id);
if (std.mem.eql(u8, p.name, name))
return p.id;
}
tp.trace(tp.channel.debug, .{ "command", "get_id", "failed", name });
return null;
} }
pub fn get_name(id: ID) ?[]const u8 { pub fn get_name(id: ID) ?[]const u8 {
@ -178,6 +173,7 @@ pub fn get_id_cache(name: []const u8, id: *?ID) ?ID {
return p.id; return p.id;
}; };
} }
tp.trace(tp.channel.debug, .{ "command", "get_id_cache", "failed", name });
return null; return null;
} }

View file

@ -47,6 +47,17 @@
["ctrl+x ctrl+r", "open_recent"], ["ctrl+x ctrl+r", "open_recent"],
["ctrl+space", "enter_mode", "select"], ["ctrl+space", "enter_mode", "select"],
["alt+0", "add_integer_argument_digit", 0],
["alt+1", "add_integer_argument_digit", 1],
["alt+2", "add_integer_argument_digit", 2],
["alt+3", "add_integer_argument_digit", 3],
["alt+4", "add_integer_argument_digit", 4],
["alt+5", "add_integer_argument_digit", 5],
["alt+6", "add_integer_argument_digit", 6],
["alt+7", "add_integer_argument_digit", 7],
["alt+8", "add_integer_argument_digit", 8],
["alt+9", "add_integer_argument_digit", 9],
["ctrl+c l = =", "format"], ["ctrl+c l = =", "format"],
["ctrl+c l = r", "format"], ["ctrl+c l = r", "format"],
["ctrl+c l g g", "goto_definition"], ["ctrl+c l g g", "goto_definition"],

View file

@ -190,6 +190,17 @@
["«", "smart_insert_pair", "«", "»"], ["«", "smart_insert_pair", "«", "»"],
["»", "smart_insert_pair_close", "«", "»"], ["»", "smart_insert_pair_close", "«", "»"],
["alt+0", "add_integer_argument_digit", 0],
["alt+1", "add_integer_argument_digit", 1],
["alt+2", "add_integer_argument_digit", 2],
["alt+3", "add_integer_argument_digit", 3],
["alt+4", "add_integer_argument_digit", 4],
["alt+5", "add_integer_argument_digit", 5],
["alt+6", "add_integer_argument_digit", 6],
["alt+7", "add_integer_argument_digit", 7],
["alt+8", "add_integer_argument_digit", 8],
["alt+9", "add_integer_argument_digit", 9],
["left_control", "enable_jump_mode"], ["left_control", "enable_jump_mode"],
["right_control", "enable_jump_mode"], ["right_control", "enable_jump_mode"],
["left_alt", "enable_fast_scroll"], ["left_alt", "enable_fast_scroll"],

View file

@ -41,7 +41,6 @@
["n", "goto_next_match"], ["n", "goto_next_match"],
["N", "goto_prev_match"], ["N", "goto_prev_match"],
["0", "move_begin"],
["^", "smart_move_begin"], ["^", "smart_move_begin"],
["$", "move_end"], ["$", "move_end"],
[":", "open_command_palette"], [":", "open_command_palette"],
@ -90,7 +89,18 @@
["f", "move_to_char", "move_to_char_right"], ["f", "move_to_char", "move_to_char_right"],
["<C-CR>", ["move_down"], ["move_begin"]], ["<C-CR>", ["move_down"], ["move_begin"]],
["<CR>", ["move_down"], ["move_begin"]] ["<CR>", ["move_down"], ["move_begin"]],
["0", "move_begin_or_add_integer_argument_zero"],
["1", "add_integer_argument_digit", 1],
["2", "add_integer_argument_digit", 2],
["3", "add_integer_argument_digit", 3],
["4", "add_integer_argument_digit", 4],
["5", "add_integer_argument_digit", 5],
["6", "add_integer_argument_digit", 6],
["7", "add_integer_argument_digit", 7],
["8", "add_integer_argument_digit", 8],
["9", "add_integer_argument_digit", 9]
] ]
}, },
"visual": { "visual": {

View file

@ -25,6 +25,31 @@ const builtin_keybinds = std.static_string_map.StaticStringMap([]const u8).initC
.{ "emacs", @embedFile("builtin/emacs.json") }, .{ "emacs", @embedFile("builtin/emacs.json") },
}); });
var integer_argument: ?usize = null;
var commands: Commands = undefined;
const Commands = command.Collection(struct {
pub const Target = void;
const Ctx = command.Context;
const Meta = command.Metadata;
const Result = command.Result;
pub fn add_integer_argument_digit(_: *void, ctx: Ctx) Result {
var digit: usize = undefined;
if (!try ctx.args.match(.{tp.extract(&digit)}))
return error.InvalidIntegerParameterArgument;
if (digit > 9)
return error.InvalidIntegerParameterDigit;
integer_argument = if (integer_argument) |x| x * 10 + digit else digit;
}
pub const add_integer_argument_digit_meta: Meta = .{ .arguments = &.{.integer} };
});
pub fn init() !void {
var v: void = {};
try commands.init(&v);
}
pub fn mode(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode { pub fn mode(mode_name: []const u8, allocator: std.mem.Allocator, opts: anytype) !Mode {
return Handler.create(mode_name, allocator, opts) catch |e| switch (e) { return Handler.create(mode_name, allocator, opts) catch |e| switch (e) {
error.NotFound => return error.Stop, error.NotFound => return error.Stop,
@ -86,10 +111,18 @@ pub const Mode = struct {
selection_style: SelectionStyle, selection_style: SelectionStyle,
init_command: ?Command = null, init_command: ?Command = null,
deinit_command: ?Command = null, deinit_command: ?Command = null,
initialized: bool = false,
pub fn run_init(self: *Mode) void {
if (self.initialized) return;
self.initialized = true;
clear_integer_argument();
if (self.init_command) |init_command| init_command.execute_const();
}
pub fn deinit(self: *Mode) void { pub fn deinit(self: *Mode) void {
if (self.deinit_command) |deinit_| if (self.deinit_command) |deinit_command|
deinit_.execute_const(); deinit_command.execute_const();
self.allocator.free(self.mode); self.allocator.free(self.mode);
self.input_handler.deinit(); self.input_handler.deinit();
if (self.event_handler) |eh| eh.deinit(); if (self.event_handler) |eh| eh.deinit();
@ -144,11 +177,11 @@ fn get_or_load_namespace(namespace_name: []const u8) LoadError!*const Namespace
pub fn set_namespace(namespace_name: []const u8) LoadError!void { pub fn set_namespace(namespace_name: []const u8) LoadError!void {
const new_namespace = try get_or_load_namespace(namespace_name); const new_namespace = try get_or_load_namespace(namespace_name);
if (globals.current_namespace) |old_namespace| if (globals.current_namespace) |old_namespace|
if (old_namespace.deinit_command) |deinit| if (old_namespace.deinit_command) |deinit_command|
deinit.execute_const(); deinit_command.execute_const();
globals.current_namespace = new_namespace; globals.current_namespace = new_namespace;
if (new_namespace.init_command) |init| if (new_namespace.init_command) |init_command|
init.execute_const(); init_command.execute_const();
} }
fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadError!*const BindingSet { fn get_mode_binding_set(mode_name: []const u8, insert_command: []const u8) LoadError!*const BindingSet {
@ -262,10 +295,17 @@ const Command = struct {
}; };
var buf: [2048]u8 = undefined; var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args); @memcpy(buf[0..self.args.len], self.args);
if (integer_argument) |int_arg| {
if (cbor.match(self.args, .{}) catch false and has_integer_argument(id)) {
integer_argument = null;
try command.execute(id, command.fmt(.{int_arg}));
return;
}
}
try command.execute(id, .{ .args = .{ .buf = buf[0..self.args.len] } }); try command.execute(id, .{ .args = .{ .buf = buf[0..self.args.len] } });
} }
pub fn execute_const(self: *const @This()) void { fn execute_const(self: *const @This()) void {
var buf: [2048]u8 = undefined; var buf: [2048]u8 = undefined;
@memcpy(buf[0..self.args.len], self.args); @memcpy(buf[0..self.args.len], self.args);
command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e| { command.executeName(self.command, .{ .args = .{ .buf = buf[0..self.args.len] } }) catch |e| {
@ -275,6 +315,11 @@ const Command = struct {
}; };
} }
fn has_integer_argument(id: command.ID) bool {
const args = command.get_arguments(id) orelse return false;
return args.len == 1 and args[0] == .integer;
}
fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!Command { fn load(allocator: std.mem.Allocator, tokens: []const std.json.Value) (parse_flow.ParseError || parse_vim.ParseError)!Command {
if (tokens.len == 0) return error.InvalidFormat; if (tokens.len == 0) return error.InvalidFormat;
var state: enum { command, args } = .command; var state: enum { command, args } = .command;
@ -724,6 +769,14 @@ pub fn current_key_event_sequence_fmt() KeyEventSequenceFmt {
return .{ .key_events = globals.current_sequence.items }; return .{ .key_events = globals.current_sequence.items };
} }
pub fn current_integer_argument() ?usize {
return integer_argument;
}
pub fn clear_integer_argument() void {
integer_argument = null;
}
const expectEqual = std.testing.expectEqual; const expectEqual = std.testing.expectEqual;
const parse_test_cases = .{ const parse_test_cases = .{

View file

@ -2,6 +2,8 @@ const vaxis = @import("vaxis");
const meta = @import("std").meta; const meta = @import("std").meta;
const utf8Encode = @import("std").unicode.utf8Encode; const utf8Encode = @import("std").unicode.utf8Encode;
const utf8Decode = @import("std").unicode.utf8Decode;
const utf8ValidateSlice = @import("std").unicode.utf8ValidateSlice;
const FormatOptions = @import("std").fmt.FormatOptions; const FormatOptions = @import("std").fmt.FormatOptions;
pub const key = vaxis.Key; pub const key = vaxis.Key;
@ -74,6 +76,7 @@ pub const KeyEvent = struct {
} }
pub fn eql_unshifted(self: @This(), other: @This()) bool { pub fn eql_unshifted(self: @This(), other: @This()) bool {
if (self.text.len > 0 or other.text.len > 0) return false;
const self_mods = self.mods_no_caps(); const self_mods = self.mods_no_caps();
const other_mods = other.mods_no_caps(); const other_mods = other.mods_no_caps();
return self.key_unshifted == other.key_unshifted and self_mods == other_mods; return self.key_unshifted == other.key_unshifted and self_mods == other_mods;
@ -117,7 +120,7 @@ pub const KeyEvent = struct {
pub fn from_message( pub fn from_message(
event_: Event, event_: Event,
keypress_: Key, keypress_: Key,
keypress_shifted: Key, keypress_shifted_: Key,
text: []const u8, text: []const u8,
modifiers: Mods, modifiers: Mods,
) @This() { ) @This() {
@ -128,6 +131,11 @@ pub const KeyEvent = struct {
key.left_alt, key.right_alt => modifiers & ~mod.alt, key.left_alt, key.right_alt => modifiers & ~mod.alt,
else => modifiers, else => modifiers,
}; };
var keypress_shifted: Key = keypress_shifted_;
if (text.len > 0 and text.len < 5 and utf8ValidateSlice(text)) blk: {
keypress_shifted = utf8Decode(text) catch break :blk;
}
const keypress, const mods = if (keypress_shifted == keypress_) const keypress, const mods = if (keypress_shifted == keypress_)
map_key_to_unshifed_legacy(keypress_shifted, mods_) map_key_to_unshifed_legacy(keypress_shifted, mods_)
else else

View file

@ -1743,7 +1743,7 @@ pub const Editor = struct {
try move(root, &cursel.cursor, metrics); try move(root, &cursel.cursor, metrics);
} }
fn with_cursors_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { fn with_cursors_const_once(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void {
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.disable_selection(root, self.metrics); cursel.disable_selection(root, self.metrics);
try with_cursor_const(root, move, cursel, self.metrics); try with_cursor_const(root, move, cursel, self.metrics);
@ -1751,6 +1751,18 @@ pub const Editor = struct {
self.collapse_cursors(); self.collapse_cursors();
} }
fn with_cursors_const_repeat(self: *Self, root: Buffer.Root, move: cursor_operator_const, ctx: Context) error{Stop}!void {
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
cursel.disable_selection(root, self.metrics);
try with_cursor_const(root, move, cursel, self.metrics);
};
self.collapse_cursors();
}
}
fn with_cursor_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void { fn with_cursor_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void {
try move(root, &cursel.cursor, ctx, metrics); try move(root, &cursel.cursor, ctx, metrics);
} }
@ -1788,7 +1800,7 @@ pub const Editor = struct {
cursel.check_selection(root, metrics); cursel.check_selection(root, metrics);
} }
pub fn with_selections_const(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void { pub fn with_selections_const_once(self: *Self, root: Buffer.Root, move: cursor_operator_const) error{Stop}!void {
var someone_stopped = false; var someone_stopped = false;
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel|
with_selection_const(root, move, cursel, self.metrics) catch { with_selection_const(root, move, cursel, self.metrics) catch {
@ -1798,6 +1810,21 @@ pub const Editor = struct {
return if (someone_stopped) error.Stop else {}; return if (someone_stopped) error.Stop else {};
} }
pub fn with_selections_const_repeat(self: *Self, root: Buffer.Root, move: cursor_operator_const, ctx: Context) error{Stop}!void {
var someone_stopped = false;
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel|
with_selection_const(root, move, cursel, self.metrics) catch {
someone_stopped = true;
};
self.collapse_cursors();
if (someone_stopped) break;
}
return if (someone_stopped) error.Stop else {};
}
fn with_selection_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void { fn with_selection_const_arg(root: Buffer.Root, move: cursor_operator_const_arg, cursel: *CurSel, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void {
const sel = try cursel.enable_selection(root, metrics); const sel = try cursel.enable_selection(root, metrics);
try move(root, &sel.end, ctx, metrics); try move(root, &sel.end, ctx, metrics);
@ -1831,28 +1858,11 @@ pub const Editor = struct {
return if (someone_stopped) error.Stop else {}; return if (someone_stopped) error.Stop else {};
} }
fn with_cursel(root: Buffer.Root, op: cursel_operator, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
return op(root, cursel, allocator);
}
fn with_cursels(self: *Self, root_: Buffer.Root, move: cursel_operator, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_;
var someone_stopped = false;
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
root = with_cursel(root, move, cursel, allocator) catch ret: {
someone_stopped = true;
break :ret root;
};
};
self.collapse_cursors();
return if (someone_stopped) error.Stop else root;
}
fn with_cursel_mut(self: *Self, root: Buffer.Root, op: cursel_operator_mut, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { fn with_cursel_mut(self: *Self, root: Buffer.Root, op: cursel_operator_mut, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
return op(self, root, cursel, allocator); return op(self, root, cursel, allocator);
} }
fn with_cursels_mut(self: *Self, root_: Buffer.Root, move: cursel_operator_mut, allocator: Allocator) error{Stop}!Buffer.Root { fn with_cursels_mut_once(self: *Self, root_: Buffer.Root, move: cursel_operator_mut, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_; var root = root_;
var someone_stopped = false; var someone_stopped = false;
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
@ -1865,6 +1875,24 @@ pub const Editor = struct {
return if (someone_stopped) error.Stop else root; return if (someone_stopped) error.Stop else root;
} }
fn with_cursels_mut_repeat(self: *Self, root_: Buffer.Root, move: cursel_operator_mut, allocator: Allocator, ctx: Context) error{Stop}!Buffer.Root {
var root = root_;
var someone_stopped = false;
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
root = self.with_cursel_mut(root, move, cursel, allocator) catch ret: {
someone_stopped = true;
break :ret root;
};
};
self.collapse_cursors();
if (someone_stopped) break;
}
return if (someone_stopped) error.Stop else root;
}
fn with_cursel_const(root: Buffer.Root, op: cursel_operator_const, cursel: *CurSel) error{Stop}!void { fn with_cursel_const(root: Buffer.Root, op: cursel_operator_const, cursel: *CurSel) error{Stop}!void {
return op(root, cursel); return op(root, cursel);
} }
@ -2889,9 +2917,9 @@ pub const Editor = struct {
} }
pub const cut_to_end_vim_meta: Meta = .{ .description = "Cut to end of line (vim)" }; pub const cut_to_end_vim_meta: Meta = .{ .description = "Cut to end of line (vim)" };
pub fn join_next_line(self: *Self, _: Context) Result { pub fn join_next_line(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
try self.with_cursors_const(b.root, move_cursor_end); try self.with_cursors_const_repeat(b.root, move_cursor_end, ctx);
var root = try self.delete_to(move_cursor_right_until_non_whitespace, b.root, b.allocator); var root = try self.delete_to(move_cursor_right_until_non_whitespace, b.root, b.allocator);
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
root = try self.insert(root, cursel, " ", b.allocator); root = try self.insert(root, cursel, " ", b.allocator);
@ -2899,21 +2927,21 @@ pub const Editor = struct {
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
pub const join_next_line_meta: Meta = .{ .description = "Join next line" }; pub const join_next_line_meta: Meta = .{ .description = "Join next line", .arguments = &.{.integer} };
pub fn move_left(self: *Self, _: Context) Result { pub fn move_left(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_left) catch {}; self.with_cursors_const_repeat(root, move_cursor_left, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_left_meta: Meta = .{ .description = "Move cursor left" }; pub const move_left_meta: Meta = .{ .description = "Move cursor left", .arguments = &.{.integer} };
pub fn move_right(self: *Self, _: Context) Result { pub fn move_right(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_right) catch {}; self.with_cursors_const_repeat(root, move_cursor_right, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_right_meta: Meta = .{ .description = "Move cursor right" }; 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 { 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); move_cursor_left_unless(root, cursor, is_eol_left, metrics);
@ -2923,19 +2951,19 @@ pub const Editor = struct {
move_cursor_right_unless(root, cursor, is_eol_right_vim, metrics); move_cursor_right_unless(root, cursor, is_eol_right_vim, metrics);
} }
pub fn move_left_vim(self: *Self, _: Context) Result { pub fn move_left_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_left_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_left_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_left_vim_meta: Meta = .{ .description = "Move cursor left (vim)" }; pub const move_left_vim_meta: Meta = .{ .description = "Move cursor left (vim)", .arguments = &.{.integer} };
pub fn move_right_vim(self: *Self, _: Context) Result { pub fn move_right_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_right_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_right_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_right_vim_meta: Meta = .{ .description = "Move cursor right (vim)" }; 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 { 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)) { if (is_non_word_char_at_cursor(root, cursor, metrics)) {
@ -3010,40 +3038,40 @@ pub const Editor = struct {
move_cursor_right_until(root, cursor, is_non_whitespace_at_cursor, metrics); move_cursor_right_until(root, cursor, is_non_whitespace_at_cursor, metrics);
} }
pub fn move_word_left(self: *Self, _: Context) Result { pub fn move_word_left(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_word_left) catch {}; self.with_cursors_const_repeat(root, move_cursor_word_left, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_word_left_meta: Meta = .{ .description = "Move cursor left by word" }; pub const move_word_left_meta: Meta = .{ .description = "Move cursor left by word", .arguments = &.{.integer} };
pub fn move_word_left_vim(self: *Self, _: Context) Result { pub fn move_word_left_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_word_left_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_word_left_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_word_left_vim_meta: Meta = .{ .description = "Move cursor left by word (vim)" }; pub const move_word_left_vim_meta: Meta = .{ .description = "Move cursor left by word (vim)", .arguments = &.{.integer} };
pub fn move_word_right(self: *Self, _: Context) Result { pub fn move_word_right(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_word_right) catch {}; self.with_cursors_const_repeat(root, move_cursor_word_right, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_word_right_meta: Meta = .{ .description = "Move cursor right by word" }; pub const move_word_right_meta: Meta = .{ .description = "Move cursor right by word", .arguments = &.{.integer} };
pub fn move_word_right_vim(self: *Self, _: Context) Result { pub fn move_word_right_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_word_right_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_word_right_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_word_right_vim_meta: Meta = .{ .description = "Move cursor right by word (vim)" }; pub const move_word_right_vim_meta: Meta = .{ .description = "Move cursor right by word (vim)", .arguments = &.{.integer} };
pub fn move_word_right_end_vim(self: *Self, _: Context) Result { pub fn move_word_right_end_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_word_right_end_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_word_right_end_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_word_right_end_vim_meta: Meta = .{ .description = "Move cursor right by end of word (vim)" }; pub const move_word_right_end_vim_meta: Meta = .{ .description = "Move cursor right by end of word (vim)", .arguments = &.{.integer} };
fn move_cursor_to_char_left(root: Buffer.Root, cursor: *Cursor, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void { fn move_cursor_to_char_left(root: Buffer.Root, cursor: *Cursor, ctx: Context, metrics: Buffer.Metrics) error{Stop}!void {
var egc: []const u8 = undefined; var egc: []const u8 = undefined;
@ -3101,58 +3129,69 @@ pub const Editor = struct {
} }
pub const move_or_select_to_char_right_meta: Meta = .{ .arguments = &.{.integer} }; pub const move_or_select_to_char_right_meta: Meta = .{ .arguments = &.{.integer} };
pub fn move_up(self: *Self, _: Context) Result { pub fn move_up(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_up) catch {}; self.with_cursors_const_repeat(root, move_cursor_up, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_up_meta: Meta = .{ .description = "Move cursor up" }; pub const move_up_meta: Meta = .{ .description = "Move cursor up", .arguments = &.{.integer} };
pub fn move_up_vim(self: *Self, _: Context) Result { pub fn move_up_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_up_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_up_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_up_vim_meta: Meta = .{ .description = "Move cursor up (vim)" }; pub const move_up_vim_meta: Meta = .{ .description = "Move cursor up (vim)", .arguments = &.{.integer} };
pub fn add_cursor_up(self: *Self, _: Context) Result { pub fn add_cursor_up(self: *Self, ctx: Context) Result {
const root = try self.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
try self.push_cursor(); try self.push_cursor();
const primary = self.get_primary(); const primary = self.get_primary();
const root = try self.buf_root();
move_cursor_up(root, &primary.cursor, self.metrics) catch {}; move_cursor_up(root, &primary.cursor, self.metrics) catch {};
}
self.clamp(); self.clamp();
} }
pub const add_cursor_up_meta: Meta = .{ .description = "Add cursor up" }; pub const add_cursor_up_meta: Meta = .{ .description = "Add cursor up", .arguments = &.{.integer} };
pub fn move_down(self: *Self, _: Context) Result { pub fn move_down(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_down) catch {}; self.with_cursors_const_repeat(root, move_cursor_down, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_down_meta: Meta = .{ .description = "Move cursor down" }; pub const move_down_meta: Meta = .{ .description = "Move cursor down", .arguments = &.{.integer} };
pub fn move_down_vim(self: *Self, _: Context) Result { pub fn move_down_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_down_vim) catch {}; self.with_cursors_const_repeat(root, move_cursor_down_vim, ctx) catch {};
self.clamp(); self.clamp();
} }
pub const move_down_vim_meta: Meta = .{ .description = "Move cursor down (vim)" }; pub const move_down_vim_meta: Meta = .{ .description = "Move cursor down (vim)", .arguments = &.{.integer} };
pub fn add_cursor_down(self: *Self, _: Context) Result { pub fn add_cursor_down(self: *Self, ctx: Context) Result {
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
try self.push_cursor(); try self.push_cursor();
const primary = self.get_primary(); const primary = self.get_primary();
const root = try self.buf_root(); const root = try self.buf_root();
move_cursor_down(root, &primary.cursor, self.metrics) catch {}; move_cursor_down(root, &primary.cursor, self.metrics) catch {};
}
self.clamp(); self.clamp();
} }
pub const add_cursor_down_meta: Meta = .{ .description = "Add cursor down" }; pub const add_cursor_down_meta: Meta = .{ .description = "Add cursor down", .arguments = &.{.integer} };
pub fn add_cursor_next_match(self: *Self, _: Context) Result { pub fn add_cursor_next_match(self: *Self, ctx: Context) Result {
try self.send_editor_jump_source(); try self.send_editor_jump_source();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
if (self.matches.items.len == 0) { if (self.matches.items.len == 0) {
const root = self.buf_root() catch return; const root = self.buf_root() catch return;
self.with_cursors_const(root, move_cursor_word_begin) catch {}; self.with_cursors_const_once(root, move_cursor_word_begin) catch {};
try self.with_selections_const(root, move_cursor_word_end); try self.with_selections_const_once(root, move_cursor_word_end);
} else if (self.get_next_match(self.get_primary().cursor)) |match| { } else if (self.get_next_match(self.get_primary().cursor)) |match| {
try self.push_cursor(); try self.push_cursor();
const primary = self.get_primary(); const primary = self.get_primary();
@ -3161,10 +3200,11 @@ pub const Editor = struct {
match.has_selection = true; match.has_selection = true;
primary.cursor.move_to(root, match.end.row, match.end.col, self.metrics) catch return; primary.cursor.move_to(root, match.end.row, match.end.col, self.metrics) catch return;
} }
}
self.clamp(); self.clamp();
try self.send_editor_jump_destination(); try self.send_editor_jump_destination();
} }
pub const add_cursor_next_match_meta: Meta = .{ .description = "Add cursor at next highlighted match" }; pub const add_cursor_next_match_meta: Meta = .{ .description = "Add cursor at next highlighted match", .arguments = &.{.integer} };
pub fn add_cursor_all_matches(self: *Self, _: Context) Result { pub fn add_cursor_all_matches(self: *Self, _: Context) Result {
if (self.matches.items.len == 0) return; if (self.matches.items.len == 0) return;
@ -3229,13 +3269,13 @@ pub const Editor = struct {
return root; return root;
} }
pub fn pull_up(self: *Self, _: Context) Result { pub fn pull_up(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, pull_cursel_up, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, pull_cursel_up, b.allocator, ctx);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
pub const pull_up_meta: Meta = .{ .description = "Pull line up" }; pub const pull_up_meta: Meta = .{ .description = "Pull line up", .arguments = &.{.integer} };
fn pull_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { fn pull_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_; var root = root_;
@ -3256,13 +3296,13 @@ pub const Editor = struct {
return root; return root;
} }
pub fn pull_down(self: *Self, _: Context) Result { pub fn pull_down(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, pull_cursel_down, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, pull_cursel_down, b.allocator, ctx);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
pub const pull_down_meta: Meta = .{ .description = "Pull line down" }; pub const pull_down_meta: Meta = .{ .description = "Pull line down", .arguments = &.{.integer} };
fn dupe_cursel_up(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { fn dupe_cursel_up(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_; var root = root_;
@ -3278,13 +3318,13 @@ pub const Editor = struct {
return root; return root;
} }
pub fn dupe_up(self: *Self, _: Context) Result { pub fn dupe_up(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, dupe_cursel_up, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, dupe_cursel_up, b.allocator, ctx);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
pub const dupe_up_meta: Meta = .{ .description = "Duplicate line or selection up/backwards" }; pub const dupe_up_meta: Meta = .{ .description = "Duplicate line or selection up/backwards", .arguments = &.{.integer} };
fn dupe_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { fn dupe_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_; var root = root_;
@ -3299,13 +3339,13 @@ pub const Editor = struct {
return root; return root;
} }
pub fn dupe_down(self: *Self, _: Context) Result { pub fn dupe_down(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, dupe_cursel_down, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, dupe_cursel_down, b.allocator, ctx);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
pub const dupe_down_meta: Meta = .{ .description = "Duplicate line or selection down/forwards" }; pub const dupe_down_meta: Meta = .{ .description = "Duplicate line or selection down/forwards", .arguments = &.{.integer} };
fn toggle_cursel_prefix(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { fn toggle_cursel_prefix(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_; var root = root_;
@ -3330,7 +3370,7 @@ pub const Editor = struct {
@memcpy(self.prefix_buf[0..prefix.len], prefix); @memcpy(self.prefix_buf[0..prefix.len], prefix);
self.prefix = self.prefix_buf[0..prefix.len]; self.prefix = self.prefix_buf[0..prefix.len];
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, toggle_cursel_prefix, b.allocator); const root = try self.with_cursels_mut_once(b.root, toggle_cursel_prefix, b.allocator);
try self.update_buf(root); try self.update_buf(root);
} }
pub const toggle_prefix_meta: Meta = .{ .arguments = &.{.string} }; pub const toggle_prefix_meta: Meta = .{ .arguments = &.{.string} };
@ -3366,12 +3406,12 @@ pub const Editor = struct {
} else return try self.indent_cursor(root_, cursel.cursor, allocator); } else return try self.indent_cursor(root_, cursel.cursor, allocator);
} }
pub fn indent(self: *Self, _: Context) Result { pub fn indent(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, indent_cursel, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, indent_cursel, b.allocator, ctx);
try self.update_buf(root); try self.update_buf(root);
} }
pub const indent_meta: Meta = .{ .description = "Indent current line" }; pub const indent_meta: Meta = .{ .description = "Indent current line", .arguments = &.{.integer} };
fn unindent_cursor(self: *Self, root: Buffer.Root, cursor: *Cursor, cursor_protect: ?*Cursor, allocator: Allocator) error{Stop}!Buffer.Root { fn unindent_cursor(self: *Self, root: Buffer.Root, cursor: *Cursor, cursor_protect: ?*Cursor, allocator: Allocator) error{Stop}!Buffer.Root {
var newroot = root; var newroot = root;
@ -3415,32 +3455,32 @@ pub const Editor = struct {
self.cursels = self.cursels_saved.clone() catch return; self.cursels = self.cursels_saved.clone() catch return;
} }
pub fn unindent(self: *Self, _: Context) Result { pub fn unindent(self: *Self, ctx: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
errdefer self.restore_cursels(); errdefer self.restore_cursels();
const previous_len = self.cursels.items.len; const previous_len = self.cursels.items.len;
const root = try self.with_cursels_mut(b.root, unindent_cursel, b.allocator); const root = try self.with_cursels_mut_repeat(b.root, unindent_cursel, b.allocator, ctx);
if (self.cursels.items.len != previous_len) if (self.cursels.items.len != previous_len)
self.restore_cursels(); self.restore_cursels();
try self.update_buf(root); try self.update_buf(root);
} }
pub const unindent_meta: Meta = .{ .description = "Unindent current line" }; pub const unindent_meta: Meta = .{ .description = "Unindent current line", .arguments = &.{.integer} };
pub fn move_scroll_up(self: *Self, _: Context) Result { pub fn move_scroll_up(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_up) catch {}; self.with_cursors_const_repeat(root, move_cursor_up, ctx) catch {};
self.view.move_up() catch {}; self.view.move_up() catch {};
self.clamp(); self.clamp();
} }
pub const move_scroll_up_meta: Meta = .{ .description = "Move and scroll up" }; pub const move_scroll_up_meta: Meta = .{ .description = "Move and scroll up", .arguments = &.{.integer} };
pub fn move_scroll_down(self: *Self, _: Context) Result { pub fn move_scroll_down(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_cursors_const(root, move_cursor_down) catch {}; self.with_cursors_const_repeat(root, move_cursor_down, ctx) catch {};
self.view.move_down(root) catch {}; self.view.move_down(root) catch {};
self.clamp(); self.clamp();
} }
pub const move_scroll_down_meta: Meta = .{ .description = "Move and scroll down" }; pub const move_scroll_down_meta: Meta = .{ .description = "Move and scroll down", .arguments = &.{.integer} };
pub fn move_scroll_left(self: *Self, _: Context) Result { pub fn move_scroll_left(self: *Self, _: Context) Result {
self.view.move_left() catch {}; self.view.move_left() catch {};
@ -3526,21 +3566,21 @@ pub const Editor = struct {
pub fn smart_move_begin(self: *Self, _: Context) Result { pub fn smart_move_begin(self: *Self, _: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_cursors_const(root, smart_move_cursor_begin); try self.with_cursors_const_once(root, smart_move_cursor_begin);
self.clamp(); self.clamp();
} }
pub const smart_move_begin_meta: Meta = .{ .description = "Move cursor to beginning of line (smart)" }; pub const smart_move_begin_meta: Meta = .{ .description = "Move cursor to beginning of line (smart)" };
pub fn move_begin(self: *Self, _: Context) Result { pub fn move_begin(self: *Self, _: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_cursors_const(root, move_cursor_begin); try self.with_cursors_const_once(root, move_cursor_begin);
self.clamp(); self.clamp();
} }
pub const move_begin_meta: Meta = .{ .description = "Move cursor to beginning of line" }; pub const move_begin_meta: Meta = .{ .description = "Move cursor to beginning of line" };
pub fn move_end(self: *Self, _: Context) Result { pub fn move_end(self: *Self, _: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_cursors_const(root, move_cursor_end); try self.with_cursors_const_once(root, move_cursor_end);
self.clamp(); self.clamp();
} }
pub const move_end_meta: Meta = .{ .description = "Move cursor to end of line" }; pub const move_end_meta: Meta = .{ .description = "Move cursor to end of line" };
@ -3601,6 +3641,7 @@ pub const Editor = struct {
pub fn cancel(self: *Self, _: Context) Result { pub fn cancel(self: *Self, _: Context) Result {
self.cancel_all_selections(); self.cancel_all_selections();
self.cancel_all_matches(); self.cancel_all_matches();
@import("keybind").clear_integer_argument();
} }
pub const cancel_meta: Meta = .{ .description = "Cancel current action" }; pub const cancel_meta: Meta = .{ .description = "Cancel current action" };
@ -3620,98 +3661,98 @@ pub const Editor = struct {
} }
pub const select_line_vim_meta: Meta = .{ .description = "Select the line around the cursor (vim)" }; pub const select_line_vim_meta: Meta = .{ .description = "Select the line around the cursor (vim)" };
pub fn select_up(self: *Self, _: Context) Result { pub fn select_up(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_up); try self.with_selections_const_repeat(root, move_cursor_up, ctx);
self.clamp(); self.clamp();
} }
pub const select_up_meta: Meta = .{ .description = "Select up" }; pub const select_up_meta: Meta = .{ .description = "Select up", .arguments = &.{.integer} };
pub fn select_down(self: *Self, _: Context) Result { pub fn select_down(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_down); try self.with_selections_const_repeat(root, move_cursor_down, ctx);
self.clamp(); self.clamp();
} }
pub const select_down_meta: Meta = .{ .description = "Select down" }; pub const select_down_meta: Meta = .{ .description = "Select down", .arguments = &.{.integer} };
pub fn select_scroll_up(self: *Self, _: Context) Result { pub fn select_scroll_up(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_up); try self.with_selections_const_repeat(root, move_cursor_up, ctx);
self.view.move_up() catch {}; self.view.move_up() catch {};
self.clamp(); self.clamp();
} }
pub const select_scroll_up_meta: Meta = .{ .description = "Select and scroll up" }; pub const select_scroll_up_meta: Meta = .{ .description = "Select and scroll up", .arguments = &.{.integer} };
pub fn select_scroll_down(self: *Self, _: Context) Result { pub fn select_scroll_down(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_down); try self.with_selections_const_repeat(root, move_cursor_down, ctx);
self.view.move_down(root) catch {}; self.view.move_down(root) catch {};
self.clamp(); self.clamp();
} }
pub const select_scroll_down_meta: Meta = .{ .description = "Select and scroll down" }; pub const select_scroll_down_meta: Meta = .{ .description = "Select and scroll down", .arguments = &.{.integer} };
pub fn select_left(self: *Self, _: Context) Result { pub fn select_left(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_left); try self.with_selections_const_repeat(root, move_cursor_left, ctx);
self.clamp(); self.clamp();
} }
pub const select_left_meta: Meta = .{ .description = "Select left" }; pub const select_left_meta: Meta = .{ .description = "Select left", .arguments = &.{.integer} };
pub fn select_right(self: *Self, _: Context) Result { pub fn select_right(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_right); try self.with_selections_const_repeat(root, move_cursor_right, ctx);
self.clamp(); self.clamp();
} }
pub const select_right_meta: Meta = .{ .description = "Select right" }; pub const select_right_meta: Meta = .{ .description = "Select right", .arguments = &.{.integer} };
pub fn select_word_left(self: *Self, _: Context) Result { pub fn select_word_left(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_left); try self.with_selections_const_repeat(root, move_cursor_word_left, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_left_meta: Meta = .{ .description = "Select left by word" }; pub const select_word_left_meta: Meta = .{ .description = "Select left by word", .arguments = &.{.integer} };
pub fn select_word_left_vim(self: *Self, _: Context) Result { pub fn select_word_left_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_left_vim); try self.with_selections_const_repeat(root, move_cursor_word_left_vim, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_left_vim_meta: Meta = .{ .description = "Select left by word (vim)" }; pub const select_word_left_vim_meta: Meta = .{ .description = "Select left by word (vim)", .arguments = &.{.integer} };
pub fn select_word_right(self: *Self, _: Context) Result { pub fn select_word_right(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_right); try self.with_selections_const_repeat(root, move_cursor_word_right, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_right_meta: Meta = .{ .description = "Select right by word" }; pub const select_word_right_meta: Meta = .{ .description = "Select right by word", .arguments = &.{.integer} };
pub fn select_word_right_vim(self: *Self, _: Context) Result { pub fn select_word_right_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_right_vim); try self.with_selections_const_repeat(root, move_cursor_word_right_vim, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_right_vim_meta: Meta = .{ .description = "Select right by word (vim)" }; pub const select_word_right_vim_meta: Meta = .{ .description = "Select right by word (vim)", .arguments = &.{.integer} };
pub fn select_word_right_end_vim(self: *Self, _: Context) Result { pub fn select_word_right_end_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_right_end_vim); try self.with_selections_const_repeat(root, move_cursor_word_right_end_vim, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_right_end_vim_meta: Meta = .{ .description = "Select right by end of word (vim)" }; pub const select_word_right_end_vim_meta: Meta = .{ .description = "Select right by end of word (vim)", .arguments = &.{.integer} };
pub fn select_word_begin(self: *Self, _: Context) Result { pub fn select_word_begin(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_begin); try self.with_selections_const_repeat(root, move_cursor_word_begin, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_begin_meta: Meta = .{ .description = "Select to beginning of word" }; pub const select_word_begin_meta: Meta = .{ .description = "Select to beginning of word", .arguments = &.{.integer} };
pub fn select_word_end(self: *Self, _: Context) Result { pub fn select_word_end(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_word_end); try self.with_selections_const_repeat(root, move_cursor_word_end, ctx);
self.clamp(); self.clamp();
} }
pub const select_word_end_meta: Meta = .{ .description = "Select to end of word" }; pub const select_word_end_meta: Meta = .{ .description = "Select to end of word", .arguments = &.{.integer} };
pub fn select_to_char_left(self: *Self, ctx: Context) Result { pub fn select_to_char_left(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
@ -3737,44 +3778,44 @@ pub const Editor = struct {
} }
pub const select_to_char_right_meta: Meta = .{ .arguments = &.{.integer} }; pub const select_to_char_right_meta: Meta = .{ .arguments = &.{.integer} };
pub fn select_begin(self: *Self, _: Context) Result { pub fn select_begin(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_begin); try self.with_selections_const_repeat(root, move_cursor_begin, ctx);
self.clamp(); self.clamp();
} }
pub const select_begin_meta: Meta = .{ .description = "Select to beginning of line" }; pub const select_begin_meta: Meta = .{ .description = "Select to beginning of line", .arguments = &.{.integer} };
pub fn smart_select_begin(self: *Self, _: Context) Result { pub fn smart_select_begin(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, smart_move_cursor_begin); try self.with_selections_const_repeat(root, smart_move_cursor_begin, ctx);
self.clamp(); self.clamp();
} }
pub const smart_select_begin_meta: Meta = .{ .description = "Select to beginning of line (smart)" }; pub const smart_select_begin_meta: Meta = .{ .description = "Select to beginning of line (smart)", .arguments = &.{.integer} };
pub fn select_end(self: *Self, _: Context) Result { pub fn select_end(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_end); try self.with_selections_const_repeat(root, move_cursor_end, ctx);
self.clamp(); self.clamp();
} }
pub const select_end_meta: Meta = .{ .description = "Select to end of line" }; pub const select_end_meta: Meta = .{ .description = "Select to end of line", .arguments = &.{.integer} };
pub fn select_buffer_begin(self: *Self, _: Context) Result { pub fn select_buffer_begin(self: *Self, ctx: Context) Result {
try self.send_editor_jump_source(); try self.send_editor_jump_source();
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_buffer_begin); try self.with_selections_const_repeat(root, move_cursor_buffer_begin, ctx);
self.clamp(); self.clamp();
try self.send_editor_jump_destination(); try self.send_editor_jump_destination();
} }
pub const select_buffer_begin_meta: Meta = .{ .description = "Select to start of file" }; pub const select_buffer_begin_meta: Meta = .{ .description = "Select to start of file", .arguments = &.{.integer} };
pub fn select_buffer_end(self: *Self, _: Context) Result { pub fn select_buffer_end(self: *Self, ctx: Context) Result {
try self.send_editor_jump_source(); try self.send_editor_jump_source();
const root = try self.buf_root(); const root = try self.buf_root();
try self.with_selections_const(root, move_cursor_buffer_end); try self.with_selections_const_repeat(root, move_cursor_buffer_end, ctx);
self.clamp(); self.clamp();
try self.send_editor_jump_destination(); try self.send_editor_jump_destination();
} }
pub const select_buffer_end_meta: Meta = .{ .description = "Select to end of file" }; pub const select_buffer_end_meta: Meta = .{ .description = "Select to end of file", .arguments = &.{.integer} };
pub fn select_page_up(self: *Self, _: Context) Result { pub fn select_page_up(self: *Self, _: Context) Result {
try self.send_editor_jump_source(); try self.send_editor_jump_source();
@ -5369,7 +5410,7 @@ pub const Editor = struct {
pub fn to_upper(self: *Self, _: Context) Result { pub fn to_upper(self: *Self, _: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, to_upper_cursel, b.allocator); const root = try self.with_cursels_mut_once(b.root, to_upper_cursel, b.allocator);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
@ -5397,7 +5438,7 @@ pub const Editor = struct {
pub fn to_lower(self: *Self, _: Context) Result { pub fn to_lower(self: *Self, _: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, to_lower_cursel, b.allocator); const root = try self.with_cursels_mut_once(b.root, to_lower_cursel, b.allocator);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }
@ -5444,7 +5485,7 @@ pub const Editor = struct {
pub fn switch_case(self: *Self, _: Context) Result { pub fn switch_case(self: *Self, _: Context) Result {
const b = try self.buf_for_update(); const b = try self.buf_for_update();
const root = try self.with_cursels_mut(b.root, switch_case_cursel, b.allocator); const root = try self.with_cursels_mut_once(b.root, switch_case_cursel, b.allocator);
try self.update_buf(root); try self.update_buf(root);
self.clamp(); self.clamp();
} }

View file

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const log = @import("log"); const log = @import("log");
const tp = @import("thespian");
const location_history = @import("location_history"); const location_history = @import("location_history");
const command = @import("command"); const command = @import("command");
const cmd = command.executeName; const cmd = command.executeName;
@ -97,7 +98,7 @@ const cmds_ = struct {
} }
pub const extend_line_below_meta: Meta = .{ .description = "Select current line, if already selected, extend to next line" }; 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 { pub fn move_next_word_start(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return; const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root(); const root = try ed.buf_root();
@ -106,13 +107,13 @@ const cmds_ = struct {
cursel.selection = null; cursel.selection = null;
}; };
ed.with_selections_const(root, Editor.move_cursor_word_right_vim) catch {}; ed.with_selections_const_repeat(root, Editor.move_cursor_word_right_vim, ctx) catch {};
ed.clamp(); ed.clamp();
} }
pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start" }; pub const move_next_word_start_meta: Meta = .{ .description = "Move next word start", .arguments = &.{.integer} };
pub fn move_prev_word_start(_: *void, _: Ctx) Result { pub fn move_prev_word_start(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return; const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root(); const root = try ed.buf_root();
@ -121,10 +122,10 @@ const cmds_ = struct {
cursel.selection = null; cursel.selection = null;
}; };
ed.with_selections_const(root, move_cursor_word_left_helix) catch {}; ed.with_selections_const_repeat(root, move_cursor_word_left_helix, ctx) catch {};
ed.clamp(); ed.clamp();
} }
pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start" }; pub const move_prev_word_start_meta: Meta = .{ .description = "Move previous word start", .arguments = &.{.integer} };
pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result { pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
@ -137,11 +138,14 @@ const cmds_ = struct {
} }
pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" }; pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" };
pub fn select_right_helix(_: *void, _: Ctx) Result { pub fn select_right_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return; const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root(); const root = try ed.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = try cursel.enable_selection(root, ed.metrics); const sel = try cursel.enable_selection(root, ed.metrics);
@ -157,15 +161,19 @@ const cmds_ = struct {
cursel.cursor = sel.end; cursel.cursor = sel.end;
cursel.check_selection(root, ed.metrics); cursel.check_selection(root, ed.metrics);
}; };
}
ed.clamp(); ed.clamp();
} }
pub const select_right_helix_meta: Meta = .{ .description = "Select right" }; pub const select_right_helix_meta: Meta = .{ .description = "Select right", .arguments = &.{.integer} };
pub fn select_left_helix(_: *void, _: Ctx) Result { pub fn select_left_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return; const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root(); const root = try ed.buf_root();
var repeat: usize = 1;
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
while (repeat > 0) : (repeat -= 1) {
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| { for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
if (cursel.selection == null) { if (cursel.selection == null) {
cursel.selection = Selection.from_cursor(&cursel.cursor); cursel.selection = Selection.from_cursor(&cursel.cursor);
@ -184,9 +192,10 @@ const cmds_ = struct {
cursel.check_selection(root, ed.metrics); cursel.check_selection(root, ed.metrics);
}; };
}
ed.clamp(); ed.clamp();
} }
pub const select_left_helix_meta: Meta = .{ .description = "Select left" }; pub const select_left_helix_meta: Meta = .{ .description = "Select left", .arguments = &.{.integer} };
pub fn select_to_char_right_helix(_: *void, ctx: Ctx) Result { pub fn select_to_char_right_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return; const mv = tui.mainview() orelse return;

View file

@ -46,6 +46,14 @@ const cmds_ = struct {
} }
pub const @"wq!_meta": Meta = .{ .description = "wq! (write file and quit without saving)" }; pub const @"wq!_meta": Meta = .{ .description = "wq! (write file and quit without saving)" };
pub fn move_begin_or_add_integer_argument_zero(_: *void, _: Ctx) 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: *void, ctx: Ctx) Result { pub fn enter_mode_at_next_char(self: *void, ctx: Ctx) Result {
_ = self; // autofix _ = self; // autofix
_ = ctx; // autofix _ = ctx; // autofix

View file

@ -29,9 +29,12 @@ pub fn layout(_: *Self) Widget.Layout {
var buf: [256]u8 = undefined; var buf: [256]u8 = undefined;
var fbs = std.io.fixedBufferStream(&buf); var fbs = std.io.fixedBufferStream(&buf);
const writer = fbs.writer(); const writer = fbs.writer();
writer.print(" ", .{}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
writer.print("{}", .{integer_argument}) catch {};
writer.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {}; writer.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
const len = fbs.getWritten().len; const len = fbs.getWritten().len;
return .{ .static = if (len > 0) len + 2 else 0 }; return .{ .static = if (len > 0) len else 0 };
} }
pub fn render(self: *Self, theme: *const Widget.Theme) bool { pub fn render(self: *Self, theme: *const Widget.Theme) bool {
@ -41,6 +44,9 @@ pub fn render(self: *Self, theme: *const Widget.Theme) bool {
self.plane.set_style(theme.statusbar); self.plane.set_style(theme.statusbar);
self.plane.fill(" "); self.plane.fill(" ");
self.plane.home(); self.plane.home();
_ = self.plane.print(" ", .{}) catch {};
if (keybind.current_integer_argument()) |integer_argument|
_ = self.plane.print("{}", .{integer_argument}) catch {};
_ = self.plane.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {}; _ = self.plane.print("{} ", .{keybind.current_key_event_sequence_fmt()}) catch {};
return false; return false;
} }

View file

@ -154,6 +154,7 @@ fn init(allocator: Allocator) InitError!*Self {
try frame_clock.start(); try frame_clock.start();
try self.commands.init(self); try self.commands.init(self);
try keybind.init();
errdefer self.deinit(); errdefer self.deinit();
switch (builtin.os.tag) { switch (builtin.os.tag) {
.windows => { .windows => {
@ -658,6 +659,7 @@ fn enter_overlay_mode(self: *Self, mode: type) command.Result {
if (self.input_mode_outer_) |_| try cmds.exit_overlay_mode(self, .{}); if (self.input_mode_outer_) |_| try cmds.exit_overlay_mode(self, .{});
self.input_mode_outer_ = self.input_mode_; self.input_mode_outer_ = self.input_mode_;
self.input_mode_ = try mode.create(self.allocator); self.input_mode_ = try mode.create(self.allocator);
if (self.input_mode_) |*m| m.run_init();
refresh_hover(); refresh_hover();
} }
@ -673,8 +675,7 @@ fn enter_input_mode(self: *Self, new_mode: Mode) command.Result {
self.input_mode_ = null; self.input_mode_ = null;
} }
self.input_mode_ = new_mode; self.input_mode_ = new_mode;
if (new_mode.init_command) |cmd| if (self.input_mode_) |*m| m.run_init();
cmd.execute_const();
} }
fn refresh_input_mode(self: *Self) command.Result { fn refresh_input_mode(self: *Self) command.Result {
@ -689,6 +690,7 @@ fn refresh_input_mode(self: *Self) command.Result {
self.input_mode_ = null; self.input_mode_ = null;
} }
self.input_mode_ = new_mode; self.input_mode_ = new_mode;
if (self.input_mode_) |*m| m.run_init();
} }
fn set_theme_by_name(self: *Self, name: []const u8) !void { fn set_theme_by_name(self: *Self, name: []const u8) !void {
@ -950,6 +952,7 @@ const cmds = struct {
self.input_mode_outer_ = self.input_mode_; self.input_mode_outer_ = self.input_mode_;
self.input_mode_ = input_mode_; self.input_mode_ = input_mode_;
self.mini_mode_ = mini_mode_; self.mini_mode_ = mini_mode_;
if (self.input_mode_) |*m| m.run_init();
} }
pub fn exit_mini_mode(self: *Self, _: Ctx) Result { pub fn exit_mini_mode(self: *Self, _: Ctx) Result {