- iTerm 2を長く使っていたユーザーが、テキストベースの設定とdotfiles共有のためにWezTermを再び試し、Lua設定APIで外観・キーバインド・マルチプレクシング・ワークスペースを細かくカスタマイズ
- 設定は
wezterm.luaに置き、保存すると即座にホットリロードされ、CTRL + SHIFT + LのデバッグオーバーレイでログとLua REPLをすぐ確認できる
- Lua設定により、1,000種類以上の内蔵カラースキーム、システムのダーク/ライトモード連携、フォント、macOSのウィンドウ装飾、powerlineスタイルのステータスバーを動的に構成できる
- デフォルトのキーバインドは維持も無効化もでき、
Option + Arrowでの単語移動、CMD +,での設定を開く操作、leader keyとkey tableベースのpane操作を自分で定義する
- 内蔵マルチプレクシングとworkspacesはtmuxセッションのようにプロジェクトごとのウィンドウ・pane・tab状態を保持し、Luaでプロジェクト選択UIや切り替えフローまで作れる
iTerm 2からWezTermへ移ってみた理由
- iTerm 2をほぼ10年近く使ってきたが、設定をすべてテキストで管理してdotfilesに入れ、仕事用・個人用マシンで共有できるターミナルを求めていた
- WezTermは強力なAPIとLua configが強みで、2回目の挑戦では見た目も良く機能的なターミナル構成を実現できた
- 扱う範囲は外観、キーバインド、マルチプレクシング、ワークスペース移動、ステータスバー、動的テーマ設定
- 今回の記事で扱わない機能もある
- カスタムハイパーリンクのハイライトルール
- 検索可能なscrollback
- quick copy mode
- 画像サポート
- 例の構成はmacOSでの利用体験に合わせており、他のシステムでは試していないが、WezTerm自体はさまざまな環境で動作する
- 最終的な設定例はgistで公開されている
インストールとLua設定の基本構造
- WezTermのインストールは公式インストールガイドに従えばよく、macOSではHomebrewでインストールできる
brew install wezterm
- 設定ファイルは
$XDG_CONFIG_HOME/wezterm/wezterm.luaに置く構成を使っている
- macOSを含むほとんどのシステムでは
~/.config/wezterm/wezterm.luaとして解釈される
~/.wezterm.luaひとつに置く代わりにディレクトリを使うと、設定を複数ファイルに分けて管理しやすい
local wezterm = require 'wezterm'
local config = wezterm.config_builder()
return config
- WezTermの設定はLuaで書くため、YAMLやTOMLより複雑な動的設定を作りやすい
- Luaはプログラミング言語なのでYAML/TOMLより学習は難しいが、Ruby・Python・JavaScriptのような動的言語の経験があればサンプルコードは読みやすい
- Luaの学習資料としてGetting Started guideを参照できる
ホットリロードとデバッグオーバーレイ
- WezTermは設定ファイルの変更を検知して自動リロードするため、ターミナルを再起動せずに設定の反映をすぐ確認できる
- 関数名の間違いのようなエラーがある設定を保存すると、即座にランタイムエラーのポップアップが表示される
wezterm.config_builderZ()
- ログは
wezterm.log_info()で出力できる
wezterm.log_info("hello world! my name is " .. wezterm.hostname())
CTRL + SHIFT + Lでdebug overlayを開くと、ログとLua REPLを確認できる
1 + 1のようなLua式を実行できる
wezterm.home_dirのようなweztermモジュールの値も確認できる
- ホットリロードとデバッグオーバーレイを組み合わせると、設定を変えて確認するフィードバックループが短くなる
外観設定: カラー、システムテーマ、フォント、ウィンドウスタイル
-
カラースキームと動的設定
- WezTermは1,000種類以上の内蔵カラースキームを提供し、
config.color_schemeで選択する
config.color_scheme = 'Tokyo Night'
- カラースキーム一覧はWezTerm colorschemesで確認できる
window-config-reloadedイベントとwezterm.color.get_builtin_schemes()を使えば、新しいウィンドウごとにランダムなカラースキームを適用する動的設定も可能
- ウィンドウごとのconfig overrideを設定したあとで静的カラースキームに戻すと、ホットリロードがすぐ反映されない場合がある
- デバッグターミナルで
window:set_config_overrides({})を実行するか、ウィンドウを閉じて再度開くとoverrideを消せる
-
システムのダーク/ライトモード連携
- 設定を
appearance.luaモジュールに分離し、OSの外観に応じてカラースキームを切り替えられる
wezterm.gui.get_appearance()で"Dark"文字列を探してダークモードかどうかを判定する
wezterm.guiがない環境では、デフォルトでダークモードとして扱う
if appearance.is_dark() then
config.color_scheme = 'Tokyo Night'
else
config.color_scheme = 'Tokyo Night Day'
end
-
フォントとウィンドウ装飾
- WezTermはデフォルトでJetBrains MonoとNerd Font Symbolsを含んでいる
- 例の設定ではBerkeley Mono 13ptを使っている
config.font = wezterm.font({ family = 'Berkeley Mono' })
config.font_size = 13
- macOSのウィンドウスタイル例には、半透明背景、blur、タイトルバーの削除、タブバーのフォント設定が含まれる
config.window_background_opacity = 0.9
config.macos_window_background_blur = 30
config.window_decorations = 'RESIZE'
ステータスバーとWezTermイベントAPI
update-statusイベントのコールバックで、右側のステータスバーにpowerline形式のセグメントを追加する
- 最初の例では、現在のカラースキームの背景色・前景色と
wezterm.hostname()を使ってhostnameセグメントを表示する
wezterm.onはターミナルイベントにコールバックを接続する
update-statusはステータス更新の準備ができたときに定期的に発生する
- WezTermは一度に1つの更新だけが実行されるよう管理し、コードに時間がかかりすぎるとハンドラを停止する
window:effective_config().resolved_paletteで、overrideが反映された現在のパレットを取得できる
wezterm.formatは文字列に色、太字、下線などのスタイルを適用するのに使う
- 後半の例では、ステータスバーのセグメントをさらに増やしている
- 現在のworkspace
- 日付と時刻
- hostname
wezterm.color.parse、color:lighten、color:darken、wezterm.color.gradientで現在のテーマに基づくグラデーション色を計算する
キーバインド: macOSでの単語移動と設定を開く操作
- WezTermはデフォルトのキーバインドを備えており、この例ではそれを維持している
- デフォルトのキーバインドを無効にするには
config.disable_default_key_bindings = trueを使える
- macOSでiTerm 2やTerminal.appのように
Option + LeftArrow、Option + RightArrowで単語単位の移動をするため、ESC + b、ESC + fを送信する
{
key = 'LeftArrow',
mods = 'OPT',
action = wezterm.action.SendString '\x1bb',
}
CMD + ,でWezTermの設定ファイルを新しいタブで開くように構成できる
- 例では
nvimとwezterm.config_fileを使っている
- macOSのFinderからWezTermを起動した場合、
PATHにエディタのバイナリがなく、コマンド実行に失敗することがある
config.set_environment_variablesでデフォルト環境変数を補えば、その後のspawnコマンドにも同じ環境が引き継がれる
config.set_environment_variables = {
PATH = '/opt/homebrew/bin:' .. os.getenv('PATH')
}
内蔵マルチプレクシングとleader key
- WezTermの内蔵マルチプレクシングはtmuxの代わりに使える機能
- 内蔵機能はpaneごとのscrollback buffer、より良いマウス操作、より簡単な選択機能、全体的により高速な性能を提供する
CTRL + SHIFT + Pでcommand paletteを開き、“Shell: Split Horizontally”、“Shell: Split Vertically”のようなコマンドを実行できる
- デフォルトの分割ショートカットは衝突を避けるため複雑なので、leader keyでよりシンプルな組み合わせを作れる
config.leader = { key = 'a', mods = 'CTRL', timeout_milliseconds = 1000 }
- 例ではtmux風に、leaderのあと
"で水平分割、%で垂直分割を実行する
{
key = '"',
mods = 'LEADER',
action = wezterm.action.SplitHorizontal { domain = 'CurrentPaneDomain' },
}
CTRL + A自体をターミナルに送りたいときは、LEADER|CTRLの組み合わせで再びCTRL + Aを送るようにマッピングする
pane移動、リサイズ、key table
- pane移動はvim方向キー風の
h、j、k、lをleader組み合わせにマッピングする
ActivatePaneDirectionでDown、Up、Left、Rightへの移動を実行する
- 繰り返し出てくるLuaテーブルは関数で簡潔にできる
local function move_pane(key, direction)
return {
key = key,
mods = 'LEADER',
action = wezterm.action.ActivatePaneDirection(direction),
}
end
- paneのリサイズはマウスでも可能だが、この例ではキーボード中心の操作フローのためにkey tableを使う
- key tableは特定モードに入り、そのモード専用のキーバインドを使う仕組み
- leader keyに似ているが、1回動作したあとすぐ無効化されないようにできる
- リサイズのように同じキーを繰り返し入力する作業に向いている
- 例では
LEADER + rでresize_panes key tableを有効化し、h/j/k/lでpaneサイズを3セルずつ調整する
timeout_milliseconds = 1000で1秒後にリサイズモードを抜けるよう設定する
- Neovimユーザーは、WezTerm paneとvim paneの間の移動を助けるsmart-splits.nvimを参照できる
プロジェクトごとのworkspace構成
- 複数のプロジェクトを同時に扱うときは、プロジェクトごとに別々のマルチプレクサインスタンス、ウィンドウ、pane、tab状態を保持したくなる
- tmuxのsessionに近い目的を、WezTermではworkspacesで扱う
projects.luaを作り、プロジェクト選択関数をメイン設定から読み込む構成を使う
- 初期の例ではプロジェクトパスを直接列挙し、
InputSelectorで選択UIを表示する
fuzzy = trueであいまい検索を有効にする
- 選択結果は最初はdebug overlayにログとして出力するだけ
LEADER + pはプロジェクト選択UIを開き、LEADER + fは既存workspace一覧をあいまい検索で表示するよう設定する
action = wezterm.action.ShowLauncherArgs { flags = 'FUZZY|WORKSPACES' }
- プロジェクトパスを手動管理する不便さは、
wezterm.glob()でプロジェクトフォルダ配下のサブディレクトリを自動収集して軽減できる
- 例では
~/Projects配下のサブディレクトリをプロジェクトとして使い、ホームディレクトリもプロジェクト一覧に含める
- プロジェクトを選ぶと
SwitchToWorkspaceを実行する
- workspaceがあれば切り替える
- なければ新しく作成する
- workspace名にはパスの最後のセグメントを使う
- 新しいターミナルのカレントワーキングディレクトリは、選んだプロジェクトパスに設定する
さらに試せること
- WezTermには機能一覧にあるように、さらに多くの機能がある
- Lua設定の可能性はWezTerm API docsでさらに確認できる
- 段階的に作った設定は、実際に使う項目だけ残し、自分のスタイルに合わせて書き直したうえで必要な機能を足していく進め方がよい
- WezTermが気に入ったなら、オープンソース開発を続けるWezを支援することもできる
1件のコメント
Hacker News の意見
最近 WezTerm に乗り換えて、とても満足している
以前は kitty を使っていて、iTerm2 から移行したときは設定とシンプルさが気に入っていたが、標準機能の面では WezTerm のほうがずっと優れている。ターミナル設定もエディタの1画面に収まるほど短く、その後はターミナルがただ邪魔にならないので気にすることがない
kitty を諦める決定打になったのは、問題に出くわしたり iTerm2 の機能を再現しようとしたりするたびに、メンテナーの短く突き放すような返答を目にしたことだった
例えば「kitty で tmux をどう設定すればいいか?」という質問に、「やるな、tmux はバカげている」のように答えて閉じていたと記憶していて、結局諦めた
もちろん本人のプロジェクトだし優れたプログラマーだが、もう少し謙虚さがあっても悪くないと思う
なじみのない人向けに説明すると、特定のショートカットで表示/非表示を切り替えられ、表示時にはすべてのウィンドウの上に浮かんで、どこからでもすぐにターミナルを使えるようにするウィンドウのこと。なくても生きてはいけるが、わざわざ手放したくはない機能なので、WezTerm の他の機能が良くても、これがなければ私にとっては最初から候補外になる
表面的には最高の製品に見えることが多いが、いったん深く使い始めると、その人の選択に閉じ込められて戻る場所がなくなる
ただ、今年初めに WezTerm を使い始めたときは本当に驚いた。Wez の文章をたくさん読んだが、毎回トーンが完璧で、本当に素晴らしいと感じた
Linux の古い
make menuconfigUI すら正しくレンダリングできず、設定で直すこともできないのに、それは本人が設定というものを信じていないからだ、という点が象徴的だ。Issue トラッカーの投稿を読んでいると、侮辱的なだけでなく、ランダムなターミナルの使い方を自分も同じように激しく「防御」しなければならないような感情まで湧いてきて、健全ではないはっきりとは分からないが、仮にターミナルに関するあらゆる点で彼と意見が同じだったとしても、彼のターミナルを使うくらいなら自分でターミナルを作る、という気持ちは変わらないと思う
私の経験では、Wez は GitHub Issue に驚くほど素早く反応し、実際に間違っている部分ならたいてい1〜2日で直してくれる
SSH 越しの modifier キーに関する小さな不満が1つ2つある以外は、機能はほぼ自分のニーズに完全に合っていて、しかも速い。以前は Alacritty を熱心に使っていたが、リモート Emacs でより良い modifier サポートが必要だった
すでに閉じられた Issue にコメントしたのは確かだが、いずれにせよ「won’t do」として閉じられていて、私のコメントは無視された。すでに却下された同じ内容で新しい Issue を開きたいとは思わなかった
https://github.com/wez/wezterm/issues/4706
C-S-"のような組み合わせはターミナルプロトコルがサポートしていないため、送る良い方法がない。そこで WezTerm に代わりに" c s '"を送らせ、Emacs に追加のバインディングを入れた。この F12 トリックのおかげで、ターミナルプロトコルがサポートできないかなり複雑な組み合わせも送れたもともと WezTerm は、Kubuntu から Ubuntu に移行したときに Konsole をインストールしなくても Konsole の一部機能、たとえば 無限スクロールバック やスクロールバック検索を使いたくてインストールしたものだった。
Gnome でも Konsole をインストールできるのは知っていたが、何かしっくりこない感じがあった。WezTerm はすぐに気に入ったが、この記事を読んで、設定できることについてはまだ表面をなぞった程度だと分かった。
唯一の不満は、多くの機能が見つけにくいことだ。ドキュメントは本当に良く、作者も積極的で親切だが、たとえばキーボードだけで大きなテキストブロックを選択する方法を見つけて一度試し、「これは覚えておこう!」と思ったのにすぐ忘れてしまい、今ではまた完全な初心者のようにマウスで選択している。そもそも頻繁にやる作業ではないので、そのドキュメントページをまたググるより、そのほうが速いからだ。
ちなみに、言っているページは https://wezfurlong.org/wezterm/copymode.html で、名前だけ見ると探していたもののように見えるが、まったく別機能の https://wezfurlong.org/wezterm/quickselect.html ではない。これも非常に便利で良いアイデアだが、私が探していたものではなかった。
https://github.com/bbkane/dotfiles/tree/master/wezterm
デフォルトの状態でも便利だが、本当の強みは、マッチさせる正規表現をカスタマイズできる点にある。
eza --iconsの出力にあるファイル名をすべて見つける正規表現を1つ追加したところ、\p{Co}がアイコンにマッチする。なのでlsをezaにエイリアス設定しておけば、そのまま shift+QuickSelect の文字で任意のファイル名を入力できる。まだその方法を見つけられておらず、そのためよく Konsole セッションも一緒に立ち上げている。リアルタイムログを tail するとき、この2つの機能は本当に強力だ。
Mac Terminal、iTerm2、Kitty、Warp などを試した末、結局 WezTerm に落ち着いた。
設定が簡単で速く、見た目も良い。テーマも1つ作った。
https://tiniri.vlad.studio/
複数のターミナルを比較しようとして、このサイトを偶然見つけた: https://terminaltrove.com/terminals/
ターミナルを比較したい人にはかなり役立ちそうだ。
WezTerm を1年ほど使っていて GitHub で Wez をスポンサーしているが、素晴らしいターミナルだし、彼は良い開発者だ。
この記事では扱わないと言っていたが、私のお気に入り機能の1つは Quick Copy と他のコピーモードだ。本当にいつも使っている。
C-S-Spaceを押して、コピーしたい項目の横に表示された文字を入力すればよい。大きなユースケースは、macOS ノートPCから会社の Linux マシンへ mosh+tmux のような機能を使うことだ。常にセッションを立ち上げておき、Mac から会社のマシンで作業できる。再接続可能なセッションに pane と tab まで提供してくれるので、席を離れていても作業でき、品質も非常に高い。
WezTermで気に入らない点なんてあるのか、と思う
私がいちばん気に入っているのに、よく見過ごされがちな機能は完全なクロスOSサポート。Linux、macOS、Windowsを全部使う私のような環境なら、WezTermひとつを覚えればそれで済む。ターミナル設定のかなりの部分もOS間で共有している
私だけの問題か、tmuxの使い方のせいかもしれないが、行がwrapされた状態でコピーすると、コピーされたテキストに改行が含まれるように見えた。非常に気になったし、WezTermとtmuxの相互作用が原因だと思う
良いバグレポートを作れるほど証拠が十分ではないし、WezTermには独自のターミナル多重化サーバーである「ssh domains」があるので、修正するほど重要ではないのかもしれない。とはいえWezTermの多重化サーバーをあちこちにインストールしたいわけではなく、SSHで入る先にはだいたいtmuxがすでにある
それ以外はWezTermは素晴らしい
macOSの設定オプションは限られていて、いろいろな回避策が必要になる。LinuxをmacOSに合わせることもできるが、そもそもmacOSのショートカット自体があまり好きではない。結果として妙な混在状態になり、切り替えるたびに間違ったキーを押し続けることになる
うまく合う設定を見つけた人がいるのか気になる
理由も分からないし、診断方法も分からない。大きなスクロールバックが唯一の推測だが、それは諦められない。結局Kittyに移行した。Kittyにもそれなりに問題はあるが、少なくとも動作はする
Lua設定言語は環境ごとの変更をかなり簡単にできるので真価を発揮する。Rustの過小評価されている副次効果もあると思う。現代のRustツールは、言語とOSの間の抽象化レイヤーが優れているからか、クロスプラットフォーム対応が優秀な場合が多い
2019年ごろ、GitHub Actions、issue、ドキュメント化の面でwezリポジトリと他のものを一度比較してみただけで十分に納得し、それ以来戻っていない。標準でtab機能までサポートしている
サポートと速度の面でurxvtを本当に置き換えた最初のターミナル。以前はtermiteを使いつつ、termiteが妙な状況でバグる場合に備えてurxvtをバックアップとして残していた
私がiTerm2に残っている理由は
Edit->Selection Respects Soft Boundariesのため「有効にすると、パイプ文字
|の縦線をvimやemacsのpane区切り線のように解釈し、選択範囲がその地点でwrapされる」という機能私の場合はIRCでテキストを選択するときに必要になる
https://github.com/wez/wezterm/issues/596
すべての機能を必要とする人はいないが、各自が欲しいニッチ機能が10個あり、その10個は互いに同じではない。だからニッチ機能10個を得るために肥大化を受け入れることになる
追記: 乱暴な言い方だった、申し訳ない。IRCで使うという部分を見落としていた
ほぼ10年前にmacOSを初めて使い始めて以来、iTerm2ユーザーだった
ところが最近、本当に遅くなったと感じていて、
cmd-tabで切り替えたときにターミナルが表示されるまで1秒以上待つようになり、限界に達したいくつか代替を見てみたが、ぴったり合うものはなかった。あるターミナルは動作するのにアカウント/ログインが必要で大きな欠点だったし、別のターミナルは{Java,Type}Scriptで書かれていて、Node.jsストリームの非同期性のせいで
yesのような単純なコマンドすら壊していた今のところWezTermはとても気に入っている。かなり速く、非常に設定しやすく、iTerm2で設定していたテーマであるOneDark(base16)が標準で入っている
最初に感じた唯一の不便は、Lua設定に
cmd-leftとcmd-rightの構成を自分で追加する必要があった点で、標準値に入っていたらよかったと思うWezはRustシステムプログラミングの本も出している
https://leanpub.com/sysprog
良い記事なのでWezTermを一度試してみるかもしれないが、最近ちょうどKittyに移ったばかりなので、あまり頻繁に乗り換えたくはない
ただしKittyについては訂正が必要。KittyはYAMLやTOMLを使わず、独自のシンプルな設定DSLを使い、それ以外はPythonで完全にスクリプト可能