Merge #7463 'incsearch + hlsearch highlight all'

This commit is contained in:
Justin M. Keyes
2018-02-01 23:25:55 +01:00
5 changed files with 230 additions and 18 deletions

View File

@@ -3299,7 +3299,17 @@ A jump table for the options with a short description can be found at |Q_op|.
pattern and/or a lot of text the match may not be found. This is to pattern and/or a lot of text the match may not be found. This is to
avoid that Vim hangs while you are typing the pattern. avoid that Vim hangs while you are typing the pattern.
The |hl-IncSearch| highlight group determines the highlighting. The |hl-IncSearch| highlight group determines the highlighting.
See also: 'hlsearch'. When 'hlsearch' is on, all matched strings are highlighted too while typing
a search command. See also: 'hlsearch'.
If you don't want turn 'hlsearch' on, but want to highlight all matches
while searching, you can turn on and off 'hlsearch' with autocmd.
Example: >
augroup vimrc-incsearch-highlight
autocmd!
autocmd CmdlineEnter /,\? :set hlsearch
autocmd CmdlineLeave /,\? :set nohlsearch
augroup END
<
CTRL-L can be used to add one character from after the current match CTRL-L can be used to add one character from after the current match
to the command line. If 'ignorecase' and 'smartcase' are set and the to the command line. If 'ignorecase' and 'smartcase' are set and the
command line has no uppercase characters, the added character is command line has no uppercase characters, the added character is

View File

