この記事は Vim 8.0 Advent Calendar の 2 日目の記事です。
Vim 8.0 では、外部リソースとのやりとりを行う機能としてチャンネルが追加されました。
本記事では、チャンネルの基本的な使い方として、ソケット通信を行う方法について簡単に説明します。
詳細については Vim 付属の help を参照してください。
チャンネルを使う
この例では、ローカルホストの HTTP サーバに対して ch_sendraw()
関数を使ってリクエストを送り、結果をハンドラで受け取って表示しています。
" リモートからのレスポンスがあった際に呼ばれるハンドラ関数を定義します
function! s:handle(ch, msg) abort
" レスポンスを表示します。実際にはタイミング次第ではレスポンスが分割される可能性もあり得ます
echo a:msg
" ch_close() 関数でチャンネルを閉じることができます
" リモートから切断された場合は自動的に閉じられます
if ch_status(a:ch) !=# 'closed'
call ch_close(a:ch)
endif
echo ch_status(s:ch)
" => closed
endfunction
" チャンネルを開く際に渡すオプションを用意します
" ここではモードを "raw"、コールバックに先ほど定義したハンドラ関数の参照を指定しています
let s:options = {'mode': 'raw', 'callback': function('s:handle')}
" ch_open() でチャンネルを開きます
let s:ch = ch_open('localhost:80', s:options)
" ここで s:ch 変数は channel 型の値になります。channel 型は、チャンネル機能のために新しく追加された型です
echo ch_status(s:ch)
" => open
" 生の HTTP を叩きます
call ch_sendraw(s:ch, "GET / HTTP/1.0\r\n\r\n")
ここで、s:handle()
関数は非同期に呼び出されます。つまり、サーバからのレスポンスがあるまでの間、ユーザーは編集を続けることができます。
ただし、マルチスレッドではない点に注意してください。s:handle()
が呼び出され、実行されている間はユーザーは Vim の操作ができません。重い処理はしないように注意が必要です。
チャンネルのモード
先ほどの例では raw モードでチャンネルを使用しました。チャンネルには他にも多くのメッセージの送信/受信の方法があります。
チャンネルで行う通信には 4 つのモードがあります。モードによって Vim はメッセージを解釈し、コールバックをメッセージ単位で呼び出してくれたり、メッセージのエンコード/デコードを行ってくれます。
-
JSON
JSON 単位でメッセージをやりとりします。
メッセージには JSON を Vim のオブジェクトに変換したものが渡されます。 -
JS
JSON に似ていますが、JavaScript のオブジェクトを使ってメッセージをやりとりします。
オブジェクトのキーの""
が省略されたり、配列に空の要素が許可されたりなどの違いがあります。 -
NL
行単位でメッセージをやりとりします。
メッセージには末尾の改行を取り除いたものが渡されます。 -
RAW
Vim はメッセージを解釈しません。そのままのデータが使われます。
その他のメッセージの読み書きの方法
例では ch_sendraw()
と、ハンドラを使った読み書きを行いました。他にもいくつか紹介します。
ch_sendexpr()
ch_sendraw()
チャンネルに対してメッセージを送ります。
JSON
モードや JS
モードの際は ch_sendexpr()
を使い、Vim のオブジェクトを渡すことでエンコードされた値が送られます。
NL
モードや RAW
モードの際は ch_sendraw()
を使い、文字列を渡すことで生のデータを送れます。
ch_read()
ch_readraw()
チャンネルのバッファにあるメッセージを読みます。読みとったメッセージはバッファから取り除かれます。また、ハンドラに処理されたメッセージもバッファからすでにないため、読めません。
JSON
モードや JS
モードの際は ch_read()
を使い、デコードされた値が得られます。
NL
モードや RAW
モードの際は ch_readraw()
を使い、生のデータを得られます。NL
モードの場合は、改行単位でメッセージを読み取ります。
ch_evalexpr()
ch_evalraw()
チャンネルに対してメッセージを送り、そのレスポンスを待って、レスポンスを返します。send と read を一度にやるものです。使い分けについても前述のものと同じです。
JSON /JS
|
NL /RAW
|
|
---|---|---|
送信 | ch_sendexpr() |
ch_sendraw() |
受信 | ch_read() |
ch_readraw() |
送受信 | ch_evalexpr() |
ch_evalraw() |
チャンネルには他にもまだまだ機能やオプションがあります。詳細は :help channel
を参照してみてください。