💾 Archived View for gmi.noulin.net › vim › channel.gmi captured on 2023-07-10 at 14:23:33. Gemini links have been rewritten to link to archived content
View Raw
More Information
⬅️ Previous capture (2023-01-29)
➡️ Next capture (2023-09-28)
-=-=-=-=-=-=-
- channel.txt* For Vim version 9.0. Last change: 2022 Dec 01
VIM REFERENCE MANUAL by Bram Moolenaar
Inter-process communication *channel*
Vim uses channels to communicate with other processes.
A channel uses a socket or pipes. *socket-interface*
Jobs can be used to start processes and communicate with them.
The Netbeans interface also uses a channel. |netbeans|
1. Overview |job-channel-overview|
2. Channel demo |channel-demo|
3. Opening a channel |channel-open|
4. Using a JSON or JS channel |channel-use|
5. Channel commands |channel-commands|
6. Using a RAW or NL channel |channel-raw|
7. More channel functions |channel-more|
8. Channel functions details |channel-functions-details|
9. Starting a job with a channel |job-start|
10. Starting a job without a channel |job-start-nochannel|
11. Job functions |job-functions-details|
12. Job options |job-options|
13. Controlling a job |job-control|
14. Using a prompt buffer |prompt-buffer|
15. Language Server Protocol |language-server-protocol|
*E1277*
{only when compiled with the |+channel| feature for channel stuff}
You can check this with: `has('channel')`
{only when compiled with the |+job| feature for job stuff}
You can check this with: `has('job')`
==============================================================================
1. Overview *job-channel-overview*
There are four main types of jobs:
1. A daemon, serving several Vim instances.
Vim connects to it with a socket.
2. One job working with one Vim instance, asynchronously.
Uses a socket or pipes.
3. A job performing some work for a short time, asynchronously.
Uses a socket or pipes.
4. Running a filter, synchronously.
Uses pipes.
For when using sockets See |job-start|, |job-start-nochannel| and
|channel-open|. For 2 and 3, one or more jobs using pipes, see |job-start|.
For 4 use the ":{range}!cmd" command, see |filter|.
Over the socket and pipes these protocols are available:
RAW nothing known, Vim cannot tell where a message ends
NL every message ends in a NL (newline) character
JSON JSON encoding |json_encode()|
JS JavaScript style JSON-like encoding |js_encode()|
LSP Language Server Protocol encoding |language-server-protocol|
Common combination are:
- Using a job connected through pipes in NL mode. E.g., to run a style
checker and receive errors and warnings.
- Using a daemon, connecting over a socket in JSON mode. E.g. to lookup
cross-references in a database.
==============================================================================
2. Channel demo *channel-demo* *demoserver.py*
This requires Python. The demo program can be found in
$VIMRUNTIME/tools/demoserver.py
Run it in one terminal. We will call this T1.
Run Vim in another terminal. Connect to the demo server with: >
let channel = ch_open('localhost:8765')
In T1 you should see:
=== socket opened === ~
You can now send a message to the server: >
echo ch_evalexpr(channel, 'hello!')
The message is received in T1 and a response is sent back to Vim.
You can see the raw messages in T1. What Vim sends is:
[1,"hello!"] ~
And the response is:
[1,"got it"] ~
The number will increase every time you send a message.
The server can send a command to Vim. Type this on T1 (literally, including
the quotes):
["ex","echo 'hi there'"] ~
And you should see the message in Vim. You can move the cursor a word forward:
["normal","w"] ~
To handle asynchronous communication a callback needs to be used: >
func MyHandler(channel, msg)
echo "from the handler: " .. a:msg
endfunc
call ch_sendexpr(channel, 'hello!', {'callback': "MyHandler"})
Vim will not wait for a response. Now the server can send the response later
and MyHandler will be invoked.
Instead of giving a callback with every send call, it can also be specified
when opening the channel: >
call ch_close(channel)
let channel = ch_open('localhost:8765', {'callback': "MyHandler"})
call ch_sendexpr(channel, 'hello channel!')
When trying out channels it's useful to see what is going on. You can tell
Vim to write lines in log file: >
call ch_logfile('channellog', 'w')
See |ch_logfile()|.
==============================================================================
3. Opening a channel *channel-open*
To open a channel: >
let channel = ch_open({address} [, {options}])
if ch_status(channel) == "open"
" use the channel
Use |ch_status()| to see if the channel could be opened.
*channel-address*
{address} can be a domain name or an IP address, followed by a port number, or
a Unix-domain socket path prefixed by "unix:". E.g. >
www.example.com:80 " domain + port
127.0.0.1:1234 " IPv4 + port
[2001:db8::1]:8765 " IPv6 + port
unix:/tmp/my-socket " Unix-domain socket path
{options} is a dictionary with optional entries: *channel-open-options*
"mode" can be: *channel-mode*
"json" - Use JSON, see below; most convenient way. Default.
"js" - Use JS (JavaScript) encoding, more efficient than JSON.
"nl" - Use messages that end in a NL character
"raw" - Use raw messages
"lsp" - Use language server protocol encoding
*channel-callback* *E921*
"callback" A function that is called when a message is received that is
not handled otherwise (e.g. a JSON message with ID zero). It
gets two arguments: the channel and the received message.
Example: >
func Handle(channel, msg)
echo 'Received: ' .. a:msg
endfunc
let channel = ch_open("localhost:8765", {"callback": "Handle"})
<
When "mode" is "json" or "js" or "lsp" the "msg" argument is
the body of the received message, converted to Vim types.
When "mode" is "nl" the "msg" argument is one message,
excluding the NL.
When "mode" is "raw" the "msg" argument is the whole message
as a string.
For all callbacks: Use |function()| to bind it to arguments
and/or a Dictionary. Or use the form "dict.function" to bind
the Dictionary.
Callbacks are only called at a "safe" moment, usually when Vim
is waiting for the user to type a character. Vim does not use
multi-threading.
*close_cb*
"close_cb" A function that is called when the channel gets closed, other
than by calling ch_close(). It should be defined like this: >
func MyCloseHandler(channel)
< Vim will invoke callbacks that handle data before invoking
close_cb, thus when this function is called no more data will
be passed to the callbacks. However, if a callback causes Vim
to check for messages, the close_cb may be invoked while still
in the callback. The plugin must handle this somehow, it can
be useful to know that no more data is coming.
If it is not known if there is a message to be read, use a
try/catch block: >
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" Specifies when to drop messages:
"auto" When there is no callback to handle a message.
The "close_cb" is also considered for this.
"never" All messages will be kept.
*channel-noblock*
"noblock" Same effect as |job-noblock|. Only matters for writing.
*waittime*
"waittime" The time to wait for the connection to be made in
milliseconds. A negative number waits forever.
The default is zero, don't wait, which is useful if a local
server is supposed to be running already. On Unix Vim
actually uses a 1 msec timeout, that is required on many
systems. Use a larger value for a remote server, e.g. 10
msec at least.
*channel-timeout*
"timeout" The time to wait for a request when blocking, E.g. when using
ch_evalexpr(). In milliseconds. The default is 2000 (2
seconds).
When "mode" is "json" or "js" the "callback" is optional. When omitted it is
only possible to receive a message after sending one.
To change the channel options after opening it use |ch_setoptions()|. The
arguments are similar to what is passed to |ch_open()|, but "waittime" cannot
be given, since that only applies to opening the channel.
For example, the handler can be added or changed: >
call ch_setoptions(channel, {'callback': callback})
When "callback" is empty (zero or an empty string) the handler is removed.
After a callback has been invoked Vim will update the screen and put the
cursor back where it belongs. Thus the callback should not need to do
`:redraw`.
The timeout can be changed: >
call ch_setoptions(channel, {'timeout': msec})
<
*channel-close* *E906*
Once done with the channel, disconnect it like this: >
call ch_close(channel)
When a socket is used this will close the socket for both directions. When
pipes are used (stdin/stdout/stderr) they are all closed. This might not be
what you want! Stopping the job with job_stop() might be better.
All readahead is discarded, callbacks will no longer be invoked.
Note that a channel is closed in three stages:
- The I/O ends, log message: "Closing channel". There can still be queued
messages to read or callbacks to invoke.
- The readahead is cleared, log message: "Clearing channel". Some variables
may still reference the channel.
- The channel is freed, log message: "Freeing channel".
When the channel can't be opened you will get an error message. There is a
difference between MS-Windows and Unix: On Unix when the port doesn't exist
ch_open() fails quickly. On MS-Windows "waittime" applies.
If there is an error reading or writing a channel it will be closed.
==============================================================================
4. Using a JSON or JS channel *channel-use*
If mode is JSON then a message can be sent synchronously like this: >
let response = ch_evalexpr(channel, {expr})
This awaits a response from the other side.
When mode is JS this works the same, except that the messages use
JavaScript encoding. See |js_encode()| for the difference.
To send a message, without handling a response or letting the channel callback
handle the response: >
call ch_sendexpr(channel, {expr})
To send a message and letting the response handled by a specific function,
asynchronously: >
call ch_sendexpr(channel, {expr}, {'callback': Handler})
Vim will match the response with the request using the message ID. Once the
response is received the callback will be invoked. Further responses with the
same ID will be ignored. If your server sends back multiple responses you
need to send them with ID zero, they will be passed to the channel callback.
The {expr} is converted to JSON and wrapped in an array. An example of the
message that the receiver will get when {expr} is the string "hello":
[12,"hello"] ~
The format of the JSON sent is:
[{number},{expr}]
In which {number} is different every time. It must be used in the response
(if any):
[{number},{response}]
This way Vim knows which sent message matches with which received message and
can call the right handler. Also when the messages arrive out of order.
A newline character is terminating the JSON text. This can be used to
separate the read text. For example, in Python:
splitidx = read_text.find('\n')
message = read_text[:splitidx]
rest = read_text[splitidx + 1:]
The sender must always send valid JSON to Vim. Vim can check for the end of
the message by parsing the JSON. It will only accept the message if the end
was received. A newline after the message is optional.
When the process wants to send a message to Vim without first receiving a
message, it must use the number zero:
[0,{response}]
Then channel handler will then get {response} converted to Vim types. If the
channel does not have a handler the message is dropped.
It is also possible to use ch_sendraw() and ch_evalraw() on a JSON or JS
channel. The caller is then completely responsible for correct encoding and
decoding.
==============================================================================
5. Channel commands *channel-commands*
With a JSON channel the process can send commands to Vim that will be
handled by Vim internally, it does not require a handler for the channel.
Possible commands are: *E903* *E904* *E905*
["redraw", {forced}]
["ex", {Ex command}]
["normal", {Normal mode command}]
["expr", {expression}, {number}]
["expr", {expression}]
["call", {func name}, {argument list}, {number}]
["call", {func name}, {argument list}]
With all of these: Be careful what these commands do! You can easily
interfere with what the user is doing. To avoid trouble use |mode()| to check
that the editor is in the expected state. E.g., to send keys that must be
inserted as text, not executed as a command:
["ex","if mode() == 'i' | call feedkeys('ClassName') | endif"] ~
Errors in these commands are normally not reported to avoid them messing up
the display. If you do want to see them, set the 'verbose' option to 3 or
higher.
Command "redraw" ~
The other commands do not explicitly update the screen, so that you can send a
sequence of commands without the cursor moving around. A redraw can happen as
a side effect of some commands. You must end with the "redraw" command to
show any changed text and show the cursor where it belongs.
The argument is normally an empty string:
["redraw", ""] ~
To first clear the screen pass "force":
["redraw", "force"] ~
Command "ex" ~
The "ex" command is executed as any Ex command. There is no response for
completion or error. You could use functions in an |autoload| script:
["ex","call myscript#MyFunc(arg)"]
You can also use "call |feedkeys()|" to insert any key sequence.
When there is an error a message is written to the channel log, if it exists,
and v:errmsg is set to the error.
Command "normal" ~
The "normal" command is executed like with ":normal!", commands are not
mapped. Example to open the folds under the cursor:
["normal" "zO"]
Command "expr" with response ~
The "expr" command can be used to get the result of an expression. For
example, to get the number of lines in the current buffer:
["expr","line('