有四种主要的作业类型:
1. 守护进程 (daemon),服务于多个 Vim 实例。
Vim 使用套接字连接。
2. 用于单个 Vim 实例的单个作业,异步操作。
使用套接字或管道。
3. 用于短期若干工作的作业,异步操作。
使用套接字或管道。
4. 作为过滤器,同步操作。
使用管道。
使用套接字请见 job-start 、 job-start-nochannel 和 channel-open 。2 和 3 类
中一个或多个作业若使用管道,可见 job-start 。4 类即 ":{range}
!cmd" 命令,参见
filter 。
套接字和管道可用以下协议:
RAW 无规范,Vim 不知信息如何终止
NL 每条信息由 NL (换行) 字符终止
JSON JSON 编码 json_encode()
JS JavaScript 风格类 JSON 的编码 js_encode()
LSP 语言服务器协议编码 language-server-protocol
常用组合有:
- 使用 NL 模式的管道连接的作业。例如,运行风格检查器并接收错误与警告
。
- 使用 JSON 模式的套接字连接的守护进程。例如,从数据库中查找交叉索引。
需要 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 中可以看到该消息。要移动光标前进一个单词:
["normal","w"]
要处理异步通信,需要提供回调函数:
func MyHandler(channel, msg)
echo "from the handler: " .. a:msg
endfunc
call ch_sendexpr(channel, 'hello!', {'callback': "MyHandler"})
Vim 不会等待响应。这样,服务器就可以稍后发送响应,响应时会调用 MyHandler。
回调除了每次在发送时以外,也可以在打开通道时提供:
call ch_close(channel)
let channel = ch_open('localhost:8765', {'callback': "MyHandler"})
call ch_sendexpr(channel, 'hello!')
测试通道时可能希望能知道发生了什么。可以告诉 Vim 在日志文件里记录事件:
call ch_logfile('channellog', 'w')
见 ch_logfile() 。
要打开通道:
let channel = ch_open({address} [, {options}])
if ch_status(channel) == "open"
" use the channel
可用 ch_status() 看到通道是否已打开。
channel-address
{address}
可以是域名或 IP 地址后跟端口号,也可以是带 "unix:" 前缀的 Unix-域套
接字路径。例如
www.example.com:80 " 域名 + port
127.0.0.1:1234 " IPv4 + port
[2001:db8::1]:8765 " IPv6 + port
unix:/tmp/my-socket " Unix-域套接字路径
{options}
是可选项目的字典: channel-open-options
"mode" 可以是: channel-mode
"json" - JSON,见下;最方便的方式。缺省。
"js" - JS (JavaScript) 编码,比 JSON 经济。
"nl" - Nl 字符结尾的消息
"raw" - 原始消息
"lsp" - 语言服务器协议编码
channel-callback E921
"callback" 消息收到时,如果还未被处理 (如 ID 为零的 JSON 消息),调用本函
数。接受两个参数: 通道和收到的消息。例如:
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。
"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 实
际使用 1 毫秒等待时间,在很多系统上这是必要的。对于远程服务服
务器,使定更高的值,例如,至少 10 毫秒。
channel-timeout
"timeout" 等待请求处理完成的阻塞时间,例如 ch_evalexpr()。以毫秒计。缺省
为 2000 (2 秒)。
"mode" 为 "json" 或 "js" 时,"callback" 是可选的。省略时只有在发送一条消息后才
能收到消息。
打开通道后要改变通道选项,用 ch_setoptions() 。参数类似于 ch_open() 所用的
参数,但不能指定 "waittime",因为它只在通道打开时有意义。
例如,可以新增或改变处理函数:
call ch_setoptions(channel, {'callback': callback})
"callback" 为空 (零或空字符串) 时删除该处理函数。
回调执行后 Vim 会刷新屏幕,并复原光标之前所在的位置。因而,回调无须执行
:redraw 。
可以改变 timeout:
call ch_setoptions(channel, {'timeout': msec})
channel-close E906
用完通道后,这样可以关闭之:
call ch_close(channel)
使用套接字时,关闭双向的套接字。使用管道时 (stdin/stdout/stderr),关闭所有的管
道。这可能不是你想要的!用 job_stop() 停止作业可能更好。
放弃所有预读取,不再调用回调。
注意
通道关闭分三个阶段:
- I/O 结束,记录日志: "Closing channel"。队列中可能还有消息等待读取或回调。
- 清理预读取,记录日志: "Clearing channel"。有些变量还可能引用该通道。
- 释放通道,记录日志: "Freeing channel"。
通道如果不能打开,会报错。MS-Windows 和 Unix 有一点区别。Unix 上如果打不开端
口,会迅速失败。MS-Windows 上会等上 "waittime"。
E898 E901 E902
读写通道时如果有错,会关闭通道。
E630 E631
JSON 模式可以以同步方式发送消息:
let response = ch_evalexpr(channel, {expr})
此时,等待对方回复响应。
JS 模式也是如此,但消息使用 JavasScript 编码。其中的区别,见 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}
]
这样 Vim 就会知道哪个发送的消息对应哪个收到的消息,并调用正确的处理函数。即使
消息接收的顺序打乱也不要紧。
JSON 文本以换行符结尾。可用于分隔读取的文本。例如,Python 中:
splitidx = read_text.find('\n')
message = read_text[:splitidx]
rest = read_text[splitidx + 1:]
发送者必须发送合法的 JSON 给 Vim。Vim 会分析 JSON 来检查消息是否完整结尾。只
有完整结尾的消息才会被接受。消息结尾的换行符是可选的。
一个进程如果没有先收到消息,而要主动发送消息给 Vim 时,应使用数值零:
[0,{response}
]
此时通道处理函数会把 {response}
转换为 Vim 类型。如果通道没有处理函数,该消息
被丢弃。
JSON 或 JS 通道也可以用 ch_sendraw() 和 ch_evalraw()。调用者必须自行负责正确的
编码和解码。
通过 JSON 通道,进程可以向 Vim 发送一些 Vim 内部可以处理,无需通道处理函数的命
令。
可能命令包括: E903 E904 E905
["redraw", {forced}
]
["ex", {Ex 命令}]
["normal", {普通模式命令}
]
["expr", {expression}
, {number}
]
["expr", {expression}
]
["call", {函数名}
, {参数列表}
, {number}
]
["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'
])"]
请求中没有第三个参数。
"call" 命令
和 "expr" 类似,但不是把整个表达式作为一个字符串传递,而是分别传递函数名和一个
参数列表。这样有助于避免把参数经过转义、连接等步骤转换为字符串的过程。例如:
["call", "line", ["$"], -2]
如果不需要响应,不用提供第四个参数:
["call", "setline", ["$", ["one", "two", "three"]]]
使用 RAW 或 NL 模式时,可以这样发送信息:
let response = ch_evalraw(channel, {string})
{string}
直接传送。响应也是通道读取的直接结果。既然 Vim 不知道如何识别信息怎样
结尾,你需要自己处理。超时只及于首个字节的读取,之后将不再等待。
模式为 "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 。
要获知通道的状态: ch_status(channel)。可能的值为:
"fail" 通道打开失败。
"open" 通道可用。
"buffered" 通道已关闭,但还有待读的数据。
"closed" 通道已关闭。
要获知通道相关的作业: ch_getjob(channel)
要从通道读取一条信息:
let output = ch_read(channel)
这里使用通道的 timeout。要读取不带超时以获得所有所用的信息:
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"})
注意
通道使用 NL 模式时,ch_readraw() 每次调用只返回一行。
ch_read() 和 ch_readraw() 都适用通道 timeout。如果在该时间内没有任何消息可读,
返回空字符串。要指定其他的超时,使用以毫秒计的 "timeout" 选项:
{"timeout": 123}
要读取错误输出,使用 "part" 选项:
{"part": "err"}
要在 JS 或 JSON 通道上读取指定 ID 的信息:
{"id": 99}
如果没有给出 ID 或 ID 为 -1,返回首个信息。这里的优先级高于等待该信息的回调。
RAW 通道这里返回任何可用的信息,因为 Vim 不知道信息如何终止。
NL 通道这里返回一条信息。
JS 或 JSON 通道这里返回一条解码的信息。
包含任何序列号。
ch_canread({handle}
) ch_canread()
如果从 {handle}
有内容可读,返回非零。
{handle}
可以是通道或带有通道的作业。
用于在合适的时间才从通道读取的场合,例如在定时器中。
注意
如果通道没有回调,这些消息被丢弃。要避免这一点,加上关闭
回调函数。
也可用作 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}
的 "in" 部分。见 channel-close-in 。
{handle}
可以是通道或带有通道的作业。
不调用关闭回调函数。
也可用作 method :
GetChannel()->ch_close_in()
ch_evalexpr({handle}
, {expr}
[, {options}
]) ch_evalexpr()
发送 {expr}
到 {handle}
上。{expr}
用通道类型进行编码。不能于
原始通道。见 channel-use 。
{handle}
可以是通道或带有通道的作业。
使用 "lsp" 通道模式时,{expr}
必须为 Dict 。
E917
{options}
必须是字典。不能有 "callback" 项目。可以有 "timeout"
项目来指明此特定请求的超时。
ch_evalexpr() 等待响应并返回经解码的表达式。如果有错或超时,
返回空 String ,或者用 "lsp" 通道模式时,返回空 Dict 。
注意
在等待响应时,Vim 在处理其它的信息。要确保这不会有问题。
也可用作 method :
GetChannel()->ch_evalexpr(expr)
ch_evalraw({handle}
, {string}
[, {options}
]) ch_evalraw()
发送 {expr}
到 {handle}
上。
{handle}
可以是通道或带有通道的作业。
类似于 ch_evalexpr() ,但不对请求编码也不对响应结果解码。调用
者负责内容的正确性。也不对 NL 模式的通道附加换行符,调用者须自
行负责。响应中的 NL 则被清除。
注意
Vim 不能判定原始通道上的文本何时被完整地收到。它只返回第
一部分,你需用 ch_readraw() 来读取其余的部分。
见 channel-use 。
也可用作 method :
GetChannel()->ch_evalraw(rawstring)
ch_getbufnr({handle}
, {what}
) ch_getbufnr()
返回 {handle}
用于字符串 {what}
的缓冲区号。
{handle}
可以是通道或带有通道的作业。
{what}
可以是 "err",代表 stderr,"out" 代表 stdout,或空,代
表套接字输出。
如果没有这样的缓冲区,返回 -1。
也可用作 method :
GetChannel()->ch_getbufnr(what)
ch_getjob({channel}
) ch_getjob()
取得 {channel}
相关的作业。
如果没有这样的作业,在返回的作业上调用 job_status() 会返回
"fail"。
也可用作 method :
GetChannel()->ch_getjob()
ch_info({handle}
) ch_info()
返回 {handle}
相关信息的一个字典。其项目为:
"id" 通道号
"status" "open"、"buffered" 或 "closed",可见
ch_status()
用于 ch_open() 打开的通道时:
"hostname" 地址的机器名
"port" 地址的端口号
"path" Unix-域套接字的路径
"sock_status" "open" 或 "closed"
"sock_mode" "NL"、"RAW"、"JSON" 或 "JS"
"sock_io" "socket"
"sock_timeout" 毫秒计的超时
注意
"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}
写入通道日志文
件中。
如果传入 {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 中不可用。
注意
: 通道在文件中保存通信内容,小心其中可能包含机密和敏感的隐
私信息,如你在终端窗口中输入的密码等。
也可用作 method :
'logfile'->ch_logfile('w')
ch_open({address}
[, {options}
]) ch_open()
打开通道到 {address}
。见 channel 。
返回通道。错误检查可用 ch_status() 。
{address}
为字符串,接受的形式可见 channel-address 。
使用 IPv6 地址时,用方括号包围。如 "[2001:db8::1]:8765"。
{options}
如果给出,必须是 Dictionary 。
见 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()
发送 {expr}
到 {handle}
上。{expr}
用通道类型进行编码。不能于
原始通道。见 channel-use 。 E912
{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}
可以是通道或带有通道的作业。
注意
改变模式可能会丢失正在排队的消息。
以下选项不可改变:
"waittime" 只适用于 ch_open()
也可用作 method :
GetChannel()->ch_setoptions(options)
ch_status({handle}
[, {options}
]) ch_status()
返回 {handle}
的状态:
"fail" 打开通道失败
"open" 通道可用
"buffered" 通道可读,不可写
"closed" 通道不可用
{handle}
可以是通道或带有通道的作业。
"buffered" 用于通道已关闭但还有数据用 ch_read() 可读的情况。
{options}
给出时,可以包含 "part" 项目,指定通道要返回状态的部
分: "out" 还是 "err"。例如,要得到错误输出的状态:
ch_status(job, {"part": "err"})
也可用作 method :
GetChannel()->ch_status()
要启动一个作业并打开对应 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"})
如果要用相同的处理函数同时处理 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" 设为零而 "in_bot" 不设置: 每次缓冲区加入一行新行时,
倒数第二行被发送到作业的 stdin。这样你可以编辑末行并在回车时将它发送。
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" 有用些吧。
要启动别处的进程而不建立通道:
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
注意
这里 ch_open() 的 waittime 给作业一秒钟时间使端口可用。
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()
启动作业,返回 Job 对象。不同于 system() 和 :!cmd ,不等待
作业完成。
要在终端窗口中启动作业,可见 term_start() 。
如果作业启动失败,在返回的 Job 对象上调用 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"')
注意
这样会启动两个过程,外壳及其启动的命令。如果不希望如此,
可用 "exec" 外壳命令。
Unix 上仅当命令本身不包含斜杠时,才用 $PATH 来搜索可执行文件。
作业使用和 Vim 相同的终端。如果从 stdin 读入,该作业和 Vim 会
竞争输入,这样不行。要避免此问题,重定向 stdin 和 stdout:
let job = job_start(['sh', '-c', "myserver </dev/null >/dev/null"])
返回的 Job 对象可用以通过 job_status() 获取状态,和通过
job_stop() 停止作业。
注意
作业对象如果没有被引用会被删除。此时 stdin 和 stderr 会被
关闭,这可能会使作业失败报错。要避免之,总是保留对作业的引用。
所以,不要这样:
call job_start('my-command')
而要:
let myjob = job_start('my-command')
不再需要作业,或者已经不介意作业是否会失败之后 (如启动时已显示
了信息) 才可以 unlet "myjob"。记住函数返回后局部于函数的变量不
复存在。如有需要,可用局部于脚本的变量:
let s:myjob = job_start('my-command')
{options}
必须是字典。可包含许多可选项目,见 job-options 。
也可用作 method :
BuildCommand()->job_start()
job_status({job}
) job_status() E916
返回字符串,返回 {job}
的状态:
"run" 作业运行中
"fail" 作业无法启动
"dead" 作业启动后结束或被终止
Unix 上不存在的命令会报告 "dead" 而不是 "fail",因为在检测到问
题之前,fork 操作已经发生。
Vim9 脚本中如果有变量声明为 "job" 类型但从未赋值,传递该变量到
job_status() 会返回 "fail"。
如果用 "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 (最强的停止方式)
数值 该数值对应的信号
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()
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 。
注意
: 设置 "mode" 时,覆盖部分特定的模式。因而,先设置
"mode",再设置部分特定的模式。
备注
: 写入文件或缓冲区和从缓冲区读取时,缺省使用 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",使用通道本身的回调。
两个参数是通道和信息。
job-err_cb err_cb
"err_cb": handler stderr 有读取内容时的回调。仅适用于通道使用管道时。如
果不设 "err_cb",使用通道本身的回调。
两个参数是通道和信息。
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 作最多每秒十次的检查。也可以通过
job_status() 调用触发检查,这样也会调用 exit_cb 处理
函数。
备注
数据可能会缓冲,进程结束后还可能调用回调。
job-timeout
"timeout": time 等待阻塞请求,如 ch_evalexpr() 等的时间,以毫秒计。缺
省是 2000 (2 秒)。
out_timeout err_timeout
"out_timeout": time stdout 所用的超时。仅适用于管道情形。
"err_timeout": time stderr 所用的超时。仅适用于管道情形。
注意
: 设置 "timeout" 时,覆盖部分特定的模式。因而,先
设置 "timeout",再设置部分特定的模式。
job-stoponexit
"stoponexit": {signal}
Vim 结束时给作业发信号 {signal}
。可取值参见
job_stop() 。
"stoponexit": "" Vim 结束时不结束作业。
缺省是 "term"。
job-term
"term": "open" 在新窗口中启动终端并重定向作业的 stdin/stdout/stderr
到终端。和 :terminal 类似。
注意
: 尚未实现!
"channel": {channel}
使用己有的通道,而不重新创建。
新作业所用的通道部分会从之前的使用者处断开。如果通道还
在被其它的作业使用,这可能会导致 I/O 错误。
已有的回调和其它设置不变。
"pty": 1 如有可能,使用 pty (伪终端) 而非管道。最常和终端窗口组
合使用,见 terminal 。
{仅在 Unix 和类似系统中才有效}
job-in_io in_top in_bot in_name in_buf
"in_io": "null" 不连接 stdin (从 /dev/null 读取)
"in_io": "pipe" 连接 stdin 到通道 (缺省)
"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
error..."
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() 改
变。
如果文件已存在,文件内容被清空。
要获知作业的状态:
echo job_status(job)
要停止作业的运行:
job_stop(job)
这是停止作业的正常方式,Unix 上会给作业发出 SIGTERM。也可以用其它方式来停止作
业,甚至可以发送任意的信号。例如,要强制终止作业,"杀了它":
job_stop(job, "kill")
更多选项,可见 job_stop() 。
如果你希望在 Vim 窗口中为作业提供输入,有几种选项:
- 使用普通缓冲区,自行处理所有可能的命令。这很复杂,因为可能的命令有很多。
- 使用终端窗口。此方式直接在窗口中键入作业输入并显示作业的输出。
见 terminal-window 。
- 使用带提示缓冲区的窗口。此方式下在 Vim 中输入一行作业命令,而在窗口中显示作
业的 (可能经过过滤的) 输出。
通过设置 'buftype' 为 "prompt" 创立提示缓冲区。通常只在缓冲区新建时使用。
用户可以在缓冲区的末行上编辑和输入一行文本行。在该提示行上输入 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 脚本:
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
可在这里获取语言服务器规范:
https://microsoft.github.io/language-server-protocol/specification
每个 LSP 协议消息以简单 HTTP 头开始,后跟 JSON-RPC 格式编码的负载。JSON-RPC 格
式描述可见:
https://www.jsonrpc.org/specification
连接到 LSP 服务器并使用 channel-mode 设为 "lsp",可以把 Vim Dict 形式的
LSP 请求/通告消息编码为 LSP JSON-RPC 消息并发送至服务器,然后接收 LSP JSON-RPC
形式的响应/通告消息并解码为 Vim Dict 。
channel-mode 设为 "lsp" 时,对通道接收到的消息,Vim 会处理 HTTP 头部,并把
JSON-RPC 负载解码为 Vim Dict 类型,并调用 channel-callback 函数或指定的
channel-onetime-callback 函数。用 ch_evalexpr() 或 ch_sendexpr() 函数在
通道上发送消息时,Vim 会附加 HTTP 头部,并把 Vim 表达式编码为 JSON。
参见 json_encode() 和 json_decode() 说明 Vim 编码和解码内建类型到 JSON 的
详情。
要用 '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)
注意
如果作业使用 stdout 来输出 LSP 消息,而用 stderr 来输出非-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()
... <handle failure>
endif
注意
请求消息不应指定 'id'
字段。即使指定,Vim 会用内部生成的标识符取而代之。
Vim 目前只支持数值类型的 'id'
字段。不管 RPC 请求是成功还是失败,回调函数都会
被调用。
要发送 JSON-RPC 请求到服务器并异步处理其响应,可用 ch_sendexpr() 函数并提供
回调函数。如果请求消息指定了 "id" 字段,Vim 会用内部生成的标识符取而代之。此函
数返回字典,其中包含消息所用的标识符。这可用于给 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 请求,用发送此请求
的 ch_sendexpr() 函数返回的 ID 来调用 ch_sendexpr() 以给服务器发送撤销消
息。例如:
" 发送 completion 请求
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 字典形式表示)。"params" 字段可选:
{
"jsonrpc": "2.0",
"id": <number>,
"method": <string>,
"params": <list|dict>
}
LSP 响应消息有以下格式 (以 Vim 字典形式表示)。"result" 和 "error" 字段可选:
{
"jsonrpc": "2.0",
"id": <number>,
"result": <vim type>
"error": <dict>
}
LSP 通告消息有以下格式 (以 Vim 字典形式表示)。"params" 字段可选:
{
"jsonrpc": "2.0",
"method": <string>,
"params": <list|dict>
}
vim:tw=78:ts=8:noet:ft=help:norl: