EmacsのターミナルエミュレーターをvtermからEatに移行しました
Emacs内のターミナルエミュレーターは、以前から長らくvtermを使っていました。ところが、iPythonやJuliaなどのREPLをvtermで使っていると、突然勝手にvterm-copy-modeに切り替わったり、逆にvterm-copy-modeから抜けられなくなったりすることが時々あり、悩みの種になっていました。調査はしてみたのですが発生頻度がそこまで高くない上に、再現性が低く、どのような条件で発生するのかもなかなか分かりませんでした。そこで今回は思い切って、別のターミナルエミュレーターへ移行してみることにしました。
移行先のEmacsのターミナルエミュレーターとして emacs-eat(以下、Eat)を選びました。C言語で実装されているvtermとは異なり、Eatは Emacs Lisp のみで実装されているようです。 なお、EatにはEshellとの統合機能もありますが、今回はEat単体の機能のみを使っています。
Eatは マニュアル も充実しているので、困ったときはまずここを参照するのがよさそうです。
インストール
NonGNU ELPAからインストールできるので、例えば leaf.el を使う場合は下記のように設定すればすぐに使えます。
(eval-and-compile
(customize-set-variable
'package-archives '(("nongnu" . "https://elpa.nongnu.org/nongnu/"))))
(leaf eat
:ensure t
:bind ("C-c v" . eat-project-other-window))
なお、ここで設定している eat-project-other-window は開いているファイルのプロジェクトのルートディレクトリで新規ターミナルを開いてくれる関数です。使い勝手のよいキーバインドに設定しておくと便利です。
初回のインストール後は適切なTerminfoが読み込まれていないことがあるので
M-x eat-compile-terminfo
を実行しておくとよいそうです。
私はこれを忘れていたので最初にEat内で top を実行しようとした際に下記のようなエラーが出て実行できませんでした。
Error opening terminal: eat-truecolor.
また、 Zshを使用していれば、 .zshrc に下記を追記しておくと、シェル統合の機能が使えるようになります。
シェルで移動したディレクトリを認識し、 Emacs側で find-file するときにその作業ディレクトリから開始されます。
他にもシェルからファイルをEmacsで開くような関数を作成するときにも使えそうです[参考:Reddit]。
[ -n "$EAT_SHELL_INTEGRATION_DIR" ] && \
source "$EAT_SHELL_INTEGRATION_DIR/zsh"
Eatの内部モード
vtermはターミナルとして動作する通常モードと、vterm-copy-mode という2つのモードを切り替えることができました。 vterm-copy-mode はターミナルが読み取り専用のバッファとして扱われ、ターミナルの過去出力や過去のコマンドをコピーすることができます。
Eatでは下記の4つの種類のモードがあります。
- "semi-char": Emacsで使う重要なキーバインド(
M-xとか)を除き、ほとんどの入力キーをターミナルに送るモード。 - "emacs": vtermのcopy-modeと同じように、ターミナルが読み取り専用バッファになり、入力キーはターミナルには送られない。
- "char": "semi-char" モードよりも厳しく、ほぼ全ての入力キーをターミナルに送る。
C-M-mとM-RETのみは送られず、これらのキーで "semi-char" モードに切り替えることができる。 - "line": eshellのようにバッファ末尾行を入力欄としてターミナルにキーを送るモード。通常のEmacsコマンドを使って入力行を編集可能。
自分が今どのモードなのかはモードラインに表示されます。 基本的にはsemi-charモードとemacsモードがvtermの通常モードと vterm-copy-mode に対応していると考えてよいと思いますし、自分も今のところこの2つのモードしか使っていません。
また、各モードの遷移には標準でキーバインドが割り当てられており、"char"モードを除いて、下記のキーバインドでモードを遷移できます
C-c C-j: semi-charモードへC-c C-e: emacsモードへC-c M-d: charモードへC-c C-l: lineモードへ
また、charモード以外では C-c C-k でシェルのプロセスを終了します。
とりあえず使ってみて
Eatの使い勝手については、Emacs標準のeshellやtermに比べて表示はもたつかず高速で、vtermと大差はない印象です。 例えばLaTeXのコンパイルのときに流れてくる大量のログであってもカクつくことなく表示されます。
vtermでうまく動作しないことがあったipythonやjuliaのREPLについても、今のところ同様の問題は起きていません。 topコマンドなどのフルスクリーンを使用するコマンドも問題なく使えますし、Gemini CLIなどのコーディングエージェントも使えます。 また、tmuxを使ってターミナルのセッションの多重化もできます(ただし、tmuxを使う場合には先述のシェル連携によるディレクトリ追跡は行われません)。
プロセスを終了させたときにウィンドウを閉じてくれなかったりなど、 デフォルトの挙動がvtermと異なる点もありますが、今のところ乗り換えたことによるデメリットはほとんどない印象です。 vtermでストレスを抱えている人はまず一度Eatを試してみる価値があると思います。