# HG changeset patch # User Bram Moolenaar # Date 1609760705 -3600 # Node ID bb29b09902d5864a9601dd24bd6be773af4ffc29 # Parent 25d5c354a83ea79066ca473e74a956a26edf9c30 patch 8.2.2295: incsearch does not detect empty pattern properly Commit: https://github.com/vim/vim/commit/d93a7fc1a98a58f8101ee780d4735079ad99ae35 Author: Bram Moolenaar Date: Mon Jan 4 12:42:13 2021 +0100 patch 8.2.2295: incsearch does not detect empty pattern properly Problem: Incsearch does not detect empty pattern properly. Solution: Return magic state when skipping over a pattern. (Christian Brabandt, closes #7612, closes #6420) diff --git a/src/ex_cmds.c b/src/ex_cmds.c --- a/src/ex_cmds.c +++ b/src/ex_cmds.c @@ -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 } diff --git a/src/ex_docmd.c b/src/ex_docmd.c --- a/src/ex_docmd.c +++ b/src/ex_docmd.c @@ -7529,9 +7529,10 @@ ex_may_print(exarg_T *eap) static void ex_submagic(exarg_T *eap) { - magic_T saved = magic_overruled; - - magic_overruled = eap->cmdidx == CMD_smagic ? MAGIC_ON : MAGIC_OFF; + optmagic_T saved = magic_overruled; + + magic_overruled = eap->cmdidx == CMD_smagic + ? OPTION_MAGIC_ON : OPTION_MAGIC_OFF; ex_substitute(eap); magic_overruled = saved; } diff --git a/src/ex_getln.c b/src/ex_getln.c --- a/src/ex_getln.c +++ b/src/ex_getln.c @@ -52,6 +52,9 @@ static void restore_cmdline(cmdline_info 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); diff --git a/src/globals.h b/src/globals.h --- a/src/globals.h +++ b/src/globals.h @@ -1945,7 +1945,6 @@ EXTERN int channel_need_redraw INIT(= FA #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); diff --git a/src/option.c b/src/option.c --- a/src/option.c +++ b/src/option.c @@ -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()) diff --git a/src/proto/regexp.pro b/src/proto/regexp.pro --- a/src/proto/regexp.pro +++ b/src/proto/regexp.pro @@ -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); diff --git a/src/regexp.c b/src/regexp.c --- a/src/regexp.c +++ b/src/regexp.c @@ -304,11 +304,7 @@ static unsigned regflags; // RF_ flags f 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; } diff --git a/src/search.c b/src/search.c --- a/src/search.c +++ b/src/search.c @@ -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 "?" diff --git a/src/structs.h b/src/structs.h --- a/src/structs.h +++ b/src/structs.h @@ -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; diff --git a/src/tag.c b/src/tag.c --- 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 --- /dev/null +++ b/src/testdir/dumps/Test_incsearch_sub_01.dump @@ -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 --- /dev/null +++ b/src/testdir/dumps/Test_incsearch_sub_02.dump @@ -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 diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim --- a/src/testdir/test_search.vim +++ b/src/testdir/test_search.vim @@ -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, "\") + + " 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 diff --git a/src/version.c b/src/version.c --- a/src/version.c +++ b/src/version.c @@ -751,6 +751,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2295, +/**/ 2294, /**/ 2293, diff --git a/src/vim9compile.c b/src/vim9compile.c --- a/src/vim9compile.c +++ b/src/vim9compile.c @@ -7048,7 +7048,7 @@ compile_catch(char_u *arg, cctx_T *cctx // 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, { 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;