Neovim,tmuxのペイン配置をキーボード操作する方法を、同じI/Fで扱えるようにする。
NOTE: Neovimでは window 、tmuxでは pane
だが、紛らわしいのでともに「Paneペイン」と呼ぶことにする。
分割されたペイン群は、全二分木として表現する。 本来はn分木で表現することも可能だが、検討の結果二分木のほうが扱いやすいためそうする。
以下に例を示す。 Pane の各番号は分割ごとに新しいペインに次の順序を割り当てたものとする。
-- pane
-- H
+- pane
+- pane
-- V
+- H
| +- pane
| +- pane
|
+- pane
-- 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)と入れ子リストで表現している。
凡例は以下の通り:
-- 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 │
| ─╯
+- pane
2つ目の子にフォーカスを変更した場合:
-- V
+- H
| +- pane
| +- pane
+- pane <<
親のあるノードがフォーカスされているとき、兄弟要素へフォーカスを変更できる。 もう一度同じ操作を行えば、元のノードにフォーカスが戻る(全二分木なので、兄弟は常に2つである)
-- V
+- H
| +- pane
| +- pane <<
+- pane
1回目の変更:
-- V
+- H
| +- pane <<
| +- pane
+- pane
2回目の変更:
-- V
+- H
| +- pane
| +- pane <<
+- pane
フォーカス中のノードが末端のペインでなく子を持つノードだったとしても、同じ親を持つ兄弟間でフォーカスを変更できる。 もう一度操作すれば元のフォーカスに戻るのも同様である。
-- V
+- H <<╮
| +- pane │
| +- pane │
| ─╯
+- pane
1回目の変更:
-- 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 │
─╯
末端ノード(ペイン)を選択しているときは、分割サイズの変更操作はできないものとする。