patch 8.0.1238: incremental search only shows one match

Problem:    Incremental search only shows one match.
Solution:   When 'incsearch' and and 'hlsearch' are both set highlight all
            matches. (haya14busa, closes #2198)
diff --git a/src/ex_getln.c b/src/ex_getln.c
index ba6a403..717a012 100644
--- a/src/ex_getln.c
+++ b/src/ex_getln.c
@@ -1715,8 +1715,9 @@
 		if (p_is && !cmd_silent && (firstc == '/' || firstc == '?'))
 		{
 		    pos_T  t;
-		    int    search_flags = SEARCH_KEEP + SEARCH_NOOF;
+		    int    search_flags = SEARCH_NOOF;
 
+		    save_last_search_pattern();
 		    cursor_off();
 		    out_flush();
 		    if (c == Ctrl_G)
@@ -1726,6 +1727,8 @@
 		    }
 		    else
 			t = match_start;
+		    if (!p_hls)
+			search_flags += SEARCH_KEEP;
 		    ++emsg_off;
 		    i = searchit(curwin, curbuf, &t,
 				 c == Ctrl_G ? FORWARD : BACKWARD,
@@ -1777,6 +1780,7 @@
 # endif
 			old_botline = curwin->w_botline;
 			update_screen(NOT_VALID);
+			restore_last_search_pattern();
 			redrawcmdline();
 		    }
 		    else
@@ -1934,12 +1938,17 @@
 	    }
 	    incsearch_postponed = FALSE;
 	    curwin->w_cursor = search_start;  /* start at old position */
+	    save_last_search_pattern();
 
 	    /* If there is no command line, don't do anything */
 	    if (ccline.cmdlen == 0)
