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

内部几乎完全重新设计了一遍, 还不算大更新?

其实lsp-mode的作者也有意愿将lsp-mode集成到emacs里, 基础功能分离就是为这个目标, 没想到现在又出了一个, 挺热闹的.

lsp-mode 代码不好搜索,部分原因也是因为它比较完善代码量大了。

这个 eglot 这样做法不太好吧,选用什么 ls 组织上钦定了,无视各种 ls 还在竞争当中,没有完全定型吗?它打算内置多少种语言呢,还是仅仅写个例子做参考而已:

(defvar eglot-server-programs '((rust-mode . ("rls"))
                                (python-mode . ("pyls"))
                                (js-mode . ("javascript-typescript-stdio"))
                                (sh-mode . ("bash-language-server" "start")))
  "Alist mapping major modes to server executables.")
  
...

;;; Rust-specific
;;;
(defun eglot--rls-probably-ready-for-p (what proc)
  "Guess if the RLS running in PROC is ready for WHAT."
  (or (eq what :textDocument/completion) ; RLS normally ready for this
                                        ; one, even if building ;
      (pcase-let ((`(,_id ,what ,done ,_detail) (eglot--spinner proc)))
        (and (equal "Indexing" what) done))))

...

除了 lsp-mode 之外,还有个 lsp-ui,是不是也再造一遍?

这个问题不大, 自己修改一下eglot-server-programs的值就可以了, 我更喜欢这种方式, 修改的地方比较集中, 反倒感觉挺方便.

看文件只有1000多行, 就实现了常用的lsp的功能

不应该说代码量太大, 还是因为宏用的太多了, 仅仅代码量大不影响describe-function

ui这种本来就是多种多样, 不同的人喜欢不同的风格.

没有看出“集中”在哪里,这些代码放在用户配置里,也不会造成不集中。何况作者想要把它提交为内置包,我不认为 Emacs 开发者能接受这种把某个特定 ls 的配置写进基础包的做法。

不能这么看。验证核心功的时侯可以写得简洁一点,要达到可用的程度,处理各种枝末细节的问题,代码自然就多了,然后开始抽象/复用。。也少不了会用到宏。

lsp-mode 现在 1100 多次提交,代码 2500 多行,注释近 500 行,测试近 300 行,似乎也没有很臃肿。


一些函数不能跳转,确实有点烦,不过在 lsp-mode 中没那么严重吧,大家还是对 spacemacs 吐槽比较多。

不能跳转也不是因为它是宏,而是因为没有实现相应的查找规则。宏没什么特别的 (最常用的 defun 就是宏),都是文本查找而已:

  • find-function-regexp

    "^\\s-*(\\(def\\(ine-skeleton\\|ine-generic-mode\\|ine-derived-mode\\|ine\\(?:-global\\)?-minor-mode\\|ine-compilation-mode\\|un-cvs-mode\\|foo\\|\\(?:[^icfgv]\\|g[^r]\\)\\(\\w\\|\\s_\\)+\\*?\\)\\|easy-mmode-define-[a-z-]+\\|easy-menu-define\\|menu-bar-make-toggle\\)\\(?:\\s-\\|
    \\|;.*
    \\)+\\('\\|(quote \\)?%s\\(\\s-\\|$\\|[()]\\)"
    
  • find-function-regexp-alist

    ((define-mode-local-override . xref-mode-local-find-override)
     (define-overloadable-function . xref-mode-local-find-overloadable-regexp)
     (flycheck-checker . flycheck-find-checker-regexp)
     (define-type . cl--typedef-regexp)
     (cl-defgeneric . cl--generic-find-defgeneric-regexp)
     (cl-defmethod . cl--generic-search-method)
     (nil . find-function-regexp)
     (defvar . find-variable-regexp)
     (defface . find-face-regexp)
     (feature . find-feature-regexp)
     (defalias . find-alias-regexp))
    

lsp是干嘛使的?

作者是此人,有没有跑,我不知道。

https://twitter.com/lunaryorn/status/792644690797625344

个人觉得flycheck比flymake好用多了,为什么不能内置呢?

貌似是因为原作者不愿意

https://github.com/flycheck/flycheck/issues/1177#issuecomment-267445833

emacs 的邮件列表里也有关于这个问题的讨论

https://lists.gnu.org/archive/html/emacs-devel/2017-10/msg00499.html

emacs 现任维护者 John Wiegley 也对 flycheck 的作者发出过邀请,希望可以将 flycheck 加入 elpa 进而成为 emacs 的一部分,不过可惜被拒绝了

https://github.com/flycheck/flycheck/issues/801

flymake 完全重写了,到时候也可以重新试试看。 其实现在安装软件都是包管理,即便不内置安装起来也不是太麻烦,选择自己喜欢的用就可以了。

2 个赞

宏太多,不会测试… 没法测试是配置的lsp server有问题,还是lsp-mode有问题,还是lsp server的哪个步骤出了问题

写 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, 虽然可能效率很高, 但是调试太麻烦了, 也很难看懂, 要么就展开再调试, 感觉麻烦