patch 9.2.0485: clipboard provider callback can be called recursively

Commit: 
https://github.com/vim/vim/commit/e85e3e5d8547cf83375fcb88803940e74df15bae
Author: Foxe Chen <[email protected]>
Date:   Fri May 15 16:00:04 2026 +0000

    patch 9.2.0485: clipboard provider callback can be called recursively
    
    Problem:  clipboard provider callback can be called recursively, leading
              to E132: Function call depth is higher than 'maxfuncdepth'
    Solution: Prevent recursive calls of
              clip_provider_copy()/clip_provider_paste() (Foxe Chen).
    
    closes: #20213
    
    Signed-off-by: Foxe Chen <[email protected]>
    Signed-off-by: Christian Brabandt <[email protected]>

diff --git a/runtime/doc/eval.txt b/runtime/doc/eval.txt
index 71585da89..2bcad93a3 100644
--- a/runtime/doc/eval.txt
+++ b/runtime/doc/eval.txt
@@ -5346,6 +5346,9 @@ a |lambda| expression.
 With the exception of the "available" callback if a callback is not provided,
 Vim will not invoke anything, and this is not an error.
 
+If the "paste" or "copy" callbacks are triggered recursively, then they will
+not be called.
+
                                                *clipboard-providers-textlock*
 In both the "paste" and "copy" callbacks, it is not allowed to change the
 buffer text, see |textlock|.
diff --git a/src/clipboard.c b/src/clipboard.c
index e47c9a5ad..745a8c47a 100644
--- a/src/clipboard.c
+++ b/src/clipboard.c
@@ -3428,6 +3428,7 @@ clip_provider_get_callback(
     static void
 clip_provider_copy(char_u *reg, char_u *provider)
 {
+    static bool        recursive = false;
     callback_T callback;
     typval_T   rettv;
     typval_T   argvars[4];
@@ -3435,6 +3436,9 @@ clip_provider_copy(char_u *reg, char_u *provider)
     char_u     type[2 + NUMBUFLEN] = {0};
     list_T     *list = NULL;
 
+    if (recursive)
+       return;
+
     if (clip_provider_get_callback(
                reg,
                provider,
@@ -3497,7 +3501,9 @@ clip_provider_copy(char_u *reg, char_u *provider)
     argvars[3].v_type = VAR_UNKNOWN;
 
     textlock++;
+    recursive = true;
     call_callback(&callback, -1, &rettv, 3, argvars);
+    recursive = false;
     clear_tv(&rettv);
     textlock--;
 
@@ -3508,6 +3514,7 @@ clip_provider_copy(char_u *reg, char_u *provider)
     static void
 clip_provider_paste(char_u *reg, char_u *provider)
 {
+    static bool        recursive = false;
     callback_T callback;
     typval_T   argvars[2];
     typval_T   rettv;
@@ -3515,6 +3522,9 @@ clip_provider_paste(char_u *reg, char_u *provider)
     char_u     *reg_type;
     list_T     *lines;
 
+    if (recursive)
+       return;
+
     if (clip_provider_get_callback(
                reg,
                provider,
@@ -3528,7 +3538,9 @@ clip_provider_paste(char_u *reg, char_u *provider)
     argvars[1].v_type = VAR_UNKNOWN;
 
     textlock++;
+    recursive = true;
     ret = call_callback(&callback, -1, &rettv, 1, argvars);
+    recursive = false;
     textlock--;
 
     if (ret == FAIL)
diff --git a/src/testdir/test_eval_stuff.vim b/src/testdir/test_eval_stuff.vim
index c3d98b96e..e1afbfbe2 100644
--- a/src/testdir/test_eval_stuff.vim
+++ b/src/testdir/test_eval_stuff.vim
@@ -765,7 +765,10 @@ func s:Paste(reg)
     else
       return ("c", [])
     endif
+  endif
 
+  if exists("g:vim_paste_recursive")
+    call getreg(a:reg)
   endif
 endfunc
 
@@ -773,6 +776,9 @@ func s:Copy(reg, type, lines)
   if exists("g:vim_copy_count")
     let g:vim_copy_count[a:reg] += 1
   endif
+  if exists("g:vim_copy_recursive")
+    call setreg(a:reg, a:lines)
+  endif
 
   let g:vim_copy = {
         \ "reg": a:reg,
@@ -1349,4 +1355,34 @@ func Test_clipboard_provider_clipboard_option()
   set clipboard&
 endfunc
 
+" Test that callback aren't called recursively
+func Test_clipboard_provider_recursive()
+  let v:clipproviders["test"] = {
+        \ "paste": {
+        \       '+': function("s:Paste"),
+        \       '*': function("s:Paste")
+        \   },
+        \ "copy": {
+        \       '+': function("s:Copy"),
+        \       '*': function("s:Copy")
+        \   }
+        \ }
+  set clipmethod=test
+
+  let g:vim_paste = "count"
+  let g:vim_paste_count = {'*': 0, '+': 0}
+  let g:vim_copy_count = {'*': 0, '+': 0}
+  let g:vim_paste_recursive = 1
+  let g:vim_copy_recursive = 1
+
+  call getreg('+')
+  call assert_equal(1, g:vim_paste_count['+'])
+  call setreg('+', 'test')
+  call assert_equal(1, g:vim_copy_count['+'])
+
+  set clipmethod&
+  unlet g:vim_paste_recursive
+  unlet g:vim_copy_recursive
+endfunc
+
 " vim: shiftwidth=2 sts=2 expandtab
diff --git a/src/version.c b/src/version.c
index 2602bccd3..fa746a9ae 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 */
+/**/
+    485,
 /**/
     484,
 /**/

-- 
-- 
You received this message from the "vim_dev" maillist.
Do not top-post! Type your reply below the text you are replying to.
For more information, visit http://www.vim.org/maillist.php

--- 
You received this message because you are subscribed to the Google Groups 
"vim_dev" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion visit 
https://groups.google.com/d/msgid/vim_dev/E1wNvBo-006TRV-4o%40256bit.org.

Raspunde prin e-mail lui