*channel.txt* For Vim バージョン 9.1. Last change: 2023 Aug 15 VIMリファレンスマニュアル by Bram Moolenaar プロセス間通信 *channel* Vim は別のプロセスと通信するのにチャネルを用います。 チャネルはソケットまたはパイプを用います。 *socket-interface* ジョブはプロセスを開始し、プロセスと通信するために使用できます。 Netbeans インターフェイスもチャネルを使っています。|netbeans| 1. 概要 |job-channel-overview| 2. チャネルデモ |channel-demo| 3. チャネルを開く |channel-open| 4. JSON、JS チャネルを使う |channel-use| 5. チャネルコマンド |channel-commands| 6. RAW、NL チャネルを使う |channel-raw| 7. その他のチャネル機能 |channel-more| 8. チャネル関数詳細 |channel-functions-details| 9. チャネルでジョブを開始する |job-start| 10. チャネルなしでジョブを開始する |job-start-nochannel| 11. ジョブ関数 |job-functions-details| 12. ジョブオプション |job-options| 13. ジョブを制御する |job-control| 14. プロンプトバッファを使う |prompt-buffer| 15. Language Server Protocol |language-server-protocol| *E1277* {Vim が |+channel| 機能付きでコンパイルされたときのみ有効} `has('channel')` でこれを確認できる {Vim が |+job| 機能付きでコンパイルされたときのみ有効} `has('job')` でこれを確認できる ============================================================================== 1. 概要 *job-channel-overview* 主に 4 種類のジョブがあります: 1. いくつかの Vim インスタンスを扱うデーモン。Vim はソケットで接続します。 2. 1 つの Vim インスタンスを 1 つのジョブが非同期に処理する。ソケットまたはパ イプを使用します。 3. 短時間、非同期で仕事をするジョブ。ソケットまたはパイプを使用します。 4. フィルタを同期して実行する。パイプを使用します。 ソケットを使用する場合 |job-start|、|job-start-nochannel|、および |channel-open| 参照。2 と 3 の場合は、パイプを使用する 1 つ以上のジョブです (|job-start| 参照)。 4 の場合、":{range}!cmd" コマンドを使用します (|filter| 参照)。 ソケットとパイプ上でこれらのプロトコルを利用できます: RAW 何も知られていない、Vim はメッセージの終わりを知らせない。 NL すべてのメッセージは NL (改行) 文字で終わります。 JSON JSON エンコーディング |json_encode()| JS JavaScript スタイルの JSON 風のエンコーディング |js_encode()| LSP Language Server Protocol のエンコーディング |language-server-protocol| 共通の組み合わせ: - NL モードでパイプを介して接続されたジョブを使用します。例えば、スタイルチェッ カーを実行し、エラーと警告を受け取ります。 - デーモンを使用して、JSON モードでソケットに接続します。例えば、データベース 内の相互参照を参照します。 ============================================================================== 2. チャネルデモ *channel-demo* *demoserver.py* デモには Python が必要です。デモプログラムは次の場所にあります。 $VIMRUNTIME/tools/demoserver.py それをあるターミナルで実行しましょう。そのターミナルを T1 と呼びます。 次に別のターミナルでVimを実行します。そして以下のコマンドでサーバーに接続しま す: > let channel = ch_open('localhost:8765') T1 の中に次のように表示されます: === socket opened === ~ これでサーバーにメッセージを送信できます: > echo ch_evalexpr(channel, 'hello!') このメッセージは T1 で受信され、Vim には応答が送り返されます。 T1 では Vim が送った生のメッセージを確認できます: [1,"hello!"] ~ そしてレスポンスはこうなります: [1,"got it"] ~ この数値はメッセージを送るたびに増加していきます。 サーバーは Vim にコマンドを送信できます。T1 において、次のように正確に (引用符 を含めて文字通りに) タイプしてください: ["ex","echo 'hi there'"] ~ するとそのメッセージが Vim に表示されます。カーソルを1単語先に移動することがで きます: ["normal","w"] ~ 非同期通信を取り扱うためにはコールバック (以下ハンドラー) が必要です: > func MyHandler(channel, msg) echo "from the handler: " .. a:msg endfunc call ch_sendexpr(channel, 'hello!', {'callback': "MyHandler"}) Vim は応答を待つことはありません。これで、サーバーは応答を後で送信し、 MyHandler が呼び出されます。 send を呼ぶたびに毎回コールバックを指定する代わりに、チャネルを開く際に指定す ることもできます: > call ch_close(channel) let channel = ch_open('localhost:8765', {'callback': "MyHandler"}) call ch_sendexpr(channel, 'hello channel!') チャネルを試してみると、何が起こっているのかを知ることができます。あなたは Vim にログファイルに行を書くよう指示することができます: > call ch_logfile('channellog', 'w') |ch_logfile()| 参照. ============================================================================== 3. チャネルを開く *channel-open* チャネルを開くには次のようにします: > let channel = ch_open({address} [, {options}]) if ch_status(channel) == "open" " チャネルを使う |ch_status()| を使用して、チャネルを開くことができたかどうかを確認します。 *channel-address* {address} はドメイン名か IP アドレスで、それにポート番号を続ける、あるいは "unix:" プリフィックスを付けて Unix ドメインソケットパス にできます。例 > www.example.com:80 " ドメイン + ポート 127.0.0.1:1234 " IPv4 + ポート [2001:db8::1]:8765 " IPv6 + ポート unix:/tmp/my-socket " Unix ドメインソケットパス {options} はオプションのエントリを持つ辞書です: *channel-open-options* "mode" は次のいずれかです: *channel-mode* "json" - JSONを使う (詳しくは下記を参照。もっとも使いやすい方法。既定) "js" - JS (JavaScript) エンコーディングを使用し、JSON よりも効率的。 "nl" - NL 文字で終わるメッセージを使う "raw" - raw メッセージを使う "lsp" - language server protocol エンコーディングを使う *channel-callback* *E921* "callback" メッセージ受信時に他のハンドラーで扱われない時に呼ばれます (例えば、ID が0の JSON メッセージ)。これはチャネルのハンドル と、受信したメッセージの 2 つの引数を取ります。例: > func Handle(channel, msg) echo '受信した: ' .. a:msg endfunc let channel = ch_open("localhost:8765", {"callback": "Handle"}) < "mode" が "json", "js", "lsp" の時には、"msg" 引数は受信した メッセージの本文で、Vim の型に変換されています。 "mode" が "nl" の時には、"msg" 引数は NL を除く 1 つのメッセー ジです。 "mode" が "raw" の時には、"msg" 引数はメッセージ全体を格納し た文字列です。 すべてのコールバック: |function()| を使用して、引数および/また は辞書にバインドします。または、"dict.function" という形式を使 用して辞書をバインドします。 コールバックは、通常、Vim がユーザーが文字を入力するのを待って いるとき、「安全な」瞬間にのみ呼び出されます。Vim はマルチス レッドを使用しません。 *close_cb* "close_cb" |ch_close()| を呼び出す以外に、チャネルが閉じられたときに呼び 出される関数。このように定義する必要があります: > func MyCloseHandler(channel) < Vim は close_cb を呼び出す前にデータを処理するコールバックを呼 び出します。したがって、この関数が呼び出されると、それ以上の データはコールバックに渡されません。ただし、コールバックにより Vim のメッセージのチェックが発生するなら、コールバック中に close_cb が呼び出されることがあります。プラグインは何とかこれ を処理する必要があり、これ以上データが来ていないことを知ってお くと便利です。 読み込むメッセージがあるか不明な場合は、try/catch ブロックを使 います: > try let msg = ch_readraw(a:channel) catch let msg = 'no message' endtry try let err = ch_readraw(a:channel, #{part: 'err'}) catch let err = 'no error' endtry < *channel-drop* "drop" メッセージをいつドロップするかを指定します: "auto" メッセージを処理するコールバックがない場合。 "close_cb" もこのために考慮されます。 "never" すべてのメッセージが保存されます。 *channel-noblock* "noblock" |job-noblock| と同じ効果です。書込みの為だけの事項です。 *waittime* "waittime" 接続がミリ秒単位で待機する時間。負の数は永遠に待ちます。 デフォルトはゼロで、待機しません。これは、ローカルサーバーがす でに実行されている場合に便利です。Unix Vim では実際には 1ms の タイムアウトが使われます。多くのシステムではそれが必要なためで す。リモートサーバーには大きな値を使用してください。例: 少なく とも 10msec。 *channel-timeout* "timeout" ブロッキング時にリクエストを待つ時間 (例: |ch_evalexpr()| を使 用するとき。ミリ秒単位。デフォルトは 2000 (2 秒) です。 "mode" が "json" か "js" の時には "callback" はオプションです。これを省略した 場合、メッセージを 1 つ受信するにはメッセージを 1 つ送信する必要があります。 チャネルオプションを開いた後に変更するには、|ch_setoptions()| を使用します。 引数は |ch_open()| に渡されるものと似ていますが、"waittime" は与えられません。 これはチャネルを開く場合にのみ適用されるためです。 例えば、ハンドラーは後から追加したり、変更したりできます: > call ch_setoptions(channel, {'callback': callback}) "callback" が空の場合 (一度も指定しないか、空文字列を指定した場合) ハンドラー は削除されます。 コールバックが呼び出された後、Vim は画面を更新し、カーソルをそれが属する場所に 戻します。コールバックは `:redraw` を行う必要はありません。 タイムアウトは次のように変更できます: > call ch_setoptions(channel, {'timeout': msec}) < *channel-close* *E906* チャネルを使い終わったら、以下のように切断してください: > call ch_close(channel) ソケットが使用されていると、両方向のソケットが閉じられます。パイプが使用されて いると (stdin/stdout/stderr)、それらはすべて閉じられます。これはあなたが望むも のではないかもしれません!|job_stop()| でジョブを停止する方が良いかもしれませ ん。すべての先読みは破棄され、コールバックは呼び出されなくなります。 チャネルは 3 つの段階で閉じられることに注意してください: - I/O が終了し、ログメッセージ: "Closing channel" が表示されます。呼び出すた めの読み込みまたはコールバックのキューに入れられたメッセージがまだ残ってい る可能性があります。 - 先読みがクリアされ、ログメッセージ: "Clearing channel" が表示されます。変 数によっては引き続きチャネルを参照することがあります。 - チャネルが解放され、ログメッセージ: "Freeing channel" が表示されます。 チャネルを開くことができない場合、エラーメッセージが表示されます。MS-Windows と Unix には違いがあります: Unix では、ポートが存在しないとき、|ch_open()| は すぐに失敗します。MS-Windows では "waittime" が適用されます。 *E898* *E901* *E902* チャネルを読み書きする際にエラーが発生した場合、チャネルは閉じられます。 *E630* *E631* ============================================================================== 4. JSON、JS チャネルを使う *channel-use* "mode" が JSON の場合は、以下のようにメッセージを同期的に送信できます: > let response = ch_evalexpr(channel, {expr}) これは通信相手から応答があるまで待ち合わせます。 mode が JS の場合は、メッセージが JavaScript エンコーディングを使用する点を除 いて、これは同じです。その違いについては、|js_encode()| 参照。 応答を処理せずにメッセージを送信する、またはチャネルコールバックに応答を処理さ せるには: > call ch_sendexpr(channel, {expr}) メッセージを送信し、応答を特別な関数で非同期的に処理する場合には、このようにし ます: > call ch_sendexpr(channel, {expr}, {'callback': Handler}) Vim は、メッセージIDを使用して要求との応答を照合します。応答が受信されると、 コールバックが呼び出されます。同じ ID を持つさらなる応答は無視されます。あなた のサーバーが複数の応答を返信する場合、ID ゼロで送信する必要があります。それら はチャネルコールバックに渡されます。 {expr} は JSON に変換され、配列で包まれます。{expr} として文字列 "hello" を送 信した場合に、通信相手が受け取るメッセージの例は次のようになります: [12,"hello"] ~ 送信される JSON のフォーマットはこのようになっています: [{number},{expr}] {number} には毎回異なる値が入ります。これは応答があるならば、必ず使われます: [{number},{response}] このようにして、受信したメッセージがどの送信メッセージに対応するかを知ることが でき、正しいハンドラーを呼び出すことができます。これによって応答メッセージの到 着順序を気にしなくても良くなります。 改行文字が JSON テキストを終了しています。これは、読み込まれたテキストを区切る ために使用できます。例えば Python では: splitidx = read_text.find('\n') message = read_text[:splitidx] rest = read_text[splitidx + 1:] 送信側はかならず有効な JSON を Vim へ送らなければなりません。Vim は JSON とし て解釈することで、受信メッセージの終端をチェックします。終端を受信することが、 メッセージを受理する唯一の方法です。メッセージの後の改行はオプションです。 サーバープロセスが Vim からのメッセージを受信すること無く、メッセージを送信す るには、数値に 0 を使う必要があります。 [0,{response}] するとチャネルのハンドラーが {response} を Vim の方に変換したものを受け取るで しょう。チャネルにハンドラーが関連付けられていない場合には、メッセージは破棄さ れます。 JSON または JS チャネルで |ch_sendraw()| および |ch_evalraw()| を使用すること もできます。その場合呼び出し元は、正しくエンコードとデコードを行う完全な責任が あります。 ============================================================================== 5. チャネルコマンド *channel-commands* JSON チャネルを使用すると、サーバープロセス側は Vim へコマンドを送信できます。 そのコマンドはチャネルのハンドラーを介さずに、Vim の内部で実行されます。 実行可能なコマンドは以下のとおりです: *E903* *E904* *E905* ["redraw", {forced}] ["ex", {Ex コマンド}] ["normal", {ノーマルモードコマンド}] ["eval", {式}, {数値}] ["expr", {式}] ["call", {関数名}, {引数リスト}, {数値}] ["call", {関数名}, {引数リスト}] これらを使うときは、これらのコマンドが何をするかに十分気をつけてください! ユーザーが何をしているかによっては容易に干渉してしまいます。トラブルを避けるに は |mode()| を使い、エディタが期待した状態にあるかチェックしてください。例え ば、コマンド実行ではなくテキストとして入力させたい文字列を送るには、以下のよう にします: ["ex","if mode() == 'i' | call feedkeys('ClassName') | endif"] ~ これらのコマンドのエラーは、表示が乱れないようにするため、通常は報告されませ ん。表示したい場合は、'verbose' オプションを 3 以上に設定してください。 コマンド "redraw" ~ 他のコマンドは明示的に画面を更新しないので、カーソルを動かさずに一連のコマンド を送ることができます。再描画は他のコマンドの副作用として発生することがありま す。変更されたテキストを表示し、それが属する場所にカーソルを表示するには、 "redraw" コマンドで終了する必要があります。 引数は通常は空の文字列です: ["redraw", ""] ~ 最初に画面をクリアするには "force" を渡してください: ["redraw", "force"] ~ コマンド "ex" ~ "ex" コマンドは Ex コマンドを実行します。完了やエラーの応答はありません。 |autoload| スクリプトの中の関数を使えます: ["ex","call myscript#MyFunc(arg)"] "call |feedkeys()|" を使用してキーシーケンスを挿入することもできます。 エラーが発生すると、チャネルログにメッセージが書き込まれ、存在する場合は v:errmsg にエラーが設定されます。 コマンド "normal" ~ "normal" コマンドは ":normal!" のように実行され、コマンドはマップされません。 カーソルの下の折り畳みを開く例: ["normal" "zO"] コマンド "expr" (応答あり) ~ "expr" コマンドは、式の結果を得るために使うことができます。例えば、現在のバッ ファ内の行数を取得するには、次のようにします: ["expr","line('$')", -2] ~ 式の結果を返します: [-2, "last line"] ~ 形式は次のとおりです: [{number}, {result}] {number} は、リクエストに指定したのと同じものです。Vim が送信するメッセージと の混乱を避けるには、負の数を使用します。リクエストとレスポンスを一致させるに は、リクエストごとに異なる番号を使用します。 {result} は評価の結果であり、JSON エンコードされています。評価が失敗したり、結 果を JSON でエンコードできない場合は、文字列 "ERROR" となります。 コマンド "expr" (応答なし) ~ このコマンドは上記の "expr" に近いのですが、応答を返信しません。 例: ["expr","setline('$', ['one', 'two', 'three'])"] ~ リクエストに第 3 引数はありません。 コマンド "call" ~ これは "expr" に似ていますが、式全体を文字列として渡す代わりに、関数の名前と引 数のリストを渡します。これは、引数の文字列への変換を避け、エスケープして連結し ます。例: ["call", "line", ["$"], -2] ~ 応答が送信されない場合は、第 4 引数を省いてください: ["call", "setline", ["$", ["one", "two", "three"]]] ~ ============================================================================== 6. RAW、NL チャネルを使う *channel-raw* モードが RAW か NL の場合には、以下のようにしてメッセージを送信します: > let response = ch_evalraw(channel, {string}) {string} はそのまま送信されます。受信した応答メッセージは直ちにチャネルから読 み込み可能になります。この時、Vim にはメッセージの終了をどう判断するかがわかり ませんから、あなた自身が面倒を見る必要があります。タイムアウトは、最初のバイト を読み取るために適用され、その後は何も待つことはありません。 mode が "nl" の場合、同様の方法でメッセージを送信できます。あなたは各メッセー ジの後に NL に入れなければなりません。したがって、一度に NL で終わる複数のメッ セージを送信することもできます。応答は最初の NL までのテキストとなります。これ は空のレスポンスの NL だけでもかまいません。チャネルタイムアウトの前に NL が読 み取られなかった場合、空の文字列が返されます。 応答を必要としないメッセージを送信するには以下のようにします: > call ch_sendraw(channel, {string}) プロセス {訳注:サーバーのこと} はレスポンスを返し、チャネルのハンドラーに渡さ れます。 *channel-onetime-callback* メッセージを送信し、レスポンスを特定の関数で非同期的に取り扱うには以下のように します: > call ch_sendraw(channel, {string}, {'callback': 'MyHandler'}) この {string} は JSON にもできます。その場合、|json_encode()| でそれを作成し |json_decode()| で受信した JSON メッセージを取り扱います。 生のチャネルで |ch_evalexpr()| または |ch_sendexpr()| を使用することはできませ ん。 Vim の文字列に NUL バイトを含めることはできません。NUL バイトを送受信するには、 バッファから読み書きしてください。|in_io-buffer| と |out_io-buffer| 参照。 ============================================================================== 7. その他のチャネル機能 *channel-more* チャネルのステータスを取得するには、ch_status(channel) を使用します。ありうる 結果は次のとおりです: "fail" チャネルを開くことができませんでした。 "open" チャネルを使用することができます。 "buffered" チャネルは閉じられましたが読み込むデータがあります。 "closed" チャネルが閉じられました。 チャネルに関連付けられたジョブを取得するには: ch_getjob(channel) チャネルから 1 つのメッセージを読むには: > let output = ch_read(channel) これは、チャネルのタイムアウトを使用します。タイムアウトなしで読むには、利用可 能なメッセージを取得するだけです: > let output = ch_read(channel, {'timeout': 0}) メッセージが利用できなかった場合、結果は JSON または JS モードのチャネルでは v:none、RAW または NL チャネルでは空の文字列です。|ch_canread()| を使用して、 何かがあるかどうかを調べることができます。 コールバックメッセージがない場合、メッセージは破棄されます。これを回避するに は、チャネルにコールバックを追加します。 使用可能な RAW チャネルからすべての通常出力を読み込むには: > let output = ch_readraw(channel) 使用可能な RAW チャネルからすべてのエラー出力を読み込むには: > let output = ch_readraw(channel, {"part": "err"}) Note チャネルが NL モードの場合、ch_readraw() は各呼び出しで1行しか返しません。 ch_read() と ch_readraw() はチャネルタイムアウトを使用します。その時間内に何も 読み込めない場合、空の文字列が返されます。別のタイムアウトをミリ秒で指定するに は、"timeout" オプションを使用します: {"timeout": 123} ~ エラー出力から読み込むには、"part" オプションを使用します: {"part": "err"} ~ 特定の ID を持つメッセージを JS または JSON チャネルで読み取るには: {"id": 99} ~ ID が指定されていないか、または ID が -1 の場合、最初のメッセージが返されます。 これは、このメッセージを待っているコールバックをすべて無効にします。 RAW チャネルの場合、Vim はメッセージの終わりを知らないので、利用可能なものを返 します。 NL チャネルの場合、これは 1 つのメッセージを返します。 JS または JSON チャネルの場合、これは1つのデコードされたメッセージを返します。 これには、任意のシーケンス番号が含まれます。 ============================================================================== 8. チャネル関数詳細 *channel-functions-details* ch_canread({handle}) *ch_canread()* {handle} から何か読むものがあれば非ゼロを返す。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 これはチャネルから、都合のよいときに読むのに便利である。例え ば、タイマーから。 チャンルがコールバックを持っていない場合、メッセージはドロップ されることに注意。それを防ぐには close コールバックを追加する こと。 |method| としても使用できる: > GetChannel()->ch_canread() ch_close({handle}) *ch_close()* {handle} を閉じる。|channel-close| を参照。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 閉じられたコールバックは呼び出されない。 |method| としても使用できる: > GetChannel()->ch_close() ch_close_in({handle}) *ch_close_in()* {handle} の "入力" を閉じる。|channel-close-in| を参照。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 閉じられたコールバックは呼び出されない。 |method| としても使用できる: > GetChannel()->ch_close_in() ch_evalexpr({handle}, {expr} [, {options}]) *ch_evalexpr()* {handle} へ {expr} を送信する。{expr} はチャネル側と同じ型にエ ンコードされる。この関数は生のチャネルでは使用できない。 |channel-use| を参照。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 "lsp" モードでチャネル使用時は、{expr} は |Dict| でなければな らない。 *E917* {options} は辞書でなければならない。また "callback" のエントリ を持ってはならない。また個別のリクエストに対して "timeout" を 持つ事ができる。 ch_evalexpr() は応答を待ち、式をデコードした物が返される。エ ラーもしくはタイムアウトの場合は空の |String|、あるいは "lsp" モードでチャネル使用時は空の |Dict| が返る。 Note 応答を待っている間、Vimは他のメッセージを処理する。これが トラブルを引き起こさないことを確認する必要がある。 |method| としても使用できる: > GetChannel()->ch_evalexpr(expr) ch_evalraw({handle}, {string} [, {options}]) *ch_evalraw()* {handle} へ {string} を送信する。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 |ch_evalexpr()| と同様に動作する。しかしリクエストをエンコード したり応答をデコードしたりはしない。呼び出しは正しいコンテンツ である事が保証される。また NL モードでは改行が行われるが、ここ では改行が付与されない。NL は応答から削除される。 Note: Vim は Raw チャネル上で受け取るテキストがいつ完了するの か分からない。最初の部分だけを返す可能性もあり、残りを取り出す ために |ch_readraw()| を使う必要がある。 |channel-use| を参照。 |method| としても使用できる: > GetChannel()->ch_evalraw(rawstring) ch_getbufnr({handle}, {what}) *ch_getbufnr()* 文字列 {what} に使用されている {handle} のバッファ番号を得る。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 {what} は標準エラーの為の "err"、標準出力の為の "out"、もしく はソケット出力の為の空文字列が指定できる。 バッファが存在しない場合は -1 が返る。 |method| としても使用できる: > GetChannel()->ch_getbufnr(what) ch_getjob({channel}) *ch_getjob()* {channel} に関連付けられた Job を得る。 もしジョブが無い場合、戻り値の Job で |job_status()| を呼び出 すと "fail" が返される。 |method| としても使用できる: > GetChannel()->ch_getjob() ch_info({handle}) *ch_info()* {handle} に関する情報を辞書で返す。アイテムは: "id" チャネル番号 "status" ch_status() と同様に、"open", "buffered" ま たは "closed" ch_open() で開いた場合: "hostname" アドレスのホスト名 "port" アドレスのポート "path" Unix ドメインソケットのパス "sock_status" "open" または "closed" "sock_mode" "NL", "RAW", "JSON" または "JS" "sock_io" "socket" "sock_timeout" タイムアウト(ミリ秒) Note "path" は Unix ドメインソケットの場合にのみ存在し、通常は "hostname" と "port" が代わりに存在する。 job_start() で開いた場合: "out_status" "open", "buffered" または "closed" "out_mode" "NL", "RAW", "JSON" または "JS" "out_io" "null", "pipe", "file" または "buffer" "out_timeout" タイムアウト(ミリ秒) "err_status" "open", "buffered" または "closed" "err_mode" "NL", "RAW", "JSON" または "JS" "err_io" "out", "null", "pipe", "file" または "buffer" "err_timeout" タイムアウト(ミリ秒) "in_status" "open" または "closed" "in_mode" "NL", "RAW", "JSON", "JS" または "LSP" "in_io" "null", "pipe", "file" または "buffer" "in_timeout" タイムアウト(ミリ秒) |method| としても使用できる: > GetChannel()->ch_info() ch_log({msg} [, {handle}]) *ch_log()* |ch_logfile()| によってログファイルが開かれている場合はチャネ ルのログファイルに文字列 {msg} を書き込む。 テキスト "ch_log():" はメッセージの前に追加され、この関数の呼 び出しからであることを明確にし、ログファイルの中から見付けるの を容易にする。 {handle} が渡されている場合はチャネル番号がメッセージの中で使 われる。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 使用されるチャネル番号のチャネルは開いていなければならない。 |method| としても使用できる: > 'did something'->ch_log() ch_logfile({fname} [, {mode}]) *ch_logfile()* {fname} へチャネルの挙動ログ出力を開始する。 {fname} が空の場合、ロギングは停止する。 {mode} が省略されるか "a" を含むか "o" の場合、ファイルへの追 加になる。 {mode} が "w" を含み "a" を含まない場合、空のファイルで開始さ れる。 {mode} が "o" を含む場合、全ての端末出力が記録される。 その他の場合は、興味深い端末出力のみがログに記録される。 ログメッセージを書き込むには |ch_log()| を使用する。UNIX で "tail -f" にてリアルタイムで何が行われているかが見える様に、 ファイルはメッセージ毎にフラッシュされる。 最初期にログを有効にし、初期化中に端末から何を受信したのかを参 照するには、|--log| ("ao" モードを使用) を使う: > vim --log logfile < この関数は |sandbox| 内では無効である。 Note: チャネルとの通信はファイルに格納される。これには、あなた が端末ウィンドウ上でタイプしたパスワードのような、機密やプライ バシーに関する情報を含むことがあることに注意すること。 |method| としても使用できる: > 'logfile'->ch_logfile('w') ch_open({address} [, {options}]) *ch_open()* {address}へのチャネルを開く。|channel|を参照。 チャネルを返す。失敗をチェックするには |ch_status()| を使用す る。 {address} は文字列で、受け付け可能な形式は |channel-address| を参照のこと。 {options} が与えられる場合は辞書でなければならない。 |channel-open-options| を参照。 |method| としても使用できる: > GetAddress()->ch_open() ch_read({handle} [, {options}]) *ch_read()* {handle} から読み込みメッセージを受信する。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 NL チャネルの場合、何も読み込むものがない (チャネルが閉じられ た) 場合を除いて NL を受信するまで待つ。 |channel-more| を参照。 |method| としても使用できる: > GetChannel()->ch_read() ch_readblob({handle} [, {options}]) *ch_readblob()* ch_read() と同様に動作するが、バイナリデータを読み込んで |Blob| を返す。 |channel-more| を参照。 |method| としても使用できる: > GetChannel()->ch_readblob() ch_readraw({handle} [, {options}]) *ch_readraw()* ch_read() と同様に動作するが JS や JSON の場合でもメッセージは デコードされない。NL チャネルの場合、NL を受信するまで待つこと はないが、それ以外は ch_read() と同様に動作する。 |channel-more| を参照。 |method| としても使用できる: > GetChannel()->ch_readraw() ch_sendexpr({handle}, {expr} [, {options}]) *ch_sendexpr()* {handle} へ {expr} を送信する。{expr} はチャネル側と同じ型にエ ンコードされる。この関数は生のチャネルでは使用できない。 |channel-use| を参照。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 チャネルは開いていなければならない。 "lsp" モードでチャネル使用時は、{expr} は |Dict| でなければな らない。 チャネルのモードが "lsp" の場合、辞書を返す。それ以外は空の文 字列を返す。{options} に "callback" の項目が存在する場合、リク エストメッセージのIDを含む辞書を返す。IDはLSPサーバーへキャン セルのリクエスト(必要な場合)を送るのに使うことができる。エラー のときは空の辞書を返す。 {expr} に対して応答メッセージを受けないのであれば、{options} に "callback" 項目を指定しないこと。 |method| としても使用できる: > GetChannel()->ch_sendexpr(expr) ch_sendraw({handle}, {expr} [, {options}]) *ch_sendraw()* 文字列 |String| または |Blob| {expr} を {handle} に送る。 |ch_sendexpr()| と同様に動作するが、リクエストをエンコードした り応答をデコードしたりはしない。呼び出しは正しいコンテンツで ある事が保証される。また NL モードでは改行が行われるが、ここ では改行が付与されない。NL は応答から削除される。 |channel-use| を参照。 |method| としても使用できる: > GetChannel()->ch_sendraw(rawexpr) ch_setoptions({handle}, {options}) *ch_setoptions()* {handle} にオプションを設定する: "callback" チャネルのコールバック "timeout" デフォルトの読み込みタイムアウト(ミリ秒) "mode" チャネル全体のモード より詳しい説明は |ch_open()| を参照。 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 Note: モードの変更はキューイングされているメッセージを失ってし まうかもしれない。 以下のオプションは変更できない: "waittime" |ch_open()|だけで適用できる |method| としても使用できる: > GetChannel()->ch_setoptions(options) ch_status({handle} [, {options}]) *ch_status()* {handle} の状態を返す: "fail" チャネルのオープンに失敗 "open" チャネルは利用可能 "buffered" チャネルは読込可能、または書き込まれて いない "closed" チャネルは利用不可 {handle} はチャネルもしくはチャネルを持つジョブであっても良い。 チャネルは閉じられているが |ch_read()| を使って残ったデータを まだ読み取る事ができる場合には "buffered" が利用できる。 {options} が指定された場合、"part" 要素でチャネルのどのパート ("out" または "err") の状態を返すかを指定できる。例えば、エ ラーの状態を得るには: > ch_status(job, {"part": "err"}) < |method| としても使用できる: > GetChannel()->ch_status() ============================================================================== 9. チャネルでジョブを開始する *job-start* *job* ジョブを開始し、stdin/stdout/stderr のチャネルを開くには: > let job = job_start(command, {options}) チャネルを得るには: > let channel = job_getchannel(job) チャネルは NL モードを使用します。別のモードが必要な場合は、{options} でこれを 指定することをお勧めします。後でモードを変更すると、一部のテキストがすでに受信 され、正しく解析されていない可能性があります。 コマンドが処理したい出力行を生成する場合は、stdout のハンドラを指定します: > let job = job_start(command, {"out_cb": "MyHandler"}) この関数は、チャネルとメッセージで呼び出されます。あなたはこれをこのように定義 します: > func MyHandler(channel, msg) ハンドラがなければ、|ch_read()| または |ch_readraw()| で出力を読み取る必要があ ります。クローズコールバックでこれを行うことができます。|read-in-close-cb| 参 照。 出力を読み取る前にジョブが終了すると、出力が失われる可能性があることに注意して ください。これはシステムによって異なります (Unix 上では、パイプの書き込み終了 を閉じると EOF が得られます)。これを避けるには、ジョブが終了する前にそれをしば らくスリープさせること。 "out_cb" に定義されたハンドラは stderr を受け取りません。もし個別に扱いたい場 合は、"err_cb" ハンドラを追加します: > let job = job_start(command, {"out_cb": "MyHandler", \ "err_cb": "ErrHandler"}) 1 つのハンドラで stderr と stdout の両方を処理する場合は、"callback" オプショ ンを使用します: > let job = job_start(command, {"callback": "MyHandler"}) システムによっては、ジョブを開始するとVimをバックグラウンドに移動することがあ り、開始されたジョブはフォーカスを取得します。これを避けるには `foreground()` 関数を使用してください。これは、早く呼び出されたとき、コールバックハンドラ内に 置いたとき、またはジョブが開始した後にタイマーを使用して呼び出すときは、必ずし も機能しない場合があります。 ch_evalraw() でコマンドにメッセージを送ることができます。チャネルが JSON また は JS モードの場合、ch_evalexpr() を使用できます。 使用できるオプションがいくつかあります。|job-options| 参照。 例えば、ジョブを開始し、その出力をバッファ "dummy" に書き込むには: > let logjob = job_start("tail -f /tmp/log", \ {'out_io': 'buffer', 'out_name': 'dummy'}) sbuf dummy バッファからのジョブ入力 ~ *in_io-buffer* バッファから読み取るジョブを実行するには: > let job = job_start({command}, \ {'in_io': 'buffer', 'in_name': 'mybuffer'}) < *E915* *E918* バッファは、|bufnr()| と同様の名前で見つけられます。バッファは、job_start() が呼び出されたときに存在し、ロードされていなければなりません。 デフォルトでは、これはバッファ全体を読み込みます。これは "in_top" と "in_bot" オプションで変更できます。 特殊モードは、"in_top" が 0 に設定され、"in_bot" が設定されていない場合です。 バッファに行が追加されるたびに、最後の 1 行がジョブ stdin に送信されます。これ により、最後の行を編集し、Enter を押したときに送信することができます。 *channel-close-in* 特殊モードを使用しないときは、最後の行が書き込まれた後にパイプまたはソケットが 閉じられます。これは、入力が終了した読み取り終了を知らせます。|ch_close_in()| を使用すると、より早く終了することもできます。 テキストの NUL バイトはジョブに渡されます (内部では Vim はこれらを NL バイトと して格納します)。 クローズコールバックでジョブ出力を読み込む ~ *read-in-close-cb* ジョブに時間がかかり、中間結果が必要ない場合は、クローズコールバックを追加して そこの出力を読み取ることができます: > func! CloseHandler(channel) while ch_status(a:channel, {'part': 'out'}) == 'buffered' echomsg ch_read(a:channel) endwhile endfunc let job = job_start(command, {'close_cb': 'CloseHandler'}) あなたは "echomsg" よりも役に立つ何かをしたいでしょう。 ============================================================================== 10. チャネルなしでジョブを開始する *job-start-nochannel* チャネルを作成せずに別のプロセスを開始するには: > let job = job_start(command, \ {"in_io": "null", "out_io": "null", "err_io": "null"}) これはバックグラウンドで {command} を開始し、Vim はそれが完了するのを待ちませ ん。 Vim が stdin、stdout、stderr のいずれも接続されていないと判断すると、チャネル は作成されません。コマンドが停止するのを避けるために、リダイレクションをコマン ドに含めることがよくあります。 使用できるオプションがいくつかあります。|job-options| 参照。 *job-start-if-needed* アドレスへの接続が動作しない時にのみジョブを開始するには、次のような操作を行い ます: > let channel = ch_open(address, {"waittime": 0}) if ch_status(channel) == "fail" let job = job_start(command) let channel = ch_open(address, {"waittime": 1000}) endif Note ch_open() の待ち時間は、ポートを利用可能にするためにジョブに 1 秒を与える ことに注意してください。 ============================================================================== 11. ジョブ関数 *job-functions-details* job_getchannel({job}) *job_getchannel()* {job}が使用しているチャネルハンドルを取得する。 ジョブにチャネルがないかどうかを確認するには: > if string(job_getchannel(job)) == 'channel fail' < |method| としても使用できる: > GetJob()->job_getchannel() job_info([{job}]) *job_info()* {job}に関する情報を持つ辞書を返す: "status" |job_status()|が返すもの "channel" |job_getchannel()|が返すもの "cmd" ジョブを開始するのに使われるコマンド引数のリス ト "process" プロセス ID "tty_in" 端末入力名、何もないときには空 "tty_out" 端末出力名、何もないときには空 "exitval" "status" が "dead" のときのみ有効 "exit_cb" 終了時に呼び出される関数 "stoponexit" |job-stoponexit| Unixのみ: "termsig" プロセスを終了させたシグナル (値については |job_stop()| を参照) "status"が "dead" の場合にのみ有効である MS-Windowsのみ: "tty_type" 使用している仮想コンソールのタイプ。 値は "winpty" または "conpty"。 'termwintype' を参照。 引数なしの場合、すべてのジョブオブジェクトのリストを返す。 |method| としても使用できる: > GetJob()->job_info() job_setoptions({job}, {options}) *job_setoptions()* {job}のオプションを変更する。サポートされているものは: "stoponexit" |job-stoponexit| "exit_cb" |job-exit_cb| |method| としても使用できる: > GetJob()->job_setoptions(options) job_start({command} [, {options}]) *job_start()* ジョブを開始し、ジョブオブジェクトを返す。|system()|と|:!cmd| とは異なり、これはジョブが終了するのを待つことはない。 端末ウィンドウ内でジョブを開始する方法については |term_start()| を参照。 ジョブが起動に失敗した場合、返されたジョブオブジェクトの |job_status()| は "fail" となり、どのコールバックも呼び出され ない。 {command}は文字列にできる。これはMS-Windowsで最も効果的である。 Unixでは、それはexecvp()に渡すために空白で区切られたパーツに分 割される。二重引用符で囲まれた引数には空白を含められる。 {command}はリストにでき、最初の項目は実行可能ファイルであり、 残りの項目は引数である。すべての項目は文字列に変換される。これ はUnixで最も効果的である。 MS-Windowsでは、|job_start()|はGUIアプリケーションを隠す。それ を表示したい場合は、|:!start|を代わりに使用すること。 コマンドはシェルではなく直接実行され、'shell' オプションは使用 されない。シェルを使用するには: > let job = job_start(["/bin/sh", "-c", "echo hello"]) < または: > let job = job_start('/bin/sh -c "echo hello"') < これは2つのプロセス、シェルとそれが実行するコマンドを開始する ことに注意すること。これを望まない場合は、"exec" シェルコマン ドを使用する。 Unixでは$PATHは、コマンドにスラッシュが含まれていない場合にの み実行可能ファイルを検索するために使用される。 ジョブはVimと同じ端末を使用する。ジョブがstdinを読む場合、ジョ ブとVimが入力を奪い合うことになるのでうまく動作しない。問題を 避けるためにstdinとstdoutをリダイレクトする: > let job = job_start(['sh', '-c', "myserver /dev/null"]) < 返されたジョブオブジェクトを使用して、|job_status()|でステータ スを取得し、|job_stop()|でジョブを停止することができる。 Note: ジョブオブジェクトは、それを参照するものがない場合削除さ れる。これは stdin および stdout を閉じ、エラーによるジョブの 失敗を引き起こす。これを回避するにはジョブへの参照を維持する。 したがって、以下の代わりに: > call job_start('my-command') < このようにし: > let myjob = job_start('my-command') < さらに、ジョブが必要なくなったとき、もしくは (開始時にメッセー ジが表示されたときのように) ジョブが失敗した後に、"myjob" を unlet する。関数のローカル変数は、関数が終了すると消滅すること に注意すること。必要であればスクリプトローカルな変数を使用する ことができる: > let s:myjob = job_start('my-command') < {options}は辞書でなければならない。多くのオプション項目を含め ることができる。|job-options|参照。 |method| としても使用できる: > BuildCommand()->job_start() job_status({job}) *job_status()* *E916* {job}のステータスをStringで返す: "run" ジョブが実行中 "fail" ジョブを開始できなかった "dead" ジョブが死んだか実行後にジョブが停止した Unixでは存在しないコマンドは、失敗が検出される前にフォークが発 生するため、"fail" ではなく "dead" になる。 もし Vim9 script の変数が "job" 型で宣言されるが割り当てされな かったなら、その変数への job_status() での問い合せは "fail" を 返す。 exitコールバックが "exit_cb" オプションで設定され、ジョブが "dead" と検出された場合、コールバックが呼び出される。 詳細は|job_info()|参照。 |method| としても使用できる: > GetJob()->job_status() job_stop({job} [, {how}]) *job_stop()* {job}を停止する。これはジョブを通知するためにも使用できる。 {how}が省略されたり、"term" の場合、ジョブは終了する。Unixでは SIGTERMが送信される。MS-Windowsでは、ジョブは強制的に終了する ("優しい"方法は存在しない)。これはプロセスグループに行くので、 子供たちも影響を受けるかもしれない。 UNIXへの影響: "term" SIGTERM (既定) "hup" SIGHUP "quit" SIGQUIT "int" SIGINT "kill" SIGKILL (停止するための最も強い方法) number その番号の信号 MS-Windowsへの影響: "term" プロセスを強制終了 (既定) "hup" CTRL_BREAK "quit" CTRL_BREAK "int" CTRL_C "kill" プロセスを強制終了 Others CTRL_BREAK Unixでは、シグナルはプロセスグループに送られる。これは、ジョブ が "sh -c command" であるときにシェルとコマンドの両方に影響を 与えることを意味する。 結果は数値で、操作が実行できる場合は1、システムでは "how" がサ ポートされていない場合は0。 ジョブが実際に停止したかどうかは、操作が実行された場合でも、 |job_status()|でチェックする必要があることに注意すること。 ジョブのステータスが "dead" の場合、シグナルは送られない。これ は、(特にプロセス番号が再利用される Unix において) 間違ったジョ ブを停止することを防ぐためである。 "kill" を使用したとき、Vim はジョブが死ぬと想定し、チャネルを 閉じる。 |method| としても使用できる: > GetJob()->job_stop() ============================================================================== 12. ジョブオプション *job-options* job_start() の {options} 引数は辞書です。すべての入力はオプションです。 job_setoptions(job, {options}) を使用して、ジョブの開始後にいくつかのオプショ ンを使用できます。ch_setoptions(channel, {options}) を使用して、ジョブに関連す るチャネルで多くのオプションを使用できます。|job_setoptions()| および |ch_setoptions()| 参照。 *in_mode* *out_mode* *err_mode* "in_mode" stdin 用のモード、パイプを使用している場合にのみ。 "out_mode" stdout 用のモード、パイプを使用している場合にのみ。 "err_mode" stderr 用のモード、パイプを使用している場合にのみ。 値については、|channel-mode| 参照。 Note: "mode" を設定すると、パーツ固有のモードが上書き されます。したがって、最初に "mode" を、後でパーツ固有 のモードを設定します。 Note: ファイルやバッファに書き込むときやバッファから読 み込むときは、NL モードがデフォルトで使用されます。 *job-noblock* "noblock": 1 書き込み時に、非ブロッキング書き込み呼び出しを使用しま す。これは Vim が間に他のメッセージを処理する必要があ る場合に止まるのを回避します。例えば、ジョブが Vim に データを返すとき。これは、`ch_sendraw()` が返されたと きに、すべてのデータがまだ書き込まれていない可能性があ ることを意味します。 このオプションはパッチ 8.1.0350 で追加され、以下を使っ て検査します: > if has("patch-8.1.350") let options['noblock'] = 1 endif < *job-callback* "callback": handler チャネルの任意の部分で何かを読むためのコールバック。 *job-out_cb* *out_cb* "out_cb": handler stdout で読み込むべきものがあるときのコールバック。 チャネルがパイプを使用している場合のみ。"out_cb" が設 定されていない場合は、チャネルコールバックが使用されま す。2 つの引数はチャネルとメッセージです。 *job-err_cb* *err_cb* "err_cb": handler stderr で読み込むべきものがあるときのコールバック。 チャネルがパイプを使用している場合のみ。"err_cb" が設 定されていない場合は、チャネルコールバックが使用されま す。2 つの引数はチャネルとメッセージです。 *job-close_cb* "close_cb": handler チャネルが閉じられるときのコールバック。 |ch_open()| の "close_cb" と同じです。|close_cb| 参照。 *job-drop* "drop": when メッセージをいつドロップするかを指定します。 |ch_open()| の "drop" と同様(|channel-drop| 参照)。 "auto" の場合、exit_cb は考慮されません。 *job-exit_cb* "exit_cb": handler ジョブが終了したときのコールバック。引数はジョブと終了 ステータスです。 Vim は、終了したジョブに対して最大 10 回/秒をチェック します。チェックは、|job_status()| を呼び出すことによっ てトリガーすることもでき、exit_cb ハンドラを呼び出すこ とができます。 データがバッファリングされ、プロセスが終了した後もコー ルバックが呼び出されることに注意してください。 *job-timeout* "timeout": time ブロッキング時にリクエストを待つ時間 (例: ch_evalexpr() を使用するとき。ミリ秒単位。デフォルトは 2000 (2 秒) です。 *out_timeout* *err_timeout* "out_timeout": time stdout のタイムアウト。パイプ使用時のみ。 "err_timeout": time stderr のタイムアウト。パイプ使用時のみ。 Note: "timeout" を設定すると、パーツ固有のモードが上書 きされます。したがって、最初に "timeout" を設定し、後 でパーツ固有のモードを設定します。 *job-stoponexit* "stoponexit": {signal} Vim が終了すると {signal} をジョブに送ります。可能な値 については、|job_stop()| 参照。 "stoponexit": "" Vim が終了してもジョブを停止しません。 デフォルトは "term" です。 *job-term* "term": "open" 新しいウィンドウでターミナルを起動し、ジョブ stdin/stdout/stderr を接続します。`:terminal` を使用す ることと同じです。 NOTE: まだ実装されていません! "channel": {channel} 新しいチャネルを作成する代わりに、既存のチャネルを使用 します。新しいジョブに使用されるチャネルの部分は、以前 使用された部分から切り離されます。チャネルが別のジョブ で引き続き使用されていた場合、I/O エラーが発生する可能 性があります。 既存のコールバックやその他の設定が残っています。 "pty": 1 可能であれば、パイプのかわりに pty (疑似 tty) を使いま す。これは端末ウィンドウとの組み合わせで最も便利です。 |terminal| を参照。 {Unix系システムのみ} *job-in_io* *in_top* *in_bot* *in_name* *in_buf* "in_io": "null" stdin を切断する (/dev/null から読み込む) "in_io": "pipe" 標準入力がチャネルに接続されている (デフォルト) "in_io": "file" stdin はファイルから読み込む "in_io": "buffer" stdin はバッファから読み込む "in_top": number "buffer" を使用する場合: 送信する最初の行 (デフォルト: 1) "in_bot": number "buffer" を使用する場合: 送信する最後の行 (デフォルト: 最後) "in_name": "/path/file" 読み込むファイルまたはバッファの名前 "in_buf": number 読み込むバッファの番号 *job-out_io* *out_name* *out_buf* "out_io": "null" stdout を切断する (/dev/null に行く) "out_io": "pipe" stdout がチャネルに接続されている (デフォルト) "out_io": "file" stdout がファイルに書き込む "out_io": "buffer" stdout はバッファに追加する (下記参照) "out_name": "/path/file" 書き込むファイルまたはバッファの名前 "out_buf": number 書き込むバッファの番号 "out_modifiable": 0 バッファに書き込むときに、'modifiable' はオフになる (下記参照) "out_msg": 0 新しいバッファに書き込むとき、最初の行は "Reading from channel output..." に設定される *job-err_io* *err_name* *err_buf* "err_io": "out" stderr のメッセージは stdout に行く "err_io": "null" stderr を切断する (/dev/null に行く) "err_io": "pipe" stderr がチャネルに接続されている (デフォルト) "err_io": "file" stderr はファイルに書き込む "err_io": "buffer" stderr はバッファに追加する (下記参照) "err_name": "/path/file" 書き込むファイルまたはバッファの名前 "err_buf": number 書き込むバッファの番号 "err_modifiable": 0 バッファに書き込むときに、'modifiable' はオフになる (下記参照) "err_msg": 0 新しいバッファに書き込むとき、最初の行は "Reading from channel error..." に設定される "block_write": number テストのためにのみ: stdin への他のすべての書き込みをブ ロックするふりをする "env": dict 新しいプロセスのための環境変数 "cwd": "/path/to/dir" 新しいプロセスのためのカレント作業ディレクトリ。ディレ クトリが存在しない場合は、エラーが発生します バッファへの書き込み ~ *out_io-buffer* out_io または err_io モードが "buffer" で、コールバックがある場合、コールバッ クを呼び出す前にテキストがバッファに追加されます。 入力と出力の両方にバッファが使用されている場合、最後の行はチャネル入力に書き込 まれたものなので、出力行は最後の行の上に置かれます。それ以外の場合は最後の行の 下に行が追加されます。 "buffer" を指定して JS または JSON モードを使用すると、デコード+エンコーディン グ後に、ゼロまたは負の ID を持つメッセージのみがバッファに追加されます。正の数 を持つメッセージはコールバックによって処理され、コマンドは通常通り処理されま す。 "out_name" または "err_name" のバッファ名は、現在のディレクトリの名前を拡張し た後も、既存のバッファの完全名と比較されます。例えば、":edit somename" でバッ ファが作成され、バッファ名が "somename" の場合、そのバッファが使用されます。 一致するバッファがない場合、新しいバッファが作成されます。新しいバッファを常に 作成するには、空の名前を使用します。|ch_getbufnr()| を使用してバッファ番号を取 得できます。 新しいバッファの場合、'buftype' は "nofile" に設定され、'bufhidden' は "hide" に設定されます。他の設定が必要な場合は、まずバッファを作成し、バッファ番号を渡 します。 *out_modifiable* *err_modifiable* "out_modifiable" と "err_modifiable" オプションは、'modifiable' オプションをオ フにするか、'modifiable' になっているバッファに書き込むために使用できます。つ まり、行がバッファに追加されますが、ユーザーはバッファを簡単に変更できません。 *out_msg* *err_msg* "out_msg" オプションは、新しいバッファが最初の行を "Reading from channel output..." に設定するかどうかを指定するために使用できます。デフォルトではメッ セージを追加します。"err_msg" はチャネルエラーでも同じです。 既存のバッファに 'modifiable' が指定されておらず、"out_modifiable" または "err_modifiable" オプションがゼロでない場合、エラーが発生し、バッファに書き込 まれません。 書き込まれたバッファがウィンドウに表示され、カーソルが最後の行の最初の列にある 場合、カーソルは新しく追加された行に移動され、ウィンドウは必要に応じてカーソル を表示するために上にスクロールされます。 追加されたすべての行に対して、取り消しが同期されます。NUL バイトは受け入れられ ます (内部では Vim はこれらを NL バイトとして格納します)。 ファイルへの書き込み ~ *E920* ファイルはアクセス許可 600 (ユーザーに対しては読み書き可能、他のユーザーはアク セス不可) で作成されます。これを変更するには、|setfperm()| を使用してください。 ファイルがすでに存在する場合は切り捨てられます。 ============================================================================== 13. ジョブを制御する *job-control* ジョブの状態を取得するには: > echo job_status(job) ジョブの実行を停止するには: > job_stop(job) これはジョブを終了させる通常の方法です。Unix では、ジョブに SIGTERM を送信しま す。他の方法でジョブを停止したり、任意の信号を送信したりすることもできます。 例えば、ジョブを強制的に停止させるには、"kill it": > job_stop(job, "kill") 他のオプションについては、|job_stop()| 参照。 ============================================================================== 14. プロンプトバッファを使う *prompt-buffer* Vimのウィンドウでジョブのための入力をタイプしたい場合、いくつかのオプションが あります: - 通常のバッファを使用して、すべての起こりうるコマンドを自分で処理します。 たくさんの起こりうるコマンドが存在するので、これは複雑になります。 - 端末ウィンドウを使用します。入力した内容がジョブに直接送られ、ジョブの出力が ウィンドウに直接表示される場合は、これはうまく機能します。 |terminal-window| 参照。 - プロンプトバッファのウィンドウを使用します。これは、Vimでジョブからの出力 (フィルタリングされている可能性もある) を表示している間に、ジョブのための行 を入力しているときにうまく動作します。 プロンプトバッファは、'buftype' を "prompt" に設定することによって作成されま す。通常は、新規作成バッファでのみおこないます。 ユーザーは、バッファの最後の行に1行のテキストを編集して入力することができます。 プロンプト行でEnterキーを押すと、|prompt_setcallback()| で設定されたコールバッ クが呼び出されます。通常は、その行をジョブに送信します。別のコールバックは、 ジョブからの出力を受け取り、バッファ内のプロンプトの下 (次のプロンプトの上) に 表示します。 プロンプトの後の最後の行のテキストのみが編集可能です。残りのバッファはノーマル モードのコマンドでは変更できません。|append()| などの関数を呼び出すことで変更 できます。他のコマンドを使用すると、バッファを壊す可能性があります。 'buftype' を "prompt" に設定した後、Vimは自動的に挿入モードを開始しません。挿 入モードに入るには `:startinsert` を使います。これにより、ユーザーは行の入力を 開始できます。 プロンプトのテキストは、|prompt_setprompt()| 関数で設定できます。もし |prompt_setprompt()| でプロンプトが設定されないなら、"% " が使われます。バッ ファに作用しているプロンプトのテキストは、|prompt_getprompt()| で得られます。 ユーザーはノーマルモードに移行し、バッファ内を移動できます。これは、古い出力の 参照やテキストのコピーに便利です。 CTRL-W キーを使用して次のウィンドウに切り替えるための CTRL-W w などのウィンド ウコマンドを開始できます。これは挿入モードでも機能します (単語を削除するには Shift-CTRL-W を使用します)。ウィンドウを離れるとき挿入モードは停止します。プロ ンプトウィンドウに戻ると、挿入モードが復元されます。 "a", "i", "A" や "I" などの挿入モードを開始するコマンドは、最終行にカーソルを 移動します。"A" は行末に移動し、"I" は行頭に移動します。 これはUnixでの例です。シェルをバックグラウンドで起動し、次のシェルコマンドを待 ち受けます。シェルからの出力はプロンプトの上に表示されます。 > " チャネルのログを作成し、何が起きているか確認できるようにする。 call ch_logfile('logfile', 'w') " 入力されたテキスト行を扱う関数。 func TextEntered(text) " Enterを付加してテキストをシェルに送信する。 call ch_sendraw(g:shell_job, a:text .. "\n") endfunc " シェルからの出力を扱う関数: プロンプトの上に追加する。 func GotOutput(channel, msg) call append(line("$") - 1, "- " .. a:msg) endfunc " シェルの終了を扱う関数: ウィンドウを閉じる。 func JobExit(job, status) quit! endfunc " バックグラウンドでシェルを起動する。 let shell_job = job_start(["/bin/sh"], #{ \ out_cb: function('GotOutput'), \ err_cb: function('GotOutput'), \ exit_cb: function('JobExit'), \ }) new set buftype=prompt let buf = bufnr('') call prompt_setcallback(buf, function("TextEntered")) eval prompt_setprompt(buf, "shell command: ") " シェルコマンドの受け付けを開始する。 startinsert < |Vim9| script で同様に: > vim9script # チャネルのログを作成し、何が起きているか確認できるようにする。 ch_logfile('logfile', 'w') var shell_job: job # 入力されたテキスト行を扱う関数。 def TextEntered(text: string) # Enterを付加してテキストをシェルに送信する。 ch_sendraw(shell_job, text .. "\n") enddef # シェルからの出力を扱う関数: プロンプトの上に追加する。 def GotOutput(channel: channel, msg: string) append(line("$") - 1, "- " .. msg) enddef # シェルの終了を扱う関数: ウィンドウを閉じる。 def JobExit(job: job, status: number) quit! enddef # バックグラウンドでシェルを起動する。 shell_job = job_start(["/bin/sh"], { out_cb: GotOutput, err_cb: GotOutput, exit_cb: JobExit, }) new set buftype=prompt var buf = bufnr('') prompt_setcallback(buf, TextEntered) prompt_setprompt(buf, "shell command: ") # シェルコマンドの受け付けを開始する。 startinsert ============================================================================== 15. Language Server Protocol *language-server-protocol* language server protocol の仕様は以下にあります: https://microsoft.github.io/language-server-protocol/specification 各 LSP プロトコルメッセージは単純な HTTP ヘッダ で始まり JSON-RPC フォーマット にエンコードされたペイロードが続きます。次で説明されています: https://www.jsonrpc.org/specification Vim |Dict| 内の LSP のリクエスト/通知メッセージをエンコードして LSP JSON-RPC メッセージ に入れて送信をするのと LSP JSON-RPC 応答/通知メッセージの受信とデ コードをして Vim |Dict| 内に収めるには、|channel-mode| を "lsp" に設定して LSP サーバーに接続します。 |channel-mode| を "lsp" に設定したチャネルからメッセージを受信したなら、Vim は HTTP ヘッダを処理し、JSON-RPC ペイロードをデコードして Vim |Dict| 型に格納し、 |channel-callback| 関数もしくは指定された |channel-onetime-callback| 関数を呼 びます。|ch_evalexpr()| か |ch_sendexpr()| 関数を用いてチャネルからメッセージ を送信した時には、Vim は HTTP ヘッダを付与し Vim の式を JSON にエンコードしま す。Vim が組み込み型を JSON へエンコードあるいはデコードする方法の詳細について は、|json_encode()| と |json_decode()| を参照してください。 'lsp' モードを使用してチャネルを開くには、|ch_open()| の引数 {options} の 'mode' 項目を 'lsp' にします。例: > let ch = ch_open(..., #{mode: 'lsp'}) 'lsp' モードを使用したチャネルをジョブで開くには、|job_start()| の引数 {options} の 'in_mode' と 'out_mode' 項目を 'lsp' にします。例: > let cmd = ['clangd', '--background-index', '--clang-tidy'] let opts = {} let opts.in_mode = 'lsp' let opts.out_mode = 'lsp' let opts.err_mode = 'nl' let opts.out_cb = function('LspOutCallback') let opts.err_cb = function('LspErrCallback') let opts.exit_cb = function('LspExitCallback') let job = job_start(cmd, opts) Note ジョブが LSP メッセージを標準出力に、非LSPメッセージを標準エラーに出す場 合、チャネルのコールバック関数は両方のメッセージフォーマットを適切に扱うか、上 記にあるように "out_cb" と "err_cb" で個別のコールバック関数を使って扱わなくて はなりません。 サーバーにむけて JSON-RPC リクエストの同期送信をするには、|ch_evalexpr()| 関数 を使います。この関数は応答を待ちサーバーからのレスポンスメッセージをデコードし て返します。|channel-timeout| を使うかあるいは引数 {options} の 'timeout' フィールドに値を設定することで応答の待ち時間の制御ができます。レスポンスがタイ ムアウトした場合、空の |Dict| が返ります。例: > let req = {} let req.method = 'textDocument/definition' let req.params = {} let req.params.textDocument = #{uri: 'a.c'} let req.params.position = #{line: 10, character: 3} let defs = ch_evalexpr(ch, req, #{timeout: 100}) if defs->empty() ... endif Note リクエストメッセージには 'id' フィールドを指定してはいけません。指定した 場合、Vimは内部的に生成する識別子の値を上書きします。Vimは現在、'id' フィール ドの種別として数値のみをサポートしています。RPC のリクエストの成功と失敗の両方 でコールバック関数が呼び出されます。 JSON-RPC リクエストをサーバーに送信して応答を非同期に処理するには、 |ch_sendexpr()| 関数を使ってコールバック関数を与えます。リクエストメッセージに "id" フィールドが設定されている場合は、Vimは内部的に生成する識別子の値を上書き します。この関数はメッセージに使用した識別子を含む Dict を返します。キャンセル リクエストを LSP サーバーに送信する(必要な場合)にも使用できます。例: > let req = {} let req.method = 'textDocument/hover' let req.id = 200 let req.params = {} let req.params.textDocument = #{uri: 'a.c'} let req.params.position = #{line: 10, character: 3} let resp = ch_sendexpr(ch, req, #{callback: 'HoverFunc'}) |ch_sendexpr()| 関数を使用してサーバーに送信した非同期 LSP リクエストの未処理 のものをキャンセルするには、リクエスト時に返されたIDとともに |ch_sendexpr()| 関数でキャンセル用メッセージをサーバーに送信します。例: > " 補完リクエストを送信 let req = {} let req.method = 'textDocument/completion' let req.params = {} let req.params.textDocument = #{uri: 'a.c'} let req.params.position = #{line: 10, character: 3} let reqstatus = ch_sendexpr(ch, req, #{callback: 'LspComplete'}) " キャンセル通知を送信 let notif = {} let notif.method = '$/cancelRequest' let notif.id = reqstatus.id call ch_sendexpr(ch, notif) JSON-RPC 通知メッセージをサーバーに送信するには、|ch_sendexpr()| 関数を使いま す。サーバーは通知のレスポンスメッセージを送信しません。"callback" 項目は指定 をしてはいけません。例: > call ch_sendexpr(ch, #{method: 'initialized'}) サーバーからの JSON-RPC リクエストメッセージへの返答には |ch_sendexpr()| 関数 を使用します。レスポンスメッセージの中の 'id' フィールドの値はサーバーへのリク エストメッセージの応答メッセージからコピーします。例: > let resp = {} let resp.id = req.id let resp.result = 1 call ch_sendexpr(ch, resp) サーバーからの JSON-RPC 通知メッセージは |channel-callback| 関数を通じて伝達さ れます。 ユースケースに依存しますが、ch_evalexpr(), ch_sendexpr(), ch_sendraw() 関数を 同じチャネルで使うことができます。 LSP リクエストメッセージは以下のフォーマット(Vim Dict で表現)を持ちます。 "params" フィールドはオプションです: > { "jsonrpc": "2.0", "id": , "method": , "params": } LSP レスポンスメッセージは以下のフォーマット(Vim Dict で表現)を持ちます。 "result" と "error" フィールドはオプションです: > { "jsonrpc": "2.0", "id": , "result": "error": } LSP 通知メッセージは以下のフォーマット(Vim Dict で表現)を持ちます。"params" フィールドはオプションです: > { "jsonrpc": "2.0", "method": , "params": } < vim:tw=78:ts=8:noet:ft=help:norl: