changeset 28592:d3c966c0cdf7 v8.2.4820

patch 8.2.4820: not simple programmatic way to find a specific mapping Commit: https://github.com/vim/vim/commit/659c240cf769925ff432b88df8719e7ace4629b0 Author: Ernie Rael <errael@raelity.com> Date: Sun Apr 24 18:40:28 2022 +0100 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 https://github.com/vim/vim/issues/10273)
author Bram Moolenaar <Bram@vim.org>
date Sun, 24 Apr 2022 19:45:03 +0200
parents 1efc196073c2
children 5315a1f26c11
files runtime/doc/builtin.txt runtime/doc/usr_41.txt src/evalfunc.c src/map.c src/proto/map.pro src/testdir/test_maparg.vim src/version.c
diffstat 7 files changed, 182 insertions(+), 29 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/builtin.txt
+++ b/runtime/doc/builtin.txt
@@ -235,6 +235,7 @@ getline({lnum})			String	line {lnum} of 
 getline({lnum}, {end})		List	lines {lnum} to {end} of current buffer
 getloclist({nr})		List	list of location list items
 getloclist({nr}, {what})	Dict	get specific location list properties
+getmappings()			List	list of all mappings, a dict for each
 getmarklist([{buf}])		List	list of global/local marks
 getmatches([{win}])		List	list of current matches
 getmousepos()			Dict	last known mouse position
@@ -3570,6 +3571,17 @@ getloclist({nr} [, {what}])				*getlocli
 			:echo getloclist(5, {'filewinid': 0})
 
 
+getmappings()						*getmappings()*
+		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: >
+			vim9script
+			echo getmappings()->filter(
+				(_, m) => match(m.rhs, 'MultiMatch') >= 0)
+
+
 getmarklist([{buf}])					*getmarklist()*
 		Without the {buf} argument returns a |List| with information
 		about all the global marks. |mark|
