0
0
mirror of https://github.com/vim/vim.git synced 2025-09-26 04:04:07 -04:00

patch 7.4.2259

Problem:    With 'incsearch' can only see the next match.
Solution:   Make CTRL-N/CTRL-P move to the previous/next match. (Christian
            Brabandt)
This commit is contained in:
Bram Moolenaar
2016-08-26 19:13:46 +02:00
parent f1f0792e55
commit 4d6f32cbfb
6 changed files with 393 additions and 28 deletions

View File

@@ -409,11 +409,19 @@ CTRL-D List names that match the pattern in front of the cursor.
*c_CTRL-N* *c_CTRL-N*
CTRL-N After using 'wildchar' which got multiple matches, go to next CTRL-N After using 'wildchar' which got multiple matches, go to next
match. Otherwise recall more recent command-line from history. match. Otherwise recall more recent command-line from history.
*/_CTRL-N*
When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-N will move
to the next match (does not take |search-offset| into account)
<S-Tab> *c_CTRL-P* *c_<S-Tab>* <S-Tab> *c_CTRL-P* *c_<S-Tab>*
CTRL-P After using 'wildchar' which got multiple matches, go to CTRL-P After using 'wildchar' which got multiple matches, go to
previous match. Otherwise recall older command-line from previous match. Otherwise recall older command-line from
history. <S-Tab> only works with the GUI, on the Amiga and history. <S-Tab> only works with the GUI, on the Amiga and
with MS-DOS. with MS-DOS.
*/_CTRL-P*
When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-P will move
to the previous match (does not take |search-offset| into account).
*c_CTRL-A* *c_CTRL-A*
CTRL-A All names that match the pattern in front of the cursor are CTRL-A All names that match the pattern in front of the cursor are
inserted. inserted.
@@ -423,6 +431,7 @@ CTRL-L A match is done on the pattern in front of the cursor. If
If there are multiple matches the longest common part is If there are multiple matches the longest common part is
inserted in place of the pattern. If the result is shorter inserted in place of the pattern. If the result is shorter
than the pattern, no completion is done. than the pattern, no completion is done.
*/_CTRL-L*
When 'incsearch' is set, entering a search pattern for "/" or When 'incsearch' is set, entering a search pattern for "/" or
"?" and the current match is displayed then CTRL-L will add "?" and the current match is displayed then CTRL-L will add
one character from the end of the current match. If one character from the end of the current match. If

View File

@@ -2110,6 +2110,7 @@ test_arglist \
test_regexp_utf8 \ test_regexp_utf8 \
test_reltime \ test_reltime \
test_ruby \ test_ruby \
test_search \
test_searchpos \ test_searchpos \
test_set \ test_set \
test_signs \ test_signs \

View File

