Closed rcorre closed 1 year ago
I love this! Thank you for the tip.
Thanks!
The following is even better because there are no errors on :registers
:
let g:clipboard = {
\ 'name': 'osc52',
\ 'copy': {
\ '+': {lines, regtype -> OSCYankString(join(lines, "\n"))},
\ '*': {lines, regtype -> OSCYankString(join(lines, "\n"))},
\ },
\ 'paste': {
\ '+': {-> [split(getreg(''), '\n'), getregtype('')]},
\ '*': {-> [split(getreg(''), '\n'), getregtype('')]},
\ },
\ }
lua version
local function copy(lines, _)
vim.fn.OSCYankString(table.concat(lines, "\n"))
end
local function paste()
return {
vim.fn.split(vim.fn.getreg(''), '\n'),
vim.fn.getregtype('')
}
end
vim.g.clipboard = {
name = "osc52",
copy = {
["+"] = copy,
["*"] = copy
},
paste = {
["+"] = paste,
["*"] = paste
}
}
this doesnt seem to work with 'yy' to yank a line, any way to fix that?
@nathanaelcunningham y
uses the "unnamed" register unless you explicitly specify a register like +
or *
.
If you want y
to always use a clipboard register (and consequently work with the clipboard provider described above), try set clipboard=unnamed
or set clipboard=unnamedplus
. See https://neovim.io/doc/user/provider.html.
To ALWAYS use the clipboard for ALL operations (instead of interacting with the '+' and/or '*' registers explicitly): set clipboard+=unnamedplus
Thank you for the solutions. I tried one but result still not perfect: paste is not working. So I added this:
local function copy(lines, _) vim.fn.OSCYankString(table.concat(lines, "\n")) end
local function paste() return { vim.fn.split(vim.fn.getreg(''), '\n'), vim.fn.getregtype('') } end
vim.g.clipboard = { name = "osc52", copy = { ["+"] = copy, ["*"] = copy }, paste = { ["+"] = paste, ["*"] = paste }}
vim.cmd [[
set clipboard+=unnamedplus
]]
Result:
Pasting doesn't work everywhere (there is a way to paste using Ctrl+Shift+v by using terminal but it's not a good way to do that):
E353: Nothing in register "
E353: Nothing in register +
So did anyone found a way to fix these problems? Don't know what can be done in the situation.
I found two ways to fix almost all problems I mentioned. However it's unrelated to this issue about clipboard provider but I hope you can adapt it for your usecases or even to clipboard provider. First of all, if you want to use this plugin, there is one solution but it has drawbacks: it copies successfully in 1-5 of 10 tries in tmux. Have no idea why. It tries to copy but no actual clipboard filling happens in some tries. Without tmux it's working fine (except the limitation of 100000 characters). Example of config. I use '+' here with mswin because I like mswin behaviour and it copies to '+' register:
autocmd TextYankPost * if v:event.operator is 'y' && v:event.regname is '+' | execute 'OSCYankReg +' | endif
source $VIMRUNTIME/mswin.vim
But I don't recommend this way because of tmux issues.
I found the Greatest Ever solution from Reddit user leeren: https://www.reddit.com/r/vim/comments/ac9eyh/comment/ed6kl67/?utm_source=share&utm_medium=web2x&context=3
Original code was good but the problem is that it has a limit of characters you're able to copy. Because bash command was so long (100000 characters long) which zsh, for example, refuses to answer too. So I created another solution using Lua:
function WriteToClipboard(text)
local is_tmux = os.getenv("TMUX")
local ssh_connection = os.getenv("SSH_CONNECTION")
if not is_tmux and not ssh_connection then
-- Not in a SSH session, nothing to do, copy will work anyway
return
end
local filename = "/tmp/vim_clipboard.tmp"
local file = io.open(filename, 'w')
file:write(text)
file:close()
if is_tmux then
-- Copy to each client because there is no way in tmux to differentiate only current client
os.execute("for pts in $(tmux list-clients -F '#{client_tty}'); do echo -ne $(cat < " .. filename ..
" | base64 -w0 | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > $pts; done")
else
os.execute("echo -ne $(cat < " .. filename .. " | base64 -w0 | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > " .. ssh_connection)
end
os.remove(filename)
end
Again I use '+' register, you can omit it in if
statement if you want or if you just don't use mswin
.
Normally mswin
setups <C-c>
keybinding for copying into '+' register for us but it won't happen if we connect via SSH without tmux since in https://github.com/vim/vim/blob/master/runtime/mswin.vim#L26 vim checks clipboard support and don't add keybindings if there is no clipboard (in tmux there is a clipboard available, so that's why it works there).
So we need to setup keybindings ourselves if we want to have clipboard working:
" CTRL-X and SHIFT-Del are Cut
vnoremap <C-X> "+x
vnoremap <S-Del> "+x
" CTRL-C and CTRL-Insert are Copy
vnoremap <C-C> "+y
vnoremap <C-Insert> "+y
" CTRL-V and SHIFT-Insert are Paste
map <C-V> "+gP
map <S-Insert> "+gP
cmap <C-V> <C-R>+
cmap <S-Insert> <C-R>+
And the final result is:
function WriteToClipboard(text)
local is_tmux = os.getenv("TMUX")
local ssh_connection = os.getenv("SSH_CONNECTION")
if not is_tmux and not ssh_connection then
-- Not in a SSH session, nothing to do, copy will work anyway
return
end
local filename = "/tmp/vim_clipboard.tmp"
local file = io.open(filename, 'w')
file:write(text)
file:close()
if is_tmux then
-- Copy to each client because there is no way in tmux to differentiate only current client
os.execute("for pts in $(tmux list-clients -F '#{client_tty}'); do echo -ne $(cat < " .. filename ..
" | base64 -w0 | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > $pts; done")
else
os.execute("echo -ne $(cat < " .. filename .. " | base64 -w0 | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > " .. ssh_connection)
end
os.remove(filename)
end
vim.cmd [[
au TextYankPost * if v:event.operator is 'y' && v:event.regname is '+' | call v:lua.WriteToClipboard(join(v:event.regcontents, "\n")) | endif
" CTRL-X and SHIFT-Del are Cut
vnoremap <C-X> "+x
vnoremap <S-Del> "+x
" CTRL-C and CTRL-Insert are Copy
vnoremap <C-C> "+y
vnoremap <C-Insert> "+y
" CTRL-V and SHIFT-Insert are Paste
map <C-V> "+gP
map <S-Insert> "+gP
cmap <C-V> <C-R>+
cmap <S-Insert> <C-R>+
source $VIMRUNTIME/mswin.vim
]]
The final result works with:
p
or Ctrl+v)P.S. Actually Linux can process 10.000.000 characters and much more. Even via SSH. /dev/pts/0 is a strong thing!
echo -ne "\e]52;c;$(< /dev/urandom tr -dc "[:alnum:]" | head -c1000000 | base64 -w0)\x07" > /dev/pts/0 && xclip -selection clipboard -out | wc -c
-> 10000000
I wanted to jump in here as well since I had this issue a while ago, and found a solution which might not be perfect, but works for all cases I typically go through: I develop on an SSH server, so I was looking for something that when:
To solve this, I have the following Lua file in my config:
local isInSsh = not (vim.env.SSH_CLIENT == nil) -- I share this config with my local machine, so I need this to ignore this provider if not on ssh
if isInSsh then
local clipboard = {}
local function refreshClipboard()
vim.fn.jobstart(
-- I have a reverse ssh tunnel to my local machine called "local", which allows me to get stuff from my macbook's pbpaste
"ssh local pbpaste",
{
stdout_buffered = true,
on_stdout = function(_, data) clipboard = data end
}
)
end
refreshClipboard() -- Initialize the clipboard when the file is loaded
local function copy(lines, regtype)
vim.fn.OSCYankString(table.concat(lines, "\n"))
clipboard = { lines, regtype }
end
-- doesn't necessarily paste the current clipboard content (if, for example, I copy something outside vim)
-- but it refreshes when called, so if you try again it will paste the current clipboard content
-- I do this as a compromise to avoid delays when pasting
local function paste()
refreshClipboard()
return clipboard
end
vim.api.nvim_create_user_command('RefreshClipboard', refreshClipboard, {})
vim.g.clipboard = {
name = "osc52",
copy = {
["+"] = copy,
["*"] = copy
},
paste = {
["+"] = paste,
["*"] = paste
}
}
end
Then I have on my ssh machine's ~/.ssh/config
:
Host local
HostName localhost
Port 2222
User <host user>
and then on my local machine's ~/.ssh/config
:
Host dev
HostName <IP for the SSH machine>
User <user>
RemoteForward 127.0.0.1:2222 127.0.0.1:22 # this allows me to access the local machine from the remote machine
ServerAliveInterval 240
This allows me to keep the clipboard in sync in almost all cases, the only quirk is that if I have vim already open and copy something outside vim (on a webpage, for example), when you paste it inside vim, it will paste the old contents of the clipboard, and sync it behind the scene, so I have to undo and paste again. I do this as a compromise, to avoid adding any delay when pasting since I'd have to wait for the ssh to connect to my local machine to get the content. In general, this doesn't happen that often with me, so I'm okay with it :)
Hope this helps someone else as well!
Updated the solution for clipboard copying. Now it has no characters limit! Can be used by anyone with tmux/without, locally/over ssh. https://github.com/ojroques/vim-oscyank/issues/24#issuecomment-1175539644
Thanks @avently. I have been using your awesome solution for a while in my linux machine and it works very well. Though I ran into some problems when I started using it in my macOS (intel) machine (local Alacritty + zsh + tmux) yesterday because of two issues:
base64
in macOs doesn't have -w0
option. In fact, it would just work in macOS without -w0
(https://stackoverflow.com/questions/46463027/base64-doesnt-have-w-option-in-mac)echo -ne
is not very well portable because different systems and shells could translate it differently. Apparently, my setup couldn't interpret it correctly as we want. It's recommended to use printf
instead. (https://stackoverflow.com/a/43528813, https://www.in-ulm.de/~mascheck/various/echo+printf/)Here is my current updated script which works in macOS (local Alacritty + zsh + tmux) and Linux (ssh Alacritty + zsh + tmux). I've not tested in other combinations:
function WriteToClipboard(text)
local is_tmux = os.getenv("TMUX")
local ssh_connection = os.getenv("SSH_CONNECTION")
if not is_tmux and not ssh_connection then
-- Not in a SSH session, nothing to do, copy will work anyway
return
end
-- https://stackoverflow.com/questions/46463027/base64-doesnt-have-w-option-in-mac
local is_macos = vim.fn.has("macunix")
local base64_opts = "-w0"
if is_macos then
base64_opts = ""
end
local filename = "/tmp/vim_clipboard.tmp"
local file = io.open(filename, "w")
file:write(text)
file:close()
if is_tmux then
-- Copy to each client because there is no way in tmux to differentiate only current client
os.execute(
"for pts in $(tmux list-clients -F '#{client_tty}'); do printf $(cat < "
.. filename
.. " | base64 "
.. base64_opts
.. " | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > $pts; done"
)
else
os.execute(
"echo -ne $(cat < "
.. filename
.. " | base64 -w0 | sed 's/\\(.*\\)/\\\\e]52;c;\\0\\\\x07/') > "
.. ssh_connection
)
end
os.remove(filename)
end
vim.cmd [[
au TextYankPost * if v:event.operator is 'y' && v:event.regname is '+' | call v:lua.WriteToClipboard(join(v:event.regcontents, "\n")) | endif
" CTRL-X and SHIFT-Del are Cut
vnoremap <C-X> "+x
vnoremap <S-Del> "+x
" CTRL-C and CTRL-Insert are Copy
vnoremap <C-C> "+y
vnoremap <C-Insert> "+y
" CTRL-V and SHIFT-Insert are Paste
map <C-V> "+gP
map <S-Insert> "+gP
cmap <C-V> <C-R>+
cmap <S-Insert> <C-R>+
source $VIMRUNTIME/mswin.vim
]]
If you get the following error using @kabouzeid's config:
Error detected while processing function provider#clipboard#Call[9]..function provider#clipboard#Call[6]..11[10]..<lambda>1:
line 1:
E117: Unknown function: OSCYankString
Try the following instead:
let g:clipboard = {
\ 'name': 'osc52',
\ 'copy': {
\ '+': {lines, regtype -> OSCYank(join(lines, "\n"))},
\ '*': {lines, regtype -> OSCYank(join(lines, "\n"))},
\ },
\ 'paste': {
\ '+': {-> [split(getreg(''), '\n'), getregtype('')]},
\ '*': {-> [split(getreg(''), '\n'), getregtype('')]},
\ },
\ }
It seems OSCYankString
has been renamed to OSCYank
.
I have not found an elegant way to get paste working, but yanking from a remote vim now works seamlessly.
OSCYank
is great, but only works if I'm manually yanking text. I really wanted it to work with anything that copies to the clipboard, like fugitive'sGBrowse!
command. Neovim (not vim, AFAIK) allows users to implement a custom clipboard provider, which I managed to integrate with OSCYank like so:Now regular yanks (using
clipboard=unnamedplus
) as well as commands likeGBrowse!
will put text on the local clipboard.Would this make sense to include in the README? I only discovered
g:clipboard
today, so I have no idea if it might cause unexpected problems, but so far I've found it useful and wanted to share :)