changeset 35375:76f1a20420fd v9.1.0476

patch 9.1.0476: Cannot see matched text in popup menu Commit: https://github.com/vim/vim/commit/40c1c3317d92f8c2adadf744fab72e4458e2a9fa Author: glepnir <glephunter@gmail.com> Date: Tue Jun 11 19:37:04 2024 +0200 patch 9.1.0476: Cannot see matched text in popup menu Problem: Cannot see matched text in popup menu Solution: Introduce 2 new highlighting groups: PmenuMatch and PmenuMatchSel (glepnir) ping @habamax, @neutaaaaan @romainl because vim/colorschemes may need some updates, @lifepillar for updating vim-colortemplate closes: #14694 Signed-off-by: glepnir <glephunter@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Tue, 11 Jun 2024 19:45:03 +0200
parents b091ef9a0d32
children 570c9df322bb
files runtime/doc/options.txt runtime/doc/syntax.txt runtime/doc/tags runtime/doc/version9.txt src/highlight.c src/insexpand.c src/optiondefs.h src/popupmenu.c src/proto/insexpand.pro src/proto/search.pro src/search.c src/testdir/dumps/Test_pum_highlights_03.dump src/testdir/dumps/Test_pum_highlights_04.dump src/testdir/dumps/Test_pum_highlights_05.dump src/testdir/dumps/Test_pum_highlights_06.dump src/testdir/dumps/Test_pum_highlights_07.dump src/testdir/test_popup.vim src/version.c src/vim.h
diffstat 19 files changed, 405 insertions(+), 8 deletions(-) [+]
line wrap: on
line diff
--- a/runtime/doc/options.txt
+++ b/runtime/doc/options.txt
@@ -1,4 +1,4 @@
-*options.txt*	For Vim version 9.1.  Last change: 2024 Jun 05
+*options.txt*	For Vim version 9.1.  Last change: 2024 Jun 11
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -4275,6 +4275,7 @@ A jump table for the options with a shor
 				     T:DiffText,>:SignColumn,-:Conceal,
 				     B:SpellBad,P:SpellCap,R:SpellRare,
 				     L:SpellLocal,+:Pmenu,=:PmenuSel,
+				     k:PmenuMatch,<:PmenuMatchSel,
 				     [:PmenuKind,]:PmenuKindSel,
 				     {:PmenuExtra,}:PmenuExtraSel,
 				     x:PmenuSbar,X:PmenuThumb,*:TabLine,
@@ -4341,6 +4342,8 @@ A jump table for the options with a shor
 	|hl-PmenuExtraSel| }  popup menu "extra" selected line
 	|hl-PmenuSbar|	 x  popup menu scrollbar
 	|hl-PmenuThumb|	 X  popup menu scrollbar thumb
+	|hl-PmenuMatch|	 k  popup menu matched text
+	|hl-PmenuMatchSel| <  popup menu matched text in selected line
 
 	The display modes are:
 		r	reverse		(termcap entry "mr" and "me")
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -1,4 +1,4 @@
-*syntax.txt*	For Vim version 9.1.  Last change: 2024 Jun 09
+*syntax.txt*	For Vim version 9.1.  Last change: 2024 Jun 11
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -5681,6 +5681,11 @@ PmenuExtraSel	Popup menu: Selected item 
 PmenuSbar	Popup menu: Scrollbar.
 							*hl-PmenuThumb*
 PmenuThumb	Popup menu: Thumb of the scrollbar.