@@ -137,6 +137,9 @@ _RTLENTRYF
#endif #endif
sort_func_compare(const void *s1, const void *s2); sort_func_compare(const void *s1, const void *s2);
#endif #endif
#ifdef FEAT_SEARCH_EXTRA
static void set_search_match(pos_T *t);
#endif
/* /*
* getcmdline() - accept a command line starting with firstc. * getcmdline() - accept a command line starting with firstc.
@@ -178,6 +181,9 @@ getcmdline(
colnr_T old_curswant; colnr_T old_curswant;
colnr_T old_leftcol; colnr_T old_leftcol;
linenr_T old_topline; linenr_T old_topline;
pos_T cursor_start;
pos_T match_start = curwin->w_cursor;
pos_T match_end;
# ifdef FEAT_DIFF # ifdef FEAT_DIFF
int old_topfill; int old_topfill;
# endif # endif
@@ -223,7 +229,9 @@ getcmdline(
ccline.overstrike = FALSE; /* always start in insert mode */ ccline.overstrike = FALSE; /* always start in insert mode */
#ifdef FEAT_SEARCH_EXTRA #ifdef FEAT_SEARCH_EXTRA
clearpos(&match_end);
old_cursor = curwin->w_cursor; /* needs to be restored later */ old_cursor = curwin->w_cursor; /* needs to be restored later */
cursor_start = old_cursor;
old_curswant = curwin->w_curswant; old_curswant = curwin->w_curswant;
old_leftcol = curwin->w_leftcol; old_leftcol = curwin->w_leftcol;
old_topline = curwin->w_topline; old_topline = curwin->w_topline;
@@ -996,6 +1004,15 @@ getcmdline(
/* Truncate at the end, required for multi-byte chars. */ /* Truncate at the end, required for multi-byte chars. */
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
#ifdef FEAT_SEARCH_EXTRA
if (ccline.cmdlen == 0)
old_cursor = cursor_start;
else
{
old_cursor = match_start;
decl(&old_cursor);
}
#endif
redrawcmd(); redrawcmd();
} }
else if (ccline.cmdlen == 0 && c != Ctrl_W else if (ccline.cmdlen == 0 && c != Ctrl_W
@@ -1021,6 +1038,10 @@ getcmdline(
msg_col = 0; msg_col = 0;
msg_putchar(' '); /* delete ':' */ msg_putchar(' '); /* delete ':' */
} }
#ifdef FEAT_SEARCH_EXTRA
if (ccline.cmdlen == 0)
old_cursor = cursor_start;
#endif
redraw_cmdline = TRUE; redraw_cmdline = TRUE;
goto returncmd; /* back to cmd mode */ goto returncmd; /* back to cmd mode */
} }
@@ -1104,6 +1125,10 @@ getcmdline(
ccline.cmdbuff[i++] = ccline.cmdbuff[j++]; ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
/* Truncate at the end, required for multi-byte chars. */ /* Truncate at the end, required for multi-byte chars. */
ccline.cmdbuff[ccline.cmdlen] = NUL; ccline.cmdbuff[ccline.cmdlen] = NUL;
#ifdef FEAT_SEARCH_EXTRA
if (ccline.cmdlen == 0)
old_cursor = cursor_start;
#endif
redrawcmd(); redrawcmd();
goto cmdline_changed; goto cmdline_changed;
@@ -1440,26 +1465,31 @@ getcmdline(
if (p_is && !cmd_silent && (firstc == '/' || firstc == '?')) if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))
{ {
/* Add a character from under the cursor for 'incsearch' */ /* Add a character from under the cursor for 'incsearch' */
if (did_incsearch if (did_incsearch)
&& !equalpos(curwin->w_cursor, old_cursor))
{ {
c = gchar_cursor(); curwin->w_cursor = match_end;
/* If 'ignorecase' and 'smartcase' are set and the if (!equalpos(curwin->w_cursor, old_cursor))
* command line has no uppercase characters, convert
* the character to lowercase */
if (p_ic && p_scs && !pat_has_uppercase(ccline.cmdbuff))
c = MB_TOLOWER(c);
if (c != NUL)
{ {
if (c == firstc || vim_strchr((char_u *)( c = gchar_cursor();
p_magic ? "\\^$.*[" : "\\^$"), c) /* If 'ignorecase' and 'smartcase' are set and the
!= NULL) * command line has no uppercase characters, convert
* the character to lowercase */
if (p_ic && p_scs
&& !pat_has_uppercase(ccline.cmdbuff))
c = MB_TOLOWER(c);
if (c != NUL)
{ {
/* put a backslash before special characters */ if (c == firstc || vim_strchr((char_u *)(
stuffcharReadbuff(c); p_magic ? "\\^$.*[" : "\\^$"), c)
c = '\\'; != NULL)
{
/* put a backslash before special
* characters */
stuffcharReadbuff(c);
c = '\\';
}
break;
} }
break;
} }
} }
goto cmdline_not_changed; goto cmdline_not_changed;
@@ -1473,7 +1503,75 @@ getcmdline(
case Ctrl_N: /* next match */ case Ctrl_N: /* next match */
case Ctrl_P: /* previous match */ case Ctrl_P: /* previous match */
if (xpc.xp_numfiles > 0) #ifdef FEAT_SEARCH_EXTRA
if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))
{
pos_T t;
int search_flags = SEARCH_KEEP + SEARCH_NOOF
+ SEARCH_PEEK;
if (char_avail())
continue;
cursor_off();
out_flush();
if (c == Ctrl_N)
{
t = match_end;
search_flags += SEARCH_COL;
}
else
t = match_start;
++emsg_off;
i = searchit(curwin, curbuf, &t,
c == Ctrl_N ? FORWARD : BACKWARD,
ccline.cmdbuff, count, search_flags,
RE_SEARCH, 0, NULL);
--emsg_off;
if (i)
{
old_cursor = match_start;
match_end = t;
match_start = t;
if (c == Ctrl_P && firstc == '/')
{
/* move just before the current match, so that
* when nv_search finishes the cursor will be
* put back on the match */
old_cursor = t;
(void)decl(&old_cursor);
}
if (lt(t, old_cursor) && c == Ctrl_N)
{
/* wrap around */
old_cursor = t;
if (firstc == '?')
(void)incl(&old_cursor);
else
(void)decl(&old_cursor);
}
set_search_match(&match_end);
curwin->w_cursor = match_start;
changed_cline_bef_curs();
update_topline();
validate_cursor();
highlight_match = TRUE;
old_curswant = curwin->w_curswant;
old_leftcol = curwin->w_leftcol;
old_topline = curwin->w_topline;
# ifdef FEAT_DIFF
old_topfill = curwin->w_topfill;
# endif
old_botline = curwin->w_botline;
update_screen(NOT_VALID);
redrawcmdline();
}
else
vim_beep(BO_ERROR);
goto cmdline_not_changed;
}
#endif
else if (xpc.xp_numfiles > 0)
{ {
if (nextwild(&xpc, (c == Ctrl_P) ? WILD_PREV : WILD_NEXT, if (nextwild(&xpc, (c == Ctrl_P) ? WILD_PREV : WILD_NEXT,
0, firstc != '@') == FAIL) 0, firstc != '@') == FAIL)
@@ -1821,19 +1919,11 @@ cmdline_changed:
{ {
pos_T save_pos = curwin->w_cursor; pos_T save_pos = curwin->w_cursor;
/* match_start = curwin->w_cursor;
* First move cursor to end of match, then to the start. This set_search_match(&curwin->w_cursor);
* moves the whole match onto the screen when 'nowrap' is set.
*/
curwin->w_cursor.lnum += search_match_lines;
curwin->w_cursor.col = search_match_endcol;
if (curwin->w_cursor.lnum > curbuf->b_ml.ml_line_count)
{
curwin->w_cursor.lnum = curbuf->b_ml.ml_line_count;
coladvance((colnr_T)MAXCOL);
}
validate_cursor(); validate_cursor();
end_pos = curwin->w_cursor; end_pos = curwin->w_cursor;
match_end = end_pos;
curwin->w_cursor = save_pos; curwin->w_cursor = save_pos;
} }
else else
@@ -1894,6 +1984,8 @@ returncmd:
if (did_incsearch) if (did_incsearch)
{ {
curwin->w_cursor = old_cursor; curwin->w_cursor = old_cursor;
if (gotesc)
curwin->w_cursor = cursor_start;
curwin->w_curswant = old_curswant; curwin->w_curswant = old_curswant;
curwin->w_leftcol = old_leftcol; curwin->w_leftcol = old_leftcol;
curwin->w_topline = old_topline; curwin->w_topline = old_topline;
@@ -6983,3 +7075,21 @@ script_get(exarg_T *eap, char_u *cmd)
return (char_u *)ga.ga_data; return (char_u *)ga.ga_data;
} }
#ifdef FEAT_SEARCH_EXTRA
static void
set_search_match(pos_T *t)
{
/*
* First move cursor to end of match, then to the start. This
* moves the whole match onto the screen when 'nowrap' is set.
*/
t->lnum += search_match_lines;
t->col = search_match_endcol;
if (t->lnum > curbuf->b_ml.ml_line_count)
{
t->lnum = curbuf->b_ml.ml_line_count;
coladvance((colnr_T)MAXCOL);
}
}
#endif

View File

@@ -181,6 +181,7 @@ NEW_TESTS = test_arglist.res \
test_perl.res \ test_perl.res \
test_quickfix.res \ test_quickfix.res \
test_ruby.res \ test_ruby.res \
test_search.res \
test_signs.res \ test_signs.res \
test_startup.res \ test_startup.res \
test_startup_utf8.res \ test_startup_utf8.res \

242
src/testdir/test_search.vim Normal file
View File

@@ -0,0 +1,242 @@
" Test for the search command
func Test_search_cmdline()
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
call test_disable_char_avail(1)
new
call setline(1, [' 1', ' 2 these', ' 3 the', ' 4 their', ' 5 there', ' 6 their', ' 7 the', ' 8 them', ' 9 these', ' 10 foobar'])
" Test 1
" CTRL-N / CTRL-P skips through the previous search history
set noincsearch
:1
call feedkeys("/foobar\<cr>", 'tx')
call feedkeys("/the\<cr>",'tx')
call assert_equal('the', @/)
call feedkeys("/thes\<c-p>\<c-p>\<cr>",'tx')
call assert_equal('foobar', @/)
" Test 2
" Ctrl-N goes from one match to the next
" until the end of the buffer
set incsearch nowrapscan
:1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
:1
" second match
call feedkeys("/the\<c-n>\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
:1
" third match
call feedkeys("/the".repeat("\<c-n>", 2)."\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
:1
" fourth match
call feedkeys("/the".repeat("\<c-n>", 3)."\<cr>", 'tx')
call assert_equal(' 5 there', getline('.'))
:1
" fifth match
call feedkeys("/the".repeat("\<c-n>", 4)."\<cr>", 'tx')
call assert_equal(' 6 their', getline('.'))
:1
" sixth match
call feedkeys("/the".repeat("\<c-n>", 5)."\<cr>", 'tx')
call assert_equal(' 7 the', getline('.'))
:1
" seventh match
call feedkeys("/the".repeat("\<c-n>", 6)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
:1
" eigth match
call feedkeys("/the".repeat("\<c-n>", 7)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
:1
" no further match
call feedkeys("/the".repeat("\<c-n>", 8)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
" Test 3
" Ctrl-N goes from one match to the next
" and continues back at the top
set incsearch wrapscan
:1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
:1
" second match
call feedkeys("/the\<c-n>\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
:1
" third match
call feedkeys("/the".repeat("\<c-n>", 2)."\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
:1
" fourth match
call feedkeys("/the".repeat("\<c-n>", 3)."\<cr>", 'tx')
call assert_equal(' 5 there', getline('.'))
:1
" fifth match
call feedkeys("/the".repeat("\<c-n>", 4)."\<cr>", 'tx')
call assert_equal(' 6 their', getline('.'))
:1
" sixth match
call feedkeys("/the".repeat("\<c-n>", 5)."\<cr>", 'tx')
call assert_equal(' 7 the', getline('.'))
:1
" seventh match
call feedkeys("/the".repeat("\<c-n>", 6)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
:1
" eigth match
call feedkeys("/the".repeat("\<c-n>", 7)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
:1
" back at first match
call feedkeys("/the".repeat("\<c-n>", 8)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" Test 4
" CTRL-P goes to the previous match
set incsearch nowrapscan
$
" first match
call feedkeys("?the\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" first match
call feedkeys("?the\<c-n>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" second match
call feedkeys("?the".repeat("\<c-p>", 1)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<c-p>", 7)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<c-p>", 8)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" Test 5
" CTRL-P goes to the previous match
set incsearch wrapscan
$
" first match
call feedkeys("?the\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
$
" first match at the top
call feedkeys("?the\<c-n>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" second match
call feedkeys("?the".repeat("\<c-p>", 1)."\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
$
" last match
call feedkeys("?the".repeat("\<c-p>", 7)."\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
$
" back at the bottom of the buffer
call feedkeys("?the".repeat("\<c-p>", 8)."\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
" Test 6
" CTRL-L adds to the search pattern
set incsearch wrapscan
1
" first match
call feedkeys("/the\<c-l>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" go to next match of 'thes'
call feedkeys("/the\<c-l>\<c-n>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
1
" wrap around
call feedkeys("/the\<c-l>\<c-n>\<c-n>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" wrap around
set nowrapscan
call feedkeys("/the\<c-l>\<c-n>\<c-n>\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
" Test 7
" <bs> remove from match, but stay at current match
set incsearch wrapscan
1
" first match
call feedkeys("/thei\<cr>", 'tx')
call assert_equal(' 4 their', getline('.'))
1
" delete one char, add another
call feedkeys("/thei\<bs>s\<cr>", 'tx')
call assert_equal(' 9 these', getline('.'))
1
" delete one char, add another, go to previous match, add one char
call feedkeys("/thei\<bs>s\<bs>\<c-p>\<c-l>\<cr>", 'tx')
call assert_equal(' 8 them', getline('.'))
1
" delete all chars, start from the beginning again
call feedkeys("/them". repeat("\<bs>",4).'the\>'."\<cr>", 'tx')
call assert_equal(' 3 the', getline('.'))
" clean up
call test_disable_char_avail(0)
bw!
endfunc
func Test_search_cmdline2()
if !exists('+incsearch')
return
endif
" need to disable char_avail,
" so that expansion of commandline works
call test_disable_char_avail(1)
new
call setline(1, [' 1', ' 2 these', ' 3 the theother'])
" Test 1
" Ctrl-P goes correctly back and forth
set incsearch
1
" first match
call feedkeys("/the\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
1
" go to next match (on next line)
call feedkeys("/the\<c-n>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to next match (still on line 3)
call feedkeys("/the\<c-n>\<c-n>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to next match (still on line 3)
call feedkeys("/the\<c-n>\<c-n>\<c-n>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 3)
call feedkeys("/the\<c-n>\<c-n>\<c-n>\<c-p>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 3)
call feedkeys("/the\<c-n>\<c-n>\<c-n>\<c-p>\<c-p>\<cr>", 'tx')
call assert_equal(' 3 the theother', getline('.'))
1
" go to previous match (on line 2)
call feedkeys("/the\<c-n>\<c-n>\<c-n>\<c-p>\<c-p>\<c-p>\<cr>", 'tx')
call assert_equal(' 2 these', getline('.'))
" clean up
call test_disable_char_avail(0)
bw!
endfunc

View File

@@ -763,6 +763,8 @@ static char *(features[]) =
static int included_patches[] = static int included_patches[] =
{ /* Add new patch number below this line */ { /* Add new patch number below this line */
/**/
2259,
/**/ /**/
2258, 2258,
/**/ /**/