Clipboard
在Neovim、Tmux和系统剪贴板之间复制粘贴,考虑Mac、Linux及Linux下X11和Wayland的差异(借助pbcopy、xclip和wl-copy)。Nvim 的Clipboard Provider其实内置有对它们的支持,但是没搞明白怎么同时支持系统剪贴板和tmux,索性自定义一下:
Neovim
lua
local function setup_clipboard()
local has_mac = vim.fn.has('mac') == 1
local has_wsl = vim.fn.has('wsl') == 1 or vim.fn.executable('clip.exe') == 1
local has_x11 = vim.env.DISPLAY ~= nil and vim.fn.executable('xclip') == 1
local has_wayland = vim.env.WAYLAND_DISPLAY ~= nil and vim.fn.executable('wl-copy') == 1
local has_tmux = vim.env.TMUX ~= nil
vim.opt.clipboard = 'unnamedplus'
-- 如果没有外部工具,使用 nvim 默认剪贴板
local has_external_clipboard = has_mac or has_wsl or has_x11 or has_wayland
if not has_external_clipboard then return end
vim.g.clipboard = {
name = 'custom',
copy = {
['+'] = function(lines)
local text = table.concat(lines, '\n')
-- 复制到系统剪贴板
if has_mac then
vim.fn.system('pbcopy', text)
elseif has_wayland then
vim.fn.system('wl-copy', text)
elseif has_x11 then
vim.fn.system('xclip -i -sel c', text)
elseif has_wsl then
vim.fn.system('clip.exe', text)
end
-- 同步到 tmux
if has_tmux then vim.fn.system('tmux set-buffer -- ' .. vim.fn.shellescape(text)) end
end,
['*'] = function(lines) vim.g.clipboard.copy['+'](lines) end,
},
paste = {
['+'] = function()
local text = ''
if has_mac then
text = vim.fn.system('reattach-to-user-namespace pbpaste')
elseif has_wayland then
text = vim.fn.system('wl-paste --no-newline')
elseif has_x11 then
text = vim.fn.system('xclip -o -sel c')
elseif has_wsl then
-- 使用 powershell.exe 并去除 Windows 换行符
text = vim.fn.system('powershell.exe -NoProfile -Command "Get-Clipboard"')
-- 去除 ^M (CR) 字符
text = text:gsub('\r\n', '\n'):gsub('\r', '')
end
-- ssh session
if has_tmux and vim.env.XDG_SESSION_TYPE == 'tty' then text = vim.fn.system('tmux show-buffer') end
return vim.split(text, '\n')
end,
['*'] = function() return vim.g.clipboard.paste['+']() end,
},
cache_enabled = 0,
}
end
setup_clipboard()为了体验,可以在复制之后添加以短暂的高亮:
lua
autocmd('TextYankPost', {
pattern = '*',
callback = function()
-- highlight yanked text for 700ms
vim.highlight.on_yank({ higroup = 'IncSearch', timeout = 700 })
end,
})Tmux
bash
# 绑定v键为进入复制模式
bind v copy-mode
bind-key -T copy-mode-vi v send-keys -X begin-selection
unbind -T copy-mode-vi Enter
# y键复制
bind -T copy-mode-vi y send-keys -X copy-selection
# P键粘贴
bind P run "tmux paste-buffer"
if-shell -b '[ "$(uname -s)" == "Darwin" ]' {
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel "pbcopy"
bind P run "tmux set-buffer \"\$(reattach-to-user-namespace pbpaste)\"; tmux paste-buffer"
}
if-shell -b '[ "$(uname -s)" == "Linux" ]' {
if-shell -b '[ "$(echo $XDG_SESSION_TYPE)" == "wayland" ]' {
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'wl-copy'
bind P run "tmux set-buffer \"\$(wl-paste)\"; tmux paste-buffer"
}
if-shell -b '[ "$(echo $XDG_SESSION_TYPE)" == "x11" ]' {
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'xclip -i -sel c'
bind P run "tmux set-buffer \"\$(xclip -o -sel c)\"; tmux paste-buffer"
}
}
# WSL clipboard support
if-shell -b 'command -v powershell.exe' {
bind -T copy-mode-vi y send-keys -X copy-pipe-and-cancel 'clip.exe'
bind P run "tmux set-buffer \"\$(powershell.exe -Command Get-Clipboard)\"; tmux paste-buffer"
}