feat(clipboard)!: use OSC 52 as fallback clipboard provider (#31730)

We currently enable the OSC 52 clipboard provider by setting g:clipboard
when a list of conditions are met, one of which is that $SSH_TTY must be
set. We include this condition because often OSC 52 is not the best
clipboard provider, so if there are "local" providers available Nvim
should prefer those over OSC 52.

However, if no other providers are available, Nvim should use OSC 52
even when $SSH_TTY is not set. When a user is in an SSH session then the
checks for the other clipboard providers will still (typically) fail, so
OSC 52 continues to be enabled by default in SSH sessions.

This is marked as a breaking change because there are some cases where
OSC 52 wasn't enabled before and is now (or vice versa).
This commit is contained in:
Gregory Anders
2024-12-31 09:59:03 -06:00
committed by GitHub
parent 0bef3b911c
commit a389dc2f95
5 changed files with 37 additions and 41 deletions

View File

@@ -169,6 +169,14 @@ function! provider#clipboard#Executable() abort
let s:copy['*'] = s:copy['+'] let s:copy['*'] = s:copy['+']
let s:paste['*'] = s:paste['+'] let s:paste['*'] = s:paste['+']
return 'tmux' return 'tmux'
elseif get(get(g:, 'termfeatures', {}), 'osc52') && &clipboard ==# ''
" Don't use OSC 52 when 'clipboard' is set. It can be slow and cause a lot
" of user prompts. Users can opt-in to it by setting g:clipboard manually.
let s:copy['+'] = v:lua.require'vim.ui.clipboard.osc52'.copy('+')
let s:copy['*'] = v:lua.require'vim.ui.clipboard.osc52'.copy('*')
let s:paste['+'] = v:lua.require'vim.ui.clipboard.osc52'.paste('+')
let s:paste['*'] = v:lua.require'vim.ui.clipboard.osc52'.paste('*')
return 'OSC 52'
endif endif
let s:err = 'clipboard: No clipboard tool. :help clipboard' let s:err = 'clipboard: No clipboard tool. :help clipboard'

View File

@@ -174,7 +174,9 @@ TREESITTER
TUI TUI
TODO OSC 52 is used as a fallback clipboard provider when no other
|clipboard-tool| is found, even when not using SSH |clipboard-osc52|. To
disable OSC 52 queries, set the "osc52" key of |g:termfeatures| to false.
VIMSCRIPT VIMSCRIPT

View File

@@ -259,23 +259,21 @@ For Windows WSL, try this g:clipboard definition:
*clipboard-osc52* *clipboard-osc52*
Nvim bundles a clipboard provider that allows copying to the system clipboard 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 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 causes the terminal emulator to write to or read from the system clipboard.
supports OSC 52 then it will write the copied text into the system clipboard.
Nvim will attempt to automatically determine if the host terminal emulator When Nvim is running in the |TUI|, it will automatically attempt to determine if
supports the OSC 52 sequence and enable the OSC 52 clipboard provider if it the host terminal emulator supports OSC 52. If it does, then Nvim will use OSC
does as long as all of the following are true: 52 for copying and pasting if no other |clipboard-tool| is found and when
'clipboard' is unset.
• Nvim is running in the |TUI| *g:termfeatures*
• |g:clipboard| is unset To disable the automatic detection, set the "osc52" key of |g:termfeatures| to
• 'clipboard' is not set to "unnamed" or "unnamedplus" |v:false| in the |config| file. Example: >lua
• $SSH_TTY is set local termfeatures = vim.g.termfeatures or {}
termfeatures.osc52 = false
If any of the above conditions are not met then the OSC 52 clipboard provider vim.g.termfeatures = termfeatures
will not be used by default and Nvim will fall back to discovering a <
|clipboard-tool| through the usual process. To force Nvim to always use the OSC 52 provider you can use the following
To force Nvim to use the OSC 52 provider you can use the following
|g:clipboard| definition: >lua |g:clipboard| definition: >lua
vim.g.clipboard = { vim.g.clipboard = {

View File

@@ -6,7 +6,15 @@ for _, ui in ipairs(vim.api.nvim_list_uis()) do
end end
end end
if not tty or vim.g.clipboard ~= nil or vim.o.clipboard ~= '' or not os.getenv('SSH_TTY') then -- Do not query when any of the following is true:
-- * TUI is not attached
-- * OSC 52 support is explicitly disabled via g:termfeatures
-- * Using a badly behaved terminal
if
not tty
or (vim.g.termfeatures ~= nil and vim.g.termfeatures.osc52 == false)
or vim.env.TERM_PROGRAM == 'Apple_Terminal'
then
return return
end end
@@ -17,28 +25,13 @@ require('vim.termcap').query('Ms', function(cap, found, seq)
assert(cap == 'Ms') assert(cap == 'Ms')
-- Check 'clipboard' and g:clipboard again to avoid a race condition
if vim.o.clipboard ~= '' or vim.g.clipboard ~= nil then
return
end
-- If the terminal reports a sequence other than OSC 52 for the Ms capability -- If the terminal reports a sequence other than OSC 52 for the Ms capability
-- then ignore it. We only support OSC 52 (for now) -- then ignore it. We only support OSC 52 (for now)
if not seq or not seq:match('^\027%]52') then if not seq or not seq:match('^\027%]52') then
return return
end end
local osc52 = require('vim.ui.clipboard.osc52') local termfeatures = vim.g.termfeatures or {}
termfeatures.osc52 = true
vim.g.clipboard = { vim.g.termfeatures = termfeatures
name = 'OSC 52',
copy = {
['+'] = osc52.copy('+'),
['*'] = osc52.copy('*'),
},
paste = {
['+'] = osc52.paste('+'),
['*'] = osc52.paste('*'),
},
}
end) end)

View File

@@ -3184,7 +3184,6 @@ describe('TUI', function()
local req = args.data local req = args.data
local payload = req:match('^\027P%+q([%x;]+)$') local payload = req:match('^\027P%+q([%x;]+)$')
if payload and vim.text.hexdecode(payload) == 'Ms' then if payload and vim.text.hexdecode(payload) == 'Ms' then
vim.g.xtgettcap = 'Ms'
local resp = string.format('\027P1+r%s=%s\027\\', payload, vim.text.hexencode('\027]52;;\027\\')) local resp = string.format('\027P1+r%s=%s\027\\', payload, vim.text.hexencode('\027]52;;\027\\'))
vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp) vim.api.nvim_chan_send(vim.bo[args.buf].channel, resp)
return true return true
@@ -3202,9 +3201,6 @@ describe('TUI', function()
}, { }, {
env = { env = {
VIMRUNTIME = os.getenv('VIMRUNTIME'), VIMRUNTIME = os.getenv('VIMRUNTIME'),
-- Only queries when SSH_TTY is set
SSH_TTY = '/dev/pts/1',
}, },
}) })
@@ -3212,8 +3208,7 @@ describe('TUI', function()
local child_session = n.connect(child_server) local child_session = n.connect(child_server)
retry(nil, 1000, function() retry(nil, 1000, function()
eq('Ms', eval("get(g:, 'xtgettcap', '')")) eq({ true, { osc52 = true } }, { child_session:request('nvim_eval', 'g:termfeatures') })
eq({ true, 'OSC 52' }, { child_session:request('nvim_eval', 'g:clipboard.name') })
end) end)
end) end)
end) end)