年末年始にvimのコードリーディングにトライしてみた。 読んだのはこちら github.com
僕はターミナルアプリを作った事があり描画部分でバグが出ていたのだが、ターミナルでレイアウト描画する仕組みがわかってなかったので直せなかった。
vimでもそのあたりやってるはずなので、実際にコードを読んでそのあたりの仕組みを知りたいと思っていたのがきっかけ。
読み進め方
vimをソースからデバッグbuildし、gdbでデバッグ実行しつつ、対象箇所をvimで読み進めた。
僕は普段はintellijを使っているが、使い慣れていないCLionをインストールしてどうのこうのするよりも、今回はvimとctagsで読むことにした。
ターミナルに文字がレイアウトして出力される仕組み
os_unix.cのmch_write()で実行されていた。
https://github.com/vim/vim/blob/v9.0.2087/src/os_unix.c#L435
やってることは標準出力に文字列を出力しているだけであるが、出力されているのがANSIエスケープシーケンスを含んだ文字列であった。
この文字列データを受け取ったターミナル側がこのANSIエスケープシーケンスを解釈し、任意の場所に文字列を表示させたり、色を変えたりする仕組みのようだった。 こういう機構のことを初めて知った。
こんな文字列が出力されていた。
"\033[?25l\033[m1\033[18;155H\033[1m\033[38;5;17m\033[48;5;45m2\033[m\03 3[38;5;17m\033[48;5;45m \033[1;26H\033[?25hh1m\033[38;5;17m\033[48;5;45m ㏑:11\b/1☰☰\b℅\033[18;153H:11\033[m\033[38;5;17m\033[48;5;45m \033[1;15 H\033[?25h.......
その他わかったこと
main.cにmain_loop()ってのがあって、その最下部でnormal_cmd()ってのが呼び出されてる。
https://github.com/vim/vim/blob/v9.0.2087/src/main.c#L1562
このnormal.cのnormal_cmd()の内部でキー入力を待つ部分があって、そこでキー入力を待ってる。
normalモードからiキーでinsertmodeにはいれるわけだが、入力されたキーと実行するコマンドの対応付けがされたテーブル構造がある。 nv_cmds[]という構造体の配列。
'i'キーにはnv_edit()関数が紐づいているので、iキーを押すとこれが呼び出される。
https://github.com/vim/vim/blob/v9.0.2087/src/nv_cmds.h#L217
内部にはまた無限ループ構造があって、キー入力を待ち受ける場所があって、キー入力されるとそれをターミナルに出力する、みたいなのが大きな流れだった。
リーディング中
fileから文字列を読み込んで、ターミナルに出力されるまでの処理を追っていたが正月休みが終わってしまった。
なんか構造体の範囲外のメモリ領域までつかってデータを保持してたりしてて、「これがCの世界か、、」と感動したりしてた。
https://github.com/vim/vim/blob/v9.0.2087/src/memline.c#L116