aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2023-11-09 21:53:34 +0800
committerGitHub <noreply@github.com>2023-11-09 21:53:34 +0800
commit267c346e2cd10081bfebe86be59f137a1b7470e5 (patch)
tree8a17cad14b298c4f62c793da95c6d59924e82b84 /src
parent610f50ddaff260e2e2f7236f6a3982ee7554c2a6 (diff)
parent04d299c17014b0802c79613252e4de26da84a7c9 (diff)
downloadrneovim-267c346e2cd10081bfebe86be59f137a1b7470e5.tar.gz
rneovim-267c346e2cd10081bfebe86be59f137a1b7470e5.tar.bz2
rneovim-267c346e2cd10081bfebe86be59f137a1b7470e5.zip
Merge pull request #25951 from zeertzjq/vim-8.2.4140
vim-patch:8.2.{4140,4820,4825,4861,4932}
Diffstat (limited to 'src')
-rw-r--r--src/nvim/eval.lua82
-rw-r--r--src/nvim/mapping.c160
2 files changed, 223 insertions, 19 deletions
diff --git a/src/nvim/eval.lua b/src/nvim/eval.lua
index 221be492af..a2ba0f0189 100644
--- a/src/nvim/eval.lua
+++ b/src/nvim/eval.lua
@@ -6142,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.
@@ -6170,7 +6171,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
@@ -6189,9 +6190,16 @@ M.funcs = {
(|mapmode-ic|)
"sid" The script local ID, used for <sid> mappings
(|<SID>|). 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-<nowait>|).
+ "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()|.
@@ -6254,6 +6262,43 @@ 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
+ \ })
+ <It can be tricky to find mappings for particular |:map-modes|.
+ |mapping-dict|'s "mode_bits" can simplify this. For example,
+ the mode_bits for Normal, Insert or Command-line modes are
+ 0x19. To find all the mappings available in those modes you
+ can do: >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})
+ <The values of the mode_bits are defined in Nvim's
+ src/nvim/vim.h file and they can be discovered at runtime
+ using |:map-commands| and "maplist()". Example: >vim
+ omap xyzzy <Nop>
+ 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 = {},
+ signature = 'maplist([{abbr}])'
+ },
mapnew = {
args = 2,
base = 1,
@@ -6268,12 +6313,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
@@ -6282,8 +6335,21 @@ M.funcs = {
" ...
call mapset('n', 0, save_map)
<Note that if you are going to replace a map in several modes,
- e.g. with `:map!`, you need to save the mapping for all of
- them, since they can differ.
+ e.g. with `:map!`, you need to save/restore the mapping for
+ all of them, when they might differ.
+
+ In the second form, with {dict} as the only argument, mode
+ and abbr are taken from the dict.
+ Example: >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 4435e91993..2ee1e2e02d 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;
@@ -2113,6 +2116,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));
@@ -2120,6 +2124,8 @@ 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));
+ PUT(dict, "mode_bits", INTEGER_OBJ(mp->m_mode));
return dict;
}
@@ -2192,7 +2198,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 {
@@ -2205,21 +2211,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);
@@ -2280,6 +2364,59 @@ void f_mapset(typval_T *argvars, typval_T *rettv, EvalFuncData fptr)
sid, lnum, false);
}
+/// "maplist()" function
+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);
+
+ // 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 (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];
+ }
+ 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, abbr, 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)
{
@@ -2697,7 +2834,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)));
}
}
}