Skip to content

Commit b0acacd

Browse files
committed
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.
1 parent b31a3ac commit b0acacd

File tree

5 files changed

+180
-20
lines changed

5 files changed

+180
-20
lines changed

src/ex_getln.c

Lines changed: 112 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -264,11 +264,78 @@ set_search_match(pos_T *t)
264264

265265
/*
266266
* Return TRUE when 'incsearch' highlighting is to be done.
267+
* Sets search_first_line and search_last_line to the address range.
267268
*/
268269
static int
269-
do_incsearch_highlighting(int firstc)
270+
do_incsearch_highlighting(int firstc, incsearch_state_T *is_state,
271+
int *skiplen, int *patlen)
270272
{
271-
return p_is && !cmd_silent && (firstc == '/' || firstc == '?');
273+
*skiplen = 0;
274+
*patlen = ccline.cmdlen;
275+
276+
if (p_is && !cmd_silent)
277+
{
278+
// by default search all lines
279+
search_first_line = 0;
280+
search_last_line = MAXLNUM;
281+
282+
if (firstc == '/' || firstc == '?')
283+
return TRUE;
284+
if (firstc == ':')
285+
{
286+
char_u *cmd = skip_range(ccline.cmdbuff, NULL);
287+
char_u *p;
288+
int delim;
289+
char_u *end;
290+
291+
if (*cmd == 's' || *cmd == 'g' || *cmd == 'v')
292+
{
293+
// Skip over "substitute" to find the pattern separator.
294+
for (p = cmd; ASCII_ISALPHA(*p); ++p)
295+
;
296+
if (*p != NUL)
297+
{
298+
delim = *p++;
299+
end = skip_regexp(p, delim, p_magic, NULL);
300+
if (end > p)
301+
{
302+
char_u *dummy;
303+
exarg_T ea;
304+
pos_T save_cursor = curwin->w_cursor;
305+
306+
// found a non-empty pattern
307+
*skiplen = (int)(p - ccline.cmdbuff);
308+
*patlen = (int)(end - p);
309+
310+
// parse the address range
311+
vim_memset(&ea, 0, sizeof(ea));
312+
ea.line1 = 1;
313+
ea.line2 = 1;
314+
ea.cmd = ccline.cmdbuff;
315+
ea.addr_type = ADDR_LINES;
316+
parse_cmd_address(&ea, &dummy);
317+
curwin->w_cursor = is_state->search_start;
318+
if (ea.addr_count > 0)
319+
{
320+
search_first_line = ea.line1;
321+
search_last_line = ea.line2;
322+
}
323+
else if (*cmd == 's')
324+
{
325+
// :s defaults to the current line
326+
search_first_line = curwin->w_cursor.lnum;
327+
search_last_line = curwin->w_cursor.lnum;
328+
}
329+
330+
curwin->w_cursor = save_cursor;
331+
return TRUE;
332+
}
333+
}
334+
}
335+
}
336+
}
337+
338+
return FALSE;
272339
}
273340

