Compare commits

...

2 commits

Author SHA1 Message Date
66ed4a5af4
fix: pull the last line of a file up or down
closes #527
2026-04-14 22:56:52 +02:00
UnsaltedScholar
1e7d595309 Add angle bracket textobject actions 2026-04-14 17:10:17 +02:00
4 changed files with 135 additions and 5 deletions

View file

@ -44,6 +44,7 @@ pub const char_pairs = [_]struct { []const u8, []const u8 }{
.{ "`", "`" },
.{ "(", ")" },
.{ "[", "]" },
.{ "<", ">" },
.{ "{", "}" },
.{ "", "" },
.{ "", "" },
@ -56,6 +57,7 @@ pub const char_pairs = [_]struct { []const u8, []const u8 }{
pub const open_close_pairs = [_]struct { []const u8, []const u8 }{
.{ "(", ")" },
.{ "[", "]" },
.{ "<", ">" },
.{ "{", "}" },
.{ "", "" },
.{ "", "" },

View file

@ -86,6 +86,8 @@
["di)", "cut_inside_parentheses"],
["di[", "cut_inside_square_brackets"],
["di]", "cut_inside_square_brackets"],
["di<LT>", "cut_inside_angle_brackets"],
["di<GT>", "cut_inside_angle_brackets"],
["di{", "cut_inside_braces"],
["di}", "cut_inside_braces"],
["di'", "cut_inside_single_quotes"],
@ -96,6 +98,8 @@
["da)", "cut_around_parentheses"],
["da[", "cut_around_square_brackets"],
["da]", "cut_around_square_brackets"],
["da<LT>", "cut_around_angle_brackets"],
["da<GT>", "cut_around_angle_brackets"],
["da{", "cut_around_braces"],
["da}", "cut_around_braces"],
["da'", "cut_around_single_quotes"],
@ -112,6 +116,8 @@
["ci)", ["enter_mode", "insert"], ["cut_inside_parentheses"]],
["ci[", ["enter_mode", "insert"], ["cut_inside_square_brackets"]],
["ci]", ["enter_mode", "insert"], ["cut_inside_square_brackets"]],
["ci<LT>", ["enter_mode", "insert"], ["cut_inside_angle_brackets"]],
["ci<GT>", ["enter_mode", "insert"], ["cut_inside_angle_brackets"]],
["ci{", ["enter_mode", "insert"], ["cut_inside_braces"]],
["ci}", ["enter_mode", "insert"], ["cut_inside_braces"]],
["ci'", ["enter_mode", "insert"], ["cut_inside_single_quotes"]],
@ -122,6 +128,8 @@
["ca)", ["enter_mode", "insert"], ["cut_around_parentheses"]],
["ca[", ["enter_mode", "insert"], ["cut_around_square_brackets"]],
["ca]", ["enter_mode", "insert"], ["cut_around_square_brackets"]],
["ca<LT>", ["enter_mode", "insert"], ["cut_around_angle_brackets"]],
["ca<GT>", ["enter_mode", "insert"], ["cut_around_angle_brackets"]],
["ca{", ["enter_mode", "insert"], ["cut_around_braces"]],
["ca}", ["enter_mode", "insert"], ["cut_around_braces"]],
["ca'", ["enter_mode", "insert"], ["cut_around_single_quotes"]],
@ -134,6 +142,8 @@
["yi)", ["copy_inside_parentheses"], ["cancel"]],
["yi[", ["copy_inside_square_brackets"], ["cancel"]],
["yi]", ["copy_inside_square_brackets"], ["cancel"]],
["yi<LT>", ["copy_inside_angle_brackets"], ["cancel"]],
["yi<GT>", ["copy_inside_angle_brackets"], ["cancel"]],
["yi{", ["copy_inside_braces"], ["cancel"]],
["yi}", ["copy_inside_braces"], ["cancel"]],
["yi'", ["copy_inside_single_quotes"], ["cancel"]],
@ -144,6 +154,8 @@
["ya)", ["copy_around_parentheses"], ["cancel"]],
["ya[", ["copy_around_square_brackets"], ["cancel"]],
["ya]", ["copy_around_square_brackets"], ["cancel"]],
["ya<LT>", ["copy_around_angle_brackets"], ["cancel"]],
["ya<GT>", ["copy_around_angle_brackets"], ["cancel"]],
["ya{", ["copy_around_braces"], ["cancel"]],
["ya}", ["copy_around_braces"], ["cancel"]],
["ya'", ["copy_around_single_quotes"], ["cancel"]],
@ -224,6 +236,8 @@
["i)", "select_inside_parentheses"],
["i[", "select_inside_square_brackets"],
["i]", "select_inside_square_brackets"],
["i<LT>", "select_inside_angle_brackets"],
["i<GT>", "select_inside_angle_brackets"],
["i{", "select_inside_braces"],
["i}", "select_inside_braces"],
["i'", "select_inside_single_quotes"],
@ -234,6 +248,8 @@
["a)", "select_around_parentheses"],
["a[", "select_around_square_brackets"],
["a]", "select_around_square_brackets"],
["a<LT>", "select_around_angle_brackets"],
["a<GT>", "select_around_angle_brackets"],
["a{", "select_around_braces"],
["a}", "select_around_braces"],
["a'", "select_around_single_quotes"],

View file

@ -4232,11 +4232,25 @@ pub const Editor = struct {
fn pull_cursel_up(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_;
const saved = cursel.*;
errdefer cursel.* = saved;
const sel = cursel.expand_selection_to_line(root, self.metrics);
var sfa = std.heap.stackFallback(4096, self.allocator);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
const cut_text_raw = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text_raw);
const is_last_no_nl = sel.end.row == cursel.cursor.row;
var cut_text_buf: ?[]u8 = null;
defer if (cut_text_buf) |t| sfa_allocator.free(t);
const cut_text: []const u8 = if (is_last_no_nl) blk: {
const buf = sfa_allocator.alloc(u8, cut_text_raw.len + 1) catch return error.Stop;
cut_text_buf = buf;
@memcpy(buf[0..cut_text_raw.len], cut_text_raw);
buf[cut_text_raw.len] = '\n';
break :blk buf;
} else cut_text_raw;
root = try self.delete_selection(root, cursel, allocator);
try cursel.cursor.move_up(root, self.metrics);
root = self.insert(root, cursel, cut_text, allocator) catch return error.Stop;
@ -4246,6 +4260,17 @@ pub const Editor = struct {
try sel_.begin.move_up(root, self.metrics);
try sel_.end.move_up(root, self.metrics);
}
if (is_last_no_nl) {
const last_content_row = root.lines() - 2;
var del_begin: Cursor = .{ .row = last_content_row, .col = 0 };
del_begin.move_end(root, self.metrics);
var tmp: CurSel = .{
.cursor = del_begin,
.selection = .{ .begin = del_begin, .end = .{ .row = last_content_row + 1, .col = 0 } },
};
root = try self.delete_selection(root, &tmp, allocator);
}
return root;
}
@ -4260,14 +4285,35 @@ pub const Editor = struct {
fn pull_cursel_down(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root {
var root = root_;
const saved = cursel.*;
errdefer cursel.* = saved;
const cursor_row_before = cursel.cursor.row;
const lines_before = root.lines();
const sel = cursel.expand_selection_to_line(root, self.metrics);
var sfa = std.heap.stackFallback(4096, self.allocator);
const sfa_allocator = sfa.get();
const cut_text = copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
const cut_text = if (sel.empty())
&.{}
else
copy_selection(root, sel.*, sfa_allocator, self.metrics) catch return error.Stop;
defer sfa_allocator.free(cut_text);
root = try self.delete_selection(root, cursel, allocator);
try cursel.cursor.move_down(root, self.metrics);
root = self.insert(root, cursel, cut_text, allocator) catch return error.Stop;
const moved_down = blk: {
cursel.cursor.move_down(root, self.metrics) catch break :blk false;
break :blk true;
};
if (moved_down) {
root = self.insert(root, cursel, cut_text, allocator) catch return error.Stop;
} else {
if (cursor_row_before >= lines_before - 1) return error.Stop;
cursel.cursor.move_end(root, self.metrics);
root = self.insert(root, cursel, "\n", allocator) catch return error.Stop;
const cut_no_nl = if (std.mem.endsWith(u8, cut_text, "\n"))
cut_text[0 .. cut_text.len - 1]
else
cut_text;
if (cut_no_nl.len > 0)
root = self.insert(root, cursel, cut_no_nl, allocator) catch return error.Stop;
}
cursel.* = saved;
try cursel.cursor.move_down(root, self.metrics);
if (cursel.selection) |*sel_| {

View file

@ -202,6 +202,24 @@ const cmds_ = struct {
}
pub const select_around_square_brackets_meta: Meta = .{ .description = "Select around []" };
pub fn select_inside_angle_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(root, select_inside_angle_brackets_textobject, ed.metrics);
}
pub const select_inside_angle_brackets_meta: Meta = .{ .description = "Select inside <>" };
pub fn select_around_angle_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(root, select_around_angle_brackets_textobject, ed.metrics);
}
pub const select_around_angle_brackets_meta: Meta = .{ .description = "Select around <>" };
pub fn select_inside_braces(_: *void, _: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
@ -316,6 +334,26 @@ const cmds_ = struct {
}
pub const cut_around_square_brackets_meta: Meta = .{ .description = "Cut around []" };
pub fn cut_inside_angle_brackets(_: *void, ctx: 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(root, select_inside_angle_brackets_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_inside_angle_brackets_meta: Meta = .{ .description = "Cut inside <>" };
pub fn cut_around_angle_brackets(_: *void, ctx: 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(root, select_around_angle_brackets_textobject, ed.metrics);
try ed.cut_internal_vim(ctx);
}
pub const cut_around_angle_brackets_meta: Meta = .{ .description = "Cut around <>" };
pub fn cut_inside_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
@ -436,6 +474,26 @@ const cmds_ = struct {
}
pub const copy_around_square_brackets_meta: Meta = .{ .description = "Copy around []" };
pub fn copy_inside_angle_brackets(_: *void, ctx: 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(root, select_inside_angle_brackets_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_inside_angle_brackets_meta: Meta = .{ .description = "Copy inside <>" };
pub fn copy_around_angle_brackets(_: *void, ctx: 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(root, select_around_angle_brackets_textobject, ed.metrics);
try ed.copy_internal_vim(ctx);
}
pub const copy_around_angle_brackets_meta: Meta = .{ .description = "Copy around <>" };
pub fn copy_inside_braces(_: *void, ctx: Ctx) Result {
const mv = tui.mainview() orelse return;
const ed = mv.get_active_editor() orelse return;
@ -575,6 +633,14 @@ fn select_around_square_brackets_textobject(root: Buffer.Root, cursel: *CurSel,
return try select_scope_textobject(root, cursel, metrics, "[", "]", .around);
}
fn select_inside_angle_brackets_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_scope_textobject(root, cursel, metrics, "<", ">", .inside);
}
fn select_around_angle_brackets_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_scope_textobject(root, cursel, metrics, "<", ">", .around);
}
fn select_inside_braces_textobject(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) !void {
return try select_scope_textobject(root, cursel, metrics, "{", "}", .inside);
}