+							*hl-PmenuMatch*
+PmenuMatch	Popup menu: Matched text in normal item
+							*hl-PmenuMatchSel*
+PmenuMatchSel	Popup menu: Matched text in selected item
+
 							*hl-PopupNotification*
 PopupNotification
 		Popup window created with |popup_notification()|.  If not
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -8090,6 +8090,8 @@ hl-PmenuExtra	syntax.txt	/*hl-PmenuExtra
 hl-PmenuExtraSel	syntax.txt	/*hl-PmenuExtraSel*
 hl-PmenuKind	syntax.txt	/*hl-PmenuKind*
 hl-PmenuKindSel	syntax.txt	/*hl-PmenuKindSel*
+hl-PmenuMatch	syntax.txt	/*hl-PmenuMatch*
+hl-PmenuMatchSel	syntax.txt	/*hl-PmenuMatchSel*
 hl-PmenuSbar	syntax.txt	/*hl-PmenuSbar*
 hl-PmenuSel	syntax.txt	/*hl-PmenuSel*
 hl-PmenuThumb	syntax.txt	/*hl-PmenuThumb*
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -1,4 +1,4 @@
-*version9.txt*  For Vim version 9.1.  Last change: 2024 Jun 05
+*version9.txt*  For Vim version 9.1.  Last change: 2024 Jun 10
 
 
 		  VIM REFERENCE MANUAL    by Bram Moolenaar
@@ -41596,6 +41596,9 @@ Autocommands: ~
 Highlighting: ~
 
 |hl-MsgArea|		highlighting of the Command-line and messages area
+|hl-PmenuMatch|		Popup menu: highlighting of matched text
+|hl-PmenuMatchSel|     	Popup menu: highlighting of matched text in selected
+			line
 
 Commands: ~
 
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -258,6 +258,8 @@ static char *(highlight_init_both[]) = {
     "default link CurSearch Search",
     "default link PmenuKind Pmenu",
     "default link PmenuKindSel PmenuSel",
+    "default link PmenuMatch Pmenu",
+    "default link PmenuMatchSel PmenuSel",
     "default link PmenuExtra Pmenu",
     "default link PmenuExtraSel PmenuSel",
     CENT("Normal cterm=NONE", "Normal gui=NONE"),
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -1432,6 +1432,15 @@ ins_compl_show_pum(void)
 #define DICT_EXACT	(2)	// "dict" is the exact name of a file
 
 /*
+ * Get current completion leader
+ */
+    char_u *
+ins_compl_leader(void)
+{
+    return compl_leader;
+}
+
+/*
  * Add any identifiers that match the given pattern "pat" in the list of
  * dictionary files "dict_start" to the list of completions.
  */
--- a/src/optiondefs.h
+++ b/src/optiondefs.h
@@ -301,7 +301,7 @@ struct vimoption
 # define ISP_LATIN1 (char_u *)"@,161-255"
 #endif
 
