mirror of
https://github.com/neovim/neovim.git
synced 2026-07-31 04:39:07 +00:00
feat(lua): vim.keycode() gets structured parse result #38636
Problems: 1. Can't get individual parts of a key-chord separately: modifiers, key. 2. Can't separate a key-combo into individual key-chords. Solution: Enhance `vim.keycode()` to optionally return a structured parse result as a list of key-chords: - `key_raw` the key-chord (problem 2) - `mod` the modifiers of `key_raw` (problem 1) - `key_orig` the key part of the key-chord, only here if differing from `key` (this doesn't solve any of the above mentioned problems, but it may provide useful and it's (in terms of code) free) - `key` a normalized version of `key_orig` (solving problem 1), example(the first is `key_orig` and second is `key`): `lt` and `<`, `Bar` and `|` (in `<C-Bar>`)
This commit is contained in:
@@ -1271,7 +1271,7 @@ vim.inspect() *vim.inspect()*
|
||||
• https://github.com/kikito/inspect.lua
|
||||
• https://github.com/mpeterv/vinspect
|
||||
|
||||
vim.keycode({str}) *vim.keycode()*
|
||||
vim.keycode({str}, {info}) *vim.keycode()*
|
||||
Translates keycodes.
|
||||
|
||||
Example: >lua
|
||||
@@ -1280,10 +1280,20 @@ vim.keycode({str}) *vim.keycode()*
|
||||
<
|
||||
|
||||
Parameters: ~
|
||||
• {str} (`string`) String to be converted.
|
||||
• {str} (`string`) String to be converted.
|
||||
• {info} (`boolean?`) Also returns a normalized detailed info about the
|
||||
key as a second return value.
|
||||
|
||||
Return: ~
|
||||
Return (multiple): ~
|
||||
(`string`)
|
||||
(`table?`) List of dicts where each dict represents a key chord, and
|
||||
has fields:
|
||||
• {key} (`string`) The key chord normalized without modifiers.
|
||||
• {key_orig} (`string?`) Non-normalized version of key chord, only
|
||||
present when it differs from {key}. Example: `lt` and `<`.
|
||||
• {key_raw} (`string`) The original key chord with modifiers.
|
||||
• {mod} (`('M'|'T'|'C'|'S'|'2'|'3'|'4'|'D')[]`) A list of single
|
||||
character modifiers of the key.
|
||||
|
||||
See also: ~
|
||||
• |nvim_replace_termcodes()|
|
||||
|
||||
@@ -313,6 +313,8 @@ LUA
|
||||
• |vim.pack.get()| output includes revision of a pending update.
|
||||
• |vim.pack.get()| can fetch new updates before computing the output.
|
||||
• |vim.o| now accepts table style values for assignment.
|
||||
• |vim.pos| and |vim.range| can now convert between mark positions.
|
||||
• |vim.keycode()| returns structured info as return value 2.
|
||||
|
||||
OPTIONS
|
||||
|
||||
|
||||
@@ -1260,6 +1260,23 @@ function vim.print(...)
|
||||
return vim._print(false, ...)
|
||||
end
|
||||
|
||||
--- @class vim.keycode.info
|
||||
--- @inlinedoc
|
||||
---
|
||||
--- The key chord normalized without modifiers.
|
||||
--- @field key string
|
||||
---
|
||||
--- Non-normalized version of key chord,
|
||||
--- only present when it differs from {key}.
|
||||
--- Example: `lt` and `<`.
|
||||
--- @field key_orig string?
|
||||
---
|
||||
--- The original key chord with modifiers.
|
||||
--- @field key_raw string
|
||||
---
|
||||
--- A list of single character modifiers of the key.
|
||||
--- @field mod ('M'|'T'|'C'|'S'|'2'|'3'|'4'|'D')[]
|
||||
|
||||
--- Translates keycodes.
|
||||
---
|
||||
--- Example:
|
||||
@@ -1269,11 +1286,21 @@ end
|
||||
--- vim.g.mapleader = k'<bs>'
|
||||
--- ```
|
||||
---
|
||||
---
|
||||
--- @param str string String to be converted.
|
||||
--- @param info boolean? Also returns a normalized
|
||||
--- detailed info about the key as a second return value.
|
||||
--- @return string
|
||||
--- @return vim.keycode.info[]? List of dicts where
|
||||
--- each dict represents a key chord, and has fields:
|
||||
--- @see |nvim_replace_termcodes()|
|
||||
function vim.keycode(str)
|
||||
return vim.api.nvim_replace_termcodes(str, true, true, true)
|
||||
function vim.keycode(str, info)
|
||||
if info then
|
||||
local keycode = vim.keycode(str)
|
||||
return keycode, vim._keyarg(keycode)
|
||||
else
|
||||
return vim.api.nvim_replace_termcodes(str, true, true, true)
|
||||
end
|
||||
end
|
||||
|
||||
--- @param server_addr string
|
||||
|
||||
@@ -2391,7 +2391,7 @@ static int command_line_handle_key(CommandLineState *s)
|
||||
end:
|
||||
// put the character in the command line
|
||||
if (IS_SPECIAL(s->c) || mod_mask != 0) {
|
||||
put_on_cmdline(get_special_key_name(s->c, mod_mask), -1, true);
|
||||
put_on_cmdline(get_special_key_name(s->c, mod_mask, NULL), -1, true);
|
||||
} else {
|
||||
int j = utf_char2bytes(s->c, IObuff);
|
||||
IObuff[j] = NUL; // exclude composing chars
|
||||
|
||||
@@ -1904,7 +1904,7 @@ static void insert_special(int c, int allow_modmask, int ctrlv)
|
||||
allow_modmask = true;
|
||||
}
|
||||
if (IS_SPECIAL(c) || (mod_mask && allow_modmask)) {
|
||||
char *p = get_special_key_name(c, mod_mask);
|
||||
char *p = get_special_key_name(c, mod_mask, NULL);
|
||||
int len = (int)strlen(p);
|
||||
c = (uint8_t)p[len - 1];
|
||||
if (len > 2) {
|
||||
|
||||
@@ -28,12 +28,7 @@
|
||||
#include "keycodes.c.generated.h"
|
||||
|
||||
// Some useful tables.
|
||||
|
||||
static const struct modmasktable {
|
||||
uint16_t mod_mask; ///< Bit-mask for particular key modifier.
|
||||
uint16_t mod_flag; ///< Bit(s) for particular key modifier.
|
||||
char name; ///< Single letter name of modifier.
|
||||
} mod_mask_table[] = {
|
||||
const struct modmasktable mod_mask_table[] = {
|
||||
{ MOD_MASK_ALT, MOD_MASK_ALT, 'M' },
|
||||
{ MOD_MASK_META, MOD_MASK_META, 'T' },
|
||||
{ MOD_MASK_CTRL, MOD_MASK_CTRL, 'C' },
|
||||
@@ -260,9 +255,10 @@ int handle_x_keys(const int key)
|
||||
}
|
||||
|
||||
/// @return a string which contains the name of the given key when the given modifiers are down.
|
||||
char *get_special_key_name(int c, int modifiers)
|
||||
char *get_special_key_name(int c, int modifiers, struct keycode_data *data)
|
||||
{
|
||||
static char string[MAX_KEY_NAME_LEN + 1];
|
||||
bool is_ascii_ctrl = false;
|
||||
|
||||
string[0] = '<';
|
||||
int idx = 1;
|
||||
@@ -293,31 +289,38 @@ char *get_special_key_name(int c, int modifiers)
|
||||
// extract modifiers.
|
||||
if (c > 0
|
||||
&& utf_char2len(c) == 1) {
|
||||
if (table_idx < 0
|
||||
&& (!vim_isprintc(c) || (c & 0x7f) == ' ')
|
||||
&& (c & 0x80)) {
|
||||
c &= 0x7f;
|
||||
modifiers |= MOD_MASK_ALT;
|
||||
// try again, to find the un-alted key in the special key table
|
||||
table_idx = find_special_key_in_table(c);
|
||||
}
|
||||
if (table_idx < 0 && !vim_isprintc(c) && c < ' ') {
|
||||
c += '@';
|
||||
modifiers |= MOD_MASK_CTRL;
|
||||
|
||||
is_ascii_ctrl = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (data) {
|
||||
data->modifiers = 0;
|
||||
}
|
||||
|
||||
// translate the modifier into a string
|
||||
for (int i = 0; mod_mask_table[i].name != 'A'; i++) {
|
||||
if ((modifiers & mod_mask_table[i].mod_mask)
|
||||
== mod_mask_table[i].mod_flag) {
|
||||
string[idx++] = mod_mask_table[i].name;
|
||||
string[idx++] = '-';
|
||||
|
||||
if (data) {
|
||||
data->modifiers |= mod_mask_table[i].mod_flag;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (table_idx < 0) { // unknown special key, may output t_xx
|
||||
if (IS_SPECIAL(c)) {
|
||||
if (data) {
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
data->key = (String){ &string[idx], 4 };
|
||||
}
|
||||
|
||||
string[idx++] = 't';
|
||||
string[idx++] = '_';
|
||||
string[idx++] = (char)(uint8_t)KEY2TERMCAP0(c);
|
||||
@@ -326,11 +329,37 @@ char *get_special_key_name(int c, int modifiers)
|
||||
// Not a special key, only modifiers, output directly.
|
||||
int len = utf_char2len(c);
|
||||
if (len == 1 && vim_isprintc(c)) {
|
||||
if (data) {
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
data->_key_mem = (char)c + 32;
|
||||
data->key = (String){ &data->_key_mem, 1 };
|
||||
if (!is_ascii_ctrl) {
|
||||
data->modifiers |= MOD_MASK_SHIFT;
|
||||
}
|
||||
data->key_orig = (String){ &string[idx], (size_t)len };
|
||||
} else {
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
data->key = (String){ &string[idx], (size_t)len };
|
||||
}
|
||||
}
|
||||
|
||||
string[idx++] = (char)(uint8_t)c;
|
||||
} else if (len > 1) {
|
||||
if (data) {
|
||||
data->key = (String){ &string[idx], (size_t)len };
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
}
|
||||
|
||||
idx += utf_char2bytes(c, string + idx);
|
||||
} else {
|
||||
char *s = transchar(c);
|
||||
|
||||
if (data) {
|
||||
data->_key_mem = (char)c;
|
||||
data->key = (String){ &data->_key_mem, 1 };
|
||||
data->key_orig = (String){ &string[idx], strlen(s) };
|
||||
}
|
||||
|
||||
while (*s) {
|
||||
string[idx++] = *s++;
|
||||
}
|
||||
@@ -338,6 +367,18 @@ char *get_special_key_name(int c, int modifiers)
|
||||
}
|
||||
} else { // use name of special key
|
||||
const String *s = &key_names_table[table_idx].name;
|
||||
|
||||
if (data) {
|
||||
if (0 <= c && c <= 0x7f) {
|
||||
data->_key_mem = (char)c;
|
||||
data->key = (String){ &data->_key_mem, 1 };
|
||||
data->key_orig = *s;
|
||||
} else {
|
||||
data->key = *s;
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
if ((int)s->size + idx + 2 <= MAX_KEY_NAME_LEN) {
|
||||
STRCPY(string + idx, s->data);
|
||||
idx += (int)s->size;
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "nvim/api/private/defs.h"
|
||||
#include "nvim/ascii_defs.h"
|
||||
#include "nvim/eval/typval_defs.h" // IWYU pragma: keep
|
||||
#include "nvim/message_defs.h"
|
||||
|
||||
// Keycode definitions for special keys.
|
||||
//
|
||||
@@ -504,4 +506,10 @@ enum {
|
||||
FSK_SIMPLIFY = 0x08, ///< simplify <C-H>, etc.
|
||||
};
|
||||
|
||||
extern const struct modmasktable {
|
||||
uint16_t mod_mask; ///< Bit-mask for particular key modifier.
|
||||
uint16_t mod_flag; ///< Bit(s) for particular key modifier.
|
||||
char name; ///< Single letter name of modifier.
|
||||
} mod_mask_table[];
|
||||
|
||||
#include "keycodes.h.generated.h"
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
#include "nvim/ex_eval.h"
|
||||
#include "nvim/fold.h"
|
||||
#include "nvim/globals.h"
|
||||
#include "nvim/keycodes.h"
|
||||
#include "nvim/lua/base64.h"
|
||||
#include "nvim/lua/converter.h"
|
||||
#include "nvim/lua/spell.h"
|
||||
@@ -41,6 +42,7 @@
|
||||
#include "nvim/mbyte_defs.h"
|
||||
#include "nvim/memline.h"
|
||||
#include "nvim/memory.h"
|
||||
#include "nvim/message_defs.h"
|
||||
#include "nvim/pos_defs.h"
|
||||
#include "nvim/regexp.h"
|
||||
#include "nvim/regexp_defs.h"
|
||||
@@ -678,6 +680,51 @@ static int nlua_with(lua_State *L)
|
||||
return rets;
|
||||
}
|
||||
|
||||
// Parses internal representation of key into detailed dict about each key chord.
|
||||
static int nlua_keyarg(lua_State *L)
|
||||
{
|
||||
luaL_argcheck(L, lua_isstring(L, 1), 1, "string expected");
|
||||
|
||||
char *escaped = vim_strsave_escape_ks((char *)lua_tostring(L, 1));
|
||||
const char *p = escaped;
|
||||
|
||||
lua_newtable(L);
|
||||
|
||||
struct keycode_data data;
|
||||
|
||||
while (*p != NUL) {
|
||||
const char *key = str2special(&p, true, true, &data);
|
||||
|
||||
lua_createtable(L, 0, 3);
|
||||
|
||||
lua_newtable(L);
|
||||
for (int i = 0; mod_mask_table[i].name != 'A'; i++) {
|
||||
if ((data.modifiers & mod_mask_table[i].mod_mask) == mod_mask_table[i].mod_flag) {
|
||||
lua_pushlstring(L, &mod_mask_table[i].name, 1);
|
||||
lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1);
|
||||
}
|
||||
}
|
||||
lua_setfield(L, -2, "mod");
|
||||
|
||||
lua_pushlstring(L, data.key.data, data.key.size);
|
||||
lua_setfield(L, -2, "key");
|
||||
|
||||
lua_pushstring(L, key);
|
||||
lua_setfield(L, -2, "key_raw");
|
||||
|
||||
if (data.key_orig.size != 0) {
|
||||
lua_pushlstring(L, data.key_orig.data, data.key_orig.size);
|
||||
lua_setfield(L, -2, "key_orig");
|
||||
}
|
||||
|
||||
lua_rawseti(L, -2, (int)lua_objlen(L, -2) + 1);
|
||||
}
|
||||
|
||||
xfree(escaped);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
// Access to internal functions. For use in runtime/
|
||||
static void nlua_state_add_internal(lua_State *const lstate)
|
||||
{
|
||||
@@ -695,6 +742,9 @@ static void nlua_state_add_internal(lua_State *const lstate)
|
||||
|
||||
lua_pushcfunction(lstate, &nlua_with);
|
||||
lua_setfield(lstate, -2, "_with_c");
|
||||
|
||||
lua_pushcfunction(lstate, &nlua_keyarg);
|
||||
lua_setfield(lstate, -2, "_keyarg");
|
||||
}
|
||||
|
||||
void nlua_state_add_stdlib(lua_State *const lstate, bool is_thread)
|
||||
|
||||
@@ -1223,7 +1223,7 @@ static char *translate_mapping(const char *const str_in, const char *const cpo_v
|
||||
str += 2;
|
||||
}
|
||||
if (IS_SPECIAL(c) || modifiers) { // special key
|
||||
ga_concat(&ga, get_special_key_name(c, modifiers));
|
||||
ga_concat(&ga, get_special_key_name(c, modifiers, NULL));
|
||||
continue; // for (str)
|
||||
}
|
||||
}
|
||||
@@ -1948,7 +1948,7 @@ int put_escstr(FILE *fd, const char *strstart, int what)
|
||||
str += 2;
|
||||
}
|
||||
if (IS_SPECIAL(c) || modifiers) { // special key
|
||||
if (fputs(get_special_key_name(c, modifiers), fd) < 0) {
|
||||
if (fputs(get_special_key_name(c, modifiers, NULL), fd) < 0) {
|
||||
return FAIL;
|
||||
}
|
||||
continue;
|
||||
|
||||
@@ -1986,7 +1986,7 @@ int msg_outtrans_special(const char *strstart, bool from, int maxlen)
|
||||
text = "<Space>";
|
||||
str++;
|
||||
} else {
|
||||
text = str2special(&str, from, false);
|
||||
text = str2special(&str, from, false, NULL);
|
||||
}
|
||||
if (text[0] != NUL && text[1] == NUL) {
|
||||
// single-byte character or illegal byte
|
||||
@@ -2024,7 +2024,7 @@ char *str2special_save(const char *const str, const bool replace_spaces,
|
||||
|
||||
const char *p = str;
|
||||
while (*p != NUL) {
|
||||
ga_concat(&ga, str2special(&p, replace_spaces, replace_others));
|
||||
ga_concat(&ga, str2special(&p, replace_spaces, replace_others, NULL));
|
||||
}
|
||||
ga_append(&ga, NUL);
|
||||
return (char *)ga.ga_data;
|
||||
@@ -2049,14 +2049,14 @@ char *str2special_arena(const char *const str, const bool replace_spaces,
|
||||
const char *p = str;
|
||||
size_t len = 0;
|
||||
while (*p) {
|
||||
len += strlen(str2special(&p, replace_spaces, replace_others));
|
||||
len += strlen(str2special(&p, replace_spaces, replace_others, NULL));
|
||||
}
|
||||
|
||||
char *buf = arena_alloc(arena, len + 1, false);
|
||||
size_t pos = 0;
|
||||
p = str;
|
||||
while (*p) {
|
||||
const char *s = str2special(&p, replace_spaces, replace_others);
|
||||
const char *s = str2special(&p, replace_spaces, replace_others, NULL);
|
||||
size_t s_len = strlen(s);
|
||||
memcpy(buf + pos, s, s_len);
|
||||
pos += s_len;
|
||||
@@ -2078,8 +2078,8 @@ char *str2special_arena(const char *const str, const bool replace_spaces,
|
||||
/// for the second time.
|
||||
/// On illegal byte return a string with only that byte.
|
||||
const char *str2special(const char **const sp, const bool replace_spaces,
|
||||
const TriState replace_others)
|
||||
FUNC_ATTR_NONNULL_ALL FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
||||
const TriState replace_others, struct keycode_data *data)
|
||||
FUNC_ATTR_NONNULL_ARG(1) FUNC_ATTR_WARN_UNUSED_RESULT FUNC_ATTR_NONNULL_RET
|
||||
{
|
||||
static char buf[7];
|
||||
|
||||
@@ -2088,6 +2088,12 @@ const char *str2special(const char **const sp, const bool replace_spaces,
|
||||
// string if it is a multi-byte character.
|
||||
const char *const p = mb_unescape(sp);
|
||||
if (p != NULL) {
|
||||
if (data) {
|
||||
data->modifiers = 0;
|
||||
data->key = (String){ (char *)p, strlen(p) };
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
}
|
||||
|
||||
return p;
|
||||
}
|
||||
}
|
||||
@@ -2134,8 +2140,22 @@ const char *str2special(const char **const sp, const bool replace_spaces,
|
||||
|| (replace_spaces && c == ' ')
|
||||
|| (replace_others != kFalse && c == '<')
|
||||
|| (replace_others == kTrue && (c == '|' || c == '\\'))) {
|
||||
return get_special_key_name(c, modifiers);
|
||||
return get_special_key_name(c, modifiers, data);
|
||||
}
|
||||
|
||||
if (data) {
|
||||
data->modifiers = 0;
|
||||
if ('A' <= c && c <= 'Z') {
|
||||
data->_key_mem = (char)c + 32;
|
||||
data->key = (String){ &data->_key_mem, 1 };
|
||||
data->modifiers |= MOD_MASK_SHIFT;
|
||||
data->key_orig = (String){ buf, 1 };
|
||||
} else {
|
||||
data->key = (String){ buf, 1 };
|
||||
data->key_orig = (String){ NULL, 0 };
|
||||
}
|
||||
}
|
||||
|
||||
buf[0] = (char)c;
|
||||
buf[1] = NUL;
|
||||
return buf;
|
||||
@@ -2150,7 +2170,7 @@ void str2specialbuf(const char *sp, char *buf, size_t len)
|
||||
FUNC_ATTR_NONNULL_ALL
|
||||
{
|
||||
while (*sp) {
|
||||
const char *s = str2special(&sp, false, false);
|
||||
const char *s = str2special(&sp, false, false, NULL);
|
||||
const size_t s_len = strlen(s);
|
||||
if (len <= s_len) {
|
||||
break;
|
||||
|
||||
@@ -29,3 +29,10 @@ typedef struct msg_hist {
|
||||
bool append; ///< Message should be appended to previous entry, as opposed
|
||||
///< to on a new line (|ui-messages|->msg_show->append).
|
||||
} MessageHistoryEntry;
|
||||
|
||||
struct keycode_data {
|
||||
int modifiers; ///< Bitmask of modifiers.
|
||||
char _key_mem; ///< Storage for {key} to point to.
|
||||
String key; ///< A "normalized" version of key.
|
||||
String key_orig; ///< Alternative version of {key}. Empty string means unset.
|
||||
};
|
||||
|
||||
@@ -4795,7 +4795,7 @@ static int put_set(FILE *fd, char *cmd, OptIndex opt_idx, void *varp)
|
||||
OptInt wc;
|
||||
if (wc_use_keyname(varp, &wc)) {
|
||||
// print 'wildchar' and 'wildcharm' as a key name
|
||||
if (fputs(get_special_key_name((int)wc, 0), fd) < 0) {
|
||||
if (fputs(get_special_key_name((int)wc, 0, NULL), fd) < 0) {
|
||||
return FAIL;
|
||||
}
|
||||
} else if (fprintf(fd, "%" PRId64, value_num) < 0) {
|
||||
@@ -6372,7 +6372,7 @@ static void option_value2string(vimoption_T *opt, int opt_flags)
|
||||
OptInt wc = 0;
|
||||
|
||||
if (wc_use_keyname(varp, &wc)) {
|
||||
xstrlcpy(NameBuff, get_special_key_name((int)wc, 0), sizeof(NameBuff));
|
||||
xstrlcpy(NameBuff, get_special_key_name((int)wc, 0, NULL), sizeof(NameBuff));
|
||||
} else if (wc != 0) {
|
||||
xstrlcpy(NameBuff, transchar((int)wc), sizeof(NameBuff));
|
||||
} else {
|
||||
|
||||
@@ -93,7 +93,7 @@ getkey:
|
||||
}
|
||||
|
||||
#ifdef NVIM_LOG_DEBUG
|
||||
char *keyname = key == K_EVENT ? "K_EVENT" : get_special_key_name(key, mod_mask);
|
||||
char *keyname = key == K_EVENT ? "K_EVENT" : get_special_key_name(key, mod_mask, NULL);
|
||||
DLOG("input: %s", keyname);
|
||||
#endif
|
||||
|
||||
|
||||
63
test/functional/editor/keycode_spec.lua
Normal file
63
test/functional/editor/keycode_spec.lua
Normal file
@@ -0,0 +1,63 @@
|
||||
local t = require('test.testutil')
|
||||
local n = require('test.functional.testnvim')()
|
||||
|
||||
local eq = t.eq
|
||||
local exec_lua = n.exec_lua
|
||||
local clear = n.clear
|
||||
|
||||
describe('vim.keycode() detailed info', function()
|
||||
before_each(clear)
|
||||
|
||||
local function parse(key, simplify)
|
||||
local parsed = exec_lua([[return select(2,vim.keycode(...,true))]], key)
|
||||
return vim.tbl_map(function(tbl)
|
||||
if simplify == 1 then
|
||||
return { tbl.mod, tbl.key }
|
||||
elseif simplify == 2 then
|
||||
return { tbl.mod, tbl.key, tbl.key_raw }
|
||||
end
|
||||
return tbl
|
||||
end, parsed)
|
||||
end
|
||||
|
||||
it('works', function()
|
||||
eq({ { mod = { 'M' }, key = 'f', key_raw = '<M-f>' } }, parse('<A-f>'))
|
||||
eq({ { { 'M' }, 'Home' } }, parse('<A-Home>', 1))
|
||||
eq({ { { 'M' }, 'f' }, { {}, 'b' }, { {}, 'c' } }, parse('<A-f>b<char-99>', 1))
|
||||
|
||||
eq({ { { 'M' }, '>' } }, parse('<A->>', 1))
|
||||
eq({ { { 'M' }, 't_>>' } }, parse('<A-t_>>>', 1))
|
||||
end)
|
||||
|
||||
it('normalizes', function()
|
||||
-- stylua: ignore start
|
||||
eq({ { {}, '\\', '<Bslash>' } }, parse('\\', 2))
|
||||
eq({ { { 'M' }, '\\', '<M-Bslash>' } }, parse('<A-\\>', 2))
|
||||
eq({ { { 'C' }, '\\', '<C-\\>' } }, parse('<C-\\>', 2))
|
||||
eq({ { {}, '|', '<Bar>' } }, parse('|', 2))
|
||||
eq({ { { 'M' }, '|', '<M-Bar>' } }, parse('<A-|>', 2))
|
||||
eq({ { { 'C' }, '|', '<C-Bar>' } }, parse('<C-|>', 2))
|
||||
eq({ { {}, '\127', '\127' } }, parse('\127', 2))
|
||||
eq({ { { 'M' }, '\127', '<M-^?>' } }, parse('<A-\127>', 2))
|
||||
eq({ { { 'C' }, '\127', '<C-^?>' } }, parse('<C-\127>', 2))
|
||||
|
||||
eq({ { {}, '<', '<lt>' } }, parse('<', 2))
|
||||
eq({ { {}, '\t', '<Tab>' } }, parse('\t', 2))
|
||||
eq({ { {}, '\r', '<CR>' } }, parse('\r', 2))
|
||||
eq({ { {}, '\n', '<NL>' } }, parse('\n', 2))
|
||||
eq({ { {}, '\27', '<Esc>' } }, parse('\27', 2))
|
||||
eq({ { {}, ' ', '<Space>' } }, parse(' ', 2))
|
||||
|
||||
eq({ { { 'S' }, 'a', 'A' } }, parse('A', 2))
|
||||
eq({ { { 'C' }, 'a', '<C-A>' } }, parse('<C-a>', 2))
|
||||
-- stylua: ignore end
|
||||
|
||||
eq({ { mod = {}, key = '<', key_raw = '<lt>', key_orig = 'lt' } }, parse('<'))
|
||||
end)
|
||||
|
||||
it('handles utf8-chars', function()
|
||||
eq({ { {}, 'ö' }, { { 'M' }, 'ó' }, { {}, 'ú' }, { {}, 'ü' } }, parse('ö<A-ó>úü', 1))
|
||||
eq({ { {}, 'a' }, { {}, '\226\129\129' }, { {}, 'b' } }, parse('a\226\129\129b', 1))
|
||||
eq({ { {}, 'a' }, { {}, '\242\129\129\129' }, { {}, 'b' } }, parse('a\242\129\129\129b', 1))
|
||||
end)
|
||||
end)
|
||||
Reference in New Issue
Block a user