patch 9.1.1011: popupmenu internal error with some abbr in completion item
Problem: Popup menu internal error with some abbr in completion item.
Solution: Don't compute attributes when there is no corresponding text.
Reduce indent in pum_redraw() while at it (zeertzjq).
fixes: #16427
closes: #16435
Signed-off-by: zeertzjq <zeertzjq@outlook.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
diff --git a/src/popupmenu.c b/src/popupmenu.c
index 14cc69a..d9ab997 100644
--- a/src/popupmenu.c
+++ b/src/popupmenu.c
@@ -414,7 +414,7 @@
int_u char_pos = 0;
int is_select = FALSE;
- if ((hlf != HLF_PSI && hlf != HLF_PNI)
+ if (*text == NUL || (hlf != HLF_PSI && hlf != HLF_PNI)
|| (highlight_attr[HLF_PMSI] == highlight_attr[HLF_PSI]
&& highlight_attr[HLF_PMNI] == highlight_attr[HLF_PNI]))
return NULL;
@@ -662,131 +662,129 @@
if (s == NULL)
s = p;
w = ptr2cells(p);
- if (*p == NUL || *p == TAB || totwidth + w > pum_width)
+ if (*p != NUL && *p != TAB && totwidth + w <= pum_width)
{
- // Display the text that fits or comes before a Tab.
- // First convert it to printable characters.
- char_u *st;
- int *attrs = NULL;
- int saved = *p;
+ width += w;
+ continue;
+ }
- if (saved != NUL)
- *p = NUL;
- st = transstr(s);
- if (saved != NUL)
- *p = saved;
+ // Display the text that fits or comes before a Tab.
+ // First convert it to printable characters.
+ char_u *st;
+ int *attrs = NULL;
+ int saved = *p;
- if (item_type == CPT_ABBR)
- attrs = pum_compute_text_attrs(st, hlf,
- pum_array[idx].pum_user_abbr_hlattr);
+ if (saved != NUL)
+ *p = NUL;
+ st = transstr(s);
+ if (saved != NUL)
+ *p = saved;
+
+ if (item_type == CPT_ABBR)
+ attrs = pum_compute_text_attrs(st, hlf,
+ pum_array[idx].pum_user_abbr_hlattr);
#ifdef FEAT_RIGHTLEFT
- if (pum_rl)
+ if (pum_rl)
+ {
+ if (st != NULL)
{
- if (st != NULL)
+ char_u *rt = reverse_text(st);
+
+ if (rt != NULL)
{
- char_u *rt = reverse_text(st);
+ char_u *rt_start = rt;
+ int cells;
- if (rt != NULL)
+ cells = vim_strsize(rt);
+ if (cells > pum_width)
{
- char_u *rt_start = rt;
- int cells;
-
- cells = vim_strsize(rt);
- if (cells > pum_width)
+ do
{
- do
- {
- cells -= has_mbyte
+ cells -= has_mbyte
? (*mb_ptr2cells)(rt) : 1;
- MB_PTR_ADV(rt);
- } while (cells > pum_width);
+ MB_PTR_ADV(rt);
+ } while (cells > pum_width);
- if (cells < pum_width)
- {
- // Most left character requires
- // 2-cells but only 1 cell is
- // available on screen. Put a
- // '<' on the left of the pum
- // item
- *(--rt) = '<';
- cells++;
- }
- }
-
- if (attrs == NULL)
- screen_puts_len(rt, (int)STRLEN(rt),
- row, col - cells + 1, attr);
- else
- pum_screen_puts_with_attrs(row,
- col - cells + 1, cells, rt,
- (int)STRLEN(rt), attrs);
-
- vim_free(rt_start);
- }
- vim_free(st);
- }
- col -= width;
- }
- else
-#endif
- {
- if (st != NULL)
- {
- int size = (int)STRLEN(st);
- int cells = (*mb_string2cells)(st, size);
-
- // only draw the text that fits
- while (size > 0
- && col + cells > pum_width + pum_col)
- {
- --size;
- if (has_mbyte)
+ if (cells < pum_width)
{
- size -= (*mb_head_off)(st, st + size);
- cells -= (*mb_ptr2cells)(st + size);
+ // Most left character requires 2-cells
+ // but only 1 cell is available on
+ // screen. Put a '<' on the left of
+ // the pum item.
+ *(--rt) = '<';
+ cells++;
}
- else
- --cells;
}
if (attrs == NULL)
- screen_puts_len(st, size, row, col, attr);
+ screen_puts_len(rt, (int)STRLEN(rt), row,
+ col - cells + 1, attr);
else
- pum_screen_puts_with_attrs(row, col, cells,
- st, size, attrs);
+ pum_screen_puts_with_attrs(row,
+ col - cells + 1, cells, rt,
+ (int)STRLEN(rt), attrs);
- vim_free(st);
+ vim_free(rt_start);
}
- col += width;
+ vim_free(st);
}
-
- if (attrs != NULL)
- VIM_CLEAR(attrs);
-
- if (*p != TAB)
- break;
-
- // Display two spaces for a Tab.
-#ifdef FEAT_RIGHTLEFT
- if (pum_rl)
- {
- screen_puts_len((char_u *)" ", 2, row, col - 1,
- attr);
- col -= 2;
- }
- else
-#endif
- {
- screen_puts_len((char_u *)" ", 2, row, col,
- attr);
- col += 2;
- }
- totwidth += 2;
- s = NULL; // start text at next char
- width = 0;
+ col -= width;
}
else
- width += w;
+#endif
+ {
+ if (st != NULL)
+ {
+ int size = (int)STRLEN(st);
+ int cells = (*mb_string2cells)(st, size);
+
+ // only draw the text that fits
+ while (size > 0
+ && col + cells > pum_width + pum_col)
+ {
+ --size;
+ if (has_mbyte)
+ {
+ size -= (*mb_head_off)(st, st + size);
+ cells -= (*mb_ptr2cells)(st + size);
+ }
+ else
+ --cells;
+ }
+
+ if (attrs == NULL)
+ screen_puts_len(st, size, row, col, attr);
+ else
+ pum_screen_puts_with_attrs(row, col, cells,
+ st, size, attrs);
+
+ vim_free(st);
+ }
+ col += width;
+ }
+
+ if (attrs != NULL)
+ VIM_CLEAR(attrs);
+
+ if (*p != TAB)
+ break;
+
+ // Display two spaces for a Tab.
+#ifdef FEAT_RIGHTLEFT
+ if (pum_rl)
+ {
+ screen_puts_len((char_u *)" ", 2, row, col - 1, attr);
+ col -= 2;
+ }
+ else
+#endif
+ {
+ screen_puts_len((char_u *)" ", 2, row, col, attr);
+ col += 2;
+ }
+ totwidth += 2;
+ s = NULL; // start text at next char
+ width = 0;
}
if (j > 0)
diff --git a/src/testdir/dumps/Test_pum_highlights_19.dump b/src/testdir/dumps/Test_pum_highlights_19.dump
new file mode 100644
index 0000000..4cb3534
--- /dev/null
+++ b/src/testdir/dumps/Test_pum_highlights_19.dump
@@ -0,0 +1,20 @@
+|f+0&#ffffff0|o@1> @71
+|f+0#00e0e07#e0e0e08|o@1|b+0#0000001&|a|r| @3|!| @3| +0#4040ff13#ffffff0@59
+|f+0#0000e05#ffd7ff255|o@1|b+0#0000001&|a|z| @3|!| @3| +0#4040ff13#ffffff0@59
+|~| @73
+|~| @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| |2| +0#0000000&@34
diff --git a/src/testdir/test_popup.vim b/src/testdir/test_popup.vim
index d4d1c2f..fb5d07b 100644
--- a/src/testdir/test_popup.vim
+++ b/src/testdir/test_popup.vim
@@ -1528,6 +1528,39 @@
call StopVimInTerminal(buf)
endfunc
+func Test_pum_highlights_match_with_abbr()
+ CheckScreendump
+ let lines =<< trim END
+ func Omni_test(findstart, base)
+ if a:findstart
+ return col(".")
+ endif
+ return {
+ \ 'words': [
+ \ { 'word': 'foobar', 'abbr': "foobar\t\t!" },
+ \ { 'word': 'foobaz', 'abbr': "foobaz\t\t!" },
+ \]}
+ endfunc
+
+ set omnifunc=Omni_test
+ set completeopt=menuone,noinsert
+ hi PmenuMatchSel ctermfg=6 ctermbg=7
+ 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, "foo")
+ call VerifyScreenDump(buf, 'Test_pum_highlights_19', {})
+
+ call term_sendkeys(buf, "\<C-E>\<Esc>")
+ call TermWait(buf)
+
+ call StopVimInTerminal(buf)
+endfunc
+
func Test_pum_user_abbr_hlgroup()
CheckScreendump
let lines =<< trim END
diff --git a/src/version.c b/src/version.c
index b7a22dd..a008d32 100644
--- a/src/version.c
+++ b/src/version.c
@@ -705,6 +705,8 @@
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
+ 1011,
+/**/
1010,
/**/
1009,