refactor: reduce duplication of context getting code in helix mode
This commit is contained in:
parent
b3db0922ed
commit
631e8fca41
2 changed files with 108 additions and 121 deletions
|
|
@ -7,6 +7,7 @@ const command = @import("command");
|
|||
const cmd = command.executeName;
|
||||
|
||||
const tui = @import("../tui.zig");
|
||||
const MainView = tui.MainView;
|
||||
const Editor = @import("../editor.zig").Editor;
|
||||
const CurSel = @import("../editor.zig").CurSel;
|
||||
const Buffer = @import("Buffer");
|
||||
|
|
@ -29,11 +30,12 @@ pub fn deinit() void {
|
|||
}
|
||||
|
||||
const Commands = command.Collection(cmds_);
|
||||
const Ctx = command.Context;
|
||||
const Meta = command.Metadata;
|
||||
const Result = command.Result;
|
||||
|
||||
const cmds_ = struct {
|
||||
pub const Target = void;
|
||||
const Ctx = command.Context;
|
||||
const Meta = command.Metadata;
|
||||
const Result = command.Result;
|
||||
|
||||
pub fn w(_: *void, _: Ctx) Result {
|
||||
try cmd("save_file", .{});
|
||||
|
|
@ -201,9 +203,7 @@ const cmds_ = struct {
|
|||
pub const save_selection_meta: Meta = .{ .description = "Save current selection to location history" };
|
||||
|
||||
pub fn split_selection_on_newline(_: *void, _: Ctx) Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = try ed.buf_root();
|
||||
const ed, const root = get_buf() orelse return;
|
||||
const cursels = try ed.cursels.toOwnedSlice(ed.allocator);
|
||||
defer ed.allocator.free(cursels);
|
||||
for (cursels) |*cursel_| if (cursel_.*) |*cursel| {
|
||||
|
|
@ -214,19 +214,14 @@ 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 {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = ed.buf_root() catch return;
|
||||
const ed, const root = get_buf() orelse return;
|
||||
try ed.with_cursels_const_once_arg(root, &match_bracket, ctx);
|
||||
ed.clamp();
|
||||
}
|
||||
pub const match_brackets_meta: Meta = .{ .description = "Goto matching bracket" };
|
||||
|
||||
pub fn extend_line_below(_: *void, ctx: Ctx) Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = try ed.buf_root();
|
||||
|
||||
const ed, const root = get_buf() orelse return;
|
||||
var repeat: usize = 1;
|
||||
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
|
||||
while (repeat > 0) : (repeat -= 1) {
|
||||
|
|
@ -265,73 +260,6 @@ const cmds_ = struct {
|
|||
}
|
||||
pub const extend_next_long_word_start_meta: Meta = .{ .description = "Extend next long word start", .arguments = &.{.integer} };
|
||||
|
||||
fn is_eol(c: []const u8) bool {
|
||||
return char_class(c) == .eol;
|
||||
}
|
||||
|
||||
fn is_whitespace(c: []const u8) bool {
|
||||
return char_class(c) == .whitespace;
|
||||
}
|
||||
|
||||
fn is_word_boundary(root: Buffer.Root, cursor: Cursor, metrics: Buffer.Metrics, comptime direction: enum { left, right }) bool {
|
||||
const nxt = char_class(switch (direction) {
|
||||
.left => cursor.char_left(root, metrics),
|
||||
.right => cursor.char_right(root, metrics),
|
||||
});
|
||||
const cur = char_class(cursor.char_at(root, metrics));
|
||||
if (cur == .eol) return false;
|
||||
return switch (nxt) {
|
||||
.end, .eol => true,
|
||||
.whitespace => cur != .whitespace,
|
||||
else => nxt != cur,
|
||||
};
|
||||
}
|
||||
|
||||
fn move_cursor_prev_word_start(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) error{Stop}!void {
|
||||
var cursor = cursel.cursor;
|
||||
if (is_word_boundary(root, cursor, metrics, .left))
|
||||
cursor.move_left(root, metrics) catch {};
|
||||
|
||||
var sel = Selection.from_cursor_inclusive(&cursor, root, metrics);
|
||||
defer {
|
||||
sel.begin = cursor;
|
||||
cursel.cursor = cursor;
|
||||
cursel.selection = sel;
|
||||
}
|
||||
|
||||
// Consume whitespace
|
||||
while (cursor.test_at(root, is_whitespace, metrics)) {
|
||||
cursor.move_left(root, metrics) catch return;
|
||||
// Stop at beginning of line
|
||||
if (cursor.test_left(root, is_eol, metrics)) return;
|
||||
}
|
||||
|
||||
// Consume word/non-word chars
|
||||
while (!is_word_boundary(root, cursor, metrics, .left)) {
|
||||
cursor.move_left(root, metrics) catch return;
|
||||
// Stop at beginning of line
|
||||
if (cursor.test_left(root, is_eol, metrics)) return;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_cursor_prev_word_start_extend(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) error{Stop}!void {
|
||||
var selection = cursel.selection;
|
||||
// check if we already had a selection and extend it
|
||||
defer if (selection) |*pre_sel| {
|
||||
pre_sel.normalize();
|
||||
if (cursel.selection) |*sel| sel.end = pre_sel.end;
|
||||
};
|
||||
try move_cursor_prev_word_start(root, cursel, metrics);
|
||||
}
|
||||
|
||||
fn move_cursels_const_repeat(move: Editor.cursel_operator_const, ctx: Ctx) Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = try ed.buf_root();
|
||||
try ed.with_cursels_const_repeat(root, move, ctx);
|
||||
ed.clamp();
|
||||
}
|
||||
|
||||
pub fn move_prev_word_start(_: *void, ctx: Ctx) Result {
|
||||
try move_cursels_const_repeat(move_cursor_prev_word_start, ctx);
|
||||
}
|
||||
|
|
@ -374,9 +302,7 @@ const cmds_ = struct {
|
|||
pub const extend_next_long_word_end_meta: Meta = .{ .description = "Extend next long word end", .arguments = &.{.integer} };
|
||||
|
||||
pub fn cut_forward_internal_inclusive(_: *void, _: Ctx) Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const b = try ed.buf_for_update();
|
||||
const ed, const b = get_buf_for_update() orelse return;
|
||||
tui.clipboard_start_group();
|
||||
const root = try ed.cut_to(move_noop, b.root);
|
||||
try ed.update_buf(root);
|
||||
|
|
@ -385,10 +311,7 @@ const cmds_ = struct {
|
|||
pub const cut_forward_internal_inclusive_meta: Meta = .{ .description = "Cut next character to internal clipboard (inclusive)" };
|
||||
|
||||
pub fn select_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();
|
||||
|
||||
const ed, const root = get_buf() orelse return;
|
||||
var repeat: usize = 1;
|
||||
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
|
||||
while (repeat > 0) : (repeat -= 1) {
|
||||
|
|
@ -413,10 +336,7 @@ const cmds_ = struct {
|
|||
pub const select_right_helix_meta: Meta = .{ .description = "Select right", .arguments = &.{.integer} };
|
||||
|
||||
pub fn select_left_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();
|
||||
|
||||
const ed, const root = get_buf() orelse return;
|
||||
var repeat: usize = 1;
|
||||
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
|
||||
while (repeat > 0) : (repeat -= 1) {
|
||||
|
|
@ -484,12 +404,10 @@ 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 = "";
|
||||
const ed, const root = get_buf() orelse return error.Stop;
|
||||
|
||||
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);
|
||||
|
|
@ -503,12 +421,10 @@ const cmds_ = struct {
|
|||
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 = "";
|
||||
const ed, const root = get_buf() orelse return;
|
||||
|
||||
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);
|
||||
|
|
@ -522,10 +438,7 @@ const cmds_ = struct {
|
|||
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;
|
||||
const root = ed.buf_root() catch return;
|
||||
|
||||
const ed, const root = get_buf() orelse return;
|
||||
tui.clipboard_start_group();
|
||||
|
||||
for (ed.cursels.items) |*cursel_| if (cursel_.*) |*cursel| if (cursel.selection) |sel|
|
||||
|
|
@ -551,9 +464,8 @@ const cmds_ = struct {
|
|||
pub const paste_clipboard_before_meta: Meta = .{ .description = "Paste from clipboard before selection" };
|
||||
|
||||
pub fn replace_with_character_helix(_: *void, ctx: Ctx) Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
var root = ed.buf_root() catch return;
|
||||
const ed, const b = get_buf_for_update() orelse return;
|
||||
var root = b.root;
|
||||
root = try ed.with_cursels_mut_once_arg(root, replace_cursel_with_character, ed.allocator, ctx);
|
||||
try ed.update_buf(root);
|
||||
ed.clamp();
|
||||
|
|
@ -562,6 +474,87 @@ const cmds_ = struct {
|
|||
pub const replace_with_character_helix_meta: Meta = .{ .description = "Replace with character" };
|
||||
};
|
||||
|
||||
fn get_context() ?struct { *MainView, *Editor } {
|
||||
const mv = tui.mainview() orelse return null;
|
||||
const ed = mv.get_active_editor() orelse return null;
|
||||
return .{ mv, ed };
|
||||
}
|
||||
|
||||
fn get_buf() ?struct { *Editor, Buffer.Root } {
|
||||
_, const ed = get_context() orelse return null;
|
||||
return .{ ed, ed.buf_root() catch return null };
|
||||
}
|
||||
|
||||
fn get_buf_for_update() ?struct { *Editor, *const Buffer } {
|
||||
_, const ed = get_context() orelse return null;
|
||||
return .{ ed, ed.buf_for_update() catch return null };
|
||||
}
|
||||
|
||||
fn is_eol(c: []const u8) bool {
|
||||
return char_class(c) == .eol;
|
||||
}
|
||||
|
||||
fn is_whitespace(c: []const u8) bool {
|
||||
return char_class(c) == .whitespace;
|
||||
}
|
||||
|
||||
fn is_word_boundary(root: Buffer.Root, cursor: Cursor, metrics: Buffer.Metrics, comptime direction: enum { left, right }) bool {
|
||||
const nxt = char_class(switch (direction) {
|
||||
.left => cursor.char_left(root, metrics),
|
||||
.right => cursor.char_right(root, metrics),
|
||||
});
|
||||
const cur = char_class(cursor.char_at(root, metrics));
|
||||
if (cur == .eol) return false;
|
||||
return switch (nxt) {
|
||||
.end, .eol => true,
|
||||
.whitespace => cur != .whitespace,
|
||||
else => nxt != cur,
|
||||
};
|
||||
}
|
||||
|
||||
fn move_cursor_prev_word_start(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) error{Stop}!void {
|
||||
var cursor = cursel.cursor;
|
||||
if (is_word_boundary(root, cursor, metrics, .left))
|
||||
cursor.move_left(root, metrics) catch {};
|
||||
|
||||
var sel = Selection.from_cursor_inclusive(&cursor, root, metrics);
|
||||
defer {
|
||||
sel.begin = cursor;
|
||||
cursel.cursor = cursor;
|
||||
cursel.selection = sel;
|
||||
}
|
||||
|
||||
// Consume whitespace
|
||||
while (cursor.test_at(root, is_whitespace, metrics)) {
|
||||
cursor.move_left(root, metrics) catch return;
|
||||
// Stop at beginning of line
|
||||
if (cursor.test_left(root, is_eol, metrics)) return;
|
||||
}
|
||||
|
||||
// Consume word/non-word chars
|
||||
while (!is_word_boundary(root, cursor, metrics, .left)) {
|
||||
cursor.move_left(root, metrics) catch return;
|
||||
// Stop at beginning of line
|
||||
if (cursor.test_left(root, is_eol, metrics)) return;
|
||||
}
|
||||
}
|
||||
|
||||
fn move_cursor_prev_word_start_extend(root: Buffer.Root, cursel: *CurSel, metrics: Buffer.Metrics) error{Stop}!void {
|
||||
var selection = cursel.selection;
|
||||
// check if we already had a selection and extend it
|
||||
defer if (selection) |*pre_sel| {
|
||||
pre_sel.normalize();
|
||||
if (cursel.selection) |*sel| sel.end = pre_sel.end;
|
||||
};
|
||||
try move_cursor_prev_word_start(root, cursel, metrics);
|
||||
}
|
||||
|
||||
fn move_cursels_const_repeat(move: Editor.cursel_operator_const, ctx: Ctx) Result {
|
||||
const ed, const root = get_buf() orelse return;
|
||||
try ed.with_cursels_const_repeat(root, move, ctx);
|
||||
ed.clamp();
|
||||
}
|
||||
|
||||
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
|
||||
|
|
@ -599,9 +592,7 @@ fn match_bracket(root: Buffer.Root, cursel: *CurSel, ctx: command.Context, metri
|
|||
}
|
||||
|
||||
fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direction: Direction) command.Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = try ed.buf_root();
|
||||
const ed, const root = get_buf() orelse return;
|
||||
|
||||
// NOR mode moves n words selecting the last one
|
||||
var repeat: usize = 0;
|
||||
|
|
@ -624,6 +615,8 @@ fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direct
|
|||
sel.begin = sel.end;
|
||||
}
|
||||
cursel.cursor = sel.end;
|
||||
if (direction == .forwards)
|
||||
sel.end.move_right(root, ed.metrics) catch {};
|
||||
cursel.selection = sel;
|
||||
};
|
||||
ed.with_selections_const_repeat(root, move, command.fmt(.{1})) catch {};
|
||||
|
|
@ -631,9 +624,7 @@ fn move_to_word(ctx: command.Context, move: Editor.cursor_operator_const, direct
|
|||
}
|
||||
|
||||
fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const, _: Direction) command.Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = try ed.buf_root();
|
||||
const ed, const root = get_buf() orelse return;
|
||||
|
||||
var repeat: usize = 1;
|
||||
_ = ctx.args.match(.{tp.extract(&repeat)}) catch false;
|
||||
|
|
@ -652,9 +643,7 @@ fn extend_to_word(ctx: command.Context, move: Editor.cursor_operator_const, _: D
|
|||
}
|
||||
|
||||
fn to_char_helix(ctx: command.Context, move: Editor.cursel_operator_mut_once_arg) command.Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const root = ed.buf_root() catch return;
|
||||
const ed, const root = get_buf() orelse return;
|
||||
try ed.with_cursels_const_once_arg(root, move, ctx);
|
||||
ed.clamp();
|
||||
}
|
||||
|
|
@ -1229,8 +1218,9 @@ const pasting_function = @TypeOf(insert_before);
|
|||
const find_char_function = @TypeOf(move_cursor_to_char_left_beyond_eol);
|
||||
|
||||
fn paste_helix(ctx: command.Context, do_paste: pasting_function) command.Result {
|
||||
const mv = tui.mainview() orelse return;
|
||||
const ed = mv.get_active_editor() orelse return;
|
||||
const ed, const b = get_buf_for_update() orelse return;
|
||||
var root = b.root;
|
||||
|
||||
var text_: []const u8 = undefined;
|
||||
|
||||
const clipboard: []const tui.ClipboardEntry = if (ctx.args.buf.len > 0 and try ctx.args.match(.{tp.extract(&text_)}))
|
||||
|
|
@ -1243,9 +1233,6 @@ fn paste_helix(ctx: command.Context, do_paste: pasting_function) command.Result
|
|||
return;
|
||||
}
|
||||
|
||||
const b = try ed.buf_for_update();
|
||||
var root = b.root;
|
||||
|
||||
// Chunks from clipboard are paired to selections
|
||||
// If more selections than chunks in the clipboard, the exceding selections
|
||||
// use the last chunk in the clipboard
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@ const syntax = @import("syntax");
|
|||
|
||||
const Widget = @import("Widget.zig");
|
||||
const MessageFilter = @import("MessageFilter.zig");
|
||||
const MainView = @import("mainview.zig");
|
||||
pub const MainView = @import("mainview.zig");
|
||||
|
||||
// exports for unittesting
|
||||
pub const exports = struct {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue