Compare commits

..

2 commits

Author SHA1 Message Date
a3a6830043
refactor: re-intruduce move_to_char direction indicator 2025-04-08 10:39:02 +02:00
Levi
fb985a703a
feat: Helix & Vim mode: adding more commands (#218)
* Helix mode: select_left

* select_to_char_right implementation

* Vim select_to_char_left

* Helix select_to_char_left

* Helix & Vim: select_end

* select_to_char_left: Avoid panic with no selection

* select_left_helix: handling panic and shrinking code

* Correcting helix left and right select

* Helix mode: select_left

* select_to_char_right implementation

* Vim select_to_char_left

* Helix select_to_char_left

* Helix & Vim: select_end

* select_to_char_left: Avoid panic with no selection

* select_left_helix: handling panic and shrinking code

* Correcting helix left and right select

* Enable_selection on init_command

* move_to_char modification

* move_or_select

---------

Co-authored-by: CJ van den Berg <cj@vdbonline.com>
2025-04-08 10:28:29 +02:00
6 changed files with 128 additions and 30 deletions

View file

@ -50,8 +50,8 @@
["ctrl+l", "scroll_view_center_cycle"], ["ctrl+l", "scroll_view_center_cycle"],
["ctrl+n", "goto_next_match"], ["ctrl+n", "goto_next_match"],
["ctrl+p", "goto_prev_match"], ["ctrl+p", "goto_prev_match"],
["ctrl+b", "move_to_char", "left"], ["ctrl+b", "move_to_char", "move_or_select_to_char_left"],
["ctrl+t", "move_to_char", "right"], ["ctrl+t", "move_to_char", "move_or_select_to_char_right"],
["ctrl+x", "cut"], ["ctrl+x", "cut"],
["ctrl+c", "copy"], ["ctrl+c", "copy"],
["ctrl+v", "system_paste"], ["ctrl+v", "system_paste"],

View file

@ -62,7 +62,7 @@
["shift+`", "switch_case"], ["shift+`", "switch_case"],
["shift+t", "till_prev_char"], ["shift+t", "till_prev_char"],
["shift+f", "move_to_char", "left"], ["shift+f", "move_to_char", "move_to_char_left"],
["shift+w", "move_next_long_word_start"], ["shift+w", "move_next_long_word_start"],
["shift+b", "move_prev_long_word_start"], ["shift+b", "move_prev_long_word_start"],
["shift+e", "move_next_long_word_end"], ["shift+e", "move_next_long_word_end"],
@ -107,7 +107,7 @@
["l", "move_right"], ["l", "move_right"],
["t", "find_till_char"], ["t", "find_till_char"],
["f", "move_to_char", "right"], ["f", "move_to_char", "move_to_char_right"],
["`", "to_lower"], ["`", "to_lower"],
@ -256,6 +256,7 @@
"line_numbers": "relative", "line_numbers": "relative",
"cursor": "block", "cursor": "block",
"selection": "inclusive", "selection": "inclusive",
"init_command": ["enable_selection"],
"press": [ "press": [
["ctrl+b", "select_page_up"], ["ctrl+b", "select_page_up"],
["ctrl+f", "select_page_down"], ["ctrl+f", "select_page_down"],
@ -322,7 +323,7 @@
["shift+`", "switch_case"], ["shift+`", "switch_case"],
["shift+t", "extend_till_prev_char"], ["shift+t", "extend_till_prev_char"],
["shift+f", "extend_prev_char"], ["shift+f", "move_to_char", "select_to_char_left_vim"],
["shift+w", "extend_next_long_word_start"], ["shift+w", "extend_next_long_word_start"],
["shift+b", "extend_prev_long_word_start"], ["shift+b", "extend_prev_long_word_start"],
@ -371,17 +372,17 @@
["shift+1", "shell_insert_output"], ["shift+1", "shell_insert_output"],
["shift+4", "shell_keep_pipe"], ["shift+4", "shell_keep_pipe"],
["h", "select_left"], ["h", "select_left_helix"],
["j", "select_down"], ["j", "select_down"],
["k", "select_up"], ["k", "select_up"],
["l", "select_right"], ["l", "select_right_helix"],
["left", "select_left"], ["left", "select_left"],
["down", "select_down"], ["down", "select_down"],
["up", "select_up"], ["up", "select_up"],
["right", "select_right"], ["right", "select_right"],
["t", "extend_till_char"], ["t", "extend_till_char"],
["f", "extend_next_char"], ["f", "move_to_char", "select_to_char_right_helix"],
["`", "switch_to_lowercase"], ["`", "switch_to_lowercase"],
@ -398,7 +399,7 @@
["g e", "move_buffer_end"], ["g e", "move_buffer_end"],
["g f", "goto_file"], ["g f", "goto_file"],
["g h", "move_begin"], ["g h", "move_begin"],
["g l", "move_end"], ["g l", "select_end"],
["g s", "smart_move_begin"], ["g s", "smart_move_begin"],
["g d", "goto_definition"], ["g d", "goto_definition"],
["g y", "goto_type_definition"], ["g y", "goto_type_definition"],

View file

@ -86,8 +86,8 @@
["<C-k>", "TODO"], ["<C-k>", "TODO"],
["F", "move_to_char", "left"], ["F", "move_to_char", "move_to_char_left"],
["f", "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"]]
@ -100,6 +100,7 @@
"line_numbers": "relative", "line_numbers": "relative",
"cursor": "block", "cursor": "block",
"selection": "normal", "selection": "normal",
"init_command": ["enable_selection"],
"press": [ "press": [
["<Esc>", ["cancel"], ["enter_mode", "normal"]], ["<Esc>", ["cancel"], ["enter_mode", "normal"]],
["k", "select_up"], ["k", "select_up"],
@ -115,9 +116,12 @@
["0", "move_begin"], ["0", "move_begin"],
["^", "smart_move_begin"], ["^", "smart_move_begin"],
["$", "move_end"], ["$", "select_end"],
[":", "open_command_palette"], [":", "open_command_palette"],
["f", "move_to_char", "select_to_char_right"],
["F", "move_to_char", "select_to_char_left_vim"],
["p", ["paste_internal_vim"], ["enter_mode", "normal"]], ["p", ["paste_internal_vim"], ["enter_mode", "normal"]],
["P", ["paste_internal_vim"], ["enter_mode", "normal"]], ["P", ["paste_internal_vim"], ["enter_mode", "normal"]],

View file

@ -96,7 +96,7 @@ pub const CurSel = struct {
self.* = .{}; self.* = .{};
} }
fn enable_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) !*Selection { pub fn enable_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) !*Selection {
return switch (tui.get_selection_style()) { return switch (tui.get_selection_style()) {
.normal => self.enable_selection_normal(), .normal => self.enable_selection_normal(),
.inclusive => try self.enable_selection_inclusive(root, metrics), .inclusive => try self.enable_selection_inclusive(root, metrics),
@ -147,7 +147,7 @@ pub const CurSel = struct {
} }
} }
fn check_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) void { pub fn check_selection(self: *Self, root: Buffer.Root, metrics: Buffer.Metrics) void {
if (self.selection) |sel| if (sel.empty()) { if (self.selection) |sel| if (sel.empty()) {
self.disable_selection(root, metrics); self.disable_selection(root, metrics);
}; };
@ -2123,7 +2123,7 @@ pub const Editor = struct {
return if (cursor.col == first) cursor.move_begin() else cursor.move_to(root, cursor.row, first, metrics); return if (cursor.col == first) cursor.move_begin() else cursor.move_to(root, cursor.row, first, metrics);
} }
fn move_cursor_right(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { pub fn move_cursor_right(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {
try cursor.move_right(root, metrics); try cursor.move_right(root, metrics);
} }
@ -3089,6 +3089,18 @@ pub const Editor = struct {
} }
pub const move_to_char_right_meta: Meta = .{ .arguments = &.{.integer} }; pub const move_to_char_right_meta: Meta = .{ .arguments = &.{.integer} };
pub fn move_or_select_to_char_left(self: *Self, ctx: Context) Result {
const selected = if (self.get_primary().selection) |_| true else false;
if (selected) try self.select_to_char_left(ctx) else try self.move_to_char_left(ctx);
}
pub const move_or_select_to_char_left_meta: Meta = .{ .arguments = &.{.integer} };
pub fn move_or_select_to_char_right(self: *Self, ctx: Context) Result {
const selected = if (self.get_primary().selection) |_| true else false;
if (selected) try self.select_to_char_right(ctx) else try self.move_to_char_right(ctx);
}
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, _: 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(root, move_cursor_up) catch {};
@ -3592,6 +3604,12 @@ pub const Editor = struct {
} }
pub const cancel_meta: Meta = .{ .description = "Cancel current action" }; pub const cancel_meta: Meta = .{ .description = "Cancel current action" };
pub fn enable_selection(self: *Self, _: Context) Result {
const root = try self.buf_root();
_ = try self.get_primary().enable_selection(root, self.metrics);
}
pub const enable_selection_meta: Meta = .{ .description = "Enable selection" };
pub fn select_line_vim(self: *Self, _: Context) Result { pub fn select_line_vim(self: *Self, _: Context) Result {
self.selection_mode = .line; self.selection_mode = .line;
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel|
@ -3702,6 +3720,16 @@ pub const Editor = struct {
} }
pub const select_to_char_left_meta: Meta = .{ .arguments = &.{.integer} }; pub const select_to_char_left_meta: Meta = .{ .arguments = &.{.integer} };
pub fn select_to_char_left_vim(self: *Self, ctx: Context) Result {
const root = try self.buf_root();
for (self.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
if (cursel.selection) |*sel| try sel.begin.move_right(root, self.metrics);
};
self.with_selections_const_arg(root, move_cursor_to_char_left, ctx) catch {};
self.clamp();
}
pub const select_to_char_left_vim_meta: Meta = .{ .arguments = &.{.integer} };
pub fn select_to_char_right(self: *Self, ctx: Context) Result { pub fn select_to_char_right(self: *Self, ctx: Context) Result {
const root = try self.buf_root(); const root = try self.buf_root();
self.with_selections_const_arg(root, move_cursor_to_char_right, ctx) catch {}; self.with_selections_const_arg(root, move_cursor_to_char_right, ctx) catch {};

View file

@ -8,6 +8,7 @@ const tui = @import("../tui.zig");
const Editor = @import("../editor.zig").Editor; const Editor = @import("../editor.zig").Editor;
const Buffer = @import("Buffer"); const Buffer = @import("Buffer");
const Cursor = Buffer.Cursor; const Cursor = Buffer.Cursor;
const Selection = Buffer.Selection;
var commands: Commands = undefined; var commands: Commands = undefined;
@ -135,6 +136,73 @@ const cmds_ = struct {
ed.clamp(); ed.clamp();
} }
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 {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = try cursel.enable_selection(root, ed.metrics);
// handling left to right transition
const sel_begin: i32 = @intCast(sel.begin.col);
const sel_end: i32 = @intCast(sel.end.col);
if ((sel_begin - sel_end) == 1 and sel.begin.row == sel.end.row) {
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
sel.begin.col -= 1;
}
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
cursel.check_selection(root, ed.metrics);
};
ed.clamp();
}
pub const select_right_helix_meta: Meta = .{ .description = "Select right" };
pub fn select_left_helix(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
if (cursel.selection == null) {
cursel.selection = Selection.from_cursor(&cursel.cursor);
try cursel.selection.?.begin.move_right(root, ed.metrics);
}
if (cursel.selection) |*sel| {
try Editor.move_cursor_left(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
if (sel.begin.col == sel.end.col and sel.begin.row == sel.end.row) {
try sel.begin.move_right(root, ed.metrics);
try Editor.move_cursor_left(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
}
}
cursel.check_selection(root, ed.metrics);
};
ed.clamp();
}
pub const select_left_helix_meta: Meta = .{ .description = "Select left" };
pub fn select_to_char_right_helix(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = try ed.buf_root();
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| {
const sel = try cursel.enable_selection(root, ed.metrics);
try Editor.move_cursor_to_char_right(root, &sel.end, ctx, ed.metrics);
try Editor.move_cursor_right(root, &sel.end, ed.metrics);
cursel.cursor = sel.end;
cursel.check_selection(root, ed.metrics);
};
ed.clamp();
}
pub const select_to_char_right_helix_meta: Meta = .{ .description = "Move to char right" };
}; };
fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void { fn move_cursor_word_left_helix(root: Buffer.Root, cursor: *Cursor, metrics: Buffer.Metrics) error{Stop}!void {

View file

@ -1,3 +1,4 @@
const std = @import("std");
const tp = @import("thespian"); const tp = @import("thespian");
const input = @import("input"); const input = @import("input");
@ -16,6 +17,7 @@ const Commands = command.Collection(cmds);
allocator: Allocator, allocator: Allocator,
key: [6]u8 = undefined, key: [6]u8 = undefined,
direction: Direction, direction: Direction,
operation_command: []const u8,
operation: Operation, operation: Operation,
commands: Commands = undefined, commands: Commands = undefined,
@ -30,14 +32,18 @@ const Operation = enum {
}; };
pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } { pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } {
var direction: Direction = undefined; var operation_command: []const u8 = undefined;
const select = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| true else false else false; _ = ctx.args.match(.{tp.extract(&operation_command)}) catch return error.InvalidMoveToCharArgument;
_ = ctx.args.match(.{tp.extract(&direction)}) catch return error.InvalidMoveToCharArgument;
const direction: Direction = if (std.mem.indexOf(u8, operation_command, "_left")) |_| .left else .right;
const operation: Operation = if (tui.get_active_editor()) |editor| if (editor.get_primary().selection) |_| .select else .move else .move;
const self: *Self = try allocator.create(Self); const self: *Self = try allocator.create(Self);
self.* = .{ self.* = .{
.allocator = allocator, .allocator = allocator,
.direction = direction, .direction = direction,
.operation = if (select) .select else .move, .operation_command = try allocator.dupe(u8, operation_command),
.operation = operation,
}; };
try self.commands.init(self); try self.commands.init(self);
var mode = try keybind.mode("mini/move_to_char", allocator, .{ var mode = try keybind.mode("mini/move_to_char", allocator, .{
@ -49,6 +55,7 @@ pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tu
pub fn deinit(self: *Self) void { pub fn deinit(self: *Self) void {
self.commands.deinit(); self.commands.deinit();
self.allocator.free(self.operation_command);
self.allocator.destroy(self); self.allocator.destroy(self);
} }
@ -70,17 +77,7 @@ pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool {
} }
fn execute_operation(self: *Self, ctx: command.Context) command.Result { fn execute_operation(self: *Self, ctx: command.Context) command.Result {
const cmd = switch (self.direction) { try command.executeName(self.operation_command, ctx);
.left => switch (self.operation) {
.move => "move_to_char_left",
.select => "select_to_char_left",
},
.right => switch (self.operation) {
.move => "move_to_char_right",
.select => "select_to_char_right",
},
};
try command.executeName(cmd, ctx);
try command.executeName("exit_mini_mode", .{}); try command.executeName("exit_mini_mode", .{});
} }