changeset 14515:3648e74dd523 v8.1.0271

patch 8.1.0271: 'incsearch' doesn't work for :s, :g or :v commit https://github.com/vim/vim/commit/b0acacd767a2b0618a7f3c08087708f4329580d0 Author: Bram Moolenaar <Bram@vim.org> Date: Sat Aug 11 16:40:43 2018 +0200 patch 8.1.0271: 'incsearch' doesn't work for :s, :g or :v Problem: 'incsearch' doesn't work for :s, :g or :v. Solution: Also use 'incsearch' for other commands that use a pattern.
author Christian Brabandt <cb@256bit.org>
date Sat, 11 Aug 2018 16:45:05 +0200
parents 7ac24a4dc6ea
children 1e3bfb9cee4c
files src/ex_getln.c src/globals.h src/screen.c src/testdir/test_search.vim src/version.c
diffstat 5 files changed, 182 insertions(+), 22 deletions(-) [+]
line wrap: on
line diff
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -264,11 +264,78 @@ set_search_match(pos_T *t)
 
 /*
  * Return TRUE when 'incsearch' highlighting is to be done.
+ * Sets search_first_line and search_last_line to the address range.
  */
     static int
-do_incsearch_highlighting(int firstc)
+do_incsearch_highlighting(int firstc, incsearch_state_T *is_state,
+						     int *skiplen, int *patlen)
 {
-    return p_is && !cmd_silent && (firstc == '/' || firstc == '?');
+    *skiplen = 0;
+    *patlen = ccline.cmdlen;
+
+    if (p_is && !cmd_silent)
+    {
+	// by default search all lines
+	search_first_line = 0;
+	search_last_line = MAXLNUM;
+
+	if (firstc == '/' || firstc == '?')
+	    return TRUE;
+	if (firstc == ':')
+	{
+	    char_u *cmd = skip_range(ccline.cmdbuff, NULL);
+	    char_u *p;
+	    int	    delim;
+	    char_u *end;
+
+	    if (*cmd == 's' || *cmd == 'g' || *cmd == 'v')
+	    {
+		// Skip over "substitute" to find the pattern separator.
+		for (p = cmd; ASCII_ISALPHA(*p); ++p)
+		    ;
+		if (*p != NUL)
+		{
+		    delim = *p++;
+		    end = skip_regexp(p, delim, p_magic, NULL);
+		    if (end > p)
+		    {
+			char_u  *dummy;
+			exarg_T ea;
+			pos_T	save_cursor = curwin->w_cursor;
+
+			// found a non-empty pattern
+			*skiplen = (int)(p - ccline.cmdbuff);
+			*patlen = (int)(end - p);
+
+			// parse the address range
+			vim_memset(&ea, 0, sizeof(ea));
+			ea.line1 = 1;
+			ea.line2 = 1;
+			ea.cmd = ccline.cmdbuff;
+			ea.addr_type = ADDR_LINES;
+			parse_cmd_address(&ea, &dummy);
+			curwin->w_cursor = is_state->search_start;
+			if (ea.addr_count > 0)
+			{
+			    search_first_line = ea.line1;
+			    search_last_line = ea.line2;
+			}
+			else if (*cmd == 's')
+			{
+			    // :s defaults to the current line
+			    search_first_line = curwin->w_cursor.lnum;
+			    search_last_line = curwin->w_cursor.lnum;
+			}
+
+			curwin->w_cursor = save_cursor;
+			return TRUE;
+		    }
+		}
+	    }
+	}
+    }
+
+    return FALSE;
 }
 
 /*
@@ -280,14 +347,16 @@ may_do_incsearch_highlighting(
 	long		    count,
 	incsearch_state_T   *is_state)
 {
+    int		skiplen, patlen;
     int		i;
     pos_T	end_pos;
     struct cmdline_info	save_ccline;
 #ifdef FEAT_RELTIME
     proftime_T	tm;
 #endif
-
-    if (!do_incsearch_highlighting(firstc))
+    int		c;
+
+    if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
 	return;
 
     // If there is a character waiting, search and redraw later.
@@ -298,12 +367,19 @@ may_do_incsearch_highlighting(
     }
     is_state->incsearch_postponed = FALSE;
 
-    // start at old position
-    curwin->w_cursor = is_state->search_start;
+    if (search_first_line == 0)
+	// start at the original cursor position
+	curwin->w_cursor = is_state->search_start;
+    else
+    {
+	// start at the first line in the range
+	curwin->w_cursor.lnum = search_first_line;
+	curwin->w_cursor.col = 0;
+    }
     save_last_search_pattern();
 
     // If there is no command line, don't do anything.
-    if (ccline.cmdlen == 0)
+    if (patlen == 0)
     {
 	i = 0;
 	set_no_hlsearch(TRUE); // turn off previous highlight
@@ -322,15 +398,24 @@ may_do_incsearch_highlighting(
 #endif
 	if (!p_hls)
 	    search_flags += SEARCH_KEEP;
-	i = do_search(NULL, firstc, ccline.cmdbuff, count, search_flags,
+	c = ccline.cmdbuff[skiplen + patlen];
+	ccline.cmdbuff[skiplen + patlen] = NUL;
+	i = do_search(NULL, firstc == ':' ? '/' : firstc,
+				 ccline.cmdbuff + skiplen, count, search_flags,
 #ifdef FEAT_RELTIME
 		&tm, NULL
 #else
 		NULL, NULL
 #endif
 		);
+	ccline.cmdbuff[skiplen + patlen] = c;
 	--emsg_off;
 
+	if (curwin->w_cursor.lnum < search_first_line
+		|| curwin->w_cursor.lnum > search_last_line)
+	    // match outside of address range
+	    i = 0;
+
 	// if interrupted while searching, behave like it failed
 	if (got_int)
 	{
@@ -369,8 +454,11 @@ may_do_incsearch_highlighting(
 
     // Disable 'hlsearch' highlighting if the pattern matches everything.
     // Avoids a flash when typing "foo\|".
+    c = ccline.cmdbuff[skiplen + patlen];
+    ccline.cmdbuff[skiplen + patlen] = NUL;
     if (empty_pattern(ccline.cmdbuff))
 	set_no_hlsearch(TRUE);
+    ccline.cmdbuff[skiplen + patlen] = c;
 
     validate_cursor();
     // May redraw the status line to show the cursor position.
@@ -398,25 +486,27 @@ may_do_incsearch_highlighting(
  */
     static int
 may_adjust_incsearch_highlighting(
-	int		firstc,
-	long		count,
+	int			firstc,
+	long			count,
 	incsearch_state_T	*is_state,
-	int		c)
+	int			c)
 {
+    int	    skiplen, patlen;
     pos_T   t;
     char_u  *pat;
     int	    search_flags = SEARCH_NOOF;
     int	    i;
-
-    if (!do_incsearch_highlighting(firstc))
+    int	    save;
+
+    if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
 	return OK;
-    if (ccline.cmdlen == 0)
+    if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL)
 	return FAIL;
 
