Space を連打して目当ての形を探す時間

tmux でペインを何枚か開いたあと、幅や高さがバラバラになったのを揃えたくなります。
そのためのキーが prefix + Space で、押すたびに組み込みのレイアウトが切り替わります。

ただ、この巡回には 5 種類が入っています。

レイアウト形
even-horizontal全ペインを左右に等分
even-vertical全ペインを上下に等分
main-horizontal上に大きいメインペイン、下に残りを横並び
main-vertical左に大きいメインペイン、右に残りを縦並び
tiled格子状に敷き詰め

私が使うのは最初の 2 つだけです。
残りの 3 つは通り道でしかなく、しかも今どれにいるかは画面を見て判断するしかないので、「あと何回押せば戻るか」を毎回考えていました。
目当ての形を 1 回通り過ぎると、あと 4 回押す羽目になります。

これは設定で絞れます。

設定: Space を 2 択の反転にする

~/.tmux.conf に次を足しました。私の環境では prefix を C-a にしていますが、設定自体は prefix が何でも同じです。

# prefix + Space のレイアウト巡回を「左右平等 ⇄ 上下平等」の2択トグルに絞る
bind Space if-shell -F '#{==:#{@layout},h}' \
  'set -w @layout v ; select-layout even-vertical ; display "layout: 上下平等"' \
  'set -w @layout h ; select-layout even-horizontal ; display "layout: 左右平等"'

やっていることは 3 つです。

  • @layout というウィンドウ変数に今の状態を覚えておく。 tmux は @ で始まる名前をユーザー定義のオプションとして受け付けるので、自分用の変数として使えます。set -w でウィンドウ単位に持たせています
  • if-shell -F で変数を見て分岐する。 -F を付けると外部シェルを起動せず、tmux のフォーマット文字列をそのまま条件として評価します。#{==:A,B} は A と B が等しいかどうかの比較です
  • 分岐先で変数を反転させてからレイアウトを適用する。 ついでに display でステータス行に今どちらになったかを出します

初回は @layout が空なので「h と等しい」が偽になり、左右平等から始まります。
以降は押すたびに h ⇄ v が入れ替わり、他の 3 種類には行きません。

反映は tmux を再起動しなくても済みます。

tmux source-file ~/.tmux.conf

動いているかを画面を見ずに確かめる

キーバインドの動作確認は、実際にペインを割って押してみるのが早いのですが、手元では別の方法で検証しました。
send-keys で C-a Space を送っても、それはペインの中のシェルに届くだけで tmux 自身の prefix としては解釈されません。
なので、バインドした中身のコマンドを使い捨てセッションに対して直接実行しました。

tmux new-session -d -s lt -x 120 -y 40
tmux split-window -t lt
tmux split-window -t lt
for i in 1 2 3; do
  tmux if-shell -t lt -F '#{==:#{@layout},h}' \
    'set -w -t lt @layout v ; select-layout -t lt even-vertical' \
    'set -w -t lt @layout h ; select-layout -t lt even-horizontal'
  tmux display -p -t lt '#{@layout} #{window_layout}'
done
tmux kill-session -t lt

出力はこうなりました。

h 0f20,120x40,0,0{40x40,0,0,94,39x40,41,0,95,39x40,81,0,96}
v 51d3,120x40,0,0[120x13,0,0,94,120x13,0,14,95,120x12,0,28,96]
h 0f20,120x40,0,0{40x40,0,0,94,39x40,41,0,95,39x40,81,0,96}

#{window_layout} はレイアウトを文字列で表したもので、{ } が左右分割、[ ] が上下分割です。
3 ペインが 40x40 ずつ横に並んだ状態と、120x13 ずつ縦に並んだ状態が交互に出ているので、狙いどおりです。
この文字列は select-layout の引数にそのまま渡せる形式でもあるので、気に入った配置を保存しておく用途にも使えます。

知っておくとよいこと

  • 状態は変数で持っているので、外から崩すと 1 回ずれる。 マウスで境界をドラッグしたり resize-pane で変えたあとに Space を押すと、「直前にトグルで選んだ側の反対」へ行きます。結果として見た目と違う側に飛ぶことがありますが、もう 1 回押せば済むので実用上は困っていません。気になる場合は #{window_layout} の先頭が { か [ かで判定する手もあります
  • 5 種類の巡回が要るときは、コマンドラインから呼べる。 prefix + : で next-layout を打てば元の動作です。main-vertical などは名前で select-layout main-vertical と直接指定もできます
  • if-shell の真偽それぞれには複数コマンドを書ける。 クォートの中で ; 区切りにすれば、tmux がそのまま連続実行します。今回のように「変数更新 → レイアウト適用 → 表示」をひとまとめにできます

まとめ

prefix + Space の 5 種類巡回は、使うレイアウトが決まっている人には遠回りです。
if-shell -F とユーザー定義オプションの組み合わせで、押すたびに 2 択を反転させるトグルに変えられます。
設定は 3 行、検証は使い捨てセッションに if-shell を直接投げて #{window_layout} の括弧を見るだけです。

ペイン整列のたびに Space を連打していた人は、自分が実際に使うレイアウトを数えてみると、たぶん 2 つ以下です。