确切地说,你不需要在IDE里面编写或者阅读代码。
IDE用于Render资源文件比较合适,但处理文本,并不划算。
这的文本文件,包括源代码,配置文件,文档等非二进制文件。
先说说IDE带的便利:
- 函数或者变量的自动补全
- 函数或者类定义的跳转
但这些便利,是有代价的。
IDE需要不断地扫描项目中的源码,这是一个词法与语法分析的过程,与编译过程类似,只是不产生机器码而已。
这是一个非常耗时的过程,使用过Android Studio或者Eclipse的同学应该体验深刻,项目启动时实在太慢了。
启动之后,文件已经有修改,特别是Git分支切换或者版本回滚时,整个机器卡壳几十秒钟是时常的事。
而且IDE还要求文件是可以编译的,如果项目外文件或者项目文件不完整,还不能实现这两个功能。
插一个真事,有一次搞ROS开发,ROS包基本都是Cpp与Python,关键是ROS没有IDE,而且开源的项目文件非常多,有几个同事就瞎眼了。
经常找不到代码在哪里,时不时要问我,哪个功能在哪里,就算知道代码也找不到文件。这就是长期依赖IDE的后果。
所以,为这样两个功能,把机器搞得龟速,值得吗?
解决方案
这个问题,要权衡利弊,以退为进 – 放弃IDE,停止这种耗时扫描
对付文本文件,直接可以采用蛮力搜索。
具体地说,想找某个符号定义,直接在整个目录中搜索。对于库中的符号,第一次手输,之后使用字符串补全。
注意,这里对编程语言,项目能不能编译,是没有要求的。
看似暴力,但直接了当,逻辑简单,绿色无污染。
因为单个文本文件,很少会超过10M, 而整个项目的所有文本文件累计很少会超过50M。
grep整个目录通常就几秒的事情,而且大部分情况是一瞬间的事。
这样做的结果是:
- 不再依赖于任何IDE
- 不再依赖于项目管理
- 不再受限于编程语言
这个思想不限于Emacs,我这前使用Vim也是这样干的。
不管Emacs还是Vim都是跨平台的,意味着你从Windows到Unix甚至Mac都是如履平地的。
有什么比这个更加有吸引力?
Vim可以使用Quickfix实现同样的功能。
这里只介绍Emacs的方案。
grep的关键一点是,可以反复地对结果进行筛选,这个非常非常重要。
想像一下grep一次给你1000条件记录,有多大意义?
但如果可以再次在这个1000中筛选,那就非常不一样。
简单反复筛选可以做到万里挑一!
举例
按这个思想,在QMK项目中,找程序入口,
这是一个相当规模的开源项目, 除去lib下的文件都有30000多个,这是wc统计的结果:
think@DESKTOP-70MSAQ1 MINGW64 /f/AA-MyWork/QMK/keychron/qmk_firmware (mykeyboard-v1)
$ git ls-files | wc -l
37042
从三万个文件中找出一个函数,并不容易:
-
搜索main, 得到7000多条记录
-
搜索.c以过滤非C文件:
-
搜索void int, 原因main函数的参数或返回值无非这两个
-
搜索全字匹配main:
至此,只有不到70项,可以肉眼找到程序入口了。
这个过程中的 所有buffer都是保留 ,意味着如果我第二步筛选的条件错了,还可以回到第一步结果中搜索,直到你满意。
这是包括Vim在内的很多软件无法做到的,但这个在试错过程中,又非常重要。
具体实现
代码非常简单,主要还是调用Emacs的grep与occur命令,再切换到结果窗口. 开头几行只为了保存历史。
(global-set-key (kbd (concat custom-user-prefix-key "/")) 'ich/grep)
(global-set-key (kbd (concat custom-user-prefix-key "l")) 'ich/occur)
(defun ich/grep ( pattern directory )
(interactive
(list
(custom/select-a-pattern "Please input a pattern (grep): ")
(custom/select-a-directory "Please select a directory (grep): ")
) )
(custom/search-history-add pattern)
(custom/save-search-history)
(setq old-directory default-directory)
(cd directory)
(grep (concat "rg . -n -H --no-heading -i -e " pattern))
(cd old-directory)
(setq find nil)
(setq i 0)
(while (and (not find) (< i (length (window-list))))
(if (equal "*grep*" (buffer-name))
(setq find t)
(other-window 1))
(setq i (1+ i)))
(if find
(quit-window))
(switch-to-buffer "*grep*"))
(defun ich/occur ( pattern )
(interactive
(list
(custom/select-a-pattern "Please input a pattern (occur): ")))
(custom/search-history-add pattern)
(custom/save-search-history)
(occur pattern)
(setq find nil)
(setq i 0)
(while (and (not find) (< i (length (window-list))))
(if (equal "*Occur*" (buffer-name))
(setq find t)
(other-window 1))
(setq i (1+ i)))
(if find
(quit-window))
(switch-to-buffer "*Occur*"))