]> git.ipfire.org Git - thirdparty/vim.git/commitdiff
patch 9.2.0484: TextPutPre triggers clipboard provider callback twice v9.2.0484
authorFoxe Chen <chen.foxe@gmail.com>
Fri, 15 May 2026 15:45:11 +0000 (15:45 +0000)
committerChristian Brabandt <cb@256bit.org>
Fri, 15 May 2026 15:45:11 +0000 (15:45 +0000)
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.foxe@gmail.com>
Signed-off-by: Christian Brabandt <cb@256bit.org>
src/register.c
src/testdir/test_eval_stuff.vim
src/version.c

index 86ac02cfc153797d8139db7eff073f9958b89cf7..9daa7729e849d11eedc5bda8ee3e9c67a3c4bb8c 100644 (file)
@@ -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())
     {
index 39b1227aefee60f259b56487c31044cfc6d86a19..c3d98b96e3811ac7c4cf623b16468989a9e9508a 100644 (file)
@@ -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
index acf47f67a3eda30b2db40b3c25e37c5068d842bc..2602bccd37a064a6dc7280f988b686f648b3d6fa 100644 (file)
@@ -729,6 +729,8 @@ static char *(features[]) =
 
 static int included_patches[] =
 {   /* Add new patch number below this line */
+/**/
+    484,
 /**/
     483,
 /**/