diff options
author | Bram Moolenaar <Bram@vim.org> | 2016-08-26 19:13:46 +0200 |
---|---|---|
committer | Bram Moolenaar <Bram@vim.org> | 2016-08-26 19:13:46 +0200 |
commit | 4d6f32cbfbaf324ac4a25c0206a5db0e9f7a48f7 (patch) | |
tree | 1f99127dcddcda19da0a408a32612bc33f968fa5 /src | |
parent | f1f0792e55e72cdc7c833b30f565a9b02f18bb1e (diff) | |
download | vim-4d6f32cbfbaf324ac4a25c0206a5db0e9f7a48f7.zip |
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)
Diffstat (limited to 'src')
-rw-r--r-- | src/Makefile | 1 | ||||
-rw-r--r-- | src/ex_getln.c | 166 | ||||
-rw-r--r-- | src/testdir/Make_all.mak | 1 | ||||
-rw-r--r-- | src/testdir/test_search.vim | 242 | ||||
-rw-r--r-- | src/version.c | 2 |
5 files changed, 384 insertions, 28 deletions
diff --git a/src/Makefile b/src/Makefile index af0ffc08d..dd9d24744 100644 --- a/src/Makefile +++ b/src/Makefile @@ -2110,6 +2110,7 @@ test_arglist \ test_regexp_utf8 \ test_reltime \ test_ruby \ + test_search \ test_searchpos \ test_set \ test_signs \ diff --git a/src/ex_getln.c b/src/ex_getln.c index 642e09010..925e2f490 100644 --- a/src/ex_getln.c +++ b/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,26 +1465,31 @@ 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) { - 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)) - c = MB_TOLOWER(c); - if (c != NUL) + curwin->w_cursor = match_end; + if (!equalpos(curwin->w_cursor, old_cursor)) { - if (c == firstc || vim_strchr((char_u *)( - p_magic ? "\\^$.*[" : "\\^$"), c) - != NULL) + 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)) + c = MB_TOLOWER(c); + if (c != NUL) { - /* put a backslash before special characters */ - stuffcharReadbuff(c); - c = '\\'; + if (c == firstc || vim_strchr((char_u *)( + p_magic ? "\\^$.*[" : "\\^$"), c) + != NULL) + { + /* put a backslash before special + * characters */ + stuffcharReadbuff(c); + c = '\\'; + } + break; } - break; } } goto cmdline_not_changed; @@ -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 diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 3a0742d64..c8d23eafc 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -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 \ diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim new file mode 100644 index 000000000..4f1cfcc8e --- /dev/null +++ b/src/testdir/test_search.vim @@ -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 diff --git a/src/version.c b/src/version.c index 83bf09bc3..c2b66980c 100644 --- a/src/version.c +++ b/src/version.c @@ -764,6 +764,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ /**/ + 2259, +/**/ 2258, /**/ 2257, |