From b16afe4d556af7c3e86b311cfffd1c68a5eed71f Mon Sep 17 00:00:00 2001 From: Famiu Haque Date: Mon, 11 Apr 2022 22:20:24 +0600 Subject: [PATCH] feat(highlight): implement CurSearch highlight Adds a `CurSearch` highlight group to highlight the current search result under the cursor. --- runtime/doc/options.txt | 8 +++-- runtime/doc/syntax.txt | 3 ++ runtime/doc/vim_diff.txt | 2 ++ src/nvim/highlight_defs.h | 2 ++ src/nvim/highlight_group.c | 2 +- src/nvim/match.c | 12 ++++++- src/nvim/move.c | 16 +++++---- test/functional/ui/cursor_spec.lua | 4 +-- test/functional/ui/searchhl_spec.lua | 50 ++++++++++++++++++++++++++++ 9 files changed, 87 insertions(+), 12 deletions(-) diff --git a/runtime/doc/options.txt b/runtime/doc/options.txt index 7ea6a92a34..1966a344b2 100644 --- a/runtime/doc/options.txt +++ b/runtime/doc/options.txt @@ -3176,8 +3176,12 @@ A jump table for the options with a short description can be found at |Q_op|. 'hlsearch' 'hls' boolean (default on) global When there is a previous search pattern, highlight all its matches. - The |hl-Search| highlight group determines the highlighting. Note that - only the matching text is highlighted, any offsets are not applied. + The |hl-Search| highlight group determines the highlighting for all + matches not under the cursor while the |hl-CurSearch| highlight group + (if defined) determines the highlighting for the match under the + cursor. If |hl-CurSearch| is not defined, then |hl-Search| is used for + both. Note that only the matching text is highlighted, any offsets + are not applied. See also: 'incsearch' and |:match|. When you get bored looking at the highlighted matches, you can turn it off with |:nohlsearch|. This does not change the option value, as diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index 6875f43b86..e9d75002d3 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5090,6 +5090,9 @@ ColorColumn used for the columns set with 'colorcolumn' *hl-Conceal* Conceal placeholder characters substituted for concealed text (see 'conceallevel') + *hl-CurSearch* +CurSearch used for highlighting a search pattern under the cursor + (see 'hlsearch') *hl-Cursor* Cursor character under the cursor lCursor the character under the cursor when |language-mapping| diff --git a/runtime/doc/vim_diff.txt b/runtime/doc/vim_diff.txt index 59f085977b..f5cd24b734 100644 --- a/runtime/doc/vim_diff.txt +++ b/runtime/doc/vim_diff.txt @@ -351,6 +351,8 @@ Functions: Highlight groups: |hl-ColorColumn|, |hl-CursorColumn| are lower priority than most other groups + |hl-CurSearch| highlights match under cursor instead of last match found + using |n| or |N| |hl-CursorLine| is low-priority unless foreground color is set *hl-VertSplit* superseded by |hl-WinSeparator| diff --git a/src/nvim/highlight_defs.h b/src/nvim/highlight_defs.h index e0ee649013..0515842b61 100644 --- a/src/nvim/highlight_defs.h +++ b/src/nvim/highlight_defs.h @@ -63,6 +63,7 @@ typedef enum { HLF_E, // error messages HLF_I, // incremental search HLF_L, // last search string + HLF_LC, // current search match HLF_M, // "--More--" message HLF_CM, // Mode (e.g., "-- INSERT --") HLF_N, // line number for ":number" and ":#" commands @@ -123,6 +124,7 @@ EXTERN const char *hlf_names[] INIT(= { [HLF_E] = "ErrorMsg", [HLF_I] = "IncSearch", [HLF_L] = "Search", + [HLF_LC] = "CurSearch", [HLF_M] = "MoreMsg", [HLF_CM] = "ModeMsg", [HLF_N] = "LineNr", diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index 3092aaefab..a9ced84280 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -1927,7 +1927,7 @@ void highlight_changed(void) } highlight_attr[hlf] = hl_get_ui_attr(hlf, final_id, - hlf == HLF_INACTIVE); + (hlf == HLF_INACTIVE || hlf == HLF_LC)); if (highlight_attr[hlf] != highlight_attr_last[hlf]) { if (hlf == HLF_MSG) { diff --git a/src/nvim/match.c b/src/nvim/match.c index af89319a09..4129e84fc2 100644 --- a/src/nvim/match.c +++ b/src/nvim/match.c @@ -4,6 +4,7 @@ // match.c: functions for highlighting matches #include +#include "nvim/buffer_defs.h" #include "nvim/charset.h" #include "nvim/fold.h" #include "nvim/highlight_group.h" @@ -667,7 +668,16 @@ int update_search_hl(win_T *wp, linenr_T lnum, colnr_T col, char_u **line, match if (shl->endcol < next_col) { shl->endcol = next_col; } - shl->attr_cur = shl->attr; + // Use "CurSearch" highlight for current search match + if (shl == search_hl + && (HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) + && wp->w_cursor.lnum == lnum + && wp->w_cursor.col >= shl->startcol + && wp->w_cursor.col < shl->endcol) { + shl->attr_cur = win_hl_attr(wp, HLF_LC) ? win_hl_attr(wp, HLF_LC) : HL_ATTR(HLF_LC); + } else { + shl->attr_cur = shl->attr; + } // Match with the "Conceal" group results in hiding // the match. if (cur != NULL diff --git a/src/nvim/move.c b/src/nvim/move.c index eda3298101..c55a9a296b 100644 --- a/src/nvim/move.c +++ b/src/nvim/move.c @@ -95,27 +95,31 @@ static void comp_botline(win_T *wp) win_check_anchored_floats(wp); } -/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set. +/// Redraw when w_cline_row changes and 'relativenumber' or 'cursorline' is set +/// or if the 'CurSearch' highlight is defined. /// Also when concealing is on and 'concealcursor' is not active. void redraw_for_cursorline(win_T *wp) FUNC_ATTR_NONNULL_ALL { if ((wp->w_valid & VALID_CROW) == 0 && !pum_visible() - && (wp->w_p_rnu || win_cursorline_standout(wp))) { - // win_line() will redraw the number column and cursorline only. + && (wp->w_p_rnu || win_cursorline_standout(wp) + || HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC])) { + // win_line() will redraw the number column and cursorline only + // and also update the CurSearch highlight (if needed). redraw_later(wp, VALID); } } /// Redraw when w_virtcol changes and 'cursorcolumn' is set or 'cursorlineopt' -/// contains "screenline". +/// contains "screenline" or when the 'CurSearch' highlight is defined. /// Also when concealing is on and 'concealcursor' is active. static void redraw_for_cursorcolumn(win_T *wp) FUNC_ATTR_NONNULL_ALL { if ((wp->w_valid & VALID_VIRTCOL) == 0 && !pum_visible()) { - if (wp->w_p_cuc) { - // When 'cursorcolumn' is set need to redraw with SOME_VALID. + if (wp->w_p_cuc || HL_ATTR(HLF_LC) || wp->w_hl_ids[HLF_LC]) { + // When 'cursorcolumn' is set or 'CurSearch' is defined + // need to redraw with SOME_VALID. redraw_later(wp, SOME_VALID); } else if (wp->w_p_cul && (wp->w_p_culopt_flags & CULOPT_SCRLINE)) { // When 'cursorlineopt' contains "screenline" need to redraw with VALID. diff --git a/test/functional/ui/cursor_spec.lua b/test/functional/ui/cursor_spec.lua index 4c51547e2c..92300a8fa2 100644 --- a/test/functional/ui/cursor_spec.lua +++ b/test/functional/ui/cursor_spec.lua @@ -212,10 +212,10 @@ describe('ui/cursor', function() if m.blinkwait then m.blinkwait = 700 end end if m.hl_id then - m.hl_id = 61 + m.hl_id = 62 m.attr = {background = Screen.colors.DarkGray} end - if m.id_lm then m.id_lm = 62 end + if m.id_lm then m.id_lm = 63 end end -- Assert the new expectation. diff --git a/test/functional/ui/searchhl_spec.lua b/test/functional/ui/searchhl_spec.lua index 2e60930127..068a77a2e4 100644 --- a/test/functional/ui/searchhl_spec.lua +++ b/test/functional/ui/searchhl_spec.lua @@ -109,6 +109,56 @@ describe('search highlighting', function() ]]) end) + it('works for match under cursor', function() + screen:set_default_attr_ids({ + [1] = {background = Screen.colors.Yellow}, + [2] = {foreground = Screen.colors.Gray100, background = Screen.colors.Gray0}, + [3] = {foreground = Screen.colors.Red}, + }) + + command('highlight CurSearch guibg=Black guifg=White') + insert([[ + There is no way that a bee should be + able to fly. Its wings are too small + to get its fat little body off the + ground. The bee, of course, flies + anyway because bees don't care what + humans think is impossible.]]) + + feed('/bee') + screen:expect{grid=[[ + There is no way that a {2:^bee} should be | + able to fly. Its wings are too small | + to get its fat little body off the | + ground. The {1:bee}, of course, flies | + anyway because {1:bee}s don't care what | + humans think is impossible. | + {3:search hit BOTTOM, continuing at TOP} | + ]]} + + feed('nn') + screen:expect{grid=[[ + There is no way that a {1:bee} should be | + able to fly. Its wings are too small | + to get its fat little body off the | + ground. The {1:bee}, of course, flies | + anyway because {2:^bee}s don't care what | + humans think is impossible. | + /bee | + ]]} + + feed('N') + screen:expect{grid=[[ + There is no way that a {1:bee} should be | + able to fly. Its wings are too small | + to get its fat little body off the | + ground. The {2:^bee}, of course, flies | + anyway because {1:bee}s don't care what | + humans think is impossible. | + ?bee | + ]]} + end) + it('highlights after EOL', function() insert("\n\n\n\n\n\n")