]> granicus.if.org Git - vim/commitdiff
patch 7.4.2259 v7.4.2259
authorBram Moolenaar <Bram@vim.org>
Fri, 26 Aug 2016 17:13:46 +0000 (19:13 +0200)
committerBram Moolenaar <Bram@vim.org>
Fri, 26 Aug 2016 17:13:46 +0000 (19:13 +0200)
Problem:    With 'incsearch' can only see the next match.
Solution:   Make CTRL-N/CTRL-P move to the previous/next match. (Christian
            Brabandt)

runtime/doc/cmdline.txt
src/Makefile
src/ex_getln.c
src/testdir/Make_all.mak
src/testdir/test_search.vim [new file with mode: 0644]
src/version.c

index 818667876c74e3262149372beb169c65f8c210ae..7fe2a3442ea5befe95938bdb8120e037a9b91f36 100644 (file)
@@ -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
index af0ffc08db9167328393b4a47e954cd283c406ef..dd9d24744c6928f28139e8a8bab398608f3daf8f 100644 (file)
@@ -2110,6 +2110,7 @@ test_arglist \
        test_regexp_utf8 \
        test_reltime \
        test_ruby \
+       test_search \
        test_searchpos \
        test_set \
        test_signs \
index 642e090107bc682bcacf85206551ea9a49abfbd1..925e2f4909b423c9a543a88817ff1156fa7ffb31 100644 (file)
@@ -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
index 3a0742d64c81c4d135faa1e36ec157ddb009cc6c..c8d23eafc62e931c8ba5f86508308d65fc4c52e1 100644 (file)
@@ -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 (file)
index 0000000..4f1cfcc
--- /dev/null
@@ -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
index 83bf09bc380278bcd062fecb623c70fcc8d9eee3..c2b66980c53ab1521c0333165fb24318e6293cd8 100644 (file)
@@ -763,6 +763,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    2259,
 /**/
     2258,
 /**/