patch 9.1.0936: cannot highlight completed text

Problem:  cannot highlight completed text
Solution: (optionally) highlight auto-completed text using the
          ComplMatchIns highlight group (glepnir)

closes: #16173

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/runtime/doc/fold.txt b/runtime/doc/fold.txt
index 61f3b67..cf92089 100644
--- a/runtime/doc/fold.txt
+++ b/runtime/doc/fold.txt
@@ -160,7 +160,7 @@
 fold levels on previous lines until an independent fold level is found.
 
 If this proves difficult, the next best thing could be to cache all fold levels
-in a buffer-local variable (b:foldlevels) that is only updated on |b:changedtick|: 
+in a buffer-local variable (b:foldlevels) that is only updated on |b:changedtick|:
 >vim
   vim9script
   def MyFoldFunc(): number
diff --git a/runtime/doc/syntax.txt b/runtime/doc/syntax.txt
index eb6c3b2..7640ee1 100644
--- a/runtime/doc/syntax.txt
+++ b/runtime/doc/syntax.txt
@@ -1,4 +1,4 @@
-*syntax.txt*	For Vim version 9.1.  Last change: 2024 Dec 12
+*syntax.txt*	For Vim version 9.1.  Last change: 2024 Dec 16
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -5857,6 +5857,8 @@
 PmenuMatch	Popup menu: Matched text in normal item.
 							*hl-PmenuMatchSel*
 PmenuMatchSel	Popup menu: Matched text in selected item.
+							*hl-ComplMatchIns*
+ComplMatchIns	Matched text of the currently inserted completion.
 							*hl-PopupNotification*
 PopupNotification
 		Popup window created with |popup_notification()|.  If not
