私は普段リモートサーバーにSSHして、emacs + tmux環境で作業をしています。
その時に、emacsのコードをローカルのクリップボードにコピーしたかったり、逆にローカルでコピーしたコードをリモートのemacsにペーストしたいということがよくあります。
最近になってようやく解を見つけたので、紹介したいと思います。
環境について
- クライアント
- Windows 10
- ターミナルとしてWSL1 (Penguin)を使用
- X clientはX410
- サーバ
- CentOS 6.9
- emacsclient 26.3
- tmux 2.3
- xsel 1.2.0
方法
emacs(client), tmux, xselはそれぞれサーバに入っている前提です。また、SSH接続する際にはX11 Forwarding
を有効にしてあります(alias ssh='ssh -X'
でも、~/.ssh/config
で書くもよしです)。
以前に書いた記事で詳しく紹介しておりますが、tmuxは今開かれている環境変数を返すtmux showenv
コマンドというものがあります。これで返して来た環境変数を使ってxsel
コマンドを動かしてあげることによって、実現可能です。
具体的には
hoge | env $(tmux showenv DISPLAY) xsel -bi
のようにすれば、例えどんな環境変数を持ったアプリケーションが起動していても、正しいX(クリップボード)にアクセス出来るという仕組みです1。
stack overflowの記事を参考に、以下のようなelispを~/.emacs
などに書きます。
;; sync with x clipboard
(unless window-system
(defun xsel-cut-function (text &optional push)
(with-temp-buffer
(insert text)
(call-shell-region (point-min) (point-max) "env $(tmux showenv DISPLAY) xsel -bi")))
(defun xsel-paste-function()
(let ((xsel-output (shell-command-to-string "env $(tmux showenv DISPLAY) xsel --clipboard --output")))
(unless (string= (car kill-ring) xsel-output)
xsel-output )))
(setq interprogram-cut-function 'xsel-cut-function)
(setq interprogram-paste-function 'xsel-paste-function))
基本的に上のものを使わせてもらいましたが、call-process-region
ではどうやら$
マークを正しく評価出来ずうまくいかなかったので、ここをprocessではなくshellで行うように書き換えました。
結果
挙動のテスト結果のgifを置いておきます。
テストとして
- 異なるDISPLAY変数を持つ2つのログイン済端末を用意(Windows Terminalのタブで2つ用意し、一つは19.0, もう一つは20.0であることを確認済)
- 19.0の端末でtmuxを開き、emacsclientを立ち上げる
- "from emacs (19.0)" という文字列をemacsのkill ringに格納
- Windows上のクリップボードからメモ帳に貼り付け
- 逆に、Windowsのクリップボードに"from windows (client)"という文字列を格納し、19.0で開いているemacsにyank
- 20.0の端末で19.0で開いているtmuxをattachする
- 19.0の端末でログアウトする(ここでDISPLAY=19.0の接続が切れる)
- 20.0の端末で開かれているemacs上で"from emacs (20.0)"という文字列をkill ringに格納
- Windows上のクリップボードからメモ帳に貼り付け
- Windowsのクリップボードに"from windows (client)"という文字列を格納し、20.0で開いているemacsにyank
の操作を行っています。
この一連の動作でDISPLAY変数が変わってもクリップボードが共有されていることが確認出来ました。
-
これまでは、これを応用して
xclip
をenv $(tmux showenv DISPLAY) xclip
にaliasし、MELPAにあるxclip-mode
を使っていて、概ねうまくいっていました。しかしxclip
コマンドを評価するのはemacs(client)の起動時で、tmuxをDetachして別のDISPLAY変数の端末でAttachすると、前のDISPLAY変数でxclip
を実行してしまい、うまくいきませんでした。そこで、コマンドが実行される度に式を評価してもらうようにelispを書きました。 ↩