aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorzeertzjq <zeertzjq@outlook.com>2022-12-04 09:09:00 +0800
committerzeertzjq <zeertzjq@outlook.com>2022-12-04 10:07:05 +0800
commit46e4be0fd0002233bde613295607ce5eeb498567 (patch)
tree4e12bbbb51cca63f4f745c6061139768a381380b
parenta91ba088abf7b21f640f671ad7f211c0957b4765 (diff)
downloadrneovim-46e4be0fd0002233bde613295607ce5eeb498567.tar.gz
rneovim-46e4be0fd0002233bde613295607ce5eeb498567.tar.bz2
rneovim-46e4be0fd0002233bde613295607ce5eeb498567.zip
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
-rw-r--r--src/nvim/ex_cmds.c4
-rw-r--r--src/nvim/ex_docmd.c8
-rw-r--r--src/nvim/ex_getln.c48
-rw-r--r--src/nvim/globals.h6
-rw-r--r--src/nvim/option.c6
-rw-r--r--src/nvim/regexp.c17
-rw-r--r--src/nvim/regexp_defs.h18
-rw-r--r--src/nvim/search.c2
-rw-r--r--src/nvim/tag.c4
-rw-r--r--src/nvim/testdir/test_search.vim29
-rw-r--r--test/functional/legacy/search_spec.lua43
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, "\<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
+
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('<esc>')
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('<esc>')
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('<Esc>')
+
+ -- 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()