前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Emacs Tree-sitter 初体验

Emacs Tree-sitter 初体验

作者头像
飞驰的西瓜
发布2023-09-06 12:55:16
1.1K0
发布2023-09-06 12:55:16
举报
文章被收录于专栏:EmacsTalkEmacsTalk

Tree-sitter 简介

Emacs 29 的一大亮点就是原生支持了 tree-sitter[1](后文简称 ts),它的官网是这么定义的:

Tree-sitter is a parser generator tool and an incremental parsing library.

现在较为流行的编辑器,如 neovim 都已支持 ts 作为解析库。

在 Emacs 中,之前都是采用正则的方式去解析语言的语法结构,这种方式虽然大多数情况是没有问题的,但主要有以下几个问题:

  1. 正则比较消耗 CPU,导致对大文件进行高亮时容易导致 Emacs 卡主,参见:Regexp Problems (GNU Emacs Lisp Reference Manual)[2]
  2. 对于有些语法结构,正则并不能很好的工作,参见:The true power of regular expressions[3]

因此,有了 tree-sitter 这种 parser generator 后,就可以比较方便地写出不同语言的解析器[4],而且能够保证高效。

安装步骤

由于 tree-sitter 核心部分使用 C 编写,因此 Emacs 默认并不链接它,需要用户自己制定,

代码语言:javascript
复制
./configure --with-tree-sitter

不同平台的包管理器都有预编译好的动态文件,在 macOS 上可以用如下命令:

代码语言:javascript
复制
brew install tree-sitter

编译完 Emacs 后,可以使用如下命令来测试:

代码语言:javascript
复制
(treesit-available-p)

返回 t 则说明配置成功。但到这里安装并没有完成,这只是完成了核心部分,不同语言的解析器需要单独安装,不过幸好社区内已经有人整理好了,

  • https://github.com/emacs-tree-sitter/tree-sitter-langs/releases

Emacs 默认从 treesit-extra-load-path 指定的路径去加载动态链接库,如果这个值是 nil ,它默认会从 user-emacs-directory 下的 tree-sitter 内加载。

所以在下载好对应平台的压缩包后,可以解压到上面指定的目录内。一般来说,语言和动态链接库名字都是一一对应的,比如 c 对应 libtree-sitter-c.dylib (Linux 下后缀为 so),所以需要对解压后的内容进行重命名,下面的命令供参考(依赖 fd[5]):

代码语言:javascript
复制
fd -t f dylib --exclude 'libtree*' --exec mv {} libtree-sitter-{/} \;

如果语言和动态链接库名字不对应,可以通过 treesit-load-name-override-list 来配置,比如:

代码语言:javascript
复制
(setq treesit-load-name-override-list
      '((c++ "libtree-sitter-cpp" "tree_sitter_cpp")
        (js "libtree-sitter-javascript" "tree_sitter_javascript"))
      )

在安装完不同语言的动态链接库后,需要开启 Emacs 对应的 mode 来使用,比如 c-mode 对应的 c-ts-mode ,Emacs 为不同的语言重新实现了一个以 ts-mode 结尾的新 mode,在 29 版本中,主要有如下 12 个:

代码语言:javascript
复制
12 candidates:
    lisp/progmodes/c-ts-mode.el
    lisp/progmodes/cmake-ts-mode.el
    lisp/progmodes/dockerfile-ts-mode.el
    lisp/progmodes/erts-mode.el
    lisp/progmodes/go-ts-mode.el
    lisp/progmodes/java-ts-mode.el
    lisp/progmodes/json-ts-mode.el
    lisp/progmodes/ruby-ts-mode.el
    lisp/progmodes/rust-ts-mode.el
    lisp/progmodes/typescript-ts-mode.el
    lisp/textmodes/toml-ts-mode.el
    lisp/textmodes/yaml-ts-mode.el

为了能够自动打开对应的 ts-mode ,可以通过配置 major-mode-remap-alist 来实现:

代码语言:javascript
复制
(setq major-mode-remap-alist
      '((yaml-mode . yaml-ts-mode)
        (sh-mode . bash-ts-mode)
        (js-mode . js-ts-mode)
        (css-mode . css-ts-mode)
        (c-mode . c-ts-mode)
        (c++-mode . c++-ts-mode)
        (c-or-c++-mode . c-or-c++-ts-mode)
        (python-mode . python-ts-mode)))

这样在进入 c-mode 时,会自动替换成 c-ts-mode ,到这里 tree-sitter 就算安装成功了。

社区插件

虽然 tree-sitter 还相对较新,但是社区内已经有些基于它实现的包了,比如:

  • mickeynp/combobulate: Structured Editing and Navigation in Emacs[6]

其他在进行的包有:

  • puni:feature Request: integration with tree[7]

对于 expand-region[8] 的用户,可以通过下面的配置来使用 ts:

代码语言:javascript
复制
(when (treesit-available-p)
  (defun my/treesit-mark-bigger-node ()
    "https://emacs-china.org/t/treesit-expand-region-el/23406"
    (let* ((root (treesit-buffer-root-node))
           (node (treesit-node-descendant-for-range root (region-beginning) (region-end)))
           (node-start (treesit-node-start node))
           (node-end (treesit-node-end node)))
      ;; Node fits the region exactly. Try its parent node instead.
      (when (and (= (region-beginning) node-start) (= (region-end) node-end))
        (when-let ((node (treesit-node-parent node)))
          (setq node-start (treesit-node-start node)
                node-end (treesit-node-end node))))
      (set-mark node-end)
      (goto-char node-start)))

  (add-to-list 'er/try-expand-list 'my/treesit-mark-bigger-node))

参考

  • How to Get Started with Tree-Sitter in Emacs 29[9]
  • Tree Sitter and the Complications of Parsing Languages[10]

参考资料

[1]

tree-sitter: https://tree-sitter.github.io/tree-sitter/

[2]

Regexp Problems (GNU Emacs Lisp Reference Manual): https://www.gnu.org/software/emacs/manual/html_node/elisp/Regexp-Problems.html

[3]

The true power of regular expressions: https://www.npopov.com/2012/06/15/The-true-power-of-regular-expressions.html#matching-context-free-languages

[4]

写出不同语言的解析器: https://tree-sitter.github.io/tree-sitter/creating-parsers

[5]

fd: https://github.com/sharkdp/fd

[6]

mickeynp/combobulate: Structured Editing and Navigation in Emacs: https://github.com/mickeynp/combobulate/

[7]

feature Request: integration with tree: https://github.com/AmaiKinono/puni/issues/38

[8]

expand-region: https://github.com/magnars/expand-region.el

[9]

How to Get Started with Tree-Sitter in Emacs 29: https://www.masteringemacs.org/article/how-to-get-started-tree-sitter

[10]

Tree Sitter and the Complications of Parsing Languages: https://www.masteringemacs.org/article/tree-sitter-complications-of-parsing-languages

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2023-08-05,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 EmacsTalk 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • Tree-sitter 简介
  • 安装步骤
  • 社区插件
  • 参考
    • 参考资料
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档