Clipboard
在Neovim、Tmux和系统剪贴板之间复制粘贴,考虑Mac、Linux及Linux下X11和Wayland的差异(借助pbcopy、xclip和wl-copy)。Nvim 的Clipboard Provider其实内置有对它们的支持,但是没搞明白怎么同时支持系统剪贴板和tmux,索性笨办法自定义一下:
Neovim
lua
-- clipboard
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
-- GUI 环境:有系统剪贴板工具
local has_gui_clipboard = has_mac or has_wsl or has_x11 or has_wayland
-- 情况1:GUI 环境 - 系统剪贴板/tmux/nvim 互通
if has_gui_clipboard then
vim.opt.clipboard = 'unnamedplus'
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
text = vim.fn.system('powershell.exe -NoProfile -Command "Get-Clipboard"')
text = text:gsub('\r\n', '\n'):gsub('\r', '')
end
return vim.split(text, '\n')
end,
['*'] = function() return vim.g.clipboard.paste['+']() end,
},
cache_enabled = 0,
}
-- 情况2:终端无 GUI + tmux - tmux/nvim 互通
elseif has_tmux then
vim.opt.clipboard = 'unnamedplus'
vim.g.clipboard = {
name = 'tmux',
copy = {
['+'] = function(lines)
local text = table.concat(lines, '\n')
vim.fn.system('tmux set-buffer -- ' .. vim.fn.shellescape(text))
end,
['*'] = function(lines) vim.g.clipboard.copy['+'](lines) end,
},
paste = {
['+'] = function()
local text = vim.fn.system('tmux show-buffer')
return vim.split(text, '\n')
end,
['*'] = function() return vim.g.clipboard.paste['+']() end,
},
cache_enabled = 0,
}
-- 情况3:无任何外部工具
-- 不设置 clipboard,让 nvim 使用默认行为(内部寄存器正常工作)
end
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"
}