剪贴板

听上去是一个很基础的东西嘛,这有什么好记录的,看到这个标题的人一定会这样想吧!

背景

我的工作环境:

显然,我没有被迫从众或者效率问题 (对于我这个情况,其实不用 vim 才是降低效率 233) 而使用 IDE / vscode!

能根据开发中发现的毛病,增加实战经验

总之,实际体验还是可以的,毕竟终端环境大差不差,就不用去管垃圾的 win 11 了。

当然,标题的体验除外。熟悉 windows terminal 的,用右键进行复制粘贴应该算是家常便饭了吧。每次粘贴都需要把一只手从键盘上移开,去按一下鼠标右键,割裂感很强,虽然我现在突然记起了 qmk 其实配的鼠标映射,在日常已经被我忘的一干二净了。

下面用 mermaid 简述一下实际使用过程中的几种场景,体验等同与 Linux 桌面下体验的评价为 prefect:

flowchart LR
subgraph Q
direction LR
 a1["场景1:从 vim 里复制代码到 windows 的剪贴板"]
 a2["场景2:在 tmux 中的不同 pane 的 vim 间粘贴代码"]
 a3["场景3:把 windows 剪贴板的东西粘贴进终端"]
 a4["场景4:把终端输出复制到 windows 剪贴板里"]
 a5["场景5:vim 里粘贴 tmux 复制的东西"]
end
subgraph A
direction LR
 b1["YOU CAN'T! (bad)"]
 b2["windows 右键 (NG)"]
 b3["tmux 的 copy-mode (prefect)"]
 b4["Ctrl + Shift v (prefect)"]
end
a1 --> b1
a2 --> b1
a3 --> b2
a4 --> b3
a5 --> b4

如果只有右键能进行 win 和 ssh 间的复制粘贴我还会直接用网上的答案来配相关工具来同步剪贴板,但是 tmux 的 copy-mode 居然可以完成把 linux 的同步到 windows 这一点就让我觉得匪夷所思,所以决定要研究一番。

我第二天核实了一下,这个 ctrl + shift v 其实是 windows terminal 的快捷键...

除去这个粘贴快捷键的欺骗,对比了下 wt 和 cmd 的 ssh 复制功能,发现从 tmux 中复制出文本到 windows 剪贴板的特性只有 wt 有,这就又很邪门了。

情报

Linux selections

如果管 Linux 的剪贴板叫 clipboard,那了解起来脑子确实会费成一团,因为它还有一个古老的名字叫 selections。

这东西的演变历史我没能找到,总之可以简单的理解为独立的剪贴板,类似剪贴分区的概念,selections 有三种类型:

这也解释了之前我用 st 的剪贴板功能复制之后需要使用中键粘贴,而不是像 windows 的终端那样默认右键直接复制到剪贴板里。有两个独立的剪贴板也不错,知道有这个东西就好,也不用强行把两个统一起来。

值得一提的是,这个东西实际上是 Xorg 的,也就是需要基于 Xserver 的,在纯 tty 中,这个机制是不存在的,而 tty 要怎么复制粘贴则是另一个兔子洞,不在本次探索范围内 :|

vim +clipboard

vim 的复制方式并不特殊,vim 里的复制手段是直接和系统的剪贴板做交互,当 vim 拥有 +clipboard 特性时 (archlinux repo里 vim 没有,gvim 才有) ,可以通过两个寄存器打通与系统剪贴板的交互,分别为:

这两个都可以理解为「白纸黑字指名要写进系统的剪贴板」,所以除非额外开个软件作为跳板与 Linux 系统剪贴板进行交互,跨 ssh 肯定是拿不到复制内容的。

另外还需要弄清楚的则是 tmux 和 windows terminal 为什么可以完成从 Linux 端到 Windows 的复制链。

tmux

参考官方文档,总共有两种方法:

第一种似乎不是很难配置,而是需要终端能适配 OSC52

