diff options
author | raichoo <raichoo@googlemail.com> | 2017-03-25 14:09:31 +0100 |
---|---|---|
committer | James McCoy <jamessan@jamessan.com> | 2017-06-26 07:36:36 -0400 |
commit | 7955cf35158f56a207ece32127adece23fd0fba1 (patch) | |
tree | f6ed4f3b7d2006b245302fdf80bb7c81e9dd29c2 | |
parent | 2b377d89dbe4174a74e021ffe4e8b86d0d729d0e (diff) | |
download | rneovim-7955cf35158f56a207ece32127adece23fd0fba1.tar.gz rneovim-7955cf35158f56a207ece32127adece23fd0fba1.tar.bz2 rneovim-7955cf35158f56a207ece32127adece23fd0fba1.zip |
vim-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)
https://github.com/vim/vim/commit/4d6f32cbfbaf324ac4a25c0206a5db0e9f7a48f7
-rw-r--r-- | runtime/doc/cmdline.txt | 9 | ||||
-rw-r--r-- | src/nvim/ex_getln.c | 140 | ||||
-rw-r--r-- | src/nvim/testdir/test_search.vim | 243 | ||||
-rw-r--r-- | src/nvim/version.c | 2 |
4 files changed, 367 insertions, 27 deletions
diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt index 948c921431..ef8f4bc317 100644 --- a/runtime/doc/cmdline.txt +++ b/runtime/doc/cmdline.txt @@ -376,10 +376,18 @@ 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. + */_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. @@ -389,6 +397,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 diff --git a/src/nvim/ex_getln.c b/src/nvim/ex_getln.c index 858374c68e..efa8cd24bc 100644 --- a/src/nvim/ex_getln.c +++ b/src/nvim/ex_getln.c @@ -106,6 +106,9 @@ typedef struct command_line_state { linenr_T old_topline; int old_topfill; linenr_T old_botline; + pos_T cursor_start; + pos_T match_start; + pos_T match_end; int did_incsearch; int incsearch_postponed; int did_wild_list; // did wild_list() recently @@ -167,6 +170,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) s->save_State = State; s->save_p_icm = vim_strsave(p_icm); s->ignore_drag_release = true; + s->match_start = curwin->w_cursor; if (s->firstc == -1) { s->firstc = NUL; @@ -179,7 +183,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) } ccline.overstrike = false; // always start in insert mode + clearpos(&s->match_end); s->old_cursor = curwin->w_cursor; // needs to be restored later + s->cursor_start = s->old_cursor; s->old_curswant = curwin->w_curswant; s->old_leftcol = curwin->w_leftcol; s->old_topline = curwin->w_topline; @@ -283,6 +289,9 @@ static uint8_t *command_line_enter(int firstc, long count, int indent) if (s->did_incsearch) { curwin->w_cursor = s->old_cursor; + if (s->gotesc) { + curwin->w_cursor = s->cursor_start; + } curwin->w_curswant = s->old_curswant; curwin->w_leftcol = s->old_leftcol; curwin->w_topline = s->old_topline; @@ -929,6 +938,12 @@ static int command_line_handle_key(CommandLineState *s) // Truncate at the end, required for multi-byte chars. ccline.cmdbuff[ccline.cmdlen] = NUL; + if (ccline.cmdlen == 0) { + s->old_cursor = s->cursor_start; + } else { + s->old_cursor = s->match_start; + decl(&s->old_cursor); + } redrawcmd(); } else if (ccline.cmdlen == 0 && s->c != Ctrl_W && ccline.cmdprompt == NULL && s->indent == 0) { @@ -1001,6 +1016,9 @@ static int command_line_handle_key(CommandLineState *s) // Truncate at the end, required for multi-byte chars. ccline.cmdbuff[ccline.cmdlen] = NUL; + if (ccline.cmdlen == 0) { + s->old_cursor = s->cursor_start; + } redrawcmd(); return command_line_changed(s); @@ -1230,24 +1248,27 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_L: if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { // Add a character from under the cursor for 'incsearch' - if (s->did_incsearch && !equalpos(curwin->w_cursor, s->old_cursor)) { - s->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)) { - s->c = mb_tolower(s->c); - } - - if (s->c != NUL) { - if (s->c == s->firstc - || vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c) - != NULL) { - // put a backslash before special characters - stuffcharReadbuff(s->c); - s->c = '\\'; + if (s->did_incsearch) { + curwin->w_cursor = s->match_end; + if (!equalpos(curwin->w_cursor, s->old_cursor)) { + s->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)) { + s->c = mb_tolower(s->c); + } + if (s->c != NUL) { + if (s->c == s->firstc + || vim_strchr((char_u *)(p_magic ? "\\^$.*[" : "\\^$"), s->c) + != NULL) { + // put a backslash before special characters + stuffcharReadbuff(s->c); + s->c = '\\'; + } + break; } - break; } } return command_line_not_changed(s); @@ -1261,7 +1282,67 @@ static int command_line_handle_key(CommandLineState *s) case Ctrl_N: // next match case Ctrl_P: // previous match - if (s->xpc.xp_numfiles > 0) { + if (p_is && !cmd_silent && (s->firstc == '/' || s->firstc == '?')) { + pos_T t; + int search_flags = SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK; + + if (char_avail()) { + return 1; + } + ui_busy_start(); + ui_flush(); + if (s->c == Ctrl_N) { + t = s->match_end; + search_flags += SEARCH_COL; + } else { + t = s->match_start; + } + emsg_off++; + s->i = searchit(curwin, curbuf, &t, + s->c == Ctrl_N ? FORWARD : BACKWARD, + ccline.cmdbuff, s->count, search_flags, + RE_SEARCH, 0, NULL); + emsg_off--; + ui_busy_stop(); + if (s->i) { + s->old_cursor = s->match_start; + s->match_end = t; + s->match_start = t; + if (s->c == Ctrl_P && s->firstc == '/') { + // move just before the current match, so that + // when nv_search finishes the cursor will be + // put back on the match + s->old_cursor = t; + (void)decl(&s->old_cursor); + } + if (lt(t, s->old_cursor) && s->c == Ctrl_N) { + // wrap around + s->old_cursor = t; + if (s->firstc == '?') { + (void)incl(&s->old_cursor); + } else { + (void)decl(&s->old_cursor); + } + } + + set_search_match(&s->match_end); + curwin->w_cursor = s->match_start; + changed_cline_bef_curs(); + update_topline(); + validate_cursor(); + highlight_match = true; + s->old_curswant = curwin->w_curswant; + s->old_leftcol = curwin->w_leftcol; + s->old_topline = curwin->w_topline; + s->old_topfill = curwin->w_topfill; + s->old_botline = curwin->w_botline; + update_screen(NOT_VALID); + redrawcmdline(); + } else { + vim_beep(BO_ERROR); + } + return command_line_not_changed(s); + } else if (s->xpc.xp_numfiles > 0) { if (nextwild(&s->xpc, (s->c == Ctrl_P) ? WILD_PREV : WILD_NEXT, 0, s->firstc != '@') == FAIL) { break; @@ -1566,16 +1647,11 @@ static int command_line_changed(CommandLineState *s) if (s->i != 0) { 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); - } + s->match_start = curwin->w_cursor; + set_search_match(&curwin->w_cursor); validate_cursor(); end_pos = curwin->w_cursor; + s->match_end = end_pos; curwin->w_cursor = save_pos; } else { end_pos = curwin->w_cursor; // shutup gcc 4 @@ -5519,3 +5595,15 @@ histentry_T *hist_get_array(const uint8_t history_type, int **const new_hisidx, *new_hisnum = &(hisnum[history_type]); return history[history_type]; } + +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); + } +} diff --git a/src/nvim/testdir/test_search.vim b/src/nvim/testdir/test_search.vim index e85525e663..9c29236ac9 100644 --- a/src/nvim/testdir/test_search.vim +++ b/src/nvim/testdir/test_search.vim @@ -1,5 +1,248 @@ " Test for the search command +func Test_search_cmdline() + throw 'skipped: Nvim does not support test_disable_char_avail()' + 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() + throw 'skipped: Nvim does not support test_disable_char_avail()' + 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 + func Test_use_sub_pat() split let @/ = '' diff --git a/src/nvim/version.c b/src/nvim/version.c index a28dbcf061..dceceb46ad 100644 --- a/src/nvim/version.c +++ b/src/nvim/version.c @@ -185,7 +185,7 @@ static const int included_patches[] = { // 2262 NA // 2261 NA // 2260 NA - // 2259, + 2259, // 2258 NA // 2257 NA 2256, |