Fix clipboard copy across terminal emulator -> tmux -> SSH -> tmux -> nvim
This commit is contained in:
Executable
+27
@@ -0,0 +1,27 @@
|
||||
#!/bin/sh
|
||||
# Copy stdin to the system clipboard, regardless of platform or how deep in
|
||||
# ssh/tmux this shell is: prefer a local clipboard tool for the current
|
||||
# display server, and fall back to OSC 52 (wrapped for tmux's DCS
|
||||
# passthrough, since tmux's own Ms-based OSC 52 forwarding doesn't reliably
|
||||
# reach every terminal) when no local tool is available.
|
||||
if command -v pbcopy >/dev/null 2>&1; then
|
||||
exec pbcopy
|
||||
elif [ -n "$WAYLAND_DISPLAY" ] && command -v wl-copy >/dev/null 2>&1; then
|
||||
# wl-copy/xclip both hold the selection by staying alive as a background
|
||||
# process (X11/Wayland selections have no central store -- the owner has
|
||||
# to serve paste requests live). setsid -f detaches that process from
|
||||
# our session so tmux's copy-pipe job cleanup can't kill it right after.
|
||||
setsid -f wl-copy
|
||||
elif [ -n "$DISPLAY" ] && command -v xclip >/dev/null 2>&1; then
|
||||
setsid -f xclip -selection clipboard
|
||||
else
|
||||
# No local clipboard tool -- most likely this is a remote host reached
|
||||
# over ssh, possibly with one or more local tmux hops between here and
|
||||
# the actual display that this process has no way to detect. Send both
|
||||
# the raw form and the tmux-DCS-passthrough-wrapped form: whichever one
|
||||
# the chain in between doesn't understand is silently dropped, and the
|
||||
# other gets through.
|
||||
data=$(base64 | tr -d '\n')
|
||||
printf '\033]52;c;%s\a' "$data" > /dev/tty
|
||||
printf '\033Ptmux;\033\033]52;c;%s\a\033\\' "$data" > /dev/tty
|
||||
fi
|
||||
@@ -60,18 +60,24 @@ opt.splitright = true
|
||||
opt.updatetime = 300
|
||||
opt.shortmess:append("c")
|
||||
|
||||
-- Clipboard: OSC 52 copy-only. This sends the "+ / "* register content to
|
||||
-- the real terminal via escape sequence, so <leader>y works whether nvim is
|
||||
-- local or several ssh/tmux hops deep -- no xclip/wl-copy/pbcopy needed on
|
||||
-- the remote host. Paste is intentionally left as a no-op: OSC 52 read
|
||||
-- support is inconsistent across terminals and can hang waiting for a
|
||||
-- reply, so use the terminal's own native paste (e.g. Ctrl-Shift-V) instead.
|
||||
local osc52 = require("vim.ui.clipboard.osc52")
|
||||
-- Clipboard: copy-only, via dotfiles/bin/clipboard-copy. This is a small
|
||||
-- script (rather than nvim's built-in raw-OSC-52-only provider) because
|
||||
-- reaching the real clipboard needs more than a raw OSC 52 write: it picks
|
||||
-- pbcopy/xclip/wl-copy when nvim is running locally, and when none of those
|
||||
-- are available (nvim on a remote host with no local display) sends OSC 52
|
||||
-- both raw and wrapped for tmux's DCS passthrough, since it can't know how
|
||||
-- many local tmux hops are between it and the real terminal and tmux's own
|
||||
-- native OSC 52 forwarding isn't reliable. Works whether nvim is local or
|
||||
-- several ssh/tmux hops deep, on any machine sharing this dotfiles repo.
|
||||
-- Paste is intentionally left as a no-op: OSC 52 read support is
|
||||
-- inconsistent across terminals and can hang waiting for a reply, so use
|
||||
-- the terminal's own native paste (e.g. Ctrl-Shift-V) instead.
|
||||
local clipboard_copy = vim.fn.expand("~/dotfiles/bin/clipboard-copy")
|
||||
vim.g.clipboard = {
|
||||
name = "OSC 52 (copy-only)",
|
||||
name = "clipboard-copy (copy-only)",
|
||||
copy = {
|
||||
["+"] = osc52.copy("+"),
|
||||
["*"] = osc52.copy("*"),
|
||||
["+"] = { clipboard_copy },
|
||||
["*"] = { clipboard_copy },
|
||||
},
|
||||
paste = {
|
||||
["+"] = function() return {} end,
|
||||
|
||||
+16
-7
@@ -49,8 +49,8 @@ bind a setw synchronize-panes
|
||||
setw -g mode-keys vi
|
||||
set-option -g -q mouse on
|
||||
|
||||
bind y run 'tmux save-buffer - | reattach-to-user-namespace pbcopy'
|
||||
bind C-y run 'tmux save-buffer - | reattach-to-user-namespace pbcopy'
|
||||
bind y run 'tmux save-buffer - | $HOME/dotfiles/bin/clipboard-copy'
|
||||
bind C-y run 'tmux save-buffer - | $HOME/dotfiles/bin/clipboard-copy'
|
||||
|
||||
# Let OSC 52 escape sequences (e.g. nvim's clipboard writes) pass through
|
||||
# this tmux to the real terminal, even when nested behind ssh/tmux layers.
|
||||
@@ -68,14 +68,23 @@ if-shell "tmux -V | awk '{print $2}' | awk -F. '{if ($1*1000+$2 >= 3003) exit 0;
|
||||
if-shell "tmux -V | awk '{print $2}' | awk -F. '{if ($1*1000+$2 >= 3002) exit 0; else exit 1}'" 'set -s extended-keys on; set -as terminal-features "*:extkeys"'
|
||||
|
||||
# Also make tmux's own copies (mouse-drag select in copy-mode, prefix-[ y,
|
||||
# etc.) reach the real system CLIPBOARD via OSC 52 -- not just PRIMARY/
|
||||
# middle-click -- by declaring the "Ms" (set-clipboard) terminfo capability
|
||||
# tmux needs to emit the escape sequence itself, then telling tmux to use it.
|
||||
# "external" only ever writes; it never asks the terminal to reply, so this
|
||||
# can't hang the way an OSC 52 read query can.
|
||||
# etc.) reach the real system CLIPBOARD -- not just PRIMARY/middle-click.
|
||||
# tmux's built-in mechanism for this is declaring the "Ms" terminfo
|
||||
# capability and setting set-clipboard so tmux emits the OSC 52 escape
|
||||
# itself, which is what these two lines do. Kept as a harmless attempt for
|
||||
# terminals where it works, but confirmed unreliable against Alacritty (both
|
||||
# pane-forwarded and tmux-generated OSC 52 silently failed to reach the
|
||||
# clipboard even with Ms correctly registered) -- copy-command below is the
|
||||
# mechanism actually proven to work here, via bin/clipboard-copy.
|
||||
set -as terminal-overrides ',*:Ms=\E]52;c;%p2%s\007'
|
||||
set -g set-clipboard external
|
||||
|
||||
# The default/no-argument form of copy-pipe (used by double/triple-click and
|
||||
# a couple of default bindings) pipes to this option instead of a literal
|
||||
# command -- route it through the same working script as the explicit
|
||||
# copy-pipe bindings in tmux/modern.conf.
|
||||
set -g copy-command '$HOME/dotfiles/bin/clipboard-copy'
|
||||
|
||||
# iTerm2 supports SIXEL images (since 3.3.0) but doesn't set a distinct TERM
|
||||
# to identify itself, so this matches on its usual "xterm-256color" report --
|
||||
# any other client reporting that same TERM will also be treated as
|
||||
|
||||
@@ -7,9 +7,9 @@ bind-key -t vi-copy NPage page-down
|
||||
bind-key -t vi-copy WheelUpPane halfpage-up
|
||||
bind-key -t vi-copy WheelDownPane halfpage-down
|
||||
bind-key -t vi-copy v begin-selection
|
||||
bind-key -t vi-copy y copy-pipe "reattach-to-user-namespace pbcopy"
|
||||
bind-key -t vi-copy y copy-pipe "$HOME/dotfiles/bin/clipboard-copy"
|
||||
unbind-key -t vi-copy Enter
|
||||
bind-key -t vi-copy Enter copy-pipe "reattach-to-user-namespace pbcopy"
|
||||
bind-key -t vi-copy Enter copy-pipe "$HOME/dotfiles/bin/clipboard-copy"
|
||||
|
||||
bind-key -n PPage if-shell -F "#{alternate_on}" "send-keys PPage" "copy-mode -e; send-keys PPage"
|
||||
bind-key -n WheelUpPane if-shell -F -t = "#{alternate_on}" "send-keys -M" "select-pane -t =; copy-mode -e; send-keys -M"
|
||||
|
||||
@@ -6,9 +6,16 @@ bind-key -T copy-mode-vi NPage send-keys -X page-down
|
||||
bind-key -T copy-mode-vi WheelUpPane send-keys -X halfpage-up
|
||||
bind-key -T copy-mode-vi WheelDownPane send-keys -X halfpage-down
|
||||
bind-key -T copy-mode-vi v send-keys -X begin-selection
|
||||
bind-key -T copy-mode-vi y send-keys -X copy-pipe "reattach-to-user-namespace pbcopy"
|
||||
bind-key -T copy-mode-vi y send-keys -X copy-pipe "$HOME/dotfiles/bin/clipboard-copy"
|
||||
unbind-key -T copy-mode-vi Enter
|
||||
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe "reattach-to-user-namespace pbcopy"
|
||||
bind-key -T copy-mode-vi Enter send-keys -X copy-pipe "$HOME/dotfiles/bin/clipboard-copy"
|
||||
|
||||
# The default MouseDragEnd1Pane binding (copy-selection-and-cancel) relies on
|
||||
# tmux's own OSC 52 write via the Ms terminfo capability, which doesn't
|
||||
# reliably reach every terminal (confirmed broken against Alacritty here).
|
||||
# Route mouse-drag copies through the same clipboard-copy script as keyboard
|
||||
# yanks instead, so both use the mechanism that's actually proven to work.
|
||||
bind-key -T copy-mode-vi MouseDragEnd1Pane send-keys -X copy-pipe-and-cancel "$HOME/dotfiles/bin/clipboard-copy"
|
||||
|
||||
# Let mouse wheel / PageUp pass through to apps using the alternate screen
|
||||
# (vim, less, etc.) instead of always entering copy-mode
|
||||
|
||||
Reference in New Issue
Block a user