]> granicus.if.org Git - vim/commitdiff
patch 8.2.2295: incsearch does not detect empty pattern properly v8.2.2295
authorBram Moolenaar <Bram@vim.org>
Mon, 4 Jan 2021 11:42:13 +0000 (12:42 +0100)
committerBram Moolenaar <Bram@vim.org>
Mon, 4 Jan 2021 11:42:13 +0000 (12:42 +0100)
Problem:    Incsearch does not detect empty pattern properly.
Solution:   Return magic state when skipping over a pattern. (Christian
            Brabandt, closes #7612, closes #6420)

15 files changed:
src/ex_cmds.c
src/ex_docmd.c
src/ex_getln.c
src/globals.h
src/option.c
src/proto/regexp.pro
src/regexp.c
src/search.c
src/structs.h
src/tag.c
src/testdir/dumps/Test_incsearch_sub_01.dump [new file with mode: 0644]
src/testdir/dumps/Test_incsearch_sub_02.dump [new file with mode: 0644]
src/testdir/test_search.vim
src/version.c
src/vim9compile.c

index 734949c336e7d32eeda1ae7f6dccdb1ed0228058..c61810fe6c6948dc377d0f7a99015b0f1990f44f 100644 (file)
@@ -3672,7 +3672,7 @@ ex_substitute(exarg_T *eap)
            delimiter = *cmd++;             // remember delimiter character
            pat = cmd;                      // remember start of search pat
            cmd = skip_regexp_ex(cmd, delimiter, magic_isset(),
-                                                             &eap->arg, NULL);
+                                                       &eap->arg, NULL, NULL);
            if (cmd[0] == delimiter)        // end delimiter found
                *cmd++ = NUL;               // replace it with a NUL
        }
@@ -4856,7 +4856,7 @@ ex_global(exarg_T *eap)
        if (delim)
            ++cmd;              // skip delimiter if there is one
        pat = cmd;              // remember start of pattern
-       cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL);
+       cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL, NULL);
        if (cmd[0] == delim)                // end delimiter found
            *cmd++ = NUL;                   // replace it with a NUL
     }
index dc9ea165f953b80d9d16c80bd8bda95d68ddaa0e..044c18a85bce8a5e2d6c82eb8fd5bf7f0a542611 100644 (file)
@@ -7529,9 +7529,10 @@ ex_may_print(exarg_T *eap)
     static void
 ex_submagic(exarg_T *eap)
 {
-    magic_T saved = magic_overruled;
+    optmagic_T saved = magic_overruled;
 
-    magic_overruled = eap->cmdidx == CMD_smagic ? MAGIC_ON : MAGIC_OFF;
+    magic_overruled = eap->cmdidx == CMD_smagic
+                                         ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF;
     ex_substitute(eap);
     magic_overruled = saved;
 }
index 04b10ead46fb65bc3e070d9af9987dad90df98db..a3e1f4995cf6c97572acc120ec25026bc39232d1 100644 (file)
@@ -52,6 +52,9 @@ static void   restore_cmdline(cmdline_info_T *ccp);
 static int     cmdline_paste(int regname, int literally, int remcr);
 static void    redrawcmdprompt(void);
 static int     ccheck_abbr(int);
+#ifdef FEAT_SEARCH_EXTRA
+static int     empty_pattern_magic(char_u *pat, size_t len, magic_T magic_val);
+#endif
 
 #ifdef FEAT_CMDWIN
 static int     open_cmdwin(void);
@@ -89,15 +92,34 @@ abandon_cmdline(void)
  * as a trailing \|, which can happen while typing a pattern.
  */
     static int
-empty_pattern(char_u *p)
+empty_pattern(char_u *p, int delim)
 {
-    size_t n = STRLEN(p);
+    size_t     n = STRLEN(p);
+    magic_T    magic_val = MAGIC_ON;
+
+    if (n > 0)
+       (void) skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val);
+    else
+       return TRUE;
 
+    return empty_pattern_magic(p, n, magic_val);
+}
+
+    static int
+empty_pattern_magic(char_u *p, size_t len, magic_T magic_val)
+{
     // remove trailing \v and the like
-    while (n >= 2 && p[n - 2] == '\\'
-                         && vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL)
-       n -= 2;
-    return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
+    while (len >= 2 && p[len - 2] == '\\'
+                        && vim_strchr((char_u *)"mMvVcCZ", p[len - 1]) != NULL)
+       len -= 2;
+
+    // true, if the pattern is empty, or the pattern ends with \| and magic is
+    // set (or it ends with '|' and very magic is set)
+    return len == 0 || (len > 1
+           && ((p[len - 2] == '\\'
+                                && p[len - 1] == '|' && magic_val == MAGIC_ON)
+               || (p[len - 2] != '\\'
+                            && p[len - 1] == '|' && magic_val == MAGIC_ALL)));
 }
 
 // Struct to store the viewstate during 'incsearch' highlighting.
