Compare commits

..

5 Commits

Author SHA1 Message Date
Yasuhiro Matsumoto
54b6c0c0e7 patch 9.2.0261: terminal: redraws are slow
Problem:  terminal: redraws are slow (Mao-Yining)
Solution: Disable redrawing in handle_movecursor()
          (Yasuhiro Matsumoto)

handle_movecursor callback was calling update_cursor() with redraw=TRUE
on every cursor move inside vterm_input_write(). This triggered
gui_mch_flush() (GdiFlush + DWriteContext_Flush) and TextChangedT
autocmd for each cursor move. ConPTY output contains ~17 cursor moves
per 4KB chunk, each flush taking ~5ms, resulting in 80-110ms per chunk.

Fix by passing FALSE to update_cursor() in handle_movecursor since
write_to_term() already calls update_cursor() with proper redraw after
vterm_input_write() finishes.

Also set vterm_screen_set_damage_merge() to VTERM_DAMAGE_SCROLL so that
damage callbacks are buffered until vterm_screen_flush_damage() instead
of being emitted per cell.

fixes:  #19845
closes: #19846

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2026-03-26 22:12:52 +00:00
Yasuhiro Matsumoto
575961c791 patch 9.2.0260: statusline not redrawn after closing a popup window
Problem:  When a popup window overlapping a status line is closed or
          hidden, the status line is not redrawn, leaving ghost
          artifacts from the popup.
Solution: popup_free() and popup_hide() call
          redraw_all_later(UPD_NOT_VALID) which marks window contents
          for redraw but does not set w_redr_status. The diff-based path
          in may_update_popup_mask() that normally sets w_redr_status
          is skipped when redrawing_all_win is TRUE. Add status_redraw_all
          calls to ensure status lines are properly redrawn
          (Yasuhiro Matsumoto).

closes: #19830

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2026-03-26 21:19:34 +00:00
Yasuhiro Matsumoto
f6e1dd11f4 patch 9.2.0259: tabpanel: corrupted display during scrolling causing flicker
Problem:  tabpanel: corrupted tabpanel during scrolling causing flicker
Solution: When the tabpanel is visible, force a line-by-line redraw in
          win_do_lines() similarly to popup handling (Yasuhiro Matsumoto).

When a vertical tabpanel is visible, terminal scroll operations in
win_do_lines() affect the full screen width, corrupting the tabpanel
area. The tabpanel is then redrawn via redraw_tabpanel, causing visible
flicker. Return FAIL to force line-by-line redraw instead, analogous to
the existing popup_visible check.

closes: #19832

Signed-off-by: Yasuhiro Matsumoto <mattn.jp@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2026-03-26 21:03:46 +00:00
Huihui Huang
b90145672d patch 9.2.0258: memory leak in add_mark()
Problem:  memory leak in add_mark()
Solution: Free lpos in the error case when it hasn't been added to the
          dict yet (Huihui Huang)

closes: #19827

Signed-off-by: Huihui Huang <625173@qq.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2026-03-26 20:59:03 +00:00
Shane Harper
02abcf381f patch 9.2.0257: unnecessary memory allocation in set_callback()
Problem:  Unnecessary memory allocation in set_callback(); after
          set_callback(), callers must manually free the source
          callback's name if cb_free_name is set.
Solution: Refactor set_callback() to re-use the callback name when
          possible to avoid extra memory allocations and clean up so the
          callers do not have to take care themselves (Shane Harper).

closes: #19831

Signed-off-by: Shane Harper <shane@shaneharper.net>
Signed-off-by: Christian Brabandt <cb@256bit.org>
2026-03-26 20:46:52 +00:00
16 changed files with 89 additions and 35 deletions

View File

