On Fr, 29 Jul 2016, Yegappan Lakshmanan wrote:

> On Fri, Jul 29, 2016 at 11:03 AM, Christian Brabandt <cbli...@256bit.org> 
> wrote:
> >> On Thu, Jul 28, 2016 at 11:16 PM, Christian Brabandt <cbli...@256bit.org> 
> >> wrote:
> >> > On Do, 28 Jul 2016, Bram Moolenaar wrote:
> >> >
> >> >> I think it should.  Most users will have 'wrapscan' on, since it is the
> >> >> default.  If someone switches it off he must have a reason for it.
> >> >
> >> > okay, fixed with the latest version
> >> >
> >>
> >> I tested the latest patch and the confirmed that the problems I reported 
> >> earlier
> >> are fixed. I saw some new issues. Take the following text:
> >>
> >>   1
> >>   2 these
> >>   3 the
> >>   4 their
> >>   5 there
> >>   6 their
> >>   7 the
> >>   8 them
> >>   9 these
> >>
> >> The cursor is in line 1 and I have 'nowrapscan' set. I search for "the" and
> >> press CTRL-N 7 times and "the" in "these" is highlighted. Now I press
> >> CTRL-L to copy "s" and then erase it. Now if I press CTRL-P, I expect
> >> that the cursor will move to line 8. Instead the cursor moves to line 7.
> >>
> >> Another problem: Place the cursor in line 1. Enter "/thes" and then press
> >> CTRL-N. The "thes" in line 9 is highlighted. Now if you press backspace,
> >> the cursor jumps back to line 3. I expected that the cursor will remain
> >> in line 9.
> >
> > Thanks, will look at these and add some tests.
> >
> >> I think, the CTRL-N and CTRL-P should respect the search direction.
> >> For example, if I search a pattern using "?text", pressing CTRL-N
> >> should search backwards. Currently CTRL-N always searches
> >> forward (irrespective of the search direction). Note that this is
> >> different from how "n" and "N" work.
> >
> > Please don't make me do this. Currently the inconsistent search
> > direction is one of my biggest annoyances of Vim. I really really really
> > hate it, that I can't rely on the fact that N searches backwards and n
> > forward.
> >
> 
> I don't have any preference between the two options. I was just pointing
> out the deviation from the behavior of 'n' and 'N' commands.

I attach an updated patch, which fixes all issues mentioned so far.
Additionally I added a test for this.

I will be away the next 2-3 weeks, so won't have time to work on this 
further. However, I'd appreciate feedback and will work further on it
after I return, if there are more issues.

Oh and before I forget to mention it. I made Ctrl-N always go further 
down and Ctrl-P always go further up and I don't intend to change that.

Best,
Christian

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to vim_dev+unsubscr...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
From f7e06483300fcc632414ad49afb30f4459336d78 Mon Sep 17 00:00:00 2001
From: Christian Brabandt <c...@256bit.org>
Date: Tue, 26 Jul 2016 11:18:51 +0200
Subject: [PATCH] Make Ctrl-N/P jump to next/previous search match

Currently, you cannot move from one match to the next match
when doing a search '/' or '?'.
This patch adds the functionality to use 'Ctrl-N' to move the
cursor to the next match, if 'insearch' is set. Similarily 'Ctrl-P' will
move to the previous match.

Also c_CTRL-N and c_CTRL-P are already used to move within in history of
search patterns, I have for now made them something different in search
mode, when incsearch is set. This is because c_CTRL-L already does
something useful in search mode and second, because Ctrl-N and
Ctrl-P are already used to select next/previous match in completion mode
so it seems logically to also extend their use in search mode.

Bugfixes: - works correctly with Ctrl-P after ? search
          - after clearing the search command line, starts searching
            back at the original position
          - works correctly, when using \? in a forward search / and
            then jumping backwards using Ctrl-P
          - obey to 'wrapscan' setting
          - beep, when no further match is found
          - fix cursor moved when backspacing a character and adding
            another char
          - when wrapping around and finishing the search using <cr>
            make sure, cursor is back at the match
          - add test_search.vim for testing

Updated enhanced search patch
---
 runtime/doc/cmdline.txt     |   9 ++
 src/ex_getln.c              | 163 +++++++++++++++++++++++++++++-------
 src/testdir/Make_all.mak    |   1 +
 src/testdir/test_search.vim | 198 ++++++++++++++++++++++++++++++++++++++++++++
 4 files changed, 343 insertions(+), 28 deletions(-)
 create mode 100644 src/testdir/test_search.vim

diff --git a/runtime/doc/cmdline.txt b/runtime/doc/cmdline.txt
index 8186678..7fe2a34 100644
--- a/runtime/doc/cmdline.txt
+++ b/runtime/doc/cmdline.txt
@@ -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
diff --git a/src/ex_getln.c b/src/ex_getln.c
index 642e090..7ce04b4 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,29 @@ 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 +1501,74 @@ 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;
+
+			if (char_avail())
+			    continue;
+			cursor_off();
+			out_flush();
+			if (c == Ctrl_N)
+			{
+			    t = match_end;
+			    inc(&t);
+			}
+			else
+			{
+			    t = match_start;
+			    dec(&t);
+			}
+			++emsg_off;
+			i = searchit(curwin, curbuf, &t, c == Ctrl_N ? FORWARD : BACKWARD,
+				ccline.cmdbuff, count, SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK + SEARCH_COL,
+				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 +1916,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 +1981,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 +7072,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 2965b70..272077e 100644
--- a/src/testdir/Make_all.mak
+++ b/src/testdir/Make_all.mak
@@ -184,6 +184,7 @@ NEW_TESTS = test_arglist.res \
 	    test_perl.res \
 	    test_quickfix.res \
 	    test_ruby.res \
+	    test_search.res \
 	    test_startup.res \
 	    test_stat.res \
 	    test_syntax.res \
diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim
new file mode 100644
index 0000000..a2b7e1e
--- /dev/null
+++ b/src/testdir/test_search.vim
@@ -0,0 +1,198 @@
+" 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
+
+" vim: shiftwidth=2 sts=2 expandtab ts=2
-- 
2.1.4

Raspunde prin e-mail lui