patch 9.2.0599: popup: title set with popup_setoptions() is not shown
Commit:
https://github.com/vim/vim/commit/949caf9e914d94309026f4899e0aaaf9e341a0d2
Author: Hirohito Higashi <
h.eas...@gmail.com>
Date: Fri Jun 5 11:51:39 2026 +0000
patch 9.2.0599: popup: title set with popup_setoptions() is not shown
Problem: When only the title is changed with popup_setoptions(), the
popup is not redrawn until another event happens, so the new
title does not appear right away.
Solution: Redraw the popup when the title changes. Also allocate the new
title and border highlights before freeing the old ones, so the
current value is not lost on allocation failure.
fixes: #20426
closes: #20430
Co-Authored-By: Claude Opus 4.8 (1M context) <
nor...@anthropic.com>
Signed-off-by: Hirohito Higashi <
h.eas...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/src/popupwin.c b/src/popupwin.c
index 87ea44de5..cae19b908 100644
--- a/src/popupwin.c
+++ b/src/popupwin.c
@@ -55,6 +55,7 @@ typedef struct {
int blend;
int flags;
int zindex;
+ char_u *title;
char_u *scrollbar_highlight;
char_u *thumb_highlight;
char_u *border_highlight[4];
@@ -808,8 +809,13 @@ apply_general_options(win_T *wp, dict_T *dict)
str = dict_get_string(dict, "title", FALSE);
if (str != NULL)
{
- vim_free(wp->w_popup_title);
- wp->w_popup_title = vim_strsave(str);
+ char_u *title = vim_strsave(str);
+
+ if (title != NULL)
+ {
+ vim_free(wp->w_popup_title);
+ wp->w_popup_title = title;
+ }
}
nr = dict_get_bool(dict, "wrap", -1);
@@ -975,16 +981,25 @@ apply_general_options(win_T *wp, dict_T *dict)
str = tv_get_string(&li->li_tv);
if (*str != NUL)
{
- vim_free(wp->w_border_highlight[i]);
- wp->w_border_highlight[i] = vim_strsave(str);
+ char_u *hl = vim_strsave(str);
+
+ if (hl != NULL)
+ {
+ vim_free(wp->w_border_highlight[i]);
+ wp->w_border_highlight[i] = hl;
+ }
}
}
if (list->lv_len == 1 && wp->w_border_highlight[0] != NULL)
for (i = 1; i < 4; ++i)
{
- vim_free(wp->w_border_highlight[i]);
- wp->w_border_highlight[i] =
- vim_strsave(wp->w_border_highlight[0]);
+ char_u *hl = vim_strsave(wp->w_border_highlight[0]);
+
+ if (hl != NULL)
+ {
+ vim_free(wp->w_border_highlight[i]);
+ wp->w_border_highlight[i] = hl;
+ }
}
}
}
@@ -4007,6 +4022,7 @@ popup_save_style(win_T *wp, popup_style_snapshot_T *style)
style->blend = wp->w_popup_blend;
style->flags = wp->w_popup_flags;
style->zindex = wp->w_zindex;
+ style->title = wp->w_popup_title;
style->scrollbar_highlight = wp->w_scrollbar_highlight;
style->thumb_highlight = wp->w_thumb_highlight;
for (i = 0; i < 4; i++)
@@ -4023,6 +4039,7 @@ popup_style_changed(win_T *wp, popup_style_snapshot_T *style)
if (style->firstline != wp->w_firstline
|| style->flags != wp->w_popup_flags
+ || style->title != wp->w_popup_title
|| style->scrollbar_highlight != wp->w_scrollbar_highlight
|| style->thumb_highlight != wp->w_thumb_highlight)
return true;
diff --git a/src/testdir/dumps/Test_popupwin_setoptions_title_1.dump b/src/testdir/dumps/Test_popupwin_setoptions_title_1.dump
new file mode 100644
index 000000000..67e2e6c4f
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_setoptions_title_1.dump
@@ -0,0 +1,10 @@
+> +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @31|╔+0#0000001#ffd7ff255|t|i|t|l|e|-|1|╗| +0#4040ff13#ffffff0@32
+|~| @31|║+0#0000001#ffd7ff255|h|e|l@1|o| @1|║| +0#4040ff13#ffffff0@32
+|~| @31|╚+0#0000001#ffd7ff255|═@6|╝| +0#4040ff13#ffffff0@32
+|~| @73
+|~| @73
+|~| @73
+| +0#0000000&@56|0|,|0|-|1| @8|A|l@1|
diff --git a/src/testdir/dumps/Test_popupwin_setoptions_title_2.dump b/src/testdir/dumps/Test_popupwin_setoptions_title_2.dump
new file mode 100644
index 000000000..939ea7a29
--- /dev/null
+++ b/src/testdir/dumps/Test_popupwin_setoptions_title_2.dump
@@ -0,0 +1,10 @@
+> +0&#ffffff0@74
+|~+0#4040ff13&| @73
+|~| @73
+|~| @31|╔+0#0000001#ffd7ff255|t|i|t|l|e|-|2|╗| +0#4040ff13#ffffff0@32
+|~| @31|║+0#0000001#ffd7ff255|h|e|l@1|o| @1|║| +0#4040ff13#ffffff0@32
+|~| @31|╚+0#0000001#ffd7ff255|═@6|╝| +0#4040ff13#ffffff0@32
+|~| @73
+|~| @73
+|~| @73
+|:+0#0000000&|c|a|l@1| |p|o|p|u|p|_|s|e|t|o|p|t|i|o|n|s|(|g|:|i|d|,| |#|{|t|i|t|l|e|:| |'|t|i|t|l|e|-|2|'|}|)| @7|0|,|0|-|1| @8|A|l@1|
diff --git a/src/testdir/test_popupwin.vim b/src/testdir/test_popupwin.vim
index 11e431246..077c08f61 100644
--- a/src/testdir/test_popupwin.vim
+++ b/src/testdir/test_popupwin.vim
@@ -1753,6 +1753,27 @@ func Test_popup_set_firstline()
call StopVimInTerminal(buf)
endfunc
+func Test_popup_setoptions_title_redraw()
+ CheckScreendump
+
+ " Changing the title with popup_setoptions() must redraw the popup right
+ " away, without waiting for a later event such as a cursor movement.
+ let lines =<< trim END
+ let g:id = popup_create('hello', #{title: 'title-1', border: []})
+ redraw
+ END
+ call writefile(lines, 'XtestPopupTitleRedraw', 'D')
+ let buf = RunVimInTerminal('-S XtestPopupTitleRedraw', #{rows: 10})
+
+ call VerifyScreenDump(buf, 'Test_popupwin_setoptions_title_1', {})
+
+ " No other event happens between popup_setoptions() and the dump.
+ call term_sendkeys(buf, ":call popup_setoptions(g:id, #{title: 'title-2'})\<CR>")
+ call VerifyScreenDump(buf, 'Test_popupwin_setoptions_title_2', {})
+
+ call StopVimInTerminal(buf)
+endfunc
+
" this tests that we don't get stuck with an error in "win_execute()"
func Test_popup_filter_win_execute_error()
CheckScreendump
diff --git a/src/version.c b/src/version.c
index 25903a8ed..669a5cf03 100644
--- a/src/version.c
+++ b/src/version.c
@@ -729,6 +729,8 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
+/**/
+ 599,
/**/
598,
/**/