# 设置 clipboard 类型
set -s set-clipboard [on / external]
# 设置 tmux 的 Ms 支持
tmux info |grep Ms # 如果有输出模板形式的string说明已经支持
# 如果上面是[missing]说明不支持,需要如下配置
set -as terminal-features ',[${TERM}]:clipboard' # 注意,包括[]在内的字符需要被替换,老版本使用 terminal-overrides

Ms 是什么?我不知道全称是什么,但应该是用来匹配目标终端生成 OSC52 的一个组件吧

第二种方法是我之前抄在 .tmux.conf 里的,直接装上对应的工具然后抄一下配置就行了,不过这类工具都是有些前提的,比如说要在 Xorg 环境里

set -s copy-command 'xsel -i' # 3.2~
bind-key -T copy-mode-vi y  send-keys -X copy-pipe-and-cancel "xclip -sel clipboard -in" # 2.3 - 3.1 这是我的配置,显然我配错了
bind -tvi-copy C-j  copy-pipe 'xsel -i'

tmux 的剪贴板内容可以用这个命令获得 tmux save-buffer -,更方便脚本来操作

OSC 52

Operating System Command,非常酷的东西,包括在终端上显示的字都是通过这东西实现的。

st 本来是支持 OSC52 的,先是找到了相关的 commit,然后拉取最新代码,发现居然不行,然后checkout到了那个 commit,居然是可以的,通过简单的 文本检索 和 debug 后,发现需要在config.h里设置一个allowwindowops才能启用 OSC52。google 了一下就找到了原因

请教了一下 gpt ,还真有关于 OSC52

假设一个非自然场景,ssh 到一台机器上,然后这个bash自动打印一个 OSC52,而你使用的终端正好支持 OSC52,替换掉了你的剪贴板,把这个作为前提,会发生什么后续呢?有一定的风险,但我感觉风险完全可控,因为如果剪贴板中有回车,并不是直接当作用户输入回车

windows terminal

wt 是支持 OSC52 的,但是需要解决一个问题,当在 tmux 里时,默认是无法正常输出 OSC52 的,这要说到之前设置的 set-clipboard属性了,这个属性总共有三个选项:

目标

vim 能通过 OSC52 将选取内容复制到 windows 的剪贴板。

方案

根据获取的情报,思路已经很清晰了。先获取选取内容,然后将选取内容转换为 OSC52。

flowchart LR
获取vim选区内容 --调用外部脚本--> 输出OSC52

遵循 UNIX 传统,KISS,简单写一个专门将文本流转化为 OSC52 输出的脚本。

#!/bin/bash

set -e

if [ ! -t 0 ]; then
    encode=$(cat |base64)
    echo -ne "\033]52;c;$encode\a"
else
    echo -e "osc52: \033[1;31merror:\033[0m TRY 'stdout |osc52'" >&2
    exit 1
fi

然后配置一下 vim,想象下情景:选中一块区域,然后按下 <c-y> 将选取复制到剪贴板上,注意,这里没有配置 <c-p>,只能进插入模式进行 ctrl shift v。

" use OSC52, only support yank, paste by <ctrl-shift V> in insert mode
vnoremap <c-y> "my:call OSC52('m')<CR>

function! OSC52(reg)
    let tmp_fn = system("mktemp")[:-2]
    let ret = writefile(split(getreg(a:reg), "\n"), tmp_fn, "b")
    if ret == 0
        exec "!cat ".tmp_fn." |osc52 && rm ".tmp_fn." || echo ".tmp_fn
        return
    endif
    echo ret
endfunction

总结

努力的让解决方案看起来不那么 suck,然而中间过程遇到了无数个坑,包括但不限于

此次兔子洞的探索以疲惫结束。接下去 vim 的折腾点应该就是 针对 quickfix 的个人主义优化了,虽然还有些 bug 放着没管。


参考

这篇文章讲的很详细,但是其实这并不复杂(知道了实际上有两块剪贴板之后)。让我想起了 linux ate my ram 的教诲,其他参考:

对于某些被认定为常识之物,浅意识里总会遵循着一套能用就行的行为准则不求甚解,但实际上这些东西往里探确确实实是一个兔子洞。

GO DOWN THE RABBIT HOLE.