Recently, a friend asked how to copy the contents of vim on the server to the local clipboard without using the mouse. To be honest, I always select and copy directly with the mouse. But since my friend asked, I had to do some research. I really found a solution that doesn’t use the mouse. This method can not only copy the contents of vim to the local clipboard, but also to the local tmux clipboard, very convenient. Today, I’ll share it with you.

I introduced command line escape characters (ANSI Escape Sequences) in Colorful Terminal. Escape characters start with 0x1b and are followed by a byte with a value in the range @A-Z[\]^_, with different letters indicating different meanings. The one I introduced in the previous article is 0x1b[, called Control Sequence Introducer, abbreviated as CSI. With CSI you can display bold, italic, and underline characters at the command line, as well as different colors, and even simple animations. To copy content today, you need to use 0xeb], called Operating System Command, abbreviated OSC. The complete OSC command is as follows.

OSC Description Status Format
0 Set window title Only window title ESC ] 0 ; [title] \a
2 Set window title Converted to 0
4 Set/read color palette Supported ESC ] 4 ; index1;rgb1;…;indexN;rgbN \a
9 iTerm2 notifications Supported ESC ] 9 ; [message] \a
10 Set foreground color Supported ESC ] 10 ; [X11 color spec] \a
11 Set background color Supported ESC ] 11 ; [X11 color spec] \a
50 Set the cursor shape Supported ESC ] 50 ; CursorShape=[0
52 Clipboard operations Only “c” ESC ] 52 ; c ; [base64 data] \a
777 urxvt modules Only “notify” ESC ] 777 ; notify ; [title] ; [body] \a

One of the copy commands looks like this.

1
OSC52;c;base64-encoded-text\a

It starts with OSC, which is 0x1b[, followed by a semicolon; then the number 52, which indicates the operation of the clipboard; then a semicolon connected to a letter c, which indicates copying (currently the only one), followed by a semicolon; then the content being copied, which needs to be converted to base64 encoding; and finally the ringing symbol (bel). The copy feature requires terminal software support . That is, if the terminal software supports it, when it receives the 0x1b[52;c;aGVsbG8=\a character, it will not display it directly on the terminal, but will copy the hello to the system clipboard.

Not all terminal emulators support the copy command (OSC52), and I found a list of supported ones from the web at

Terminal OSC52 support
Alacritty yes
GNOME Terminal (and other VTE-based terminals) not yet
hterm (Chromebook) yes
iTerm2 yes
kitty yes
screen yes
tmux yes
Windows Terminal yes
rxvt yes (to be confirmed)
urxvt yes (with a script, see here)
foot yes
wezterm yes

I’m using iTerm2, and it’s not supported by default. You need to check Applications in terminal may access clipboard under Preferences -> General -> Selection. After opening, execute the following command.

1
echo -ne "\033]52;c;$(echo -n hello | base64)\a"

Then you can paste hello by pressing the system’s paste shortcut ⌘ + v.

So how can I combine the OSC52 command with vim? I looked up vim-oscyank from this article). Because of the need to support windows platform and neovim, oscyank is rather complicated. But the core of oscyank is also the OSC52 command. If you don’t want to tinker with it, you can just use this plugin, but it is perfectly possible to implement a simpler version ourselves.

First we need to get the copied contents of vim, which can be done with the event TextYankPost. We can add this line to vimrc.

1
autocmd TextYankPost * echo v:event

When we copy or delete content in vim we see the following output.

1
{'regcontents': ['hello'], 'visual': v:false, 'inclusive': v:false, 'regname': '', 'operator': 'y', 'regtype': 'V'}

The copied content can be extracted by the key regcontents. This is a list, with each element corresponding to one line of content. The next step is to convert the copied content into OSC52 commands to be sent to the terminal software, and the code is simple.

1
2
3
4
5
6
7
function Copy()
  let c = join(v:event.regcontents,"\n")
  let c64 = system("base64", c)
  let s = "\e]52;c;" . trim(c64) . "\x07"
  call chansend(v:stderr, s)
endfunction
autocmd TextYankPost * call Copy()

Here we first change autocmd to call the Copy function we wrote. Note that v:event is a global variable that can be accessed directly in Copy. Then use \n to concatenate the contents to be copied, and then use system to call the system base64 command to finish the encoding (you can also use plain viml to implement the base64 function, it’s too complicated). Finally, splice the OSC52 command. Here \e is 0xeb and \x07 is \a. Finally, send the instruction to the terminal via charsend. The terminal will write the copied content to the system clipboard when it receives it.

charsend seems to be a neovim-specific command, but to support vim, you need the following code (from oscyank 😇).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
function! s:raw_echo(str)
  if has('win32') && has('nvim')
    call chansend(v:stderr, a:str)
  else
    if filewritable('/dev/fd/2')
      call writefile([a:str], '/dev/fd/2', 'b')
    else
      exec("silent! !echo " . shellescape(a:str))
      redraw!
    endif
  endif
endfunction

Wait, after all that, does this replication have anything to do with ssh? What about copying content from remote vim to local via ssh?

Actually, that’s already been said. Because ssh sends all the output of the remote vim (including the OSC52 command) to the local terminal for processing. As long as the local terminal supports the OSC52 command, it will copy the corresponding content to the local system clipboard 💯

One final note on using tmux. As soon as tmux is turned on, whether on the local or remote machine, the replication feature mentioned earlier will be disabled. I’ve looked up a lot of information on the web and it says that in addition to setting set -g set-clipboard on, you have to update the Ms configuration via terminal-overrides. Maybe it’s a tmux version problem, but I’ve experimented with the latest tmux and found that I only need to add set -g set-clipboard on. There is no problem to start tmux locally or remotely, or both. Another benefit of having set-clipboard turned on is that you can use the clipboard with vim and tmux. In other words, you can use the tmux shortcut to paste what you copied in vim, which is very delicious!

That’s all there is to this article. To summarize, you can copy by sending OSC52 commands to the terminal emulation software. This way you can cross ssh sessions and achieve the effect of remote copying. This way you can also interoperate the clipboard between vim and tmux, which is very convenient. However, while convenient, this solution essentially uses command-line escape sequences and relies on terminal emulation software. If you are interested, it is also recommended to play with other OSC commands (for example, OSC9 can control iTerm2 to send system notifications, etc.).