@@ -149,7 +171,7 @@ typedef struct {
     pos_T      match_end;
     int                did_incsearch;
     int                incsearch_postponed;
-    magic_T    magic_overruled_save;
+    optmagic_T magic_overruled_save;
 } incsearch_state_T;
 
     static void
@@ -207,6 +229,7 @@ do_incsearch_highlighting(
     pos_T      save_cursor;
     int                use_last_pat;
     int                retval = FALSE;
+    magic_T     magic = 0;
 
     *skiplen = 0;
     *patlen = ccline.cmdlen;
@@ -252,9 +275,9 @@ do_incsearch_highlighting(
            || STRNCMP(cmd, "vglobal", p - cmd) == 0)
     {
        if (*cmd == 's' && cmd[1] == 'm')
-           magic_overruled = MAGIC_ON;
+           magic_overruled = OPTION_MAGIC_ON;
        else if (*cmd == 's' && cmd[1] == 'n')
-           magic_overruled = MAGIC_OFF;
+           magic_overruled = OPTION_MAGIC_OFF;
     }
     else if (STRNCMP(cmd, "sort", MAX(p - cmd, 3)) == 0)
     {
@@ -288,7 +311,7 @@ do_incsearch_highlighting(
     p = skipwhite(p);
     delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++;
     *search_delim = delim;
-    end = skip_regexp(p, delim, magic_isset());
+    end = skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic);
 
     use_last_pat = end == p && *end == delim;
 
@@ -302,7 +325,7 @@ do_incsearch_highlighting(
        int  empty;
 
        *end = NUL;
-       empty = empty_pattern(p);
+       empty = empty_pattern_magic(p, STRLEN(p), magic);
        *end = c;
        if (empty)
            goto theend;
@@ -535,7 +558,8 @@ may_do_incsearch_highlighting(
     {
        next_char = ccline.cmdbuff[skiplen + patlen];
        ccline.cmdbuff[skiplen + patlen] = NUL;
-       if (empty_pattern(ccline.cmdbuff) && !no_hlsearch)
+       if (empty_pattern(ccline.cmdbuff + skiplen, search_delim)
+                                                              && !no_hlsearch)
        {
            redraw_all_later(SOME_VALID);
            set_no_hlsearch(TRUE);
index dcb0f63243f70dd747cefff39b52c16660ef3952..e4e73f21075b3b9ba12ca9acba454cc9cfdbf2a2 100644 (file)
@@ -1945,7 +1945,6 @@ EXTERN int channel_need_redraw INIT(= FALSE);
 #define FOR_ALL_LIST_ITEMS(l, li) \
     for ((li) = (l)->lv_first; (li) != NULL; (li) = (li)->li_next)
 
-// While executing a regexp and set to MAGIC_ON or MAGIC_OFF this overrules
-// p_magic.  Otherwise set to MAGIC_NOT_SET.
-
-EXTERN magic_T magic_overruled INIT(= MAGIC_NOT_SET);
+// While executing a regexp and set to OPTION_MAGIC_ON or OPTION_MAGIC_OFF this
+// overrules p_magic.  Otherwise set to OPTION_MAGIC_NOT_SET.
+EXTERN optmagic_T magic_overruled INIT(= OPTION_MAGIC_NOT_SET);
index 094a72354141f3c1652dd270adc245e191c4e3e8..b4893a10ad3f57177194b2a412c54f7b2cd86d91 100644 (file)
@@ -7009,9 +7009,9 @@ magic_isset(void)
 {
     switch (magic_overruled)
     {
-       case MAGIC_ON:      return TRUE;
-       case MAGIC_OFF:     return FALSE;
-       case MAGIC_NOT_SET: break;
+       case OPTION_MAGIC_ON:      return TRUE;
+       case OPTION_MAGIC_OFF:     return FALSE;
+       case OPTION_MAGIC_NOT_SET: break;
     }
 #ifdef FEAT_EVAL
     if (in_vim9script())
index d3a77541515d173623bd129a2cf86cc4c6d7ec09..15b1225b0791df6bfe795e2ad787fe4a31105f62 100644 (file)
@@ -2,7 +2,7 @@
 int re_multiline(regprog_T *prog);
 char_u *skip_regexp(char_u *startp, int delim, int magic);
 char_u *skip_regexp_err(char_u *startp, int delim, int magic);
-char_u *skip_regexp_ex(char_u *startp, int dirc, int magic, char_u **newp, int *dropped);
+char_u *skip_regexp_ex(char_u *startp, int dirc, int magic, char_u **newp, int *dropped, magic_T *magic_val);
 reg_extmatch_T *ref_extmatch(reg_extmatch_T *em);
 void unref_extmatch(reg_extmatch_T *em);
 char_u *regtilde(char_u *source, int magic);
index 0fd6de61ec4d1a85a2ddd4889401d6f7501e70e5..9d2d441fc4d7935aae13ab148f8beae3f59c817f 100644 (file)
@@ -304,11 +304,7 @@ static unsigned    regflags;       // RF_ flags for prog
 static int     had_eol;        // TRUE when EOL found by vim_regcomp()
 #endif
 
-static int     reg_magic;      // magicness of the pattern:
-#define MAGIC_NONE     1       // "\V" very unmagic
-#define MAGIC_OFF      2       // "\M" or 'magic' off
-#define MAGIC_ON       3       // "\m" or 'magic'
-#define MAGIC_ALL      4       // "\v" very magic
+static magic_T reg_magic;      // magicness of the pattern
 
 static int     reg_string;     // matching with a string instead of a buffer
                                // line
@@ -548,7 +544,7 @@ skip_regexp(
     int                delim,
     int                magic)
 {
-    return skip_regexp_ex(startp, delim, magic, NULL, NULL);
+    return skip_regexp_ex(startp, delim, magic, NULL, NULL, NULL);
 }
 
 /*
@@ -577,6 +573,7 @@ skip_regexp_err(
  * expression and change "\?" to "?".  If "*newp" is not NULL the expression
  * is changed in-place.
  * If a "\?" is changed to "?" then "dropped" is incremented, unless NULL.
+ * If "magic_val" is not NULL, returns the effective magicness of the pattern
  */
     char_u *
 skip_regexp_ex(
@@ -584,9 +581,10 @@ skip_regexp_ex(
     int                dirc,
     int                magic,
     char_u     **newp,
-    int                *dropped)
+    int                *dropped,
+    magic_T    *magic_val)
 {
-    int                mymagic;
+    magic_T    mymagic;
     char_u     *p = startp;
 
     if (magic)
@@ -632,6 +630,8 @@ skip_regexp_ex(
                mymagic = MAGIC_NONE;
        }
     }
+    if (magic_val != NULL)
+       *magic_val = mymagic;
     return p;
 }
 
index 6305b58b190e8547c1389b3f48037aa198187e91..3a4d4589a0e87a31ea257adc7c9344bf1791365d 100644 (file)
@@ -1342,7 +1342,7 @@ do_search(
             */
            ps = strcopy;
            p = skip_regexp_ex(pat, search_delim, magic_isset(),
-                                                              &strcopy, NULL);
+                                                       &strcopy, NULL, NULL);
            if (strcopy != ps)
            {
                // made a copy of "pat" to change "\?" to "?"
index e2045104ca03158868d73dded883814b751a59ed..c016f199750f79011825292485ffd928c9b33f18 100644 (file)
@@ -4321,8 +4321,20 @@ typedef struct
 // with iconv() to be able to allocate a buffer.
 #define ICONV_MULT 8
 
+// Used for "magic_overruled".
 typedef enum {
-    MAGIC_NOT_SET,     // p_magic not overruled
-    MAGIC_ON,          // magic on inside regexp
-    MAGIC_OFF          // magic off inside regexp
+    OPTION_MAGIC_NOT_SET,      // p_magic not overruled
+    OPTION_MAGIC_ON,           // magic on inside regexp
+    OPTION_MAGIC_OFF           // magic off inside regexp
+} optmagic_T;
+
+// Magicness of a pattern, used by regexp code.
+// The order and values matter:
+//  magic <= MAGIC_OFF includes MAGIC_NONE
+//  magic >= MAGIC_ON  includes MAGIC_ALL
+typedef enum {
+    MAGIC_NONE = 1,            // "\V" very unmagic
+    MAGIC_OFF = 2,             // "\M" or 'magic' off
+    MAGIC_ON = 3,              // "\m" or 'magic'
+    MAGIC_ALL = 4              // "\v" very magic
 } magic_T;
index c4896b3846498696912b455286a5172b68eafcdc..3dfb8fee5dd7253e938c01ed540da50074968727 100644 (file)
--- a/src/tag.c
+++ b/src/tag.c
@@ -3312,7 +3312,7 @@ jumpto_tag(
     int                keep_help)      // keep help flag (FALSE for cscope)
 {
     int                save_secure;
-    int                save_magic_overruled;
+    optmagic_T save_magic_overruled;
     int                save_p_ws, save_p_scs, save_p_ic;
     linenr_T   save_lnum;
     char_u     *str;
@@ -3505,7 +3505,7 @@ jumpto_tag(
        ++sandbox;
 #endif
        save_magic_overruled = magic_overruled;
-       magic_overruled = MAGIC_OFF;    // always execute with 'nomagic'
+       magic_overruled = OPTION_MAGIC_OFF;     // always execute with 'nomagic'
 #ifdef FEAT_SEARCH_EXTRA
        // Save value of no_hlsearch, jumping to a tag is not a real search
        save_no_hlsearch = no_hlsearch;
diff --git a/src/testdir/dumps/Test_incsearch_sub_01.dump b/src/testdir/dumps/Test_incsearch_sub_01.dump
new file mode 100644 (file)
index 0000000..5924fbd
--- /dev/null
@@ -0,0 +1,9 @@
+|f+0&#ffffff0|o@1| |1| @64
+|f|o@1| |2| @64
+|f|o@1| |3| @64
+|f|o@1| |4| @64
+|a|b|c|||d|e|f| @62
+|~+0#4040ff13&| @68
+|~| @68
+|~| @68
+|:+0#0000000&|%|s|/|\|v|a|b|c||> @59
diff --git a/src/testdir/dumps/Test_incsearch_sub_02.dump b/src/testdir/dumps/Test_incsearch_sub_02.dump
new file mode 100644 (file)
index 0000000..c6b58cb
--- /dev/null
@@ -0,0 +1,9 @@
+|f+0&#ffffff0|o@1| |1| @64
+|f|o@1| |2| @64
+|f|o@1| |3| @64
+|f|o@1| |4| @64
+|a|b|c|||d|e|f| @62
+|~+0#4040ff13&| @68
+|~| @68
+|~| @68
+|:+0#0000000&|1|,|5|s|/|\|v||> @60
index 1018294613439b0c7e15068512acd5f0215cd004..2dc53d97de428cf15f31e72586d55af82ec2eecc 100644 (file)
@@ -1808,4 +1808,33 @@ func Test_incsearch_highlighting_newline()
   bw
 endfunc
 
+func Test_incsearch_substitute_dump2()
+  CheckOption incsearch
+  CheckScreendump
+
+  call writefile([
+       \ 'set incsearch hlsearch scrolloff=0',
+       \ 'for n in range(1, 4)',
+       \ '  call setline(n, "foo " . n)',
+       \ 'endfor',
+       \ 'call setline(5, "abc|def")',
+       \ '3',
+       \ ], 'Xis_subst_script2')
+  let buf = RunVimInTerminal('-S Xis_subst_script2', {'rows': 9, 'cols': 70})
+
+  call term_sendkeys(buf, ':%s/\vabc|')
+  sleep 100m
+  call VerifyScreenDump(buf, 'Test_incsearch_sub_01', {})
+  call term_sendkeys(buf, "\<Esc>")
+
+  " The following should not be highlighted
+  call term_sendkeys(buf, ':1,5s/\v|')
+  sleep 100m
+  call VerifyScreenDump(buf, 'Test_incsearch_sub_02', {})
+
+
+  call StopVimInTerminal(buf)
+  call delete('Xis_subst_script2')
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
index db20152a312557109f3c52c75dd2fc41a7ba72a0..12877de0a0e4d5deb17e29d449466e5ce6b1a1b7 100644 (file)
@@ -750,6 +750,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2295,
 /**/
     2294,
 /**/
index 3eae641f295be365c72da5904bd4c3ab043ed46b..80f918d9e000f9b59afb046901e439d509c9edbb 100644 (file)
@@ -7048,7 +7048,7 @@ compile_catch(char_u *arg, cctx_T *cctx UNUSED)
        // Push v:exception, push {expr} and MATCH
        generate_instr_type(cctx, ISN_PUSHEXC, &t_string);
 
-       end = skip_regexp_ex(p + 1, *p, TRUE, &tofree, &dropped);
+       end = skip_regexp_ex(p + 1, *p, TRUE, &tofree, &dropped, NULL);
        if (*end != *p)
        {
            semsg(_(e_separator_mismatch_str), p);
@@ -7372,7 +7372,7 @@ compile_exec(char_u *line, exarg_T *eap, cctx_T *cctx)
     {
        int delim = *eap->arg;
 
-       p = skip_regexp_ex(eap->arg + 1, delim, TRUE, NULL, NULL);
+       p = skip_regexp_ex(eap->arg + 1, delim, TRUE, NULL, NULL, NULL);
        if (*p == delim)
        {
            eap->arg = p + 1;