From a91ba088abf7b21f640f671ad7f211c0957b4765 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 4 Dec 2022 08:38:38 +0800 Subject: vim-patch:8.2.2182: Vim9: value of 'magic' is still relevant Problem: Vim9: value of 'magic' is still relevant. Solution: Always behave like 'magic' is on in Vim9 script (closes vim/vim#7509) https://github.com/vim/vim/commit/f4e2099e39ed4d71aed0f9a9579455aed5ec6cc2 EX_NONWHITE_OK is N/A: only applies to Vim9 script. Co-authored-by: Bram Moolenaar --- src/nvim/arglist.c | 4 ++-- src/nvim/buffer.c | 2 +- src/nvim/cmdexpand.c | 6 +++--- src/nvim/ex_cmds.c | 14 ++++++++------ src/nvim/ex_docmd.c | 16 ++++++++-------- src/nvim/ex_getln.c | 14 +++++++------- src/nvim/globals.h | 4 ++++ src/nvim/insexpand.c | 4 ++-- src/nvim/normal.c | 4 ++-- src/nvim/option.c | 14 ++++++++++++++ src/nvim/regexp_defs.h | 6 ++++++ src/nvim/search.c | 10 +++++----- src/nvim/shada.c | 2 +- src/nvim/tag.c | 12 ++++++------ 14 files changed, 69 insertions(+), 43 deletions(-) diff --git a/src/nvim/arglist.c b/src/nvim/arglist.c index 0d52cf647f..64d77a7ecc 100644 --- a/src/nvim/arglist.c +++ b/src/nvim/arglist.c @@ -29,7 +29,7 @@ #include "nvim/memline_defs.h" #include "nvim/memory.h" #include "nvim/message.h" -#include "nvim/option_defs.h" +#include "nvim/option.h" #include "nvim/os/input.h" #include "nvim/path.h" #include "nvim/pos.h" @@ -380,7 +380,7 @@ static void arglist_del_files(garray_T *alist_ga) if (p == NULL) { break; } - regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { xfree(p); break; diff --git a/src/nvim/buffer.c b/src/nvim/buffer.c index c9fe5f9670..b67aee6907 100644 --- a/src/nvim/buffer.c +++ b/src/nvim/buffer.c @@ -2250,7 +2250,7 @@ int buflist_findpat(const char *pattern, const char *pattern_end, bool unlisted, } regmatch_T regmatch; - regmatch.regprog = vim_regcomp(p, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp(p, magic_isset() ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { xfree(pat); return -1; diff --git a/src/nvim/cmdexpand.c b/src/nvim/cmdexpand.c index f1231908c7..9619c81636 100644 --- a/src/nvim/cmdexpand.c +++ b/src/nvim/cmdexpand.c @@ -1505,7 +1505,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons arg = (const char *)skipwhite(skiptowhite(arg)); if (*arg != NUL) { xp->xp_context = EXPAND_NOTHING; - arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), p_magic); + arg = (const char *)skip_regexp((char *)arg + 1, (uint8_t)(*arg), magic_isset()); } } return (const char *)find_nextcmd(arg); @@ -1544,7 +1544,7 @@ static const char *set_context_by_cmdname(const char *cmd, cmdidx_T cmdidx, cons if (delim) { // Skip "from" part. arg++; - arg = (const char *)skip_regexp((char *)arg, delim, p_magic); + arg = (const char *)skip_regexp((char *)arg, delim, magic_isset()); } // Skip "to" part. while (arg[0] != NUL && (uint8_t)arg[0] != delim) { @@ -2458,7 +2458,7 @@ static int ExpandFromContext(expand_T *xp, char *pat, int *num_file, char ***fil return nlua_expand_pat(xp, pat, num_file, file); } - regmatch.regprog = vim_regcomp(pat, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp(pat, magic_isset() ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { return FAIL; } diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 8ae7646268..33f7cde45e 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3131,7 +3131,7 @@ static bool sub_joining_lines(exarg_T *eap, char *pat, const char *sub, const ch if (save) { if ((cmdmod.cmod_flags & CMOD_KEEPPATTERNS) == 0) { - save_re_pat(RE_SUBST, pat, p_magic); + save_re_pat(RE_SUBST, pat, magic_isset()); } // put pattern in history add_to_history(HIST_SEARCH, pat, true, NUL); @@ -3356,7 +3356,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T which_pat = RE_LAST; // use last used regexp delimiter = (char_u)(*cmd++); // remember delimiter character pat = cmd; // remember start of search pat - cmd = skip_regexp_ex(cmd, delimiter, p_magic, &eap->arg, NULL); + cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), &eap->arg, NULL); if (cmd[0] == delimiter) { // end delimiter found *cmd++ = NUL; // replace it with a NUL has_second_delim = true; @@ -3472,7 +3472,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T sub = xstrdup(sub); sub_copy = sub; } else { - char *newsub = regtilde(sub, p_magic, cmdpreview); + char *newsub = regtilde(sub, magic_isset(), cmdpreview); if (newsub != sub) { // newsub was allocated, free it later. sub_copy = newsub; @@ -3895,7 +3895,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T sublen = vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, (char_u *)sub, (char_u *)sub_firstline, 0, - REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0)); + REGSUB_BACKSLASH + | (magic_isset() ? REGSUB_MAGIC : 0)); textlock--; // If getting the substitute string caused an error, don't do @@ -3937,7 +3938,8 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T (void)vim_regsub_multi(®match, sub_firstlnum - regmatch.startpos[0].lnum, (char_u *)sub, (char_u *)new_end, sublen, - REGSUB_COPY | REGSUB_BACKSLASH | (p_magic ? REGSUB_MAGIC : 0)); + REGSUB_COPY | REGSUB_BACKSLASH + | (magic_isset() ? REGSUB_MAGIC : 0)); textlock--; sub_nsubs++; did_sub = true; @@ -4389,7 +4391,7 @@ void ex_global(exarg_T *eap) delim = *cmd; // get the delimiter cmd++; // skip delimiter if there is one pat = cmd; // remember start of pattern - cmd = skip_regexp_ex(cmd, delim, p_magic, &eap->arg, NULL); + cmd = skip_regexp_ex(cmd, delim, magic_isset(), &eap->arg, NULL); if (cmd[0] == delim) { // end delimiter found *cmd++ = NUL; // replace it with a NUL } diff --git a/src/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index 1ea344dc0e..c28f6cccce 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -3355,7 +3355,7 @@ static linenr_T get_address(exarg_T *eap, char **ptr, cmd_addr_T addr_type, int goto error; } if (skip) { // skip "/pat/" - cmd = skip_regexp(cmd, c, p_magic); + cmd = skip_regexp(cmd, c, magic_isset()); if (*cmd == c) { cmd++; } @@ -5828,21 +5828,21 @@ void ex_may_print(exarg_T *eap) /// ":smagic" and ":snomagic". static void ex_submagic(exarg_T *eap) { - int magic_save = p_magic; + const magic_T saved = magic_overruled; - p_magic = (eap->cmdidx == CMD_smagic); + magic_overruled = eap->cmdidx == CMD_smagic ? MAGIC_ON : MAGIC_OFF; ex_substitute(eap); - p_magic = magic_save; + magic_overruled = saved; } /// ":smagic" and ":snomagic" preview callback. static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) { - int magic_save = p_magic; + const magic_T saved = magic_overruled; - p_magic = (eap->cmdidx == CMD_smagic); + magic_overruled = eap->cmdidx == CMD_smagic ? MAGIC_ON : MAGIC_OFF; int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr); - p_magic = magic_save; + magic_overruled = saved; return retv; } @@ -6503,7 +6503,7 @@ static void ex_findpat(exarg_T *eap) if (*eap->arg == '/') { // Match regexp, not just whole words whole = false; eap->arg++; - char *p = skip_regexp(eap->arg, '/', p_magic); + char *p = skip_regexp(eap->arg, '/', magic_isset()); if (*p) { *p++ = NUL; p = skipwhite(p); diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 8d4504ca4a..881f4fc0c6 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -98,7 +98,7 @@ typedef struct { pos_T match_end; bool did_incsearch; bool incsearch_postponed; - int magic_save; + magic_T magic_overruled_save; } incsearch_state_T; typedef struct command_line_state { @@ -211,7 +211,7 @@ static void init_incsearch_state(incsearch_state_T *s) s->match_start = curwin->w_cursor; s->did_incsearch = false; s->incsearch_postponed = false; - s->magic_save = p_magic; + s->magic_overruled_save = magic_overruled; clearpos(&s->match_end); s->save_cursor = curwin->w_cursor; // may be restored later s->search_start = curwin->w_cursor; @@ -281,9 +281,9 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0 || strncmp(cmd, "vglobal", (size_t)(p - cmd)) == 0) { if (*cmd == 's' && cmd[1] == 'm') { - p_magic = true; + magic_overruled = MAGIC_ON; } else if (*cmd == 's' && cmd[1] == 'n') { - p_magic = false; + magic_overruled = MAGIC_OFF; } } else if (strncmp(cmd, "sort", (size_t)MAX(p - cmd, 3)) == 0) { // skip over ! and flags @@ -318,7 +318,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s p = skipwhite(p); delim = (delim_optional && vim_isIDc(*p)) ? ' ' : *p++; *search_delim = delim; - end = skip_regexp(p, delim, p_magic); + end = skip_regexp(p, delim, magic_isset()); use_last_pat = end == p && *end == delim; if (end == p && !use_last_pat) { @@ -556,7 +556,7 @@ static int may_add_char_to_search(int firstc, int *c, incsearch_state_T *s) *c = mb_tolower(*c); } if (*c == search_delim - || vim_strchr((p_magic ? "\\~^$.*[" : "\\^$"), *c) != NULL) { + || vim_strchr((magic_isset() ? "\\~^$.*[" : "\\^$"), *c) != NULL) { // put a backslash before special characters stuffcharReadbuff(*c); *c = '\\'; @@ -588,7 +588,7 @@ static void finish_incsearch_highlighting(int gotesc, incsearch_state_T *s, bool search_first_line = 0; search_last_line = MAXLNUM; - p_magic = s->magic_save; + magic_overruled = s->magic_overruled_save; validate_cursor(); // needed for TAB redraw_all_later(UPD_SOME_VALID); diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 8f9fa6673c..50a2ee3b07 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1087,6 +1087,10 @@ EXTERN char windowsVersion[20] INIT(= { 0 }); EXTERN int exit_need_delay INIT(= 0); +/// 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); + /// Skip win_fix_cursor() call for 'splitkeep' when cmdwin is closed. EXTERN bool skip_win_fix_cursor INIT(= false); /// Skip win_fix_scroll() call for 'splitkeep' when closing tab page. diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 625e48258c..6531da6419 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -1336,7 +1336,7 @@ static void ins_compl_dictionaries(char_u *dict_start, char_u *pat, int flags, i xfree(pat_esc); xfree(ptr); } else { - regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp((char *)pat, magic_isset() ? RE_MAGIC : 0); if (regmatch.regprog == NULL) { goto theend; } @@ -4430,7 +4430,7 @@ static unsigned quote_meta(char_u *dest, char_u *src, int len) } FALLTHROUGH; case '~': - if (!p_magic) { // quote these only if magic is set + if (!magic_isset()) { // quote these only if magic is set break; } FALLTHROUGH; diff --git a/src/nvim/normal.c b/src/nvim/normal.c index 44df917de6..dac7e6f90b 100644 --- a/src/nvim/normal.c +++ b/src/nvim/normal.c @@ -3507,9 +3507,9 @@ static void nv_ident(cmdarg_T *cap) xfree(p); } else { if (cmdchar == '*') { - aux_ptr = (p_magic ? "/.*~[^$\\" : "/^$\\"); + aux_ptr = (magic_isset() ? "/.*~[^$\\" : "/^$\\"); } else if (cmdchar == '#') { - aux_ptr = (p_magic ? "/?.*~[^$\\" : "/?^$\\"); + aux_ptr = (magic_isset() ? "/?.*~[^$\\" : "/?^$\\"); } else if (tag_cmd) { if (curbuf->b_help) { // ":help" handles unescaped argument diff --git a/src/nvim/option.c b/src/nvim/option.c index e67bacce61..eac8e09d9e 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5147,6 +5147,20 @@ int fill_culopt_flags(char *val, win_T *wp) return OK; } +/// Get the value of 'magic' taking "magic_overruled" into account. +bool magic_isset(void) +{ + switch (magic_overruled) { + case MAGIC_ON: + return true; + case MAGIC_OFF: + return false; + case MAGIC_NOT_SET: + break; + } + return p_magic; +} + /// Set the callback function value for an option that accepts a function name, /// lambda, et al. (e.g. 'operatorfunc', 'tagfunc', etc.) /// @return OK if the option is successfully set to a function, otherwise FAIL diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index a3b77f295a..3b94723d4f 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -15,6 +15,12 @@ #include "nvim/pos.h" #include "nvim/types.h" +typedef enum { + MAGIC_NOT_SET, ///< p_magic not overruled + MAGIC_ON, ///< magic on inside regexp + MAGIC_OFF, ///< magic off inside regexp +} magic_T; + // The number of sub-matches is limited to 10. // The first one (index 0) is the whole match, referenced with "\0". // The second one (index 1) is the first sub-match, referenced with "\1". diff --git a/src/nvim/search.c b/src/nvim/search.c index c1db0c9232..cb10dfbc4a 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -138,7 +138,7 @@ int search_regcomp(char_u *pat, int pat_save, int pat_use, int options, regmmatc int i; rc_did_emsg = false; - magic = p_magic; + magic = magic_isset(); // If no pattern given, use a previously defined pattern. if (pat == NULL || *pat == NUL) { @@ -1089,7 +1089,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, long count, i // Find end of regular expression. // If there is a matching '/' or '?', toss it. ps = (char_u *)strcopy; - p = skip_regexp_ex(pat, search_delim, p_magic, &strcopy, NULL); + p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL); if (strcopy != (char *)ps) { // made a copy of "pat" to change "\?" to "?" searchcmdlen += (int)(strlen(pat) - strlen(strcopy)); @@ -3468,7 +3468,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool snprintf((char *)pat, patlen, whole ? "\\<%.*s\\>" : "%.*s", (int)len, ptr); // ignore case according to p_ic, p_scs and pat regmatch.rm_ic = ignorecase(pat); - regmatch.regprog = vim_regcomp((char *)pat, p_magic ? RE_MAGIC : 0); + regmatch.regprog = vim_regcomp((char *)pat, magic_isset() ? RE_MAGIC : 0); xfree(pat); if (regmatch.regprog == NULL) { goto fpip_end; @@ -3476,7 +3476,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool } char *inc_opt = (*curbuf->b_p_inc == NUL) ? p_inc : curbuf->b_p_inc; if (*inc_opt != NUL) { - incl_regmatch.regprog = vim_regcomp(inc_opt, p_magic ? RE_MAGIC : 0); + incl_regmatch.regprog = vim_regcomp(inc_opt, magic_isset() ? RE_MAGIC : 0); if (incl_regmatch.regprog == NULL) { goto fpip_end; } @@ -3485,7 +3485,7 @@ void find_pattern_in_path(char *ptr, Direction dir, size_t len, bool whole, bool if (type == FIND_DEFINE && (*curbuf->b_p_def != NUL || *p_def != NUL)) { def_regmatch.regprog = vim_regcomp(*curbuf->b_p_def == NUL ? p_def : curbuf->b_p_def, - p_magic ? RE_MAGIC : 0); + magic_isset() ? RE_MAGIC : 0); if (def_regmatch.regprog == NULL) { goto fpip_end; } diff --git a/src/nvim/shada.c b/src/nvim/shada.c index c72d2377aa..151cb834be 100644 --- a/src/nvim/shada.c +++ b/src/nvim/shada.c @@ -1252,7 +1252,7 @@ static void shada_read(ShaDaReadDef *const sd_reader, const int flags) // string is close to useless: you can only use it with :& or :~ and // that’s all because s//~ is not available until the first call to // regtilde. Vim was not calling this for some reason. - (void)regtilde(cur_entry.data.sub_string.sub, p_magic, false); + (void)regtilde(cur_entry.data.sub_string.sub, magic_isset(), false); // Do not free shada entry: its allocated memory was saved above. break; case kSDItemHistoryEntry: diff --git a/src/nvim/tag.c b/src/nvim/tag.c index e99ac894dc..cba671600a 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -1200,7 +1200,8 @@ static void prepare_pats(pat_T *pats, int has_re) pats->headlen = 0; } else { for (pats->headlen = 0; pats->head[pats->headlen] != NUL; pats->headlen++) { - if (vim_strchr((p_magic ? ".[~*\\$" : "\\$"), pats->head[pats->headlen]) != NULL) { + if (vim_strchr(magic_isset() ? ".[~*\\$" : "\\$", + pats->head[pats->headlen]) != NULL) { break; } } @@ -1211,7 +1212,7 @@ static void prepare_pats(pat_T *pats, int has_re) } if (has_re) { - pats->regmatch.regprog = vim_regcomp(pats->pat, p_magic ? RE_MAGIC : 0); + pats->regmatch.regprog = vim_regcomp(pats->pat, magic_isset() ? RE_MAGIC : 0); } else { pats->regmatch.regprog = NULL; } @@ -2812,7 +2813,6 @@ static char_u *tag_full_fname(tagptrs_T *tagp) /// @return OK for success, NOTAGFILE when file not found, FAIL otherwise. static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) { - int save_magic; bool save_p_ws; int save_p_scs, save_p_ic; linenr_T save_lnum; @@ -2955,8 +2955,8 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) curwin->w_set_curswant = true; postponed_split = 0; - save_magic = p_magic; - p_magic = false; // always execute with 'nomagic' + const magic_T save_magic_overruled = magic_overruled; + magic_overruled = MAGIC_OFF; // always execute with 'nomagic' // Save value of no_hlsearch, jumping to a tag is not a real search const bool save_no_hlsearch = no_hlsearch; @@ -3063,7 +3063,7 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) sandbox--; } - p_magic = save_magic; + magic_overruled = save_magic_overruled; // restore no_hlsearch when keeping the old search pattern if (search_options) { set_no_hlsearch(save_no_hlsearch); -- cgit From 46e4be0fd0002233bde613295607ce5eeb498567 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 4 Dec 2022 09:09:00 +0800 Subject: vim-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 vim/vim#7612, closes vim/vim#6420) https://github.com/vim/vim/commit/d93a7fc1a98a58f8101ee780d4735079ad99ae35 --- src/nvim/ex_cmds.c | 4 +-- src/nvim/ex_docmd.c | 8 +++--- src/nvim/ex_getln.c | 48 +++++++++++++++++++++++----------- src/nvim/globals.h | 6 ++--- src/nvim/option.c | 6 ++--- src/nvim/regexp.c | 17 ++++++------ src/nvim/regexp_defs.h | 18 ++++++++++--- src/nvim/search.c | 2 +- src/nvim/tag.c | 4 +-- src/nvim/testdir/test_search.vim | 29 ++++++++++++++++++++ test/functional/legacy/search_spec.lua | 43 +++++++++++++++++++++++++++--- 11 files changed, 140 insertions(+), 45 deletions(-) diff --git a/src/nvim/ex_cmds.c b/src/nvim/ex_cmds.c index 33f7cde45e..78d0888e27 100644 --- a/src/nvim/ex_cmds.c +++ b/src/nvim/ex_cmds.c @@ -3356,7 +3356,7 @@ static int do_sub(exarg_T *eap, proftime_T timeout, long cmdpreview_ns, handle_T which_pat = RE_LAST; // use last used regexp delimiter = (char_u)(*cmd++); // remember delimiter character pat = cmd; // remember start of search pat - cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), &eap->arg, NULL); + cmd = skip_regexp_ex(cmd, delimiter, magic_isset(), &eap->arg, NULL, NULL); if (cmd[0] == delimiter) { // end delimiter found *cmd++ = NUL; // replace it with a NUL has_second_delim = true; @@ -4391,7 +4391,7 @@ void ex_global(exarg_T *eap) delim = *cmd; // get the delimiter 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/nvim/ex_docmd.c b/src/nvim/ex_docmd.c index c28f6cccce..88ae0a8226 100644 --- a/src/nvim/ex_docmd.c +++ b/src/nvim/ex_docmd.c @@ -5828,9 +5828,9 @@ void ex_may_print(exarg_T *eap) /// ":smagic" and ":snomagic". static void ex_submagic(exarg_T *eap) { - const magic_T saved = magic_overruled; + const 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; } @@ -5838,9 +5838,9 @@ static void ex_submagic(exarg_T *eap) /// ":smagic" and ":snomagic" preview callback. static int ex_submagic_preview(exarg_T *eap, long cmdpreview_ns, handle_T cmdpreview_bufnr) { - const magic_T saved = magic_overruled; + const 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; int retv = ex_substitute_preview(eap, cmdpreview_ns, cmdpreview_bufnr); magic_overruled = saved; diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 881f4fc0c6..1ba55a86d8 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -98,7 +98,7 @@ typedef struct { pos_T match_end; bool did_incsearch; bool incsearch_postponed; - magic_T magic_overruled_save; + optmagic_T magic_overruled_save; } incsearch_state_T; typedef struct command_line_state { @@ -234,6 +234,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s pos_T save_cursor; bool use_last_pat; bool retval = false; + magic_T magic = 0; *skiplen = 0; *patlen = ccline.cmdlen; @@ -281,9 +282,9 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s || strncmp(cmd, "snomagic", (size_t)MAX(p - cmd, 3)) == 0 || strncmp(cmd, "vglobal", (size_t)(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", (size_t)MAX(p - cmd, 3)) == 0) { // skip over ! and flags @@ -318,7 +319,7 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s 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; if (end == p && !use_last_pat) { @@ -328,10 +329,8 @@ static bool do_incsearch_highlighting(int firstc, int *search_delim, incsearch_s // Don't do 'hlsearch' highlighting if the pattern matches everything. if (!use_last_pat) { char c = *end; - int empty; - *end = NUL; - empty = empty_pattern(p); + bool empty = empty_pattern_magic(p, strlen(p), magic); *end = c; if (empty) { goto theend; @@ -486,13 +485,13 @@ static void may_do_incsearch_highlighting(int firstc, long count, incsearch_stat } else { end_pos = curwin->w_cursor; // shutup gcc 4 } - // + // Disable 'hlsearch' highlighting if the pattern matches // everything. Avoids a flash when typing "foo\|". if (!use_last_pat) { 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(UPD_SOME_VALID); set_no_hlsearch(true); } @@ -2031,16 +2030,35 @@ static int command_line_not_changed(CommandLineState *s) /// Guess that the pattern matches everything. Only finds specific cases, such /// as a trailing \|, which can happen while typing a pattern. -static int empty_pattern(char *p) +static bool empty_pattern(char *p, int delim) { size_t n = strlen(p); + magic_T magic_val = MAGIC_ON; - // remove trailing \v and the like - while (n >= 2 && p[n - 2] == '\\' - && vim_strchr("mMvVcCZ", p[n - 1]) != NULL) { - n -= 2; + if (n > 0) { + (void)skip_regexp_ex(p, delim, magic_isset(), NULL, NULL, &magic_val); + } else { + return true; } - return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|'); + + return empty_pattern_magic(p, n, magic_val); +} + +static bool empty_pattern_magic(char *p, size_t len, magic_T magic_val) +{ + // remove trailing \v and the like + while (len >= 2 && p[len - 2] == '\\' + && vim_strchr("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))); } handle_T cmdpreview_get_bufnr(void) diff --git a/src/nvim/globals.h b/src/nvim/globals.h index 50a2ee3b07..c05e49e9a9 100644 --- a/src/nvim/globals.h +++ b/src/nvim/globals.h @@ -1087,9 +1087,9 @@ EXTERN char windowsVersion[20] INIT(= { 0 }); EXTERN int exit_need_delay INIT(= 0); -/// 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); /// Skip win_fix_cursor() call for 'splitkeep' when cmdwin is closed. EXTERN bool skip_win_fix_cursor INIT(= false); diff --git a/src/nvim/option.c b/src/nvim/option.c index eac8e09d9e..b1feac7d1b 100644 --- a/src/nvim/option.c +++ b/src/nvim/option.c @@ -5151,11 +5151,11 @@ int fill_culopt_flags(char *val, win_T *wp) bool magic_isset(void) { switch (magic_overruled) { - case MAGIC_ON: + case OPTION_MAGIC_ON: return true; - case MAGIC_OFF: + case OPTION_MAGIC_OFF: return false; - case MAGIC_NOT_SET: + case OPTION_MAGIC_NOT_SET: break; } return p_magic; diff --git a/src/nvim/regexp.c b/src/nvim/regexp.c index e58a8fa06a..7335345161 100644 --- a/src/nvim/regexp.c +++ b/src/nvim/regexp.c @@ -316,11 +316,7 @@ static int re_has_z; ///< \z item detected static unsigned regflags; ///< RF_ flags for prog static int had_eol; ///< true when EOL found by vim_regcomp() -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 @@ -485,7 +481,7 @@ static char_u *skip_anyof(char *p) /// Skip strings inside [ and ]. char *skip_regexp(char *startp, int delim, int magic) { - return skip_regexp_ex(startp, delim, magic, NULL, NULL); + return skip_regexp_ex(startp, delim, magic, NULL, NULL, NULL); } /// Call skip_regexp() and when the delimiter does not match give an error and @@ -506,9 +502,11 @@ char *skip_regexp_err(char *startp, int delim, int magic) /// 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. -char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *dropped) +/// If "magic_val" is not NULL, returns the effective magicness of the pattern +char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *dropped, + magic_T *magic_val) { - int mymagic; + magic_T mymagic; char *p = startp; if (magic) { @@ -549,6 +547,9 @@ char *skip_regexp_ex(char *startp, int dirc, int magic, char **newp, int *droppe } } } + if (magic_val != NULL) { + *magic_val = mymagic; + } return p; } diff --git a/src/nvim/regexp_defs.h b/src/nvim/regexp_defs.h index 3b94723d4f..16bb2db464 100644 --- a/src/nvim/regexp_defs.h +++ b/src/nvim/regexp_defs.h @@ -15,10 +15,22 @@ #include "nvim/pos.h" #include "nvim/types.h" +/// 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; // The number of sub-matches is limited to 10. diff --git a/src/nvim/search.c b/src/nvim/search.c index cb10dfbc4a..842927e60c 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -1089,7 +1089,7 @@ int do_search(oparg_T *oap, int dirc, int search_delim, char *pat, long count, i // Find end of regular expression. // If there is a matching '/' or '?', toss it. ps = (char_u *)strcopy; - p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL); + p = skip_regexp_ex(pat, search_delim, magic_isset(), &strcopy, NULL, NULL); if (strcopy != (char *)ps) { // made a copy of "pat" to change "\?" to "?" searchcmdlen += (int)(strlen(pat) - strlen(strcopy)); diff --git a/src/nvim/tag.c b/src/nvim/tag.c index cba671600a..bbfa0f518e 100644 --- a/src/nvim/tag.c +++ b/src/nvim/tag.c @@ -2955,8 +2955,8 @@ static int jumpto_tag(const char_u *lbuf_arg, int forceit, int keep_help) curwin->w_set_curswant = true; postponed_split = 0; - const magic_T save_magic_overruled = magic_overruled; - magic_overruled = MAGIC_OFF; // always execute with 'nomagic' + const optmagic_T save_magic_overruled = magic_overruled; + magic_overruled = OPTION_MAGIC_OFF; // always execute with 'nomagic' // Save value of no_hlsearch, jumping to a tag is not a real search const bool save_no_hlsearch = no_hlsearch; diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 0702e89ebb..8bb7d8687f 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1969,6 +1969,35 @@ 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 + func Test_no_last_search_pattern() CheckOption incsearch diff --git a/test/functional/legacy/search_spec.lua b/test/functional/legacy/search_spec.lua index 5a94fca794..3f1f85cf28 100644 --- a/test/functional/legacy/search_spec.lua +++ b/test/functional/legacy/search_spec.lua @@ -14,7 +14,7 @@ describe('search cmdline', function() before_each(function() clear() - command('set nohlsearch') + command('set nohlsearch inccommand=') screen = Screen.new(20, 3) screen:attach() screen:set_default_attr_ids({ @@ -472,8 +472,8 @@ describe('search cmdline', function() funcs.winsaveview()) end) + -- oldtest: Test_search_cmdline4(). it("CTRL-G with 'incsearch' and ? goes in the right direction", function() - -- oldtest: Test_search_cmdline4(). screen:try_resize(40, 4) command('enew!') funcs.setline(1, {' 1 the first', ' 2 the second', ' 3 the third'}) @@ -573,8 +573,8 @@ describe('search cmdline', function() ]]) end) + -- oldtest: Test_incsearch_sort_dump(). it('incsearch works with :sort', function() - -- oldtest: Test_incsearch_sort_dump(). screen:try_resize(20, 4) command('set incsearch hlsearch scrolloff=0') funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'}) @@ -589,8 +589,8 @@ describe('search cmdline', function() feed('') end) + -- oldtest: Test_incsearch_vimgrep_dump(). it('incsearch works with :vimgrep family', function() - -- oldtest: Test_incsearch_vimgrep_dump(). screen:try_resize(30, 4) command('set incsearch hlsearch scrolloff=0') funcs.setline(1, {'another one 2', 'that one 3', 'the one 1'}) @@ -640,6 +640,41 @@ describe('search cmdline', function() ]]) feed('') end) + + -- oldtest: Test_incsearch_substitute_dump2() + it('detects empty pattern properly vim-patch:8.2.2295', function() + screen:try_resize(70, 6) + exec([[ + set incsearch hlsearch scrolloff=0 + for n in range(1, 4) + call setline(n, "foo " . n) + endfor + call setline(5, "abc|def") + 3 + ]]) + + feed([[:%s/\vabc|]]) + screen:expect([[ + foo 1 | + foo 2 | + foo 3 | + foo 4 | + abc|def | + :%s/\vabc|^ | + ]]) + feed('') + + -- The following should not be highlighted + feed([[:1,5s/\v|]]) + screen:expect([[ + foo 1 | + foo 2 | + foo 3 | + foo 4 | + abc|def | + :1,5s/\v|^ | + ]]) + end) end) describe('Search highlight', function() -- cgit From 3f1ee12d311fffe30936217c74ef0352bb7d97d9 Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 4 Dec 2022 09:43:38 +0800 Subject: vim-patch:8.2.3265: smartcase does not work correctly in very magic pattern Problem: Smartcase does not work correctly in very magic pattern. Solution: Take the magicness into account when skipping over regexp items. (Christian Brabandt, closes vim/vim#8682, closes vim/vim#7845) https://github.com/vim/vim/commit/78ba933d18439ff1a02f6be4c571e73ddceb3cd4 Co-authored-by: Christian Brabandt --- src/nvim/search.c | 10 +++++++- src/nvim/testdir/test_search.vim | 54 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 63 insertions(+), 1 deletion(-) diff --git a/src/nvim/search.c b/src/nvim/search.c index 842927e60c..9814822f76 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -373,6 +373,10 @@ bool pat_has_uppercase(char_u *pat) FUNC_ATTR_NONNULL_ALL { char_u *p = pat; + magic_T magic_val = MAGIC_ON; + + // get the magicness of the pattern + (void)skip_regexp_ex((char *)pat, NUL, magic_isset(), NULL, NULL, &magic_val); while (*p != NUL) { const int l = utfc_ptr2len((char *)p); @@ -382,7 +386,7 @@ bool pat_has_uppercase(char_u *pat) return true; } p += l; - } else if (*p == '\\') { + } else if (*p == '\\' && magic_val == MAGIC_ON) { if (p[1] == '_' && p[2] != NUL) { // skip "\_X" p += 3; } else if (p[1] == '%' && p[2] != NUL) { // skip "\%X" @@ -392,6 +396,10 @@ bool pat_has_uppercase(char_u *pat) } else { p += 1; } + } else if ((*p == '%' || *p == '_') && magic_val == MAGIC_ALL) { + if (p[1] != NUL) { // skip "_X" and %X + p += 2; + } } else if (mb_isupper(*p)) { return true; } else { diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 8bb7d8687f..51e7064c94 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1998,6 +1998,60 @@ func Test_incsearch_substitute_dump2() call delete('Xis_subst_script2') endfunc +func Test_pattern_is_uppercase_smartcase() + new + let input=['abc', 'ABC', 'Abc', 'abC'] + call setline(1, input) + call cursor(1,1) + " default, matches firstline + %s/abc//g + call assert_equal(['', 'ABC', 'Abc', 'abC'], + \ getline(1, '$')) + + set smartcase ignorecase + sil %d + call setline(1, input) + call cursor(1,1) + " with smartcase and incsearch set, matches everything + %s/abc//g + call assert_equal(['', '', '', ''], getline(1, '$')) + + sil %d + call setline(1, input) + call cursor(1,1) + " with smartcase and incsearch set and found an uppercase letter, + " match only that. + %s/abC//g + call assert_equal(['abc', 'ABC', 'Abc', ''], + \ getline(1, '$')) + + sil %d + call setline(1, input) + call cursor(1,1) + exe "norm! vG$\" + " \%V should not be detected as uppercase letter + %s/\%Vabc//g + call assert_equal(['', '', '', ''], getline(1, '$')) + + call setline(1, input) + call cursor(1,1) + exe "norm! vG$\" + " \v%V should not be detected as uppercase letter + %s/\v%Vabc//g + call assert_equal(['', '', '', ''], getline(1, '$')) + + call setline(1, input) + call cursor(1,1) + exe "norm! vG$\" + " \v%VabC should be detected as uppercase letter + %s/\v%VabC//g + call assert_equal(['abc', 'ABC', 'Abc', ''], + \ getline(1, '$')) + + set smartcase& ignorecase& + bw! +endfunc + func Test_no_last_search_pattern() CheckOption incsearch -- cgit From 9476dd2f923d9e5d86237836131f67024613308f Mon Sep 17 00:00:00 2001 From: zeertzjq Date: Sun, 4 Dec 2022 09:46:26 +0800 Subject: vim-patch:8.2.3292: underscore in very magic pattern causes a hang Problem: Underscore in very magic pattern causes a hang. Pattern with \V are case sensitive. (Yutao Yuan) Solution: Adjust condition for magicness and advance pointer. (Christian Brabandt, closes vim/vim#8707, closes vim/vim#8704, closes vim/vim#8705) https://github.com/vim/vim/commit/bc67e5a0a494f5fc48e872d747371e31a782d171 Co-authored-by: Christian Brabandt --- src/nvim/search.c | 4 +++- src/nvim/testdir/test_search.vim | 11 +++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/src/nvim/search.c b/src/nvim/search.c index 9814822f76..009e8b4e19 100644 --- a/src/nvim/search.c +++ b/src/nvim/search.c @@ -386,7 +386,7 @@ bool pat_has_uppercase(char_u *pat) return true; } p += l; - } else if (*p == '\\' && magic_val == MAGIC_ON) { + } else if (*p == '\\' && magic_val <= MAGIC_ON) { if (p[1] == '_' && p[2] != NUL) { // skip "\_X" p += 3; } else if (p[1] == '%' && p[2] != NUL) { // skip "\%X" @@ -399,6 +399,8 @@ bool pat_has_uppercase(char_u *pat) } else if ((*p == '%' || *p == '_') && magic_val == MAGIC_ALL) { if (p[1] != NUL) { // skip "_X" and %X p += 2; + } else { + p++; } } else if (mb_isupper(*p)) { return true; diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index 51e7064c94..a9cad3ed44 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -2048,6 +2048,17 @@ func Test_pattern_is_uppercase_smartcase() call assert_equal(['abc', 'ABC', 'Abc', ''], \ getline(1, '$')) + call setline(1, input) + call cursor(1,1) + " \Vabc should match everything + %s/\Vabc//g + call assert_equal(['', '', '', ''], getline(1, '$')) + + call setline(1, input + ['_abc']) + " _ matches normally + %s/\v_.*//g + call assert_equal(['abc', 'ABC', 'Abc', 'abC', ''], getline(1, '$')) + set smartcase& ignorecase& bw! endfunc -- cgit