diff --git a/runtime/doc/news.txt b/runtime/doc/news.txt index fa77e8d086..985adb5b0d 100644 --- a/runtime/doc/news.txt +++ b/runtime/doc/news.txt @@ -208,6 +208,9 @@ The following new APIs and features were added. • The |TermResponse| autocommand event can be used with |v:termresponse| to read escape sequence responses from the terminal. +• A clipboard provider which uses OSC 52 to copy the selection to the system + clipboard is now bundled by default. |clipboard-osc52| + ============================================================================== CHANGED FEATURES *news-changed* diff --git a/runtime/doc/provider.txt b/runtime/doc/provider.txt index 9e70ff8945..1b49ee3a3d 100644 --- a/runtime/doc/provider.txt +++ b/runtime/doc/provider.txt @@ -253,7 +253,35 @@ For Windows WSL, try this g:clipboard definition: \ }, \ 'cache_enabled': 0, \ } +< + *clipboard-osc52* +Nvim bundles a clipboard provider that allows copying to the system clipboard +using OSC 52. OSC 52 is an Operating System Command control sequence that +writes the copied text to the terminal emulator. If the terminal emulator +supports OSC 52 then it will write the copied text into the system clipboard. +This is most useful when using Nvim remotely (e.g. via ssh) as Nvim does not +have direct access to the system clipboard in that case. + +Because not all terminal emulators support OSC 52, this provider must be opted +into explicitly by setting the following |g:clipboard| definition: >lua + + vim.g.clipboard = { + name = 'OSC 52', + copy = { + ['+'] = require('vim.clipboard.osc52').copy, + ['*'] = require('vim.clipboard.osc52').copy, + }, + paste = { + ['+'] = require('vim.clipboard.osc52').paste, + ['*'] = require('vim.clipboard.osc52').paste, + }, + } +< +Note that not all terminal emulators support reading from the system clipboard +(and even for those that do, users should be aware of the security +implications), so using OSC 52 for pasting may not be possible. +< ============================================================================== Paste *provider-paste* *paste* diff --git a/runtime/lua/vim/clipboard/osc52.lua b/runtime/lua/vim/clipboard/osc52.lua new file mode 100644 index 0000000000..0e8f9d378f --- /dev/null +++ b/runtime/lua/vim/clipboard/osc52.lua @@ -0,0 +1,38 @@ +local M = {} + +function M.copy(lines) + local s = table.concat(lines, '\n') + io.stdout:write(string.format('\x1b]52;;%s\x1b\\', vim.base64.encode(s))) +end + +function M.paste() + local contents = nil + local id = vim.api.nvim_create_autocmd('TermResponse', { + callback = function(args) + local resp = args.data ---@type string + local encoded = resp:match('\x1b%]52;%w?;([A-Za-z0-9+/=]*)') + if encoded then + contents = vim.base64.decode(encoded) + return true + end + end, + }) + + io.stdout:write('\x1b]52;;?\x1b\\') + + vim.wait(1000, function() + return contents ~= nil + end) + + -- Delete the autocommand if it didn't already delete itself + pcall(vim.api.nvim_del_autocmd, id) + + if contents then + return vim.split(contents, '\n') + end + + vim.notify('Timed out waiting for a clipboard response from the terminal', vim.log.levels.WARN) + return 0 +end + +return M