patch 9.1.1296: completion: incorrect truncation logic

Problem:  completion: incorrect truncation logic (after: v9.1.1284)
Solution: replace string allocation with direct screen rendering and
          fixe RTL/LTR truncation calculations (glepnir)

closes: #17081

Signed-off-by: glepnir <glephunter@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/popupmenu.c b/src/popupmenu.c
index 556c2c3..07c99f0 100644
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -604,10 +604,15 @@
     int		last_isabbr = FALSE;
     int		orig_attr = -1;
     int		scroll_range = pum_size - pum_height;
-    char_u	*new_str = NULL;
-    char_u	*ptr = NULL;
     int		remaining = 0;
-    int		fcs_trunc = curwin->w_fill_chars.trunc;
+    int		fcs_trunc;
+
+#ifdef  FEAT_RIGHTLEFT
+    if (pum_rl)
+	fcs_trunc = curwin->w_fill_chars.truncrl;
+    else
+#endif
+	fcs_trunc = curwin->w_fill_chars.trunc;
 
     hlf_T	hlfsNorm[3];
     hlf_T	hlfsSel[3];
@@ -722,10 +727,19 @@
 
 			    if (rt != NULL)
 			    {
-				char_u		*rt_start = rt;
-				int		cells;
+				char_u	    *rt_start = rt;
+				int	    cells;
+				int	    over_cell = 0;
+				int	    truncated = FALSE;
 
 				cells = mb_string2cells(rt , -1);
+				truncated = pum_width == p_pmw
+						&& pum_width - totwidth < cells;
+
+				if (pum_width == p_pmw && !truncated
+					&& (j + 1 < 3 && pum_get_item(idx, order[j + 1]) != NULL))
+				    truncated = TRUE;
+
 				if (cells > pum_width)
 				{
 				    do
@@ -746,13 +760,9 @@
 				    }
 				}
 
-				// truncated
-				if (pum_width == p_pmw
-					&& totwidth + 1 + cells >= pum_width)
+				if (truncated)
 				{
 				    char_u  *orig_rt = rt;
-				    char_u  *old_rt = NULL;
-				    int	    over_cell = 0;
 				    int	    size = 0;
 
 				    remaining = pum_width - totwidth - 1;
@@ -768,26 +778,19 @@
 				    size = (int)STRLEN(orig_rt);
 				    if (cells < remaining)
 					over_cell =  remaining - cells;
-				    new_str = alloc(size + over_cell + 1 + utf_char2len(fcs_trunc));
-				    if (!new_str)
-					return;
-				    ptr = new_str;
-				    if (fcs_trunc != NUL && fcs_trunc != '>')
-					ptr += (*mb_char2bytes)(fcs_trunc, ptr);
+
+				    cells = mb_string2cells(orig_rt, size);
+				    width = cells + over_cell + 1;
+				    rt = orig_rt;
+
+				    if (fcs_trunc != NUL)
+					screen_putchar(fcs_trunc, row, col - width + 1, attr);
 				    else
-					*ptr++ = '<';
-				    if (over_cell)
-				    {
-					vim_memset(ptr, ' ', over_cell);
-					ptr += over_cell;
-				    }
-				    memcpy(ptr, orig_rt, size);
-				    ptr[size] = NUL;
-				    old_rt = rt_start;
-				    rt = rt_start = new_str;
-				    vim_free(old_rt);
-				    cells = mb_string2cells(rt, -1);
-				    width = cells;
+					screen_putchar('<', row, col - width + 1, attr);
+
+				    if (over_cell > 0)
+					screen_fill(row, row + 1, col - width + 2,
+						    col - width + 2 + over_cell, ' ', ' ', attr);
 				}
 
 				if (attrs == NULL)
@@ -809,10 +812,16 @@
 		    {
 			if (st != NULL)
 			{
-			    int size = (int)STRLEN(st);
-			    int cells = (*mb_string2cells)(st, size);
-			    char_u *st_end = NULL;
-			    int over_cell = 0;
+			    int		size = (int)STRLEN(st);
+			    int		cells = (*mb_string2cells)(st, size);
+			    char_u	*st_end = NULL;
+			    int		over_cell = 0;
+			    int		truncated = pum_width == p_pmw
+						&& pum_width - totwidth < cells;
+
+			    if (pum_width == p_pmw && !truncated
+				    && (j + 1 < 3 && pum_get_item(idx, order[j + 1]) != NULL))
+				truncated = TRUE;
 
 			    // only draw the text that fits
 			    while (size > 0
@@ -829,8 +838,7 @@
 			    }
 
 			    // truncated
-			    if (pum_width == p_pmw
-				    && totwidth + 1 + cells >= pum_width)
+			    if (truncated)
 			    {
 				remaining = pum_width - totwidth - 1;
 				if (cells > remaining)
@@ -846,28 +854,8 @@
 
 				if (cells < remaining)
 				    over_cell =  remaining - cells;
-				new_str = alloc(size + over_cell + 1 + utf_char2len(fcs_trunc));
-				if (!new_str)
-				    return;
-				memcpy(new_str, st, size);
-				ptr = new_str + size;
-				if (over_cell > 0)
-				{
-				    vim_memset(ptr, ' ', over_cell);
-				    ptr += over_cell;
-				}
-
-				if (fcs_trunc != NUL)
-				    ptr += (*mb_char2bytes)(fcs_trunc, ptr);
-				else
-				    *ptr++ = '>';
-
-				*ptr = NUL;
-				vim_free(st);
-				st = new_str;
-				cells = mb_string2cells(st, -1);
-				size = (int)STRLEN(st);
-				width = cells;
+				cells = mb_string2cells(st, size);
+				width = cells + over_cell + 1;
 			    }
 
 			    if (attrs == NULL)
@@ -875,6 +863,18 @@
 			    else
 				pum_screen_puts_with_attrs(row, col, cells,
 							      st, size, attrs);
+			    if (truncated)
+			    {
+				if (over_cell > 0)
+				    screen_fill(row, row + 1, col + cells,
+					    col + cells + over_cell, ' ', ' ', attr);
+				if (fcs_trunc != NUL)
+				    screen_putchar(fcs_trunc, row,
+					    col + cells + over_cell, attr);
+				else
+				    screen_putchar('>', row,
+					    col + cells + over_cell, attr);
+			    }
 
 			    vim_free(st);
 			}