最近踩过的坑,另外想看看大家当初是怎么从 ELisp stackoverflow 的水位到能写插件的水平?
感谢大佬分享的 GitHub - chrisdone/elisp-guide: A quick guide to Emacs Lisp programming ,对新人挺有帮助
我才知道 C-M-x
比 C-x C-e
好用。
我开始写自己的配置的时候,刚好用了新出的一批插件。为了我自己用得爽,给它们提交了不少 PR,包括 awesome-tab、awesome-pair、awesome-tray(懒猫全家桶 )、selectrum、ctrlf。通过读它们的代码,还有作者的 code review,我熟悉了很多 Emacs 内置的 API 还有 elisp 的惯用写法。另外自己也写了一些编辑文本用的命令。这一段经历起了很好的打基础作用。
大概搞了半年以后,我发现现有的 ctags 插件都不能让我满意,就开始写 Citre 了(结果 Citre 都写好了我自己的配置还没完工 )。在写的过程中我发现:
-
自己造一个工具,比和 Emacs 现有工具整合更容易。xref 已经是比较整洁的了,Emacs 的补全机制才是一团浆糊,幸好我搞 selectrum 的时候已经踩过一遍坑了。这种时候上网查查「怎样写一个 minor-mode/补全后端/imenu 后端/等等」,或者看看现有插件的实现是很有帮助的。
-
保证插件在生长到一定规模以后还可以扩展,比把功能写对更难。Citre 长到快 2000 行的时候,ctags 的开发者及时建议我把代码分层,这起到了关键作用。不然的话 Citre 不会发展到今天这个规模。
其实有一颗造轮子的心,然后干就完了,不需要想太多。
看到楼主的文章里说一开始没有搞懂「position」是什么东西,其实这个写多了就条件反射了。一个 buffer 最开始是 1,最后面是 (buffer-size)
,narrow 以后最开始是 (point-min)
,最后面是 (point-max)
,光标是 (point)
。我觉得楼主这个阶段的话,可以先写一些给自己用的编辑命令,我有几个点子供参考:
-
可不可以写一个回到行首的命令,当光标在行中间的时候回到行首,在行首的时候跳过这一行开头的空白?同样还可以写一个类似的跳到行尾的命令。
-
有时候需要删掉一串连着的空格,这个倒是有
hungry-delete
可以用,但有时候又不想按一下退格就把空格都删了,这种时候hungry-delete
就很烦。可不可以写一个命令,当光标至少有一侧是空格时,把两边的空格都干掉;当两侧都没有空格时,插入一个空格? -
Emacs 内置的 tabify 和 untabify 很好用,但没有选区的时候就不工作。可不可以写个命令包装一下,让它们在没有选取的时候操作整个 buffer?
我是通过维护[quote=“kinono, post:3, topic:17815”] Citre 长到快 2000 行的时候,ctags 的开发者及时建议我把代码分层,这起到了关键作用。 [/quote]
确实,代码量一大,没有很好的结构,后期更改太痛苦了,这是我重构 pyim 时的直观感受。。。 但没有痛苦就没有成长, 有时候很无奈啊。
@alphapapa 的这篇开发手册也值得一看: GitHub - alphapapa/emacs-package-dev-handbook: An Emacs package development handbook. Built with Emacs, by Emacs package developers, for Emacs package developers.
内容详实,除了 Emacs/Elisp 本身的各种议题之外,它还介绍了测试&打包框架类库以及其它辅助开发的工具(例如录屏等等):
- Animations / Screencasts
- Asynchronicity
- Auditing / Reviewing
- Binding
- Buffers
- Checkers / linters
- Collections (lists, vectors, hash-tables, etc.)
- Color
- Data structure
- Date / Time
- Debugging
- Destructuring
- Documentation
- Editing
- General
- Highlighting / font-locking
- Multiprocessing (generators, threads)
- Networking
- Packaging
- Pattern matching
- Processes (incl. IPC, RPC)
- Optimization
- Refactoring
- Regular expressions
- Strings
- Testing
- User interface
- Version control
- XML / HTML
另外提供了大量常用函数的性能分析对比,例如:
#+BEGIN_SRC elisp :exports both :cache yes
(let ((list (cl-loop for i from 1 to 1000
collect i)))
(bench-multi :times 100
:ensure-equal t
:forms (("(-non-nil (--map (when ..." (-non-nil
(--map (when (cl-evenp it) it) list)))
("(delq nil (--map (when ..." (delq nil
(--map (when (cl-evenp it) it) list)))
("cl-loop" (cl-loop for i in list
when (cl-evenp i)
collect i))
("-select" (-select #'cl-evenp list))
("cl-remove-if-not" (cl-remove-if-not #'cl-evenp list))
("seq-filter" (seq-filter #'cl-evenp list)))))
#+END_SRC
#+RESULTS[6b2e97c1ebead84a53fd771684cc3e155e7f6b1e]:
| Form | x faster than next | Total runtime | # of GCs | Total GC runtime |
|----------------------------+--------------------+---------------+----------+------------------|
| -select | 1.17 | 0.01540391 | 0 | 0.0 |
| cl-loop | 1.05 | 0.01808226 | 0 | 0.0 |
| seq-filter | 1.13 | 0.01891708 | 0 | 0.0 |
| (delq nil (--map (when ... | 1.15 | 0.02134727 | 0 | 0.0 |
| cl-remove-if-not | 1.18 | 0.02459478 | 0 | 0.0 |
| (-non-nil (--map (when ... | slowest | 0.02903999 | 0 | 0.0 |