diff --git a/issues/PLAN71_tmux-menu-design.md b/issues/PLAN71_tmux-menu-design.md new file mode 100644 index 00000000..010a7cb0 --- /dev/null +++ b/issues/PLAN71_tmux-menu-design.md @@ -0,0 +1,354 @@ +# PLAN71: prefix S のセッションの一覧とメニューをコマンド tmux-menu からも開く の設計 + +要求と受け入れ条件は [PLAN71_tmux-menu.md](PLAN71_tmux-menu.md) にある。この文書は +「どう作るか」だけを扱う。土台の `tmux-session` の設計は PLAN69 にある。確定仕様は +[docs/specifications/tmux-named-session.md](../docs/specifications/tmux-named-session.md) である。 + +## 機能一覧 + +| # | 機能 | 誰が使うか | +| --- | --- | --- | +| F1 | tmux の中で `tmux-menu` を打つと、今の pane にセッションの一覧を出す。選ぶと `prefix S` と同じメニューが出る | tmux の中にいる利用者 | +| F2 | tmux の外で `tmux-menu` を打つと、attach と同時に一覧を出す | tmux の外にいる利用者 | +| F3 | `prefix S` を F1 と同じ定義で開く(一覧を開く定義を 1 か所にする) | tmux の中にいる利用者 | +| F4 | F1〜F3 を base イメージへ入れ、ホストで使う手順を示す | base を建てる利用者と、ホストの tmux の利用者 | + +## 構成要素 + +| 要素 | 変更 | 責務 | +| --- | --- | --- | +| `containers/base/tmux-session` | 変える | 呼ばれた名前 `tmux-menu` を `menu` へ振り分ける。`menu` がセッションを受け取らないときは一覧を開く(決定 1)。一覧を開く `choose-tree` の定義をこのファイルだけに持つ(決定 2) | +| `containers/base/tmux.conf` | 変える | `prefix S` の行を、キーを押した pane の ID を渡して `run-shell` で `tmux-menu` を呼ぶ形に変える(決定 2・決定 4) | +| `containers/base/Dockerfile` の「tmux セッションの整理コマンド」の節 | 変える | symlink の `RUN` に `tmux-menu` を 1 つ足す | +| `tests/containers/test_tmux_session.py` | 変える | F1・F2・F3 と、`menu -c 端末 <セッション>` の形が変わらないことを確かめる。`SHORT_NAMES` に `tmux-menu` を足す(下の「テスト基盤の更新」) | +| `tests/containers/test_tmux_conf.py` | 変える | 既存の `test_prefix_s_opens_session_chooser` を書き換え、`prefix S` の割り当てが `TMUX_PANE=#{pane_id}` を付けて `tmux-menu` を呼ぶことを `list-keys` で確かめる | +| `docs/user/environment-variables.md` の「セッションを名指しで扱う」 | 変える | `tmux-menu` の使い方を足す。ホストの手順の symlink を 5 つにし、`~/.tmux.conf` の行を新しい割り当てへ差し替える | +| `docs/specifications/tmux-named-session.md` | 変える(確定仕様化で) | `menu` の 2 つの形と `tmux-menu`、`prefix S` の新しい行 | +| `issues/PLAN71_tmux-menu-measure.py` | 足す(この設計で) | 「実測」の再現用スクリプト。確定仕様化のときに消す | +| `CHANGELOG.md` | 変える | `[Unreleased]` の `### Added` に足す。反映に `devbase build base --no-cache` が要ることを書く | + +次のものは変えない。 + +- `tmux-session` の `go` / `peek` / `kill` と、`menu -c 端末 <セッション>` が出すメニューの中身 +- `containers/base/tmux-first` / `containers/base/tmux-clean` +- `prefix s`(tmux の既定の `choose-tree -Zs`) + +## 決定の記録 + +### 決定 1: `tmux-menu` は `menu` サブコマンドの短縮名にし、セッションを受け取らない形を一覧を開く動きにする + +利用者が決めた名前 `tmux-menu` と、サブコマンドの名前 `menu` がそろう。`tmux-go` = `go` と同じ +規則で読める。今の `menu -c 端末 <セッション>` はセッションを必ず受け取るため、受け取らない +形は空いている。空いた形に一覧を割り当てれば、今の形の呼び出し元(今の `/etc/tmux.conf` と +ホストへ写した行)は何も変えずに動く。 + +新しいサブコマンド(`ui` など)を足して `tmux-menu` をそちらへ振り分ける案は採らない。 +`tmux-menu` と `menu` が別の動きになり、名前から動きが読めない。今の `menu` を別名へ改名する +案も採らない。ホストへ写した `~/.tmux.conf` の行が壊れる。 + +### 決定 2: 一覧を開く定義は `tmux-session` だけに持ち、`prefix S` は `run-shell` で `tmux-menu` を呼ぶ + +`prefix S` と `tmux-menu` が同じ一覧を開くことを、定義を 1 つにして保つ。2 か所に同じ template を +持つと、片方だけを直したときに 2 つの入口の動きが分かれる。実測のとおり、template を +`run-shell` の文字列へ直接書く形は `#{…}` の先の展開で壊れる。スクリプトの中に置けば +`run-shell` の展開を通らない。ホストの手順も `~/.tmux.conf` の 1 行が短くなる。 + +`prefix S` の行を今のまま残し、`tmux-session` にも同じ template を書く案は採らない。一致を +テストで縛ることはできるが、同じ文字列を 2 か所で持つこと自体が残る。 + +### 決定 3: tmux の外では attach 先を引数で取らない + +名前の決まったセッションへ行くなら `tmux-go` がある。`tmux-menu` は「まず一覧を見る」入口で、 +attach 先は tmux の既定(**繋がっている端末の無いセッションを優先し、その中で直近に使ったもの**。 +すべてに端末が繋がっていれば全体で直近に使ったもの)で足りる。一覧からどのセッションへも移れる。 +セッションを引数で取ると、決定 1 の「セッションも `-c` も受け取らない形だけが一覧を開く」規則が崩れる。 + +### 決定 4: `prefix S` はキーを押した pane の ID を `TMUX_PANE` で渡し、`choose-tree` の `-t` に使う + +`run-shell` から起動したシェルには `TMUX_PANE` が無い。`-t` の無い `choose-tree` は、呼び出し元の +pane を継がず、全セッションから直近に操作されたセッションを選び直す。`prefix S` を押してから +`choose-tree` が走るまでの間に別のセッションの端末が操作されると、一覧がその端末に出る(実測の 5)。 +`run-shell` は渡された文字列の `#{…}` を先に展開するため、`TMUX_PANE=#{pane_id} tmux-menu` と +書けば押した pane の ID(`%3` の形。引用の要らない文字だけ)が渡る(実測の 6)。 + +一覧を出す pane を決めるのは `TMUX_PANE` そのものである。tmux は、端末を持たないクライアントから +来たコマンドの現在の pane を、そのクライアントの環境の `TMUX_PANE` で決める(tmux の `cmd-find.c` +の `cmd_find_inside_pane`)。`-t` の無い `choose-tree` でも、`TMUX_PANE` があればその pane に出る +(実測の 7a)。それでも `tmux-menu` は `TMUX_PANE` を `-t` にも渡す。どの pane に出すかをコマンドの +行に書いて意図を読めるようにし、tmux が環境から現在の pane を引く規則に頼らないためである。 +`-t` は保険であり、`-t` の有無で振る舞いは変わらない(実測の 7a・7b)。 + +`tmux-menu` に `-t` のオプションを足して ID を渡す案は採らない。`menu` の受け付ける形が増え、 +tmux の中でコマンドを打つ形(`TMUX_PANE` がシェルにある)と同じ入口に揃わない。 + +## 実測(2026-09-24、ホストの tmux 3.7b) + +再現用のスクリプトは [PLAN71_tmux-menu-measure.py](PLAN71_tmux-menu-measure.py) にある +(`python3 issues/PLAN71_tmux-menu-measure.py`。一時ディレクトリのソケットでサーバを立て、利用者の +サーバに触れない)。`python3` の `pty` で端末を繋ぎ、`tmux-session` の代わりに引数を書き出すだけの +偽物を置く。一覧を開くスクリプト `open-list` は `tmux-menu` の代わりで、`TMUX_PANE` があれば +`-t "$TMUX_PANE"` を付けて次の `