lua: add vim.register_keystroke_callback (#12536)

* feat: Add vim.register_keystroke_callback

* fixup: Forgot to remove mention of old option

* fixup: Answer jamessan comments

* fixup: Answer norcalli comments

* fixup: portability

* Update runtime/doc/lua.txt

Co-authored-by: Ashkan Kiani <ashkan.k.kiani@gmail.com>
This commit is contained in:
TJ DeVries
2020-08-14 10:03:17 -04:00
committed by GitHub
parent aa48c1c724
commit 3ccdbc570d
6 changed files with 235 additions and 1 deletions

View File

@@ -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
}

View File

@@ -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