feat: added some missing features to vim keybinding parser

closes: #65
This commit is contained in:
Robert Burnett 2024-11-18 09:35:26 -06:00 committed by CJ van den Berg
parent b7985baffb
commit a74a36b4bc
Signed by: neurocyte
GPG key ID: 8EB1E1BB660E3FB9
2 changed files with 87 additions and 10 deletions

View file

@ -495,11 +495,22 @@ const expectEqual = std.testing.expectEqual;
const parse_test_cases = .{ const parse_test_cases = .{
//input, expected //input, expected
.{ "j", &.{KeyEvent{ .key = 'j' }} }, .{ "j", &.{KeyEvent{ .key = 'j' }} },
.{ "J", &.{KeyEvent{ .key = 'j', .modifiers = input.mod.shift }} },
.{ "jk", &.{ KeyEvent{ .key = 'j' }, KeyEvent{ .key = 'k' } } }, .{ "jk", &.{ KeyEvent{ .key = 'j' }, KeyEvent{ .key = 'k' } } },
.{ "<Space>", &.{KeyEvent{ .key = input.key.space }} }, .{ "<Space>", &.{KeyEvent{ .key = input.key.space }} },
.{ "<C-x><C-c>", &.{ KeyEvent{ .key = 'x', .modifiers = input.mod.ctrl }, KeyEvent{ .key = 'c', .modifiers = input.mod.ctrl } } }, .{ "<C-x><C-c>", &.{ KeyEvent{ .key = 'x', .modifiers = input.mod.ctrl }, KeyEvent{ .key = 'c', .modifiers = input.mod.ctrl } } },
.{ "<A-x><Tab>", &.{ KeyEvent{ .key = 'x', .modifiers = input.mod.alt }, KeyEvent{ .key = input.key.tab } } }, .{ "<A-x><Tab>", &.{ KeyEvent{ .key = 'x', .modifiers = input.mod.alt }, KeyEvent{ .key = input.key.tab } } },
.{ "<S-A-x><D-Del>", &.{ KeyEvent{ .key = 'x', .modifiers = input.mod.alt | input.mod.shift }, KeyEvent{ .key = input.key.delete, .modifiers = input.mod.super } } }, .{ "<S-A-x><D-Del>", &.{
KeyEvent{ .key = 'x', .modifiers = input.mod.alt | input.mod.shift },
KeyEvent{ .key = input.key.delete, .modifiers = input.mod.super },
} },
.{ ".", &.{KeyEvent{ .key = '.' }} },
.{ ",", &.{KeyEvent{ .key = ',' }} },
.{ "`", &.{KeyEvent{ .key = '`' }} },
.{ "<S--><Home>", &.{
KeyEvent{ .key = '-', .modifiers = input.mod.shift },
KeyEvent{ .key = input.key.home },
} },
}; };
test "parse" { test "parse" {

View file

@ -14,10 +14,11 @@ pub const ParseError = error{
InvalidInitialCharacter, InvalidInitialCharacter,
InvalidStartOfControlBinding, InvalidStartOfControlBinding,
InvalidStartOfShiftBinding, InvalidStartOfShiftBinding,
InvalidStartOfDeleteBinding, InvalidStartOfDelBinding,
InvalidStartOfHomeBinding,
InvalidCRBinding, InvalidCRBinding,
InvalidSpaceBinding, InvalidSpaceBinding,
InvalidDeleteBinding, InvalidDelBinding,
InvalidTabBinding, InvalidTabBinding,
InvalidUpBinding, InvalidUpBinding,
InvalidEscapeBinding, InvalidEscapeBinding,
@ -29,6 +30,10 @@ pub const ParseError = error{
InvalidEscapeSequenceDelimiter, InvalidEscapeSequenceDelimiter,
InvalidModifier, InvalidModifier,
InvalidEscapeSequenceEnd, InvalidEscapeSequenceEnd,
InvalidHomeBinding,
InvalidEndBinding,
InvalidBSBinding,
InvalidInsertBinding,
}; };
var parse_error_buf: [256]u8 = undefined; var parse_error_buf: [256]u8 = undefined;
@ -62,6 +67,10 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
down, down,
left, left,
right, right,
home,
end,
insert,
bs,
}; };
var state: State = .base; var state: State = .base;
var function_key_number: u8 = 0; var function_key_number: u8 = 0;
@ -72,19 +81,29 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
var i: usize = 0; var i: usize = 0;
while (i < str.len) { while (i < str.len) {
switch (state) { switch (state) {
// zig fmt: off
.base => { .base => {
switch (str[i]) { switch (str[i]) {
'<' => { '<' => {
state = .escape_sequence_start; state = .escape_sequence_start;
i += 1; i += 1;
}, },
'a'...'z', '\\', '[', ']', '/', '`', '-', '=', ';', '0'...'9' => { //lowercase characters
try result.append(.{ .event = event, .key = str[i] }); 'a'...'z',
'0'...'9',
'`', '-', '=', '[', ']', '\\', ';', '\'', ',', '.', '/', => {
try result.append(.{ .key = str[i] });
i += 1;
},
//uppercase letters also allowed here
'A'...'Z', => {
try result.append(.{ .key = std.ascii.toLower(str[i]), .modifiers = input.mod.shift});
i += 1; i += 1;
}, },
else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), else => return parse_error(error.InvalidInitialCharacter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }),
} }
}, },
// zig fmt: on
.escape_sequence_start => { .escape_sequence_start => {
switch (str[i]) { switch (str[i]) {
'A' => { 'A' => {
@ -128,6 +147,12 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
'R' => { 'R' => {
state = .right; state = .right;
}, },
'I' => {
state = .insert;
},
'B' => {
state = .bs;
},
'E' => { 'E' => {
state = .esc; state = .esc;
}, },
@ -142,12 +167,47 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
'e' => { 'e' => {
state = .del; state = .del;
}, },
else => return parse_error(error.InvalidStartOfDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), else => return parse_error(error.InvalidStartOfDelBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }),
} }
}, },
else => return parse_error(error.InvalidEscapeSequenceStart, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), 'H' => {
state = .home;
},
else => return parse_error(error.InvalidStartOfHomeBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }),
} }
}, },
.insert => {
if (std.mem.indexOf(u8, str[i..], "Insert") == 0) {
try result.append(.{ .key = input.key.insert, .modifiers = modifiers });
modifiers = 0;
state = .escape_sequence_end;
i += 4;
} else return parse_error(error.InvalidInsertBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] });
},
.end => {
if (std.mem.indexOf(u8, str[i..], "End") == 0) {
try result.append(.{ .key = input.key.end, .modifiers = modifiers });
modifiers = 0;
state = .escape_sequence_end;
i += 3;
} else return parse_error(error.InvalidEndBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] });
},
.home => {
if (std.mem.indexOf(u8, str[i..], "Home") == 0) {
try result.append(.{ .key = input.key.home, .modifiers = modifiers });
modifiers = 0;
state = .escape_sequence_end;
i += 4;
} else return parse_error(error.InvalidHomeBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] });
},
.bs => {
if (std.mem.indexOf(u8, str[i..], "BS") == 0) {
try result.append(.{ .key = input.key.backspace, .modifiers = modifiers });
modifiers = 0;
state = .escape_sequence_end;
i += 2;
} else return parse_error(error.InvalidBSBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] });
},
.cr => { .cr => {
if (std.mem.indexOf(u8, str[i..], "CR") == 0) { if (std.mem.indexOf(u8, str[i..], "CR") == 0) {
try result.append(.{ .event = event, .key = input.key.enter, .modifiers = modifiers }); try result.append(.{ .event = event, .key = input.key.enter, .modifiers = modifiers });
@ -170,7 +230,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 3; i += 3;
} else return parse_error(error.InvalidDeleteBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }); } else return parse_error(error.InvalidDelBinding, "str: {s}, i: {} c: {c}", .{ str, i, str[i] });
}, },
.tab => { .tab => {
if (std.mem.indexOf(u8, str[i..], "Tab") == 0) { if (std.mem.indexOf(u8, str[i..], "Tab") == 0) {
@ -249,10 +309,15 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
else => return parse_error(error.InvalidEscapeSequenceDelimiter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }), else => return parse_error(error.InvalidEscapeSequenceDelimiter, "str: {s}, i: {} c: {c}", .{ str, i, str[i] }),
} }
}, },
// zig fmt: off
.char_or_key_or_modifier => { .char_or_key_or_modifier => {
switch (str[i]) { switch (str[i]) {
'a'...'z', ';', '0'...'9' => { //lowercase characters only inside the escape sequence
try result.append(.{ .event = event, .key = str[i], .modifiers = modifiers }); 'a'...'z',
'0'...'9',
'`', '-', '=', '[', ']', '\\', ';', '\'', ',', '.', '/',
=> {
try result.append(.{ .key = str[i], .modifiers = modifiers });
modifiers = 0; modifiers = 0;
state = .escape_sequence_end; state = .escape_sequence_end;
i += 1; i += 1;
@ -262,6 +327,7 @@ pub fn parse_key_events(allocator: std.mem.Allocator, event: input.Event, str: [
}, },
} }
}, },
// zig fmt: on
.modifier => { .modifier => {
modifiers |= switch (str[i]) { modifiers |= switch (str[i]) {
'A' => input.mod.alt, 'A' => input.mod.alt,