feat(api): add support for lua function & description in keymap

Behavioral changes:

1. Added support for lua function in keymaps in
--------------------------------------------
- nvim_set_keymap
  Can set lua function as keymap rhs like following:
```lua
vim.api.nvim_{buf_}set_keymap('n', '<leader>lr', '', {callback = vim.lsp.buf.references})
```
  Note: lua function can only be set from lua . If api function being
  called from viml or over rpc this option isn't available.
- nvim_{buf_}get_keymap
  When called from lua, lua function is returned is `callback` key .
  But in other cases callback contains number of the function ref.
- :umap, nvim_del_keymap & nvim_buf_del_keymap clears lua keymaps correctly.
- :map commands for displaing rhs .
   For lua keymaps rhs is displayed as <Lua function ref_no>
   Note: lua keymap cannot be set through viml command / functions.
- mapargs()
  When dict is false it returns string in `<Lua function ref_no>`
  format (same format as :map commands).
  When dict is true it returns ref_no number in `callback` key.
- mapcheck()
  returns string in `<Lua function ref_no>` format (same format as :map commands).

2. Added support for keymap description
---------------------------------------
- nvim_{buf_}set_keymap: added `desc` option in opts table .
 ```lua
vim.api.nvim_set_keymap('n', '<leader>w', '<cmd>w<cr>', {desc='Save current file'})
```
- nvim_{buf_}get_keymap: contains `desc` in returned list.
- commands like `:nmap <leader>w` will show description in a new line below rhs.
- `maparg()` return dict contains `desc`.
This commit is contained in:
shadmansaleh
2021-12-30 07:10:45 +06:00
parent 5c1b8b77c5
commit b411f436d3
16 changed files with 460 additions and 69 deletions

View File

@@ -835,7 +835,7 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err)
/// @param[out] err Error details, if any
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key holds the associated buffer handle.
ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
ArrayOf(Dictionary) nvim_buf_get_keymap(uint64_t channel_id, Buffer buffer, String mode, Error *err)
FUNC_API_SINCE(3)
{
buf_T *buf = find_buffer_by_handle(buffer, err);
@@ -844,7 +844,7 @@ ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err)
return (Array)ARRAY_DICT_INIT;
}
return keymap_array(mode, buf);
return keymap_array(mode, buf, channel_id == LUA_INTERNAL_CALL);
}
/// Sets a buffer-local |mapping| for the given mode.

View File

