aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
Diffstat (limited to 'src')
-rw-r--r--src/nvim/getchar.c4
-rw-r--r--src/nvim/keymap.c13
-rw-r--r--src/nvim/lua/executor.c37
-rw-r--r--src/nvim/lua/vim.lua56
4 files changed, 109 insertions, 1 deletions
diff --git a/src/nvim/getchar.c b/src/nvim/getchar.c
index 5ab5a7db1b..dc11e4a232 100644
--- a/src/nvim/getchar.c
+++ b/src/nvim/getchar.c
@@ -27,6 +27,7 @@
#include "nvim/ex_docmd.h"
#include "nvim/ex_getln.h"
#include "nvim/func_attr.h"
+#include "nvim/lua/executor.h"
#include "nvim/main.h"
#include "nvim/mbyte.h"
#include "nvim/memline.h"
@@ -1535,6 +1536,9 @@ int vgetc(void)
*/
may_garbage_collect = false;
+ // Exec lua callbacks for on_keystroke
+ nlua_execute_log_keystroke(c);
+
return c;
}
diff --git a/src/nvim/keymap.c b/src/nvim/keymap.c
index a553110552..2b6f022d9d 100644
--- a/src/nvim/keymap.c
+++ b/src/nvim/keymap.c
@@ -530,13 +530,24 @@ unsigned int trans_special(const char_u **srcp, const size_t src_len,
{
int modifiers = 0;
int key;
- unsigned int dlen = 0;
key = find_special_key(srcp, src_len, &modifiers, keycode, false, in_string);
if (key == 0) {
return 0;
}
+ return special_to_buf(key, modifiers, keycode, dst);
+}
+
+/// Put the character sequence for "key" with "modifiers" into "dst" and return
+/// the resulting length.
+/// When "keycode" is TRUE prefer key code, e.g. K_DEL instead of DEL.
+/// The sequence is not NUL terminated.
+/// This is how characters in a string are encoded.
+unsigned int special_to_buf(int key, int modifiers, bool keycode, char_u *dst)
+{
+ unsigned int dlen = 0;
+
// Put the appropriate modifier in a string.
if (modifiers != 0) {
dst[dlen++] = K_SPECIAL;
diff --git a/src/nvim/lua/executor.c b/src/nvim/lua/executor.c
index 86da517685..5ad9731a97 100644
--- a/src/nvim/lua/executor.c
+++ b/src/nvim/lua/executor.c
@@ -1465,3 +1465,40 @@ void nlua_free_typval_dict(dict_T *const d)
d->lua_table_ref = LUA_NOREF;
}
}
+
+void nlua_execute_log_keystroke(int c)
+{
+ char_u buf[NUMBUFLEN];
+ size_t buf_len = special_to_buf(c, mod_mask, false, buf);
+
+ lua_State *const lstate = nlua_enter();
+
+#ifndef NDEBUG
+ int top = lua_gettop(lstate);
+#endif
+
+ // [ vim ]
+ lua_getglobal(lstate, "vim");
+
+ // [ vim, vim._log_keystroke ]
+ lua_getfield(lstate, -1, "_log_keystroke");
+ luaL_checktype(lstate, -1, LUA_TFUNCTION);
+
+ // [ vim, vim._log_keystroke, buf ]
+ lua_pushlstring(lstate, (const char *)buf, buf_len);
+
+ if (lua_pcall(lstate, 1, 0, 0)) {
+ nlua_error(
+ lstate,
+ _("Error executing vim.log_keystroke lua callback: %.*s"));
+ }
+
+ // [ vim ]
+ lua_pop(lstate, 1);
+
+#ifndef NDEBUG
+ // [ ]
+ assert(top == lua_gettop(lstate));
+#endif
+}
+
diff --git a/src/nvim/lua/vim.lua b/src/nvim/lua/vim.lua
index 820b237c4f..bfa8b91208 100644
--- a/src/nvim/lua/vim.lua
+++ b/src/nvim/lua/vim.lua
@@ -489,4 +489,60 @@ function vim.defer_fn(fn, timeout)
return timer
end
+local on_keystroke_callbacks = {}
+
+--- Register a lua {fn} with an {id} to be run after every keystroke.
+---
+--@param fn function: Function to call. It should take one argument, which is a string.
+--- The string will contain the literal keys typed.
+--- See |i_CTRL-V|
+---
+--- If {fn} is nil, it removes the callback for the associated {ns_id}
+--@param ns_id number? Namespace ID. If not passed or 0, will generate and return a new
+--- namespace ID from |nvim_create_namesapce()|
+---
+--@return number Namespace ID associated with {fn}
+---
+--@note {fn} will be automatically removed if an error occurs while calling.
+--- This is to prevent the annoying situation of every keystroke erroring
+--- while trying to remove a broken callback.
+--@note {fn} will not be cleared from |nvim_buf_clear_namespace()|
+--@note {fn} will receive the keystrokes after mappings have been evaluated
+function vim.register_keystroke_callback(fn, ns_id)
+ vim.validate {
+ fn = { fn, 'c', true},
+ ns_id = { ns_id, 'n', true }
+ }
+
+ if ns_id == nil or ns_id == 0 then
+ ns_id = vim.api.nvim_create_namespace('')
+ end
+
+ on_keystroke_callbacks[ns_id] = fn
+ return ns_id
+end
+
+--- Function that executes the keystroke callbacks.
+--@private
+function vim._log_keystroke(char)
+ local failed_ns_ids = {}
+ local failed_messages = {}
+ for k, v in pairs(on_keystroke_callbacks) do
+ local ok, err_msg = pcall(v, char)
+ if not ok then
+ vim.register_keystroke_callback(nil, k)
+
+ table.insert(failed_ns_ids, k)
+ table.insert(failed_messages, err_msg)
+ end
+ end
+
+ if failed_ns_ids[1] then
+ error(string.format(
+ "Error executing 'on_keystroke' with ns_ids of '%s'\n With messages: %s",
+ table.concat(failed_ns_ids, ", "),
+ table.concat(failed_messages, "\n")))
+ end
+end
+
return module