*channel.txt*    Nvim


		 NVIM REFERENCE MANUAL    by Thiago de Arruda


Nvim asynchronous IO					*channel*

				      Type |gO| to see the table of contents.

==============================================================================
1. Introduction						    *channel-intro*

Channels are Nvim's way of communicating with external processes.

There are several ways to open a channel:

1. Through stdin/stdout when `nvim` is started with `--headless` and a startup
   script or `--cmd` command opens the stdio channel using |stdioopen()|.
2. Through stdin, stdout and stderr of a process spawned by |jobstart()|.
3. Through the PTY master end opened with `jobstart(…, {'pty': v:true})`.
4. By connecting to a TCP/IP socket or named pipe with |sockconnect()|.
5. By another process connecting to a socket listened to by Nvim. This only
   supports RPC channels, see |rpc-connecting|.

Channels support multiple modes or protocols. In the most basic
mode of operation, raw bytes are read and written to the channel.
The |RPC| protocol, based on the msgpack-rpc standard, enables nvim and the
process at the other end to send remote calls and events to each other.
The builtin |terminal-emulator| is also implemented on top of PTY channels.

Channel Id						*channel-id*

Each channel is identified by an integer id, unique for the life of the
current Nvim session. Functions like |stdioopen()| return channel ids;
functions like |chansend()| consume channel ids.

==============================================================================
2. Reading and writing raw bytes			      *channel-bytes*

Channels opened by Vimscript functions operate with raw bytes by default. For
a job channel using RPC, bytes can still be read over its stderr. Similarly,
only bytes can be written to Nvim's own stderr.

						*channel-callback*
- on_stdout({chan-id}, {data}, {name})		*on_stdout*
- on_stderr({chan-id}, {data}, {name})		*on_stderr*
- on_stdin({chan-id}, {data}, {name})		*on_stdin*
- on_data({chan-id}, {data}, {name})		*on_data*

    Scripts can react to channel activity (received data) via callback
    functions assigned to the `on_stdout`, `on_stderr`, `on_stdin`, or
    `on_data` option keys. Callbacks should be fast: avoid potentially
    slow/expensive work.

    Parameters: ~
      - {chan-id}   Channel handle. |channel-id|
      - {data}	    Raw data (|readfile()|-style list of strings) read from
		    the channel. EOF is a single-item list: `['']`. First and
		    last items may be partial lines! |channel-lines|
      - {name}	    Stream name (string) like "stdout", so the same function
		    can handle multiple streams. Event names depend on how the
		    channel was opened and in what mode/protocol.

						*channel-buffered*
    The callback is invoked immediately as data is available, where
    a single-item list `['']` indicates EOF (stream closed).  Alternatively
    set the `stdout_buffered`, `stderr_buffered`, `stdin_buffered`, or
    `data_buffered` option keys to invoke the callback only after all output
    was gathered and the stream was closed.
						*E5210*
    If a buffering mode is used without a callback, the data is saved in the
    stream {name} key of the options dict. It is an error if the key exists.

							      *channel-lines*
    Stream event handlers receive data as it becomes available from the OS,
    thus the first and last items in the {data} list may be partial lines.
    Empty string completes the previous partial line. Examples (not including
    the final `['']` emitted at EOF):
      - `foobar` may arrive as `['fo'], ['obar']`
      - `foo\nbar` may arrive as
	- `['foo','bar']`
	- or `['foo',''], ['bar']`
	- or `['foo'], ['','bar']`
	- or `['fo'], ['o','bar']`

    There are two ways to deal with this:
    - 1. To wait for the entire output, use |channel-buffered| mode.
    - 2. To read line-by-line, use the following code: >vim
	let s:lines = ['']
	func! s:on_event(job_id, data, event) dict
	  let eof = (a:data == [''])
	  " Complete the previous line.
	  let s:lines[-1] .= a:data[0]
	  " Append (last item may be a partial line, until EOF).
	  call extend(s:lines, a:data[1:])
	endf
<

If the callback functions are |Dictionary-function|s, |self| refers to the
options dictionary containing the callbacks. |Partial|s can also be used as
callbacks.

Data can be sent to the channel using the |chansend()| function. Here is a
simple example, echoing some data through a cat-process:
>vim
    function! s:OnEvent(id, data, event) dict
      let str = join(a:data, "\n")
      echomsg str
    endfunction
    let id = jobstart(['cat'], {'on_stdout': function('s:OnEvent') } )
    call chansend(id, "hello!")
<

Here is an example of setting a buffer to the result of grep, but only after
all data has been processed:
>vim
    function! s:OnEvent(id, data, event) dict
      call nvim_buf_set_lines(2, 0, -1, v:true, a:data)
    endfunction
    let id = jobstart(['grep', '^[0-9]'], { 'on_stdout': function('s:OnEvent'),
					  \ 'stdout_buffered':v:true } )

    call chansend(id, "stuff\n10 PRINT \"NVIM\"\nxx")
    " no output is received, buffer is empty

    call chansend(id, "xx\n20 GOTO 10\nzz\n")
    call chanclose(id, 'stdin')
    " now buffer has result
