feat: add flow native keybinding parser
This commit is contained in:
parent
34f7c0a7bd
commit
524e5be47c
7 changed files with 742 additions and 535 deletions
|
@ -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 });
|
||||
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();
|
||||
}
|
||||
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,
|
||||
};
|
||||
|
|
|
@ -1,171 +1,173 @@
|
|||
{
|
||||
"flow": {
|
||||
"normal": {
|
||||
"syntax": "flow",
|
||||
"on_match_failure": "insert",
|
||||
"bindings": [
|
||||
["<C-e>", "open_recent"],
|
||||
["<C-r>", "open_recent_project"],
|
||||
["<C-j>", "toggle_panel"],
|
||||
["<C-z>", "undo"],
|
||||
["<C-y>", "redo"],
|
||||
["<C-q>", "quit"],
|
||||
["<C-o>", "open_file"],
|
||||
["<C-w>", "close_file"],
|
||||
["<C-s>", "save_file"],
|
||||
["<C-l>", "cmd_cycle3", "scroll_view_center", "scroll_view_top", "scroll_view_bottom"],
|
||||
["<C-n>", "goto_next_match"],
|
||||
["<C-p>", "goto_prev_match"],
|
||||
["<C-b>", "move_to_char", "false"],
|
||||
["<C-t>", "move_to_char", "true"],
|
||||
["<C-x>", "cut"],
|
||||
["<C-c>", "copy"],
|
||||
["<C-v>", "system_paste"],
|
||||
["<C-u>", "pop_cursor"],
|
||||
["<C-k><C-u>", "delete_to_begin"],
|
||||
["<C-k><C-k>", "delete_to_end"],
|
||||
["<C-k><C-d>", "move_cursor_next_match"],
|
||||
["<C-k><C-t>", "change_theme"],
|
||||
["<C-k><C-i>", "hover"],
|
||||
["<C-f>", "find"],
|
||||
["<C-g>", "goto"],
|
||||
["<C-d>", "add_cursor_next_match"],
|
||||
["<C-a>", "select_all"],
|
||||
["<C-i>", "insert_bytes", "\t"],
|
||||
["<C-/>", "toggle_comment"],
|
||||
["<C-CR>", "smart_insert_line_after"],
|
||||
["<C-Space>", "completion"],
|
||||
["<C-End>", "move_buffer_end"],
|
||||
["<C-Home>", "move_buffer_begin"],
|
||||
["<C-Up>", "move_scroll_up"],
|
||||
["<C-Down>", "move_scroll_down"],
|
||||
["<C-PageUp>", "move_scroll_page_up"],
|
||||
["<C-PageDown>", "move_scroll_page_down"],
|
||||
["<C-Left>", "move_word_left"],
|
||||
["<C-Right>", "move_word_right"],
|
||||
["<C-Backspace>", "delete_word_left"],
|
||||
["<C-Delete>", "delete_word_right"],
|
||||
["<C-F5>", "toggle_inspector_view"],
|
||||
["<C-F10>", "toggle_whitespace_mode"],
|
||||
["<C-F12>", "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"],
|
||||
|
||||
["<C-S-s>", "save_as"],
|
||||
["<C-S-p>", "open_command_palette"],
|
||||
["<C-S-d>", "dupe_down"],
|
||||
["<C-S-z>", "redo"],
|
||||
["<C-S-q>", "quit_without_saving"],
|
||||
["<C-S-w>", "close_file_without_saving"],
|
||||
["<C-S-f>", "find_in_files"],
|
||||
["<C-S-l>", "add_cursor_all_matches_async"],
|
||||
["<C-S-i>", "toggle_inspector_view_async"],
|
||||
["<C-S-m>", "show_diagnostics"],
|
||||
["<C-S-Ce>", "smart_insert_line_before"],
|
||||
["<C-S-End>", "select_buffer_end"],
|
||||
["<C-S-Home>", "select_buffer_begin"],
|
||||
["<C-S-Up>", "select_scroll_up"],
|
||||
["<C-S-Down>", "select_scroll_down"],
|
||||
["<C-S-Left>", "select_word_left"],
|
||||
["<C-S-Right>", "select_word_right"],
|
||||
["<C-S-Space>", "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"],
|
||||
|
||||
["<A-o>", "open_previous_file"],
|
||||
["<A-j>", "join_next_line"],
|
||||
["<A-n>", "goto_next_file_or_diagnostic"],
|
||||
["<A-p>", "goto_prev_file_or_diagnostic"],
|
||||
["<A-u>", "to_upper"],
|
||||
["<A-l>", "to_lower"],
|
||||
["<A-c>", "switch_case"],
|
||||
["<A-i>", "toggle_inputview"],
|
||||
["<A-b>", "move_word_left"],
|
||||
["<A-f>", "move_word_right"],
|
||||
["<A-s>", "filter", "sort"],
|
||||
["<A-v>", "paste"],
|
||||
["<A-x>", "open_command_palette"],
|
||||
["<A-Left>", "jump_back"],
|
||||
["<A-Right>", "jump_forward"],
|
||||
["<A-Up>", "pull_up"],
|
||||
["<A-Down>", "pull_down"],
|
||||
["<A-CR>", "insert_line"],
|
||||
["<A-F10>", "gutter_mode_next"],
|
||||
["<A-F12>", "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"],
|
||||
|
||||
["<A-S-p>", "open_command_palette"],
|
||||
["<A-S-d>", "dupe_up"],
|
||||
["<A-S-f>", "format"],
|
||||
["<A-S-s>", "filter", "sort", "-u"],
|
||||
["<A-S-v>", "paste"],
|
||||
["<A-S-i>", "add_cursors_to_line_ends"],
|
||||
["<A-S-Left>", "shrink_selection"],
|
||||
["<A-S-Right>", "expand_selection"],
|
||||
["<A-S-Home>", "move_scroll_left"],
|
||||
["<A-S-End>", "move_scroll_right"],
|
||||
["<A-S-Up>", "add_cursor_up"],
|
||||
["<A-S-Down>", "add_cursor_down"],
|
||||
["<A-S-F12>", "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"],
|
||||
|
||||
["<S-F3>", "goto_prev_match"],
|
||||
["<S-F10>", "toggle_syntax_highlighting"],
|
||||
["<S-F12>", "references"],
|
||||
["<S-Left>", "select_left"],
|
||||
["<S-Right>", "select_right"],
|
||||
["<S-Up>", "select_up"],
|
||||
["<S-Down>", "select_down"],
|
||||
["<S-Home>", "smart_select_begin"],
|
||||
["<S-End>", "select_end"],
|
||||
["<S-PageUp>", "select_page_up"],
|
||||
["<S-PageDown>", "select_page_down"],
|
||||
["<S-CR>", "smart_insert_line_before"],
|
||||
["<S-Backspace>", "delete_backward"],
|
||||
["<S-Tab>", "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"],
|
||||
|
||||
["<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"],
|
||||
["<Esc>", "cancel"],
|
||||
["<CR>", "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"],
|
||||
["<PageUp>", "move_page_up"],
|
||||
["<PageDown>", "move_page_down"],
|
||||
["<Tab>", "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": [
|
||||
["<C-f><C-f><C-f><C-f><C-f>", "home_sheeran"],
|
||||
["<C-j>", "toggle_panel"],
|
||||
["<C-q>", "quit"],
|
||||
["<C-w>", "quit"],
|
||||
["<C-o>", "open_file"],
|
||||
["<C-e>", "open_recent"],
|
||||
["<C-r>", "open_recent_project"],
|
||||
["<C-p>", "open_command_palette"],
|
||||
["<C-k><C-t>", "change_theme"],
|
||||
["<C-S-p>", "open_command_palette"],
|
||||
["<C-S-q>", "quit_without_saving"],
|
||||
["<C-S-r>", "restart"],
|
||||
["<C-S-f>", "find_in_files"],
|
||||
["<C-S-l>", "toggle_panel"],
|
||||
["<A-S-p>", "open_command_palette"],
|
||||
["<A-n>", "goto_next_file_or_diagnostic"],
|
||||
["<A-p>", "goto_prev_file_or_diagnostic"],
|
||||
["<A-l>", "toggle_panel"],
|
||||
["<A-i>", "toggle_inputview"],
|
||||
["<A-x>", "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"],
|
||||
["<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"],
|
||||
["<CR>", "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": [
|
||||
["<C-j>", "toggle_panel"],
|
||||
["<C-q>", "quit"],
|
||||
["<C-w>", "close_file"],
|
||||
["<C-p>", "palette_menu_up"],
|
||||
["<C-n>", "palette_menu_down"],
|
||||
["<C-e>", "palette_menu_down"],
|
||||
["<C-r>", "palette_menu_down"],
|
||||
["<C-t>", "palette_menu_down"],
|
||||
["<C-v>", "system_paste"],
|
||||
["<C-c>", "palette_menu_cancel"],
|
||||
["<C-g>", "palette_menu_cancel"],
|
||||
["<C-Esc>", "palette_menu_cancel"],
|
||||
["<C-Up>", "palette_menu_up"],
|
||||
["<C-Down>", "palette_menu_down"],
|
||||
["<C-PageUp>", "palette_menu_pageup"],
|
||||
["<C-PageDown>", "palette_menu_pagedown"],
|
||||
["<C-CR>", "palette_menu_activate"],
|
||||
["<C-Backspace>", "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"],
|
||||
|
||||
["<C-S-e>", "palette_menu_up"],
|
||||
["<C-S-r>", "palette_menu_up"],
|
||||
["<C-S-p>", "palette_menu_down"],
|
||||
["<C-S-q>", "quit_without_saving"],
|
||||
["<C-S-w>", "close_file_without_saving"],
|
||||
["<C-S-l>", "overlay_toggle_panel"],
|
||||
["<C-S-i>", "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"],
|
||||
|
||||
["<A-S-p>", "palette_menu_down"],
|
||||
["alt+shift+p", "palette_menu_down"],
|
||||
|
||||
["<A-p>", "palette_menu_up"],
|
||||
["<A-l>", "toggle_panel"],
|
||||
["<A-i>", "toggle_inputview"],
|
||||
["alt+p", "palette_menu_up"],
|
||||
["alt+l", "toggle_panel"],
|
||||
["alt+i", "toggle_inputview"],
|
||||
|
||||
["<F9>", "theme_prev"],
|
||||
["<F10>", "theme_next"],
|
||||
["<F11>", "toggle_panel"],
|
||||
["<F12>", "toggle_inputview"],
|
||||
["<Esc>", "palette_menu_cancel"],
|
||||
["<Up>", "palette_menu_up"],
|
||||
["<Down>", "palette_menu_down"],
|
||||
["<PageUp>", "palette_menu_pageup"],
|
||||
["<PageDown>", "palette_menu_pagedown"],
|
||||
["<CR>", "palette_menu_activate"],
|
||||
["<Backspace>", "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": [
|
||||
["<C-q>", "quit"],
|
||||
["<C-u>", "mini_mode_reset"],
|
||||
["<C-g>", "mini_mode_cancel"],
|
||||
["<C-c>", "mini_mode_cancel"],
|
||||
["<C-l>", "scroll_view_center"],
|
||||
["<C-Space>", "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"],
|
||||
|
||||
["<Esc>", "mini_mode_cancel"],
|
||||
["<CR>", "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"]
|
||||
["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": [
|
||||
["<C-a>", "cursor_line_start"],
|
||||
|
|
85
src/keybind/dynamic/parse_flow.zig
Normal file
85
src/keybind/dynamic/parse_flow.zig
Normal file
|
@ -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 },
|
||||
});
|
||||
};
|
290
src/keybind/dynamic/parse_vim.zig
Normal file
290
src/keybind/dynamic/parse_vim.zig
Normal file
|
@ -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();
|
||||
}
|
|
@ -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),
|
||||
},
|
||||
.{
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
.{
|
||||
|
|
|
@ -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),
|
||||
},
|
||||
.{
|
||||
|
|
Loading…
Add table
Reference in a new issue