Humanity

Edit the world by your favorite way

Vim script で Chrome Debugging Protocol を扱うライブラリを作りたい

github.com

タイトルの通りなのですが、Chrome Debugging Protocol (以下 CDP) を扱うためには、

  1. CDP は WebSocket ベースなので WebSocket を実装しなきゃならない
  2. バイト列をパースする際にヌルバイトを Vim では扱えない

となって悩んでいます。 ヌルバイトを扱う部分は Python/Ruby/Lua インターフェースを使ったり、外部コマンドでやったりと策がないわけではないのですが、 どうせなら Vim で扱う方法を考えてみようというわけです。

CDP を実装しようとしたきっかけ

Chrome を (CDP が許す限り) 意のままに操ることができるからです。 まだ CDP をざっくりとしか調べてないのであまり風呂敷を広げすぎるのもアレかと思うのですが、 大体こんな事ができるんじゃないかと思っています。 できるか分からない部分に関して一応 (多分) とか付けときます。

  • (多分) Chrome を画像ビューアとして使ったり?
  • (多分) ライブリロード的なことができたり?
  • なんと JS のデバッグもできる (本来の機能)

正直今回この記事を書こうと思ったモチベーションはこんな事ができるんだよっていう紹介ではなくて、 バイト列をパースする際にヌルバイトを扱えない問題に対する提案がメインです。 あと単純に自分が何に悩んでいるかを整理したいというのもあります。

WebSocket の実装

対象の Chrome は通常 localhost で動いていて、over SSL でもないので Vim 8 の channel で扱えます。 channel は ch_open() でソケット通信したり、job_start() で実行したコマンドの入出力が channel で扱えます。 ようはざっくり入出力を抽象化したオブジェクト (ハンドル) と考えればいいです。

最初のハンドシェーク部分に関してはまんま HTTP なので実装できましたが、 バイト列 (=ヌルバイトを含む文字列) を Vim で扱う際にできれば (if_python などの外部インターフェースを使わず) pure vim script で実装したい… そうなると今の channel の機能では扱えないことが分かりました (次の章で解説)。

ちなみに、先ほど channel を入出力を抽象化したオブジェクト (ハンドル) と言いましたが、そう言ってしまうにはまだ機能が少なくて、 例えばファイル/バッファも channel で扱えるようになって ch_read() 等で固定長読み込む事ができるようになれば、getline() (行単位で文字列を取得) だけじゃなく もっと自由度と抽象度の高いスクリプトが書けるようになると思います。

バイト列 (=ヌルバイトを含む文字列) を Vim で扱う

閑話休題

まず、Vim script の文字列ではヌルバイトを含む文字列を扱う事ができません。 扱えないといっても、 echo "hell\0o"hell になる (多言語の人から見たら信じられない仕様だと思いますw) ように Vim script の文字列の値として使えないだけで、バッファ上で扱うことはできます。 ヌルバイトを含んだバッファで :%!xxd とすると現在のバッファの内容が hex dump した内容に置き換わりますが、 そうするとヌルバイトもちゃんと xxd コマンドに渡されていることが分かります。 ちなみに xxd コマンドは Vim に標準添付されているので、一度バッファ上に内容を持ってしまえば job 機能から xxd を呼び出して hex dump した内容を持てば Vim script で扱うことが可能です。 つまり目標としては「WebSocket で受け取ったバイト列をバッファに読み込む」です。

しかし、調べてみると ch_open('localhost:12345') のように開いたチャンネル (ソケット) からは受け取り方法としてバッファを指定できないことが分かりました。 job 機能だったら job_start() の option に {'out_io': 'buffer', 'out_buf': bufnr} のようにバッファを指定できます。 しかし channel にはそれが無いのです。

しかし、さらに調べる内にある方法でできなくはないことが分かりました。 できなくはないけどがかなり辛いハックになりそうだということも分かりました (つらい)。

その方法とは、ch_logfile({fname} [, {mode}]) で書き込んだログファイルに現在 Vim で使用している channel が受け取ったメッセージの内容が書き込まれていたので、これをパースすればできなくはないということです。 しかしこの関数はそもそも引数から分かる通り、ロギングしたい channel を渡す設計ではなく Vim で使用している全 channel のログが {fname} にすべて書き込まれます。 なんでこんな設計になっているかというと、まぁ恐らくテスト用 (あるいはデバッグ用) に追加した関数だからだと思います。 それに現在ロギングしているかどうかも Vim script で判断できないので、この関数を使うと既存でロギングされていたファイルへは書き込まれなくなることが予想されます。

そんな感じで ch_logfile() はそもそもそんな用途に使うもんじゃねーよ感がビンビンですが、 ただ各行に channel の id と一緒に吐き出されるのでパースするのは不可能じゃなさそうです。 この channel id は ch_info() で取得することができます。 ただ複数行のメッセージが来た場合も考えて単純に各行取得したんじゃパースできないため、ちゃんとやるのも結構大変な印象です。

本来なら先ほど言ったように ch_open() の option でも job option みたいに {'out_io': 'buffer', 'out_buf': bufnr} みたいに渡せると バッファ経由でヌルバイト含むバイト列も扱えそうなのですが、何とかならないでしょうか…

