aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/gen/gen_keycodes.lua86
-rw-r--r--src/gen/hashy.lua20
-rw-r--r--src/nvim/CMakeLists.txt8
-rw-r--r--src/nvim/keycodes.c250
-rw-r--r--src/nvim/keycodes.lua208
-rw-r--r--test/benchmark/keycodes_spec.lua84
6 files changed, 414 insertions, 242 deletions
diff --git a/src/gen/gen_keycodes.lua b/src/gen/gen_keycodes.lua
new file mode 100644
index 0000000000..53f0c24e58
--- /dev/null
+++ b/src/gen/gen_keycodes.lua
@@ -0,0 +1,86 @@
+local names_file = arg[1]
+
+local hashy = require('gen.hashy')
+local keycodes = require('nvim.keycodes')
+
+local keycode_names = keycodes.names
+
+--- @type table<string,integer>
+--- Maps lower-case key names to their original indexes.
+local name_orig_idx = {}
+
+--- @type table<string,integer>
+--- Maps keys to the original indexes of their preferred names.
+local key_orig_idx = {}
+
+--- @type [string, string][]
+--- When multiple keys have the same name (e.g. TAB and K_TAB), only the first one
+--- is added to the two tables above, and the other keys are added here.
+local extra_keys = {}
+
+for i, keycode in ipairs(keycode_names) do
+ local key = keycode[1]
+ local name = keycode[2]
+ local name_lower = name:lower()
+ if name_orig_idx[name_lower] == nil then
+ name_orig_idx[name_lower] = i
+ if key_orig_idx[key] == nil then
+ key_orig_idx[key] = i
+ end
+ else
+ table.insert(extra_keys, keycode)
+ end
+end
+
+local hashorder = vim.tbl_keys(name_orig_idx)
+table.sort(hashorder)
+local hashfun
+hashorder, hashfun = hashy.hashy_hash('get_special_key_code', hashorder, function(idx)
+ return 'key_names_table[' .. idx .. '].name.data'
+end, true)
+
+--- @type table<string,integer>
+--- Maps keys to the (after hash) indexes of the entries with preferred names.
+local key_hash_idx = {}
+
+for i, lower_name in ipairs(hashorder) do
+ local orig_idx = name_orig_idx[lower_name]
+ local key = keycode_names[orig_idx][1]
+ if key_orig_idx[key] == orig_idx then
+ key_hash_idx[key] = i
+ end
+end
+
+local names_tgt = assert(io.open(names_file, 'w'))
+names_tgt:write([[
+static const struct key_name_entry {
+ int key; ///< Special key code or ascii value
+ String name; ///< Name of key
+ const String *pref_name; ///< Pointer to preferred key name
+ ///< (may be NULL or point to the name in another entry)
+} key_names_table[] = {]])
+
+for i, lower_name in ipairs(hashorder) do
+ local keycode = keycode_names[name_orig_idx[lower_name]]
+ local key = keycode[1]
+ local name = keycode[2]
+ local pref_idx = key_hash_idx[key]
+ names_tgt:write(
+ ('\n {%s, {"%s", %u}, %s},'):format(
+ key,
+ name,
+ #name,
+ pref_idx == i and 'NULL' or ('&key_names_table[%u].name'):format(pref_idx - 1)
+ )
+ )
+end
+
+for _, keycode in ipairs(extra_keys) do
+ local key = keycode[1]
+ local name = keycode[2]
+ names_tgt:write(('\n {%s, {"%s", %u}, NULL},'):format(key, name, #name))
+end
+
+names_tgt:write('\n};\n\n')
+names_tgt:write('static ' .. hashfun)
+names_tgt:close()
diff --git a/src/gen/hashy.lua b/src/gen/hashy.lua
index 74b7655324..48292bfc0e 100644
--- a/src/gen/hashy.lua
+++ b/src/gen/hashy.lua
@@ -54,7 +54,7 @@ function M.build_pos_hash(strings)
return len_pos_buckets, maxlen, worst_buck_size
end
-function M.switcher(put, tab, maxlen, worst_buck_size)
+function M.switcher(put, tab, maxlen, worst_buck_size, lower)
local neworder = {} --- @type string[]
put ' switch (len) {\n'
local bucky = worst_buck_size > 1
@@ -66,7 +66,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
local keys = vim.tbl_keys(posbuck)
if #keys > 1 then
table.sort(keys)
- put('switch (str[' .. (pos - 1) .. ']) {\n')
+ put(('switch (%s(str[%s])) {\n'):format(lower and 'TOLOWER_ASC' or '', pos - 1))
for _, c in ipairs(keys) do
local buck = posbuck[c]
local startidx = #neworder
@@ -102,7 +102,7 @@ function M.switcher(put, tab, maxlen, worst_buck_size)
return neworder
end
-function M.hashy_hash(name, strings, access)
+function M.hashy_hash(name, strings, access, lower)
local stats = {}
local put = function(str)
table.insert(stats, str)
@@ -116,27 +116,27 @@ function M.hashy_hash(name, strings, access)
else
put(' int low = -1;\n')
end
- local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size)
+ local neworder = M.switcher(put, len_pos_buckets, maxlen, worst_buck_size, lower)
if maxlen == 1 then
put([[
return -1;
]])
elseif worst_buck_size > 1 then
- put([[
+ put(([[
for (int i = low; i < high; i++) {
- if (!memcmp(str, ]] .. access('i') .. [[, len)) {
+ if (!%s(str, %s, len)) {
return i;
}
}
return -1;
-]])
+]]):format(lower and 'mb_strnicmp' or 'memcmp', access('i')))
else
- put([[
- if (low < 0 || memcmp(str, ]] .. access('low') .. [[, len)) {
+ put(([[
+ if (low < 0 || %s(str, %s, len)) {
return -1;
}
return low;
-]])
+]]):format(lower and 'mb_strnicmp' or 'memcmp', access('low')))
end
put '}\n\n'
return neworder, table.concat(stats)
diff --git a/src/nvim/CMakeLists.txt b/src/nvim/CMakeLists.txt
index 36bcd5fbce..39c78b3228 100644
--- a/src/nvim/CMakeLists.txt
+++ b/src/nvim/CMakeLists.txt
@@ -304,6 +304,7 @@ set(CHAR_BLOB_GENERATOR ${GENERATOR_DIR}/gen_char_blob.lua)
set(EVENTS_GENERATOR ${GENERATOR_DIR}/gen_events.lua)
set(EX_CMDS_GENERATOR ${GENERATOR_DIR}/gen_ex_cmds.lua)
set(FUNCS_GENERATOR ${GENERATOR_DIR}/gen_eval.lua)
+set(KEYCODES_GENERATOR ${GENERATOR_DIR}/gen_keycodes.lua)
set(GENERATOR_C_GRAMMAR ${GENERATOR_DIR}/c_grammar.lua)
set(GENERATOR_HASHY ${GENERATOR_DIR}/hashy.lua)
set(GENERATOR_PRELOAD ${GENERATOR_DIR}/preload_nlua.lua)
@@ -318,6 +319,7 @@ set(GENERATED_EVENTS_NAMES_MAP ${GENERATED_DIR}/auevents_name_map.generated.h)
set(GENERATED_EX_CMDS_DEFS ${GENERATED_DIR}/ex_cmds_defs.generated.h)
set(GENERATED_EX_CMDS_ENUM ${GENERATED_INCLUDES_DIR}/ex_cmds_enum.generated.h)
set(GENERATED_FUNCS ${GENERATED_DIR}/funcs.generated.h)
+set(GENERATED_KEYCODE_NAMES ${GENERATED_DIR}/keycode_names.generated.h)
set(GENERATED_API_METADATA ${GENERATED_DIR}/api/private/api_metadata.generated.h)
set(GENERATED_KEYSETS_DEFS ${GENERATED_DIR}/keysets_defs.generated.h)
set(GENERATED_OPTIONS ${GENERATED_DIR}/options.generated.h)
@@ -673,6 +675,7 @@ list(APPEND NVIM_GENERATED_FOR_SOURCES
"${GENERATED_API_DISPATCH}"
"${GENERATED_EX_CMDS_DEFS}"
"${GENERATED_EVENTS_NAMES_MAP}"
+ "${GENERATED_KEYCODE_NAMES}"
"${GENERATED_OPTIONS}"
"${GENERATED_OPTIONS_MAP}"
"${VIM_MODULE_FILE}"
@@ -696,6 +699,11 @@ add_custom_command(OUTPUT ${GENERATED_EVENTS_ENUM} ${GENERATED_EVENTS_NAMES_MAP}
DEPENDS ${LUA_GEN_DEPS} ${EVENTS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/auevents.lua
)
+add_custom_command(OUTPUT ${GENERATED_KEYCODE_NAMES}
+ COMMAND ${LUA_GEN} ${KEYCODES_GENERATOR} ${GENERATED_KEYCODE_NAMES}
+ DEPENDS ${LUA_GEN_DEPS} ${KEYCODES_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/keycodes.lua
+)
+
add_custom_command(OUTPUT ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
COMMAND ${LUA_GEN} ${OPTIONS_GENERATOR} ${GENERATED_OPTIONS} ${GENERATED_OPTIONS_ENUM} ${GENERATED_OPTIONS_MAP} ${GENERATED_OPTION_VARS}
DEPENDS ${LUA_GEN_DEPS} ${OPTIONS_GENERATOR} ${CMAKE_CURRENT_LIST_DIR}/options.lua
diff --git a/src/nvim/keycodes.c b/src/nvim/keycodes.c
index b6ba73f7c1..9cb2321eee 100644
--- a/src/nvim/keycodes.c
+++ b/src/nvim/keycodes.c
@@ -6,6 +6,7 @@
#include <string.h>
#include <uv.h>
+#include "nvim/api/private/defs.h"
#include "nvim/ascii_defs.h"
#include "nvim/charset.h"
#include "nvim/errors.h"
@@ -24,6 +25,7 @@
#include "nvim/strings.h"
#ifdef INCLUDE_GENERATED_DECLARATIONS
+# include "keycode_names.generated.h"
# include "keycodes.c.generated.h"
#endif
@@ -142,214 +144,6 @@ static uint8_t modifier_keys_table[] = {
NUL
};
-static const struct key_name_entry {
- int key; // Special key code or ascii value
- const char *name; // Name of key
-} key_names_table[] = {
- { ' ', "Space" },
- { TAB, "Tab" },
- { K_TAB, "Tab" },
- { NL, "NL" },
- { NL, "NewLine" }, // Alternative name
- { NL, "LineFeed" }, // Alternative name
- { NL, "LF" }, // Alternative name
- { CAR, "CR" },
- { CAR, "Return" }, // Alternative name
- { CAR, "Enter" }, // Alternative name
- { K_BS, "BS" },
- { K_BS, "BackSpace" }, // Alternative name
- { ESC, "Esc" },
- { ESC, "Escape" }, // Alternative name
- { CSI, "CSI" },
- { '|', "Bar" },
- { '\\', "Bslash" },
- { K_DEL, "Del" },
- { K_DEL, "Delete" }, // Alternative name
- { K_KDEL, "kDel" },
- { K_KDEL, "KPPeriod" }, // libtermkey name
- { K_UP, "Up" },
- { K_DOWN, "Down" },
- { K_LEFT, "Left" },
- { K_RIGHT, "Right" },
- { K_XUP, "xUp" },
- { K_XDOWN, "xDown" },
- { K_XLEFT, "xLeft" },
- { K_XRIGHT, "xRight" },
- { K_KUP, "kUp" },
- { K_KUP, "KP8" },
- { K_KDOWN, "kDown" },
- { K_KDOWN, "KP2" },
- { K_KLEFT, "kLeft" },
- { K_KLEFT, "KP4" },
- { K_KRIGHT, "kRight" },
- { K_KRIGHT, "KP6" },
-
- { K_F1, "F1" },
- { K_F2, "F2" },
- { K_F3, "F3" },
- { K_F4, "F4" },
- { K_F5, "F5" },
- { K_F6, "F6" },
- { K_F7, "F7" },
- { K_F8, "F8" },
- { K_F9, "F9" },
- { K_F10, "F10" },
-
- { K_F11, "F11" },
- { K_F12, "F12" },
- { K_F13, "F13" },
- { K_F14, "F14" },
- { K_F15, "F15" },
- { K_F16, "F16" },
- { K_F17, "F17" },
- { K_F18, "F18" },
- { K_F19, "F19" },
- { K_F20, "F20" },
-
- { K_F21, "F21" },
- { K_F22, "F22" },
- { K_F23, "F23" },
- { K_F24, "F24" },
- { K_F25, "F25" },
- { K_F26, "F26" },
- { K_F27, "F27" },
- { K_F28, "F28" },
- { K_F29, "F29" },
- { K_F30, "F30" },
-
- { K_F31, "F31" },
- { K_F32, "F32" },
- { K_F33, "F33" },
- { K_F34, "F34" },
- { K_F35, "F35" },
- { K_F36, "F36" },
- { K_F37, "F37" },
- { K_F38, "F38" },
- { K_F39, "F39" },
- { K_F40, "F40" },
-
- { K_F41, "F41" },
- { K_F42, "F42" },
- { K_F43, "F43" },
- { K_F44, "F44" },
- { K_F45, "F45" },
- { K_F46, "F46" },
- { K_F47, "F47" },
- { K_F48, "F48" },
- { K_F49, "F49" },
- { K_F50, "F50" },
-
- { K_F51, "F51" },
- { K_F52, "F52" },
- { K_F53, "F53" },
- { K_F54, "F54" },
- { K_F55, "F55" },
- { K_F56, "F56" },
- { K_F57, "F57" },
- { K_F58, "F58" },
- { K_F59, "F59" },
- { K_F60, "F60" },
-
- { K_F61, "F61" },
- { K_F62, "F62" },
- { K_F63, "F63" },
-
- { K_XF1, "xF1" },
- { K_XF2, "xF2" },
- { K_XF3, "xF3" },
- { K_XF4, "xF4" },
-
- { K_HELP, "Help" },
- { K_UNDO, "Undo" },
- { K_FIND, "Find" }, // DEC key, often used as 'Home'
- { K_KSELECT, "Select" }, // DEC key, often used as 'End'
- { K_INS, "Insert" },
- { K_INS, "Ins" }, // Alternative name
- { K_KINS, "kInsert" },
- { K_KINS, "KP0" },
- { K_HOME, "Home" },
- { K_KHOME, "kHome" },
- { K_KHOME, "KP7" },
- { K_XHOME, "xHome" },
- { K_ZHOME, "zHome" },
- { K_END, "End" },
- { K_KEND, "kEnd" },
- { K_KEND, "KP1" },
- { K_XEND, "xEnd" },
- { K_ZEND, "zEnd" },
- { K_PAGEUP, "PageUp" },
- { K_PAGEDOWN, "PageDown" },
- { K_KPAGEUP, "kPageUp" },
- { K_KPAGEUP, "KP9" },
- { K_KPAGEDOWN, "kPageDown" },
- { K_KPAGEDOWN, "KP3" },
- { K_KORIGIN, "kOrigin" },
- { K_KORIGIN, "KP5" },
-
- { K_KPLUS, "kPlus" },
- { K_KPLUS, "KPPlus" },
- { K_KMINUS, "kMinus" },
- { K_KMINUS, "KPMinus" },
- { K_KDIVIDE, "kDivide" },
- { K_KDIVIDE, "KPDiv" },
- { K_KMULTIPLY, "kMultiply" },
- { K_KMULTIPLY, "KPMult" },
- { K_KENTER, "kEnter" },
- { K_KENTER, "KPEnter" },
- { K_KPOINT, "kPoint" },
- { K_KCOMMA, "kComma" },
- { K_KCOMMA, "KPComma" },
- { K_KEQUAL, "kEqual" },
- { K_KEQUAL, "KPEquals" },
-
- { K_K0, "k0" },
- { K_K1, "k1" },
- { K_K2, "k2" },
- { K_K3, "k3" },
- { K_K4, "k4" },
- { K_K5, "k5" },
- { K_K6, "k6" },
- { K_K7, "k7" },
- { K_K8, "k8" },
- { K_K9, "k9" },
-
- { '<', "lt" },
-
- { K_MOUSE, "Mouse" },
- { K_LEFTMOUSE, "LeftMouse" },
- { K_LEFTMOUSE_NM, "LeftMouseNM" },
- { K_LEFTDRAG, "LeftDrag" },
- { K_LEFTRELEASE, "LeftRelease" },
- { K_LEFTRELEASE_NM, "LeftReleaseNM" },
- { K_MOUSEMOVE, "MouseMove" },
- { K_MIDDLEMOUSE, "MiddleMouse" },
- { K_MIDDLEDRAG, "MiddleDrag" },
- { K_MIDDLERELEASE, "MiddleRelease" },
- { K_RIGHTMOUSE, "RightMouse" },
- { K_RIGHTDRAG, "RightDrag" },
- { K_RIGHTRELEASE, "RightRelease" },
- { K_MOUSEDOWN, "ScrollWheelUp" },
- { K_MOUSEUP, "ScrollWheelDown" },
- { K_MOUSELEFT, "ScrollWheelRight" },
- { K_MOUSERIGHT, "ScrollWheelLeft" },
- { K_MOUSEDOWN, "MouseDown" }, // OBSOLETE: Use
- { K_MOUSEUP, "MouseUp" }, // ScrollWheelXXX instead
- { K_X1MOUSE, "X1Mouse" },
- { K_X1DRAG, "X1Drag" },
- { K_X1RELEASE, "X1Release" },
- { K_X2MOUSE, "X2Mouse" },
- { K_X2DRAG, "X2Drag" },
- { K_X2RELEASE, "X2Release" },
- { K_DROP, "Drop" },
- { K_ZERO, "Nul" },
- { K_SNR, "SNR" },
- { K_PLUG, "Plug" },
- { K_IGNORE, "Ignore" },
- { K_COMMAND, "Cmd" },
- { 0, NULL }
- // NOTE: When adding a long name update MAX_KEY_NAME_LEN.
-};
-
static struct mousetable {
int pseudo_code; // Code for pseudo mouse event
int button; // Which mouse button is it?
@@ -544,15 +338,18 @@ char *get_special_key_name(int c, int modifiers)
}
}
} else { // use name of special key
- size_t len = strlen(key_names_table[table_idx].name);
+ const String *s = key_names_table[table_idx].pref_name != NULL
+ ? key_names_table[table_idx].pref_name
+ : &key_names_table[table_idx].name;
- if ((int)len + idx + 2 <= MAX_KEY_NAME_LEN) {
- STRCPY(string + idx, key_names_table[table_idx].name);
- idx += (int)len;
+ if ((int)s->size + idx + 2 <= MAX_KEY_NAME_LEN) {
+ STRCPY(string + idx, s->data);
+ idx += (int)s->size;
}
}
string[idx++] = '>';
string[idx] = NUL;
+
return string;
}
@@ -795,17 +592,13 @@ static int extract_modifiers(int key, int *modp, const bool simplify, bool *cons
/// @return the index when found, -1 when not found.
int find_special_key_in_table(int c)
{
- int i;
-
- for (i = 0; key_names_table[i].name != NULL; i++) {
+ for (int i = 0; i < (int)ARRAY_SIZE(key_names_table); i++) {
if (c == key_names_table[i].key) {
- break;
+ return i;
}
}
- if (key_names_table[i].name == NULL) {
- i = -1;
- }
- return i;
+
+ return -1;
}
/// Find the special key with the given name
@@ -823,20 +616,13 @@ int get_special_key_code(const char *name)
return TERMCAP2KEY((uint8_t)name[2], (uint8_t)name[3]);
}
- for (int i = 0; key_names_table[i].name != NULL; i++) {
- const char *const table_name = key_names_table[i].name;
- int j;
- for (j = 0; ascii_isident((uint8_t)name[j]) && table_name[j] != NUL; j++) {
- if (TOLOWER_ASC(table_name[j]) != TOLOWER_ASC((uint8_t)name[j])) {
- break;
- }
- }
- if (!ascii_isident((uint8_t)name[j]) && table_name[j] == NUL) {
- return key_names_table[i].key;
- }
+ const char *name_end = name;
+ while (ascii_isident(*name_end)) {
+ name_end++;
}
- return 0;
+ int idx = get_special_key_code_hash(name, (size_t)(name_end - name));
+ return idx >= 0 ? key_names_table[idx].key : 0;
}
/// Look up the given mouse code to return the relevant information in the other arguments.
diff --git a/src/nvim/keycodes.lua b/src/nvim/keycodes.lua
new file mode 100644
index 0000000000..04f09eb005
--- /dev/null
+++ b/src/nvim/keycodes.lua
@@ -0,0 +1,208 @@
+return {
+ --- @type [string, string][] List of [key, name] tuples.
+ --- For keys with multiple names, put the preferred name first.
+ --- For multiple keys with the same name, put the preferred key first.
+ names = {
+ { [[' ']], 'Space' },
+ { [[TAB]], 'Tab' },
+ { [[K_TAB]], 'Tab' },
+ { [[NL]], 'NL' },
+ { [[NL]], 'NewLine' }, -- Alternative name
+ { [[NL]], 'LineFeed' }, -- Alternative name
+ { [[NL]], 'LF' }, -- Alternative name
+ { [[CAR]], 'CR' },
+ { [[CAR]], 'Return' }, -- Alternative name
+ { [[CAR]], 'Enter' }, -- Alternative name
+ { [[K_BS]], 'BS' },
+ { [[K_BS]], 'BackSpace' }, -- Alternative name
+ { [[ESC]], 'Esc' },
+ { [[ESC]], 'Escape' }, -- Alternative name
+ { [[CSI]], 'CSI' },
+ { [['|']], 'Bar' },
+ { [['\\']], 'Bslash' },
+ { [[K_DEL]], 'Del' },
+ { [[K_DEL]], 'Delete' }, -- Alternative name
+ { [[K_KDEL]], 'kDel' },
+ { [[K_KDEL]], 'KPPeriod' }, -- libtermkey name
+ { [[K_UP]], 'Up' },
+ { [[K_DOWN]], 'Down' },
+ { [[K_LEFT]], 'Left' },
+ { [[K_RIGHT]], 'Right' },
+ { [[K_XUP]], 'xUp' },
+ { [[K_XDOWN]], 'xDown' },
+ { [[K_XLEFT]], 'xLeft' },
+ { [[K_XRIGHT]], 'xRight' },
+ { [[K_KUP]], 'kUp' },
+ { [[K_KUP]], 'KP8' },
+ { [[K_KDOWN]], 'kDown' },
+ { [[K_KDOWN]], 'KP2' },
+ { [[K_KLEFT]], 'kLeft' },
+ { [[K_KLEFT]], 'KP4' },
+ { [[K_KRIGHT]], 'kRight' },
+ { [[K_KRIGHT]], 'KP6' },
+
+ { [[K_F1]], 'F1' },
+ { [[K_F2]], 'F2' },
+ { [[K_F3]], 'F3' },
+ { [[K_F4]], 'F4' },
+ { [[K_F5]], 'F5' },
+ { [[K_F6]], 'F6' },
+ { [[K_F7]], 'F7' },
+ { [[K_F8]], 'F8' },
+ { [[K_F9]], 'F9' },
+ { [[K_F10]], 'F10' },
+
+ { [[K_F11]], 'F11' },
+ { [[K_F12]], 'F12' },
+ { [[K_F13]], 'F13' },
+ { [[K_F14]], 'F14' },
+ { [[K_F15]], 'F15' },
+ { [[K_F16]], 'F16' },
+ { [[K_F17]], 'F17' },
+ { [[K_F18]], 'F18' },
+ { [[K_F19]], 'F19' },
+ { [[K_F20]], 'F20' },
+
+ { [[K_F21]], 'F21' },
+ { [[K_F22]], 'F22' },
+ { [[K_F23]], 'F23' },
+ { [[K_F24]], 'F24' },
+ { [[K_F25]], 'F25' },
+ { [[K_F26]], 'F26' },
+ { [[K_F27]], 'F27' },
+ { [[K_F28]], 'F28' },
+ { [[K_F29]], 'F29' },
+ { [[K_F30]], 'F30' },
+
+ { [[K_F31]], 'F31' },
+ { [[K_F32]], 'F32' },
+ { [[K_F33]], 'F33' },
+ { [[K_F34]], 'F34' },
+ { [[K_F35]], 'F35' },
+ { [[K_F36]], 'F36' },
+ { [[K_F37]], 'F37' },
+ { [[K_F38]], 'F38' },
+ { [[K_F39]], 'F39' },
+ { [[K_F40]], 'F40' },
+
+ { [[K_F41]], 'F41' },
+ { [[K_F42]], 'F42' },
+ { [[K_F43]], 'F43' },
+ { [[K_F44]], 'F44' },
+ { [[K_F45]], 'F45' },
+ { [[K_F46]], 'F46' },
+ { [[K_F47]], 'F47' },
+ { [[K_F48]], 'F48' },
+ { [[K_F49]], 'F49' },
+ { [[K_F50]], 'F50' },
+
+ { [[K_F51]], 'F51' },
+ { [[K_F52]], 'F52' },
+ { [[K_F53]], 'F53' },
+ { [[K_F54]], 'F54' },
+ { [[K_F55]], 'F55' },
+ { [[K_F56]], 'F56' },
+ { [[K_F57]], 'F57' },
+ { [[K_F58]], 'F58' },
+ { [[K_F59]], 'F59' },
+ { [[K_F60]], 'F60' },
+
+ { [[K_F61]], 'F61' },
+ { [[K_F62]], 'F62' },
+ { [[K_F63]], 'F63' },
+
+ { [[K_XF1]], 'xF1' },
+ { [[K_XF2]], 'xF2' },
+ { [[K_XF3]], 'xF3' },
+ { [[K_XF4]], 'xF4' },
+
+ { [[K_HELP]], 'Help' },
+ { [[K_UNDO]], 'Undo' },
+ { [[K_FIND]], 'Find' }, -- DEC key, often used as 'Home'
+ { [[K_KSELECT]], 'Select' }, -- DEC key, often used as 'End'
+ { [[K_INS]], 'Insert' },
+ { [[K_INS]], 'Ins' }, -- Alternative name
+ { [[K_KINS]], 'kInsert' },
+ { [[K_KINS]], 'KP0' },
+ { [[K_HOME]], 'Home' },
+ { [[K_KHOME]], 'kHome' },
+ { [[K_KHOME]], 'KP7' },
+ { [[K_XHOME]], 'xHome' },
+ { [[K_ZHOME]], 'zHome' },
+ { [[K_END]], 'End' },
+ { [[K_KEND]], 'kEnd' },
+ { [[K_KEND]], 'KP1' },
+ { [[K_XEND]], 'xEnd' },
+ { [[K_ZEND]], 'zEnd' },
+ { [[K_PAGEUP]], 'PageUp' },
+ { [[K_PAGEDOWN]], 'PageDown' },
+ { [[K_KPAGEUP]], 'kPageUp' },
+ { [[K_KPAGEUP]], 'KP9' },
+ { [[K_KPAGEDOWN]], 'kPageDown' },
+ { [[K_KPAGEDOWN]], 'KP3' },
+ { [[K_KORIGIN]], 'kOrigin' },
+ { [[K_KORIGIN]], 'KP5' },
+
+ { [[K_KPLUS]], 'kPlus' },
+ { [[K_KPLUS]], 'KPPlus' },
+ { [[K_KMINUS]], 'kMinus' },
+ { [[K_KMINUS]], 'KPMinus' },
+ { [[K_KDIVIDE]], 'kDivide' },
+ { [[K_KDIVIDE]], 'KPDiv' },
+ { [[K_KMULTIPLY]], 'kMultiply' },
+ { [[K_KMULTIPLY]], 'KPMult' },
+ { [[K_KENTER]], 'kEnter' },
+ { [[K_KENTER]], 'KPEnter' },
+ { [[K_KPOINT]], 'kPoint' },
+ { [[K_KCOMMA]], 'kComma' },
+ { [[K_KCOMMA]], 'KPComma' },
+ { [[K_KEQUAL]], 'kEqual' },
+ { [[K_KEQUAL]], 'KPEquals' },
+
+ { [[K_K0]], 'k0' },
+ { [[K_K1]], 'k1' },
+ { [[K_K2]], 'k2' },
+ { [[K_K3]], 'k3' },
+ { [[K_K4]], 'k4' },
+ { [[K_K5]], 'k5' },
+ { [[K_K6]], 'k6' },
+ { [[K_K7]], 'k7' },
+ { [[K_K8]], 'k8' },
+ { [[K_K9]], 'k9' },
+
+ { [['<']], 'lt' },
+
+ { [[K_MOUSE]], 'Mouse' },
+ { [[K_LEFTMOUSE]], 'LeftMouse' },
+ { [[K_LEFTMOUSE_NM]], 'LeftMouseNM' },
+ { [[K_LEFTDRAG]], 'LeftDrag' },
+ { [[K_LEFTRELEASE]], 'LeftRelease' },
+ { [[K_LEFTRELEASE_NM]], 'LeftReleaseNM' },
+ { [[K_MOUSEMOVE]], 'MouseMove' },
+ { [[K_MIDDLEMOUSE]], 'MiddleMouse' },
+ { [[K_MIDDLEDRAG]], 'MiddleDrag' },
+ { [[K_MIDDLERELEASE]], 'MiddleRelease' },
+ { [[K_RIGHTMOUSE]], 'RightMouse' },
+ { [[K_RIGHTDRAG]], 'RightDrag' },
+ { [[K_RIGHTRELEASE]], 'RightRelease' },
+ { [[K_MOUSEDOWN]], 'ScrollWheelUp' },
+ { [[K_MOUSEUP]], 'ScrollWheelDown' },
+ { [[K_MOUSELEFT]], 'ScrollWheelRight' },
+ { [[K_MOUSERIGHT]], 'ScrollWheelLeft' },
+ { [[K_MOUSEDOWN]], 'MouseDown' }, -- OBSOLETE: Use ScrollWheelUp instead
+ { [[K_MOUSEUP]], 'MouseUp' }, -- OBSOLETE: Use ScrollWheelDown instead
+ { [[K_X1MOUSE]], 'X1Mouse' },
+ { [[K_X1DRAG]], 'X1Drag' },
+ { [[K_X1RELEASE]], 'X1Release' },
+ { [[K_X2MOUSE]], 'X2Mouse' },
+ { [[K_X2DRAG]], 'X2Drag' },
+ { [[K_X2RELEASE]], 'X2Release' },
+ { [[K_DROP]], 'Drop' },
+ { [[K_ZERO]], 'Nul' },
+ { [[K_SNR]], 'SNR' },
+ { [[K_PLUG]], 'Plug' },
+ { [[K_IGNORE]], 'Ignore' },
+ { [[K_COMMAND]], 'Cmd' },
+ -- NOTE: When adding a long name update MAX_KEY_NAME_LEN.
+ },
+}
diff --git a/test/benchmark/keycodes_spec.lua b/test/benchmark/keycodes_spec.lua
new file mode 100644
index 0000000000..443df2a6fb
--- /dev/null
+++ b/test/benchmark/keycodes_spec.lua
@@ -0,0 +1,84 @@
+local n = require('test.functional.testnvim')()
+local clear = n.clear
+local api = n.api
+local fn = n.fn
+
+local keycodes = require('src.nvim.keycodes')
+
+describe('nvim_replace_termcodes performance', function()
+ it('200 calls with a key repeated 5000 times', function()
+ clear()
+ local stats = {}
+ local sum = 0
+ local ms = 1 / 1000000
+
+ for _, keycode in ipairs(keycodes.names) do
+ local notation = ('<%s>'):format(keycode[2])
+ local str = notation:rep(5000)
+
+ local start = vim.uv.hrtime()
+ for _ = 1, 200 do
+ api.nvim_replace_termcodes(str, false, true, true)
+ end
+ local elapsed = vim.uv.hrtime() - start
+
+ table.insert(stats, elapsed)
+ sum = sum + elapsed
+ io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
+ io.stdout:flush()
+ end
+ io.stdout:write('\n')
+
+ table.sort(stats)
+ print(('%18s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
+ print(
+ (' %14.6f ms'):rep(6):format(
+ sum / #stats * ms,
+ stats[1] * ms,
+ stats[1 + math.floor(#stats * 0.25)] * ms,
+ stats[1 + math.floor(#stats * 0.5)] * ms,
+ stats[1 + math.floor(#stats * 0.75)] * ms,
+ stats[#stats] * ms
+ )
+ )
+ end)
+end)
+
+describe('keytrans() performance', function()
+ it('200 calls with a key repeated 5000 times', function()
+ clear()
+ local stats = {}
+ local sum = 0
+ local ms = 1 / 1000000
+
+ for _, keycode in ipairs(keycodes.names) do
+ local notation = ('<%s>'):format(keycode[2])
+ local str = api.nvim_replace_termcodes(notation, false, true, true):rep(5000)
+
+ local start = vim.uv.hrtime()
+ for _ = 1, 200 do
+ fn.keytrans(str)
+ end
+ local elapsed = vim.uv.hrtime() - start
+
+ table.insert(stats, elapsed)
+ sum = sum + elapsed
+ io.stdout:write(('\n%-20s%14.6f ms'):format(notation, elapsed * ms))
+ io.stdout:flush()
+ end
+ io.stdout:write('\n')
+
+ table.sort(stats)
+ print((' %17s'):rep(6):format('avg', 'min', '25%', 'median', '75%', 'max'))
+ print(
+ (' %14.6f ms'):rep(6):format(
+ sum / #stats * ms,
+ stats[1] * ms,
+ stats[1 + math.floor(#stats * 0.25)] * ms,
+ stats[1 + math.floor(#stats * 0.5)] * ms,
+ stats[1 + math.floor(#stats * 0.75)] * ms,
+ stats[#stats] * ms
+ )
+ )
+ end)
+end)