-# define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea"
+# define HIGHLIGHT_INIT "8:SpecialKey,~:EndOfBuffer,@:NonText,d:Directory,e:ErrorMsg,i:IncSearch,l:Search,y:CurSearch,m:MoreMsg,M:ModeMsg,n:LineNr,a:LineNrAbove,b:LineNrBelow,N:CursorLineNr,G:CursorLineSign,O:CursorLineFold,r:Question,s:StatusLine,S:StatusLineNC,c:VertSplit,t:Title,v:Visual,V:VisualNOS,w:WarningMsg,W:WildMenu,f:Folded,F:FoldColumn,A:DiffAdd,C:DiffChange,D:DiffDelete,T:DiffText,>:SignColumn,-:Conceal,B:SpellBad,P:SpellCap,R:SpellRare,L:SpellLocal,+:Pmenu,=:PmenuSel,k:PmenuMatch,<:PmenuMatchSel,[:PmenuKind,]:PmenuKindSel,{:PmenuExtra,}:PmenuExtraSel,x:PmenuSbar,X:PmenuThumb,*:TabLine,#:TabLineSel,_:TabLineFill,!:CursorColumn,.:CursorLine,o:ColorColumn,q:QuickFixLine,z:StatusLineTerm,Z:StatusLineTermNC,g:MsgArea"
 
 // Default python version for pyx* commands
 #if defined(FEAT_PYTHON) && defined(FEAT_PYTHON3)
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -417,6 +417,88 @@ pum_under_menu(int row, int col, int onl
 }
 
 /*
+ * displays text on the popup menu with specific attributes.
+ */
+    static void
+pum_screen_put_with_attr(int row, int col, char_u *text, int textlen, int attr)
+{
+    int		i;
+    int		leader_len;
+    int		char_len;
+    int		cells;
+    int		new_attr;
+    char_u	*rt_leader = NULL;
+    char_u	*match_leader = NULL;
+    char_u	*ptr = text;
+    garray_T	*ga = NULL;
+    char_u	*leader = ins_compl_leader();
+    int		in_fuzzy = (get_cot_flags() & COT_FUZZY) != 0;
+
+    if ((highlight_attr[HLF_PMSI] == highlight_attr[HLF_PSI] &&
+         highlight_attr[HLF_PMNI] == highlight_attr[HLF_PNI]))
+    {
+        screen_puts_len(text, textlen, row, col, attr);
+        return;
+    }
+
+#ifdef FEAT_RIGHTLEFT
+    if (leader != NULL && curwin->w_p_rl)
+        rt_leader = reverse_text(leader);
+#endif
+    match_leader = rt_leader != NULL ? rt_leader : leader;
+    leader_len = match_leader ? (int)STRLEN(match_leader) : 0;
+
+    if (match_leader != NULL && leader_len > 0 && in_fuzzy)
+        ga = fuzzy_match_str_with_pos(text, match_leader);
+
+    // Render text with proper attributes
+    while (*ptr != NUL && ptr < text + textlen)
+    {
+        char_len = mb_ptr2len(ptr);
+        cells = mb_ptr2cells(ptr);
+        new_attr = attr;
+
+        if (ga != NULL)
+        {
+            // Handle fuzzy matching
+            for (i = 0; i < ga->ga_len; i++)
+            {
+                int *match_pos = ((int *)ga->ga_data) + i;
+                int actual_char_pos = 0;
+                char_u *temp_ptr = text;
+                while (temp_ptr < ptr)
+                {
+                    temp_ptr += mb_ptr2len(temp_ptr);
+                    actual_char_pos++;
+                }
+                if (actual_char_pos == match_pos[0])
+                {
+                    new_attr = highlight_attr[(attr == highlight_attr[HLF_PSI]
+							? HLF_PMSI : HLF_PMNI)];
+                    break;
+                }
+            }
+        }
+        else if (!in_fuzzy && (ptr - text < leader_len) &&
+				(STRNCMP(text, match_leader, leader_len) == 0))
+                new_attr = highlight_attr[(attr == highlight_attr[HLF_PSI]
+						    ? HLF_PMSI : HLF_PMNI)];
+
+        screen_puts_len(ptr, char_len, row, col, new_attr);
+        col += cells;
+        ptr += char_len;
+    }
+
+    if (ga != NULL)
+    {
+        ga_clear(ga);
+        vim_free(ga);
+    }
+    if (rt_leader)
+        vim_free(rt_leader);
+}
+
+/*
  * Redraw the popup menu, using "pum_first" and "pum_selected".
  */
     void
@@ -567,8 +649,7 @@ pum_redraw(void)
 					    size++;
 					}
 				    }
-				    screen_puts_len(rt, (int)STRLEN(rt),
-						   row, col - size + 1, attr);
+				    pum_screen_put_with_attr(row, col -size + 1, rt, (int)STRLEN(rt), attr);
 				    vim_free(rt_start);
 				}
 				vim_free(st);
@@ -596,7 +677,7 @@ pum_redraw(void)
 				    else
 					--cells;
 				}
-				screen_puts_len(st, size, row, col, attr);
+				pum_screen_put_with_attr(row, col, st, size, attr);
 				vim_free(st);
 			    }
 			    col += width;
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -30,6 +30,7 @@ int ins_compl_long_shown_match(void);
 unsigned int get_cot_flags(void);
 int pum_wanted(void);
 void ins_compl_show_pum(void);