+	    {
 		i = 0;
+		SET_NO_HLSEARCH(TRUE); /* turn off previous highlight */
+	    }
 	    else
 	    {
+		int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
 		cursor_off();		/* so the user knows we're busy */
 		out_flush();
 		++emsg_off;    /* So it doesn't beep if bad expr */
@@ -1947,8 +1956,10 @@
 		/* Set the time limit to half a second. */
 		profile_setlimit(500L, &tm);
 #endif
+		if (!p_hls)
+		    search_flags += SEARCH_KEEP;
 		i = do_search(NULL, firstc, ccline.cmdbuff, count,
-			SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK,
+			search_flags,
 #ifdef FEAT_RELTIME
 			&tm, NULL
 #else
@@ -2005,6 +2016,7 @@
 	    save_cmdline(&save_ccline);
 	    update_screen(SOME_VALID);
 	    restore_cmdline(&save_ccline);
+	    restore_last_search_pattern();
 
 	    /* Leave it at the end to make CTRL-R CTRL-W work. */
 	    if (i != 0)
diff --git a/src/proto/search.pro b/src/proto/search.pro
index 63955ad..41c2006 100644
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -5,6 +5,8 @@
 void save_re_pat(int idx, char_u *pat, int magic);
 void save_search_patterns(void);
 void restore_search_patterns(void);
+void save_last_search_pattern(void);
+void restore_last_search_pattern(void);
 void free_search_patterns(void);
 int ignorecase(char_u *pat);
 int ignorecase_opt(char_u *pat, int ic_in, int scs);
diff --git a/src/search.c b/src/search.c
index c3c5503..e56cb1b 100644
--- a/src/search.c
+++ b/src/search.c
@@ -100,11 +100,14 @@
 #if defined(FEAT_AUTOCMD) || defined(FEAT_EVAL) || defined(PROTO)
 /* copy of spats[], for keeping the search patterns while executing autocmds */
 static struct spat  saved_spats[2];
-static int	    saved_last_idx = 0;
+#endif
 # ifdef FEAT_SEARCH_EXTRA
+/* copy of spats[RE_SEARCH], for keeping the search patterns while incremental
+ * searching */
+static struct spat  saved_last_search_spat;
+static int	    saved_last_idx = 0;
 static int	    saved_no_hlsearch = 0;
 # endif
-#endif
 
 static char_u	    *mr_pattern = NULL;	/* pattern used by search_regcomp() */
 #ifdef FEAT_RIGHTLEFT
@@ -329,9 +332,9 @@
     {
 	vim_free(spats[0].pat);
 	spats[0] = saved_spats[0];
-#if defined(FEAT_EVAL)
+# if defined(FEAT_EVAL)
 	set_vv_searchforward();
-#endif
+# endif
 	vim_free(spats[1].pat);
 	spats[1] = saved_spats[1];
 	last_idx = saved_last_idx;
@@ -360,6 +363,38 @@
 }
 #endif
 
+#ifdef FEAT_SEARCH_EXTRA
+/*
+ * Save and restore the search pattern for incremental highlight search
+ * feature.
+ *
+ * It's similar but differnt from save_search_patterns() and
+ * restore_search_patterns(), because the search pattern must be restored when
+ * cannceling incremental searching even if it's called inside user functions.
+ */
+    void
+save_last_search_pattern(void)
+{
+    saved_last_search_spat = spats[RE_SEARCH];
+    if (spats[RE_SEARCH].pat != NULL)
+	saved_last_search_spat.pat = vim_strsave(spats[RE_SEARCH].pat);
+    saved_last_idx = last_idx;
+    saved_no_hlsearch = no_hlsearch;
+}
+
+    void
+restore_last_search_pattern(void)
+{
+    vim_free(spats[RE_SEARCH].pat);
+    spats[RE_SEARCH] = saved_last_search_spat;
+# if defined(FEAT_EVAL)
+    set_vv_searchforward();
+# endif
+    last_idx = saved_last_idx;
+    SET_NO_HLSEARCH(saved_no_hlsearch);
+}
+#endif
+
 /*
  * Return TRUE when case should be ignored for search pattern "pat".
  * Uses the 'ignorecase' and 'smartcase' options.
diff --git a/src/testdir/test_search.vim b/src/testdir/test_search.vim
index 19e631e..eb78110 100644
--- a/src/testdir/test_search.vim
+++ b/src/testdir/test_search.vim
@@ -1,5 +1,7 @@
 " Test for the search command
 
+source shared.vim
+
 func Test_search_cmdline()
   if !exists('+incsearch')
     return
@@ -431,3 +433,118 @@
   set undolevels&
   enew!
 endfunc
+
+func Test_search_cmdline_incsearch_highlight()
+  if !exists('+incsearch')
+    return
+  endif
+  set incsearch hlsearch
+  " need to disable char_avail,
+  " so that expansion of commandline works
+  call test_override("char_avail", 1)
+  new
+  call setline(1, ['aaa  1 the first', '  2 the second', '  3 the third'])
+
+  1
+  call feedkeys("/second\<cr>", 'tx')
+  call assert_equal('second', @/)
+  call assert_equal('  2 the second', getline('.'))
+
+  " Canceling search won't change @/
+  1
+  let @/ = 'last pattern'
+  call feedkeys("/third\<C-c>", 'tx')
+  call assert_equal('last pattern', @/)
+  call feedkeys("/third\<Esc>", 'tx')
+  call assert_equal('last pattern', @/)
+  call feedkeys("/3\<bs>\<bs>", 'tx')
+  call assert_equal('last pattern', @/)
+  call feedkeys("/third\<c-g>\<c-t>\<Esc>", 'tx')
+  call assert_equal('last pattern', @/)
+
+  " clean up
+  set noincsearch nohlsearch
+  bw!
+endfunc
+
+func Test_search_cmdline_incsearch_highlight_attr()
+  if !exists('+incsearch') || !has('terminal') || has('gui_running')
+    return
+  endif
+  let h = winheight(0)
+  if h < 3
+    return
+  endif
+  let g:buf = term_start([GetVimProg(), '--clean', '-c', 'set noswapfile'], {'term_rows': 3})
+
+  " Prepare buffer text
+  let lines = ['abb vim vim vi', 'vimvivim']
+  call term_sendkeys(g:buf, 'i' . join(lines, "\n") . "\<esc>gg0")
+  call term_wait(g:buf, 200)
+  call assert_equal(lines[0], term_getline(g:buf, 1))
+
+  " Get attr of normal(a0), incsearch(a1), hlsearch(a2) highlight
+  call term_sendkeys(g:buf, ":set incsearch hlsearch\<cr>")
+  call term_sendkeys(g:buf, '/b')
+  call term_wait(g:buf, 200)
+  let screen_line1 = term_scrape(g:buf, 1)
+  call assert_true(len(screen_line1) > 2)
+  " a0: attr_normal
+  let a0 = screen_line1[0].attr
+  " a1: attr_incsearch
+  let a1 = screen_line1[1].attr
+  " a2: attr_hlsearch
+  let a2 = screen_line1[2].attr
+  call assert_notequal(a0, a1)
+  call assert_notequal(a0, a2)
+  call assert_notequal(a1, a2)
+  call term_sendkeys(g:buf, "\<cr>gg0")
+
+  " Test incremental highlight search
+  call term_sendkeys(g:buf, "/vim")
+  call term_wait(g:buf, 200)
+  " Buffer:
+  " abb vim vim vi
+  " vimvivim
+  " Search: /vim
+  let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a2,a2,a2,a0,a0,a0]
+  let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
+  call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
+  call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
+
+  " Test <C-g>
+  call term_sendkeys(g:buf, "\<C-g>\<C-g>")
+  call term_wait(g:buf, 200)
+  let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0]
+  let attr_line2 = [a1,a1,a1,a0,a0,a2,a2,a2]
+  call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
+  call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
+
+  " Test <C-t>
+  call term_sendkeys(g:buf, "\<C-t>")
+  call term_wait(g:buf, 200)
+  let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a1,a1,a1,a0,a0,a0]
+  let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
+  call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
+  call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
+
+  " Type Enter and a1(incsearch highlight) should become a2(hlsearch highlight)
+  call term_sendkeys(g:buf, "\<cr>")
+  call term_wait(g:buf, 200)
+  let attr_line1 = [a0,a0,a0,a0,a2,a2,a2,a0,a2,a2,a2,a0,a0,a0]
+  let attr_line2 = [a2,a2,a2,a0,a0,a2,a2,a2]
+  call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
+  call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
+
+  " Test nohlsearch. a2(hlsearch highlight) should become a0(normal highlight)
+  call term_sendkeys(g:buf, ":1\<cr>")
+  call term_sendkeys(g:buf, ":set nohlsearch\<cr>")
+  call term_sendkeys(g:buf, "/vim")
+  call term_wait(g:buf, 200)
+  let attr_line1 = [a0,a0,a0,a0,a1,a1,a1,a0,a0,a0,a0,a0,a0,a0]
+  let attr_line2 = [a0,a0,a0,a0,a0,a0,a0,a0]
+  call assert_equal(attr_line1, map(term_scrape(g:buf, 1)[:len(attr_line1)-1], 'v:val.attr'))
+  call assert_equal(attr_line2, map(term_scrape(g:buf, 2)[:len(attr_line2)-1], 'v:val.attr'))
+
+  bwipe!
+endfunc
diff --git a/src/version.c b/src/version.c
index ff69454..7d4ba56 100644
--- a/src/version.c
+++ b/src/version.c
@@ -762,6 +762,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    1238,
+/**/
     1237,
 /**/
     1236,