基于tree-sitter的结构性编辑插件

https://github.com/manateelazycat/grammatical-edit 是基于 tree-sitter 的结构化编辑插件, 这个项目的目标是实现 ParEdit、thing-edit、textobj 这三类插件的功能合集。

最主要的优点是基于 tree-sitter 的 AST 而不是正则来实现语法对象编辑,只要 tree-sitter 支持的编程语言,都可以进行结构性编辑,不需要像 smartparens 或者 awesome-pair.el 针对一些偏门语言增加 workaround, 甚至是明天才发布的新编程语言都可以支持。

安装方法

  1. 先安装 tree-sitter: https://emacs-tree-sitter.github.io/installation/
  2. 下载 grammatical-edit 后在配置文件中加入(require 'grammatical-edit)

配置启用的语言

(dolist (hook (list
               'c-mode-common-hook
               'c-mode-hook
               'c++-mode-hook
               'java-mode-hook
               'haskell-mode-hook
               'emacs-lisp-mode-hook
               'lisp-interaction-mode-hook
               'lisp-mode-hook
               'maxima-mode-hook
               'ielm-mode-hook
               'sh-mode-hook
               'makefile-gmake-mode-hook
               'php-mode-hook
               'python-mode-hook
               'js-mode-hook
               'go-mode-hook
               'qml-mode-hook
               'jade-mode-hook
               'css-mode-hook
               'ruby-mode-hook
               'coffee-mode-hook
               'rust-mode-hook
               'qmake-mode-hook
               'lua-mode-hook
               'swift-mode-hook
               'minibuffer-inactive-mode-hook
               ))
  (add-hook hook '(lambda () (grammatical-edit-mode 1))))

快捷键配置

(define-key grammatical-edit-mode-map (kbd "(") 'grammatical-edit-open-round)
(define-key grammatical-edit-mode-map (kbd "[") 'grammatical-edit-open-bracket)
(define-key grammatical-edit-mode-map (kbd "{") 'grammatical-edit-open-curly)
(define-key grammatical-edit-mode-map (kbd ")") 'grammatical-edit-close-round)
(define-key grammatical-edit-mode-map (kbd "]") 'grammatical-edit-close-bracket)
(define-key grammatical-edit-mode-map (kbd "}") 'grammatical-edit-close-curly)
(define-key grammatical-edit-mode-map (kbd "=") 'grammatical-edit-equal)

(define-key grammatical-edit-mode-map (kbd "%") 'grammatical-edit-match-paren)
(define-key grammatical-edit-mode-map (kbd "\"") 'grammatical-edit-double-quote)

(define-key grammatical-edit-mode-map (kbd "SPC") 'grammatical-edit-space)
(define-key grammatical-edit-mode-map (kbd "RET") 'grammatical-edit-newline)

(define-key grammatical-edit-mode-map (kbd "M-o") 'grammatical-edit-backward-delete)
(define-key grammatical-edit-mode-map (kbd "C-d") 'grammatical-edit-forward-delete)
(define-key grammatical-edit-mode-map (kbd "C-k") 'grammatical-edit-kill)

(define-key grammatical-edit-mode-map (kbd "M-\"") 'grammatical-edit-wrap-double-quote)
(define-key grammatical-edit-mode-map (kbd "M-[") 'grammatical-edit-wrap-bracket)
(define-key grammatical-edit-mode-map (kbd "M-{") 'grammatical-edit-wrap-curly)
(define-key grammatical-edit-mode-map (kbd "M-(") 'grammatical-edit-wrap-round)
(define-key grammatical-edit-mode-map (kbd "M-)") 'grammatical-edit-unwrap)

(define-key grammatical-edit-mode-map (kbd "M-p") 'grammatical-edit-jump-right)
(define-key grammatical-edit-mode-map (kbd "M-n") 'grammatical-edit-jump-left)
(define-key grammatical-edit-mode-map (kbd "M-:") 'grammatical-edit-jump-out-pair-and-newline)

添加 tree-sitter 对 elisp 的支持

1. git clone https://github.com/Wilfred/tree-sitter-elisp
2. gcc ./src/parser.c -fPIC -I./ --shared -o elisp.so
3. cp ./elisp.so ~/.tree-sitter-langs/bin
(tree-sitter-load 'elisp "elisp")
(add-to-list 'tree-sitter-major-mode-language-alist '(emacs-lisp-mode . elisp))

添加Vue对tree-sitter支持

1. git clone https://github.com/ikatyang/tree-sitter-vue.git
2. gcc ./src/parser.c ./src/scanner.cc -fPIC -I./ --shared -o vue.so
3. cp ./vue.so ~/.tree-sitter-langs/bin (~/.tree-sitter-langs/bin is path of your tree-sitter-langs repo)
(tree-sitter-load 'vue "vue")
(add-to-list 'tree-sitter-major-mode-language-alist '(web-mode . vue))

备注

grammatical-edit的代码 fork 于 awesome-pair.el , 仍然有一部分代码需要从原来正则匹配转换成 AST 逻辑,欢迎大家试用反馈。

7赞

masteringemacs 也用 treesitter 做了一个类似的东西,这大概就是英雄所见略同把

https://www.masteringemacs.org/article/tree-sitter-complications-of-parsing-languages https://github.com/mickeynp/combobulate

https://github.com/manateelazycat/grammatical-edit/commit/54830880ab04863ebf743649a998401a91896299

用tree-sitter AST 重写了 grammatical-edit-web-mode-kill ,全部用语法对象来判断,不但代码比原来一堆正则解析更简单,功能也更加稳定了。

向lazycat再分享一个基于tree-sitter 的search https://github.com/BrianHicks/tree-grepper

这个插件主要是搜索结构模式? 主要用在哪些场景?

是的这个插件只关心结构搜索

就像作者博客里写到的 https://bytes.zone/posts/tree-grepper/ (可以看看作者的场景) Tree-grepper is focused only on search: it doesn't do linting (like semgrep) or AST-based refactoring (like comby.)

具体说有什么其他应用场景,就需要大家一起来探索脑洞了,简单来讲tree-greeper就是基于Tree-sitter’s s-expressions query的进一步增强。

感觉可以做为结构模式搜索去研究重复代码,但是感觉没人眼看快啊,哈哈哈哈

  1. 他可以找到一个变量的类型,进而增强tag系统的能力。

  2. 可以很快的了解一个类的某个方法是在哪里及如何被用到的。

重构的差不多了,现在 vue.js 这种 web-mode kill tag的操作简直幸运流水, tree-sitter AST API把我原来大量的正则匹配的方法全部替换掉了,而且代码非常稳定和通用。

1赞