Commit: patch 9.2.0484: TextPutPre triggers clipboard provider callback twice

1 view
Skip to first unread message

Christian Brabandt

unread,
May 15, 2026, 12:00:13 PM (5 days ago) May 15
to vim...@googlegroups.com
patch 9.2.0484: TextPutPre triggers clipboard provider callback twice

Commit: https://github.com/vim/vim/commit/bec23ef65c9a488959ae31e6d372f0c33abc7920
Author: Foxe Chen <chen...@gmail.com>
Date: Fri May 15 15:45:11 2026 +0000

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 <chen...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

diff --git a/src/register.c b/src/register.c
index 86ac02cfc..9daa7729e 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(&regname);
#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 39b1227ae..c3d98b96e 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 acf47f67a..2602bccd3 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,
/**/
Reply all
Reply to author
Forward
0 new messages