tree-sitter 支持 Elisp 本身吗?

我看了tree-sitter官网本身是支持Emacs Lisp的,我准备基于 tree-sitter 的语法分析树来写一个集合 awesome-pair.el / thing-edit.el / syntax-move 的集成插件。

按照 GitHub - Wilfred/tree-sitter-elisp: tree-sitter grammar for emacs lisp 的说明,npm 安装了,同时 tree-sitter 也弄好了。

加了 (add-to-list 'tree-sitter-major-mode-language-alist '(emacs-lisp-mode . elisp)) 配置不管用

测试代码是 (tree-sitter-node-at-point), 这个测试代码在Python文件可以,但是在 Elisp 文件不行。

有没有朋友折腾过 tree-sitter ,让其支持 Emacs Lisp ?

1 个赞

基于 tree-sitter 的原理写了一个

(defun grammatical-edit-jump-right ()
  (interactive)
  (let* ((current-node (tree-sitter-node-at-point))
         (next-node (tsc-get-next-sibling current-node))
         (current-node-text (tsc-node-text current-node)))
    (cond ((looking-at "\\s-+")
           (search-forward-regexp "\\s-+" nil t))
          ((eolp)
           (next-line 1)
           (beginning-of-line)
           (search-forward-regexp "\\s-+" nil t))
          ((> (length current-node-text) 0)
           (forward-char (length current-node-text)))
          (next-node
           (goto-char (tsc-node-end-position next-node)))
          )))

这个函数会按照AST对象向右移动,比 awesome-pair-jump-right 智能的多。

首先 clone tree-sitter-elisp 这个 repo,然后编译

gcc -fPIC -I./ --shared -o elisp.so

把编译出来的shared library 放在 ~/.tree-sitter/bin 下面 (tree-sitter 默认 load-path)

然后

(tree-sitter-load 'elisp "elisp")

(add-to-list 'tree-sitter-major-mode-language-alist '(emacs-lisp-mode . elisp))

应该就可以了,记得 enable tree-sitter-mode


找茬时间

string with properties

;; #<cursor>("(xr \"\\\\(,\\\\)\\\\|\\\\(。\\\\)\\\\|\\\\(?\\" 0 1 (fontified nil) 1 4 (fontified nil) 4 5 (fontified nil) 5 7 (fontified nil) 7 8 (fontified nil) 8 9 (fontified nil) 9 11 (fontified nil) 11 12 (fontified nil) 12 14 (fontified nil) 14 15 (fontified nil) 15 17 (fontified nil) 17 18 (fontified nil) 18 19 (fontified nil) 19 21 (fontified nil) 21 22 (fontified nil) 22 24 (fontified nil) 24 25 (fontified nil) 25 27 (fontified nil) 27 28 (fontified nil) 28 29 (fontified nil) 29 30 (fontified nil))

(ts-node-type (tree-sitter-node-at-point))
;; => "#("

record literal

;; #<cursor>s(foo 1 2 3 4)

(ts-node-type (tree-sitter-node-at-point))
;; => ERROR

reference literal

;; '(#1=(a) b #<cursor>1#)

(ts-node-type (tree-sitter-node-at-point))
;; => ERROR

uninterned symbol

;; <cursor>#:uninterned

(ts-node-type (tree-sitter-node-at-point))
;; => ERROR

opaque object (这个倒不能说他弄错了,因为 Emacs reader 的逻辑是遇到 #< 立刻停止并报错)

;; <cursor>#<test>

(ts-node-type (tree-sitter-node-at-point))
;; => ERROR

:rofl:

4 个赞

大佬牛逼,可以了,分享一下我的编译步骤:

;; Add Emacs-Lisp for tree-sitter:
;;
;; 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-langs/bin is path of your tree-sitter-langs repo)

GitHub - manateelazycat/grammatical-edit: Grammatical edit base on tree-sitter 是基于 tree-sitter 的结构化编辑插件,用于替代 paredit 和 awesome-pair.el

最主要的优点是基于 tree-sitter 的 AST 而不是正则来实现语法对象编辑,只要 tree-sitter 支持的编程语言,即使开发者并不熟悉某个语言,也可以很好的工作。

4 个赞

tree-sitter 真是个好东西,Vue.js 也支持,分享下 vue 的 parser 支持配置

;; Add Vue for 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))

说到 tree-sitter 必须说明现有的动态模块都依靠 buffer-string 将文档给 tree-sitter 调用,每次编辑文档就会在内存中复制文档,效率很低,希望 tree-sitter 开发者能修复自定义 malloc 的 bug,这样也就能把 tree-sitter 内置到 emacs 中

希望emacs优化一下自己的模块设计,让更多的工具融合进来。

怎么优化?您有主意和其实现吗(

tree sitter本来就有自定allocator的功能,出了bug的话tree sitter应该修复

这个不是问题吗?

在说别人有问题的时候,怎么就不能先想想自己的问题呢?

动态模块懂得正确使用 gap 和防止 buffer text 在内存中挪动吗?不只是要添加功能,还要考虑新功能的安全性。

大家好好交流,人与人之间本身就是不一样

技术好的请保持谦虚,道理说清楚就好,不要怼别人,技术差一点的,抱着学技术交流目的,不太来回抬杠

如果大家不懂的基本尊重他人,只能每个人都禁言一段时间冷静冷静。

4 个赞

而且,Emacs 如果把 gap 和 relocating allocator 都暴露给动态模块,以后如果 Emacs 内部改变这些功能,一大堆动态模块就没法使用了

从实践中来讲,只是调用几次memcpy根本不会构成性能问题,除非能提出明确的 profile 数据,不然换做是我是不会采纳这个建议的。

所有有效率的edit buffer结构都不是连续存储所有字符的(包括但不限于piece table,gap buffer),不能直接给 tree sitter 用。把 tree sitter 整合进 emacs,可以以可控的方式获取 string 喂给 tree sitter,节省调用buffer-substring函数的开销(毕竟这函数还有一堆抛出lisp exception和boundary check的玩意)

另外你为什么觉得优化 emacs 和优化 tree sitter 是对立的东西?

你这么说就夸张了,照这样说所有 emacs 功能都应该用 C 写,不然没法满足你 2.6GHz 的手速

不只是 memcpy,还会改变 coding system,复制 interval 和去除 gap,所以绝对不能重复的调用 `buffer-string’

这也就是几次算术运算,if 判斷和多一次 memcpy。

没看到有做这个(