diff --git a/runtime/doc/tags b/runtime/doc/tags
index a8b4987..56004e9 100644
--- a/runtime/doc/tags
+++ b/runtime/doc/tags
@@ -8136,6 +8136,7 @@
 hitest.vim	syntax.txt	/*hitest.vim*
 hjkl	usr_02.txt	/*hjkl*
 hl-ColorColumn	syntax.txt	/*hl-ColorColumn*
+hl-ComplMatchIns	syntax.txt	/*hl-ComplMatchIns*
 hl-Conceal	syntax.txt	/*hl-Conceal*
 hl-CurSearch	syntax.txt	/*hl-CurSearch*
 hl-Cursor	syntax.txt	/*hl-Cursor*
diff --git a/runtime/doc/todo.txt b/runtime/doc/todo.txt
index b1318cf..342332e 100644
--- a/runtime/doc/todo.txt
+++ b/runtime/doc/todo.txt
@@ -1,4 +1,4 @@
-*todo.txt*      For Vim version 9.1.  Last change: 2024 Dec 04
+*todo.txt*      For Vim version 9.1.  Last change: 2024 Dec 16
 
 
 		  VIM REFERENCE MANUAL	  by Bram Moolenaar
@@ -1093,9 +1093,6 @@
 MS-Windows: buffer completion doesn't work when using backslash (or slash)
 for a path separator. (xtal8, #2201)
 
-Would be nice for Insert mode completion to highlight the text that was added
-(and may change when picking another completion).
-
 Test more runtime files.
 
 Window not closed when deleting buffer. (Harm te Hennepe, 2017 Aug 27, #2029)
diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt
index bcda3d0..8993e3c 100644
--- a/runtime/doc/version9.txt
+++ b/runtime/doc/version9.txt
@@ -41653,6 +41653,7 @@
 
 Highlighting: ~
 
+|hl-ComplMatchIns|	matched text of the currently inserted completion.
 |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
diff --git a/src/Makefile b/src/Makefile
index f3adaec..b677ebc 100644
--- a/src/Makefile
+++ b/src/Makefile
@@ -359,7 +359,7 @@
 #CONF_OPT_GUI = --enable-gui=motif --with-motif-lib="-static -lXm -shared"
 
 # Uncomment this line to run an individual test with gvim.
-#GUI_TESTARG = GUI_FLAG=-g 
+#GUI_TESTARG = GUI_FLAG=-g
 
 # DARWIN - detecting Mac OS X
 # Uncomment this line when you want to compile a Unix version of Vim on
diff --git a/src/drawline.c b/src/drawline.c
index b49e653..ec91331 100644
--- a/src/drawline.c
+++ b/src/drawline.c
@@ -1139,6 +1139,7 @@
     long	vcol_prev = -1;		// "wlv.vcol" of previous character
     char_u	*line;			// current line
     char_u	*ptr;			// current position in "line"
+    int		in_curline = wp == curwin && lnum == curwin->w_cursor.lnum;
 
 #ifdef FEAT_PROP_POPUP
     char_u	*p_extra_free2 = NULL;   // another p_extra to be freed
@@ -1172,6 +1173,7 @@
 					// 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
 #ifdef FEAT_SYN_HL
     int		vcol_save_attr = 0;	// saved attr for 'cursorcolumn'
     int		syntax_attr = 0;	// attributes desired by syntax
@@ -1415,8 +1417,7 @@
 	    }
 
 	    // Check if the character under the cursor should not be inverted
-	    if (!highlight_match && lnum == curwin->w_cursor.lnum
-								&& wp == curwin
+	    if (!highlight_match && in_curline
 #ifdef FEAT_GUI
 		    && !gui.in_use
 #endif
@@ -3939,6 +3940,14 @@
 	if (wlv.draw_state == WL_LINE)
 	    vcol_prev = wlv.vcol;
 
+	if (wlv.draw_state == WL_LINE
+		&& (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.draw_state < WL_LINE || skip_cells <= 0)
diff --git a/src/highlight.c b/src/highlight.c
index 1a4c76d..a4b2d48 100644
--- a/src/highlight.c
+++ b/src/highlight.c
@@ -262,6 +262,7 @@
     "default link PmenuMatchSel PmenuSel",
     "default link PmenuExtra Pmenu",
     "default link PmenuExtraSel PmenuSel",
+    "default link ComplMatchIns Normal",
     CENT("Normal cterm=NONE", "Normal gui=NONE"),
     NULL
 };
diff --git a/src/insexpand.c b/src/insexpand.c
index d3a6300..700ed54 100644
--- a/src/insexpand.c
+++ b/src/insexpand.c
@@ -173,6 +173,7 @@
 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 string_T	  compl_orig_text = {NULL, 0};  // text as it was before
 					    // completion started
 static int	  compl_cont_mode = 0;
@@ -198,6 +199,11 @@
 
 static int	  *compl_fuzzy_scores;
 
+// "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;
+
 static int ins_compl_add(char_u *str, int len, char_u *fname, char_u **cptext, typval_T *user_data, int cdir, int flags, int adup, int *user_hl);
 static void ins_compl_longest_match(compl_T *match);
 static void ins_compl_del_pum(void);
@@ -898,6 +904,32 @@
 }
 
 /*
+ * when len is -1 mean use whole length of p otherwise part of p
+ */
+    static void
+ins_compl_insert_bytes(char_u *p, int len)
+{
+    if (len == -1)
+	len = (int)STRLEN(p);
+    ins_bytes_len(p, 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((char_u *)"ComplMatchIns");
+
+    return -1;
+}
+
+/*
  * Reduce the longest common string for match "match".
  */
     static void
@@ -917,7 +949,7 @@
 	compl_leader.length = match->cp_str.length;
 	had_match = (curwin->w_cursor.col > compl_col);
 	ins_compl_delete();
-	ins_bytes(compl_leader.string + get_compl_len());
+	ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
 	ins_redraw(FALSE);
 
 	// When the match isn't there (to avoid matching itself) remove it
@@ -967,7 +999,7 @@
 
 	had_match = (curwin->w_cursor.col > compl_col);
 	ins_compl_delete();
-	ins_bytes(compl_leader.string + get_compl_len());
+	ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
 	ins_redraw(FALSE);
 
 	// When the match isn't there (to avoid matching itself) remove it
@@ -1060,12 +1092,6 @@
     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;
-
 /*
  * Update the screen and when there is any scrolling remove the popup menu.
  */
@@ -1817,6 +1843,7 @@
     compl_cont_status = 0;
     compl_started = FALSE;
     compl_matches = 0;
+    compl_ins_end_col = 0;
     VIM_CLEAR_STRING(compl_pattern);
     VIM_CLEAR_STRING(compl_leader);
     edit_submode_extra = NULL;
@@ -1965,7 +1992,7 @@
 {
     ins_compl_del_pum();
     ins_compl_delete();
-    ins_bytes(compl_leader.string + get_compl_len());
+    ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
     compl_used_match = FALSE;
 
     if (compl_started)
@@ -2410,7 +2437,7 @@
 	    int	    compl_len = get_compl_len();
 
 	    if ((int)plen > compl_len)
-		ins_bytes_len(p + compl_len, (int)(plen - compl_len));
+		ins_compl_insert_bytes(p + compl_len, (int)(plen - compl_len));
 	}
 	retval = TRUE;
     }
@@ -4260,7 +4287,7 @@
     // Make sure we don't go over the end of the string, this can happen with
     // illegal bytes.
     if (compl_len < (int)compl_shown_match->cp_str.length)
-	ins_bytes(compl_shown_match->cp_str.string + compl_len);
+	ins_compl_insert_bytes(compl_shown_match->cp_str.string + compl_len, -1);
     if (match_at_original_text(compl_shown_match))
 	compl_used_match = FALSE;
     else
@@ -4537,7 +4564,7 @@
     // Insert the text of the new completion, or the compl_leader.
     if (compl_no_insert && !started)
     {
-	ins_bytes(compl_orig_text.string + get_compl_len());
+	ins_compl_insert_bytes(compl_orig_text.string + get_compl_len(), -1);
 	compl_used_match = FALSE;
     }
     else if (insert_match)
