Area de transferência remota

Dica: O comando pbcopy no Mac é equivalente a xclip -selection clipboard no Linux. O comando pbpaste no Mac corresponde a xclip -selection clipboard -o no Linux. O comando xclip não funciona porque requer o X Window instalado.

Copiar e Colar

Eu edito muito código em servidores remotos via SSH, sem interface gráfica, geralmente uso o Neovim. Seria ótimo poder copiar e colar entre a máquina remota e a local. Colar da máquina local para a remota é simples, pois o terminal já faz isso. Porém, copiar da máquina remota para a local exige um pouco de código.

Usando netcat

Inicialmente, pensei em usar o netcat para criar um pequeno servidor que direciona a saída para o pbcopy. Assim, bastaria enviar dados para o servidor.

Para iniciar o “serviço de cópia”:

nc -l 9090 | pbcopy

Para enviar o texto para o servidor:

echo "texto para ser enviado" | nc maquina-local 9090

O problema dessa abordagem é que ela não é muito prática e o texto é enviado sem criptografia pela rede. Uma alternativa melhor seria usar SSH diretamente. Porém, isso requer que a máquina local execute um servidor SSH. Também é possível criar um túnel SSH, mas essa solução ainda não é ideal.

Usando SSH

echo "texto para ser enviado" | ssh maquina-local 'pbcopy'

Com SSH, também é possível obter o texto da área de transferência da máquina local usando o seguinte comando:

ssh maquina-local 'pbpaste'

Embora o SSH não seja a melhor solução, ele elimina a necessidade de iniciar um serviço para escutar uma porta. Podemos integrar o comando no evento TextYankPost do Neovim. Assim, sempre que um texto for copiado, o Neovim enviará o texto via SSH.

augroup remote_clipboard
  au!
  au TextYankPost * call system("ssh maquina-local 'pbcopy'", @")
augroup END

Usando OSC52

Existe uma maneira mais eficiente de obter a mesma funcionalidade sem precisar iniciar um servidor SSH local ou enviar o texto sem criptografia pela rede. Para isso, usamos o OSC (Operating System Command), um subconjunto dos códigos de escape do padrão ANSI. Emuladores de terminal modernos como o iTerm2 podem executar diversas sequências OSC, como exibir links, alterar o título da janela e, no nosso caso, copiar texto para a área de transferência usando o código 52. Veja o exemplo:

echo -ne "]52;c;$(echo -n "texto para ser enviado" | base64)\a"

É necessário converter o texto para base64 antes de enviá-lo. Isso pode causar um problema: os utilitários base64 do Linux e do Mac, por padrão, quebram a linha se o texto for muito longo, o que atrapalha a decodificação. Como resultado, o texto pode ser truncado se for muito grande.

Para resolver esse problema, existe um parâmetro para evitar a quebra de linha na saída do base64, mas esse parâmetro difere entre Linux e Mac. Portanto, precisamos ajustar nosso script para acomodar essa diferença.

Esta é uma versão do script que pode ser executada tanto no Mac (Darwin) quanto no Linux.

augroup remote_clipboard
    au!
    function Copy()
        if system('uname -s') == "Darwin\n"
            let l:c64 = system("base64 --break=0", @")
        else
            let l:c64 = system("base64 -w0", @")
        endif
        let l:s = "\e]52;c;" . l:c64 . "\x07"
        call chansend(v:stderr, l:s)
    endfunction
    autocmd TextYankPost * call Copy()
augroup END

Essa última versão funciona bem, mas faz duas chamadas ao sistema operacional: uma para identificar o sistema em uso e outra para converter para base64. Para melhorar, coloquei a detecção da plataforma e a conversão em um script separado e chamei esse script na função Copy do código anterior.

Aqui está um exemplo do script que usa o parâmetro adequado dependendo do sistema operacional.

#!/bin/zsh

if [[ "${OSTYPE}" == "darwin"* ]]; then
    base64 --break=0 "$@"
elif [[ "${OSTYPE}" == "linux"* ]]; then
    base64 -w0 "$@"
fi

Nomeei o script de b64. Ele será útil em outras situações onde precisar converter para base64 sem truncar a linha.

Aqui está a versão final do script que permite que, sempre que eu copiar um texto no Neovim, o texto também fique disponível na área de transferência da máquina local.

" Envia o texto copiado para a área de transferência do cliente
" Requer um emulador de terminal com suporte a OSC52
augroup remote_clipboard
    au!
    function Copy()
        let l:c64 = system("b64", @")
        let l:s = "\e]52;c;" . l:c64 . "\x07"
        call chansend(v:stderr, l:s)
    endfunction
    autocmd TextYankPost * call Copy()
augroup END

Essa solução final funciona muito bem porque não preciso lembrar de nada: nenhuma nova tecla de atalho, nenhum novo serviço. Ela simplesmente funciona. Claro, há pontos importantes a considerar, como a necessidade de um emulador de terminal compatível com OSC52. No meu caso, o que uso já suporta, então não há mudanças para mim.

Além disso, essa solução utiliza a mesma conexão SSH do terminal, garantindo que o texto seja enviado de forma criptografada.

Cesar Gimenes

Última modificação