首先异步子进程的输出是断断续续的,比如一个 JSON、MessagePack 对象会分成 N 次传到子进程的 filter 函数,比如问 Neovim 当前行的内容:
(make-network-process
:name "nvim"
:buffer "*nvim output*"
:service (car (file-expand-wildcards (concat temporary-file-directory "nvim*/*")))
:family 'local
:coding 'binary)
(process-send-string
(get-process "nvim")
(msgpack-encode '(0 1 "nvim_get_current_line" [])))
第一个问题就是需要判断返回结束了没有,假设这个问题在 filter 里可以解决,怎么封装成 API 看起来很困难,API 可以分为同步和异步两种,以同步为例:
(nvim-request 'get-current-line)
;; => "Hello"
用 (while ... (accept-process-output ...)
堵塞 Emacs 等 filter 报告接收完成,似乎是这个思路,感觉很难实现。看过 jsonrpc.el 的代码,看起来非常复杂。
cireu
2
这个需要在response header里知道response的总长度,才能知道怎么去handle
嗯,LSP 用的就是这个方法,但也有别的思路,JSON streaming - Wikipedia 像 MessagePack 用不着知道总长度,它的长度就隐含在返回里,#xa2 就表示长度是 2 的字符串,再读 2 byte 就结束了。
现假设这个问题解决了,后面封装成 API 也很困难,如果自己实现 LSP 客户端的话,第一个问题就是如何写个 JSONRPC 客户端,感觉非常麻烦,没有现成可用库的话。
相当于实现个 MessagePack RPC 客户端的库,类似 GNU ELPA - jsonrpc
本来是想实现个 Neovim 客户端的,Neovim 已经有了十来种语言的库
已经专门写了个 MessagePack 库,结果卡这里了。
cireu
6
你可以用class从jsonrpc-connection
继承,然后改写几个parse/format的方法,这样就可以复用JSONRPC的代码了
对于同步与异步的问题,最简单粗暴(但有用)的方法就是统一返回Promise,Promise是对一个将来要到来的值的抽象。如果不想用Promise,那就分开两个函数一个sync一个async
1 个赞
我的个人观点其实可以用别的语言写这个json的解析库,做lsp客户端。
然后再通过IPC把补全的结果直接给Emacs
Emacs 只用实时汇报项目的位置和原文件的修改内容就行了。
现在这种用elisp直接处理lsp的返回结果,性能太差。
估计把 jsonrpc--async-request-1
看明白大致就知道 jsonrpc 是如何工作的,同步和异步都是用的它实现的
cireu
9
哦,你说基于子进程实现同步API吗?就是用accept-process-output
来block Emacs(这个block不会影响filter执行)然后在filter里用throw中断block就好了。
我的意思是直接复用jsonrpc的API就可以了,虽然名字叫jsonrpc,但是不影响我们继承原class后改写成基于msgpack的RPC。
An example: ;;; naria2-jsonrpc.el --- Control aria2 in Emacs via WebSocket -*- lexical-bindi - Pastebin.com
嗯,jsonrpc-request 是这么实现的,现在搞清楚异步是如何实现的就 OK 了。
我记得Ivy也是支持异步的,不知道他的异步是如何完成的。