基于抽象语法树自动查找孤立函数

什么是 find-orphan.el ?

当我们完成一个大型项目后,经常需要重构代码以去掉项目过程中的无用代码,我们经常会按照下面流程进行操作:

  1. 打开源代码文件,找到函数定义
  2. 使用ripgrep工具把每个函数名都在项目目录下进行递归搜索
  3. 找到对应的源码文件,删除没有任何引用的孤立函数

如果在一个大型项目中一直重复上面的操作会非常痛苦, find-orphan.el 就是解决上面痛点的贴心插件。

现在你只需要打开源文件,然后执行命令 find-orphan-function-in-directory 即可,它会自动搜索所有函数名, 并用ripgrep自动在工程中搜索,最后告诉你所有孤立函数的列表,方便你快速删除无用代码。

原理

  1. 基于AST来查找所有函数名
  2. 使用ripgrep来统计函数名在项目中出现的次数
  3. 打印那些统计次数小于2的函数名

安装

  1. 安装tree-sitter
  2. 安装ripgrep
  3. 下载find-orphan.el并加载到load-path

把下面的代码添加到 ~/.emacs 中, 替换 <path-to-find-orphan>为 find-orphan.el 所在目录

(add-to-list 'load-path "<path-to-find-orphan>")
(require 'find-orphan)

使用

  • find-orphan-function-in-buffer : 在当前文档自动查找所有孤立函数
  • find-orphan-function-in-directory : 在当前工程自动查找所有孤立函数
7 个赞

适用于python等动态语言吗?

当然啊,所有tree-sitter支持得语言都可以

哦哦,有时间再尝试一下了

tree-sitter 和 TabNine 有啥关系?

然后又和EAF有啥关系?

你想表达什么?

你先想清楚你要表达什么吧。

我真的不知道我就写一个自动找函数的功能,你为啥说一下和帖子无关的东西,喜欢跑到别人的帖子里面刷存在感呢?

1 个赞

也许你可以来这个帖子回复。

对不同语言,tree-sitter生成的node的type各有不同,所以需要针对不同语言写不同的query。

估计懒猫只在某种/某些语言里试了find-orphan,我拿java试了下,不工作。改了下find-orphan-function里的查询规则,发现还得区分重载 :frowning_face: (有兴趣的赶紧贡献起来啊,哈哈)

说到底,tree-sitter只是生成了语法树,缺少语义信息,有些事儿不太适合用它来做(或者不能简单拿来用)。

我测试了elisp和python,你先用 tree-sitter-debug-mode 看看java是啥type,然后添加多余的query string就可以了

等我贡献java的哈,不过最近太忙了,而java的要完善到可用估计要花点时间(以前也没用过tree-sitter),所以没那么快。

Java的我已经加了,就是增加一条查询语句就可以了

find-orphan 主要是找那种项目过程中的临时代码,就是那种连重载的机会都没有的函数自动找出来,find-orphan 的初衷是帮助作者找到那些自己都忘记的临时代码。

1 个赞

tree-sitter 主要是通过AST来简化 token 的定义,比如:

  1. Elisp: (function_definition name: (symbol) @x)
  2. Python: (function_definition name: (identifier) @x)
  3. Java: (method_declaration name: (identifier) @x)

虽然每种语言的 type 不一样,但是只需要调整 type 的名字就可以精确匹配函数名称,如果没有AST的话,就要写一堆正则来匹配代码的不同风格。

当然语义上下文分析还是要靠 LSP Server, tree-sitter 只要在静态分析上比正则靠谱比正则性能快已经非常不错了。

是的,tree-sitter这种生成精确的AST、且能通过S表达式来查询AST的方式看起来还是很顺手的。

最近正好有这个需求哈哈

go语言是 function_declaration

(find-orphan-get-match-nodes "(function_declaration name: (identifier) @x)")

欢迎发补丁哇

2021-11-26.9721dcf.master 大佬我这里没有 remove-if-not 这个函数

已经用 cl-remove-if-not 函数替换,请更新最新版

1 个赞

想问下,Emacs 里面用 tree-sitter 会有性能问题吗?tree-sitter 的 parse 之类一次的成本时间成本大概是多少?

给你一些样本,下面单位是秒:

  1. 在一个1000行的文件中找 35 个函数的名字: 0.000795
  2. 提取当前光标处的字符串AST节点: 0.000016
  3. 提取当前光标处的复杂Sexp AST节点:0.0000041
  4. 扫描一次文件(600行Python)的Class和Function列表:0.00382

tree-sitter最简单的操作(提取当前光标AST节点)到完整扫描一次全文件所有节点的性能消耗分别从:0.016ms ~ 3.8ms

awesome-tray 昨天的版本我把全文件扫描信息加到每次鼠标移动都计算一次也不卡,今天加了一个延时(5秒种才执行一次tree-sitter代码)。

我的直观感受,只要不是一直计算的场景,如果你只是把 tree-sitter 用到用户按键触发的场景(比如 meow),耗时一般是 0.0x 毫秒,性能非常好。

4 个赞