From bec23ef65c9a488959ae31e6d372f0c33abc7920 Mon Sep 17 00:00:00 2001 From: Foxe Chen Date: Fri, 15 May 2026 15:45:11 +0000 Subject: [PATCH] patch 9.2.0484: TextPutPre triggers clipboard provider callback twice Problem: TextPutPre triggers clipboard provider callback twice when do_put() runs autocommands that themselves request the clipboard. Solution: Guard do_put() and put_do_autocmd() with inc_clip_provider()/dec_clip_provider() so the provider is queried at most once per put operation (Foxe Chen). closes: #20215 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- src/register.c | 32 +++++++++++++++++++++-- src/testdir/test_eval_stuff.vim | 46 +++++++++++++++++++++++++++++++++ src/version.c | 2 ++ 3 files changed, 78 insertions(+), 2 deletions(-) diff --git a/src/register.c b/src/register.c index 86ac02cfc1..9daa7729e8 100644 --- a/src/register.c +++ b/src/register.c @@ -1196,6 +1196,16 @@ put_do_autocmd( if (list == NULL) return; + // Make sure regcontents will be up to date +# ifdef FEAT_CLIPBOARD_PROVIDER + inc_clip_provider(); + call_clip_provider_request(regname); +# endif +# ifdef FEAT_CLIPBOARD + if (clipmethod != CLIPMETHOD_PROVIDER) + regname = may_get_selection(regname); +# endif + if (regname == '.') { if (last_insert_ga.ga_data != NULL) @@ -1231,6 +1241,9 @@ put_do_autocmd( (void)dict_add_string_len(v_event, "operator", buf, (int)buflen); add_regtype_to_dict(regname, v_event, buf, sizeof(buf)); +# ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +# endif (void)dict_add_bool(v_event, "visual", VIsual_active); @@ -1713,6 +1726,7 @@ do_put( adjust_clip_reg(®name); #endif #ifdef FEAT_CLIPBOARD_PROVIDER + inc_clip_provider(); call_clip_provider_request(regname); #endif #ifdef FEAT_CLIPBOARD @@ -1749,6 +1763,11 @@ do_put( // TextPutPost after TextPutPre. if (has_textputpre()) put_do_autocmd('.', NULL, NULL, false, dir); +#endif +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif +#ifdef FEAT_EVAL if (has_textputpost()) put_do_autocmd('.', NULL, NULL, true, dir); @@ -1771,7 +1790,12 @@ do_put( insert_string.string = expr_result; else if (get_spec_reg(regname, &insert_string.string, &allocated, TRUE) && insert_string.string == NULL) + { +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif return; + } // Autocommands may be executed when saving lines for undo. This might // make "y_array" invalid, so we start undo now to avoid that. @@ -2001,8 +2025,8 @@ do_put( // move to start of next multi-byte character curwin->w_cursor.col += (*mb_ptr2len)(ml_get_cursor()); else - if (c != TAB || cur_ve_flags != VE_ALL) - ++curwin->w_cursor.col; + if (c != TAB || cur_ve_flags != VE_ALL) + ++curwin->w_cursor.col; ++col; } else @@ -2521,6 +2545,10 @@ end: curbuf->b_op_end = orig_end; } +#ifdef FEAT_CLIPBOARD_PROVIDER + dec_clip_provider(); +#endif + #ifdef FEAT_EVAL if (has_textputpost()) { diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim index 39b1227aef..c3d98b96e3 100644 --- a/src/testdir/test_eval_stuff.vim +++ b/src/testdir/test_eval_stuff.vim @@ -1127,6 +1127,52 @@ func Test_clipboard_provider_accessed_once() bw! + new + " Emitting TextPutPre/TextPutPost/TextYankPost may cause a clipboard access + " + " Note that TextPutPost will always cause a second clipboard access, since a + " TextPutPre may have changed the clipboard, meaning another "paste" call is + " needed to make sure everything is up to date. + augroup TextAutocmd + autocmd! + autocmd TextPutPost * let g:putpost = 1 + autocmd TextPutPre * let g:putpre = 1 + autocmd TextYankPost * let g:yankpost = 1 + augroup END + + let g:putpost = 0 + let g:putpre = 0 + let g:yankpost = 0 + + let g:vim_paste_count = {'*': 0, '+': 0} + let g:vim_copy_count = {'*': 0, '+': 0} + + call setline(1, "hello world!") + + yank + + + yank * + + put + + + put * + + call assert_equal(2, g:vim_paste_count['+']) + call assert_equal(1, g:vim_copy_count['+']) + + call assert_equal(2, g:vim_paste_count['*']) + call assert_equal(1, g:vim_copy_count['*']) + + call assert_equal(1, g:putpost) + call assert_equal(1, g:putpre) + call assert_equal(1, g:yankpost) + + bw! + unlet g:putpost + unlet g:putpre + unlet g:yankpost + autocmd! TextAutocmd + set clipmethod& set clipboard& endfunc diff --git a/src/version.c b/src/version.c index acf47f67a3..2602bccd37 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 */ +/**/ + 484, /**/ 483, /**/