キー入力周りをいじる。これは https://github.com/akahuku/wasavi/issues/35 への対応である。
wasavi のキーボード周りはちょっと…いやかなり複雑で、keydown や keypress イベントに引っ掛けたリスナで即 vi 的な処理を行っているわけではない。
まずキーボードマネージャというものがあり、この中で keydown と keypress、および composition events をまとめて扱っている。特に Opera 用の composition events エミュレータ層はかなりうんざりさせられる複雑さである。キーボードマネージャ内でいろいろなつじつま合わせを行った後、改めてキー入力イベントを発生させる。
次にマップマネージャがあり、これがキーボードマネージャからのイベントをリスンしている。これの仕事はもちろんマップの展開である。適宜マップの展開を行った後、改めてキー入力を発生させる。
最後に wasavi の process() がそれを受け取り、モードに応じた処理、abbreviation の処理、対応するカッコの点滅処理、および自動的なフォーマットの処理などなどを行う。などなどである。この他にもいろいろな処理がある。
複雑なのは、あいまいなマップの展開で次の入力に応じて展開を決定する場合や、クリップボードの読み書きを行う場合や、ex コマンドの実行完了を待つ場合や、スクロールコマンドの処理完了を待つ場合など、動作が非同期に行われる箇所があるのだ。これら全てに正しく応答しないといけない。
一方、dot コマンドやキーボードマクロを任意のタイミングで実行するためにキー入力をエミュレートしなければならない部分もある。実はこちらで非同期処理への対応が不完全だったり同じような処理が分散しているのである。このへんを直したい。
まずキーボードマネージャ内にデキュー、つまり double ended queue を設けて、発生したキー入力イベントをこれに押し込んだ後、FIFO 的にイベントを発生させるようにした。キューではなくデキューなのは、マップの展開が行われた場合、展開されたシーケンスはキューの最後ではなく先頭に押し込めなければならないからだ。
それからデキューのロック機構を設けて、非同期処理が間に入った場合は適宜ロックを行うようにした。
ただ、ファイル I/O 系の ex コマンドの実行を途中で中断する場合などに [cci]^C[/cci] を押した場合は、まさに SIGINT 的な特別扱いをしないといけない。
キーボード周りとは関係ないけれど、先に挙げた issue では map コマンドの右辺でダブルクオートを書けないよ、というバグが指摘されている。実はこれはバグではなく、POSIX では map コマンドの引数中のダブルクオートは [cci]^V[/cci] でエスケープしなければならないのである。vim はダブルクオートをそのまま map の引数として扱う(vi compatible にすればたぶん vi 通りの動作をするのだろう。未確認)。そんなわけで同時にそのへんも直した。