diff --git a/src/keybind/builtin/flow.json b/src/keybind/builtin/flow.json index f527c3b..c619abc 100644 --- a/src/keybind/builtin/flow.json +++ b/src/keybind/builtin/flow.json @@ -136,9 +136,6 @@ ["alt+u", "to_upper"], ["alt+l", "to_lower"], ["alt+c", "switch_case"], - ["ctrl+_", "underline"], - ["ctrl+=", "underline_with_char", "=", "solid"], - ["ctrl+plus", "underline_with_char", "="], ["alt+b", "move_word_left"], ["alt+f", "move_word_right"], ["alt+s", "filter", "sort"], @@ -440,7 +437,7 @@ ["backspace", "mini_mode_delete_backwards"] ] }, - "mini/get_char": { + "mini/move_to_char": { "press": [ ["ctrl+g", "mini_mode_cancel"], ["ctrl+c", "mini_mode_cancel"], diff --git a/src/tui/editor.zig b/src/tui/editor.zig index 3cf398f..dc89c6a 100644 --- a/src/tui/editor.zig +++ b/src/tui/editor.zig @@ -3632,60 +3632,6 @@ pub const Editor = struct { } pub const dupe_down_meta: Meta = .{ .description = "Duplicate line or selection down/forwards", .arguments = &.{.integer} }; - fn underline_cursel_with_char(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator, ctx: command.Context) error{Stop}!Buffer.Root { - var symbol: []const u8 = undefined; - var mode: enum { solid, space } = .space; - if (!(ctx.args.match(.{tp.extract(&symbol)}) catch return error.Stop) and - !(ctx.args.match(.{ tp.extract(&symbol), tp.extract(&mode) }) catch return error.Stop)) - return error.Stop; - var root = root_; - const sel: Selection = if (cursel.selection) |sel_| sel_ else Selection.line_from_cursor(cursel.cursor, root, self.metrics); - var sfa = std.heap.stackFallback(4096, self.allocator); - const sfa_allocator = sfa.get(); - const text = copy_selection(root, sel, sfa_allocator, self.metrics) catch return error.Stop; - defer sfa_allocator.free(text); - - var underlined: std.Io.Writer.Allocating = .init(sfa_allocator); - defer underlined.deinit(); - - var text_egcs = text; - while (text_egcs.len > 0) { - var colcount: c_int = 1; - const egc_len = self.metrics.egc_length(self.metrics, text_egcs, &colcount, 0); - switch (text_egcs[0]) { - '\t' => underlined.writer.writeAll("\t") catch {}, - '\n' => underlined.writer.writeAll(text_egcs[0..egc_len]) catch {}, - ' ' => underlined.writer.writeAll(switch (mode) { - .space => text_egcs[0..egc_len], - .solid => symbol, - }) catch {}, - else => while (colcount > 0) : (colcount -= 1) underlined.writer.writeAll(symbol) catch {}, - } - text_egcs = text_egcs[egc_len..]; - } - - cursel.cursor = sel.end; - if (cursel.selection) |_| { - cursel.disable_selection(root, self.metrics); - } else { - var test_eof = sel.end; - test_eof.move_right(root, self.metrics) catch { // test for EOF - root = self.insert(root, cursel, "\n", allocator) catch return error.Stop; - }; - } - root = self.insert(root, cursel, underlined.written(), allocator) catch return error.Stop; - cursel.selection = .{ .begin = sel.end, .end = cursel.cursor }; - return root; - } - - pub fn underline_with_char(self: *Self, ctx: Context) Result { - const b = try self.buf_for_update(); - const root = try self.with_cursels_mut_once_arg(b.root, underline_cursel_with_char, b.allocator, ctx); - try self.update_buf(root); - self.clamp(); - } - pub const underline_with_char_meta: Meta = .{ .arguments = &.{.string} }; - fn toggle_cursel_prefix(self: *Self, root_: Buffer.Root, cursel: *CurSel, allocator: Allocator) error{Stop}!Buffer.Root { var root = root_; const saved = cursel.*; diff --git a/src/tui/mode/mini/get_char.zig b/src/tui/mode/mini/get_char.zig deleted file mode 100644 index 379acd4..0000000 --- a/src/tui/mode/mini/get_char.zig +++ /dev/null @@ -1,92 +0,0 @@ -const std = @import("std"); -const tp = @import("thespian"); - -const input = @import("input"); -const key = @import("renderer").input.key; -const mod = @import("renderer").input.modifier; -const event_type = @import("renderer").input.event_type; -const keybind = @import("keybind"); -const command = @import("command"); -const EventHandler = @import("EventHandler"); - -const tui = @import("../../tui.zig"); - -const Allocator = @import("std").mem.Allocator; -const fmt = @import("std").fmt; - -pub fn Create(options: type) type { - return struct { - const Self = @This(); - - const Commands = command.Collection(cmds); - - const ValueType = if (@hasDecl(options, "ValueType")) options.ValueType else void; - - allocator: Allocator, - input: ?ValueType = null, - value: ValueType, - ctx: command.Context, - commands: Commands = undefined, - - pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } { - const self = try allocator.create(Self); - errdefer allocator.destroy(self); - self.* = .{ - .allocator = allocator, - .ctx = .{ .args = try ctx.args.clone(allocator) }, - .value = if (@hasDecl(options, "start")) options.start(self) else {}, - }; - try self.commands.init(self); - var mode = try keybind.mode("mini/get_char", allocator, .{ - .insert_command = "mini_mode_insert_bytes", - }); - mode.event_handler = EventHandler.to_owned(self); - return .{ mode, .{ .name = options.name(self) } }; - } - - pub fn deinit(self: *Self) void { - if (@hasDecl(options, "deinit")) - options.deinit(self); - self.allocator.free(self.ctx.args.buf); - self.commands.deinit(); - self.allocator.destroy(self); - } - - pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { - return false; - } - - const cmds = struct { - pub const Target = Self; - const Ctx = command.Context; - const Meta = command.Metadata; - const Result = command.Result; - - pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { - var code_point: u32 = 0; - if (!try ctx.args.match(.{tp.extract(&code_point)})) - return error.InvalidMoveToCharInsertCodePointArgument; - var buf: [6]u8 = undefined; - const bytes = input.ucs32_to_utf8(&[_]u32{code_point}, &buf) catch return error.InvalidMoveToCharCodePoint; - return options.process_egc(self, buf[0..bytes]); - } - pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; - - pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { - var bytes: []const u8 = undefined; - if (!try ctx.args.match(.{tp.extract(&bytes)})) - return error.InvalidMoveToCharInsertBytesArgument; - const egc = tui.egc_last(bytes); - var buf: [6]u8 = undefined; - @memcpy(buf[0..egc.len], egc); - return options.process_egc(self, buf[0..egc.len]); - } - pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; - - pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { - command.executeName("exit_mini_mode", .{}) catch {}; - } - pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; - }; - }; -} diff --git a/src/tui/mode/mini/move_to_char.zig b/src/tui/mode/mini/move_to_char.zig index e9ef5f7..1d76631 100644 --- a/src/tui/mode/mini/move_to_char.zig +++ b/src/tui/mode/mini/move_to_char.zig @@ -1,17 +1,25 @@ const std = @import("std"); -const cbor = @import("cbor"); +const tp = @import("thespian"); + +const input = @import("input"); +const keybind = @import("keybind"); const command = @import("command"); +const EventHandler = @import("EventHandler"); const tui = @import("../../tui.zig"); -pub const Type = @import("get_char.zig").Create(@This()); -pub const create = Type.create; +const Allocator = @import("std").mem.Allocator; -pub const ValueType = struct { - direction: Direction, - operation_command: []const u8, - operation: Operation, -}; +const Self = @This(); + +const Commands = command.Collection(cmds); + +allocator: Allocator, +key: [6]u8 = undefined, +direction: Direction, +operation_command: []const u8, +operation: Operation, +commands: Commands = undefined, const Direction = enum { left, @@ -24,9 +32,9 @@ const Operation = enum { extend, }; -pub fn start(self: *Type) ValueType { - var operation_command: []const u8 = "move_to_char_left"; - _ = self.ctx.args.match(.{cbor.extract(&operation_command)}) catch {}; +pub fn create(allocator: Allocator, ctx: command.Context) !struct { tui.Mode, tui.MiniMode } { + var operation_command: []const u8 = undefined; + _ = ctx.args.match(.{tp.extract(&operation_command)}) catch return error.InvalidMoveToCharArgument; const direction: Direction = if (std.mem.indexOf(u8, operation_command, "_left")) |_| .left else .right; var operation: Operation = undefined; @@ -42,31 +50,80 @@ pub fn start(self: *Type) ValueType { operation = .move; } - return .{ + const self = try allocator.create(Self); + errdefer allocator.destroy(self); + self.* = .{ + .allocator = allocator, .direction = direction, - .operation_command = operation_command, + .operation_command = try allocator.dupe(u8, operation_command), .operation = operation, }; + try self.commands.init(self); + var mode = try keybind.mode("mini/move_to_char", allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }); + mode.event_handler = EventHandler.to_owned(self); + return .{ mode, .{ .name = self.name() } }; } -pub fn name(self: *Type) []const u8 { - return switch (self.value.operation) { - .move => switch (self.value.direction) { +pub fn deinit(self: *Self) void { + self.commands.deinit(); + self.allocator.free(self.operation_command); + self.allocator.destroy(self); +} + +fn name(self: *Self) []const u8 { + return switch (self.operation) { + .move => switch (self.direction) { .left => "↶ move", .right => "↷ move", }, - .select => switch (self.value.direction) { + .select => switch (self.direction) { .left => "󰒅 ↶ select", .right => "󰒅 ↷ select", }, - .extend => switch (self.value.direction) { + .extend => switch (self.direction) { .left => "󰒅 ↶ extend", .right => "󰒅 ↷ extend", }, }; } -pub fn process_egc(self: *Type, egc: []const u8) command.Result { - try command.executeName(self.value.operation_command, command.fmt(.{egc})); +pub fn receive(_: *Self, _: tp.pid_ref, _: tp.message) error{Exit}!bool { + return false; +} + +fn execute_operation(self: *Self, ctx: command.Context) command.Result { + try command.executeName(self.operation_command, ctx); try command.executeName("exit_mini_mode", .{}); } + +const cmds = struct { + pub const Target = Self; + const Ctx = command.Context; + const Meta = command.Metadata; + const Result = command.Result; + + pub fn mini_mode_insert_code_point(self: *Self, ctx: Ctx) Result { + var code_point: u32 = 0; + if (!try ctx.args.match(.{tp.extract(&code_point)})) + return error.InvalidMoveToCharInsertCodePointArgument; + var buf: [6]u8 = undefined; + const bytes = input.ucs32_to_utf8(&[_]u32{code_point}, &buf) catch return error.InvalidMoveToCharCodePoint; + return self.execute_operation(command.fmt(.{buf[0..bytes]})); + } + pub const mini_mode_insert_code_point_meta: Meta = .{ .arguments = &.{.integer} }; + + pub fn mini_mode_insert_bytes(self: *Self, ctx: Ctx) Result { + var bytes: []const u8 = undefined; + if (!try ctx.args.match(.{tp.extract(&bytes)})) + return error.InvalidMoveToCharInsertBytesArgument; + return self.execute_operation(ctx); + } + pub const mini_mode_insert_bytes_meta: Meta = .{ .arguments = &.{.string} }; + + pub fn mini_mode_cancel(_: *Self, _: Ctx) Result { + command.executeName("exit_mini_mode", .{}) catch {}; + } + pub const mini_mode_cancel_meta: Meta = .{ .description = "Cancel input" }; +}; diff --git a/src/tui/mode/mini/underline.zig b/src/tui/mode/mini/underline.zig deleted file mode 100644 index c55cd23..0000000 --- a/src/tui/mode/mini/underline.zig +++ /dev/null @@ -1,17 +0,0 @@ -const std = @import("std"); -const cbor = @import("cbor"); -const command = @import("command"); - -const tui = @import("../../tui.zig"); - -pub const Type = @import("get_char.zig").Create(@This()); -pub const create = Type.create; - -pub fn name(_: *Type) []const u8 { - return "underline"; -} - -pub fn process_egc(_: *Type, egc: []const u8) command.Result { - try command.executeName("underline_with_char", command.fmt(.{ egc, "solid" })); - try command.executeName("exit_mini_mode", .{}); -} diff --git a/src/tui/tui.zig b/src/tui/tui.zig index ac38613..45063d6 100644 --- a/src/tui/tui.zig +++ b/src/tui/tui.zig @@ -1189,11 +1189,6 @@ const cmds = struct { } pub const replace_meta: Meta = .{ .description = "Replace with character" }; - pub fn underline(self: *Self, ctx: Ctx) Result { - return enter_mini_mode(self, @import("mode/mini/underline.zig"), ctx); - } - pub const underline_meta: Meta = .{ .description = "Underline with character" }; - pub fn open_file(self: *Self, ctx: Ctx) Result { if (get_active_selection(self.allocator)) |text| { defer self.allocator.free(text);