@@ -29,6 +29,8 @@ return {
"script";
"expr";
"unique";
"callback";
"desc";
};
get_commands = {
"builtin";

View File

@@ -594,6 +594,7 @@ Array string_to_array(const String input, bool crlf)
void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String rhs,
Dict(keymap) *opts, Error *err)
{
LuaRef lua_funcref = LUA_NOREF;
bool global = (buffer == -1);
if (global) {
buffer = 0;
@@ -604,6 +605,9 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
return;
}
if (opts != NULL && opts->callback.type == kObjectTypeLuaRef) {
lua_funcref = api_new_luaref(opts->callback.data.luaref);
}
MapArguments parsed_args = MAP_ARGUMENTS_INIT;
if (opts) {
#define KEY_TO_BOOL(name) \
@@ -623,9 +627,13 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
parsed_args.buffer = !global;
set_maparg_lhs_rhs((char_u *)lhs.data, lhs.size,
(char_u *)rhs.data, rhs.size,
(char_u *)rhs.data, rhs.size, lua_funcref,
CPO_TO_CPO_FLAGS, &parsed_args);
if (opts != NULL && opts->desc.type == kObjectTypeString) {
parsed_args.desc = xstrdup(opts->desc.data.string.data);
} else {
parsed_args.desc = NULL;
}
if (parsed_args.lhs_len > MAXMAPLEN) {
api_set_error(err, kErrorTypeValidation, "LHS exceeds maximum map length: %s", lhs.data);
goto fail_and_free;
@@ -658,7 +666,8 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
bool is_noremap = parsed_args.noremap;
assert(!(is_unmap && is_noremap));
if (!is_unmap && (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
if (!is_unmap && lua_funcref == LUA_NOREF
&& (parsed_args.rhs_len == 0 && !parsed_args.rhs_is_noop)) {
if (rhs.size == 0) { // assume that the user wants RHS to be a <Nop>
parsed_args.rhs_is_noop = true;
} else {
@@ -668,9 +677,13 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
api_set_error(err, kErrorTypeValidation, "Parsing of nonempty RHS failed: %s", rhs.data);
goto fail_and_free;
}
} else if (is_unmap && parsed_args.rhs_len) {
api_set_error(err, kErrorTypeValidation,
"Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
} else if (is_unmap && (parsed_args.rhs_len || parsed_args.rhs_lua != LUA_NOREF)) {
if (parsed_args.rhs_len) {
api_set_error(err, kErrorTypeValidation,
"Gave nonempty RHS in unmap command: %s", parsed_args.rhs);
} else {
api_set_error(err, kErrorTypeValidation, "Gave nonempty RHS for unmap");
}
goto fail_and_free;
}
@@ -700,9 +713,12 @@ void modify_keymap(Buffer buffer, bool is_unmap, String mode, String lhs, String
goto fail_and_free;
} // switch
parsed_args.rhs_lua = LUA_NOREF; // don't clear ref on success
fail_and_free:
NLUA_CLEAR_REF(parsed_args.rhs_lua);
xfree(parsed_args.rhs);
xfree(parsed_args.orig_rhs);
XFREE_CLEAR(parsed_args.desc);
return;
}
@@ -1052,8 +1068,9 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...)
///
/// @param mode The abbreviation for the mode
/// @param buf The buffer to get the mapping array. NULL for global
/// @param from_lua Whether it is called from internal lua api.
/// @returns Array of maparg()-like dictionaries describing mappings
ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf, bool from_lua)
{
Array mappings = ARRAY_DICT_INIT;
dict_T *const dict = tv_dict_alloc();
@@ -1073,8 +1090,19 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf)
// Check for correct mode
if (int_mode & current_maphash->m_mode) {
mapblock_fill_dict(dict, current_maphash, buffer_value, false);
ADD(mappings, vim_to_object((typval_T[]) { { .v_type = VAR_DICT, .vval.v_dict = dict } }));
Object api_dict = vim_to_object((typval_T[]) { { .v_type = VAR_DICT,
.vval.v_dict = dict } });
if (from_lua) {
Dictionary d = api_dict.data.dictionary;
for (size_t j = 0; j < d.size; j++) {
if (strequal("callback", d.items[j].key.data)) {
d.items[j].value.type = kObjectTypeLuaRef;
d.items[j].value.data.luaref = api_new_luaref((LuaRef)d.items[j].value.data.integer);
break;
}
}
}
ADD(mappings, api_dict);
tv_dict_clear(dict);
}
}

View File

@@ -1538,10 +1538,10 @@ Dictionary nvim_get_mode(void)
/// @param mode Mode short-name ("n", "i", "v", ...)
/// @returns Array of maparg()-like dictionaries describing mappings.
/// The "buffer" key is always zero.
ArrayOf(Dictionary) nvim_get_keymap(String mode)
ArrayOf(Dictionary) nvim_get_keymap(uint64_t channel_id, String mode)
FUNC_API_SINCE(3)
{
return keymap_array(mode, NULL);
return keymap_array(mode, NULL, channel_id == LUA_INTERNAL_CALL);
}
/// Sets a global |mapping| for the given mode.
@@ -1566,7 +1566,10 @@ ArrayOf(Dictionary) nvim_get_keymap(String mode)
/// @param lhs Left-hand-side |{lhs}| of the mapping.
/// @param rhs Right-hand-side |{rhs}| of the mapping.
/// @param opts Optional parameters map. Accepts all |:map-arguments|
/// as keys excluding |<buffer>| but including |noremap|.
/// as keys excluding |<buffer>| but including |noremap| and "desc".
/// |desc| can be used to give a description to keymap.
/// When called from Lua, also accepts a "callback" key that takes
/// a Lua function to call when the mapping is executed.
/// Values are Booleans. Unknown key is an error.
/// @param[out] err Error details, if any.
void nvim_set_keymap(String mode, String lhs, String rhs, Dict(keymap) *opts, Error *err)