patch 9.1.0642: Check that mapping rhs starts with lhs fails if not simplified

Commit: 
https://github.com/vim/vim/commit/9d997addc7bd0fd132a809cf497ed816e61fcd25
Author: zeertzjq <zeert...@outlook.com>
Date:   Mon Jul 29 21:10:07 2024 +0200

    patch 9.1.0642: Check that mapping rhs starts with lhs fails if not 
simplified
    
    Problem:  Check that mapping rhs starts with lhs doesn't work if lhs is
              not simplified.
    Solution: Keep track of the mapblock containing the alternative lhs and
              also compare with it (zeertzjq).
    
    fixes: #15376
    closes: #15384
    
    Signed-off-by: zeertzjq <zeert...@outlook.com>
    Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/getchar.c b/src/getchar.c
index 4af176978..54222ec30 100644
--- a/src/getchar.c
+++ b/src/getchar.c
@@ -3156,6 +3156,7 @@ handle_mapping(
        int     save_m_noremap;
        int     save_m_silent;
        char_u  *save_m_keys;
+       char_u  *save_alt_m_keys;
 #else
 # define save_m_noremap mp->m_noremap
 # define save_m_silent mp->m_silent
@@ -3204,6 +3205,7 @@ handle_mapping(
        save_m_noremap = mp->m_noremap;
        save_m_silent = mp->m_silent;
        save_m_keys = NULL;  // only saved when needed
+       save_alt_m_keys = NULL;  // only saved when needed
 
        /*
         * Handle ":map <expr>": evaluate the {rhs} as an expression.  Also
@@ -3221,6 +3223,8 @@ handle_mapping(
            may_garbage_collect = FALSE;
 
            save_m_keys = vim_strsave(mp->m_keys);
+           save_alt_m_keys = mp->m_alt != NULL
+                                   ? vim_strsave(mp->m_alt->m_keys) : NULL;
            map_str = eval_map_expr(mp, NUL);
 
            // The mapping may do anything, but we expect it to take care of
@@ -3278,15 +3282,20 @@ handle_mapping(
                noremap = save_m_noremap;
            else if (
 #ifdef FEAT_EVAL
-               STRNCMP(map_str, save_m_keys != NULL ? save_m_keys : mp->m_keys,
-                                                               (size_t)keylen)
-#else
-               STRNCMP(map_str, mp->m_keys, (size_t)keylen)
+               save_m_expr ?
+               (save_m_keys != NULL
+                       && STRNCMP(map_str, save_m_keys, (size_t)keylen) == 0)
+               || (save_alt_m_keys != NULL
+                       && STRNCMP(map_str, save_alt_m_keys,
+                                           STRLEN(save_alt_m_keys)) == 0) :
 #endif
-                  != 0)
-               noremap = REMAP_YES;
-           else
+               STRNCMP(map_str, mp->m_keys, (size_t)keylen) == 0
+               || (mp->m_alt != NULL
+                       && STRNCMP(map_str, mp->m_alt->m_keys,
+                                           STRLEN(mp->m_alt->m_keys)) == 0))
                noremap = REMAP_SKIP;
+           else
+               noremap = REMAP_YES;
            i = ins_typebuf(map_str, noremap,
                                         0, TRUE, cmd_silent || save_m_silent);
 #ifdef FEAT_EVAL
@@ -3296,6 +3305,7 @@ handle_mapping(
        }
 #ifdef FEAT_EVAL
        vim_free(save_m_keys);
+       vim_free(save_alt_m_keys);
 #endif
        *keylenp = keylen;
        if (i == FAIL)
diff --git a/src/map.c b/src/map.c
index c416c0a32..91ab6e3f1 100644
--- a/src/map.c
+++ b/src/map.c
@@ -85,6 +85,8 @@ map_free(mapblock_T **mpp)
 
     mp = *mpp;
     vim_free(mp->m_keys);
+    if (mp->m_alt != NULL)
+       mp->m_alt->m_alt = NULL;
     vim_free(mp->m_str);
     vim_free(mp->m_orig_str);
     *mpp = mp->m_next;
@@ -213,7 +215,7 @@ theend:
     --map_locked;
 }
 
-    static int
+    static mapblock_T *
 map_add(
        mapblock_T  **map_table,
        mapblock_T  **abbr_table,
@@ -236,7 +238,7 @@ map_add(
     mapblock_T *mp = ALLOC_CLEAR_ONE(mapblock_T);
 
     if (mp == NULL)
-       return FAIL;
+       return NULL;
 
     // If CTRL-C has been mapped, don't always use it for Interrupting.
     if (*keys == Ctrl_C)
@@ -256,7 +258,7 @@ map_add(
        vim_free(mp->m_str);
        vim_free(mp->m_orig_str);
        vim_free(mp);
-       return FAIL;
+       return NULL;
     }
     mp->m_keylen = (int)STRLEN(mp->m_keys);
     mp->m_noremap = noremap;
@@ -292,7 +294,7 @@ map_add(
        mp->m_next = map_table[n];
        map_table[n] = mp;
     }
-    return OK;
+    return mp;
 }
 
 /*
@@ -444,6 +446,7 @@ do_map(
 {
     char_u     *keys;
     mapblock_T *mp, **mpp;
+    mapblock_T *mp_result[2] = {NULL, NULL};
     char_u     *rhs;
     char_u     *p;
     int                n;
@@ -844,6 +847,8 @@ do_map(
                                        retval = 4;             // no mem
                                        goto theend;
                                    }
+                                   if (mp->m_alt != NULL)
+                                       mp->m_alt = mp->m_alt->m_alt = NULL;
                                    vim_free(mp->m_str);
                                    mp->m_str = newstr;
                                    vim_free(mp->m_orig_str);
@@ -858,6 +863,7 @@ do_map(
                                    mp->m_script_ctx = current_sctx;
                                    mp->m_script_ctx.sc_lnum += SOURCING_LNUM;
 #endif
+                                   mp_result[keyround - 1] = mp;
                                    did_it = TRUE;
                                }
                            }
@@ -921,18 +927,25 @@ do_map(
            continue;   // have added the new entry already
 
        // Get here when adding a new entry to the maphash[] list or abbrlist.
-       if (map_add(map_table, abbr_table, keys, rhs, orig_rhs,
-                   noremap, nowait, silent, mode, abbrev,
+       mp_result[keyround - 1] = map_add(map_table, abbr_table, keys,
+                   rhs, orig_rhs, noremap, nowait, silent, mode, abbrev,
 #ifdef FEAT_EVAL
                    expr, /* sid */ 0, /* scriptversion */ 0, /* lnum */ 0,
 #endif
-                   keyround1_simplified) == FAIL)
+                   keyround1_simplified);
+       if (mp_result[keyround - 1] == NULL)
        {
            retval = 4;     // no mem
            goto theend;
        }
     }
 