@@ -373,8 +373,6 @@ f_listener_add(typval_T *argvars, typval_T *rettv)
}
set_callback(&lnr->lr_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
lnr->lr_id = ++next_listener_id;
rettv->vval.v_number = lnr->lr_id;

View File

@@ -3789,8 +3789,6 @@ clip_provider_get_callback(
// func_tv owns the function name, so we must make a copy for the callback
set_callback(callback, &cb);
if (cb.cb_free_name)
vim_free(cb.cb_name);
clear_tv(&func_tv);
return OK;
}

View File

@@ -5289,6 +5289,13 @@ put_callback(callback_T *cb, typval_T *tv)
}
}
static bool
does_callback_own_cb_name(callback_T *cb)
{
// If cb_partial != NULL then *cb->cb_name is owned by the partial.
return cb->cb_partial || cb->cb_free_name;
}
/*
* Make a copy of "src" into "dest", allocating the function name if needed,
* without incrementing the refcount.
@@ -5296,19 +5303,13 @@ put_callback(callback_T *cb, typval_T *tv)
void
set_callback(callback_T *dest, callback_T *src)
{
if (src->cb_partial == NULL)
*dest = *src;
if (!does_callback_own_cb_name(src))
{
// just a function name, make a copy
dest->cb_name = vim_strsave(src->cb_name);
dest->cb_free_name = TRUE;
}
else
{
// cb_name is a pointer into cb_partial
dest->cb_name = src->cb_name;
dest->cb_free_name = FALSE;
}
dest->cb_partial = src->cb_partial;
*src = (callback_T){0};
}
/*

View File

@@ -1699,8 +1699,6 @@ f_prompt_setcallback(typval_T *argvars, typval_T *rettv UNUSED)
free_callback(&buf->b_prompt_callback);
set_callback(&buf->b_prompt_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}
/*
@@ -1728,8 +1726,6 @@ f_prompt_setinterrupt(typval_T *argvars, typval_T *rettv UNUSED)
free_callback(&buf->b_prompt_interrupt);
set_callback(&buf->b_prompt_interrupt, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}

View File

@@ -1484,7 +1484,11 @@ add_mark(list_T *l, char_u *mname, pos_T *pos, int bufnr, char_u *fname)
if (dict_add_string(d, "mark", mname) == FAIL
|| dict_add_list(d, "pos", lpos) == FAIL
|| (fname != NULL && dict_add_string(d, "file", fname) == FAIL))
{
if (lpos->lv_refcount == 0)
list_free(lpos);
return FAIL;
}
return OK;
}

View File

@@ -9239,8 +9239,6 @@ option_set_callback_func(char_u *optval UNUSED, callback_T *optcb UNUSED)
free_callback(optcb);
set_callback(optcb, &cb);
if (cb.cb_free_name)
vim_free(cb.cb_name);
free_tv(tv);
char_u *dot = NULL;

View File

@@ -1037,8 +1037,6 @@ apply_general_options(win_T *wp, dict_T *dict)
{
free_callback(&wp->w_filter_cb);
set_callback(&wp->w_filter_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}
}
nr = dict_get_bool(dict, "mapping", -1);
@@ -1069,9 +1067,6 @@ apply_general_options(win_T *wp, dict_T *dict)
free_callback(&wp->w_close_cb);
set_callback(&wp->w_close_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
return OK;
}
@@ -2527,8 +2522,6 @@ popup_create(typval_T *argvars, typval_T *rettv, create_type_T type)
if (callback.cb_name != NULL)
{
set_callback(&wp->w_filter_cb, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
}
wp->w_p_wrap = 0;
@@ -3040,6 +3033,7 @@ popup_hide(win_T *wp)
if (wp->w_winrow + popup_height(wp) >= cmdline_row)
clear_cmdline = TRUE;
redraw_all_later(UPD_NOT_VALID);
status_redraw_all();
popup_mask_refresh = TRUE;
}
@@ -3203,6 +3197,7 @@ popup_free(win_T *wp)
#endif
redraw_all_later(UPD_NOT_VALID);
status_redraw_all();
popup_mask_refresh = TRUE;
}

View File

@@ -8158,11 +8158,7 @@ qf_setprop_qftf(qf_info_T *qi UNUSED, qf_list_T *qfl, dictitem_T *di)
cb = get_callback(&di->di_tv);
if (cb.cb_name == NULL || *cb.cb_name == NUL)
return OK;
set_callback(&qfl->qf_qftf_cb, &cb);
if (cb.cb_free_name)
vim_free(cb.cb_name);
return OK;
}

View File

@@ -3845,6 +3845,13 @@ win_do_lines(
if (!no_win_do_lines_ins)
clear_cmdline = TRUE;
#if defined(FEAT_TABPANEL)
// Terminal scroll operations affect the full screen width, which would
// corrupt the vertical tabpanel area and cause flicker.
if (tabpanel_width() > 0)
return FAIL;
#endif
/*
* If the terminal can set a scroll region, use that.
* Always do this in a vertically split window. This will redraw from

View File

@@ -63,8 +63,6 @@ get_sound_callback(typval_T *arg)
soundcb->snd_next = first_callback;
first_callback = soundcb;
set_callback(&soundcb->snd_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
return soundcb;
}

View File

@@ -3326,7 +3326,9 @@ handle_movecursor(
position_cursor(wp, &pos);
}
if (term->tl_buffer == curbuf && !term->tl_normal_mode)
update_cursor(term, term->tl_cursor_visible);
// Don't redraw here, it will be done after
// vterm_input_write() is finished.
update_cursor(term, FALSE);
return 1;
}
@@ -4956,6 +4958,7 @@ create_vterm(term_T *term, int rows, int cols)
}
vterm_screen_set_callbacks(screen, &screen_callbacks, term);
vterm_screen_set_damage_merge(screen, VTERM_DAMAGE_SCROLL);
// TODO: depends on 'encoding'.
vterm_set_utf8(vterm, 1);

View File

@@ -0,0 +1,15 @@
>1+0&#ffffff0| @73
|2| @73
|3| @73
|4| @73
|5| @73
|6| @73
|[+3&&|N|o| |p+0#0000001#ffd7ff255|o|p|u|p| |o|v|e|r| |s|t|a|t|u|s|l|i|n|e| +3#0000000#ffffff0@31|1|,|1| @11|T|o|p
|1+0&&| @73
|2| @73
|3| @73
|4| @73
|5| @73
|6| @73
|[+1&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|T|o|p
| +0&&@74

View File

@@ -0,0 +1,15 @@
>1+0&#ffffff0| @73
|2| @73
|3| @73
|4| @73
|5| @73
|6| @73
|[+3&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|T|o|p
|1+0&&| @73
|2| @73
|3| @73
|4| @73
|5| @73
|6| @73
|[+1&&|N|o| |N|a|m|e|]| |[|+|]| @43|1|,|1| @11|T|o|p
|:+0&&|c|a|l@1| |p|o|p|u|p|_|c|l|o|s|e|(|w|i|n|i|d|)| @50

View File

@@ -5080,4 +5080,26 @@ func Test_popup_close_b_nwindows()
call assert_equal(0, bufexists('Xfoo'))
endfunc
func Test_popupwin_close_status_redraw()
CheckScreendump
let lines =<< trim END
split
call setline(1, range(1, 20))
let winid = popup_create('popup over statusline', #{
\ line: &lines / 2,
\ col: 5,
\ })
END
call writefile(lines, 'XtestPopupCloseStatus', 'D')
let buf = RunVimInTerminal('-S XtestPopupCloseStatus', #{rows: 15})
call VerifyScreenDump(buf, 'Test_popupwin_close_status_1', {})
" close the popup and check the status line is redrawn
call term_sendkeys(buf, ":call popup_close(winid)\<CR>")
call VerifyScreenDump(buf, 'Test_popupwin_close_status_2', {})
call StopVimInTerminal(buf)
endfunc
" vim: shiftwidth=2 sts=2

View File

@@ -912,8 +912,6 @@ f_timer_start(typval_T *argvars, typval_T *rettv)
return;
}
set_callback(&timer->tr_callback, &callback);
if (callback.cb_free_name)
vim_free(callback.cb_name);
rettv->vval.v_number = (varnumber_T)timer->tr_id;
}

View File

@@ -734,6 +734,16 @@ static char *(features[]) =
static int included_patches[] =
{ /* Add new patch number below this line */
/**/
261,
/**/
260,
/**/
259,
/**/
258,
/**/
257,
/**/
256,
/**/