274341
/*
@@ -280,14 +347,16 @@ may_do_incsearch_highlighting(
280347
long count,
281348
incsearch_state_T *is_state)
282349
{
350+
int skiplen, patlen;
283351
int i;
284352
pos_T end_pos;
285353
struct cmdline_info save_ccline;
286354
#ifdef FEAT_RELTIME
287355
proftime_T tm;
288356
#endif
357+
int c;
289358

290-
if (!do_incsearch_highlighting(firstc))
359+
if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
291360
return;
292361

293362
// If there is a character waiting, search and redraw later.
@@ -298,12 +367,19 @@ may_do_incsearch_highlighting(
298367
}
299368
is_state->incsearch_postponed = FALSE;
300369

301-
// start at old position
302-
curwin->w_cursor = is_state->search_start;
370+
if (search_first_line == 0)
371+
// start at the original cursor position
372+
curwin->w_cursor = is_state->search_start;
373+
else
374+
{
375+
// start at the first line in the range
376+
curwin->w_cursor.lnum = search_first_line;
377+
curwin->w_cursor.col = 0;
378+
}
303379
save_last_search_pattern();
304380

305381
// If there is no command line, don't do anything.
306-
if (ccline.cmdlen == 0)
382+
if (patlen == 0)
307383
{
308384
i = 0;
309385
set_no_hlsearch(TRUE); // turn off previous highlight
@@ -322,15 +398,24 @@ may_do_incsearch_highlighting(
322398
#endif
323399
if (!p_hls)
324400
search_flags += SEARCH_KEEP;
325-
i = do_search(NULL, firstc, ccline.cmdbuff, count, search_flags,
401+
c = ccline.cmdbuff[skiplen + patlen];
402+
ccline.cmdbuff[skiplen + patlen] = NUL;
403+
i = do_search(NULL, firstc == ':' ? '/' : firstc,
404+
ccline.cmdbuff + skiplen, count, search_flags,
326405
#ifdef FEAT_RELTIME
327406
&tm, NULL
328407
#else
329408
NULL, NULL
330409
#endif
331410
);
411+
ccline.cmdbuff[skiplen + patlen] = c;
332412
--emsg_off;
333413

414+
if (curwin->w_cursor.lnum < search_first_line
415+
|| curwin->w_cursor.lnum > search_last_line)
416+
// match outside of address range
417+
i = 0;
418+
334419
// if interrupted while searching, behave like it failed
335420
if (got_int)
336421
{
@@ -369,8 +454,11 @@ may_do_incsearch_highlighting(
369454

370455
// Disable 'hlsearch' highlighting if the pattern matches everything.
371456
// Avoids a flash when typing "foo\|".
457+
c = ccline.cmdbuff[skiplen + patlen];
458+
ccline.cmdbuff[skiplen + patlen] = NUL;
372459
if (empty_pattern(ccline.cmdbuff))
373460
set_no_hlsearch(TRUE);
461+
ccline.cmdbuff[skiplen + patlen] = c;
374462

375463
validate_cursor();
376464
// May redraw the status line to show the cursor position.
@@ -398,25 +486,27 @@ may_do_incsearch_highlighting(
398486
*/
399487
static int
400488
may_adjust_incsearch_highlighting(
401-
int firstc,
402-
long count,
489+
int firstc,
490+
long count,
403491
incsearch_state_T *is_state,
404-
int c)
492+
int c)
405493
{
494+
int skiplen, patlen;
406495
pos_T t;
407496
char_u *pat;
408497
int search_flags = SEARCH_NOOF;
409498
int i;
499+
int save;
410500

411-
if (!do_incsearch_highlighting(firstc))
501+
if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
412502
return OK;
413-
if (ccline.cmdlen == 0)
503+
if (patlen == 0 && ccline.cmdbuff[skiplen] == NUL)
414504
return FAIL;
415505

416-
if (firstc == ccline.cmdbuff[0])
506+
if (firstc == ccline.cmdbuff[skiplen])
417507
pat = last_search_pattern();
418508
else
419-
pat = ccline.cmdbuff;
509+
pat = ccline.cmdbuff + skiplen;
420510

421511
save_last_search_pattern();
422512
cursor_off();
@@ -435,17 +525,20 @@ may_adjust_incsearch_highlighting(
435525
if (!p_hls)
436526
search_flags += SEARCH_KEEP;
437527
++emsg_off;
528+
save = pat[patlen];
529+
pat[patlen] = NUL;
438530
i = searchit(curwin, curbuf, &t,
439531
c == Ctrl_G ? FORWARD : BACKWARD,
440532
pat, count, search_flags,
441533
RE_SEARCH, 0, NULL, NULL);
442534
--emsg_off;
535+
pat[patlen] = save;
443536
if (i)
444537
{
445538
is_state->search_start = is_state->match_start;
446539
is_state->match_end = t;
447540
is_state->match_start = t;
448-
if (c == Ctrl_T && firstc == '/')
541+
if (c == Ctrl_T && firstc != '?')
449542
{
450543
// Move just before the current match, so that when nv_search
451544
// finishes the cursor will be put back on the match.
@@ -493,7 +586,9 @@ may_adjust_incsearch_highlighting(
493586
static int
494587
may_add_char_to_search(int firstc, int *c, incsearch_state_T *is_state)
495588
{
496-
if (!do_incsearch_highlighting(firstc))
589+
int skiplen, patlen;
590+
591+
if (!do_incsearch_highlighting(firstc, is_state, &skiplen, &patlen))
497592
return FAIL;
498593

499594
// Add a character from under the cursor for 'incsearch'.
@@ -507,7 +602,7 @@ may_add_char_to_search(int firstc, int *c, incsearch_state_T *is_state)
507602
// If 'ignorecase' and 'smartcase' are set and the
508603
// command line has no uppercase characters, convert
509604
// the character to lowercase.
510-
if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff))
605+
if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff + skiplen))
511606
*c = MB_TOLOWER(*c);
512607
if (*c != NUL)
513608
{

src/globals.h

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -345,9 +345,13 @@ EXTERN int t_colors INIT(= 0); /* int value of T_CCO */
345345
* a match within one line), search_match_endcol the column number of the
346346
* character just after the match in the last line.
347347
*/
348-
EXTERN int highlight_match INIT(= FALSE); /* show search match pos */
349-
EXTERN linenr_T search_match_lines; /* lines of of matched string */
350-
EXTERN colnr_T search_match_endcol; /* col nr of match end */
348+
EXTERN int highlight_match INIT(= FALSE); // show search match pos
349+
EXTERN linenr_T search_match_lines; // lines of of matched string
350+
EXTERN colnr_T search_match_endcol; // col nr of match end
351+
#ifdef FEAT_SEARCH_EXTRA
352+
EXTERN linenr_T search_first_line INIT(= 0); // for :{FIRST},{last}s/pat
353+
EXTERN linenr_T search_last_line INIT(= MAXLNUM); // for :{first},{LAST}s/pat
354+
#endif
351355

352356
EXTERN int no_smartcase INIT(= FALSE); /* don't use 'smartcase' once */
353357

src/screen.c

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7892,6 +7892,13 @@ next_search_hl(
78927892
long nmatched;
78937893
int save_called_emsg = called_emsg;
78947894

7895+
// for :{range}s/pat only highlight inside the range
7896+
if (lnum < search_first_line || lnum > search_last_line)
7897+
{
7898+
shl->lnum = 0;
7899+
return;
7900+
}
7901+
78957902
if (shl->lnum != 0)
78967903
{
78977904
/* Check for three situations:

src/testdir/test_search.vim

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -362,6 +362,58 @@ func Test_search_cmdline3()
362362
bw!
363363
endfunc
364364

365+
func Cmdline3_prep()
366+
" need to disable char_avail,
367+
" so that expansion of commandline works
368+
call test_override("char_avail", 1)
369+
new
370+
call setline(1, [' 1', ' 2 the~e', ' 3 the theother'])
371+
set incsearch
372+
endfunc
373+
374+
func Cmdline3_cleanup()
375+
set noincsearch
376+
call test_override("char_avail", 0)
377+
bw!
378+
endfunc
379+
380+
func Test_search_cmdline3s()
381+
if !exists('+incsearch')
382+
return
383+
endif
384+
call Cmdline3_prep()
385+
1
386+
call feedkeys(":%s/the\<c-l>/xxx\<cr>", 'tx')
387+
call assert_equal(' 2 xxxe', getline('.'))
388+
389+
call Cmdline3_cleanup()
390+
endfunc
391+
392+
func Test_search_cmdline3g()
393+
if !exists('+incsearch')
394+
return
395+
endif
396+
call Cmdline3_prep()
397+
1
398+
call feedkeys(":g/the\<c-l>/d\<cr>", 'tx')
399+
call assert_equal(' 3 the theother', getline(2))
400+
401+
call Cmdline3_cleanup()
402+
endfunc
403+
404+
func Test_search_cmdline3v()
405+
if !exists('+incsearch')
406+
return
407+
endif
408+
call Cmdline3_prep()
409+
1
410+
call feedkeys(":v/the\<c-l>/d\<cr>", 'tx')
411+
call assert_equal(1, line('$'))
412+
call assert_equal(' 2 the~e', getline(1))
413+
414+
call Cmdline3_cleanup()
415+
endfunc
416+
365417
func Test_search_cmdline4()
366418
if !exists('+incsearch')
367419
return

src/version.c

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -794,6 +794,8 @@ static char *(features[]) =
794794

795795
static int included_patches[] =
796796
{ /* Add new patch number below this line */
797+
/**/
798+
271,
797799
/**/
798800
270,
799801
/**/

0 commit comments

Comments
 (0)