tmux の session/window の管理をするために、
- 現在の状態をテキスト形式で表現
- ユーザがそのテキストを編集する (neovim 等で)
- 編集後のテキストをあるべき状態として、session/window の作成・削除・移動・リネームなどを行う
というアイデア。結局実装しなかった。
以下の文章は主に ChatGPT 5.5 が書いた。
tmux editable tree text format (Tett) specification
1. 目的
このテキスト形式は、tmux の session group / session / window の現在状態をユーザが編集可能なテキストとして表現し、編集後のテキストを desired state として解釈するための形式である。
この形式では、ユーザは主に次の操作を行う。
- session の作成、削除、リネーム
- window の作成、削除、リネーム
- group の作成、削除
- window の session 間または group 間での link / unlink / move
- 既存 window を別 session または group に link するための複製
この形式は行単位 diff に依存しない。編集前状態と編集後状態をそれぞれ AST に parse し、ID をキーとして差分を解釈する。
2. 基本モデル
2.1 Object
この仕様で扱う object は以下である。
- Group
- Session
- Window
Session と Window は tmux の stable ID を持つ。
- Session ID は
$0,$1, … のような形式である。 - Window ID は
@0,@1, … のような形式である。
ID を持つ entry は既存 object を表す。 ID を持たない entry は新規作成される object を表す。
ID を持たず、かつ name も空の entry は、名前を明示しない新規作成を表す。
2.2 Group の意味
Group は、複数の session が同一の window link list を共有する単位である。
Group block 内に書かれた window は、その group に属するすべての session に link されているものとして解釈される。
Group は window の親ではなく、session-window link 関係を同期させる制約として扱う。
2.3 No group の意味
(no group) block は、group に属していない session の集合を表す。
(no group) block 内では、window entry は直前の session entry に属する。
3. 構文
3.1 Block header
[group GROUP_NAME]
または
(no group)
[group GROUP_NAME] は session group を表す。
(no group) は group に属していない session 群を表す。
3.2 Session entry
既存 session:
> [SESSION_ID] SESSION_NAME
例:
> [$0] session-a
名前指定ありの新規 session:
> SESSION_NAME
例:
> scratch
名前指定なしの新規 session:
>
これは、新規 session を作成するが、session name をユーザが指定しないことを意味する。
実装は tmux に session name の決定を任せてもよいし、プラグイン側で default name を生成してもよい。
適用後に再度 snapshot を生成すると、作成された session には実際の session ID と session name が表示される。
3.3 Window entry
既存 window:
>> [WINDOW_ID] WINDOW_NAME; "CWD"
例:
>> [@0] zsh; "~/work/a"
名前指定ありの新規 window:
>> WINDOW_NAME
例:
>> logs
名前指定なしの新規 window:
>>
これは、新規 window を作成するが、window name をユーザが指定しないことを意味する。
実装は tmux に window name の決定を任せてもよいし、プラグイン側で default name を生成してもよい。
適用後に再度 snapshot を生成すると、作成された window には実際の window ID と window name が表示される。
3.4 表示専用 metadata
; より後はコメントであり、この部分に各 entry の表示専用のメタデータが表示される。
4. AST 上の意味
4.1 Group block
次の表現:
[group dev]
> [$0] session-a
> [$1] session-b
>> [@0] zsh; "~/work/a"
>> [@1] logs; "~/work/a"
は次の状態を意味する。
- group
devに session$0と$1が属する - window
@0と@1は groupdevの共有 window list に属する - したがって、session
$0と$1はどちらも@0,@1を持つ
Group block 内の window entry は、個々の session ではなく group の shared window list に属する。
4.2 No group block
次の表現:
(no group)
> [$2] misc
>> [@2] zsh; "~/tmp"
> [$3] work
>> [@2] zsh; "~/tmp"
>> [@3] zsh; "~/work"
は次の状態を意味する。
- session
$2、$3は group に属さない - window
@2は session$2,$3に link されている - window
@3は session$3に link されている
(no group) block 内の window entry は、直前の session entry に属する。
4.3 Window ownership container
Window entry の親は、以下のいずれかである。
- group block 内の shared window list
(no group)block 内の直前の session
以後、この親を container と呼ぶ。
既存 window ID が編集後 AST でどの container に現れるかによって、link / unlink / move を解釈する。
Container は以下のいずれかで表す。
Group(GROUP_NAME)
Session(SESSION_ID)
Group block 内の window は Group(GROUP_NAME) container に属する。
(no group) block 内の window は直前の Session(SESSION_ID) container に属する。
5. 編集の意味
5.1 Session 名の編集
既存 session ID が同じで、session name が変更された場合、session rename を意味する。
例:
- > [$0] session-a
+ > [$0] session-main
意味:
rename session $0 to "session-main"
5.2 Window 名の編集
既存 window ID が同じで、window name が変更された場合、window rename を意味する。
例:
- >> [@0] zsh; "~/work/a"
+ >> [@0] shell; "~/work/a"
意味:
rename window @0 to "shell"
同じ window ID が複数箇所に現れる場合、編集後 AST に現れる名前集合を用いてリネームを判定する。
編集前の window 名を old_name とする。
- すべての entry が
old_nameの場合: リネームなし old_nameと別の名前new_nameの 2 種類のみが現れる場合:new_nameへのリネームを意味する- 3 種類以上の名前が現れる場合: validation error
old_name以外の名前が 2 種類以上現れる場合: validation error
例:
編集前:
>> [@0] win-a; "~/work"
編集後:
(no group)
> [$0] session-a
>> [@0] win-a; "~/work"
> [$1] session-b
>> [@0] win-b; "~/work"
意味:
rename window @0 to "win-b"
5.3 Session entry の削除
編集前 AST に存在した session ID が編集後 AST に存在しない場合、その session の終了を意味する。
例:
- > [$2] misc
意味:
kill session $2
Group 内の session entry が削除された場合、その session のみを終了する。
同じ group に他の session が残っている場合、group の shared window list は残る。
5.4 Group block の削除
編集前 AST に存在した group block が編集後 AST に存在しない場合、その group 全体の削除を意味する。
例:
- [group dev]
- > [$0] session-a
- > [$1] session-b
- >> [@0] zsh; "~/work/a"
- >> [@1] logs; "~/work/a"
意味:
delete group dev
unlink all windows from group dev
kill orphaned windows if they have no remaining links
kill all sessions in group dev
group block 全体の削除は、group に属する session を個別に削除する操作とは異なる。
実装は group 削除を単一の高レベル operation として扱ってよい。
5.5 Window entry の削除
編集前 AST に存在した window link が編集後 AST に存在しない場合、その container から window を unlink する意図として解釈する。
no group session から削除された場合
(no group)
> [$2] misc
- >> [@2] zsh; "~/tmp"
意味:
unlink or kill window @2 from session $2
その window が編集後 AST のどこにも存在しない場合、window の終了を意味する。
group block から削除された場合
[group dev]
> [$0] session-a
> [$1] session-b
- >> [@1] logs; "~/work/a"
意味:
unlink or kill window @1 from group dev
結果として、group dev に属するすべての session から @1 が消える。
5.6 Window entry の移動
既存 window ID が編集前 AST と編集後 AST で異なる container に現れる場合、window の link 関係の変更を意味する。
例:
(no group)
> [$0] session-a
- >> [@1] logs; "~/work/a"
> [$2] misc
+ >> [@1] logs; "~/work/a"
意味:
move or relink window @1 from session $0 to session $2
厳密には、desired state に基づいて以下の差分として扱う。
- 編集後に追加された container へ link する
- 編集後に存在しなくなった container から unlink する
source container が 1 つ、destination container が 1 つの場合、実装は move-window を使ってよい。
それ以外の場合、実装は link-window と unlink-window の組み合わせで desired state を実現してよい。
5.7 Window entry の複製
既存 window ID が、編集前よりも多くの container に現れる場合、window link の追加を意味する。
例:
(no group)
> [$0] session-a
>> [@0] zsh; "~/work/a"
> [$2] misc
>> [@0] zsh; "~/work/a"
意味:
link window @0 to session $2
同一 container 内に同じ window ID が複数回現れた場合は validation error とする。
Group block 内で同じ window ID が複数回現れた場合も validation error とする。
5.8 Group block の新規作成
編集後 AST にのみ存在する group block は、新規 group 作成を意味する。
例:
[group scratch]
> work
>> editor
>> logs
意味:
create group scratch
create session "work" in group scratch
create window "editor" in group scratch
create window "logs" in group scratch
新規 group block の内部には、新規 session entry と新規 window entry のみを含めることができる。
既存 session ID または既存 window ID を新規 group block 内に配置することは validation error とする。
また、新規 group block が session entry と window entry を 1 つも持たない場合、つまり空である場合、デフォルトの session と window が 1 つずつ作られる。
5.9 名前指定あり session entry の作成
ID を持たず、name を持つ session entry は、名前指定ありの新規 session 作成を意味する。
no group block 内の場合
(no group)
> scratch
>> shell
意味:
create a new ungrouped session named "scratch"
create a new window named "shell" in that session
group block 内の場合
[group dev]
> scratch
意味:
create a new session named "scratch" in group dev
この session は group dev の shared window list を共有する。
5.10 名前指定なし session entry の作成
行全体が > のみである session entry は、名前指定なしの新規 session 作成を意味する。
no group block 内の場合
(no group)
>
意味:
create a new ungrouped session with an unspecified name
group block 内の場合
[group dev]
> [$0] session-a
> [$1] session-b
>
>> [@0] zsh; "~/work/a"
意味:
create a new session in group dev with an unspecified name
この session は group dev の shared window list を共有する。
5.11 名前指定あり window entry の作成
ID を持たず、name を持つ window entry は、名前指定ありの新規 window 作成を意味する。
no group session 内の場合
(no group)
> [$0] session-a
>> logs
意味:
create a new window named "logs" in session $0
group block 内の場合
[group dev]
> [$0] session-a
> [$1] session-b
>> logs
意味:
create a new window named "logs" in group dev
結果として、group dev に属するすべての session にその window が現れる。
ID を持たない window entry が複数ある場合、それぞれ別個の新規 window 作成として扱う。
同じ名前の新規 window が複数存在しても、それ自体は validation error ではない。
5.12 名前指定なし window entry の作成
行全体が >> のみである window entry は、名前指定なしの新規 window 作成を意味する。
no group session 内の場合
(no group)
> [$0] session-a
>>
意味:
create a new window in session $0 with an unspecified name
group block 内の場合
[group dev]
> [$0] session-a
> [$1] session-b
>>
意味:
create a new window in group dev with an unspecified name
結果として、group dev に属するすべての session にその window が現れる。
6. 名前なし作成と初期 window
tmux では session 作成時に初期 window も作られる。
そのため、新規 session entry の下に window entry がある場合、実装は最初の window entry を初期 window として作成してよい。
例:
(no group)
>
>> logs
>> shell
意味:
create a new session with an unspecified session name
create its initial window named "logs"
create an additional window named "shell"
新規 session entry の下に window entry が 1 つもない場合は、名前未指定の session と、その session の default initial window を作成する。
例:
(no group)
>
意味:
create a new session with an unspecified session name
create its default initial window
名前指定あり session でも同様である。
例:
(no group)
> scratch
意味:
create a new session named "scratch"
create its default initial window
7. 空行との区別
空行は意味を持たない。
一方、以下は意味を持つ。
>
>>
したがって parser は、行全体が > または >> のみである場合、それぞれ anonymous session creation / anonymous window creation として扱わなければならない。
8. Validation rules
編集後 AST は適用前に validation されなければならない。
8.1 ID の妥当性
既存 ID として書かれた session ID / window ID は、編集開始時 snapshot に存在していなければならない。
存在しない ID が書かれた場合は validation error とする。
8.2 同一 container 内の重複 window
同一 container 内に同じ window ID が複数回現れてはならない。
(no group)
> [$0] session-a
>> [@0] zsh; "~/work/a"
>> [@0] zsh; "~/work/a"
これは validation error である。
8.3 同一 window ID の名前不一致
同じ window ID が複数 container に現れる場合、編集後 AST に現れる名前集合は以下の条件を満たさなければならない。
編集前の window 名を old_name とする。
old_nameのみが現れるold_nameと 1 種類の別名new_nameのみが現れる- 1 種類の別名
new_nameのみが現れる
上記のいずれかであれば有効である。
8.4 Session 名の重複
編集後 state において、名前指定された session name は tmux server 内で一意でなければならない。
同名 session が複数存在する場合は validation error とする。
名前指定なし session entry は、適用前の重複チェック対象には含めない。 ただし、実装が default name を生成する場合は、生成後の session name が既存 session name と衝突してはならない。
9. サポートしない編集
以下の編集はサポートしない。
- group header の rename
- 既存 session の group 参加
- 既存 session の group 離脱
- 既存 session の group 間移動
- 既存 window の working directory 変更
- 既存 window entry の
CWDは表示専用 metadata である。
- 既存 window entry の
- pane の作成、削除、移動、分割、結合
- window layout の編集
- 同一 container 内での window order 変更
- 同一 container 内で window entry の順序だけが変更された場合、意味を持たない。実装はこれを無視する。
10. Destructive operations
以下の operation は destructive operation として扱う。
- kill session
- kill window
- unlink window when it causes the window to be destroyed
- delete all sessions in a group
実装は destructive operation を適用する前に operation plan を提示し、ユーザ確認を求めるべきである。
11. Operation plan の例
編集前:
[group dev]
> [$0] session-a
> [$1] session-b
>> [@0] zsh; "~/work/a"
>> [@1] logs; "~/work/a"
(no group)
> [$2] misc
>> [@2] zsh; "~/tmp"
編集後:
[group dev]
> [$0] main
> [$1] session-b
>> [@0] shell; "~/work/a"
(no group)
> [$2] misc
>> [@2] zsh; "~/tmp"
>> [@1] logs; "~/work/a"
> scratch
解釈:
rename session $0: session-a -> main
rename window @0: zsh -> shell
unlink window @1 from group dev
link window @1 to session $2
create session scratch
also create unnamed window in session scratch
@1 が group dev から (no group) の session $2 に移動しているため、実装はこれを move-window として適用してもよい。
12. 形式文法
この節では、Tett の字句構造と行単位の構文を EBNF 風に定義する。
この文法は syntax を定義するものであり、ID の存在確認、重複、名前不一致、既存 window の CWD 変更禁止などの意味的制約は section 8 の validation rules に従う。
document = { blank-lines block } blank-lines ;
block = group-block | no-group-block ;
group-block = group-header
{ blank-lines | session-entry }
{ blank-lines | window-entry } ;
no-group-block = no-group-header
{ blank-lines | no-group-session } ;
no-group-session = session-entry
{ blank-lines | window-entry } ;
group-header = "[group" wsp1 group-name "]" line-end ;
no-group-header = "(no group)" line-end ;
session-entry = ">" [ wsp1 session-body ] line-end ;
session-body = existing-session | new-session ;
existing-session = "[" session-id "]" wsp0 session-name ;
new-session = session-name ;
window-entry = ">>" [ wsp1 window-body ] line-end ;
window-body = existing-window | new-window ;
existing-window = "[" window-id "]" wsp0 window-name ;
new-window = window-name ;
session-id = "$" digit { digit } ;
window-id = "@" digit { digit } ;
blank-line = wsp0 [ comment ] newline ;
blank-lines = { blank-line } ;
line-end = wsp0 [ comment ] newline ;
comment = ";" { comment-char } ;
comment-char = ? any character except "\r" and "\n" ? ;
wsp = " " | "\t" ;
wsp0 = { wsp } ;
wsp1 = wsp { wsp } ;
newline = "\n" | "\r\n" ;
digit = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9" ;
group-name, session-name, window-name は [A-Za-z0-9_-]+ である。これは tmux 自体の制約よりも厳しいが、パーサをシンプルにするための意図的な設計である。
Parser は各行の先頭に空白を許可しない。group-header、no-group-header、session-entry、window-entry は行頭から開始しなければならない。
; から行末まではコメントである。コメントは、空白だけの行の代わりにコメント行として書くことも、任意の group-header、no-group-header、session-entry、window-entry の末尾に書くこともできる。
Document 内で (no group) block は高々 1 つでなければならない。