macOSのターミナルでEmacsを使っていると、「Emacsで編集している文字列を、そのまま他のアプリに貼り付けたい」というときに、不便さを感じます。
GUI版のEmacsでは意識しない問題ですが、emacs -nw や ssh 越しのEmacsでは話が変わります。
1. ターミナルEmacsで感じた違和感
参考にした設定は、次のようなものでした。
(defun paste-to-osx (text &optional push)
(let ((process-connection-type nil))
(let ((proc (start-process "pbcopy" "*Messages*" "pbcopy")))
(process-send-string proc text)
(process-send-eof proc))))
(setq interprogram-cut-function 'paste-to-osx)
(defun copy-from-osx ()
(shell-command-to-string "pbpaste"))
(setq interprogram-paste-function 'copy-from-osx)Code language: JavaScript (javascript)
M-w でコピーした内容は、SafariやSlackに貼り付けられません。
Emacsの中ではコピーできていても、kill-ring という独自の仕組みがあり、macOS全体のクリップボードとは別物でした1。
1.1. pbcopy / pbpaste に目を向ける
一方、macOSには pbcopy と pbpaste というコマンドがあります2。
ターミナルの標準入力を、macOSのクリップボードに流し込んだり、逆に取り出したりするためのものです。
つまり、やっていることはとても素直です。
Emacsでコピーした文字列(kill-ring)を pbcopy に渡します。
反対に、貼り付けるときは pbpaste の内容をEmacsに戻しています3。
1.2. interprogram-cut-function という変数
この関数を使うタイミングを決めるのが、interprogram-cut-function4、 interprogram-paste-function5 という関数です。
Emacsでテキストを kill(コピーやカット)したときに呼ばれる関数を指定する変数です。
2. おわりに
この設定を入れた瞬間から、M-w や C-y が macOS のクリップボードと自然につながりました。
Emacsの標準操作をそのまま使えるので、特別なキー操作を覚える必要もありません。
terminal Emacs と macOS のクリップボード連携は、小さな設定で体験が大きく変わります。
最初は少し分かりにくいですが、一度仕組みを理解すると応用も効きます。
- kill-ring は Emacs 独自のコピー/カット履歴で、OS のクリップボードとは別に管理されます。GUI 環境では内部的に同期される場合がありますが、terminal Emacs では自動連携されません。 – The Kill Ring (GNU Emacs Manual)
- pbcopy / pbpaste は macOS 標準のコマンドラインツールで、標準入力・標準出力を macOS のシステムクリップボードと接続する役割を持ちます。GUI アプリと同じクリップボードを共有します。 – pbcopy(1) Manual Page
- start-process を使うことで、Emacs をブロックせずに外部コマンドへ文字列を送信できます。process-connection-type を nil にするのは、macOS で不要な pty を作らないための定石です。 – Asynchronous Processes (GNU Emacs Manual)
- interprogram-cut-function は、Emacs が kill-ring にテキストを追加した直後に呼ばれる関数を保持する変数です。引数として文字列と push フラグを受け取る仕様になっています。 – Clipboard Support (GNU Emacs Lisp Reference)
- interprogram-paste-function は、Emacs が外部プログラム側のクリップボード内容を取得する際に呼び出される関数です。戻り値として文字列を返す必要があります。 – Clipboard Support (GNU Emacs Lisp Reference)