patch 9.2.0482: runtime(osc52): triggered twice with TextPutPoste autocmd
Commit:
https://github.com/vim/vim/commit/29fa9343444754f6976a91663fd7d4de3a8e1c8e
Author: Foxe Chen <
chen...@gmail.com>
Date: Fri May 15 02:09:24 2026 +0000
patch 9.2.0482: runtime(osc52): triggered twice with TextPutPoste autocmd
Problem: runtime(osc52): triggered twice with TextPutPoste autocmd
Solution: Detect recursive trigger and return null (Foxe Chen)
closes: #20216
Signed-off-by: Foxe Chen <
chen...@gmail.com>
Signed-off-by: Christian Brabandt <
c...@256bit.org>
diff --git a/runtime/pack/dist/opt/osc52/autoload/osc52.vim b/runtime/pack/dist/opt/osc52/autoload/osc52.vim
index 3471329d7..bad4bc388 100644
--- a/runtime/pack/dist/opt/osc52/autoload/osc52.vim
+++ b/runtime/pack/dist/opt/osc52/autoload/osc52.vim
@@ -16,13 +16,20 @@ def OSCMessage(id: number)
sent_message = true
enddef
-export def Paste(reg: string): tuple<string, list<string>>
+var loop_timerid: number = -1
+
+export def Paste(reg: string): any
# Check if user has indicated that the terminal does not support OSC 52 paste
# (or has disabled it)
if get(g:, 'osc52_disable_paste', 0)
return ("c", [])
endif
+ if loop_timerid != -1
+ # This will result in the register being unchanged
+ return null
+ endif
+
# Some terminals like Kitty respect the selection type parameter on both X11
# and Wayland. If the terminal doesn't then the selection type parameter
# should be ignored (no-op)
@@ -61,6 +68,14 @@ export def Paste(reg: string): tuple<string, list<string>>
endif
endtry
+ # A TextPutPost autocmd may cause this function to be called twice, which is
+ # technically intended behaviour, however it is not necessary for this plugin.
+ # To prevent this, return immediately if we have not returned back to the main
+ # loop since the last "paste" call.
+ loop_timerid = timer_start(0, (_) => {
+ loop_timerid = -1
+ })
+
if interrupt
echo "Interrupted while waiting for OSC 52 response"
return ("c", [""])
diff --git a/src/testdir/dumps/Test_osc52_paste_05.dump b/src/testdir/dumps/Test_osc52_paste_05.dump
new file mode 100644
index 000000000..32448367f
--- /dev/null
+++ b/src/testdir/dumps/Test_osc52_paste_05.dump
@@ -0,0 +1,20 @@
+| +0&#ffffff0@74
+|h|e|l@1|o| @69
+>t|e|s|t| @70
+|w|o|r|l|d|!| @68
+|~+0#4040ff13&| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+|~| @73
+| +0#0000000&@56|3|,|1| @10|A|l@1|
diff --git a/src/testdir/test_plugin_osc52.vim b/src/testdir/test_plugin_osc52.vim
index f14d3b8c1..afb13ef90 100644
--- a/src/testdir/test_plugin_osc52.vim
+++ b/src/testdir/test_plugin_osc52.vim
@@ -90,6 +90,20 @@ func Test_osc52_paste()
call VerifyScreenDump(buf, 'Test_osc52_paste_04', {})
+ " Test that TextPutPost (e.g. from hlyank plugin) doesn't make "paste"
+ " callback be called twice for osc52.
+ call term_sendkeys(buf, "\<Esc>:au TextPutPost * let g:test = 1\<CR>")
+ call TermWait(buf)
+
+ call term_sendkeys(buf, "\"+p")
+ call TermWait(buf)
+
+ call term_sendkeys(buf, "\<Esc>]52;c;" ..
+ \ base64_encode(str2blob(["test"])) .. "\<Esc>\")
+ call TermWait(buf)
+
+ call VerifyScreenDump(buf, 'Test_osc52_paste_05', {})
+
call StopVimInTerminal(buf)
endfunc
diff --git a/src/version.c b/src/version.c
index 10a787cac..692c510e6 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 */
+/**/
+ 482,
/**/
481,
/**/