- EmacsでCodex CLIを使うため、標準のshellやeshellでは対話的なTUIアプリが正しく表示されないという問題がありました。
- そこでlibvtermベースのvtermを導入することで、ほぼ完全なターミナル互換性を確保しました。
- インストールにはCMakeやHomebrew版libtoolなどの依存関係が必要で、コンパイルを伴います。

vtermを使うと、Emacs内でコード編集とターミナル操作を同一ウィンドウで完結できます。
1. Emacsの標準ターミナルでは不十分
OpenAIのCodex CLIを使うようになって、ターミナルを利用することが増えました。
そこで、Emacsで編集作業中にアプリケーションを切り替える手間が気になり始めました。
Emacsでは、バッファを分割し、複数のファイルを同時に表示できます。
それなら、ターミナルもEmacs内で動かせば、すべてが一つのウィンドウで完結するのではないか、と考えました。
Emacsには標準でshellやterm、ansi-termといったターミナル機能があります。
しかし、これらには制限がありました。
shellは単純なコマンド実行には便利ですが、対話的なアプリケーションには対応していません。termとansi-termはターミナルエミュレータとして動作しますが、エスケープシーケンスの実装が不完全です。
複雑なTUI(Text User Interface)を持つアプリケーションが正しく表示されないことがあります。
Codex CLIは、対話的なTUIツールで、より完全なターミナルエミュレータが必要でした。
1.1. vtermという選択肢
「vterm」は、Emacs用の本格的なターミナルエミュレータです1。
C言語で書かれたlibvtermをベースにしているため、ほぼ完全なターミナル互換性を持ちます。
vtermを使えば、Emacs内でCodexCLIのような高度なTUIアプリケーションを動かせます。
ただし、vtermのインストールにはコンパイルが必要です。
Pure Emacs Lispではなく、ネイティブモジュールを使用しているためです。
2. インストールの準備
vtermをインストールする前に、いくつかの依存関係を満たす必要がありました。
vtermはコンパイルが必要なため、CMakeとビルドツールをインストールします。
macOSの場合、Homebrewを使うと簡単です。
brew install cmake libtool libvterm
cmakeはビルドシステム、libtoolはライブラリ作成ツール、libvtermは本体のライブラリです。
特に注意が必要なのはlibtoolです。
macOSには標準でlibtoolがインストールされていますが、これはApple版で互換性がありません。
Homebrew版を使う必要があります。
パスの優先順位を確認します。
which libtool
/opt/homebrew/bin/libtool または/usr/local/bin/libtoolと表示されれば正しいです。/usr/bin/libtoolと表示された場合は、パスの設定が必要です。
export PATH="/opt/homebrew/bin:$PATH"
Code language: JavaScript (javascript)
この設定は、~/.zshrcに追加しておくと永続化されます。
2.1. 【補足】Lubuntuの場合(apt, bash)
sudo apt install cmake libvterm-dev libtool-bin
2.2. Emacsのモジュールサポート確認
vtermを動かすには、Emacsがネイティブモジュールに対応している必要があります2。
確認方法は簡単です。
Emacsを起動し、以下を実行します。
M-: module-file-suffix RETCode language: JavaScript (javascript)
".so"や".dylib"といった文字列が返ってくれば、モジュールサポートが有効です。nilが返ってきた場合、Emacsをモジュールサポート付きで再ビルドする必要があります。
3. vtermのインストール
依存関係が揃ったら、vtermをインストールします。
Emacsのパッケージマネージャを使います。
vtermはMELPAというパッケージリポジトリから提供されています。
まず、MELPAを設定に追加します。
~/.emacs.d/init.elに以下を追記します。
(require 'package)
(add-to-list 'package-archives '("melpa" . "https://melpa.org/packages/") t)
(package-initialize)
Code language: PHP (php)
この設定により、Emacsは公式リポジトリに加えて、MELPAからもパッケージを取得できるようになります。
3.1. vtermのインストール実行
設定を保存してEmacsを再起動します。
その後、パッケージをインストールします。
M-x package-refresh-contents RET
M-x package-install RET vterm RET
インストール中、自動的にコンパイルが始まります。
初回は数十秒かかります。
エラーが出なければ成功です。
4. vtermの起動
インストールが完了したら、vtermを起動してみます。
M-x vterm RET
新しいバッファが開き、シェルプロンプトが表示されます。
見た目は通常のターミナルと変わりません。
ここで、Codexを実行してみます。
codex
Codexが起動し、対話的なUIが正しく表示されました。
色付き、カーソル位置、入力ボックス、すべてが期待通りに動作します。
標準のtermでは崩れていたレイアウトが、vtermでは完璧に再現されています。
4.1. コピーモード(vterm-copy-mode)
Emacsのvterm(emacs-libvterm)で出力をスクロール・コピーするには、コピーモード(vterm-copy-mode, C-c C-t)を使用します。C-c C-tでコピーモードに入ると、通常のEmacsバッファと同様にM-v/C-vでのスクロール、カーソル移動、検索(C-s/C-r)、テキスト選択・コピーが可能になります。
vtermは通常のターミナルモードでは出力時に自動的に最下部へスクロールしますが、コピーモードではこの挙動が停止し、履歴を自由に確認できます。
テキストを選択してEnterを押すとコピーされ、自動的に通常モードに戻ります。
またはqでコピーモードを抜け、通常の入力モードに戻ります。
5. vtermでの注意点
vtermをしばらく使ってみて、いくつかの特徴に気づきました。
5.1. vterm内でemacsを誤起動しないために
Emacsのvtermを日常的に使っていると、つい癖でemacsと打ってしまうことがあります。
するとEmacsの中でEmacsが起動し、画面が増えたり挙動が怪しくなったりします。
INSIDE_EMACSという環境変数を使うと、Emacsの中で起動したシェルかどうかを判定できます。
if [ -n "$INSIDE_EMACS" ]; then
emacs() {
echo "vterm内ではemacsを起動しないでください"
return 1
}
fiCode language: PHP (php)
vtermのときだけemacsを無効化するようにしました。
5.2. スクロールバックの制限(vterm-max-scrollback)
vtermにはスクロールバックの制限があります3。
デフォルトでは約13万文字分の履歴が保持されます。
長時間のセッションでは、古いログが消えることがあります。
必要に応じて制限を増やせます。
(setq vterm-max-scrollback 100000)
ただし、値を大きくしすぎると、パフォーマンスが低下します。
適度な値を設定することが重要です。
5.3. カーソルが表示されないときは
vtermのカーソルは、バッファ内で管理されているようです。
(setq-local cursor-type 'box)
init.elに追加しておくと、自動で設定できます。
;; vtermのcursorが表示されなかったので、設定し直した
(with-eval-after-load 'vterm
(defun my-vterm-cursor-settings ()
(setq-local cursor-type 'box)) ; または 'hollow, 'bar
(add-hook 'vterm-mode-hook #'my-vterm-cursor-settings))Code language: PHP (php)
6. Emacsでのウィンドウ管理の利点
vtermを使い始めて、Emacs内でウィンドウ管理をする利点を実感しました。
まず、アプリケーション切り替えが不要になりました。
コードを編集しながら、同じウィンドウ内でターミナルを確認できます。C-x oでウィンドウ間を移動するだけです。
次に、レイアウトの柔軟性です。
Emacsのウィンドウ分割は自由度が高く、縦分割、横分割、さらに細かい分割も簡単です。
エディタ、ターミナル、ドキュメント、すべてを一つの画面に配置できます。
そして、一貫した操作感です。
すべてのウィンドウがEmacsのキーバインドで操作できます。
- vtermの公式リポジトリによれば、vtermはlibvtermをベースにしたフル機能のターミナルエミュレータで、コンパイルされたコードを使用することでEmacsの他のターミナルエミュレータと比較して優れたパフォーマンスと互換性を実現しています – GitHub – akermu/emacs-libvterm
- Emacsのモジュールサポートは、Emacs 25.1から導入された機能で、
--with-modulesオプションを指定してコンパイルすることで有効になります。多くのLinuxディストリビューションのパッケージマネージャから入手できるEmacsには、デフォルトでこのオプションが有効になっています – Dynamic Modules (GNU Emacs Lisp Reference Manual) - vtermのスクロールバックサイズは
vterm-max-scrollback変数で制御されており、デフォルト値を変更することで履歴の保持量を調整できます。ただし、値を大きくしすぎるとメモリ使用量が増加し、パフォーマンスに影響を与える可能性があります – GitHub – akermu/emacs-libvterm