この記事は Vim Advent Calendar 2016 の22日目の記事です。
簡潔に言えば、最近のVim/Neovimで導入されたタイマーAPIを活用して、物理シミュレーションによるスムーススクロールプラグインを作ったよ!という内容です。
紹介するプラグイン: https://github.com/yuttie/comfortable-motion.vim
はじめに
Vimの効率的な編集操作に惚れてしまい、長くVimを使っています。といっても最近は Emacs 上で Vim と同等の操作(テキストオブジェクトなども含む)を実現する Evil というプラグイン、そしてそれをベースにした Spacemacs という Emacs のディストリビューションも使っていたりして、交互に Vim と Emacs の世界を行き来するのが数年続いています。(正確には今は Neovim を使っています。)
Emacs を使っていて個人的に外せなかったプラグインは、@kiwanami さんの emacs-inertial-scroll です。これは inertial 「慣性の」 が示す通り、画面のスクロールに慣性を持たせることで、スクロールをスムースにするものです。スクロールをスムースにする方法は他にもありますが、このプラグインは、
- 物理法則ベースのアニメーション
- 行単位でしかスクロールできない環境でのスムース化
を実現している点で感銘を受けました。ブラウザみたいなピクセル単位でスクロール可能な環境では、拡張機能によって物理ベースのスクロールの実装が既に登場していましたが、EmacsやVimのようなエディタにおいては見たことがありませんでした。
スムースなスクロール
コンピュータ上のUIの悪い所は、全ての動作が必ずしも連続的ではない点だと思います。実際に古くからUIにアニメーションが導入されたり、最近の Microsoft Office や Google の Material Design で様々なものに滑らかな動作を与えようとしている取り組みは、やはり彼らもこの点について問題意識をもっていて改善をしようとしているからだと思います。
エディタのスクロールにおいても連続性が重要となる場面があります。編集している内容に依存はしますが、似たような構造の記述が連続して存在するような場合(XMLやJSONなどのデータだと特にそうですね)や、自分で書いたのではないソースなどの場合などです。画面が切り替わった後、自分がそれまで着目していた箇所がどこに移動したのか?これを探す手間をスムーススクロールによって無くすことができるのは、個人的には大きな利点です。
最近の Vim や NeoVim にタイマー系の機能が実装されたことで、ついにスムーススクロールが実現できるようになりました。タイマーによって非同期に処理を走らせることができるので、UIを固めることなくアニメーションを実装することができます。タイマーを用いて非同期処理を実現する方法は JavaScript でもおなじみですね。
そこで今回、初自作Vimプラグインとしてcomfortable-motion.vimを作成しました。実は前にFirefoxのVimperatorプラグインとして物理ベーススムーススクロールを実装したことがありまして、この時のコードを移植することで実装しました。名前には、滑らか、かつ自分好みの動きを実現したいという思いを込めてあります。
実際の動作
インストール方法などは README.mdを見てもらうとして、実際の動きを見てもらいましょう。
C-u
/C-d
によるスクロール
C-f
/C-b
によるスクロール
動画中では行っていませんが、スクロール中に別の操作を行うことも可能で、例えばC-f
を連続で入力すれば速度を上げることができますし、C-f
を入力した直後にC-b
を入力すればスクロールがいくらかキャンセルされます。
ちなみにこのGIF動画は byzanzで撮っています。画面上の差分だけを記録してコンパクトなGIFファイルを生成してくれるのでお薦めです。あとカラースキームは自作のを使用しています。
原理
基本的にはキーを押すことで速度(力積)を与え、2種類の抵抗力により速度を減衰させるというのをシミュレートしています。抵抗力としては空気抵抗と摩擦抵抗の2種類の力を採用しています。前者ではその時の速度の2乗に比例する抵抗力が生じるのに対し、後者では常に一定の抵抗力が生じるという違いがあります。
抵抗力のパラメータをいじることで、どちらをどのくらい強くするかを調整でき、その比率によって多様な減衰曲線を表現できるのが2つの抵抗力を採用するメリットです。例えば、空気抵抗をより重く考慮するように設定すれば、例えばC-f
を連続で入力して速度を上げた場合にすぐ減衰するようになります。また、一時的に下の2つを0
にすればオートスクロールも実現できます。
現状で提供している設定用のグローバル変数は3つあり、その内2つはこれらのパラメータに対応しています。
g:comfortable_motion_interval
: シミュレーションの間隔 [default:1000.0 / 60
]g:comfortable_motion_friction
: 空気抵抗パラメータ [default:80.0
]g:comfortable_motion_air_drag
: 摩擦抵抗パラメータ [default:2.0
]
1つ目は基本的にはデフォルトから変更する必要はないと思います。残りの2つをご自分の好みに合わせて調整して下さい。デフォルトの設定は私の好みに合わせつつ、C-u
/C-d
でおよそ25行分スクロールするような設定に調整してあります。README.mdには、どちらか一方だけを使う極端な設定例も載せてありますので、まず両方試してみてその間にある好みの設定を見つけてもらうのが良いかと思います。
さいごに
今回はタイマーAPIの機能を活用することで、これまで実現できなかった機能を実現するという例を紹介しました。Atomのようなモダンなエディタや、Vimの大規模な拡張を行うNeovimの登場により、Vim界隈の活気がみなぎっているのはとても素晴らしいと感じています。またEmacsでは最近、マルチスレッドへの対応が議論されているようです。
基本的な編集機能を追求するVimやEmacsといったエディタ達が、来年も更なる飛躍を遂げることを心より願っていますし、また自分も何かしら貢献できれば思います。
ありがとうございました!