From: Foxe Chen Date: Mon, 15 Dec 2025 20:45:07 +0000 (+0100) Subject: patch 9.1.1984: terminal OSC52 support can be improved X-Git-Tag: v9.1.1984^0 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;p=thirdparty%2Fvim.git patch 9.1.1984: terminal OSC52 support can be improved Problem: terminal OSC52 support to access the clipboard can be improved Solution: Include and package the optional osc52 package, note: this requires a Vim with clipboard provider feature (Foxe Chen). related: #14995 closes: #18575 Signed-off-by: Foxe Chen Signed-off-by: Christian Brabandt --- diff --git a/Filelist b/Filelist index 4441d4cb4f..7c8d1dd9e4 100644 --- a/Filelist +++ b/Filelist @@ -856,7 +856,11 @@ RT_ALL = \ runtime/pack/dist/opt/netrw/autoload/netrw_gitignore.vim \ runtime/pack/dist/opt/netrw/doc/netrw.txt \ runtime/pack/dist/opt/netrw/plugin/netrwPlugin.vim \ - runtime/pack/dist/opt/netrw/syntax/netrw.vim + runtime/pack/dist/opt/netrw/syntax/netrw.vim \ + runtime/pack/dist/opt/osc52/plugin/osc52.vim \ + runtime/pack/dist/opt/osc52/autoload/osc52.vim \ + runtime/pack/dist/opt/osc52/doc/osc52.txt \ + runtime/pack/dist/opt/osc52/doc/tags # Runtime files for all distributions without CR/LF translation. RT_ALL_BIN = \ diff --git a/runtime/doc/tags b/runtime/doc/tags index 22b4702363..8b5d11a338 100644 --- a/runtime/doc/tags +++ b/runtime/doc/tags @@ -9616,6 +9616,7 @@ os_risc.txt os_risc.txt /*os_risc.txt* os_unix.txt os_unix.txt /*os_unix.txt* os_vms.txt os_vms.txt /*os_vms.txt* os_win32.txt os_win32.txt /*os_win32.txt* +osc52-install usr_05.txt /*osc52-install* other-features vi_diff.txt /*other-features* out_buf channel.txt /*out_buf* out_cb channel.txt /*out_cb* @@ -9640,6 +9641,7 @@ package-justify usr_25.txt /*package-justify* package-matchit usr_05.txt /*package-matchit* package-nohlsearch usr_05.txt /*package-nohlsearch* package-open eval.txt /*package-open* +package-osc52 usr_05.txt /*package-osc52* package-termdebug terminal.txt /*package-termdebug* package-translate_example repeat.txt /*package-translate_example* package-translation repeat.txt /*package-translation* diff --git a/runtime/doc/usr_05.txt b/runtime/doc/usr_05.txt index 32f8431d36..c528c09a85 100644 --- a/runtime/doc/usr_05.txt +++ b/runtime/doc/usr_05.txt @@ -1,4 +1,4 @@ -*usr_05.txt* For Vim version 9.1. Last change: 2025 Nov 09 +*usr_05.txt* For Vim version 9.1. Last change: 2025 Dec 15 VIM USER MANUAL by Bram Moolenaar @@ -399,13 +399,17 @@ The ":map" command (with no arguments) lists your current mappings. At least the ones for Normal mode. More about mappings in section |40.1|. ============================================================================== -*05.5* Adding a package *add-package* *matchit-install* *package-matchit* +*05.5* Adding a package *add-package* A package is a set of files that you can add to Vim. There are two kinds of packages: optional and automatically loaded on startup. The Vim distribution comes with a few packages that you can optionally use. -For example, the matchit plugin. This plugin makes the "%" command jump to + +------------------------------------------------------------------------------ +Adding the matchit package *matchit-install* *package-matchit* +------------------------------------------------------------------------------ +For example, the matchit package This plugin makes the "%" command jump to matching HTML tags, if/else/endif in Vim scripts, etc. Very useful, although it's not backwards compatible (that's why it is not enabled by default). @@ -434,7 +438,9 @@ an archive or as a repository. For an archive you can follow these steps: Here "fancytext" is the name of the package, it can be anything else. +------------------------------------------------------------------------------ Adding the editorconfig package *editorconfig-install* *package-editorconfig* +------------------------------------------------------------------------------ Similar to the matchit package, to load the distributed editorconfig plugin when Vim starts, add the following line to your vimrc file: > @@ -444,7 +450,9 @@ After restarting your Vim, the plugin is active and you can read about it at: > :h editorconfig.txt +------------------------------------------------------------------------------ Adding the comment package *comment-install* *package-comment* +------------------------------------------------------------------------------ Load the plugin with this command: > packadd comment @@ -457,7 +465,9 @@ the package loaded. Once the package is loaded, read about it at: > :h comment.txt +------------------------------------------------------------------------------ Adding the nohlsearch package *nohlsearch-install* *package-nohlsearch* +------------------------------------------------------------------------------ Load the plugin with this command: > packadd nohlsearch @@ -471,7 +481,9 @@ To disable the effect of the plugin after it has been loaded: > au! nohlsearch < +------------------------------------------------------------------------------ Adding the highlight-yank package *hlyank-install* *package-hlyank* +------------------------------------------------------------------------------ Load the plugin with this command: > packadd hlyank @@ -497,6 +509,20 @@ To highlight in visual mode, use: > To disable the effect of the plugin after it has been loaded: > au! hlyank +------------------------------------------------------------------------------ +Adding the osc52 package *osc52-install* *package-osc52* +------------------------------------------------------------------------------ + +Load the plugin with this command: > + packadd osc52 +< +The osc52.vim package provides support for the OSC 52 terminal command, which +allows an application to access the clipboard by communicating directly with +your terminal. + +Once the package is loaded, read about it at: > + :h osc52.txt + More information about packages can be found here: |packages|. ============================================================================== diff --git a/runtime/doc/version9.txt b/runtime/doc/version9.txt index 77a337d447..1071613fea 100644 --- a/runtime/doc/version9.txt +++ b/runtime/doc/version9.txt @@ -1,4 +1,4 @@ -*version9.txt* For Vim version 9.1. Last change: 2025 Dec 14 +*version9.txt* For Vim version 9.1. Last change: 2025 Dec 15 VIM REFERENCE MANUAL by Bram Moolenaar @@ -41627,8 +41627,9 @@ Other new features ~ ------------------ - Support for Super key mappings in GTK using . -- The new packages |package-comment|, |package-nohlsearch|, |package-hlyank| and - |help-TOC| are included. +- The new optional packages |package-comment|, |package-nohlsearch|, + |package-hlyank|, |help-TOC|, |package-helpcurwin| and |package-osc52| are + included. - An interactive tutor plugin has been included |vim-tutor-mode| and can be started via |:Tutor|. diff --git a/runtime/pack/dist/opt/osc52/autoload/osc52.vim b/runtime/pack/dist/opt/osc52/autoload/osc52.vim new file mode 100644 index 0000000000..bd664f17c8 --- /dev/null +++ b/runtime/pack/dist/opt/osc52/autoload/osc52.vim @@ -0,0 +1,92 @@ +vim9script + +export var allowed: bool = false + +export def Available(): bool + if get(g:, 'osc52_force_avail', 0) + return true + endif + return allowed +enddef + +var sent_message: bool = false + +def OSCMessage(id: number) + echom "Waiting for OSC52 response... Press CTRL-C to cancel" + sent_message = true +enddef + +export def Paste(reg: string): tuple> + # 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 + + # 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) + if reg == "+" + echoraw("\]52;c;?\\\") + else + echoraw("\]52;p;?\\\") + endif + + var timerid: number = timer_start(1000, OSCMessage) + var interrupt: bool = false + + # Wait for response from terminal. If we got interrupted (Ctrl-C), then do a + # redraw if we already sent the message, and return an empty string. + try + while true + var key: string = getcharstr(-1, {cursor: "hide"}) + + if key == "\" && match(v:termosc, '52;') != -1 + break + elseif key == "\" + interrupt = true + break + endif + endwhile + + # This doesn't seem to catch Ctrl-C sent via term_sendkeys(), which is used in + # tests. So also check the result of getcharstr()/getchar(). + catch /^Vim:Interrupt$/ + interrupt = true + finally + timer_stop(timerid) + if sent_message + sent_message = false + :redraw! + endif + endtry + + if interrupt + echo "Interrupted while waiting for OSC 52 response" + return ("c", [""]) + endif + + # Extract the base64 stuff + var stuff: string = matchstr(v:termosc, '52;.\+;\zs[A-Za-z0-9+/=]\+') + var decoded: blob + + # "stuff" may be an invalid base64 string, so catch any errors + try + decoded = base64_decode(stuff) + catch /:E475/ + decoded = null_blob + echo "Invalid OSC 52 response received" + endtry + + return ("", blob2str(decoded)) +enddef + +export def Copy(reg: string, type: string, lines: list): void + if reg == "+" + echoraw("\]52;c;" .. base64_encode(str2blob(lines)) .. "\\\") + else + echoraw("\]52;p;" .. base64_encode(str2blob(lines)) .. "\\\") + endif +enddef + +# vim: set sw=2 sts=2 : diff --git a/runtime/pack/dist/opt/osc52/doc/osc52.txt b/runtime/pack/dist/opt/osc52/doc/osc52.txt new file mode 100644 index 0000000000..2d98299b9d --- /dev/null +++ b/runtime/pack/dist/opt/osc52/doc/osc52.txt @@ -0,0 +1,64 @@ +*osc52.txt* For Vim version 9.1. Last change: 2025 Dec 15 + + + VIM REFERENCE MANUAL + +Use the OSC 52 terminal command for clipboard support +============================================================================== + +1. OVERVIEW *osc52-overview* + +The osc52.vim plugin provides support for the OSC 52 terminal command, which +allows an application to access the clipboard by communicating with the +terminal. This is useful in situations such as if you are in a SSH session. + + *osc52-support* +In order for this plugin to work, the terminal Vim is running in must +recognize and handle the OSC 52 escape sequence. You can easily check this +online. Additionally, while yanking is guaranteed to work, some terminals +don't implement the paste functionality. If the terminal doesn't support +pasting, then Vim will just block waiting for the data which will never come. +In this case just press Ctrl-C to cancel the operation. + + *osc52-selections* +Note that this only applies to users on Wayland or X11 platforms + +Some terminals support the selection type parameter in the OSC 52 command. +This originates from X11, and some terminals check this parameter and handle +it accordingly. If your terminal handles this parameter, then the "+" +register corresponds to the regular selection, and the "*" register +corresponds to the primary selection. If your terminal does not handle it, +then it is up to the terminal to handle what selection to use. + +2. HOW TO USE THE PLUGIN *osc52-how-to-use* + +The osc52.vim plugin relies on Vim's clipboard provider functionality, see +|clipboard-providers|. In short, add these commands to your vimrc to get +everything working: >vim + packadd osc52 + set clipmethod+=osc52 +< +This will make the osc52.vim provider the last resort if there are other +values in |clipmethod|. This allows Vim, for example, to access the system +clipboard directly if it can, but automatically switch to OSC 52 if it cannot +(e.g. in an SSH session). Note that this does not happen when on a platform +that doesn't use |clipmethod| for system clipboard functionality (MacOS, +Windows). If OSC 52 support is detected, then it will always be used if set +in |clipmethod| when it is the only value/method. + + *g:osc52_force_avail* +In most cases, the plugin should automatically detect and work if your +terminal supports the OSC 52 command. Internally, it does this via a Primary +Device Attributes (DA1) query. You may force enable the plugin by setting +|g:osc52_force_avail| to true. You may check if the osc52.vim plugin is being +used if the value of |v:clipmethod| is "osc52". Note that using a terminal +multiplexer such as tmux, may prevent automatic OSC 52 detection. + + *g:osc52_disable_paste* +If your terminal does not support pasting via OSC 52, or has it disabled, then +it is a good idea to set g:osc52_disable_paste to TRUE. This will cause an +empty string to be returned when Vim attempts to query the osc52.vim provider, +instead of doing a blocking wait, as said in |osc52-support|. + +============================================================================== +vim:tw=78:ts=8:fo=tcq2:ft=help: diff --git a/runtime/pack/dist/opt/osc52/doc/tags b/runtime/pack/dist/opt/osc52/doc/tags new file mode 100644 index 0000000000..9d723973b6 --- /dev/null +++ b/runtime/pack/dist/opt/osc52/doc/tags @@ -0,0 +1,7 @@ +g:osc52_disable_paste osc52.txt /*g:osc52_disable_paste* +g:osc52_force_avail osc52.txt /*g:osc52_force_avail* +osc52-how-to-use osc52.txt /*osc52-how-to-use* +osc52-overview osc52.txt /*osc52-overview* +osc52-selections osc52.txt /*osc52-selections* +osc52-support osc52.txt /*osc52-support* +osc52.txt osc52.txt /*osc52.txt* diff --git a/runtime/pack/dist/opt/osc52/plugin/osc52.vim b/runtime/pack/dist/opt/osc52/plugin/osc52.vim new file mode 100644 index 0000000000..ddec41ed17 --- /dev/null +++ b/runtime/pack/dist/opt/osc52/plugin/osc52.vim @@ -0,0 +1,45 @@ +vim9script + +# Vim plugin for OSC52 clipboard support +# +# Maintainer: The Vim Project +# Last Change: 2025 October 14 + +if !has("timers") + finish +endif + +import autoload "../autoload/osc52.vim" as osc + +v:clipproviders["osc52"] = { + "available": osc.Available, + "paste": { + "*": osc.Paste, + "+": osc.Paste + }, + "copy": { + "*": osc.Copy, + "+": osc.Copy + }, +} + +augroup VimOSC52Plugin + autocmd! + # Query support for OSC 52 using a DA1 query + autocmd TermResponseAll da1 { + if match(v:termda1, '?\zs.*52\ze') != -1 + osc.allowed = true + :silent! clipreset + else + osc.allowed = false + :silent! clipreset + endif + } + autocmd VimEnter * { + if !has("gui_running") && !get(g:, 'osc52_force_avail', 0) + echoraw("\[c") + endif + } +augroup END + +# vim: set sw=2 sts=2 : diff --git a/src/testdir/Make_all.mak b/src/testdir/Make_all.mak index 97ad9089a8..9c0d11585a 100644 --- a/src/testdir/Make_all.mak +++ b/src/testdir/Make_all.mak @@ -249,6 +249,7 @@ NEW_TESTS = \ test_plugin_man \ test_plugin_matchparen \ test_plugin_netrw \ + test_plugin_osc52 \ test_plugin_tar \ test_plugin_termdebug \ test_plugin_tohtml \ @@ -524,6 +525,7 @@ NEW_TESTS_RES = \ test_plugin_man.res \ test_plugin_matchparen.res \ test_plugin_netrw.res \ + test_plugin_osc52.res \ test_plugin_tar.res \ test_plugin_termdebug.res \ test_plugin_tohtml.res \ diff --git a/src/testdir/dumps/Test_osc52_paste_01.dump b/src/testdir/dumps/Test_osc52_paste_01.dump new file mode 100644 index 0000000000..c0c562802d --- /dev/null +++ b/src/testdir/dumps/Test_osc52_paste_01.dump @@ -0,0 +1,20 @@ +> +0&#ffffff0@74 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|W+0#0000000&|a|i|t|i|n|g| |f|o|r| |O|S|C|5|2| |r|e|s|.@2|P|r|e|s@1| |C|T|R|L|-|C| |t|o| |c|a|n|c|e|l| @10|0|,|0|-|1| @8|A|l@1| diff --git a/src/testdir/dumps/Test_osc52_paste_02.dump b/src/testdir/dumps/Test_osc52_paste_02.dump new file mode 100644 index 0000000000..8c53837f4b --- /dev/null +++ b/src/testdir/dumps/Test_osc52_paste_02.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +>h|e|l@1|o| @69 +|w|o|r|l|d|!| @68 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +| +0#0000000&@56|2|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_osc52_paste_03.dump b/src/testdir/dumps/Test_osc52_paste_03.dump new file mode 100644 index 0000000000..1557941f22 --- /dev/null +++ b/src/testdir/dumps/Test_osc52_paste_03.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +>h|e|l@1|o| @69 +|w|o|r|l|d|!| @68 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|I+0#0000000&|n|v|a|l|i|d| |O|S|C| |5|2| |r|e|s|p|o|n|s|e| |r|e|c|e|i|v|e|d| @24|2|,|1| @10|A|l@1| diff --git a/src/testdir/dumps/Test_osc52_paste_04.dump b/src/testdir/dumps/Test_osc52_paste_04.dump new file mode 100644 index 0000000000..724cd2ca4e --- /dev/null +++ b/src/testdir/dumps/Test_osc52_paste_04.dump @@ -0,0 +1,20 @@ +| +0&#ffffff0@74 +>h|e|l@1|o| @69 +|w|o|r|l|d|!| @68 +|~+0#4040ff13&| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|~| @73 +|I+0#0000000&|n|t|e|r@1|u|p|t|e|d| |w|h|i|l|e| |w|a|i|t|i|n|g| |f|o|r| |O|S|C| |5|2| |r|e|s|p|o|n|s|e| @11|2|,|1| @10|A|l@1| diff --git a/src/testdir/test_plugin_osc52.vim b/src/testdir/test_plugin_osc52.vim new file mode 100644 index 0000000000..fff40936ad --- /dev/null +++ b/src/testdir/test_plugin_osc52.vim @@ -0,0 +1,95 @@ +" Test for the OSC 52 plugin + +CheckRunVimInTerminal +" Does not run on BSD CI test runner +CheckNotBSD + +source util/screendump.vim + +" Check if plugin correctly detects OSC 52 support if possible +func Test_osc52_detect() + let lines =<< trim END + packadd osc52 + set clipmethod=osc52 + END + call writefile(lines, "Xosc52.vim", "D") + defer delete("Xosc52result") + + let buf = RunVimInTerminal("-S Xosc52.vim", {}) + + " The plugin creates an autocmd listening for DA1 responses + + " No support + call term_sendkeys(buf, "\[?62;22;c") + call TermWait(buf) + + call term_sendkeys(buf, + \ "\:call writefile([v:termda1, v:clipmethod], 'Xosc52result')\") + call TermWait(buf) + call WaitForAssert({-> + \ assert_equal(["\[?62;22;c", "none"], readfile('Xosc52result'))}) + + " Yes support + call term_sendkeys(buf, "\[?62;2;3;4;1;52;c") + call TermWait(buf) + call term_sendkeys(buf, + \ "\:call writefile([v:termda1, v:clipmethod], 'Xosc52result')\") + call TermWait(buf) + call WaitForAssert({-> assert_equal(["\[?62;2;3;4;1;52;c", "osc52"], + \ readfile('Xosc52result'))}) + + call StopVimInTerminal(buf) +endfunc + +" Test if pasting works +func Test_osc52_paste() + CheckScreendump + + let lines =<< trim END + packadd osc52 + set clipmethod=osc52 + redraw! + END + call writefile(lines, "Xosc52.vim", "D") + + let buf = RunVimInTerminal("-S Xosc52.vim", {}) + + call term_sendkeys(buf, "\[?52;c") + call TermWait(buf) + + call term_sendkeys(buf, "\"+p") + call TermWait(buf) + + " Check to see if message is shown after a second of waiting for a response + sleep 1500m + call VerifyScreenDump(buf, 'Test_osc52_paste_01', {}) + + call term_sendkeys(buf, "\]52;c;" .. + \ base64_encode(str2blob(["hello", "world!"])) .. "\\\") + call TermWait(buf) + + " Check if message is gone + call VerifyScreenDump(buf, 'Test_osc52_paste_02', {}) + + " Test when invalid base64 content received (should emit a message) + call term_sendkeys(buf, "\"+p") + call TermWait(buf) + + call term_sendkeys(buf, "\]52;c;abc\\\") + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_osc52_paste_03', {}) + + " Test if interrupt is handled and message is outputted + call term_sendkeys(buf, "\"+p") + call TermWait(buf) + + call term_sendkeys(buf, "\") + call TermWait(buf) + + call VerifyScreenDump(buf, 'Test_osc52_paste_04', {}) + + call StopVimInTerminal(buf) +endfunc + +" vim: shiftwidth=2 sts=2 expandtab diff --git a/src/version.c b/src/version.c index e1ea81c6bd..7539181d91 100644 --- a/src/version.c +++ b/src/version.c @@ -734,6 +734,8 @@ static char *(features[]) = static int included_patches[] = { /* Add new patch number below this line */ +/**/ + 1984, /**/ 1983, /**/