+    if (mp_result[0] != NULL && mp_result[1] != NULL)
+    {
+       mp_result[0]->m_alt = mp_result[1];
+       mp_result[1]->m_alt = mp_result[0];
+    }
+
 theend:
     vim_free(keys_buf);
     vim_free(alt_keys_buf);
@@ -2710,6 +2723,7 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
     int                nowait;
     char_u     *arg;
     int                dict_only;
+    mapblock_T *mp_result[2] = {NULL, NULL};
 
     // If first arg is a dict, then that's the only arg permitted.
     dict_only = argvars[0].v_type == VAR_DICT;
@@ -2806,12 +2820,20 @@ f_mapset(typval_T *argvars, typval_T *rettv UNUSED)
     do_map(MAPTYPE_UNMAP, arg, mode, is_abbr);
     vim_free(arg);
 
-    (void)map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs, noremap,
-           nowait, silent, mode, is_abbr, expr, sid, scriptversion, lnum, 0);
+    mp_result[0] = map_add(map_table, abbr_table, lhsraw, rhs, orig_rhs,
+                           noremap, nowait, silent, mode, is_abbr, expr, sid,
+                           scriptversion, lnum, 0);
     if (lhsrawalt != NULL)
-       (void)map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs, noremap,
-               nowait, silent, mode, is_abbr, expr, sid, scriptversion,
-                                                                     lnum, 1);
+       mp_result[1] = map_add(map_table, abbr_table, lhsrawalt, rhs, orig_rhs,
+                           noremap, nowait, silent, mode, is_abbr, expr, sid,
+                           scriptversion, lnum, 1);
+
+    if (mp_result[0] != NULL && mp_result[1] != NULL)
+    {
+       mp_result[0]->m_alt = mp_result[1];
+       mp_result[1]->m_alt = mp_result[0];
+    }
+
     vim_free(arg_buf);
 }
 #endif
