Neovim/tmuxのペイン配置について
Neovim,tmuxのペイン配置をキーボード操作する方法を、同じI/Fで扱えるようにする。 NOTE: Neovimでは window 、tmuxでは paneだが、紛らわしいのでともに「Paneペイン」と呼ぶことにする。
サポートしたい機能
- フォーカスの変更
- ペインの分割(作成)
- ペインの入れ替え
- 分割方向の変更
- 分割サイズの変更
ペイン構造の表現方法
分割されたペイン群は、全二分木として表現する。 本来はn分木で表現することも可能だが、検討の結果二分木のほうが扱いやすいためそうする。
以下に例を示す。 Pane の各番号は分割ごとに新しいペインに次の順序を割り当てたものとする。
例1: 分割されてない状態
-- pane例2: ヨコに1回だけ分割した状態
-- H
+- pane
+- pane例3: タテヨコに分割した状態
- タテに分割
- 上のペインをヨコに分割
-- V
+- H
| +- pane
| +- pane
|
+- pane例4: ヨコに2回分割した状態
- ヨコに分割
- 右のペインを更にヨコに分割
-- H
+- H
| +- pane
| +- pane
|
+- paneデータ表現の形
各ペインはツリー構造で表現されるが、各ノードは以下のような構造で表現できるだろう。 (便宜上TypeScriptで表現する)
type Node = Horizontal | Vertical | Pane
type Pane {
kind: "pane";
... // その他のペインに関する情報群を持ちうる。PaneIDやWindowIDなど
}
type Horizontal {
kind: "horizontal";
first: Node;
second: Node;
... // サイズなどの情報を持ちうる。未検討
}
type Vertical {
kind: "vertical";
first: Node;
second: Node;
... // サイズなどの情報を持ちうる。未検討
}
var root Node全てのHorizontal,Verticalのfirst/secondがnullableではない点に要注意(冒頭に述べた通り、全二分木であるため、欠落はない)。
操作
ペインの操作は、特別なモードを設けてその中で完結させる。 以下の各操作での状態を、ペインの図(HTML)と入れ子リストで表現している。
凡例は以下の通り:
ノードの最初の子
ノードの2番目の子
-- V
+- H <<╮ // フォーカスされたノード
| +- pane A │ // フォーカスされたノードの子孫 (1st child)
| +- pane B │
| ─╯ // フォーカスされたノードの終端
+- pane C末端ノード
-- H
+- pane A << // フォーカスされた末端ノード
+- pane B // フォーカス外のノード操作モードの開始
操作モードに入ると、操作を開始したペインにそのままフォーカスされた状態から始まる。
-- H
+- pane
+- pane <<フォーカスの変更: 親
親のあるノードがフォーカスされているとき、親ノードへフォーカスを変更できる。 また、さらにその親ノードへと繰り返し(親がなくなるまで)フォーカスを変更できる。
-- V
+- H
| +- pane
| +- pane <<
|
+- pane親ノードへフォーカスを変更した場合:
-- V
+- H <<╮
| +- pane │
| +- pane │
| ─╯
+- paneさらに親ノードへフォーカスを変更した場合:
-- V <<╮
+- H │
| +- pane │
| +- pane │
| │
+- pane │
─╯フォーカスの変更: 子
子を持つノードがフォーカスされているとき、1つ目の子ノード、2つ目の子ノードにそれぞれ直接フォーカスを変更することができる。
-- V <<╮
+- H │
| +- pane │
| +- pane │
| │
+- pane │
─╯1つ目の子にフォーカスを変更した場合:
-- V
+- H <<╮
| +- pane │
| +- pane │
| ─╯
+- pane2つ目の子にフォーカスを変更した場合:
-- V
+- H
| +- pane
| +- pane
+- pane <<フォーカスの変更: 兄弟
親のあるノードがフォーカスされているとき、兄弟要素へフォーカスを変更できる。 もう一度同じ操作を行えば、元のノードにフォーカスが戻る(全二分木なので、兄弟は常に2つである)
-- V
+- H
| +- pane
| +- pane <<
+- pane1回目の変更:
-- V
+- H
| +- pane <<
| +- pane
+- pane2回目の変更:
-- V
+- H
| +- pane
| +- pane <<
+- paneフォーカス中のノードが末端のペインでなく子を持つノードだったとしても、同じ親を持つ兄弟間でフォーカスを変更できる。 もう一度操作すれば元のフォーカスに戻るのも同様である。
-- V
+- H <<╮
| +- pane │
| +- pane │
| ─╯
+- pane1回目の変更:
-- V
+- H
| +- pane
| +- pane
|
+- pane <<2回目の変更:
-- V
+- H <<╮
| +- pane │
| +- pane │
| ─╯
+- paneペインの分割
末端のノード(=ペイン)がフォーカスされている時、タテまたはヨコに分割することができる。
-- pane <<ヨコに分割した場合:
-- H
+- pane <<
+- paneタテに分割した場合:
-- V
+- pane <<
+- pane子があるノードにフォーカスしているときは、この操作はできないものとする。
-- V
+- H <<╮
+- pane │
+- pane │
─╯→分割できない
ペインの入れ替え
子を持つノードがフォーカスされているとき、子ノード同士を入れ替えることができる。
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯入れ替え後:
-- V
+- H <<╮
+- pane B │
+- pane A │
─╯直接の子が末端ノード(ペイン)でなくても、入れ替え可能である。
-- V <<╮
+- H │
| +- pane A │
| +- pane B │
| │
+- pane C │
─╯入れ替え後:
-- V <<╮
+- pane C │
| │
+- H │
+- pane A │
+- pane B │
─╯末端ノード(ペイン)を選択しているときは、入れ替え操作はできないものとする。
分割方向の変更
子を持つノードがフォーカスされているとき、分割の方向を変更することができる。
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯入れ替え後:
-- V
+- V <<╮
+- pane A │
+- pane B │
─╯もう一度変更すれば、もとに戻る:
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯末端ノード(ペイン)を選択しているときは、分割方向の変更操作はできないものとする。
分割サイズの変更
子を持つノードがフォーカスされているとき、子ノード同士のサイズを変えることができる。 1つ目のノードを大きくするか、2つ目のノードを大きくするかの2択となる。 (区切り線を上下左右に動かすイメージ)
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯1つ目のノードを拡大した場合:
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯2つ目のノードを拡大した場合:
-- V
+- H <<╮
+- pane A │
+- pane B │
─╯フォーカスしたノードの直接の子が末端ノード(ペイン)でなくても、リサイズ可能である。
-- V <<╮
+- H │
| +- pane A │
| +- pane B │
| │
+- pane C │
─╯1つ目のノードを拡大した場合:
-- V <<╮
+- H │
| +- pane A │
| +- pane B │
| │
+- pane C │
─╯2つ目のノードを拡大した場合:
-- V <<╮
+- H │
| +- pane A │
| +- pane B │
| │
+- pane C │
─╯末端ノード(ペイン)を選択しているときは、分割サイズの変更操作はできないものとする。