+char_u *ins_compl_leader(void);
 char_u *find_word_start(char_u *ptr);
 char_u *find_word_end(char_u *ptr);
 void ins_compl_clear(void);
--- a/src/proto/search.pro
+++ b/src/proto/search.pro
@@ -40,6 +40,7 @@ int fuzzy_match(char_u *str, char_u *pat
 void f_matchfuzzy(typval_T *argvars, typval_T *rettv);
 void f_matchfuzzypos(typval_T *argvars, typval_T *rettv);
 int fuzzy_match_str(char_u *str, char_u *pat);
+garray_T *fuzzy_match_str_with_pos(char_u *str, char_u *pat);
 void fuzmatch_str_free(fuzmatch_str_T *fuzmatch, int count);
 int fuzzymatches_to_strmatches(fuzmatch_str_T *fuzmatch, char_u ***matches, int count, int funcsort);
 /* vim: set ft=c : */
--- a/src/search.c
+++ b/src/search.c
@@ -5092,6 +5092,125 @@ fuzzy_match_str(char_u *str, char_u *pat
 }
 
 /*
+ * Fuzzy match the position of string 'pat' in string 'str'.
+ * Returns a dynamic array of matching positions. If there is no match,
+ * returns NULL.
+ */
+    garray_T *
+fuzzy_match_str_with_pos(char_u *str UNUSED, char_u *pat UNUSED)
+{
+#ifdef FEAT_SEARCH_EXTRA
+    int		    score = 0;
+    garray_T	    *match_positions = ALLOC_ONE(garray_T);
+    typval_T	    tv_str;
+    list_T	    *l = NULL;
+    list_T	    *retlist = NULL;
+    list_T	    *match_str_list = NULL;
+    list_T	    *match_pos_list = NULL;
+    list_T	    *match_score_list = NULL;
+    listitem_T	    *score_item = NULL;
+    listitem_T	    *positions_item = NULL;
+    list_T	    *positions_outer_list = NULL;
+    listitem_T	    *outer_li = NULL;
+    list_T	    *positions_inner_list = NULL;
+
+    if (match_positions == NULL)
+        return NULL;
+    ga_init2(match_positions, sizeof(int), 10);
+    if (str == NULL || pat == NULL)
+    {
+        ga_clear(match_positions);
+        return NULL;
+    }
+    l = list_alloc();
+    if (l == NULL)
+    {
+        ga_clear(match_positions);
+        return NULL;
+    }
+
+    tv_str.v_type = VAR_STRING;
+    tv_str.vval.v_string = vim_strsave(str);
+    if (tv_str.vval.v_string == NULL || list_append_tv(l, &tv_str) == FAIL)
+        goto cleanup;
+
+    retlist = list_alloc();
+    if (retlist == NULL)
+        goto cleanup;
+
+    match_str_list = list_alloc();
+    match_pos_list = list_alloc();
+    match_score_list = list_alloc();
+    if (match_str_list == NULL || match_pos_list == NULL || match_score_list == NULL)
+        goto cleanup;
+
+    list_append_list(retlist, match_str_list);
+    list_append_list(retlist, match_pos_list);
+    list_append_list(retlist, match_score_list);
+
+    fuzzy_match_in_list(l, pat, FALSE, NULL, NULL, TRUE, retlist, 1);
+
+    if (retlist->lv_len != 3)
+        goto cleanup;
+
+    score_item = list_find(retlist, 2);
+    if (score_item != NULL && score_item->li_tv.v_type == VAR_LIST)
+    {
+        list_T *score_list = score_item->li_tv.vval.v_list;
+        if (score_list->lv_len > 0)
+        {
+            listitem_T *first_score_item = score_list->lv_first;
+            if (first_score_item != NULL && first_score_item->li_tv.v_type == VAR_NUMBER)
+                score = first_score_item->li_tv.vval.v_number;
+        }
+    }
+    if (score == 0)
+        goto cleanup;
+
+    positions_item = list_find(retlist, 1);
+    if (positions_item != NULL && positions_item->li_tv.v_type == VAR_LIST)
+    {
+        positions_outer_list = positions_item->li_tv.vval.v_list;
+        if (positions_outer_list->lv_len > 0)
+        {
+            outer_li = positions_outer_list->lv_first;
+            if (outer_li != NULL && outer_li->li_tv.v_type == VAR_LIST)
+            {
+                positions_inner_list = outer_li->li_tv.vval.v_list;
+                for (listitem_T *li = positions_inner_list->lv_first; li != NULL; li = li->li_next)
+                {
+                    if (li->li_tv.v_type == VAR_NUMBER)
+                    {
+                        int pos = li->li_tv.vval.v_number;
+                        ga_grow(match_positions, 1);
+                        ((int *)match_positions->ga_data)[match_positions->ga_len] = pos;
+                        match_positions->ga_len++;
+                    }
+                }
+            }
+        }
+    }
+
+    vim_free(tv_str.vval.v_string);
+    list_free(retlist);
+    list_free(l);
+    return match_positions;
+
+cleanup:
+    vim_free(tv_str.vval.v_string);
+    list_free(match_str_list);
+    list_free(match_pos_list);
+    list_free(match_score_list);
+    list_free(retlist);
+    list_free(l);
+    ga_clear(match_positions);
+    return NULL;
+#else
+    return NULL;
+#endif
+}
+
+/*
  * Free an array of fuzzy string matches "fuzmatch[count]".
  */
     void
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_03.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o> @72
+|f+0#00e0e07#ffd7ff255|o|o+0#0000001#e0e0e08| @11| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|r| @8| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|B|a|z| @8| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|l|a| @7| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |8| +0#0000000&@34
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_04.dump
@@ -0,0 +1,20 @@
+|你*0&#ffffff0> +&@72
+|你*0#00e0e07#ffd7ff255|好*0#0000001#e0e0e08| +&@10| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|好*0#0000001&|吗| +&@8| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|不*0#0000001&|好|吗| +&@6| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|可*0#0000001&|好|吗| +&@6| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |8| +0#0000000&@34
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_05.dump
@@ -0,0 +1,20 @@
+|你*0&#ffffff0|吗> +&@70
+|你*0#00e0e07#ffd7ff255|好*0#0000001#e0e0e08|吗*0#00e0e07#ffd7ff255| +0#0000001#e0e0e08@8| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|不*0#0000001&|好|吗*0#0000e05&| +0#0000001&@6| +0#4040ff13#ffffff0@59
+|你*0#0000e05#ffd7ff255|可*0#0000001&|好|吗*0#0000e05&| +0#0000001&@6| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |8| +0#0000000&@34
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_06.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@70|o>f|o|f
+| +0#4040ff13&@59| +0#0000001#e0e0e08@11|o|o+0#00e0e07#ffd7ff255|f
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@8|r|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@8|z|a|B|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@59| +0#0000001#ffd7ff255@7|a|l|a|b|o|o+0#0000e05&|f
+| +0#4040ff13#ffffff0@73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+| @73|~
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |8| +0#0000000&@34
new file mode 100644
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_07.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o> @72
+|f+0#00e0e07#ffd7ff255|o|o+0#0000001#e0e0e08| @11| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|r| @8| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|B|a|z| @8| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o|o+0#0000001&|b|a|l|a| @7| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |O|m|n|i| |c|o|m|p|l|e|t|i|o|n| |(|^|O|^|N|^|P|)| |m+0#00e0003&|a|t|c|h| |1| |o|f| |8| +0#0000000&@34
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1341,4 +1341,71 @@ func Test_pum_highlights_custom()
   call StopVimInTerminal(buf)
 endfunc
 
+" Test match relate highlight group in pmenu
+func Test_pum_highlights_match()
+  CheckScreendump
+  let lines =<< trim END
+    func Omni_test(findstart, base)
+      if a:findstart
+        return col(".")
+      endif
+      return {
+            \ 'words': [
+            \ { 'word': 'foo',},
+            \ { 'word': 'foobar',},
+            \ { 'word': 'fooBaz',},
+            \ { 'word': 'foobala',},
+            \ { 'word': '你好',},
+            \ { 'word': '你好吗',},
+            \ { 'word': '你不好吗',},
+            \ { 'word': '你可好吗',},
+            \]}
+    endfunc
+    set omnifunc=Omni_test
+    set completeopt=menu,noinsert,fuzzy
+    hi PmenuMatchSel  ctermfg=6 ctermbg=225
+    hi PmenuMatch     ctermfg=4 ctermbg=225
+  END
+  call writefile(lines, 'Xscript', 'D')
+  let  buf = RunVimInTerminal('-S Xscript', {})
+  call TermWait(buf)
+  call term_sendkeys(buf, "i\<C-X>\<C-O>")
+  call TermWait(buf, 50)
+  call term_sendkeys(buf, "fo")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_highlights_03', {})
+  call term_sendkeys(buf, "\<ESC>S\<C-x>\<C-O>")
+  call TermWait(buf, 50)
+  call term_sendkeys(buf, "你")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_highlights_04', {})
+  call term_sendkeys(buf, "吗")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_highlights_05', {})
+
+  if has('rightleft')
+    call term_sendkeys(buf, "\<C-E>\<ESC>u:set rightleft\<CR>")
+    call TermWait(buf, 50)
+    call term_sendkeys(buf, "i\<C-X>\<C-O>")
+    call TermWait(buf, 50)
+    call term_sendkeys(buf, "fo")
+    call TermWait(buf, 50)
+    call VerifyScreenDump(buf, 'Test_pum_highlights_06', {})
+    call term_sendkeys(buf, "\<C-E>\<ESC>u:set norightleft\<CR>")
+    call TermWait(buf)
+  endif
+
+  call term_sendkeys(buf, ":set completeopt-=fuzzy\<CR>")
+  call TermWait(buf)
+  call term_sendkeys(buf, "\<ESC>S\<C-x>\<C-O>")
+  call TermWait(buf, 50)
+  call term_sendkeys(buf, "fo")
+  call TermWait(buf, 50)
+  call VerifyScreenDump(buf, 'Test_pum_highlights_07', {})
+
+  call term_sendkeys(buf, "\<C-E>\<Esc>u")
+  call TermWait(buf)
+  call StopVimInTerminal(buf)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@ static char *(features[]) =
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    476,
+/**/
     475,
 /**/
     474,
--- a/src/vim.h
+++ b/src/vim.h
@@ -1500,6 +1500,8 @@ typedef enum
     , HLF_SPL	    // SpellLocal
     , HLF_PNI	    // popup menu normal item
     , HLF_PSI	    // popup menu selected item
+    , HLF_PMNI	    // popup menu matched text in normal item
+    , HLF_PMSI	    // popup menu matched text in selected item
     , HLF_PNK	    // popup menu normal item "kind"
     , HLF_PSK	    // popup menu selected item "kind"
     , HLF_PNX	    // popup menu normal item "menu" (extra text)
@@ -1525,7 +1527,7 @@ typedef enum
 		  'n', 'a', 'b', 'N', 'G', 'O', 'r', 's', 'S', 'c', 't', 'v', 'V', \
 		  'w', 'W', 'f', 'F', 'A', 'C', 'D', 'T', '-', '>', \
 		  'B', 'P', 'R', 'L', \
-		  '+', '=', '[', ']', '{', '}', 'x', 'X', \
+		  '+', '=', 'k', '<','[', ']', '{', '}', 'x', 'X', \
 		  '*', '#', '_', '!', '.', 'o', 'q', \
 		  'z', 'Z', 'g'}