Commit: patch 9.2.0485: clipboard provider callback can be called recursively

2 views
Skip to first unread message

Christian Brabandt

unread,
May 15, 2026, 12:15:13 PM (5 days ago) May 15
to vim...@googlegroups.com
patch 9.2.0485: clipboard provider callback can be called recursively

Commit: https://github.com/vim/vim/commit/e85e3e5d8547cf83375fcb88803940e74df15bae
Author: Foxe Chen <chen...@gmail.com>
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 <chen...@gmail.com>
Signed-off-by: Christian Brabandt <c...@256bit.org>

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,
/**/
Reply all
Reply to author
Forward
0 new messages