又有人给emacs开发了一个lsp客户端

写 elisp 不可避免用到宏,一些习以为常的东西都是宏,比如 defun / when / unless / dolist / with-eval-after-load / ...

那些很好用/方便的的“函数”,大胆怀疑它们是宏就对了,基本没跑。

定义了一大堆宏还不够,后来又引入 cl。可以说,没用宏就不能愉快地写 elisp。

如果对宏那么排斥,就不应该这样定义函数:

(defun foo ()
  (message "foo"))

应该写成这样:

(defalias (quote foo)
  (function
   (lambda nil
     (message "foo"))))
3 个赞

不好意思,lambda 也是宏。

lambda is a Lisp macro in ‘subr.el’.

(lambda ARGS [DOCSTRING] [INTERACTIVE] BODY)
(defalias (quote foo)
  (list (quote lambda) nil
        (quote (message "foo"))))
4 个赞

我是能用内置都就尽量用内置的,可惜flymake实在不给力。

假如一个函数或者宏内部可以定义函数, 定义的函数名字根据输入的参数来定, 比如参数是: prefix和name, 最终他定义的函数名是prefix和name的值合起来转换为符号名, 这样的函数能搜索吗?

(my-define-function (“hello” “world”)) 会定义一个函数, 名字为hello-world, 如何查看这个函数的定义? 如何找到hello-world是在哪定义的?

可以,至少可以定位到具体是在哪个文件定义的。宏最终都是要展开成函数的,展开的函数并不会跨文件。有 60 年历史的 Lisp 不可能没人想到这种问题。

你可以自己试试,用 C-h f 完全可以查到这种方式定制的函数。

dufun 自己就是宏,defun 定义的函数可以在 Emacs 中查到定义位置,没有道理别的宏不行。

至于不能具体定位,只是写的宏不够精致而已,像是 eieio 这种“第三方”定义的宏,也能用 Emacs 自带的 describe-function 查询定义位置。

你这个问题我以前也提到过。

我现在配置文件基本是这样的:

;; misc.el
(definitfun foo
  :pre
  (progn ...)
  :post
  (progn ...))

解析的时侯原地展开为:

;; misc.el
(defun misc--pre-init-foo ()
  (progn ...))

(defun misc--post-init-foo ()
  (progn ...))

这两个函数仍然在 misc.el 文件中,所以 describe-function 可以跳到 misc.el 文件头。如果我再实现一个跨行匹配 (definitfun pkg\n :pre 的方法,就能准确跳到 :pre 的位置。

然而实际上我并没有实现那个跳转方法(暂时没这个需求),而是给 imenu regex 加了一条规则用来定位整个 (definitfun pkg ...)

1 个赞

比如cquery的lsp-cquery-enable, 用describe-function可以打开cquery.el文件, 光标在文件开头, 具体定义还要自己找, 甚至看很多代码

对, 试了一下lsp-cquery-enable, 是跳到cquery.el文件开头, 但这还不够啊, 还要看很多代码才行

看了一下, 就是这个情况, 就是感觉这个不方便. 特别是跟踪调试的时候, 想起以前有一个c语言用宏实现的hash table, 虽然可能效率很高, 但是调试太麻烦了, 也很难看懂, 要么就展开再调试, 感觉麻烦

lsp-mode 的做法是更进一步,它把整个函数的实现都放在另一个宏里面,无数个 lsp-<language>-enable 都对应这同一块代码。我上边的例子,函数的主体其实还在声明的这一端,比较有存在感。

然而 eglot 项目也不是为了解决这个问题而成立的。作者只是想用一个统一的 eglot 函数作为入口,而非每一种语言对应一个 eglot-<language>。把整个函数的实现放在另一个宏里面,目前的代码里已经有了,以后随着项目越来越完善,这种情况只会更多:

其实调试不必纠结 lsp-<language>-enable 能不能定位到,直接看它后边的函数 lsp--enable-stdio-client。把它看作跟 eglot 一样,是统一的入口。lsp-<language>-enablelsp--enable-stdio-client 之间几乎只是参数传递。

那么问题来了,那个更好呢?

现在貌似lsp-mode比较流行 一 点

eglot 绝对比 lsp-mode 好用

lsp-mode 一旦 Kill buffer 就会导致各种报错, eglot 可以保持后台重新连接, 没有各种奇怪的报错.

eglot 的作者写出的代码开箱即用, 插件要看实际运行的效果, 而不是只看代码本身, 如果代码写的再好, 但是用户体验和质量做的一团糟, 我想没人想敲几个字符就 backtrace 吧?

既然大神如此推荐,我也去试试eglot,哈哈

lsp-mode 有imenu,find-references之类的功能。imenu index的支持对js开发者简直是福音。不过希望大神提出的这些问题lsp-mode能够早日解决吧。

https://github.com/emacs-lsp/lsp-mode/blob/master/lsp-methods.el#L877

(add-hook 'kill-buffer-hook #'lsp--text-document-did-close nil t)

kill-buffer會發送當前document的textDocument/didClose notification,solargraph (Ruby language server)在這種情況下會斷線?

还没有深入研究, kill-buffer 会导致 solargraph 的端口被占用, 因为 lsp-ruby 是写死端口号的.

應該用 stdin/stdout,stdin/stdout用fifo (emacs)

都不怎么滴,没nvim好用。我全都测过。同一份代码。nvim瞬间补全,而且我看过cquery cpu,触发补全时,cpu非常低。用emacs时,cpu就会非常高,而且持续好几秒,最终的表现就是补全速度非常慢。估计是发消息太太太频繁了。不如nvim客户端优化的好。

lcn cpu最低10%,eglot其次30%。lsp mode最高,能达到70%。 所以补全速度的体验也是天壤之别。有空我👆个图,让大家看看