异步子进程难以封装成 API?

首先异步子进程的输出是断断续续的,比如一个 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 的代码,看起来非常复杂。

这个需要在response header里知道response的总长度,才能知道怎么去handle

感觉楼主是在做一个应用层协议。

嗯,LSP 用的就是这个方法,但也有别的思路,JSON streaming - Wikipedia 像 MessagePack 用不着知道总长度,它的长度就隐含在返回里,#xa2 就表示长度是 2 的字符串,再读 2 byte 就结束了。

现假设这个问题解决了,后面封装成 API 也很困难,如果自己实现 LSP 客户端的话,第一个问题就是如何写个 JSONRPC 客户端,感觉非常麻烦,没有现成可用库的话。

相当于实现个 MessagePack RPC 客户端的库,类似 GNU ELPA - jsonrpc

本来是想实现个 Neovim 客户端的,Neovim 已经有了十来种语言的库

已经专门写了个 MessagePack 库,结果卡这里了。

你可以用class从jsonrpc-connection 继承,然后改写几个parse/format的方法,这样就可以复用JSONRPC的代码了

对于同步与异步的问题,最简单粗暴(但有用)的方法就是统一返回Promise,Promise是对一个将来要到来的值的抽象。如果不想用Promise,那就分开两个函数一个sync一个async

1 个赞

我的个人观点其实可以用别的语言写这个json的解析库,做lsp客户端。

然后再通过IPC把补全的结果直接给Emacs

Emacs 只用实时汇报项目的位置和原文件的修改内容就行了。

现在这种用elisp直接处理lsp的返回结果,性能太差。

估计把 jsonrpc--async-request-1 看明白大致就知道 jsonrpc 是如何工作的,同步和异步都是用的它实现的

哦,你说基于子进程实现同步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也是支持异步的,不知道他的异步是如何完成的。

要不就是回调,要不就是timer轮询