]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.1.0814: mapset() may remove unrelated mapping v9.1.0814
authorzeertzjq <zeertzjq@outlook.com>
Thu, 24 Oct 2024 19:43:43 +0000 (21:43 +0200)
committerChristian Brabandt <cb@256bit.org>
Thu, 24 Oct 2024 19:43:43 +0000 (21:43 +0200)
Problem:  mapset() may remove unrelated mapping whose {rhs} matches the
          restored mapping's {lhs}.
Solution: only match by {lhs} when unmapping for mapset() (zeertzjq).

closes: #15935

Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/map.c
src/testdir/test_map_functions.vim
src/version.c
src/vim.h

index 91ab6e3f163e08b9efcc460ae05de2ec3ed7d9b2..209d9d237fc1e49008efb882742c6419fa724b61 100644 (file)
--- a/src/map.c
+++ b/src/map.c
@@ -408,9 +408,11 @@ list_mappings(
  * noreabbr {lhs} {rhs}            : same, but no remapping for {rhs}
  * unabbr {lhs}                    : remove abbreviation for {lhs}
  *
- * maptype: MAPTYPE_MAP for :map
- *         MAPTYPE_UNMAP for :unmap
- *         MAPTYPE_NOREMAP for noremap
+ * maptype: MAPTYPE_MAP for :map or :abbr
+ *         MAPTYPE_UNMAP for :unmap or :unabbr
+ *         MAPTYPE_NOREMAP for :noremap or :noreabbr
+ *         MAPTYPE_UNMAP_LHS is like MAPTYPE_UNMAP, but doesn't try to match
+ *         with {rhs} if there is no match with {lhs}.
  *
  * arg is pointer to any arguments. Note: arg cannot be a read-only string,
  * it will be modified.
@@ -470,6 +472,7 @@ do_map(
     int                expr = FALSE;
 #endif
     int                did_simplify = FALSE;
+    int                unmap_lhs_only = FALSE;
     int                noremap;
     char_u      *orig_rhs;
 
@@ -477,6 +480,12 @@ do_map(
     map_table = maphash;
     abbr_table = &first_abbr;
 
+    if (maptype == MAPTYPE_UNMAP_LHS)
+    {
+       unmap_lhs_only = TRUE;
+       maptype = MAPTYPE_UNMAP;
+    }
+
     // For ":noremap" don't remap, otherwise do remap.
     if (maptype == MAPTYPE_NOREMAP)
        noremap = REMAP_NONE;
@@ -619,6 +628,7 @@ do_map(
        int     did_local = FALSE;
        int     keyround1_simplified = keyround == 1 && did_simplify;
        int     round;
+       int     num_rounds;
 
        if (keyround == 2)
        {
@@ -742,8 +752,8 @@ do_map(
        // an entry with a matching 'to' part. This was done to allow
        // ":ab foo bar" to be unmapped by typing ":unab foo", where "foo" will
        // be replaced by "bar" because of the abbreviation.
-       for (round = 0; (round == 0 || maptype == MAPTYPE_UNMAP) && round <= 1
-                                              && !did_it && !got_int; ++round)
+       num_rounds = maptype == MAPTYPE_UNMAP && !unmap_lhs_only ? 2 : 1;
+       for (round = 0; round < num_rounds && !did_it && !got_int; ++round)
        {
            // need to loop over all hash lists
            for (int hash = 0; hash < 256 && !got_int; ++hash)
@@ -2817,7 +2827,7 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
        if (arg == NULL)
            return;
     }
-    do_map(MAPTYPE_UNMAP, arg, mode, is_abbr);
+    do_map(MAPTYPE_UNMAP_LHS, arg, mode, is_abbr);
     vim_free(arg);
 
     mp_result[0] = map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs,
index c6563681574db272eb1ee3d24f7716aeef22e3d5..db639337fcf60904cf30fc8dad4e09786163a3cc 100644 (file)
@@ -540,6 +540,25 @@ func Test_map_restore_negative_sid()
   call delete('Xresult')
 endfunc
 
+" Check that restoring a mapping doesn't remove a mapping whose {rhs} matches
+" the restored mapping's {lhs}.
+func Test_map_restore_with_rhs_match_lhs()
+  nnoremap <F2> <F3>
+  nnoremap <F3> <F4>
+  call assert_equal('<F3>', maparg('<F2>', 'n'))
+  call assert_equal('<F4>', maparg('<F3>', 'n'))
+  let d = maparg('<F3>', 'n', v:false, v:true)
+  nunmap <F3>
+  call assert_equal('<F3>', maparg('<F2>', 'n'))
+  call assert_equal('', maparg('<F3>', 'n'))
+  call mapset(d)
+  call assert_equal('<F3>', maparg('<F2>', 'n'))
+  call assert_equal('<F4>', maparg('<F3>', 'n'))
+
+  nunmap <F2>
+  nunmap <F3>
+endfunc
+
 def Test_maplist()
   new
   def ClearMappingsAbbreviations()
index 1b3c92192559ae7fd44fc6dacc34e4b8b50c3144..aa05ea2c3528e24cdae400ed5b989185056bbd5c 100644 (file)
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    814,
 /**/
     813,
 /**/
index 387fe4fd97f7e261975aab7594b584f7401664e9..74e6b95435cfadb17cd99ed8c3c5e48f0b18770c 100644 (file)
--- a/src/vim.h
+++ b/src/vim.h
@@ -1013,9 +1013,10 @@ extern int (*dyn_libintl_wputenv)(const wchar_t *envstring);
 #define KEY_COMPLETE   0x103   // end of completion
 
 // Used for the first argument of do_map()
-#define MAPTYPE_MAP    0
-#define MAPTYPE_UNMAP  1
-#define MAPTYPE_NOREMAP        2
+#define MAPTYPE_MAP            0
+#define MAPTYPE_UNMAP          1
+#define MAPTYPE_NOREMAP                2
+#define MAPTYPE_UNMAP_LHS      3
 
 // Values for "noremap" argument of ins_typebuf().  Also used for
 // map->m_noremap and menu->noremap[].