@@ -426,7 +426,7 @@ static uint8_t *command_line_enter(int firstc, long count, int indent)
curwin->w_botline = s->old_botline; curwin->w_botline = s->old_botline;
highlight_match = false; highlight_match = false;
validate_cursor(); // needed for TAB validate_cursor(); // needed for TAB
redraw_later(SOME_VALID); redraw_all_later(SOME_VALID);
} }
if (ccline.cmdbuff != NULL) { if (ccline.cmdbuff != NULL) {
@@ -1019,17 +1019,36 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
ui_flush(); ui_flush();
pos_T t; pos_T t;
int search_flags = SEARCH_KEEP + SEARCH_NOOF + SEARCH_PEEK; char_u *pat;
int search_flags = SEARCH_NOOF;
if (s->firstc == ccline.cmdbuff[0]) {
pat = last_search_pattern();
} else {
pat = ccline.cmdbuff;
}
save_last_search_pattern();
if (next_match) { if (next_match) {
t = s->match_end; t = s->match_end;
if (lt(s->match_start, s->match_end)) {
// start searching at the end of the match
// not at the beginning of the next column
(void)decl(&t);
}
search_flags += SEARCH_COL; search_flags += SEARCH_COL;
} else { } else {
t = s->match_start; t = s->match_start;
} }
if (!p_hls) {
search_flags += SEARCH_KEEP;
}
emsg_off++; emsg_off++;
s->i = searchit(curwin, curbuf, &t, s->i = searchit(curwin, curbuf, &t,
next_match ? FORWARD : BACKWARD, next_match ? FORWARD : BACKWARD,
ccline.cmdbuff, s->count, search_flags, pat, s->count, search_flags,
RE_SEARCH, 0, NULL); RE_SEARCH, 0, NULL);
emsg_off--; emsg_off--;
ui_busy_stop(); ui_busy_stop();
@@ -1070,6 +1089,7 @@ static void command_line_next_incsearch(CommandLineState *s, bool next_match)
} else { } else {
vim_beep(BO_ERROR); vim_beep(BO_ERROR);
} }
restore_last_search_pattern();
return; return;
} }
@@ -1656,7 +1676,9 @@ static int command_line_handle_key(CommandLineState *s)
if (char_avail()) { if (char_avail()) {
return 1; return 1;
} }
command_line_next_incsearch(s, s->c == Ctrl_G); if (ccline.cmdlen != 0) {
command_line_next_incsearch(s, s->c == Ctrl_G);
}
return command_line_not_changed(s); return command_line_not_changed(s);
} }
break; break;
@@ -1759,6 +1781,20 @@ static int command_line_not_changed(CommandLineState *s)
return command_line_changed(s); return command_line_changed(s);
} }
/// Guess that the pattern matches everything. Only finds specific cases, such
/// as a trailing \|, which can happen while typing a pattern.
static int empty_pattern(char_u *p)
{
int n = STRLEN(p);
// remove trailing \v and the like
while (n >= 2 && p[n - 2] == '\\'
&& vim_strchr((char_u *)"mMvVcCZ", p[n - 1]) != NULL) {
n -= 2;
}
return n == 0 || (n >= 2 && p[n - 2] == '\\' && p[n - 1] == '|');
}
static int command_line_changed(CommandLineState *s) static int command_line_changed(CommandLineState *s)
{ {
// 'incsearch' highlighting. // 'incsearch' highlighting.
@@ -1773,20 +1809,27 @@ static int command_line_changed(CommandLineState *s)
} }
s->incsearch_postponed = false; s->incsearch_postponed = false;
curwin->w_cursor = s->search_start; // start at old position curwin->w_cursor = s->search_start; // start at old position
save_last_search_pattern();
// If there is no command line, don't do anything // If there is no command line, don't do anything
if (ccline.cmdlen == 0) { if (ccline.cmdlen == 0) {
s->i = 0; s->i = 0;
SET_NO_HLSEARCH(true); // turn off previous highlight
redraw_all_later(SOME_VALID);
} else { } else {
int search_flags = SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK;
ui_busy_start(); ui_busy_start();
ui_flush(); ui_flush();
++emsg_off; // So it doesn't beep if bad expr ++emsg_off; // So it doesn't beep if bad expr
// Set the time limit to half a second. // Set the time limit to half a second.
tm = profile_setlimit(500L); tm = profile_setlimit(500L);
if (!p_hls) {
search_flags += SEARCH_KEEP;
}
s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count, s->i = do_search(NULL, s->firstc, ccline.cmdbuff, s->count,
SEARCH_KEEP + SEARCH_OPT + SEARCH_NOOF + SEARCH_PEEK, search_flags,
&tm); &tm);
--emsg_off; emsg_off--;
// if interrupted while searching, behave like it failed // if interrupted while searching, behave like it failed
if (got_int) { if (got_int) {
(void)vpeekc(); // remove <C-C> from input stream (void)vpeekc(); // remove <C-C> from input stream
@@ -1827,6 +1870,12 @@ static int command_line_changed(CommandLineState *s)
end_pos = curwin->w_cursor; // shutup gcc 4 end_pos = curwin->w_cursor; // shutup gcc 4
} }
// Disable 'hlsearch' highlighting if the pattern matches
// everything. Avoids a flash when typing "foo\|".
if (empty_pattern(ccline.cmdbuff)) {
SET_NO_HLSEARCH(true);
}
validate_cursor(); validate_cursor();
// May redraw the status line to show the cursor position. // May redraw the status line to show the cursor position.
if (p_ru && curwin->w_status_height > 0) { if (p_ru && curwin->w_status_height > 0) {
@@ -1836,6 +1885,7 @@ static int command_line_changed(CommandLineState *s)
save_cmdline(&s->save_ccline); save_cmdline(&s->save_ccline);
update_screen(SOME_VALID); update_screen(SOME_VALID);
restore_cmdline(&s->save_ccline); restore_cmdline(&s->save_ccline);
restore_last_search_pattern();
// Leave it at the end to make CTRL-R CTRL-W work. // Leave it at the end to make CTRL-R CTRL-W work.
if (s->i != 0) { if (s->i != 0) {

View File

@@ -96,6 +96,9 @@ static int lastc_bytelen = 1; /* >1 for multi-byte char */
/* copy of spats[], for keeping the search patterns while executing autocmds */ /* copy of spats[], for keeping the search patterns while executing autocmds */
static struct spat saved_spats[2]; static struct spat saved_spats[2];
// 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_last_idx = 0;
static int saved_no_hlsearch = 0; static int saved_no_hlsearch = 0;
@@ -305,6 +308,36 @@ void free_search_patterns(void)
#endif #endif
/// Save and restore the search pattern for incremental highlight search
/// feature.
///
/// It's similar but different from save_search_patterns() and
/// restore_search_patterns(), because the search pattern must be restored when
/// cancelling 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)
{
xfree(spats[RE_SEARCH].pat);
spats[RE_SEARCH] = saved_last_search_spat;
set_vv_searchforward();
last_idx = saved_last_idx;
SET_NO_HLSEARCH(saved_no_hlsearch);
}
char_u *last_search_pattern(void)
{
return spats[RE_SEARCH].pat;
}
/* /*
* Return TRUE when case should be ignored for search pattern "pat". * Return TRUE when case should be ignored for search pattern "pat".
* Uses the 'ignorecase' and 'smartcase' options. * Uses the 'ignorecase' and 'smartcase' options.

View File

@@ -101,10 +101,10 @@ static const int included_patches[] = {
// 1399, // 1399,
// 1398, // 1398,
// 1397, // 1397,
// 1396, 1396,
// 1395, // 1395,
// 1394, // 1394,
// 1393, 1393,
// 1392, // 1392,
// 1391, // 1391,
// 1390, // 1390,
@@ -193,7 +193,7 @@ static const int included_patches[] = {
// 1307, // 1307,
// 1306, // 1306,
// 1305, // 1305,
// 1304, 1304,
// 1303, // 1303,
// 1302, // 1302,
// 1301, // 1301,
@@ -247,7 +247,7 @@ static const int included_patches[] = {
// 1253, // 1253,
// 1252, // 1252,
// 1251, // 1251,
// 1250, 1250,
// 1249, // 1249,
// 1248, // 1248,
// 1247, // 1247,
@@ -259,7 +259,7 @@ static const int included_patches[] = {
// 1241, // 1241,
// 1240, // 1240,
// 1239, // 1239,
// 1238, 1238,
// 1237, // 1237,
// 1236, // 1236,
// 1235, // 1235,

View File

@@ -2,6 +2,8 @@ local helpers = require('test.functional.helpers')(after_each)
local Screen = require('test.functional.ui.screen') local Screen = require('test.functional.ui.screen')
local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert local clear, feed, insert = helpers.clear, helpers.feed, helpers.insert
local feed_command = helpers.feed_command local feed_command = helpers.feed_command
local eq = helpers.eq
local eval = helpers.eval
describe('search highlighting', function() describe('search highlighting', function()
local screen local screen
@@ -99,7 +101,30 @@ describe('search highlighting', function()
feed("gg/li") feed("gg/li")
screen:expect([[ screen:expect([[
the first {3:li}ne | the first {3:li}ne |
in a little file | in a {2:li}ttle file |
|
{1:~ }|
{1:~ }|
{1:~ }|
/li^ |
]])
-- check that consecutive matches are caught by C-g/C-t
feed("<C-g>")
screen:expect([[
the first {2:li}ne |
in a {3:li}ttle file |
|
{1:~ }|
{1:~ }|
{1:~ }|
/li^ |
]])
feed("<C-t>")
screen:expect([[
the first {3:li}ne |
in a {2:li}ttle file |
| |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
@@ -132,7 +157,7 @@ describe('search highlighting', function()
feed("/fir") feed("/fir")
screen:expect([[ screen:expect([[
the {3:fir}st line | the {3:fir}st line |
in a {2:lit}tle file | in a little file |
| |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
@@ -144,13 +169,107 @@ describe('search highlighting', function()
feed("<esc>/ttle") feed("<esc>/ttle")
screen:expect([[ screen:expect([[
the first line | the first line |
in a {2:li}{3:ttle} file | in a li{3:ttle} file |
| |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
/ttle^ | /ttle^ |
]]) ]])
-- cancelling search resets to the old search term
feed('<esc>')
screen:expect([[
the first line |
in a {2:^lit}tle file |
|
{1:~ }|
{1:~ }|
{1:~ }|
|
]])
eq('lit', eval('@/'))
-- cancelling inc search restores the hl state
feed(':noh<cr>')
screen:expect([[
the first line |
in a ^little file |
|
{1:~ }|
{1:~ }|
{1:~ }|
:noh |
]])
feed('/first')
screen:expect([[
the {3:first} line |
in a little file |
|
{1:~ }|
{1:~ }|
{1:~ }|
/first^ |
]])
feed('<esc>')
screen:expect([[
the first line |
in a ^little file |
|
{1:~ }|
{1:~ }|
{1:~ }|
|
]])
-- test that pressing C-g in an empty command line does not move the cursor
feed('/<C-g>')
screen:expect([[
the first line |
in a little file |
|
{1:~ }|
{1:~ }|
{1:~ }|
/^ |
]])
-- same, for C-t
feed('<ESC>/<C-t>')
screen:expect([[
the first line |
in a little file |
|
{1:~ }|
{1:~ }|
{1:~ }|
/^ |
]])
-- 8.0.1304, test that C-g and C-t works with incsearch and empty pattern
feed('<esc>/fi<CR>')
feed('//')
screen:expect([[
the {3:fi}rst line |
in a little {2:fi}le |
|
{1:~ }|
{1:~ }|
{1:~ }|
//^ |
]])
feed('<C-g>')
screen:expect([[
the {2:fi}rst line |
in a little {3:fi}le |
|
{1:~ }|
{1:~ }|
{1:~ }|
//^ |
]])
end) end)
it('works with incsearch and offset', function() it('works with incsearch and offset', function()
@@ -163,7 +282,7 @@ describe('search highlighting', function()
feed("gg/mat/e") feed("gg/mat/e")
screen:expect([[ screen:expect([[
not the {3:mat}ch you're looking for | not the {3:mat}ch you're looking for |
the match is here | the {2:mat}ch is here |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|
@@ -174,7 +293,7 @@ describe('search highlighting', function()
-- Search with count and /e offset fixed in Vim patch 7.4.532. -- Search with count and /e offset fixed in Vim patch 7.4.532.
feed("<esc>2/mat/e") feed("<esc>2/mat/e")
screen:expect([[ screen:expect([[
not the match you're looking for | not the {2:mat}ch you're looking for |
the {3:mat}ch is here | the {3:mat}ch is here |
{1:~ }| {1:~ }|
{1:~ }| {1:~ }|