diff --git a/src/structs.h b/src/structs.h
index 32c35b7bf..fe4704a36 100644
--- a/src/structs.h
+++ b/src/structs.h
@@ -1309,6 +1309,9 @@ typedef struct mapblock mapblock_T;
 struct mapblock
 {
     mapblock_T *m_next;        // next mapblock in list
+    mapblock_T *m_alt;         // pointer to mapblock of the same mapping
+                               // with an alternative form of m_keys, or NULL
+                               // if there is no such mapblock
     char_u     *m_keys;        // mapped from, lhs
     char_u     *m_str;         // mapped to, rhs
     char_u     *m_orig_str;    // rhs as entered by the user
diff --git a/src/testdir/test_mapping.vim b/src/testdir/test_mapping.vim
index 117531046..654a6734e 100644
--- a/src/testdir/test_mapping.vim
+++ b/src/testdir/test_mapping.vim
@@ -1767,6 +1767,49 @@ func Test_unmap_simplifiable()
   unmap <C-I>
 endfunc
 
+" Test that the first byte of rhs is not remapped if rhs starts with lhs.
+func Test_map_rhs_starts_with_lhs()
+  new
+  func MapExpr()
+    return "\<C-R>\<C-P>"
+  endfunc
+
+  for expr in [v:false, v:true]
+    if expr
+      imap <buffer><expr> <C-R> MapExpr()
+    else
+      imap <buffer> <C-R> <C-R><C-P>
+    endif
+
+    for restore in [v:false, v:true]
+      if restore
+        let saved = maparg('<C-R>', 'i', v:false, v:true)
+        iunmap <buffer> <C-R>
+        call mapset(saved)
+      endif
+
+      let @a = 'foo'
+      call feedkeys("S\<C-R>a", 'tx')
+      call assert_equal('foo', getline('.'))
+
+      let @a = 'bar'
+      call feedkeys("S\<*C-R>a", 'tx')
+      call assert_equal('bar', getline('.'))
+    endfor
+  endfor
+
+  " When two mappings are used for <C-I> and <Tab>, remapping should work.
+  imap <buffer> <C-I> <Tab>bar
+  imap <buffer> <Tab> foo
+  call feedkeys("S\<Tab>", 'xt')
+  call assert_equal('foo', getline('.'))
+  call feedkeys("S\<*C-I>", 'xt')
+  call assert_equal('foobar', getline('.'))
+
+  delfunc MapExpr
+  bwipe!
+endfunc
+
 func Test_expr_map_escape_special()
   nnoremap … <Cmd>let g:got_ellipsis += 1<CR>
   func Func()
diff --git a/src/version.c b/src/version.c
index 8afbd0148..153685953 100644
--- a/src/version.c
+++ b/src/version.c
@@ -704,6 +704,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    642,
 /**/
     641,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/vim_dev/E1sYVpp-001swE-50%40256bit.org.

Raspunde prin e-mail lui