[追記] CDP の実装途中のスクリプト

一応共有しておきます。

WIP: [Preview] Chrome Debugging Protocol in Vim script · GitHub

今回の記事関係ないけど channel 使った HTTP の実装は vital.vim に入れたい… ただ channel では https:// な URL にリクエスト送れないので、その場合は別の実装に fallback したり、 あと Promise で無理矢理書いてる所を Observable のライブラリ作ってそれで置き換えるとかやりたいです。 Vital.Async.Observable がほしいって話は前から言ってますが一向にやる気配がありませんね…?(ウッ)

あとバイト列を扱う Vital.Data.Blob もほしい。 ほしい物がありすぎてパンク気味です…(実装力不足が悩ましい…)

Unicode の文字情報を開く Vim プラグイン作った

ふと必要になったので作りました。

github.com

動作には open-browser.vim が必要です。

github.com

なにこれ

Vim から FileFormat.Info の指定された文字のページをブラウザで開くプラグインです。

えっどういうこと?

Vim でファイルを開いている時にふと見慣れない文字に出くわすことがあると思います(絵文字とか)。 Vimga コマンドで Unicode のコードポイント情報は見れますが、他の情報が見たい場合もあります。 例えば

  • コードポイントについてる名前
  • サポートされた Unicode のバージョン

等です。そういう時便利なのが FileFormat.Info というサイトです。

例えば 💩 (U+1F4A9) という文字のページを見ると 'PILE OF POO' という名前で「Unicode 6.0.0 (October 2010)」でサポートされた事が分かります。 あと â (U+00E2) という文字LATIN SMALL LETTER A (U+0061)CIRCUMFLEX ACCENT (U+0302) の合成済み文字であるといったことが分かったりします。Unicode奥が深い…

どういう風につかうの

" カーソル下の文字のページを開きます
:OpenBrowserUnicode

" http://www.fileformat.info/info/unicode/char/21ba/ を開きます
:OpenBrowserUnicode U+21ba

" 上と同じ。"U+{16進数}" の形式でなければ引数で与えられた文字列の最初の文字のページを開きます
:OpenBrowserUnicode ↺

ぜひ

使ってみてください。

netupvim で Windows でも最新の Vim を追いかけよう

これは Vim Advent Calendar 2017 その2 の21日目の記事です。


github.com

内容は上記リポジトリの README.md のパクリです。 netupvim 便利だよ!って事が言いたかった。

序文

WindowsVim を使っているとふと最新の Vim欲しい時があります。 そんな時 netupvim があればダブルクリックで最新の Vim を取得できます。

最新の Vim をサクッと持ってくる

リリースから以下のファイルを含むフォルダを解凍し、そのまま UPDATE.bat を実行するだけです。

  • netupvim.exe
  • UPDATE.bat
  • RESTORE.bat

f:id:tyru:20171205024802g:plain

…が、ちょっと待ってください。 今回は最新版の Vim が欲しいのでしたが、普通に実行すると少し古めの Kaoriya の安定版がダウンロードされてきてしまいます。

netupvim は

  1. Kaoriya 安定版
  2. Kaoriya 開発版
  3. Kaoriya 人柱版
  4. 本家版 (vim/vim-win32-installer)

のいずれかに対応しています。 このうち 2~4 を使いたい場合は netupvim.ini を作り、それぞれ以下のように記述します(README からの引用です)。

# 開発版
source = "develop"
# 人柱版
source = "canary"
# vim/vim-win32-installer 版
source = "vim.org"

2つ注意点としては

  • これらの版はあくまでも開発・実験用であり、予告なく不安定な動作の Vim が配信され る可能性があることに留意してください。
  • また、一度 netupvim を実行した後で source プロパティを変更した場合の動作は未 定義です。直近でサポートする予定はありません。

なので先ほど実行してしまいましたが、実行後に変更はしないようにしましょう。

例えば

  1. Kaoriya 安定版
  2. Kaoriya 開発版
  3. Kaoriya 人柱版
  4. 本家版 (vim/vim-win32-installer)

みたいな感じでそれぞれのフォルダを分けて置いとくと良さそうです。

すでにダウンロードした Kaoriya Vim や本家 Vim を最新化したい

安心してください。 netupvim はダウンロードだけでなくアップデートにも対応しています。 というか記事の都合上ダウンロードを先に紹介してしまいましたが、名前からも分かる通り本来アップデートのためのツールです。

先ほど解凍した以下のファイルを vim.exe と同じフォルダにぶち込みます。

  • netupvim.exe
  • UPDATE.bat
  • RESTORE.bat

Kaoriya 安定版ならそのまま実行するだけですが、その他のバージョンの場合先ほど書いた netupvim.ini を作るのを忘れずに。 作ったら UPDATE.bat で一発更新。簡単ですね。

netupvim の更新

先ほどの GIF で気づいた方もいるでしょうが、勝手に更新されます。やっぱり本当にダブルクリックだけで全てが最新化されます。便利。

f:id:tyru:20171206012147j:plain