From 45626de63f2b8057c13df0466406c43f04d6a1e6 Mon Sep 17 00:00:00 2001 From: TJ DeVries Date: Thu, 25 May 2017 05:41:53 -0500 Subject: [PATCH] get_keymap API (#6236) * Add api function get keymap nvim_get_keymap(mode) nvim_buf_get_keymap(buffer, mode) --- src/nvim/api/buffer.c | 20 ++ src/nvim/api/private/helpers.c | 39 ++++ src/nvim/api/vim.c | 11 + src/nvim/buffer_defs.h | 7 +- src/nvim/eval.c | 57 +++-- src/nvim/getchar.c | 25 +- test/functional/api/keymap_spec.lua | 246 ++++++++++++++++++++ test/functional/eval/map_functions_spec.lua | 120 ++++++++++ 8 files changed, 502 insertions(+), 23 deletions(-) create mode 100644 test/functional/api/keymap_spec.lua create mode 100644 test/functional/eval/map_functions_spec.lua diff --git a/src/nvim/api/buffer.c b/src/nvim/api/buffer.c index 0b8d39d0d2..fc11708bd6 100644 --- a/src/nvim/api/buffer.c +++ b/src/nvim/api/buffer.c @@ -453,6 +453,26 @@ Integer nvim_buf_get_changedtick(Buffer buffer, Error *err) return buf->b_changedtick; } +/// Get a list of dictionaries describing buffer-local mappings +/// Note that the buffer key in the dictionary will represent the buffer +/// handle where the mapping is present +/// +/// @param mode The abbreviation for the mode +/// @param buffer_id Buffer handle +/// @param[out] err Error details, if any +/// @returns An array of maparg() like dictionaries describing mappings +ArrayOf(Dictionary) nvim_buf_get_keymap(Buffer buffer, String mode, Error *err) + FUNC_API_SINCE(3) +{ + buf_T *buf = find_buffer_by_handle(buffer, err); + + if (!buf) { + return (Array)ARRAY_DICT_INIT; + } + + return keymap_array(mode, buf); +} + /// Sets a buffer-scoped (b:) variable /// /// @param buffer Buffer handle diff --git a/src/nvim/api/private/helpers.c b/src/nvim/api/private/helpers.c index 1d7b305da3..ef789b3ed4 100644 --- a/src/nvim/api/private/helpers.c +++ b/src/nvim/api/private/helpers.c @@ -24,6 +24,7 @@ #include "nvim/option_defs.h" #include "nvim/version.h" #include "nvim/lib/kvec.h" +#include "nvim/getchar.h" /// Helper structure for vim_to_object typedef struct { @@ -1034,3 +1035,41 @@ void api_set_error(Error *err, ErrorType errType, const char *format, ...) err->type = errType; } + +/// Get an array containing dictionaries describing mappings +/// based on mode and buffer id +/// +/// @param mode The abbreviation for the mode +/// @param buf The buffer to get the mapping array. NULL for global +/// @returns An array of maparg() like dictionaries describing mappings +ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) +{ + Array mappings = ARRAY_DICT_INIT; + dict_T *const dict = tv_dict_alloc(); + + // Convert the string mode to the integer mode + // that is stored within each mapblock + char_u *p = (char_u *)mode.data; + int int_mode = get_map_mode(&p, 0); + + // Determine the desired buffer value + long buffer_value = (buf == NULL) ? 0 : buf->handle; + + for (int i = 0; i < MAX_MAPHASH; i++) { + for (const mapblock_T *current_maphash = get_maphash(i, buf); + current_maphash; + current_maphash = current_maphash->m_next) { + // 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 } })); + + tv_dict_clear(dict); + } + } + } + tv_dict_free(dict); + + return mappings; +} diff --git a/src/nvim/api/vim.c b/src/nvim/api/vim.c index 53e5f71fd4..0cffb2c87d 100644 --- a/src/nvim/api/vim.c +++ b/src/nvim/api/vim.c @@ -742,6 +742,17 @@ Dictionary nvim_get_mode(void) return rv; } +/// Get a list of dictionaries describing global (i.e. non-buffer) mappings +/// Note that the "buffer" key will be 0 to represent false. +/// +/// @param mode The abbreviation for the mode +/// @returns An array of maparg() like dictionaries describing mappings +ArrayOf(Dictionary) nvim_get_keymap(String mode) + FUNC_API_SINCE(3) +{ + return keymap_array(mode, NULL); +} + Array nvim_get_api_info(uint64_t channel_id) FUNC_API_SINCE(1) FUNC_API_ASYNC FUNC_API_REMOTE_ONLY { diff --git a/src/nvim/buffer_defs.h b/src/nvim/buffer_defs.h index 88fa9726a4..d96b9355f1 100644 --- a/src/nvim/buffer_defs.h +++ b/src/nvim/buffer_defs.h @@ -438,6 +438,9 @@ typedef TV_DICTITEM_STRUCT(sizeof("changedtick")) ChangedtickDictItem; #define BUF_HAS_QF_ENTRY 1 #define BUF_HAS_LL_ENTRY 2 +// Maximum number of maphash blocks we will have +#define MAX_MAPHASH 256 + /* * buffer: structure that holds information about one file * @@ -526,8 +529,8 @@ struct file_buffer { */ uint64_t b_chartab[4]; - /* Table used for mappings local to a buffer. */ - mapblock_T *(b_maphash[256]); + // Table used for mappings local to a buffer. + mapblock_T *(b_maphash[MAX_MAPHASH]); /* First abbreviation local to a buffer. */ mapblock_T *b_first_abbr; diff --git a/src/nvim/eval.c b/src/nvim/eval.c index e4b3128930..ecef470666 100644 --- a/src/nvim/eval.c +++ b/src/nvim/eval.c @@ -12102,22 +12102,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) tv_dict_alloc_ret(rettv); if (rhs != NULL) { // Return a dictionary. - char_u *lhs = str2special_save(mp->m_keys, true); - char *const mapmode = map_mode_to_chars(mp->m_mode); - dict_T *dict = rettv->vval.v_dict; - - tv_dict_add_str(dict, S_LEN("lhs"), (const char *)lhs); - tv_dict_add_str(dict, S_LEN("rhs"), (const char *)mp->m_orig_str); - tv_dict_add_nr(dict, S_LEN("noremap"), mp->m_noremap ? 1 : 0); - tv_dict_add_nr(dict, S_LEN("expr"), mp->m_expr ? 1 : 0); - tv_dict_add_nr(dict, S_LEN("silent"), mp->m_silent ? 1 : 0); - tv_dict_add_nr(dict, S_LEN("sid"), (varnumber_T)mp->m_script_ID); - tv_dict_add_nr(dict, S_LEN("buffer"), (varnumber_T)buffer_local); - tv_dict_add_nr(dict, S_LEN("nowait"), mp->m_nowait ? 1 : 0); - tv_dict_add_str(dict, S_LEN("mode"), mapmode); - - xfree(lhs); - xfree(mapmode); + mapblock_fill_dict(rettv->vval.v_dict, mp, buffer_local, true); } } } @@ -12134,6 +12119,46 @@ static void f_luaeval(typval_T *argvars, typval_T *rettv, FunPtr fptr) executor_eval_lua(cstr_as_string((char *)str), &argvars[1], rettv); } +/// Fill a dictionary with all applicable maparg() like dictionaries +/// +/// @param dict The dictionary to be filled +/// @param mp The maphash that contains the mapping information +/// @param buffer_value The "buffer" value +/// @param compatible True for compatible with old maparg() dict +void mapblock_fill_dict(dict_T *const dict, + const mapblock_T *const mp, + long buffer_value, + bool compatible) + FUNC_ATTR_NONNULL_ALL +{ + char_u *lhs = str2special_save(mp->m_keys, true); + char *const mapmode = map_mode_to_chars(mp->m_mode); + varnumber_T noremap_value; + + if (compatible) { + // Keep old compatible behavior + // This is unable to determine whether a mapping is a