Delve 是一个 Go Debugger,提供了 JSON-RPC API,试试在 Emacs 中控制它。
关于 Emacs 配合 Delve,已经有两个包了,我一个也没有用过。
- go-dlv.el
- 基于 comint-mode,实现简单,估计功能有限
- emacs-lsp/dap-mode: Emacs Debug Adapter Protocol
- …
- 使用
jsonrpc.el
让 Delve 和 Emacs 能够交流 - 研究下 Emacs 在 Debug 的流程?我没经验
- (info “(emacs) Debuggers”) gud.el
- realgud 可能这个比较好扩展?
开启服务器:
dlv debug --headless --api-version=2 --log --listen=127.0.0.1:8181
Delve 用 TCP Socket,不是 HTTP。使用 Netcat 测试连接:
~$ nc 127.0.0.1 8181 {"method":"RPCServer.ListSources","params":[],"id":3} { "id": 3, "result": { "Sources": [ "/Users/xcy/go/src/github.com/xuchunyang/helloworld/main.go", "/usr/local/Cellar/go/1.13.3/libexec/src/errors/errors.go", "/usr/local/Cellar/go/1.13.3/libexec/src/errors/wrap.go", "/usr/local/Cellar/go/1.13.3/libexec/src/fmt/format.go", …… "/usr/local/Cellar/go/1.13.3/libexec/src/unicode/tables.go", "/usr/local/Cellar/go/1.13.3/libexec/src/unicode/utf8/utf8.go", "<autogenerated>" ] }, "error": null }
成功。
需要同时管理 Server 和 Client。
(let ((default-directory "/Users/xcy/go/src/github.com/xuchunyang/helloworld/"))
(make-process
:name "dlv server"
:buffer "*dlv server*"
:connection-type 'pipe
:coding 'utf-8-emacs-unix
:command (split-string
"dlv debug --headless --api-version=2 --log --listen=127.0.0.1:8181")))
(make-network-process
:name "dlv client"
:buffer "*dlv client*"
:coding 'utf-8-emacs-unix
:host 'local
:service 8181)
(process-send-string
(get-buffer-process "*dlv client*")
"{\"method\":\"RPCServer.ListSources\",\"params\":[],\"id\":3}\n")
OK。
估计是因为 Eglot (或者说 LSP)的 Server 和 Client 之间的通信一个一个的,有很多连接,而不是只有一个。
竟然没有文档 ,其实在 (elisp)JSONRPC ,但是只有 master 版本的才有,估计等 Emacs 27 发布,而我目前用的是最新稳定版 26.3。
(eww-open-file "/Users/xcy/src/emacs/manual/html_node/elisp/JSONRPC.html")
jsonrpc-process-connection 会发送一个 Content-Length ,但 delve 不需要,所以不能直接用 jsonrpc-process-connection。
模仿它重现定一个 Class,就是去掉 Content-Length
(defclass my-class (jsonrpc-connection)
((process :initarg :process)))
(cl-defmethod jsonrpc-connection-send ((connection my-class) &rest args)
(process-send-string
(oref connection :process)
(concat (json-encode-plist args) "\n")))
Process 最重要的是设置 Filter,Devel 的返回的 JSON 会放在同一行,且以一个 \n 结束:
{"id":5,"result":...}
所以如果 Filter 中发现 Point 跑到行首了,就表示 JSON 结束了。不需要 Content-Length 或者自己判断 JSON 有没有结束,如果有需要可参考
- simple is better - JSON-RPC 2.0 Transport: Sockets
- 判断 JSONRPC Request/Response 是否结束的几个思路
当 JSON 结束时,调用 jsonrpc-connection-receive 来结束。注意 JSON 解码成 jsonprc.el 要求的 Plist,而不是 json.el 默认的 Alist。
(defun my-class--filter (proc string)
(when (buffer-live-p (process-buffer proc))
(with-current-buffer (process-buffer proc)
(save-excursion
;; Insert the text, advancing the process marker.
;;
(goto-char (process-mark proc))
(insert string)
(set-marker (process-mark proc) (point))
(when (bolp)
(let ((json-message
(let ((json-object-type 'plist))
(json-read-from-string
(buffer-substring-no-properties
(save-excursion (forward-line -1) (point))
(point))))))
(message "=> %s" json-message)
(with-temp-buffer
(jsonrpc-connection-receive x json-message))))
(message "debug: got %s want %s" (bolp) t)))))
上面的 x 表示一个 jsonrpc-connection 对象,即
;; Server 启动见以上
;; Client
(make-network-process
:name "dlv client"
:buffer "*dlv client*"
:coding 'utf-8-emacs-unix
:filter #'my-class--filter
:sentinel #'jsonrpc--process-sentinel
:host 'local
:service 8181)
(setq x (my-class :process (get-buffer-process "*dlv client*")))
尝试
(jsonrpc-request x :RPCServer.ListSources [])
OK。
先放着。
现在没有 Debug 需求,连 Delve 命令行版都没认真用过。以后对 Go 更熟悉了再说。