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:
@@ -409,11 +409,19 @@ CTRL-D List names that match the pattern in front of the cursor.
|
||||
*c_CTRL-N*
|
||||
CTRL-N After using 'wildchar' which got multiple matches, go to next
|
||||
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>*
|
||||
CTRL-P After using 'wildchar' which got multiple matches, go to
|
||||
previous match. Otherwise recall older command-line from
|
||||
history. <S-Tab> only works with the GUI, on the Amiga and
|
||||
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*
|
||||
CTRL-A All names that match the pattern in front of the cursor are
|
||||
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
|
||||
inserted in place of the pattern. If the result is shorter
|
||||
than the pattern, no completion is done.
|
||||
*/_CTRL-L*
|
||||
When 'incsearch' is set, entering a search pattern for "/" or
|
||||
"?" and the current match is displayed then CTRL-L will add
|
||||
one character from the end of the current match. If
|
||||
|
@@ -2110,6 +2110,7 @@ test_arglist \
|
||||
test_regexp_utf8 \
|
||||
test_reltime \
|
||||
test_ruby \
|
||||
test_search \
|
||||
test_searchpos \
|
||||
test_set \
|
||||
test_signs \
|
||||
|
142
src/ex_getln.c
142
src/ex_getln.c
@@ -137,6 +137,9 @@ _RTLENTRYF
|
||||
#endif
|
||||
sort_func_compare(const void *s1, const void *s2);
|
||||
#endif
|
||||
#ifdef FEAT_SEARCH_EXTRA
|
||||
static void set_search_match(pos_T *t);
|
||||
#endif
|
||||
|
||||
/*
|
||||
* getcmdline() - accept a command line starting with firstc.
|
||||
@@ -178,6 +181,9 @@ getcmdline(
|
||||
colnr_T old_curswant;
|
||||
colnr_T old_leftcol;
|
||||
linenr_T old_topline;
|
||||
pos_T cursor_start;
|
||||
pos_T match_start = curwin->w_cursor;
|
||||
pos_T match_end;
|
||||
# ifdef FEAT_DIFF
|
||||
int old_topfill;
|
||||
# endif
|
||||
@@ -223,7 +229,9 @@ getcmdline(
|
||||
|
||||
ccline.overstrike = FALSE; /* always start in insert mode */
|
||||
#ifdef FEAT_SEARCH_EXTRA
|
||||
clearpos(&match_end);
|
||||
old_cursor = curwin->w_cursor; /* needs to be restored later */
|
||||
cursor_start = old_cursor;
|
||||
old_curswant = curwin->w_curswant;
|
||||
old_leftcol = curwin->w_leftcol;
|
||||
old_topline = curwin->w_topline;
|
||||
@@ -996,6 +1004,15 @@ getcmdline(
|
||||
|
||||
/* Truncate at the end, required for multi-byte chars. */
|
||||
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();
|
||||
}
|
||||
else if (ccline.cmdlen == 0 && c != Ctrl_W
|
||||
@@ -1021,6 +1038,10 @@ getcmdline(
|
||||
msg_col = 0;
|
||||
msg_putchar(' '); /* delete ':' */
|
||||
}
|
||||
#ifdef FEAT_SEARCH_EXTRA
|
||||
if (ccline.cmdlen == 0)
|
||||
old_cursor = cursor_start;
|
||||
#endif
|
||||
redraw_cmdline = TRUE;
|
||||
goto returncmd; /* back to cmd mode */
|
||||
}
|
||||
@@ -1104,6 +1125,10 @@ getcmdline(
|
||||
ccline.cmdbuff[i++] = ccline.cmdbuff[j++];
|
||||
/* Truncate at the end, required for multi-byte chars. */
|
||||
ccline.cmdbuff[ccline.cmdlen] = NUL;
|
||||
#ifdef FEAT_SEARCH_EXTRA
|
||||
if (ccline.cmdlen == 0)
|
||||
old_cursor = cursor_start;
|
||||
#endif
|
||||
redrawcmd();
|
||||
goto cmdline_changed;
|
||||
|
||||
@@ -1440,14 +1465,17 @@ getcmdline(
|
||||
if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))
|
||||
{
|
||||
/* Add a character from under the cursor for 'incsearch' */
|
||||
if (did_incsearch
|
||||
&& !equalpos(curwin->w_cursor, old_cursor))
|
||||
if (did_incsearch)
|
||||
{
|
||||
curwin->w_cursor = match_end;
|
||||
if (!equalpos(curwin->w_cursor, old_cursor))
|
||||
{
|
||||
c = gchar_cursor();
|
||||
/* 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))
|
||||
c = MB_TOLOWER(c);
|
||||
if (c != NUL)
|
||||
{
|
||||
@@ -1455,13 +1483,15 @@ getcmdline(
|
||||
p_magic ? "\\^$.*[" : "\\^$"), c)
|
||||
!= NULL)
|
||||
{
|
||||
/* put a backslash before special characters */
|
||||
/* put a backslash before special
|
||||
* characters */
|
||||
stuffcharReadbuff(c);
|
||||
c = '\\';
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
goto cmdline_not_changed;
|
||||
}
|
||||
#endif
|
||||
@@ -1473,7 +1503,75 @@ getcmdline(
|
||||
|
||||
case Ctrl_N: /* next 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,
|
||||
0, firstc != '@') == FAIL)
|
||||
@@ -1821,19 +1919,11 @@ cmdline_changed:
|
||||
{
|
||||
pos_T save_pos = curwin->w_cursor;
|
||||
|
||||
/*
|
||||
* First move cursor to end of match, then to the start. This
|
||||
* 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);
|
||||
}
|
||||
match_start = curwin->w_cursor;
|
||||
set_search_match(&curwin->w_cursor);
|
||||
validate_cursor();
|
||||
end_pos = curwin->w_cursor;
|
||||
match_end = end_pos;
|
||||
curwin->w_cursor = save_pos;
|
||||
}
|
||||
else
|
||||
@@ -1894,6 +1984,8 @@ returncmd:
|
||||
if (did_incsearch)
|
||||
{
|
||||
curwin->w_cursor = old_cursor;
|
||||
if (gotesc)
|
||||
curwin->w_cursor = cursor_start;
|
||||
curwin->w_curswant = old_curswant;
|
||||
curwin->w_leftcol = old_leftcol;
|
||||
curwin->w_topline = old_topline;
|
||||
@@ -6983,3 +7075,21 @@ script_get(exarg_T *eap, char_u *cmd)
|
||||
|
||||
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
|
||||
|
@@ -181,6 +181,7 @@ NEW_TESTS = test_arglist.res \
|
||||
test_perl.res \
|
||||
test_quickfix.res \
|
||||
test_ruby.res \
|
||||
test_search.res \
|
||||
test_signs.res \
|
||||
test_startup.res \
|
||||
test_startup_utf8.res \
|
||||
|
242
src/testdir/test_search.vim
Normal file
242
src/testdir/test_search.vim
Normal 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
|
@@ -763,6 +763,8 @@ static char *(features[]) =
|
||||
|
||||
static int included_patches[] =
|
||||
{ /* Add new patch number below this line */
|
||||
/**/
|
||||
2259,
|
||||
/**/
|
||||
2258,
|
||||
/**/
|
||||
|
Reference in New Issue
Block a user