From a4b80c71eae09a5f6f76bce46be46efd247c7223 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Nov 2023 15:17:53 +0800 Subject: vim-patch:8.2.4140: maparg() does not indicate the type of script Problem: maparg() does not indicate the type of script where it was defined. Solution: Add "scriptversion". https://github.com/vim/vim/commit/a9528b39a666dbaa026320f73bae4b1628a7fe51 Co-authored-by: Bram Moolenaar --- src/nvim/eval.lua | 1 + src/nvim/mapping.c | 1 + 2 files changed, 2 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 221be492af..0b81e2eb65 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6189,6 +6189,7 @@ M.funcs = { (|mapmode-ic|) "sid" The script local ID, used for mappings (||). Negative for special contexts. + "scriptversion" The version of the script, always 1. "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-|). diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 4435e91993..6786b5efe9 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2113,6 +2113,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs PUT(dict, "expr", INTEGER_OBJ(mp->m_expr ? 1 : 0)); PUT(dict, "silent", INTEGER_OBJ(mp->m_silent ? 1 : 0)); PUT(dict, "sid", INTEGER_OBJ(mp->m_script_ctx.sc_sid)); + PUT(dict, "scriptversion", INTEGER_OBJ(1)); PUT(dict, "lnum", INTEGER_OBJ(mp->m_script_ctx.sc_lnum)); PUT(dict, "buffer", INTEGER_OBJ(buffer_value)); PUT(dict, "nowait", INTEGER_OBJ(mp->m_nowait ? 1 : 0)); -- cgit From 2dfcd5a22b8f26091aa7398fdb8b0ea70ed7b28d Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Nov 2023 18:16:49 +0800 Subject: vim-patch:8.2.4820: not simple programmatic way to find a specific mapping Problem: Not simple programmatic way to find a specific mapping. Solution: Add getmappings(). (Ernie Rael, closes vim/vim#10273) https://github.com/vim/vim/commit/659c240cf769925ff432b88df8719e7ace4629b0 Co-authored-by: Ernie Rael --- src/nvim/eval.lua | 18 +++++++++++++++++- src/nvim/mapping.c | 43 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 1 deletion(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 0b81e2eb65..5aa4d65362 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -3957,6 +3957,22 @@ M.funcs = { params = { { 'nr', 'integer' }, { 'what', 'any' } }, signature = 'getloclist({nr} [, {what}])', }, + getmappings = { + args = 0, + desc = [[ + Returns a |List| of all mappings. Each List item is a |Dict|, + the same as what is returned by |maparg()|, see + |mapping-dict|. + + Example to show all mappings with "MultiMatch" in rhs: > + echo getmappings()->filter({_, m -> + \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 + \ }) + ]], + name = 'getmappings', + params = {}, + signature = 'getmappings()' + }, getmarklist = { args = { 0, 1 }, base = 1, @@ -6170,7 +6186,7 @@ M.funcs = { When {dict} is there and it is |TRUE| return a dictionary containing all the information of the mapping with the - following items: + following items: *mapping-dict* "lhs" The {lhs} of the mapping as it would be typed "lhsraw" The {lhs} of the mapping as raw bytes "lhsrawalt" The {lhs} of the mapping as raw bytes, alternate diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 6786b5efe9..2cc469feec 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2281,6 +2281,49 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) sid, lnum, false); } +/// "maplist()" function +void f_getmappings(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +{ + const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + + tv_list_alloc_ret(rettv, kListLenUnknown); + + // Do it twice: once for global maps and once for local maps. + for (int buffer_local = 0; buffer_local <= 1; buffer_local++) { + for (int hash = 0; hash < 256; hash++) { + mapblock_T *mp; + if (buffer_local) { + mp = curbuf->b_maphash[hash]; + } else { + mp = maphash[hash]; + } + for (; mp; mp = mp->m_next) { + if (mp->m_simplified) { + continue; + } + + char *keys_buf = NULL; + bool did_simplify = false; + + char *lhs = str2special_save(mp->m_keys, true, false); + (void)replace_termcodes(lhs, strlen(lhs), &keys_buf, 0, flags, &did_simplify, + CPO_TO_CPO_FLAGS); + xfree(lhs); + + Dictionary dict = mapblock_fill_dict(mp, + did_simplify ? keys_buf : NULL, + buffer_local, true); + typval_T d = TV_INITIAL_VALUE; + (void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL); + assert(d.v_type == VAR_DICT); + tv_list_append_dict(rettv->vval.v_list, d.vval.v_dict); + api_free_dictionary(dict); + xfree(keys_buf); + } + } + } +} + /// "maparg()" function void f_maparg(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { -- cgit From d4dbfb092b370bced6728e07a38661a579ff5e4b Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Nov 2023 19:36:58 +0800 Subject: vim-patch:8.2.4825: can only get a list of mappings Problem: Can only get a list of mappings. Solution: Add the optional {abbr} argument. (Ernie Rael, closes vim/vim#10277) Rename to maplist(). Rename test file. https://github.com/vim/vim/commit/09661203ecefbee6a6f09438afcff1843e9dbfb4 Co-authored-by: Ernie Rael --- src/nvim/eval.lua | 36 +++++++++++++++++++----------------- src/nvim/mapping.c | 14 ++++++++++++-- 2 files changed, 31 insertions(+), 19 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index 5aa4d65362..de7fb53455 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -3957,22 +3957,6 @@ M.funcs = { params = { { 'nr', 'integer' }, { 'what', 'any' } }, signature = 'getloclist({nr} [, {what}])', }, - getmappings = { - args = 0, - desc = [[ - Returns a |List| of all mappings. Each List item is a |Dict|, - the same as what is returned by |maparg()|, see - |mapping-dict|. - - Example to show all mappings with "MultiMatch" in rhs: > - echo getmappings()->filter({_, m -> - \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 - \ }) - ]], - name = 'getmappings', - params = {}, - signature = 'getmappings()' - }, getmarklist = { args = { 0, 1 }, base = 1, @@ -6158,7 +6142,8 @@ M.funcs = { When {dict} is omitted or zero: Return the rhs of mapping {name} in mode {mode}. The returned String has special characters translated like in the output of the ":map" command - listing. + listing. When {dict} is TRUE a dictionary is returned, see + below. To get a list of all mappings see |maplist()|. When there is no mapping for {name}, an empty String is returned if {dict} is FALSE, otherwise returns an empty Dict. @@ -6271,6 +6256,23 @@ M.funcs = { params = { { 'name', 'string' }, { 'mode', 'string' }, { 'abbr', 'any' } }, signature = 'mapcheck({name} [, {mode} [, {abbr}]])', }, + maplist = { + args = { 0, 1 }, + desc = [[ + Returns a |List| of all mappings. Each List item is a |Dict|, + the same as what is returned by |maparg()|, see + |mapping-dict|. When {abbr} is there and it is |TRUE| use + abbreviations instead of mappings. + + Example to show all mappings with "MultiMatch" in rhs: >vim + echo maplist()->filter({_, m -> + \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 + \ }) + ]], + name = 'maplist', + params = {}, + signature = 'maplist([{abbr}])' + }, mapnew = { args = 2, base = 1, diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 2cc469feec..905e742f51 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2282,9 +2282,10 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) } /// "maplist()" function -void f_getmappings(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) +void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { const int flags = REPTERM_FROM_PART | REPTERM_DO_LT; + const bool abbr = argvars[0].v_type != VAR_UNKNOWN && tv_get_bool(&argvars[0]); tv_list_alloc_ret(rettv, kListLenUnknown); @@ -2292,7 +2293,16 @@ void f_getmappings(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) for (int buffer_local = 0; buffer_local <= 1; buffer_local++) { for (int hash = 0; hash < 256; hash++) { mapblock_T *mp; - if (buffer_local) { + if (abbr) { + if (hash > 0) { // there is only one abbr list + break; + } + if (buffer_local) { + mp = curbuf->b_first_abbr; + } else { + mp = first_abbr; + } + } else if (buffer_local) { mp = curbuf->b_maphash[hash]; } else { mp = maphash[hash]; -- cgit From f748a73a357710db6094d4a428cd056f5df226a9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Nov 2023 19:48:50 +0800 Subject: vim-patch:8.2.4861: it is not easy to restore saved mappings Problem: It is not easy to restore saved mappings. Solution: Make mapset() accept a dict argument. (Ernie Rael, closes vim/vim#10295) https://github.com/vim/vim/commit/51d04d16f21e19d6eded98f9530d84089102f925 Co-authored-by: Ernie Rael --- src/nvim/eval.lua | 34 ++++++++++++++--- src/nvim/mapping.c | 107 +++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 123 insertions(+), 18 deletions(-) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index de7fb53455..d65b584faa 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6194,6 +6194,7 @@ M.funcs = { "lnum" The line number in "sid", zero if unknown. "nowait" Do not wait for other, longer mappings. (|:map-|). + "abbr" True if this is an |abbreviation|. The dictionary can be used to restore a mapping with |mapset()|. @@ -6287,12 +6288,20 @@ M.funcs = { signature = 'mapnew({expr1}, {expr2})', }, mapset = { - args = 3, + args = { 1, 3 }, base = 1, desc = [=[ - Restore a mapping from a dictionary returned by |maparg()|. - {mode} and {abbr} should be the same as for the call to - |maparg()|. *E460* + Restore a mapping from a dictionary, possibly returned by + |maparg()| or |maplist()|. A buffer mapping, when dict.buffer + is true, is set on the current buffer; it is up to the caller + to ensure that the intended buffer is the current buffer. This + feature allows copying mappings from one buffer to another. + The dict.mode value may restore a single mapping that covers + more than one mode, like with mode values of '!', ' ', "nox", + or 'v'. *E1276* + + In the first form, {mode} and {abbr} should be the same as + for the call to |maparg()|. *E460* {mode} is used to define the mode in which the mapping is set, not the "mode" entry in {dict}. Example for saving and restoring a mapping: >vim @@ -6301,8 +6310,21 @@ M.funcs = { " ... call mapset('n', 0, save_map) vim + let save_maps = maplist()->filter( + \ {_, m -> m.lhs == 'K'}) + nnoremap K somethingelse + cnoremap K somethingelse2 + " ... + unmap K + for d in save_maps + call mapset(d) + endfor ]=], name = 'mapset', params = { { 'mode', 'string' }, { 'abbr', 'any' }, { 'dict', 'any' } }, diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index 905e742f51..c90c0c5529 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -123,6 +123,8 @@ static const char e_mapping_already_exists_for_str[] = N_("E227: Mapping already exists for %s"); static const char e_entries_missing_in_mapset_dict_argument[] = N_("E460: Entries missing in mapset() dict argument"); +static const char e_illegal_map_mode_string_str[] + = N_("E1276: Illegal map mode string: '%s'"); /// Get the start of the hashed map list for "state" and first character "c". mapblock_T *get_maphash_list(int state, int c) @@ -2070,11 +2072,12 @@ void f_hasmapto(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) /// /// @param mp The maphash that contains the mapping information /// @param buffer_value The "buffer" value +/// @param abbr True if abbreviation /// @param compatible True for compatible with old maparg() dict /// /// @return A Dictionary. static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhsrawalt, - const int buffer_value, const bool compatible) + const int buffer_value, const bool abbr, const bool compatible) FUNC_ATTR_NONNULL_ARG(1) { Dictionary dict = ARRAY_DICT_INIT; @@ -2121,6 +2124,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs PUT(dict, "replace_keycodes", INTEGER_OBJ(1)); } PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); + PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); return dict; } @@ -2193,7 +2197,7 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) if (mp != NULL && (rhs != NULL || rhs_lua != LUA_NOREF)) { Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_simplified : NULL, - buffer_local, true); + buffer_local, abbr, true); (void)object_to_vim(DICTIONARY_OBJ(dict), rettv, NULL); api_free_dictionary(dict); } else { @@ -2206,21 +2210,99 @@ static void get_maparg(typval_T *argvars, typval_T *rettv, int exact) xfree(alt_keys_buf); } +/// Get the mapping mode from the mode string. +/// It may contain multiple characters, eg "nox", or "!", or ' ' +/// Return 0 if there is an error. +static int get_map_mode_string(const char *const mode_string, const bool abbr) +{ + const char *p = mode_string; + const int MASK_V = MODE_VISUAL | MODE_SELECT; + const int MASK_MAP = MODE_VISUAL | MODE_SELECT | MODE_NORMAL | MODE_OP_PENDING; + const int MASK_BANG = MODE_INSERT | MODE_CMDLINE; + + if (*p == NUL) { + p = " "; // compatibility + } + int mode = 0; + int modec; + while ((modec = (uint8_t)(*p++))) { + int tmode; + switch (modec) { + case 'i': + tmode = MODE_INSERT; break; + case 'l': + tmode = MODE_LANGMAP; break; + case 'c': + tmode = MODE_CMDLINE; break; + case 'n': + tmode = MODE_NORMAL; break; + case 'x': + tmode = MODE_VISUAL; break; + case 's': + tmode = MODE_SELECT; break; + case 'o': + tmode = MODE_OP_PENDING; break; + case 't': + tmode = MODE_TERMINAL; break; + case 'v': + tmode = MASK_V; break; + case '!': + tmode = MASK_BANG; break; + case ' ': + tmode = MASK_MAP; break; + default: + return 0; // error, unknown mode character + } + mode |= tmode; + } + if ((abbr && (mode & ~MASK_BANG) != 0) + || (!abbr && (mode & (mode - 1)) != 0 // more than one bit set + && ( + // false if multiple bits set in mode and mode is fully + // contained in one mask + !(((mode & MASK_BANG) != 0 && (mode & ~MASK_BANG) == 0) + || ((mode & MASK_MAP) != 0 && (mode & ~MASK_MAP) == 0))))) { + return 0; + } + + return mode; +} + /// "mapset()" function void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) { + const char *which; char buf[NUMBUFLEN]; - const char *which = tv_get_string_buf_chk(&argvars[0], buf); - if (which == NULL) { - return; + int is_abbr; + dict_T *d; + + // If first arg is a dict, then that's the only arg permitted. + const bool dict_only = argvars[0].v_type == VAR_DICT; + + if (dict_only) { + d = argvars[0].vval.v_dict; + which = tv_dict_get_string(d, "mode", false); + is_abbr = (int)tv_dict_get_bool(d, "abbr", -1); + if (which == NULL || is_abbr < 0) { + emsg(_(e_entries_missing_in_mapset_dict_argument)); + return; + } + } else { + which = tv_get_string_buf_chk(&argvars[0], buf); + if (which == NULL) { + return; + } + is_abbr = (int)tv_get_bool(&argvars[1]); + if (tv_check_for_dict_arg(argvars, 2) == FAIL) { + return; + } + d = argvars[2].vval.v_dict; } - const int mode = get_map_mode((char **)&which, 0); - const bool is_abbr = tv_get_number(&argvars[1]) != 0; - - if (tv_check_for_dict_arg(argvars, 2) == FAIL) { + const int mode = get_map_mode_string(which, is_abbr); + if (mode == 0) { + semsg(_(e_illegal_map_mode_string_str), which); return; } - dict_T *d = argvars[2].vval.v_dict; // Get the values in the same order as above in get_maparg(). char *lhs = tv_dict_get_string(d, "lhs", false); @@ -2322,7 +2404,7 @@ void f_maplist(typval_T *argvars, typval_T *rettv, EvalFuncData fptr) Dictionary dict = mapblock_fill_dict(mp, did_simplify ? keys_buf : NULL, - buffer_local, true); + buffer_local, abbr, true); typval_T d = TV_INITIAL_VALUE; (void)object_to_vim(DICTIONARY_OBJ(dict), &d, NULL); assert(d.v_type == VAR_DICT); @@ -2751,7 +2833,8 @@ ArrayOf(Dictionary) keymap_array(String mode, buf_T *buf) // Check for correct mode if (int_mode & current_maphash->m_mode) { ADD(mappings, - DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, buffer_value, false))); + DICTIONARY_OBJ(mapblock_fill_dict(current_maphash, NULL, + buffer_value, false, false))); } } } -- cgit From 04d299c17014b0802c79613252e4de26da84a7c9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Thu, 9 Nov 2023 20:42:55 +0800 Subject: vim-patch:8.2.4932: not easy to filter the output of maplist() Problem: Not easy to filter the output of maplist(). Solution: Add mode_bits to the dictionary. (Ernie Rael, closes vim/vim#10356) https://github.com/vim/vim/commit/d8f5f766219273a8579947cc80b92580b6988a4b Co-authored-by: Ernie Rael --- src/nvim/eval.lua | 25 +++++++++++++++++++++++++ src/nvim/mapping.c | 1 + 2 files changed, 26 insertions(+) (limited to 'src') diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua index d65b584faa..a2ba0f0189 100644 --- a/src/nvim/eval.lua +++ b/src/nvim/eval.lua @@ -6195,6 +6195,11 @@ M.funcs = { "nowait" Do not wait for other, longer mappings. (|:map-|). "abbr" True if this is an |abbreviation|. + "mode_bits" Nvim's internal binary representation of "mode". + |mapset()| ignores this; only "mode" is used. + See |maplist()| for usage examples. The values + are from src/nvim/vim.h and may change in the + future. The dictionary can be used to restore a mapping with |mapset()|. @@ -6269,6 +6274,26 @@ M.funcs = { echo maplist()->filter({_, m -> \ match(get(m, 'rhs', ''), 'MultiMatch') >= 0 \ }) + vim + let saved_maps = [] + for m in maplist() + if and(m.mode_bits, 0x19) != 0 + eval saved_maps->add(m) + endif + endfor + echo saved_maps->mapnew({_, m -> m.lhs}) + vim + omap xyzzy + let op_bit = maplist()->filter( + \ {_, m -> m.lhs == 'xyzzy'})[0].mode_bits + ounmap xyzzy + echo printf("Operator-pending mode bit: 0x%x", op_bit) ]], name = 'maplist', params = {}, diff --git a/src/nvim/mapping.c b/src/nvim/mapping.c index c90c0c5529..2ee1e2e02d 100644 --- a/src/nvim/mapping.c +++ b/src/nvim/mapping.c @@ -2125,6 +2125,7 @@ static Dictionary mapblock_fill_dict(const mapblock_T *const mp, const char *lhs } PUT(dict, "mode", CSTR_AS_OBJ(mapmode)); PUT(dict, "abbr", INTEGER_OBJ(abbr ? 1 : 0)); + PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode)); return dict; } -- cgit