From 524e5be47c81f81e36eb52ca1f49f33c7b0f1c2d Mon Sep 17 00:00:00 2001 From: CJ van den Berg Date: Sat, 16 Nov 2024 02:51:39 +0100 Subject: [PATCH] feat: add flow native keybinding parser --- src/keybind/dynamic/keybind.zig | 352 ++---------------- src/keybind/dynamic/keybindings.json | 538 ++++++++++++++++----------- src/keybind/dynamic/parse_flow.zig | 85 +++++ src/keybind/dynamic/parse_vim.zig | 290 +++++++++++++++ src/tui/mode/mini/file_browser.zig | 4 +- src/tui/mode/mini/find.zig | 4 +- src/tui/mode/mini/find_in_files.zig | 4 +- 7 files changed, 742 insertions(+), 535 deletions(-) create mode 100644 src/keybind/dynamic/parse_flow.zig create mode 100644 src/keybind/dynamic/parse_vim.zig diff --git a/src/keybind/dynamic/keybind.zig b/src/keybind/dynamic/keybind.zig index 4b966f2..f3a609a 100644 --- a/src/keybind/dynamic/keybind.zig +++ b/src/keybind/dynamic/keybind.zig @@ -13,6 +13,8 @@ const command = @import("command"); const EventHandler = @import("EventHandler"); const KeyEvent = @import("KeyEvent.zig"); +const parse_flow = @import("parse_flow.zig"); +const parse_vim = @import("parse_vim.zig"); pub const mode = struct { pub const input = struct { @@ -45,11 +47,21 @@ fn Handler(namespace_name: []const u8, mode_name: []const u8) type { return struct { allocator: std.mem.Allocator, bindings: BindingSet, - pub fn create(allocator: std.mem.Allocator, _: anytype) !EventHandler { + + pub fn create(allocator: std.mem.Allocator, opts: anytype) !EventHandler { const self: *@This() = try allocator.create(@This()); self.* = .{ .allocator = allocator, - .bindings = try BindingSet.init(allocator, @embedFile("keybindings.json"), namespace_name, mode_name), + .bindings = try BindingSet.init( + allocator, + @embedFile("keybindings.json"), + namespace_name, + mode_name, + if (@hasField(@TypeOf(opts), "insert_command")) + opts.insert_command + else + "insert_chars", + ), }; return EventHandler.to_owned(self); } @@ -81,293 +93,6 @@ pub const Mode = struct { pub const KeybindHints = std.static_string_map.StaticStringMap([]const u8); -fn peek(str: []const u8, i: usize) error{OutOfBounds}!u8 { - if (i + 1 < str.len) { - return str[i + 1]; - } else return error.OutOfBounds; -} - -const ParseError = error{ - OutOfMemory, - OutOfBounds, - InvalidEscapeSequenceStart, - InvalidInitialCharacter, - InvalidStartOfControlBinding, - InvalidStartOfShiftBinding, - InvalidStartOfDeleteBinding, - InvalidCRBinding, - InvalidSpaceBinding, - InvalidDeleteBinding, - InvalidTabBinding, - InvalidUpBinding, - InvalidEscapeBinding, - InvalidDownBinding, - InvalidLeftBinding, - InvalidRightBinding, - InvalidFunctionKeyNumber, - InvalidFunctionKeyBinding, - InvalidEscapeSequenceDelimiter, - InvalidModifier, - InvalidEscapeSequenceEnd, -}; - -var parse_error_buf: [256]u8 = undefined; -var parse_error_message: []const u8 = ""; - -fn parse_error_reset() void { - parse_error_message = ""; -} - -fn parse_error(e: ParseError, comptime format: anytype, args: anytype) ParseError { - parse_error_message = std.fmt.bufPrint(&parse_error_buf, format, args) catch "error in parse_error"; - return e; -} - -fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]KeyEvent { - parse_error_reset(); - const State = enum { - base, - escape_sequence_start, - escape_sequence_delimiter, - char_or_key_or_modifier, - modifier, - escape_sequence_end, - function_key, - tab, - space, - del, - cr, - esc, - up, - down, - left, - right, - }; - var state: State = .base; - var function_key_number: u8 = 0; - var modifiers: input.Mods = 0; - var result = std.ArrayList(KeyEvent).init(allocator); - defer result.deinit(); - - var i: usize = 0; - while (i < str.len) { - switch (state) { - .base => { - switch (str[i]) { - '<' => { - state = .escape_sequence_start; - i += 1; - }, - 'a'...'z', '\\', '[', ']', '/', '`', '-', '=', ';', '0'...'9' => { - try result.append(.{ .key = str[i] }); - i += 1; - }, - else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - .escape_sequence_start => { - switch (str[i]) { - 'A' => { - state = .modifier; - }, - 'C' => { - switch (try peek(str, i)) { - 'R' => { - state = .cr; - }, - '-' => { - state = .modifier; - }, - else => return parse_error(error.InvalidStartOfControlBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - 'S' => { - switch (try peek(str, i)) { - '-' => { - state = .modifier; - }, - 'p' => { - state = .space; - }, - else => return parse_error(error.InvalidStartOfShiftBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - 'F' => { - state = .function_key; - i += 1; - }, - 'T' => { - state = .tab; - }, - 'U' => { - state = .up; - }, - 'L' => { - state = .left; - }, - 'R' => { - state = .right; - }, - 'E' => { - state = .esc; - }, - 'D' => { - switch (try peek(str, i)) { - 'o' => { - state = .down; - }, - '-' => { - state = .modifier; - }, - 'e' => { - state = .del; - }, - else => return parse_error(error.InvalidStartOfDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - else => return parse_error(error.InvalidEscapeSequenceStart, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - .cr => { - if (std.mem.indexOf(u8, str[i..], "CR") == 0) { - try result.append(.{ .key = input.key.enter, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 2; - } else return parse_error(error.InvalidCRBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .space => { - if (std.mem.indexOf(u8, str[i..], "Space") == 0) { - try result.append(.{ .key = input.key.space, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 5; - } else return parse_error(error.InvalidSpaceBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .del => { - if (std.mem.indexOf(u8, str[i..], "Del") == 0) { - try result.append(.{ .key = input.key.delete, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 3; - } else return parse_error(error.InvalidDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .tab => { - if (std.mem.indexOf(u8, str[i..], "Tab") == 0) { - try result.append(.{ .key = input.key.tab, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 3; - } else return parse_error(error.InvalidTabBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .up => { - if (std.mem.indexOf(u8, str[i..], "Up") == 0) { - try result.append(.{ .key = input.key.up, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 2; - } else return parse_error(error.InvalidUpBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .esc => { - if (std.mem.indexOf(u8, str[i..], "Esc") == 0) { - try result.append(.{ .key = input.key.escape, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 3; - } else return parse_error(error.InvalidEscapeBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .down => { - if (std.mem.indexOf(u8, str[i..], "Down") == 0) { - try result.append(.{ .key = input.key.down, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 4; - } else return parse_error(error.InvalidDownBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .left => { - if (std.mem.indexOf(u8, str[i..], "Left") == 0) { - try result.append(.{ .key = input.key.left, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 4; - } else return parse_error(error.InvalidLeftBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .right => { - if (std.mem.indexOf(u8, str[i..], "Right") == 0) { - try result.append(.{ .key = input.key.right, .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 5; - } else return parse_error(error.InvalidRightBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); - }, - .function_key => { - switch (str[i]) { - '0'...'9' => { - function_key_number *= 10; - function_key_number += str[i] - '0'; - if (function_key_number < 1 or function_key_number > 35) - return parse_error(error.InvalidFunctionKeyNumber, "function_key_number: {}", .{function_key_number}); - i += 1; - }, - '>' => { - const function_key = input.key.f1 - 1 + function_key_number; - try result.append(.{ .key = function_key, .modifiers = modifiers }); - modifiers = 0; - function_key_number = 0; - state = .base; - i += 1; - }, - else => return parse_error(error.InvalidFunctionKeyBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - .escape_sequence_delimiter => { - switch (str[i]) { - '-' => { - state = .char_or_key_or_modifier; - i += 1; - }, - else => return parse_error(error.InvalidEscapeSequenceDelimiter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - .char_or_key_or_modifier => { - switch (str[i]) { - 'a'...'z', ';', '0'...'9' => { - try result.append(.{ .key = str[i], .modifiers = modifiers }); - modifiers = 0; - state = .escape_sequence_end; - i += 1; - }, - else => { - state = .escape_sequence_start; - }, - } - }, - .modifier => { - modifiers |= switch (str[i]) { - 'A' => input.mod.alt, - 'C' => input.mod.ctrl, - 'D' => input.mod.super, - 'S' => input.mod.shift, - else => return parse_error(error.InvalidModifier, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - }; - - state = .escape_sequence_delimiter; - i += 1; - }, - .escape_sequence_end => { - switch (str[i]) { - '>' => { - state = .base; - i += 1; - }, - else => return parse_error(error.InvalidEscapeSequenceEnd, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), - } - }, - } - } - return result.toOwnedSlice(); -} - //An association of an command with a triggering key chord const Binding = struct { keys: []KeyEvent, @@ -410,6 +135,7 @@ const Hint = struct { const BindingSet = struct { allocator: std.mem.Allocator, bindings: std.ArrayList(Binding), + syntax: KeySyntax = .flow, on_match_failure: OnMatchFailure = .ignore, current_sequence: std.ArrayList(KeyEvent), current_sequence_egc: std.ArrayList(u8), @@ -418,7 +144,9 @@ const BindingSet = struct { logger: log.Logger, namespace_name: []const u8, mode_name: []const u8, + insert_command: []const u8, + const KeySyntax = enum { flow, vim }; const OnMatchFailure = enum { insert, ignore }; fn hints(self: *@This()) ![]const Hint { @@ -442,7 +170,7 @@ const BindingSet = struct { } } - fn init(allocator: std.mem.Allocator, json_string: []const u8, namespace_name: []const u8, mode_name: []const u8) !@This() { + fn init(allocator: std.mem.Allocator, json_string: []const u8, namespace_name: []const u8, mode_name: []const u8, insert_command: []const u8) !@This() { var self: @This() = .{ .allocator = allocator, .current_sequence = try std.ArrayList(KeyEvent).initCapacity(allocator, 16), @@ -453,6 +181,7 @@ const BindingSet = struct { .logger = if (!builtin.is_test) log.logger("keybind") else undefined, .namespace_name = try allocator.dupe(u8, namespace_name), .mode_name = try allocator.dupe(u8, mode_name), + .insert_command = try allocator.dupe(u8, insert_command), }; try self.load_json(json_string, namespace_name, mode_name); return self; @@ -467,6 +196,7 @@ const BindingSet = struct { if (!builtin.is_test) self.logger.deinit(); self.allocator.free(self.namespace_name); self.allocator.free(self.mode_name); + self.allocator.free(self.insert_command); } fn load_json(self: *@This(), json_string: []const u8, namespace_name: []const u8, mode_name: []const u8) !void { @@ -488,10 +218,12 @@ const BindingSet = struct { fn load_set_from_json(self: *BindingSet, mode_bindings: std.json.Value) !void { const JsonConfig = struct { bindings: []const []const []const u8, + syntax: KeySyntax, on_match_failure: OnMatchFailure, }; const parsed = try std.json.parseFromValue(JsonConfig, self.allocator, mode_bindings, .{}); defer parsed.deinit(); + self.syntax = parsed.value.syntax; self.on_match_failure = parsed.value.on_match_failure; for (parsed.value.bindings) |entry| { var state: enum { key_event, command, args } = .key_event; @@ -507,9 +239,15 @@ const BindingSet = struct { for (entry) |token| { switch (state) { .key_event => { - keys = parse_key_events(self.allocator, token) catch |e| { - self.logger.print_err("keybind.load", "ERROR: {s} {s}", .{ @errorName(e), parse_error_message }); - break; + keys = switch (self.syntax) { + .flow => parse_flow.parse_key_events(self.allocator, token) catch |e| { + self.logger.print_err("keybind.load", "ERROR: {s} {s}", .{ @errorName(e), parse_flow.parse_error_message }); + break; + }, + .vim => parse_vim.parse_key_events(self.allocator, token) catch |e| { + self.logger.print_err("keybind.load.vim", "ERROR: {s} {s}", .{ @errorName(e), parse_vim.parse_error_message }); + break; + }, }; state = .command; }, @@ -558,7 +296,7 @@ const BindingSet = struct { if (self.input_buffer.items.len > 0) { defer self.input_buffer.clearRetainingCapacity(); const id = Static.insert_chars_id orelse - command.get_id_cache("insert_chars", &Static.insert_chars_id) orelse { + command.get_id_cache(self.insert_command, &Static.insert_chars_id) orelse { return tp.exit_error(error.InputTargetNotFound, null); }; if (!builtin.is_test) { @@ -622,11 +360,8 @@ const BindingSet = struct { for (self.bindings.items) |*binding| { switch (binding.match(self.current_sequence.items)) { .matched => { - defer { - //clear current sequence if command execution fails - self.current_sequence.clearRetainingCapacity(); - self.current_sequence_egc.clearRetainingCapacity(); - } + self.current_sequence.clearRetainingCapacity(); + self.current_sequence_egc.clearRetainingCapacity(); return binding; }, .match_possible => { @@ -647,16 +382,11 @@ const BindingSet = struct { _ = key_event; if (abort_type == .match_impossible) { switch (self.on_match_failure) { - .insert => { - try self.insert_bytes(self.current_sequence_egc.items); - self.current_sequence_egc.clearRetainingCapacity(); - self.current_sequence.clearRetainingCapacity(); - }, - .ignore => { - self.current_sequence.clearRetainingCapacity(); - self.current_sequence_egc.clearRetainingCapacity(); - }, + .insert => try self.insert_bytes(self.current_sequence_egc.items), + .ignore => {}, } + self.current_sequence.clearRetainingCapacity(); + self.current_sequence_egc.clearRetainingCapacity(); } else if (abort_type == .timeout) { try self.insert_bytes(self.current_sequence_egc.items); self.current_sequence_egc.clearRetainingCapacity(); @@ -718,7 +448,7 @@ const parse_test_cases = .{ test "parse" { const alloc = std.testing.allocator; inline for (parse_test_cases) |case| { - const parsed = try parse_key_events(alloc, case[0]); + const parsed = try parse_vim.parse_key_events(alloc, case[0]); defer alloc.free(parsed); const expected: []const KeyEvent = case[1]; const actual: []const KeyEvent = parsed; @@ -744,10 +474,10 @@ const match_test_cases = .{ test "match" { const alloc = std.testing.allocator; inline for (match_test_cases) |case| { - const events = try parse_key_events(alloc, case[0]); + const events = try parse_vim.parse_key_events(alloc, case[0]); defer alloc.free(events); const binding: Binding = .{ - .keys = try parse_key_events(alloc, case[1]), + .keys = try parse_vim.parse_key_events(alloc, case[1]), .command = undefined, .args = undefined, }; diff --git a/src/keybind/dynamic/keybindings.json b/src/keybind/dynamic/keybindings.json index 7b434db..0d1bf94 100644 --- a/src/keybind/dynamic/keybindings.json +++ b/src/keybind/dynamic/keybindings.json @@ -1,171 +1,173 @@ { "flow": { "normal": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ - ["", "open_recent"], - ["", "open_recent_project"], - ["", "toggle_panel"], - ["", "undo"], - ["", "redo"], - ["", "quit"], - ["", "open_file"], - ["", "close_file"], - ["", "save_file"], - ["", "cmd_cycle3", "scroll_view_center", "scroll_view_top", "scroll_view_bottom"], - ["", "goto_next_match"], - ["", "goto_prev_match"], - ["", "move_to_char", "false"], - ["", "move_to_char", "true"], - ["", "cut"], - ["", "copy"], - ["", "system_paste"], - ["", "pop_cursor"], - ["", "delete_to_begin"], - ["", "delete_to_end"], - ["", "move_cursor_next_match"], - ["", "change_theme"], - ["", "hover"], - ["", "find"], - ["", "goto"], - ["", "add_cursor_next_match"], - ["", "select_all"], - ["", "insert_bytes", "\t"], - ["", "toggle_comment"], - ["", "smart_insert_line_after"], - ["", "completion"], - ["", "move_buffer_end"], - ["", "move_buffer_begin"], - ["", "move_scroll_up"], - ["", "move_scroll_down"], - ["", "move_scroll_page_up"], - ["", "move_scroll_page_down"], - ["", "move_word_left"], - ["", "move_word_right"], - ["", "delete_word_left"], - ["", "delete_word_right"], - ["", "toggle_inspector_view"], - ["", "toggle_whitespace_mode"], - ["", "goto_implementation"], + ["ctrl+e", "open_recent"], + ["ctrl+r", "open_recent_project"], + ["ctrl+j", "toggle_panel"], + ["ctrl+z", "undo"], + ["ctrl+y", "redo"], + ["ctrl+q", "quit"], + ["ctrl+o", "open_file"], + ["ctrl+w", "close_file"], + ["ctrl+s", "save_file"], + ["ctrl+l", "cmd_cycle3", "scroll_view_center", "scroll_view_top", "scroll_view_bottom"], + ["ctrl+n", "goto_next_match"], + ["ctrl+p", "goto_prev_match"], + ["ctrl+b", "move_to_char", false], + ["ctrl+t", "move_to_char", true], + ["ctrl+x", "cut"], + ["ctrl+c", "copy"], + ["ctrl+v", "system_paste"], + ["ctrl+u", "pop_cursor"], + ["ctrl+k>ctrl+u", "delete_to_begin"], + ["ctrl+k>ctrl+k", "delete_to_end"], + ["ctrl+k>ctrl+d", "move_cursor_next_match"], + ["ctrl+k>ctrl+t", "change_theme"], + ["ctrl+k>ctrl+i", "hover"], + ["ctrl+f", "find"], + ["ctrl+g", "goto"], + ["ctrl+d", "add_cursor_next_match"], + ["ctrl+a", "select_all"], + ["ctrl+i", "insert_bytes", "\t"], + ["ctrl+/", "toggle_comment"], + ["ctrl+enter", "smart_insert_line_after"], + ["ctrl+space", "completion"], + ["ctrl+end", "move_buffer_end"], + ["ctrl+home", "move_buffer_begin"], + ["ctrl+up", "move_scroll_up"], + ["ctrl+down", "move_scroll_down"], + ["ctrl+page_up", "move_scroll_page_up"], + ["ctrl+page_down", "move_scroll_page_down"], + ["ctrl+left", "move_word_left"], + ["ctrl+right", "move_word_right"], + ["ctrl+backspace", "delete_word_left"], + ["ctrl+delete", "delete_word_right"], + ["ctrl+f5", "toggle_inspector_view"], + ["ctrl+f10", "toggle_whitespace_mode"], + ["ctrl+f12", "goto_implementation"], - ["", "save_as"], - ["", "open_command_palette"], - ["", "dupe_down"], - ["", "redo"], - ["", "quit_without_saving"], - ["", "close_file_without_saving"], - ["", "find_in_files"], - ["", "add_cursor_all_matches_async"], - ["", "toggle_inspector_view_async"], - ["", "show_diagnostics"], - ["", "smart_insert_line_before"], - ["", "select_buffer_end"], - ["", "select_buffer_begin"], - ["", "select_scroll_up"], - ["", "select_scroll_down"], - ["", "select_word_left"], - ["", "select_word_right"], - ["", "selections_reverse"], + ["ctrl+shift+s", "save_as"], + ["ctrl+shift+p", "open_command_palette"], + ["ctrl+shift+d", "dupe_down"], + ["ctrl+shift+z", "redo"], + ["ctrl+shift+q", "quit_without_saving"], + ["ctrl+shift+w", "close_file_without_saving"], + ["ctrl+shift+f", "find_in_files"], + ["ctrl+shift+l", "add_cursor_all_matches_async"], + ["ctrl+shift+i", "toggle_inspector_view_async"], + ["ctrl+shift+m", "show_diagnostics"], + ["ctrl+shift+enter", "smart_insert_line_before"], + ["ctrl+shift+end", "select_buffer_end"], + ["ctrl+shift+home", "select_buffer_begin"], + ["ctrl+shift+up", "select_scroll_up"], + ["ctrl+shift+down", "select_scroll_down"], + ["ctrl+shift+left", "select_word_left"], + ["ctrl+shift+right", "select_word_right"], + ["ctrl+shift+space", "selections_reverse"], - ["", "open_previous_file"], - ["", "join_next_line"], - ["", "goto_next_file_or_diagnostic"], - ["", "goto_prev_file_or_diagnostic"], - ["", "to_upper"], - ["", "to_lower"], - ["", "switch_case"], - ["", "toggle_inputview"], - ["", "move_word_left"], - ["", "move_word_right"], - ["", "filter", "sort"], - ["", "paste"], - ["", "open_command_palette"], - ["", "jump_back"], - ["", "jump_forward"], - ["", "pull_up"], - ["", "pull_down"], - ["", "insert_line"], - ["", "gutter_mode_next"], - ["", "goto_declaration"], + ["alt+o", "open_previous_file"], + ["alt+j", "join_next_line"], + ["alt+n", "goto_next_file_or_diagnostic"], + ["alt+p", "goto_prev_file_or_diagnostic"], + ["alt+u", "to_upper"], + ["alt+l", "to_lower"], + ["alt+c", "switch_case"], + ["alt+i", "toggle_inputview"], + ["alt+b", "move_word_left"], + ["alt+f", "move_word_right"], + ["alt+s", "filter", "sort"], + ["alt+v", "paste"], + ["alt+x", "open_command_palette"], + ["alt+left", "jump_back"], + ["alt+right", "jump_forward"], + ["alt+up", "pull_up"], + ["alt+down", "pull_down"], + ["alt+enter", "insert_line"], + ["alt+f10", "gutter_mode_next"], + ["alt+f12", "goto_declaration"], - ["", "open_command_palette"], - ["", "dupe_up"], - ["", "format"], - ["", "filter", "sort", "-u"], - ["", "paste"], - ["", "add_cursors_to_line_ends"], - ["", "shrink_selection"], - ["", "expand_selection"], - ["", "move_scroll_left"], - ["", "move_scroll_right"], - ["", "add_cursor_up"], - ["", "add_cursor_down"], - ["", "goto_type_definition"], + ["alt+shift+p", "open_command_palette"], + ["alt+shift+d", "dupe_up"], + ["alt+shift+f", "format"], + ["alt+shift+s", "filter", "sort", "-u"], + ["alt+shift+v", "paste"], + ["alt+shift+i", "add_cursors_to_line_ends"], + ["alt+shift+left", "shrink_selection"], + ["alt+shift+right", "expand_selection"], + ["alt+shift+home", "move_scroll_left"], + ["alt+shift+end", "move_scroll_right"], + ["alt+shift+up", "add_cursor_up"], + ["alt+shift+down", "add_cursor_down"], + ["alt+shift+f12", "goto_type_definition"], - ["", "goto_prev_match"], - ["", "toggle_syntax_highlighting"], - ["", "references"], - ["", "select_left"], - ["", "select_right"], - ["", "select_up"], - ["", "select_down"], - ["", "smart_select_begin"], - ["", "select_end"], - ["", "select_page_up"], - ["", "select_page_down"], - ["", "smart_insert_line_before"], - ["", "delete_backward"], - ["", "unindent"], + ["shift+f3", "goto_prev_match"], + ["shift+f10", "toggle_syntax_highlighting"], + ["shift+f12", "references"], + ["shift+left", "select_left"], + ["shift+right", "select_right"], + ["shift+up", "select_up"], + ["shift+down", "select_down"], + ["shift+home", "smart_select_begin"], + ["shift+end", "select_end"], + ["shift+page_up", "select_page_up"], + ["shift+page_down", "select_page_down"], + ["shift+enter", "smart_insert_line_before"], + ["shift+backspace", "delete_backward"], + ["shift+tab", "unindent"], - ["", "toggle_input_mode"], - ["", "goto_next_match"], - ["", "goto_prev_match"], - ["", "toggle_inspector_view"], - ["", "dump_current_line_tree"], - ["", "dump_current_line"], - ["", "theme_prev"], - ["", "theme_next"], - ["", "toggle_panel"], - ["", "goto_definition"], - ["", "toggle_whitespace_mode"], - ["", "cancel"], - ["", "smart_insert_line"], - ["", "delete_forward"], - ["", "delete_backward"], - ["", "move_left"], - ["", "move_right"], - ["", "move_up"], - ["", "move_down"], - ["", "smart_move_begin"], - ["", "move_end"], - ["", "move_page_up"], - ["", "move_page_down"], - ["", "indent"] + ["f2", "toggle_input_mode"], + ["f3", "goto_next_match"], + ["f15", "goto_prev_match"], + ["f5", "toggle_inspector_view"], + ["f6", "dump_current_line_tree"], + ["f7", "dump_current_line"], + ["f9", "theme_prev"], + ["f10", "theme_next"], + ["f11", "toggle_panel"], + ["f12", "goto_definition"], + ["f34", "toggle_whitespace_mode"], + ["escape", "cancel"], + ["enter", "smart_insert_line"], + ["delete", "delete_forward"], + ["backspace", "delete_backward"], + ["left", "move_left"], + ["right", "move_right"], + ["up", "move_up"], + ["down", "move_down"], + ["home", "smart_move_begin"], + ["end", "move_end"], + ["page_up", "move_page_up"], + ["page_down", "move_page_down"], + ["tab", "indent"] ] }, "home": { + "syntax": "flow", "on_match_failure": "ignore", "bindings": [ - ["", "home_sheeran"], - ["", "toggle_panel"], - ["", "quit"], - ["", "quit"], - ["", "open_file"], - ["", "open_recent"], - ["", "open_recent_project"], - ["", "open_command_palette"], - ["", "change_theme"], - ["", "open_command_palette"], - ["", "quit_without_saving"], - ["", "restart"], - ["", "find_in_files"], - ["", "toggle_panel"], - ["", "open_command_palette"], - ["", "goto_next_file_or_diagnostic"], - ["", "goto_prev_file_or_diagnostic"], - ["", "toggle_panel"], - ["", "toggle_inputview"], - ["", "open_command_palette"], + ["ctrl+f>ctrl+f>ctrl+f>ctrl+f>ctrl+f", "home_sheeran"], + ["ctrl+j", "toggle_panel"], + ["ctrl+q", "quit"], + ["ctrl+w", "quit"], + ["ctrl+o", "open_file"], + ["ctrl+e", "open_recent"], + ["ctrl+r", "open_recent_project"], + ["ctrl+p", "open_command_palette"], + ["ctrl+k>ctrl+t", "change_theme"], + ["ctrl+shift+p", "open_command_palette"], + ["ctrl+shift+q", "quit_without_saving"], + ["ctrl+shift+r", "restart"], + ["ctrl+shift+f", "find_in_files"], + ["ctrl+shift+l", "toggle_panel"], + ["alt+shift+p", "open_command_palette"], + ["alt+n", "goto_next_file_or_diagnostic"], + ["alt+p", "goto_prev_file_or_diagnostic"], + ["alt+l", "toggle_panel"], + ["alt+i", "toggle_inputview"], + ["alt+x", "open_command_palette"], ["h", "open_help"], ["o", "open_file"], ["e", "open_recent"], @@ -174,114 +176,206 @@ ["c", "open_config"], ["t", "change_theme"], ["q", "quit"], - ["", "open_help"], - ["", "open_config"], - ["", "theme_prev"], - ["", "theme_next"], - ["", "toggle_panel"], - ["", "toggle_inputview"], - ["", "home_menu_up"], - ["", "home_menu_down"], - ["", "home_menu_activate"] + ["f1", "open_help"], + ["f6", "open_config"], + ["f9", "theme_prev"], + ["f10", "theme_next"], + ["f11", "toggle_panel"], + ["f12", "toggle_inputview"], + ["up", "home_menu_up"], + ["down", "home_menu_down"], + ["enter", "home_menu_activate"] ] }, "palette": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ - ["", "toggle_panel"], - ["", "quit"], - ["", "close_file"], - ["", "palette_menu_up"], - ["", "palette_menu_down"], - ["", "palette_menu_down"], - ["", "palette_menu_down"], - ["", "palette_menu_down"], - ["", "system_paste"], - ["", "palette_menu_cancel"], - ["", "palette_menu_cancel"], - ["", "palette_menu_cancel"], - ["", "palette_menu_up"], - ["", "palette_menu_down"], - ["", "palette_menu_pageup"], - ["", "palette_menu_pagedown"], - ["", "palette_menu_activate"], - ["", "overlay_delete_word_left"], + ["ctrl+j", "toggle_panel"], + ["ctrl+q", "quit"], + ["ctrl+w", "close_file"], + ["ctrl+p", "palette_menu_up"], + ["ctrl+n", "palette_menu_down"], + ["ctrl+e", "palette_menu_down"], + ["ctrl+r", "palette_menu_down"], + ["ctrl+t", "palette_menu_down"], + ["ctrl+v", "system_paste"], + ["ctrl+c", "palette_menu_cancel"], + ["ctrl+g", "palette_menu_cancel"], + ["ctrl+escape", "palette_menu_cancel"], + ["ctrl+up", "palette_menu_up"], + ["ctrl+down", "palette_menu_down"], + ["ctrl+page_up", "palette_menu_pageup"], + ["ctrl+page_down", "palette_menu_pagedown"], + ["ctrl+enter", "palette_menu_activate"], + ["ctrl+backspace", "overlay_delete_word_left"], - ["", "palette_menu_up"], - ["", "palette_menu_up"], - ["", "palette_menu_down"], - ["", "quit_without_saving"], - ["", "close_file_without_saving"], - ["", "overlay_toggle_panel"], - ["", "overlay_toggle_inputview"], + ["ctrl+shift+e", "palette_menu_up"], + ["ctrl+shift+r", "palette_menu_up"], + ["ctrl+shift+p", "palette_menu_down"], + ["ctrl+shift+q", "quit_without_saving"], + ["ctrl+shift+w", "close_file_without_saving"], + ["ctrl+shift+l", "overlay_toggle_panel"], + ["ctrl+shift+i", "overlay_toggle_inputview"], - ["", "palette_menu_down"], + ["alt+shift+p", "palette_menu_down"], - ["", "palette_menu_up"], - ["", "toggle_panel"], - ["", "toggle_inputview"], + ["alt+p", "palette_menu_up"], + ["alt+l", "toggle_panel"], + ["alt+i", "toggle_inputview"], - ["", "theme_prev"], - ["", "theme_next"], - ["", "toggle_panel"], - ["", "toggle_inputview"], - ["", "palette_menu_cancel"], - ["", "palette_menu_up"], - ["", "palette_menu_down"], - ["", "palette_menu_pageup"], - ["", "palette_menu_pagedown"], - ["", "palette_menu_activate"], - ["", "overlay_delete_backwards"] + ["f9", "theme_prev"], + ["f10", "theme_next"], + ["f11", "toggle_panel"], + ["f12", "toggle_inputview"], + ["escape", "palette_menu_cancel"], + ["up", "palette_menu_up"], + ["down", "palette_menu_down"], + ["page_up", "palette_menu_pageup"], + ["page_down", "palette_menu_pagedown"], + ["enter", "palette_menu_activate"], + ["backspace", "overlay_delete_backwards"] ] }, "mini/goto": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ - ["", "quit"], - ["", "mini_mode_reset"], - ["", "mini_mode_cancel"], - ["", "mini_mode_cancel"], - ["", "scroll_view_center"], - ["", "mini_mode_cancel"], + ["ctrl+q", "quit"], + ["ctrl+u", "mini_mode_reset"], + ["ctrl+g", "mini_mode_cancel"], + ["ctrl+c", "mini_mode_cancel"], + ["ctrl+l", "scroll_view_center"], + ["ctrl+space", "mini_mode_cancel"], - ["", "mini_mode_cancel"], - ["", "exit_mini_mode"], - ["", "mini_mode_delete_backwards"], - ["0", "mini_mode_insert_code_point", "0"], - ["1", "mini_mode_insert_code_point", "1"], - ["2", "mini_mode_insert_code_point", "2"], - ["3", "mini_mode_insert_code_point", "3"], - ["4", "mini_mode_insert_code_point", "4"], - ["5", "mini_mode_insert_code_point", "5"], - ["6", "mini_mode_insert_code_point", "6"], - ["7", "mini_mode_insert_code_point", "7"], - ["8", "mini_mode_insert_code_point", "8"], - ["9", "mini_mode_insert_code_point", "9"] + ["escape", "mini_mode_cancel"], + ["enter", "exit_mini_mode"], + ["backspace", "mini_mode_delete_backwards"], + ["0", "mini_mode_insert_code_point", 0], + ["1", "mini_mode_insert_code_point", 1], + ["2", "mini_mode_insert_code_point", 2], + ["3", "mini_mode_insert_code_point", 3], + ["4", "mini_mode_insert_code_point", 4], + ["5", "mini_mode_insert_code_point", 5], + ["6", "mini_mode_insert_code_point", 6], + ["7", "mini_mode_insert_code_point", 7], + ["8", "mini_mode_insert_code_point", 8], + ["9", "mini_mode_insert_code_point", 9] ] }, "mini/move_to_char": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ ] }, "mini/file_browser": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ + ["ctrl+q", "quit"], + ["ctrl+v", "system_paste"], + ["ctrl+u", "mini_mode_reset"], + ["ctrl+g", "mini_mode_cancel"], + ["ctrl+c", "mini_mode_cancel"], + ["ctrl+l", "scroll_view_center"], + ["ctrl+i", "mini_mode_insert_bytes", "\t"], + ["ctrl+space", "mini_mode_cancel"], + ["ctrl+backspace", "mini_mode_delete_to_previous_path_segment"], + + ["alt+v", "system_paste"], + ["alt+shift+v", "system_paste"], + + ["shift+tab", "mini_mode_reverse_complete_file"], + + ["up", "mini_mode_reverse_complete_file"], + ["down", "mini_mode_try_complete_file"], + ["right", "mini_mode_try_complete_file_forward"], + ["left", "mini_mode_delete_to_previous_path_segment"], + ["tab", "mini_mode_try_complete_file"], + ["escape", "mini_mode_cancel"], + ["enter", "mini_mode_select"], + ["backspace", "mini_mode_delete_backwards"] ] }, "mini/find_in_files": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ + ["ctrl+q", "quit"], + ["ctrl+v", "system_paste"], + ["ctrl+u", "mini_mode_reset"], + ["ctrl+g", "exit_mini_mode"], + ["ctrl+c", "exit_mini_mode"], + ["ctrl+l", "scroll_view_center"], + ["ctrl+f", "goto_next_match"], + ["ctrl+n", "goto_next_match"], + ["ctrl+p", "goto_prev_match"], + ["ctrl+i", "mini_mode_insert_bytes", "\t"], + ["ctrl+space", "exit_mini_mode"], + ["ctrl+enter", "mini_mode_insert_bytes", "\n"], + ["ctrl+backspace", "mini_mode_reset"], + + ["alt+shift+v", "system_paste"], + ["alt+v", "system_paste"], + ["alt+n", "goto_next_file"], + ["alt+p", "goto_prev_file"], + + ["shift+enter", "goto_prev_match"], + ["shift+f3", "goto_prev_match"], + + ["up", "select_prev_file"], + ["down", "select_next_file"], + ["f3", "goto_next_match"], + ["f15", "goto_prev_match"], + ["f9", "theme_prev"], + ["f10", "theme_next"], + ["escape", "exit_mini_mode"], + ["enter", "mini_mode_select"], + ["backspace", "mini_mode_delete_backwards"] ] }, "mini/find": { + "syntax": "flow", "on_match_failure": "insert", "bindings": [ + ["ctrl+q", "quit"], + ["ctrl+v", "system_paste"], + ["ctrl+u", "mini_mode_reset"], + ["ctrl+g", "mini_mode_cancel"], + ["ctrl+c", "mini_mode_cancel"], + ["ctrl+l", "scroll_view_center"], + ["ctrl+f", "goto_next_match"], + ["ctrl+n", "goto_next_match"], + ["ctrl+p", "goto_prev_match"], + ["ctrl+i", "mini_mode_insert_bytes", "\t"], + ["ctrl+space", "mini_mode_cancel"], + ["ctrl+enter", "mini_mode_insert_bytes", "\n"], + ["ctrl+backspace", "mini_mode_reset"], + + ["alt+shift+v", "system_paste"], + ["alt+v", "system_paste"], + ["alt+n", "goto_next_match"], + ["alt+p", "goto_prev_match"], + + ["shift+enter", "goto_prev_match"], + ["shift+f3", "goto_prev_match"], + + ["up", "mini_mode_history_prev"], + ["down", "mini_mode_history_next"], + ["f3", "goto_next_match"], + ["f15", "goto_prev_match"], + ["f9", "theme_prev"], + ["f10", "theme_next"], + ["escape", "mini_mode_cancel"], + ["enter", "mini_mode_select"], + ["backspace", "mini_mode_delete_backwards"] ] } }, "vim": { "normal": { + "syntax": "vim", "on_match_failure": "ignore", "bindings": [ ["j", "move_down"], @@ -319,6 +413,7 @@ ] }, "insert": { + "syntax": "vim", "on_match_failure": "insert", "bindings": [ ["jk", "enter_mode", "normal"], @@ -328,6 +423,7 @@ }, "emacs" : { "base": { + "syntax": "vim", "on_match_failure": "insert", "bindings": [ ["", "cursor_line_start"], diff --git a/src/keybind/dynamic/parse_flow.zig b/src/keybind/dynamic/parse_flow.zig new file mode 100644 index 0000000..4b5f8c0 --- /dev/null +++ b/src/keybind/dynamic/parse_flow.zig @@ -0,0 +1,85 @@ +const std = @import("std"); +const input = @import("input"); +const KeyEvent = @import("KeyEvent.zig"); + +const ParseError = error{ + OutOfMemory, + InvalidFormat, +}; + +var parse_error_buf: [256]u8 = undefined; +pub var parse_error_message: []const u8 = ""; + +fn parse_error_reset() void { + parse_error_message = ""; +} + +fn parse_error(comptime format: anytype, args: anytype) ParseError { + parse_error_message = std.fmt.bufPrint(&parse_error_buf, format, args) catch "error in parse_error"; + return error.InvalidFormat; +} + +pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]KeyEvent { + parse_error_reset(); + if (str.len == 0) return parse_error("empty", .{}); + var result_events = std.ArrayList(KeyEvent).init(allocator); + var iter_events = std.mem.tokenizeScalar(u8, str, '>'); + while (iter_events.next()) |event| { + var key: ?input.Key = null; + var mods = input.ModSet{}; + var iter = std.mem.tokenizeScalar(u8, event, '+'); + loop: while (iter.next()) |part| { + if (part.len == 0) return parse_error("empty part in '{s}'", .{str}); + const modsInfo = @typeInfo(input.ModSet).Struct; + inline for (modsInfo.fields) |field| { + if (std.mem.eql(u8, part, field.name)) { + if (@field(mods, field.name)) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str }); + @field(mods, field.name) = true; + continue :loop; + } + } + const alias_mods = .{ + .{ "cmd", "super" }, + .{ "command", "super" }, + .{ "opt", "alt" }, + .{ "option", "alt" }, + .{ "control", "ctrl" }, + }; + inline for (alias_mods) |pair| { + if (std.mem.eql(u8, part, pair[0])) { + if (@field(mods, pair[1])) return parse_error("duplicate modifier '{s}' in '{s}'", .{ part, str }); + @field(mods, pair[1]) = true; + continue :loop; + } + } + + if (key != null) return parse_error("multiple keys in '{s}'", .{str}); + key = input.key.name_map.get(part); + if (key == null) key = name_map.get(part); + if (key == null) unicode: { + const view = std.unicode.Utf8View.init(part) catch break :unicode; + var it = view.iterator(); + const cp = it.nextCodepoint() orelse break :unicode; + if (it.nextCodepoint() != null) break :unicode; + key = cp; + } + if (key == null) return parse_error("unknown key '{s}' in '{s}'", .{ part, str }); + } + if (key) |k| + try result_events.append(.{ .key = k, .modifiers = @bitCast(mods) }) + else + return parse_error("no key defined in '{s}'", .{str}); + } + return result_events.toOwnedSlice(); +} + +pub const name_map = blk: { + @setEvalBranchQuota(2000); + break :blk std.StaticStringMap(u21).initComptime(.{ + .{ "tab", input.key.tab }, + .{ "enter", input.key.enter }, + .{ "escape", input.key.escape }, + .{ "space", input.key.space }, + .{ "backspace", input.key.backspace }, + }); +}; diff --git a/src/keybind/dynamic/parse_vim.zig b/src/keybind/dynamic/parse_vim.zig new file mode 100644 index 0000000..3298c48 --- /dev/null +++ b/src/keybind/dynamic/parse_vim.zig @@ -0,0 +1,290 @@ +const std = @import("std"); +const input = @import("input"); +const KeyEvent = @import("KeyEvent.zig"); + +fn peek(str: []const u8, i: usize) error{OutOfBounds}!u8 { + if (i + 1 < str.len) { + return str[i + 1]; + } else return error.OutOfBounds; +} + +const ParseError = error{ + OutOfMemory, + OutOfBounds, + InvalidEscapeSequenceStart, + InvalidInitialCharacter, + InvalidStartOfControlBinding, + InvalidStartOfShiftBinding, + InvalidStartOfDeleteBinding, + InvalidCRBinding, + InvalidSpaceBinding, + InvalidDeleteBinding, + InvalidTabBinding, + InvalidUpBinding, + InvalidEscapeBinding, + InvalidDownBinding, + InvalidLeftBinding, + InvalidRightBinding, + InvalidFunctionKeyNumber, + InvalidFunctionKeyBinding, + InvalidEscapeSequenceDelimiter, + InvalidModifier, + InvalidEscapeSequenceEnd, +}; + +var parse_error_buf: [256]u8 = undefined; +pub var parse_error_message: []const u8 = ""; + +fn parse_error_reset() void { + parse_error_message = ""; +} + +fn parse_error(e: ParseError, comptime format: anytype, args: anytype) ParseError { + parse_error_message = std.fmt.bufPrint(&parse_error_buf, format, args) catch "error in parse_error"; + return e; +} + +pub fn parse_key_events(allocator: std.mem.Allocator, str: []const u8) ParseError![]KeyEvent { + parse_error_reset(); + const State = enum { + base, + escape_sequence_start, + escape_sequence_delimiter, + char_or_key_or_modifier, + modifier, + escape_sequence_end, + function_key, + tab, + space, + del, + cr, + esc, + up, + down, + left, + right, + }; + var state: State = .base; + var function_key_number: u8 = 0; + var modifiers: input.Mods = 0; + var result = std.ArrayList(KeyEvent).init(allocator); + defer result.deinit(); + + var i: usize = 0; + while (i < str.len) { + switch (state) { + .base => { + switch (str[i]) { + '<' => { + state = .escape_sequence_start; + i += 1; + }, + 'a'...'z', '\\', '[', ']', '/', '`', '-', '=', ';', '0'...'9' => { + try result.append(.{ .key = str[i] }); + i += 1; + }, + else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + .escape_sequence_start => { + switch (str[i]) { + 'A' => { + state = .modifier; + }, + 'C' => { + switch (try peek(str, i)) { + 'R' => { + state = .cr; + }, + '-' => { + state = .modifier; + }, + else => return parse_error(error.InvalidStartOfControlBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + 'S' => { + switch (try peek(str, i)) { + '-' => { + state = .modifier; + }, + 'p' => { + state = .space; + }, + else => return parse_error(error.InvalidStartOfShiftBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + 'F' => { + state = .function_key; + i += 1; + }, + 'T' => { + state = .tab; + }, + 'U' => { + state = .up; + }, + 'L' => { + state = .left; + }, + 'R' => { + state = .right; + }, + 'E' => { + state = .esc; + }, + 'D' => { + switch (try peek(str, i)) { + 'o' => { + state = .down; + }, + '-' => { + state = .modifier; + }, + 'e' => { + state = .del; + }, + else => return parse_error(error.InvalidStartOfDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + else => return parse_error(error.InvalidEscapeSequenceStart, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + .cr => { + if (std.mem.indexOf(u8, str[i..], "CR") == 0) { + try result.append(.{ .key = input.key.enter, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 2; + } else return parse_error(error.InvalidCRBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .space => { + if (std.mem.indexOf(u8, str[i..], "Space") == 0) { + try result.append(.{ .key = input.key.space, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 5; + } else return parse_error(error.InvalidSpaceBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .del => { + if (std.mem.indexOf(u8, str[i..], "Del") == 0) { + try result.append(.{ .key = input.key.delete, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 3; + } else return parse_error(error.InvalidDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .tab => { + if (std.mem.indexOf(u8, str[i..], "Tab") == 0) { + try result.append(.{ .key = input.key.tab, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 3; + } else return parse_error(error.InvalidTabBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .up => { + if (std.mem.indexOf(u8, str[i..], "Up") == 0) { + try result.append(.{ .key = input.key.up, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 2; + } else return parse_error(error.InvalidUpBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .esc => { + if (std.mem.indexOf(u8, str[i..], "Esc") == 0) { + try result.append(.{ .key = input.key.escape, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 3; + } else return parse_error(error.InvalidEscapeBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .down => { + if (std.mem.indexOf(u8, str[i..], "Down") == 0) { + try result.append(.{ .key = input.key.down, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 4; + } else return parse_error(error.InvalidDownBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .left => { + if (std.mem.indexOf(u8, str[i..], "Left") == 0) { + try result.append(.{ .key = input.key.left, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 4; + } else return parse_error(error.InvalidLeftBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .right => { + if (std.mem.indexOf(u8, str[i..], "Right") == 0) { + try result.append(.{ .key = input.key.right, .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 5; + } else return parse_error(error.InvalidRightBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); + }, + .function_key => { + switch (str[i]) { + '0'...'9' => { + function_key_number *= 10; + function_key_number += str[i] - '0'; + if (function_key_number < 1 or function_key_number > 35) + return parse_error(error.InvalidFunctionKeyNumber, "function_key_number: {}", .{function_key_number}); + i += 1; + }, + '>' => { + const function_key = input.key.f1 - 1 + function_key_number; + try result.append(.{ .key = function_key, .modifiers = modifiers }); + modifiers = 0; + function_key_number = 0; + state = .base; + i += 1; + }, + else => return parse_error(error.InvalidFunctionKeyBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + .escape_sequence_delimiter => { + switch (str[i]) { + '-' => { + state = .char_or_key_or_modifier; + i += 1; + }, + else => return parse_error(error.InvalidEscapeSequenceDelimiter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + .char_or_key_or_modifier => { + switch (str[i]) { + 'a'...'z', ';', '0'...'9' => { + try result.append(.{ .key = str[i], .modifiers = modifiers }); + modifiers = 0; + state = .escape_sequence_end; + i += 1; + }, + else => { + state = .escape_sequence_start; + }, + } + }, + .modifier => { + modifiers |= switch (str[i]) { + 'A' => input.mod.alt, + 'C' => input.mod.ctrl, + 'D' => input.mod.super, + 'S' => input.mod.shift, + else => return parse_error(error.InvalidModifier, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + }; + + state = .escape_sequence_delimiter; + i += 1; + }, + .escape_sequence_end => { + switch (str[i]) { + '>' => { + state = .base; + i += 1; + }, + else => return parse_error(error.InvalidEscapeSequenceEnd, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), + } + }, + } + } + return result.toOwnedSlice(); +} diff --git a/src/tui/mode/mini/file_browser.zig b/src/tui/mode/mini/file_browser.zig index 57daef3..e7003b2 100644 --- a/src/tui/mode/mini/file_browser.zig +++ b/src/tui/mode/mini/file_browser.zig @@ -50,7 +50,9 @@ pub fn Create(options: type) type { options.restore_state(self) catch {}; return .{ .{ - .input_handler = try keybind.mode.mini.file_browser.create(allocator, .{}), + .input_handler = try keybind.mode.mini.file_browser.create(allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }), .event_handler = EventHandler.to_owned(self), }, .{ diff --git a/src/tui/mode/mini/find.zig b/src/tui/mode/mini/find.zig index 596c628..0906d5f 100644 --- a/src/tui/mode/mini/find.zig +++ b/src/tui/mode/mini/find.zig @@ -46,7 +46,9 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui. } return .{ .{ - .input_handler = try keybind.mode.mini.find.create(allocator, .{}), + .input_handler = try keybind.mode.mini.find.create(allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }), .event_handler = EventHandler.to_owned(self), }, .{ diff --git a/src/tui/mode/mini/find_in_files.zig b/src/tui/mode/mini/find_in_files.zig index 321fc50..500fdef 100644 --- a/src/tui/mode/mini/find_in_files.zig +++ b/src/tui/mode/mini/find_in_files.zig @@ -40,7 +40,9 @@ pub fn create(allocator: Allocator, _: command.Context) !struct { tui.Mode, tui. }; return .{ .{ - .input_handler = try keybind.mode.mini.find_in_files.create(allocator, .{}), + .input_handler = try keybind.mode.mini.find_in_files.create(allocator, .{ + .insert_command = "mini_mode_insert_bytes", + }), .event_handler = EventHandler.to_owned(self), }, .{