目前Emacs中基于 trees-sitter 的结构化编辑插件有哪些比较好用?

Emacs 29 发布在即,其中亮点功能就是内置了对 tree-sitter 支持。tree-sitter 除了可以实现高亮外,还可以支持更加准确的结构化导航和编辑,以及代码折叠(比如 ts-fold)等等。不知道目前流行的类似包有哪些?一起交流对比下?

最近看到Master Emacs 作者在 他的网站 介绍 tree-sitter 的使用,并推荐他的结构化编辑插件:combobulate。 初步使用下来,感觉还不错。

C-c o o 就会弹出类似 magit 的 transient 菜单,帮助开始不熟悉时看看:

M-PM-N 可以快速拖动 node(可以是很多种对象,取决于光标的位置。比如,如果光标在 python 中的 def 上,就是拖动函数): drag-complex

8 个赞

可以用我的fingertip,基于treesit

3 个赞

:+1: 多谢猫哥,原来之前有分享过。

刚试了下,感觉 fingertip 是用来替换 paredit 的。

下面补个fingertip的链接,方便不了的朋友。

1 个赞

mark 等分享。在 idea 里特别喜欢的功能估计需要借助 treesit 来实现:

  1. 快速移动语法块,看起来就是lz分享的 M-PM-N,idea 里也是根据当前的最大语法块来移动的,并能根据被移动的位置来决定移动位置。比如向上移动一个方法,如果上一行是一个方法的结尾,就会移动到上一个方法之上。如果上一行是普通代码,就移动到这行普通代码之上;
  2. expand region。根据语法扩展选区,在idea 里是 alt 加上下键,快速扩展选区到单词/字符串/括号/方法等
  3. 基于语法的折叠,正好看到了 ts-fold,目前是基于 treesitter,基于 treesit 的还是 WIP

对于你提到的这几点:

  1. combobulate 中的 M-PM-N 就是你要的,看第二张动图的效果。
  2. combobulate 也有了,按 M-h 激活选取,然后按 TAB 就可以扩展了(而且在minibuffer 还有按键提示)。
  3. 折叠功能,也在作者的开发计划中,可以跟踪这个新功能请求的issue

另外,目前 combobulate 对 C/C++ 的支持正在施工当中,可以关注这个 PR:Start trying to add support for C and C++ by chriselrod · Pull Request #29 · mickeynp/combobulate · GitHub

我今天在 Python 中体验了一天,很好用,期待后续的功能加强。

1 个赞

关于emacs的treesitter,我倒是没有使用;不过在neovim的应用中,我觉得GitHub - mfussenegger/nvim-treehopper: Region selection with hints on the AST nodes of a document powered by treesitter 非常好,和狗哥的 meow 的 meow-inner-of-thing/meow-bounds-of-thing 异曲同工,不过 nvim-treehopper是根据treesitter选中,meow有它自己的规则。

另外,我觉得假如扩展meow的treesitter能力会是一件有趣的事,因为原本meow就是先选中后动作的模式,meow-mark-word/meow-next-word这样的设计在原本的文本上做标记非常直观。而meow-inner-of-thing/meow-bounds-of-thing 并不算直观。假设移植了类似nvim-treehopper,会让操作更统一。

1 个赞

先选中再动作比较直观。否则光标所在位置,都不太清楚是属于哪个块。

是的,我是认为先选中后动作直观,我讲的那个nevim插件就提供了一种选择块的方式,和meow-inner-of-thing/meow-bounds-of-thing的选中方式相比,更为直观。

meow-thing-register 这个函数可以注册自定义的 thing, 然后用 tree-sitter 来找到边界就可以。参见这个函数的注释。

至于其它的函数,因为 meow 是用按键来调用的底层命令,所以只需要改良版的命令替换掉旧的命令,比如说 forward-word 如果被替换成基于 ts 的 forward-word, 只要绑定还是 M-f 就可。

感觉全面转向 *-ts-mode 非常困难,最大的问题就是这些 mode 基本上都是从 prog-mode 上重新开发的。以 go-mode 为例,go-mode-map 中有用户的大量 key-bindings,以及 lsp flycheck org-babel 等以及 golang 的一大堆工具集成,转到 go-ts-mode 后什么都干不了。如果以原有的 go-modeparent mode 那估计就可以无缝迁移了。

Try try 这个
先删掉 go-mode,然后在 go-ts-mode 加载后执行,这样会创建一个新的 go-mode 继承自 go-ts-mode

(define-derived-mode go-mode go-ts-mode "Golang")
(dolist (ext '("\\.go\\'"))
  (setf auto-mode-alist (assoc-delete-all ext auto-mode-alist))
  (add-to-list 'auto-mode-alist `(,ext . go-mode)))
1 个赞

我现在还在用tree-sitter mode,这个是minor mode,并且不需要啥额外配置。

个人也是比较喜欢 minor-mode 这种方式。

2 个赞

我目前寫得的 TreeSitter 插件:

有興趣的請務必試試看! :slight_smile:

7 个赞

Mastering Emacs 的作者更新了一篇文章讲解 combobulate 这个包的使用,感兴趣的可以看看。Combobulate: Editing and Searching with the new Query Builder - Mastering Emacs

1 个赞

原来 ts-docstr 居然是论坛中高人做的? :grinning: 我折腾了一晚上,没能成功用起来。能够指导一下?

我在一个 c 文件的一个函数上调用 ts-docstr-at-point,ts-docstr 它提示我:

user-error: Ignored, tree-sitter-mode is not enabled in the current buffer

于是我手动调用:tree-sitter-mode,这时候是 tree-sitter 提示我:

tree-sitter--setup: No language registered for major mode ‘c-ts-mode’

我发现该提示是 tree-sitter--setup 抛出来的:


(defun tree-sitter--setup ()
  "Enable `tree-sitter' in the current buffer."
  (unless tree-sitter-language
    ;; Determine the language symbol based on `major-mode' .
    (let ((lang-symbol (alist-get major-mode tree-sitter-major-mode-language-alist)))
      (unless lang-symbol
        (error "No language registered for major mode `%s'" major-mode))
      (setq tree-sitter-language (tree-sitter-require lang-symbol))))
  (unless tree-sitter-parser
    (setq tree-sitter-parser (tsc-make-parser))
    (tsc-set-language tree-sitter-parser tree-sitter-language))
  (add-hook 'before-change-functions #'tree-sitter--before-change :append :local)
  (add-hook 'after-change-functions #'tree-sitter--after-change :append :local))

因为 tree-sitter-major-mode-language-alist 为 nil,导致 (lang-symbol (alist-get major-mode tree-sitter-major-mode-language-alist)) 的 lang-symbol 为 nil 而报错。

于是我增加配置 (add-to-list 'tree-sitter-major-mode-language-alist '(c-ts-mode . c))

再次手动调用 tree-sitter-mode,这时候出现新的错误:

tree-sitter-load: Cannot find shared library for language: c

可是我在 /Users/c/.emacs.d/tree-sitter/ 文件夹下确实是有 libtree-sitter-c.dylib 文件。

我现在不知道如何解决。

大概你没在配置里 (require 'tree-sitter-langs),这不是 Emacs 自带的包,而是那个第三方的 tree sitter 绑定提供的功能

.emacs.d/tree-sitter/ 是 Emacs 自带 treesitter 的路径,和这个第三方的不一样

1 个赞

感谢,感谢。安装你提示的包后,问题解决了。