まずは ex コマンド名の補完に絞って作ってみる。
基本的な考え方は、入力行が ^\s*([^”\S]*).*$ にマッチし、カーソルが \1 の中にある場合、つまり ex コマンド名の入力途中であるとみなされる場合、tab の押下により \1 の範囲内を補完し置き換えるようにすればいい。
しかしながら、これは本当に核の部分の基本的な仕組みであって、いろいろと考えることはある。
- この正規表現はアドレスの指定を考慮していない。アドレス指定の文法はかなり複雑 なので、補完機能の中にパーサを入れるのはちょっと無駄だ。ex コマンド実行部のパーサを流用できるようにしたい。
- ex コマンドは | で連結することができる。したがって、補完は行頭の場合のほか、| の直後にも行われないといけない。ただし、/ の引数やアドレス指定内のリテラル内での | 、および ” 以降のコメント内の | は ex コマンドの区切りではないので、補完の開始トリガーとみなしてはならない。これもやはり、ex コマンドパーサの結果を使用しないと正しい動作を行うことができない。
というわけで、ex コマンド実行部にまず拡張を施し、純粋にパースだけを行うことができるようにした。その結果を元に補完を行うかを判断することになる。
また、補完処理は補完の準備が整っていることを確認した後に非同期的に行うようにしないといけない。コマンド名のリストであれば同期的な処理で構わないが、dropbox 上のファイルパスの補完などを行うことを考えると非同期にならざるを得ない。
というわけで作ってみた。
- 補完対象を置き換える範囲をどうするか? 上記の正規表現の \1 の部分を置き換えるようにすると、
ver#foo
みたいなのがあったとき(# はカーソルを示す)、補完後の文字列は例えば version とかになる。つまりカーソルの後ろの領域も置き換えられる。一方、vim では置き換えるのはカーソル位置までなのである。つまり versionfoo とかになる。どちらがいいか。