diff --git a/runtime/colors/vim.lua b/runtime/colors/vim.lua index 5b9309ab38..dd3e83c653 100644 --- a/runtime/colors/vim.lua +++ b/runtime/colors/vim.lua @@ -60,6 +60,7 @@ hi('PmenuMatch', { link = 'Pmenu' }) hi('PmenuMatchSel', { link = 'PmenuSel' }) hi('PmenuExtra', { link = 'Pmenu' }) hi('PmenuExtraSel', { link = 'PmenuSel' }) +hi('ComplMatchIns', { link = 'Normal' }) hi('Substitute', { link = 'Search' }) hi('Whitespace', { link = 'NonText' }) hi('MsgSeparator', { link = 'StatusLine' }) diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index ad0835e80f..73704a143c 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -216,6 +216,7 @@ EDITOR • On Windows, filename arguments on the command-line prefixed with "~\" or "~/" are now expanded to the user's profile directory, not a relative path to a literal "~" directory. +• |hl-ComplMatchIns| shows matched text of the currently inserted completion. • |hl-PmenuMatch| and |hl-PmenuMatchSel| show matched text in completion popup. EVENTS diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt index df4d0f7260..f5183e3247 100644 --- a/runtime/doc/syntax.txt +++ b/runtime/doc/syntax.txt @@ -5243,6 +5243,8 @@ PmenuMatch Popup menu: Matched text in normal item. Combined with *hl-PmenuMatchSel* PmenuMatchSel Popup menu: Matched text in selected item. Combined with |hl-PmenuMatch| and |hl-PmenuSel|. + *hl-ComplMatchIns* +ComplMatchIns Matched text of the currently inserted completion. *hl-Question* Question |hit-enter| prompt and yes/no questions. *hl-QuickFixLine* diff --git a/src/nvim/drawline.c b/src/nvim/drawline.c index b8f21d7454..e15296572b 100644 --- a/src/nvim/drawline.c +++ b/src/nvim/drawline.c @@ -31,6 +31,7 @@ #include "nvim/highlight_defs.h" #include "nvim/highlight_group.h" #include "nvim/indent.h" +#include "nvim/insexpand.h" #include "nvim/mark_defs.h" #include "nvim/marktree_defs.h" #include "nvim/match.h" @@ -934,6 +935,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s colnr_T vcol_prev = -1; // "wlv.vcol" of previous character ScreenGrid *grid = &wp->w_grid; // grid specific to the window + const bool in_curline = wp == curwin && lnum == curwin->w_cursor.lnum; const bool has_fold = foldinfo.fi_level != 0 && foldinfo.fi_lines > 0; const bool has_foldtext = has_fold && *wp->w_p_fdt != NUL; @@ -954,6 +956,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s int vi_attr = 0; // attributes for Visual and incsearch highlighting int area_attr = 0; // attributes desired by highlighting int search_attr = 0; // attributes desired by 'hlsearch' + int ins_match_attr = 0; // attributes desired by PmenuMatch int vcol_save_attr = 0; // saved attr for 'cursorcolumn' int decor_attr = 0; // attributes desired by syntax and extmarks bool has_syntax = false; // this buffer has syntax highl. @@ -1117,7 +1120,7 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s } // Check if the char under the cursor should be inverted (highlighted). - if (!highlight_match && lnum == curwin->w_cursor.lnum && wp == curwin + if (!highlight_match && in_curline && cursor_is_block_during_visual(*p_sel == 'e')) { noinvcur = true; } @@ -2704,6 +2707,14 @@ int win_line(win_T *wp, linenr_T lnum, int startrow, int endrow, int col_rows, s vcol_prev = wlv.vcol; } + if (wlv.filler_todo <= 0 + && (State & MODE_INSERT) && in_curline && ins_compl_active()) { + ins_match_attr = ins_compl_col_range_attr(wlv.col); + if (ins_match_attr > 0) { + wlv.char_attr = hl_combine_attr(wlv.char_attr, ins_match_attr); + } + } + // Store character to be displayed. // Skip characters that are left of the screen for 'nowrap'. if (wlv.filler_todo > 0) { diff --git a/src/nvim/highlight_group.c b/src/nvim/highlight_group.c index f1f5f47630..636124a698 100644 --- a/src/nvim/highlight_group.c +++ b/src/nvim/highlight_group.c @@ -173,6 +173,7 @@ static const char *highlight_init_both[] = { "default link PmenuKind Pmenu", "default link PmenuKindSel PmenuSel", "default link PmenuSbar Pmenu", + "default link ComplMatchIns Normal", "default link Substitute Search", "default link StatusLineTerm StatusLine", "default link StatusLineTermNC StatusLineNC", diff --git a/src/nvim/insexpand.c b/src/nvim/insexpand.c index 2e3a2e39ac..5036ec5e45 100644 --- a/src/nvim/insexpand.c +++ b/src/nvim/insexpand.c @@ -256,6 +256,7 @@ static pos_T compl_startpos; static int compl_length = 0; static colnr_T compl_col = 0; ///< column where the text starts ///< that is being completed +static colnr_T compl_ins_end_col = 0; static char *compl_orig_text = NULL; ///< text as it was before ///< completion started /// Undo information to restore extmarks for original text. @@ -282,6 +283,11 @@ static size_t spell_bad_len = 0; // length of located bad word static int compl_selected_item = -1; +// "compl_match_array" points the currently displayed list of entries in the +// popup menu. It is NULL when there is no popup menu. +static pumitem_T *compl_match_array = NULL; +static int compl_match_arraysize; + /// CTRL-X pressed in Insert mode. void ins_ctrl_x(void) { @@ -943,6 +949,30 @@ static bool ins_compl_equal(compl_T *match, char *str, size_t len) return strncmp(match->cp_str, str, len) == 0; } +/// when len is -1 mean use whole length of p otherwise part of p +static void ins_compl_insert_bytes(char *p, int len) + FUNC_ATTR_NONNULL_ALL +{ + if (len == -1) { + len = (int)strlen(p); + } + assert(len >= 0); + ins_bytes_len(p, (size_t)len); + compl_ins_end_col = curwin->w_cursor.col - 1; +} + +/// Checks if the column is within the currently inserted completion text +/// column range. If it is, it returns a special highlight attribute. +/// -1 mean normal item. +int ins_compl_col_range_attr(int col) +{ + if (col >= compl_col && col < compl_ins_end_col) { + return syn_name2attr("ComplMatchIns"); + } + + return -1; +} + /// Reduce the longest common string for match "match". static void ins_compl_longest_match(compl_T *match) { @@ -952,7 +982,7 @@ static void ins_compl_longest_match(compl_T *match) bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(false); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); ins_redraw(false); // When the match isn't there (to avoid matching itself) remove it @@ -986,7 +1016,7 @@ static void ins_compl_longest_match(compl_T *match) *p = NUL; bool had_match = (curwin->w_cursor.col > compl_col); ins_compl_delete(false); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); ins_redraw(false); // When the match isn't there (to avoid matching itself) remove it @@ -1058,11 +1088,6 @@ unsigned get_cot_flags(void) return curbuf->b_cot_flags != 0 ? curbuf->b_cot_flags : cot_flags; } -/// "compl_match_array" points the currently displayed list of entries in the -/// popup menu. It is NULL when there is no popup menu. -static pumitem_T *compl_match_array = NULL; -static int compl_match_arraysize; - /// Remove any popup menu. static void ins_compl_del_pum(void) { @@ -1678,6 +1703,7 @@ void ins_compl_clear(void) compl_cont_status = 0; compl_started = false; compl_matches = 0; + compl_ins_end_col = 0; XFREE_CLEAR(compl_pattern); compl_patternlen = 0; XFREE_CLEAR(compl_leader); @@ -1802,7 +1828,7 @@ static void ins_compl_new_leader(void) { ins_compl_del_pum(); ins_compl_delete(true); - ins_bytes(compl_leader + get_compl_len()); + ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); compl_used_match = false; if (compl_started) { @@ -2137,7 +2163,7 @@ static bool ins_compl_stop(const int c, const int prev_mode, bool retval) const int compl_len = get_compl_len(); const int len = (int)strlen(p); if (len > compl_len) { - ins_bytes_len(p + compl_len, (size_t)(len - compl_len)); + ins_compl_insert_bytes(p + compl_len, len - compl_len); } } restore_orig_extmarks(); @@ -3639,7 +3665,7 @@ void ins_compl_insert(bool in_compl_func) // Make sure we don't go over the end of the string, this can happen with // illegal bytes. if (compl_len < (int)strlen(compl_shown_match->cp_str)) { - ins_bytes(compl_shown_match->cp_str + compl_len); + ins_compl_insert_bytes(compl_shown_match->cp_str + compl_len, -1); } compl_used_match = !match_at_original_text(compl_shown_match); @@ -3888,14 +3914,15 @@ static int ins_compl_next(bool allow_get_expansion, int count, bool insert_match // Insert the text of the new completion, or the compl_leader. if (compl_no_insert && !started) { - ins_bytes(compl_orig_text + get_compl_len()); + ins_compl_insert_bytes(compl_orig_text + get_compl_len(), -1); compl_used_match = false; restore_orig_extmarks(); } else if (insert_match) { if (!compl_get_longest || compl_used_match) { ins_compl_insert(in_compl_func); } else { - ins_bytes(compl_leader + get_compl_len()); + assert(compl_leader != NULL); + ins_compl_insert_bytes(compl_leader + get_compl_len(), -1); } if (!strcmp(compl_curr_match->cp_str, compl_orig_text)) { restore_orig_extmarks(); diff --git a/test/old/testdir/test_popup.vim b/test/old/testdir/test_popup.vim index a24e133ce6..4f5b769ace 100644 --- a/test/old/testdir/test_popup.vim +++ b/test/old/testdir/test_popup.vim @@ -1713,4 +1713,49 @@ func Test_pum_keep_select() call StopVimInTerminal(buf) endfunc +func Test_pum_matchins_higlight() + CheckScreendump + let lines =<< trim END + func Omni_test(findstart, base) + if a:findstart + return col(".") + endif + return [#{word: "foo"}, #{word: "bar"}, #{word: "你好"}] + endfunc + set omnifunc=Omni_test + hi ComplMatchIns ctermfg=red + END + call writefile(lines, 'Xscript', 'D') + let buf = RunVimInTerminal('-S Xscript', {}) + + call TermWait(buf) + call term_sendkeys(buf, "S\\") + call VerifyScreenDump(buf, 'Test_pum_matchins_01', {}) + call term_sendkeys(buf, "\\") + + call TermWait(buf) + call term_sendkeys(buf, "S\\\") + call VerifyScreenDump(buf, 'Test_pum_matchins_02', {}) + call term_sendkeys(buf, "\\") + + call TermWait(buf) + call term_sendkeys(buf, "S\\\\") + call VerifyScreenDump(buf, 'Test_pum_matchins_03', {}) + call term_sendkeys(buf, "\\") + + " restore after accept + call TermWait(buf) + call term_sendkeys(buf, "S\\\") + call VerifyScreenDump(buf, 'Test_pum_matchins_04', {}) + call term_sendkeys(buf, "\\") + + " restore after cancel completion + call TermWait(buf) + call term_sendkeys(buf, "S\\\") + call VerifyScreenDump(buf, 'Test_pum_matchins_05', {}) + call term_sendkeys(buf, "\\") + + call StopVimInTerminal(buf) +endfunc + " vim: shiftwidth=2 sts=2 expandtab