@@ -5262,7 +5274,7 @@ maparg({name} [, {mode} [, {abbr} [, {di
 
 		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
--- a/runtime/doc/usr_41.txt
+++ b/runtime/doc/usr_41.txt
@@ -1089,6 +1089,7 @@ Mappings and Menus:			    *mapping-funct
 	digraph_getlist()	get all |digraph|s
 	digraph_set()		register |digraph|
 	digraph_setlist()	register multiple |digraph|s
+	getmappings()		get list of all mappings
 	hasmapto()		check if a mapping exists
 	mapcheck()		check if a matching mapping exists
 	maparg()		get rhs of a mapping
--- a/src/evalfunc.c
+++ b/src/evalfunc.c
@@ -1871,6 +1871,8 @@ static funcentry_T global_functions[] =
 			ret_getline,	    f_getline},
     {"getloclist",	1, 2, 0,	    arg2_number_dict_any,
 			ret_list_or_dict_1, f_getloclist},
+    {"getmappings",		0, 0, 0,	    NULL,
+			ret_list_dict_any,  f_getmappings},
     {"getmarklist",	0, 1, FEARG_1,	    arg1_buffer,
 			ret_list_dict_any,  f_getmarklist},
     {"getmatches",	0, 1, 0,	    arg1_number,
--- a/src/map.c
+++ b/src/map.c
@@ -2274,6 +2274,42 @@ check_map(
     return NULL;
 }
 
+/*
+ * Fill in the empty dictionary with items as defined by maparg builtin.
+ */
+    static void
+mapblock2dict(
+	mapblock_T  *mp,
+	dict_T	    *dict,
+	char_u	    *lhsrawalt,	    // may be NULL
+	int	    buffer_local)   // false if not buffer local mapping
+{
+    char_u	    *lhs = str2special_save(mp->m_keys, TRUE);
+    char_u	    *mapmode = map_mode_to_chars(mp->m_mode);
+
+    dict_add_string(dict, "lhs", lhs);
+    vim_free(lhs);
+    dict_add_string(dict, "lhsraw", mp->m_keys);
+    if (lhsrawalt)
+	// Also add the value for the simplified entry.
+	dict_add_string(dict, "lhsrawalt", lhsrawalt);
+    dict_add_string(dict, "rhs", mp->m_orig_str);
+    dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
+    dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
+		    ? 1L : 0L);
+    dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
+    dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
+    dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
+    dict_add_number(dict, "scriptversion",
+		    (long)mp->m_script_ctx.sc_version);
+    dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
+    dict_add_number(dict, "buffer", (long)buffer_local);
+    dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
+    dict_add_string(dict, "mode", mapmode);
+
+    vim_free(mapmode);
+}
+
     static void
 get_maparg(typval_T *argvars, typval_T *rettv, int exact)
 {
@@ -2346,40 +2382,67 @@ get_maparg(typval_T *argvars, typval_T *
 
     }
     else if (rettv_dict_alloc(rettv) != FAIL && rhs != NULL)
-    {
-	// Return a dictionary.
-	char_u	    *lhs = str2special_save(mp->m_keys, TRUE);
-	char_u	    *mapmode = map_mode_to_chars(mp->m_mode);
-	dict_T	    *dict = rettv->vval.v_dict;
-
-	dict_add_string(dict, "lhs", lhs);
-	vim_free(lhs);
-	dict_add_string(dict, "lhsraw", mp->m_keys);
-	if (did_simplify)
-	    // Also add the value for the simplified entry.
-	    dict_add_string(dict, "lhsrawalt", mp_simplified->m_keys);
-	dict_add_string(dict, "rhs", mp->m_orig_str);
-	dict_add_number(dict, "noremap", mp->m_noremap ? 1L : 0L);
-	dict_add_number(dict, "script", mp->m_noremap == REMAP_SCRIPT
-								    ? 1L : 0L);
-	dict_add_number(dict, "expr", mp->m_expr ? 1L : 0L);
-	dict_add_number(dict, "silent", mp->m_silent ? 1L : 0L);
-	dict_add_number(dict, "sid", (long)mp->m_script_ctx.sc_sid);
-	dict_add_number(dict, "scriptversion",
-					    (long)mp->m_script_ctx.sc_version);
-	dict_add_number(dict, "lnum", (long)mp->m_script_ctx.sc_lnum);
-	dict_add_number(dict, "buffer", (long)buffer_local);
-	dict_add_number(dict, "nowait", mp->m_nowait ? 1L : 0L);
-	dict_add_string(dict, "mode", mapmode);
-
-	vim_free(mapmode);
-    }
+	mapblock2dict(mp, rettv->vval.v_dict,
+		    did_simplify ? mp_simplified->m_keys : NULL, buffer_local);
 
     vim_free(keys_buf);
     vim_free(alt_keys_buf);
 }
 
 /*
+ * "getmappings()" function
+ */
+    void
+f_getmappings(typval_T *argvars UNUSED, typval_T *rettv)
+{
+    dict_T	*d;
+    mapblock_T	*mp;
+    int		buffer_local;
+    char_u	*keys_buf;
+    int		did_simplify;
+    int		hash;
+    char_u	*lhs;
+    const int	flags = REPTERM_FROM_PART | REPTERM_DO_LT;
+
+    if (rettv_list_alloc(rettv) != OK)
+	return;
+
+    validate_maphash();
+
+    // Do it twice: once for global maps and once for local maps.
+    for (buffer_local = 0; buffer_local <= 1; ++buffer_local)
+    {
+	for (hash = 0; hash < 256; ++hash)
+	{
+	    if (buffer_local)
+		mp = curbuf->b_maphash[hash];
+	    else
+		mp = maphash[hash];
+	    for (; mp; mp = mp->m_next)
+	    {
+		if (mp->m_simplified)
+		    continue;
+		if ((d = dict_alloc()) == NULL)
+		    return;
+		if (list_append_dict(rettv->vval.v_list, d) == FAIL)
+		    return;
+
+		keys_buf = NULL;
+		did_simplify = FALSE;
+
+		lhs = str2special_save(mp->m_keys, TRUE);
+		(void)replace_termcodes(lhs, &keys_buf, flags, &did_simplify);
+		vim_free(lhs);
+
+		mapblock2dict(mp, d,
+				 did_simplify ? keys_buf : NULL, buffer_local);
+		vim_free(keys_buf);
+	    }
+	}
+    }
+}
+
+/*
  * "maparg()" function
  */
     void
--- a/src/proto/map.pro
+++ b/src/proto/map.pro
@@ -17,6 +17,7 @@ int makemap(FILE *fd, buf_T *buf);
 int put_escstr(FILE *fd, char_u *strstart, int what);
 void check_map_keycodes(void);
 char_u *check_map(char_u *keys, int mode, int exact, int ign_mod, int abbr, mapblock_T **mp_ptr, int *local_ptr);
+void f_getmappings(typval_T *argvars, typval_T *rettv);
 void f_maparg(typval_T *argvars, typval_T *rettv);
 void f_mapcheck(typval_T *argvars, typval_T *rettv);
 void f_mapset(typval_T *argvars, typval_T *rettv);
--- a/src/testdir/test_maparg.vim
+++ b/src/testdir/test_maparg.vim
@@ -297,4 +297,76 @@ func Test_map_restore()
 
 endfunc
 
+def Test_getmappings()
+  new
+  def ClearMaps()
+    mapclear | nmapclear | vmapclear | xmapclear | smapclear | omapclear
+    mapclear!  | imapclear | lmapclear | cmapclear | tmapclear
+    mapclear <buffer> | nmapclear <buffer> | vmapclear <buffer>
+    xmapclear <buffer> | smapclear <buffer> | omapclear <buffer>
+    mapclear! <buffer> | imapclear <buffer> | lmapclear <buffer>
+    cmapclear <buffer> | tmapclear <buffer>
+  enddef
+
+  def AddMaps(new: list<string>, accum: list<string>)
+    if len(new) > 0 && new[0] != "No mapping found"
+      accum->extend(new)
+    endif
+  enddef
+
+  ClearMaps()
+  assert_equal(0, len(getmappings()))
+
+  # Set up some mappings.
+  map dup bar
+  map <buffer> dup bufbar
+  map foo<C-V> is<F4>foo
+  vnoremap <script> <buffer> <expr> <silent> bar isbar
+  tmap baz foo
+  omap h w
+  lmap i w
+  nmap j w
+  xmap k w
+  smap l w
+  map abc <Nop>
+  nmap <M-j> x
+  nmap <M-Space> y
+
+  # Get a list of the mappings with the ':map' commands.
+  # Check getmappings() return a list of the same size.
+  assert_equal(13, len(getmappings()))
+
+  # collect all the current maps using :map commands
+  var maps_command: list<string>
+  AddMaps(split(execute('map'), '\n'), maps_command)
+  AddMaps(split(execute('map!'), '\n'), maps_command)
+  AddMaps(split(execute('tmap'), '\n'), maps_command)
+  AddMaps(split(execute('lmap'), '\n'), maps_command)
+
+  # Use getmappings to get all the maps
+  var maps_getmappings = getmappings()
+  assert_equal(len(maps_command), len(maps_getmappings))
+
+  # make sure all the mode-lhs are unique, no duplicates
+  var map_set: dict<number>
+  for d in maps_getmappings
+    map_set[d.mode .. "-" .. d.lhs .. "-" .. d.buffer] = 0
+  endfor
+  assert_equal(len(maps_getmappings), len(map_set))
+
+  # For everything returned by getmappings, should be the same as from maparg.
+  # Except for "map dup", bacause maparg returns the <buffer> version
+  for d in maps_getmappings
+    if d.lhs == 'dup' && d.buffer == 0
+      continue
+    endif
+    var d_maparg = maparg(d.lhs, d.mode, false, true)
+    assert_equal(d_maparg, d)
+  endfor
+
+  ClearMaps()
+  assert_equal(0, len(getmappings()))
+enddef
+
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -747,6 +747,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    4820,
+/**/
     4819,
 /**/
     4818,