<
For additional examples with jobs, see |job-control|.

							      *channel-pty*
Special case: PTY channels opened with `jobstart(..., {'pty': v:true})` do not
preprocess ANSI escape sequences, these will be sent raw to the callback.
However, change of PTY size can be signaled to the slave using |jobresize()|.
See also |terminal-emulator|.

Terminal characteristics (termios) for |:terminal| and PTY channels are copied
from the host TTY, or if Nvim is |--headless| it uses default values: >vim
    :echo system('nvim --headless +"te stty -a" +"sleep 1" +"1,/^$/print" +q')

==============================================================================
3. Communicating with msgpack RPC			      *channel-rpc*

When channels are opened with the `rpc` option set to true, the channel can be
used for remote method calls in both directions, see |msgpack-rpc|. Note that
rpc channels are implicitly trusted and the process at the other end can
invoke any |API| function!

==============================================================================
4. Standard IO channel					    *channel-stdio*

Nvim uses stdin/stdout to interact with the user over the terminal interface
(TUI). If Nvim is |--headless| the TUI is not started and stdin/stdout can be
used as a channel. See also |--embed|.

Call |stdioopen()| during |startup| to open the stdio channel as |channel-id| 1.
Nvim's stderr is always available as |v:stderr|, a write-only bytes channel.

Example: >vim
    func! OnEvent(id, data, event)
      if a:data == [""]
        quit
      end
      call chansend(a:id, map(a:data, {i,v -> toupper(v)}))
    endfunc
    call stdioopen({'on_stdin': 'OnEvent'})
<
Put this in `uppercase.vim` and run:  >bash
    nvim --headless --cmd "source uppercase.vim"

==============================================================================
5. Using a prompt buffer				*prompt-buffer*

Prompt buffers provide a "prompt" interface: they are like regular buffers,
except only the last section of the buffer is editable, and the user can
"submit" the prompt by hitting Enter. Useful for implementing:

- chat UI
- REPL or shell plugins
- advanced "picker" plugins

A prompt buffer is created by setting 'buftype' to "prompt". You would
normally only do that in a new, empty buffer: >vim

    :set buftype=prompt

Prompt buffers do not implicitly set 'modified' on user input or other
changes. But a user or plugin can set 'modified' explicitly, to prevent the
buffer from being easily closed.

The user can edit and input text at the end of the buffer. Pressing Enter in
the input section invokes the |prompt_setcallback()| callback, which is
typically expected to process the prompt and show results by appending to the
buffer. To input multiline text, use *Shift-<Enter>* to add a new line without
submitting the prompt, or just |put| or |paste| multiline text.

Only the section starting with the mark |':| of the buffer (after the prompt)
is editable. The rest of the buffer is not modifiable with Normal mode
commands, though it can be modified by functions such as |append()|.  Using
other commands may mess up the buffer.

After setting `buftype=prompt`:
- Nvim unsets the 'comments' option.
- Nvim does not automatically start Insert mode (use `:startinsert` if you
  want to enter Insert mode)

The prompt prefix defaults to "% ", but can be set with |prompt_setprompt()|.
You can get the effective prompt prefix for with |prompt_getprompt()|.

The user can go to Normal mode and navigate through the buffer.  This can be
useful to see older output or copy text.

By default during prompt insert-mode, the CTRL-W key can be used to start
a window command, such as CTRL-W w to switch to the next window. (Use
Shift-CTRL-W to delete a word). When leaving the window Insert mode will be
stopped. When coming back to the prompt window Insert mode will be restored.

Any command that starts Insert mode, such as "a", "i", "A" and "I", will move
the cursor to the last line.  "A" will move to the end of the line, "I" to the
start of the line.

Example: start a shell in the background and prompt for the next shell
command, displaying shell output above the prompt: >lua

    local shell_job

    -- Handles a line of user input.
    local function on_submit(text)
      -- Send the text to the shell with Enter appended.
      vim.api.nvim_chan_send(shell_job, text .. "\n")
    end

    -- Handles output from the shell.
    local function on_output(_, msg, _)
      -- Add shell output above the prompt.
      local buf = vim.api.nvim_get_current_buf()
      vim.fn.prompt_appendbuf(buf, msg)
    end

    -- Handles the shell exit.
    local function on_exit(_, _, _)
      vim.cmd("quit!")
    end

    -- Start a shell in the background.
    shell_job = vim.fn.jobstart({ "/bin/sh" }, {
      on_stdout = on_output,
      on_stderr = on_output,
      on_exit = on_exit,
    })

    vim.cmd("new")
    vim.bo.buftype = "prompt"
    local buf = vim.api.nvim_get_current_buf()
    vim.fn.prompt_setcallback(buf, on_submit)
    vim.fn.prompt_setprompt(buf, "shell command: ")

    -- Start accepting shell commands.
    vim.cmd("startinsert")
<

 vim:tw=78:ts=8:et:sw=4:ft=help:norl:
