Compare commits

..

9 commits

Author SHA1 Message Date
8b9cc87cab
fix: add back german quotes to char_pairs 2025-11-18 10:55:36 +01:00
Igor Támara
b4b44ec906 refactor: additional characters for smart_insert 2025-11-18 10:50:47 +01:00
Igor Támara
fd3401748e refactor: identify mode inside hx on bracket matching 2025-11-18 10:48:07 +01:00
Igor Támara
3d0a0571c2 fix: removed unused code 2025-11-18 10:48:07 +01:00
Igor Támara
0b80ae50db refactor: simplify match mode 2025-11-18 10:48:07 +01:00
Igor Támara
124cbcbe5f fix: Add @ to non_word characters 2025-11-18 10:48:07 +01:00
Igor Támara
4b3e71408a feat: [hx] maw and maW support 2025-11-18 10:48:07 +01:00
Igor Támara
878aef9926 feat: [hx] miw and miW support 2025-11-18 10:48:07 +01:00
Igor Támara
a35bbc7e96 refactor: prepare match minimode 2025-11-18 10:48:07 +01:00
7 changed files with 183 additions and 17 deletions

View file

@ -41,6 +41,7 @@ pub fn control_code_to_unicode(code: u8) [:0]const u8 {
pub const char_pairs = [_]struct { []const u8, []const u8 }{
.{ "\"", "\"" },
.{ "'", "'" },
.{ "`", "`" },
.{ "(", ")" },
.{ "[", "]" },
.{ "{", "}" },
@ -48,6 +49,8 @@ pub const char_pairs = [_]struct { []const u8, []const u8 }{
.{ "", "" },
.{ "", "" },
.{ "«", "»" },
.{ "¿", "?" },
.{ "¡", "!" },
};
pub const open_close_pairs = [_]struct { []const u8, []const u8 }{
@ -57,6 +60,8 @@ pub const open_close_pairs = [_]struct { []const u8, []const u8 }{
.{ "", "" },
.{ "", "" },
.{ "«", "»" },
.{ "¿", "?" },
.{ "¡", "!" },
};
fn raw_byte_to_utf8(cp: u8, buf: []u8) ![]const u8 {

View file

@ -255,6 +255,7 @@
["\"", "smart_insert_pair", "\"", "\""],
["'", "smart_insert_pair", "'", "'"],
["`", "smart_insert_pair", "`", "`"],
["(", "smart_insert_pair", "(", ")"],
[")", "smart_insert_pair_close", "(", ")"],
["[", "smart_insert_pair", "[", "]"],
@ -269,6 +270,8 @@
["", "smart_insert_pair_close", "", ""],
["«", "smart_insert_pair", "«", "»"],
["»", "smart_insert_pair_close", "«", "»"],
["¿", "smart_insert_pair", "¿", "?"],
["¡", "smart_insert_pair", "¡", "!"],
["alt+0", "add_integer_argument_digit", 0],
["alt+1", "add_integer_argument_digit", 1],

View file

@ -170,11 +170,11 @@
["x", "extend_line_below"],
["m m", "match_brackets"],
["m s", "surround_add"],
["m r", "surround_replace"],
["m d", "surround_delete"],
["m a", "select_textobject_around"],
["m i", "select_textobject_inner"],
["m a", "match", "select_textobject_around"],
["m i", "match", "select_textobject_inner"],
["m d", "match", "surround_delete"],
["m r", "match", "surround_replace"],
["m s", "match", "surround_add"],
["[ D", "goto_first_diag"],
["[ G", "goto_first_change"],
@ -463,12 +463,12 @@
["x", "extend_line_below"],
["m m", "match_brackets", "helix_sel_mode"],
["m s", "surround_add"],
["m r", "surround_replace"],
["m d", "surround_delete"],
["m a", "select_textobject_around"],
["m i", "select_textobject_inner"],
["m m", "match_brackets"],
["m a", "match", "select_textobject_around"],
["m i", "match", "select_textobject_inner"],
["m d", "match", "surround_delete"],
["m r", "match", "surround_replace"],
["m s", "match", "surround_add"],
["[ D", "goto_first_diag"],
["[ G", "goto_first_change"],

View file

@ -2252,6 +2252,7 @@ pub const Editor = struct {
'!' => true,
'?' => true,
'&' => true,
'@' => true,
'-' => true,
'<' => true,
'>' => true,
@ -2259,7 +2260,7 @@ pub const Editor = struct {
};
}
fn is_word_char(c: []const u8) bool {
pub fn is_word_char(c: []const u8) bool {
return !is_not_word_char(c);
}
@ -2271,7 +2272,7 @@ pub const Editor = struct {
return cursor.test_at(root, is_not_word_char, metrics);
}
fn is_word_boundary_left(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
pub fn is_word_boundary_left(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
if (cursor.col == 0)
return true;
if (is_non_word_char_at_cursor(root, cursor, metrics))
@ -2324,7 +2325,7 @@ pub const Editor = struct {
return false;
}
fn is_word_boundary_right(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
pub fn is_word_boundary_right(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
const line_width = root.line_width(cursor.row, metrics) catch return true;
if (cursor.col >= line_width)
return true;

View file

@ -209,11 +209,12 @@ 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(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
try ed.with_cursels_const_once_arg(root, &match_bracket, ctx);
const m = tui.input_mode().?.*.name;
try ed.with_cursels_const_once_arg(root, &match_bracket, command.fmt(.{m}));
ed.clamp();
}
pub const match_brackets_meta: Meta = .{ .description = "Goto matching bracket" };
@ -411,6 +412,44 @@ const cmds_ = struct {
}
pub const extend_to_char_right_helix_meta: Meta = .{ .description = "Extend Selection to char right" };
pub fn select_textobject_inner(_: *void, ctx: Ctx) Result {
var action: []const u8 = "";
if (!try ctx.args.match(.{tp.extract(&action)})) return error.Stop;
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
if (std.mem.eql(u8, action, "w")) {
try ed.with_cursels_const(root, select_inner_word, ed.metrics);
} else if (std.mem.eql(u8, action, "W")) {
try ed.with_cursels_const(root, select_inner_long_word, ed.metrics);
} else {
return;
}
ed.clamp();
}
pub const select_textobject_inner_meta: Meta = .{ .description = "select inside object helix" };
pub fn select_textobject_around(_: *void, ctx: Ctx) Result {
var action: []const u8 = "";
if (!try ctx.args.match(.{tp.extract(&action)})) return error.Stop;
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
const root = ed.buf_root() catch return;
if (std.mem.eql(u8, action, "w")) {
try ed.with_cursels_const(root, select_around_word, ed.metrics);
} else if (std.mem.eql(u8, action, "W")) {
try ed.with_cursels_const(root, select_inner_long_word, ed.metrics);
} else {
return;
}
ed.clamp();
}
pub const select_textobject_around_meta: Meta = .{ .description = "select around object helix" };
pub fn copy_helix(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
@ -455,7 +494,7 @@ const cmds_ = struct {
fn match_bracket(root: Buffer.Root, cursel: *CurSel, ctx: command.Context, metrics: Buffer.Metrics) error{Stop}!void {
var symbol: []const u8 = undefined;
const mode: enum { helix_sel_mode, helix_nor_mode } = if ((ctx.args.match(.{tp.extract(&symbol)}) catch false) and
std.mem.eql(u8, @tagName(.helix_sel_mode), symbol)) .helix_sel_mode else .helix_nor_mode;
std.mem.eql(u8, "SEL", symbol)) .helix_sel_mode else .helix_nor_mode;
if (mode == .helix_sel_mode) {
const begin: Cursor = if (cursel.selection) |sel| sel.begin else cursel.*.cursor;
@ -522,6 +561,93 @@ fn to_char_helix(ctx: command.Context, move: Editor.cursel_operator_mut_once_arg
ed.clamp();
}
fn select_inner_word(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
if (!cursel.cursor.test_at(root, Editor.is_word_char, metrics)) return;
var prev = cursel.cursor;
var next = cursel.cursor;
Editor.move_cursor_left_until(root, &prev, Editor.is_word_boundary_left, metrics);
Editor.move_cursor_right_until(root, &next, Editor.is_word_boundary_right, metrics);
try next.move_right(root, metrics);
const sel = try cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn select_inner_long_word(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
if (cursel.cursor.test_at(root, Editor.is_whitespace, metrics)) return;
var prev = cursel.cursor;
var next = cursel.cursor;
Editor.move_cursor_left_until(root, &prev, is_long_word_boundary_left, metrics);
Editor.move_cursor_right_until(root, &next, is_long_word_boundary_right, metrics);
try next.move_right(root, metrics);
const sel = try cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn is_tab_or_space(c: []const u8) bool {
return (c[0] == ' ') or (c[0] == '\t');
}
fn is_tab_or_espace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return cursor.test_at(root, is_tab_or_space, metrics);
}
fn is_not_tab_or_espace_at_cursor(root: Buffer.Root, cursor: *const Cursor, metrics: Buffer.Metrics) bool {
return !cursor.test_at(root, is_tab_or_space, metrics);
}
fn select_around_word(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
if (!cursel.cursor.test_at(root, Editor.is_word_char, metrics)) return;
var expander = cursel.*;
try select_inner_word(root, &expander, metrics);
const sel_e = try expander.enable_selection(root, metrics);
var prev = sel_e.begin;
var next = sel_e.end;
if (next.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_right_until(root, &next, is_not_tab_or_espace_at_cursor, metrics);
} else {
next = sel_e.end;
prev.move_left(root, metrics) catch {};
if (prev.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_left_until(root, &prev, is_not_tab_or_espace_at_cursor, metrics);
prev.move_right(root, metrics) catch {};
} else {
prev = sel_e.begin;
}
}
const sel = try cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn select_around_long_word(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
if (!cursel.cursor.test_at(root, Editor.is_word_char, metrics)) return;
var expander = cursel.*;
try select_inner_long_word(root, &expander, metrics);
const sel_e = try expander.enable_selection(root, metrics);
var prev = sel_e.begin;
var next = sel_e.end;
if (next.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_right_until(root, &next, is_not_tab_or_espace_at_cursor, metrics);
} else {
next = sel_e.end;
prev.move_left(root, metrics) catch {};
if (prev.test_at(root, is_tab_or_space, metrics)) {
Editor.move_cursor_left_until(root, &prev, is_not_tab_or_espace_at_cursor, metrics);
prev.move_right(root, metrics) catch {};
} else {
prev = sel_e.begin;
}
}
const sel = try cursel.enable_selection(root, metrics);
sel.begin = prev;
sel.end = next;
cursel.*.cursor = next;
}
fn select_cursel_to_char_left_helix(root: Buffer.Root, cursel: *CurSel, ctx: command.Context, metrics: Buffer.Metrics) error{Stop}!void {
var moving_cursor: Cursor = cursel.*.cursor;
var begin = cursel.*.cursor;

View file

@ -0,0 +1,26 @@
const std = @import("std");
const cbor = @import("cbor");
const command = @import("command");
const tp = @import("thespian");
const log = @import("log");
const tui = @import("../../tui.zig");
pub const Type = @import("get_char.zig").Create(@This());
pub const create = Type.create;
pub fn name(self: *Type) []const u8 {
var suffix: []const u8 = "";
if ((self.ctx.args.match(.{tp.extract(&suffix)}) catch false)) {
return suffix;
}
return "󰅪 match";
}
pub fn process_egc(self: *Type, egc: []const u8) command.Result {
var action: []const u8 = "";
if ((self.ctx.args.match(.{tp.extract(&action)}) catch false)) {
try command.executeName(action, command.fmt(.{egc}));
}
try command.executeName("exit_mini_mode", .{});
}

View file

@ -1279,6 +1279,11 @@ const cmds = struct {
}
pub const underline_meta: Meta = .{ .description = "Underline with character" };
pub fn match(self: *Self, ctx: Ctx) Result {
return enter_mini_mode(self, @import("mode/mini/match.zig"), ctx);
}
pub const match_meta: Meta = .{ .description = "Match mode" };
pub fn open_file(self: *Self, ctx: Ctx) Result {
if (get_active_selection(self.allocator)) |text| {
defer self.allocator.free(text);