To: vim_dev@googlegroups.com Subject: Patch 7.4.2259 Fcc: outbox From: Bram Moolenaar Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ------------ 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) Files: runtime/doc/cmdline.txt, src/ex_getln.c, src/testdir/Make_all.mak, src/testdir/test_search.vim, src/Makefile *** ../vim-7.4.2258/runtime/doc/cmdline.txt 2015-09-08 18:46:04.337233673 +0200 --- runtime/doc/cmdline.txt 2016-08-26 18:56:26.060363506 +0200 *************** *** 404,414 **** --- 409,427 ---- *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) *c_CTRL-P* *c_* CTRL-P After using 'wildchar' which got multiple matches, go to previous match. Otherwise recall older command-line from history. 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. *** ../vim-7.4.2258/src/ex_getln.c 2016-07-10 22:11:11.866751401 +0200 --- src/ex_getln.c 2016-08-26 19:03:54.320578262 +0200 *************** *** 137,142 **** --- 137,145 ---- #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,183 **** --- 181,189 ---- 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,229 **** --- 229,237 ---- 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,1001 **** --- 1004,1018 ---- /* 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,1026 **** --- 1038,1047 ---- 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,1109 **** --- 1125,1134 ---- 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,1465 **** 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)) { ! 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) { ! if (c == firstc || vim_strchr((char_u *)( ! p_magic ? "\\^$.*[" : "\\^$"), c) ! != NULL) { ! /* put a backslash before special characters */ ! stuffcharReadbuff(c); ! c = '\\'; } - break; } } goto cmdline_not_changed; --- 1465,1495 ---- if (p_is && !cmd_silent && (firstc == '/' || firstc == '?')) { /* Add a character from under the cursor for 'incsearch' */ ! 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)) ! c = MB_TOLOWER(c); ! if (c != NUL) { ! if (c == firstc || vim_strchr((char_u *)( ! p_magic ? "\\^$.*[" : "\\^$"), c) ! != NULL) ! { ! /* put a backslash before special ! * characters */ ! stuffcharReadbuff(c); ! c = '\\'; ! } ! break; } } } goto cmdline_not_changed; *************** *** 1473,1479 **** case Ctrl_N: /* next match */ case Ctrl_P: /* previous match */ ! if (xpc.xp_numfiles > 0) { if (nextwild(&xpc, (c == Ctrl_P) ? WILD_PREV : WILD_NEXT, 0, firstc != '@') == FAIL) --- 1503,1577 ---- case Ctrl_N: /* next match */ case Ctrl_P: /* previous match */ ! #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,1839 **** { 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); ! } validate_cursor(); end_pos = curwin->w_cursor; curwin->w_cursor = save_pos; } else --- 1919,1929 ---- { pos_T save_pos = curwin->w_cursor; ! 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,1899 **** --- 1984,1991 ---- 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,6985 **** --- 7075,7095 ---- 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 *** ../vim-7.4.2258/src/testdir/Make_all.mak 2016-08-20 16:56:48.254624304 +0200 --- src/testdir/Make_all.mak 2016-08-26 18:56:26.064363472 +0200 *************** *** 181,186 **** --- 181,187 ---- test_perl.res \ test_quickfix.res \ test_ruby.res \ + test_search.res \ test_signs.res \ test_startup.res \ test_startup_utf8.res \ *** ../vim-7.4.2258/src/testdir/test_search.vim 2016-08-26 19:12:15.700304803 +0200 --- src/testdir/test_search.vim 2016-08-26 18:56:26.064363472 +0200 *************** *** 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\", 'tx') + call feedkeys("/the\",'tx') + call assert_equal('the', @/) + call feedkeys("/thes\\\",'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\", 'tx') + call assert_equal(' 2 these', getline('.')) + :1 + " second match + call feedkeys("/the\\", 'tx') + call assert_equal(' 3 the', getline('.')) + :1 + " third match + call feedkeys("/the".repeat("\", 2)."\", 'tx') + call assert_equal(' 4 their', getline('.')) + :1 + " fourth match + call feedkeys("/the".repeat("\", 3)."\", 'tx') + call assert_equal(' 5 there', getline('.')) + :1 + " fifth match + call feedkeys("/the".repeat("\", 4)."\", 'tx') + call assert_equal(' 6 their', getline('.')) + :1 + " sixth match + call feedkeys("/the".repeat("\", 5)."\", 'tx') + call assert_equal(' 7 the', getline('.')) + :1 + " seventh match + call feedkeys("/the".repeat("\", 6)."\", 'tx') + call assert_equal(' 8 them', getline('.')) + :1 + " eigth match + call feedkeys("/the".repeat("\", 7)."\", 'tx') + call assert_equal(' 9 these', getline('.')) + :1 + " no further match + call feedkeys("/the".repeat("\", 8)."\", '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\", 'tx') + call assert_equal(' 2 these', getline('.')) + :1 + " second match + call feedkeys("/the\\", 'tx') + call assert_equal(' 3 the', getline('.')) + :1 + " third match + call feedkeys("/the".repeat("\", 2)."\", 'tx') + call assert_equal(' 4 their', getline('.')) + :1 + " fourth match + call feedkeys("/the".repeat("\", 3)."\", 'tx') + call assert_equal(' 5 there', getline('.')) + :1 + " fifth match + call feedkeys("/the".repeat("\", 4)."\", 'tx') + call assert_equal(' 6 their', getline('.')) + :1 + " sixth match + call feedkeys("/the".repeat("\", 5)."\", 'tx') + call assert_equal(' 7 the', getline('.')) + :1 + " seventh match + call feedkeys("/the".repeat("\", 6)."\", 'tx') + call assert_equal(' 8 them', getline('.')) + :1 + " eigth match + call feedkeys("/the".repeat("\", 7)."\", 'tx') + call assert_equal(' 9 these', getline('.')) + :1 + " back at first match + call feedkeys("/the".repeat("\", 8)."\", 'tx') + call assert_equal(' 2 these', getline('.')) + + " Test 4 + " CTRL-P goes to the previous match + set incsearch nowrapscan + $ + " first match + call feedkeys("?the\", 'tx') + call assert_equal(' 9 these', getline('.')) + $ + " first match + call feedkeys("?the\\", 'tx') + call assert_equal(' 9 these', getline('.')) + $ + " second match + call feedkeys("?the".repeat("\", 1)."\", 'tx') + call assert_equal(' 8 them', getline('.')) + $ + " last match + call feedkeys("?the".repeat("\", 7)."\", 'tx') + call assert_equal(' 2 these', getline('.')) + $ + " last match + call feedkeys("?the".repeat("\", 8)."\", 'tx') + call assert_equal(' 2 these', getline('.')) + + " Test 5 + " CTRL-P goes to the previous match + set incsearch wrapscan + $ + " first match + call feedkeys("?the\", 'tx') + call assert_equal(' 9 these', getline('.')) + $ + " first match at the top + call feedkeys("?the\\", 'tx') + call assert_equal(' 2 these', getline('.')) + $ + " second match + call feedkeys("?the".repeat("\", 1)."\", 'tx') + call assert_equal(' 8 them', getline('.')) + $ + " last match + call feedkeys("?the".repeat("\", 7)."\", 'tx') + call assert_equal(' 2 these', getline('.')) + $ + " back at the bottom of the buffer + call feedkeys("?the".repeat("\", 8)."\", '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\\", 'tx') + call assert_equal(' 2 these', getline('.')) + 1 + " go to next match of 'thes' + call feedkeys("/the\\\", 'tx') + call assert_equal(' 9 these', getline('.')) + 1 + " wrap around + call feedkeys("/the\\\\", 'tx') + call assert_equal(' 2 these', getline('.')) + 1 + " wrap around + set nowrapscan + call feedkeys("/the\\\\", 'tx') + call assert_equal(' 9 these', getline('.')) + + " Test 7 + " remove from match, but stay at current match + set incsearch wrapscan + 1 + " first match + call feedkeys("/thei\", 'tx') + call assert_equal(' 4 their', getline('.')) + 1 + " delete one char, add another + call feedkeys("/thei\s\", 'tx') + call assert_equal(' 9 these', getline('.')) + 1 + " delete one char, add another, go to previous match, add one char + call feedkeys("/thei\s\\\\", 'tx') + call assert_equal(' 8 them', getline('.')) + 1 + " delete all chars, start from the beginning again + call feedkeys("/them". repeat("\",4).'the\>'."\", '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\", 'tx') + call assert_equal(' 2 these', getline('.')) + 1 + " go to next match (on next line) + call feedkeys("/the\\", 'tx') + call assert_equal(' 3 the theother', getline('.')) + 1 + " go to next match (still on line 3) + call feedkeys("/the\\\", 'tx') + call assert_equal(' 3 the theother', getline('.')) + 1 + " go to next match (still on line 3) + call feedkeys("/the\\\\", 'tx') + call assert_equal(' 3 the theother', getline('.')) + 1 + " go to previous match (on line 3) + call feedkeys("/the\\\\\", 'tx') + call assert_equal(' 3 the theother', getline('.')) + 1 + " go to previous match (on line 3) + call feedkeys("/the\\\\\\", 'tx') + call assert_equal(' 3 the theother', getline('.')) + 1 + " go to previous match (on line 2) + call feedkeys("/the\\\\\\\", 'tx') + call assert_equal(' 2 these', getline('.')) + + " clean up + call test_disable_char_avail(0) + bw! + endfunc *** ../vim-7.4.2258/src/Makefile 2016-08-23 23:50:06.872279942 +0200 --- src/Makefile 2016-08-26 18:59:40.214724763 +0200 *************** *** 2110,2115 **** --- 2120,2126 ---- test_regexp_utf8 \ test_reltime \ test_ruby \ + test_search \ test_searchpos \ test_set \ test_signs \ *** ../vim-7.4.2258/src/version.c 2016-08-26 17:58:33.590124381 +0200 --- src/version.c 2016-08-26 18:57:46.763682480 +0200 *************** *** 765,766 **** --- 765,768 ---- { /* Add new patch number below this line */ + /**/ + 2259, /**/ -- From "know your smileys": y:-) Bad toupee /// Bram Moolenaar -- Bram@Moolenaar.net -- http://www.Moolenaar.net \\\ /// sponsor Vim, vote for features -- http://www.Vim.org/sponsor/ \\\ \\\ an exciting new programming language -- http://www.Zimbu.org /// \\\ help me help AIDS victims -- http://ICCF-Holland.org ///