@@ -4545,7 +4572,7 @@
 	if (!compl_get_longest || compl_used_match)
 	    ins_compl_insert(in_compl_func);
 	else
-	    ins_bytes(compl_leader.string + get_compl_len());
+	    ins_compl_insert_bytes(compl_leader.string + get_compl_len(), -1);
     }
     else
 	compl_used_match = FALSE;
diff --git a/src/proto/insexpand.pro b/src/proto/insexpand.pro
index 4feab85..2e769b8 100644
--- a/src/proto/insexpand.pro
+++ b/src/proto/insexpand.pro
@@ -60,5 +60,6 @@
 void ins_compl_insert(int in_compl_func);
 void ins_compl_check_keys(int frequency, int in_compl_func);
 int ins_complete(int c, int enable_pum);
+int ins_compl_col_range_attr(int col);
 void free_insexpand_stuff(void);
 /* vim: set ft=c : */
diff --git a/src/testdir/dumps/Test_pum_matchins_01.dump b/src/testdir/dumps/Test_pum_matchins_01.dump
new file mode 100644
index 0000000..efaa1eb
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_matchins_01.dump
@@ -0,0 +1,20 @@
+|f+0#ff404010#ffffff0|o@1> +0#0000000&@71
+|f+0#0000001#e0e0e08|o@1| @11| +0#4040ff13#ffffff0@59
+|b+0#0000001#ffd7ff255|a|r| @11| +0#4040ff13#ffffff0@59
+|你*0#0000001#ffd7ff255|好| +&@10| +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| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_matchins_02.dump b/src/testdir/dumps/Test_pum_matchins_02.dump
new file mode 100644
index 0000000..a3d9be3
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_matchins_02.dump
@@ -0,0 +1,20 @@
+|b+0#ff404010#ffffff0|a|r> +0#0000000&@71
+|f+0#0000001#ffd7ff255|o@1| @11| +0#4040ff13#ffffff0@59
+|b+0#0000001#e0e0e08|a|r| @11| +0#4040ff13#ffffff0@59
+|你*0#0000001#ffd7ff255|好| +&@10| +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| |2| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_matchins_03.dump b/src/testdir/dumps/Test_pum_matchins_03.dump
new file mode 100644
index 0000000..d1686b7
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_matchins_03.dump
@@ -0,0 +1,20 @@
+|你*0#ff404010#ffffff0|好> +0#0000000&@70
+|f+0#0000001#ffd7ff255|o@1| @11| +0#4040ff13#ffffff0@59
+|b+0#0000001#ffd7ff255|a|r| @11| +0#4040ff13#ffffff0@59
+|你*0#0000001#e0e0e08|好| +&@10| +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| |3| |o|f| |3| +0#0000000&@34
diff --git a/src/testdir/dumps/Test_pum_matchins_04.dump b/src/testdir/dumps/Test_pum_matchins_04.dump
new file mode 100644
index 0000000..0a324ef
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_matchins_04.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|4| @10|A|l@1| 
diff --git a/src/testdir/dumps/Test_pum_matchins_05.dump b/src/testdir/dumps/Test_pum_matchins_05.dump
new file mode 100644
index 0000000..a799fcd
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_matchins_05.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1| > @70
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|-+2#0000000&@1| |I|N|S|E|R|T| |-@1| +0&&@44|1|,|5| @10|A|l@1| 
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index 69228e1..bd36957 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1712,4 +1712,49 @@
   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\<C-X>\<C-O>")
+  call VerifyScreenDump(buf, 'Test_pum_matchins_01', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  call TermWait(buf)
+  call term_sendkeys(buf, "S\<C-X>\<C-O>\<C-N>")
+  call VerifyScreenDump(buf, 'Test_pum_matchins_02', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  call TermWait(buf)
+  call term_sendkeys(buf, "S\<C-X>\<C-O>\<C-N>\<C-N>")
+  call VerifyScreenDump(buf, 'Test_pum_matchins_03', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  " restore after accept
+  call TermWait(buf)
+  call term_sendkeys(buf, "S\<C-X>\<C-O>\<C-Y>")
+  call VerifyScreenDump(buf, 'Test_pum_matchins_04', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  " restore after cancel completion
+  call TermWait(buf)
+  call term_sendkeys(buf, "S\<C-X>\<C-O>\<Space>")
+  call VerifyScreenDump(buf, 'Test_pum_matchins_05', {})
+  call term_sendkeys(buf, "\<C-E>\<Esc>")
+
+  call StopVimInTerminal(buf)
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index a5db0d4..eb37def 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
 static int included_patches[] =
 {   /* Add new patch number below this line */
 /**/
+    936,
+/**/
     935,
 /**/
     934,