-    if (firstc == ccline.cmdbuff[0])
+    if (firstc == ccline.cmdbuff[skiplen])
 	pat = last_search_pattern();
     else
-	pat = ccline.cmdbuff;
+	pat = ccline.cmdbuff + skiplen;
 
     save_last_search_pattern();
     cursor_off();
@@ -435,17 +525,20 @@ may_adjust_incsearch_highlighting(
     if (!p_hls)
 	search_flags += SEARCH_KEEP;
     ++emsg_off;
+    save = pat[patlen];
+    pat[patlen] = NUL;
     i = searchit(curwin, curbuf, &t,
 		 c == Ctrl_G ? FORWARD : BACKWARD,
 		 pat, count, search_flags,
 		 RE_SEARCH, 0, NULL, NULL);
     --emsg_off;
+    pat[patlen] = save;
     if (i)
     {
 	is_state->search_start = is_state->match_start;
 	is_state->match_end = t;
 	is_state->match_start = t;
-	if (c == Ctrl_T && firstc == '/')
+	if (c == Ctrl_T && firstc != '?')
 	{
 	    // Move just before the current match, so that when nv_search
 	    // finishes the cursor will be put back on the match.
@@ -493,7 +586,9 @@ may_adjust_incsearch_highlighting(
     static int
 may_add_char_to_search(int firstc, int *c, incsearch_state_T *is_state)
 {
-    if (!do_incsearch_highlighting(firstc))
+    int		skiplen, patlen;
+
+    if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
 	return FAIL;
 
     // Add a character from under the cursor for 'incsearch'.
@@ -507,7 +602,7 @@ may_add_char_to_search(int firstc, int *
 	    // If 'ignorecase' and 'smartcase' are set and the
 	    // command line has no uppercase characters, convert
 	    // the character to lowercase.
-	    if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff))
+	    if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff + skiplen))
 		*c = MB_TOLOWER(*c);
 	    if (*c != NUL)
 	    {
--- a/src/globals.h
+++ b/src/globals.h
@@ -345,9 +345,13 @@ EXTERN int	t_colors INIT(= 0);	    /* in
  * a match within one line), search_match_endcol the column number of the
  * character just after the match in the last line.
  */
-EXTERN int	highlight_match INIT(= FALSE);	/* show search match pos */
-EXTERN linenr_T	search_match_lines;		/* lines of of matched string */
-EXTERN colnr_T	search_match_endcol;		/* col nr of match end */
+EXTERN int	highlight_match INIT(= FALSE);	// show search match pos
+EXTERN linenr_T	search_match_lines;		// lines of of matched string
+EXTERN colnr_T	search_match_endcol;		// col nr of match end
+#ifdef FEAT_SEARCH_EXTRA
+EXTERN linenr_T	search_first_line INIT(= 0);	  // for :{FIRST},{last}s/pat
+EXTERN linenr_T	search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat
+#endif
 
 EXTERN int	no_smartcase INIT(= FALSE);	/* don't use 'smartcase' once */
 
--- a/src/screen.c
+++ b/src/screen.c
@@ -7892,6 +7892,13 @@ next_search_hl(
     long	nmatched;
     int		save_called_emsg = called_emsg;
 
+    // for :{range}s/pat only highlight inside the range
+    if (lnum < search_first_line || lnum > search_last_line)
+    {
+	shl->lnum = 0;
+	return;
+    }
+
     if (shl->lnum != 0)
     {
 	/* Check for three situations:
--- a/src/testdir/test_search.vim
+++ b/src/testdir/test_search.vim
@@ -362,6 +362,58 @@ func Test_search_cmdline3()
   bw!
 endfunc
 
+func Cmdline3_prep()
+  " need to disable char_avail,
+  " so that expansion of commandline works
+  call test_override("char_avail", 1)
+  new
+  call setline(1, ['  1', '  2 the~e', '  3 the theother'])
+  set incsearch
+endfunc
+
+func Cmdline3_cleanup()
+  set noincsearch
+  call test_override("char_avail", 0)
+  bw!
+endfunc
+
+func Test_search_cmdline3s()
+  if !exists('+incsearch')
+    return
+  endif
+  call Cmdline3_prep()
+  1
+  call feedkeys(":%s/the\<c-l>/xxx\<cr>", 'tx')
+  call assert_equal('  2 xxxe', getline('.'))
+
+  call Cmdline3_cleanup()
+endfunc
+
+func Test_search_cmdline3g()
+  if !exists('+incsearch')
+    return
+  endif
+  call Cmdline3_prep()
+  1
+  call feedkeys(":g/the\<c-l>/d\<cr>", 'tx')
+  call assert_equal('  3 the theother', getline(2))
+
+  call Cmdline3_cleanup()
+endfunc
+
+func Test_search_cmdline3v()
+  if !exists('+incsearch')
+    return
+  endif
+  call Cmdline3_prep()
+  1
+  call feedkeys(":v/the\<c-l>/d\<cr>", 'tx')
+  call assert_equal(1, line('$'))
+  call assert_equal('  2 the~e', getline(1))
+
+  call Cmdline3_cleanup()
+endfunc
+
 func Test_search_cmdline4()
   if !exists('+incsearch')
     return
--- a/src/version.c
+++ b/src/version.c
@@ -795,6 +795,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    271,
+/**/
     270,
 /**/
     269,