Skip to content

Commit 2e51d9a

Browse files
committed
patch 8.0.1238: incremental search only shows one match
Problem: Incremental search only shows one match. Solution: When 'incsearch' and and 'hlsearch' are both set highlight all matches. (haya14busa, closes #2198)
1 parent af2d20c commit 2e51d9a

File tree

6 files changed

+185
-7
lines changed

6 files changed

+185
-7
lines changed

runtime/doc/options.txt

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4447,7 +4447,17 @@ A jump table for the options with a short description can be found at |Q_op|.
44474447
match may not be found. This is to avoid that Vim hangs while you
44484448
are typing the pattern.
44494449
The highlighting can be set with the 'i' flag in 'highlight'.
4450-
See also: 'hlsearch'.
4450+
When 'hlsearch' is on, all matched strings are highlighted too while typing
4451+
a search command. See also: 'hlsearch'.
4452+
If you don't want turn 'hlsearch' on, but want to highlight all matches
4453+
while searching, you can turn on and off 'hlsearch' with autocmd.
4454+
Example: >
4455+
augroup vimrc-incsearch-highlight
4456+
autocmd!
4457+
autocmd CmdlineEnter [/\?] :set hlsearch
4458+
autocmd CmdlineLeave [/\?] :set nohlsearch
4459+
augroup END
4460+
<
44514461
CTRL-L can be used to add one character from after the current match
44524462
to the command line. If 'ignorecase' and 'smartcase' are set and the
44534463
command line has no uppercase characters, the added character is

src/ex_getln.c

Lines changed: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1715,8 +1715,9 @@ getcmdline(
17151715
if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))
17161716
{
17171717
pos_T t;
1718-
int search_flags = SEARCH_KEEP + SEARCH_NOOF;
1718+
int search_flags = SEARCH_NOOF;
17191719

1720+
save_last_search_pattern();
17201721
cursor_off();
17211722
out_flush();
17221723
if (c == Ctrl_G)
@@ -1726,6 +1727,8 @@ getcmdline(
17261727
}
17271728
else
17281729
t = match_start;
1730+
if (!p_hls)
1731+
search_flags += SEARCH_KEEP;
17291732
++emsg_off;
17301733
i = searchit(curwin, curbuf, &t,
17311734
c == Ctrl_G ? FORWARD : BACKWARD,
@@ -1777,6 +1780,7 @@ getcmdline(
17771780
# endif
17781781
old_botline = curwin->w_botline;
17791782
update_screen(NOT_VALID);
1783+
restore_last_search_pattern();
17801784
redrawcmdline();
17811785
}
17821786
else
@@ -1934,21 +1938,28 @@ getcmdline(
19341938
}
19351939
incsearch_postponed = FALSE;
19361940
curwin->w_cursor = search_start; /* start at old position */
1941+
save_last_search_pattern();
19371942

19381943
/* If there is no command line, don't do anything */
19391944
if (ccline.cmdlen == 0)
1945+
{
19401946
i = 0;
1947+
SET_NO_HLSEARCH(TRUE); /* turn off previous highlight */
1948+
}
19411949
else
19421950
{
1951+
int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
19431952
cursor_off(); /* so the user knows we're busy */
19441953
out_flush();
19451954
++emsg_off; /* So it doesn't beep if bad expr */
19461955
#ifdef FEAT_RELTIME
19471956
/* Set the time limit to half a second. */
19481957
profile_setlimit(500L, &tm);
19491958
#endif
1959+
if (!p_hls)
1960+
search_flags += SEARCH_KEEP;
19501961
i = do_search(NULL, firstc, ccline.cmdbuff, count,
1951-
SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK,
1962+
search_flags,
19521963
#ifdef FEAT_RELTIME
19531964
&tm, NULL
19541965
#else
@@ -2005,6 +2016,7 @@ getcmdline(
20052016
save_cmdline(&save_ccline);
20062017
update_screen(SOME_VALID);
20072018
restore_cmdline(&save_ccline);
2019+
restore_last_search_pattern();
20082020

20092021
/* Leave it at the end to make CTRL-R CTRL-W work. */
20102022
if (i != 0)

src/proto/search.pro

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,8 @@ char_u *reverse_text(char_u *s);
55
void save_re_pat(int idx, char_u *pat, int magic);
66
void save_search_patterns(void);
77
void restore_search_patterns(void);
8+
void save_last_search_pattern(void);
9+
void restore_last_search_pattern(void);
810
void free_search_patterns(void);
911
int ignorecase(char_u *pat);
1012
int ignorecase_opt(char_u *pat, int ic_in, int scs);

src/search.c

Lines changed: 39 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -100,11 +100,14 @@ static int lastc_bytelen = 1; /* >1 for multi-byte char */
100100
#if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
101101
/* copy of spats[], for keeping the search patterns while executing autocmds */
102102
static struct spat saved_spats[2];
103-
static int saved_last_idx = 0;
103+
#endif
104104
# ifdef FEAT_SEARCH_EXTRA
105+
/* copy of spats[RE_SEARCH], for keeping the search patterns while incremental
106+
* searching */
107+
static struct spat saved_last_search_spat;
108+
static int saved_last_idx = 0;
105109
static int saved_no_hlsearch = 0;
106110
# endif
107-
#endif
108111

109112
static char_u *mr_pattern = NULL; /* pattern used by search_regcomp() */
110113
#ifdef FEAT_RIGHTLEFT
@@ -329,9 +332,9 @@ restore_search_patterns(void)
329332
{
330333
vim_free(spats[0].pat);
331334
spats[0] = saved_spats[0];
332-
#if defined(FEAT_EVAL)
335+
# if defined(FEAT_EVAL)
333336
set_vv_searchforward();
334-
#endif
337+
# endif
335338
vim_free(spats[1].pat);
336339
spats[1] = saved_spats[1];
337340
last_idx = saved_last_idx;
@@ -360,6 +363,38 @@ free_search_patterns(void)
360363
}
361364
#endif
362365

366+
#ifdef FEAT_SEARCH_EXTRA
367+
/*
368+
* Save and restore the search pattern for incremental highlight search
369+
* feature.
370+
*
371+
* It's similar but differnt from save_search_patterns() and
372+
* restore_search_patterns(), because the search pattern must be restored when
373+
* cannceling incremental searching even if it's called inside user functions.
374+
*/
375+
void
376+
save_last_search_pattern(void)
377+
{
378+
saved_last_search_spat = spats[RE_SEARCH];
379+
if (spats[RE_SEARCH].pat != NULL)
380+
saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
381+
saved_last_idx = last_idx;
382+
saved_no_hlsearch = no_hlsearch;
383+
}
384+
385+
void
386+
restore_last_search_pattern(void)
387+
{
388+
vim_free(spats[RE_SEARCH].pat);
389+
spats[RE_SEARCH] = saved_last_search_spat;
390+
# if defined(FEAT_EVAL)
391+
set_vv_searchforward();
392+
# endif
393+
last_idx = saved_last_idx;
394+
SET_NO_HLSEARCH(saved_no_hlsearch);
395+
}
396+
#endif
397+
363398
/*
364399
* Return TRUE when case should be ignored for search pattern "pat".
365400
* Uses the 'ignorecase' and 'smartcase' options.

src/testdir/test_search.vim

Lines changed: 117 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
11
" Test for the search command
22

3+
source shared.vim
4+
35
func Test_search_cmdline()
46
if !exists('+incsearch')
57
return
@@ -431,3 +433,118 @@ func Test_search_regexp()
431433
set undolevels&
432434
enew!
433435
endfunc
436+
437+
func Test_search_cmdline_incsearch_highlight()
438+
if !exists('+incsearch')
439+
return
440+
endif
441+
set incsearch hlsearch
442+
" need to disable char_avail,
443+
" so that expansion of commandline works
444+
call test_override("char_avail", 1)
445+
new
446+
call setline(1, ['aaa 1 the first', ' 2 the second', ' 3 the third'])
447+
448+
1
449+
call feedkeys("/second\<cr>", 'tx')
450+
call assert_equal('second', @/)
451+
call assert_equal(' 2 the second', getline('.'))
452+
453+
" Canceling search won't change @/
454+
1
455+
let @/ = 'last pattern'
456+
call feedkeys("/third\<C-c>", 'tx')
457+
call assert_equal('last pattern', @/)
458+
call feedkeys("/third\<Esc>", 'tx')
459+
call assert_equal('last pattern', @/)
460+
call feedkeys("/3\<bs>\<bs>", 'tx')
461+
call assert_equal('last pattern', @/)
462+
call feedkeys("/third\<c-g>\<c-t>\<Esc>", 'tx')
463+
call assert_equal('last pattern', @/)
464+
465+
" clean up
466+
set noincsearch nohlsearch
467+
bw!
468+
endfunc
469+
470+
func Test_search_cmdline_incsearch_highlight_attr()
471+
if !exists('+incsearch') || !has('terminal') || has('gui_running')
472+
return
473+
endif
474+
let h = winheight(0)
475+
if h < 3
476+
return
477+
endif
478+
let g:buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
479+
480+
" Prepare buffer text
481+
let lines = ['abb vim vim vi', 'vimvivim']
482+
call term_sendkeys(g:buf, 'i' . join(lines, "\n") . "\<esc>gg0")
483+
call term_wait(g:buf, 200)
484+
call assert_equal(lines[0], term_getline(g:buf, 1))
485+
486+
" Get attr of normal(a0), incsearch(a1), hlsearch(a2) highlight
487+
call term_sendkeys(g:buf, ":set incsearch hlsearch\<cr>")
488+
call term_sendkeys(g:buf, '/b')
489+
call term_wait(g:buf, 200)
490+
let screen_line1 = term_scrape(g:buf, 1)
491+
call assert_true(len(screen_line1) > 2)
492+
" a0: attr_normal
493+
let a0 = screen_line1[0].attr
494+
" a1: attr_incsearch
495+
let a1 = screen_line1[1].attr
496+
" a2: attr_hlsearch
497+
let a2 = screen_line1[2].attr
498+
call assert_notequal(a0, a1)
499+
call assert_notequal(a0, a2)
500+
call assert_notequal(a1, a2)
501+
call term_sendkeys(g:buf, "\<cr>gg0")
502+
503+
" Test incremental highlight search
504+
call term_sendkeys(g:buf, "/vim")
505+
call term_wait(g:buf, 200)
506+
" Buffer:
507+
" abb vim vim vi
508+
" vimvivim
509+
" Search: /vim
510+
let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a2,a2,a2,a0,a0,a0]
511+
let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
512+
call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
513+
call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
514+
515+
" Test <C-g>
516+
call term_sendkeys(g:buf, "\<C-g>\<C-g>")
517+
call term_wait(g:buf, 200)
518+
let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0]
519+
let attr_line2 = [a1,a1,a1,a0,a0,a2,a2,a2]
520+
call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
521+
call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
522+
523+
" Test <C-t>
524+
call term_sendkeys(g:buf, "\<C-t>")
525+
call term_wait(g:buf, 200)
526+
let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a1,a1,a1,a0,a0,a0]
527+
let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
528+
call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
529+
call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
530+
531+
" Type Enter and a1(incsearch highlight) should become a2(hlsearch highlight)
532+
call term_sendkeys(g:buf, "\<cr>")
533+
call term_wait(g:buf, 200)
534+
let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0]
535+
let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
536+
call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
537+
call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
538+
539+
" Test nohlsearch. a2(hlsearch highlight) should become a0(normal highlight)
540+
call term_sendkeys(g:buf, ":1\<cr>")
541+
call term_sendkeys(g:buf, ":set nohlsearch\<cr>")
542+
call term_sendkeys(g:buf, "/vim")
543+
call term_wait(g:buf, 200)
544+
let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a0,a0,a0,a0,a0,a0]
545+
let attr_line2 = [a0,a0,a0,a0,a0,a0,a0,a0]
546+
call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
547+
call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
548+
549+
bwipe!
550+
endfunc

src/version.c

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

762762
static int included_patches[] =
763763
{ /* Add new patch number below this line */
764+
/**/
765+
1238,
764766
/**/
765767
1237,
766768
/**/

0 commit comments

Comments
 (0)