“实战Elisp”系列旨在讲述我使用Elisp定制Emacs的经验,抛砖引玉,还请广大Emacs同好不吝赐教——如果真的有广大Emacs用户的话,哈哈哈。
在org-mode
中,一个条目(entry)可以设置多个属性(Properties)。有的属性是org-mode
内置的,有它们的特殊用途。有的属性是自定义的,用在一些插件或仅仅用于记录信息。CUSTOM_ID
属于前者,而ID
属性后者。
CUSTOM_ID
CUSTOM_ID
用于跳转。org-mode
支持丰富的外部链接格式,其中之一便是链接到指定.org
文件的指定CUSTOM_ID
的条目。
比如在一个.org
文件中有file:/Users/liutos/Dropbox/gtd/roles/writer.org::#d1bdc978-a8ce-4266-9ffa-b6041f818431
这么一段文本,那么当光标置于这个文本中时,按下快捷键C-c C-o
,Emacs便会打开文件/Users/liutos/Dropbox/gtd/roles/writer.org
,并将光标对应的条目上。
ID
ID
用于联系两个条目。一个名叫org-edna
的第三方插件能够实现两个条目间的依赖,其中一个要素便是条目的ID
属性。
比如我有一个讲解Ada语言
的任务(以一个条目的形式存在),同时也有一个学习Ada语言
的任务(另一个条目)。显然,必须先学习一番才能讲给他人听,所以第一个条目依赖于第二个条目,于是我先给学习Ada语言
的条目设置一个ID
属性,值为905fc2f4-4e28-4966-84fa-84c9e6bae96c
,然后再为讲解Ada语言
的条目中设置一个BLOCKER
属性,值为ids(905fc2f4-4e28-4966-84fa-84c9e6bae96c)
。如此一来,当讲解Ada语言
的条目出现在*Org Agenda*
中时,Emacs会将其置灰显示,代表它处于阻塞的状态,必须先处理它的依赖才行。
自动填充CUSTOM_ID和ID
建立依赖和跳转都是很常用的功能,因此我会给每一个条目都设置CUSTOM_ID
和ID
属性。为了免除每次都手动设置的麻烦,我用org-mode
的capture-template特性来实现自动填充。
capture-template是org-mode
的又一项利器,用于生成条目间共性的内容,比如行首的星号、关键字,以及写入到哪一个文件的哪一个层级中。org-mode
的官网便有一个例子
1 | (setq org-capture-templates |
在capture-template中除了可以用预置的占位符(比如上文的%U
、%i
,以及%a
),还可以调用任意的Elisp函数——这正适合填充ID
和CUSTOM_ID
这类不重复,并且有一定的格式要求的属性。ID
属性的值可以用来自于第三方插件uuidgen
的uuidgen-4
函数来生成
1 | (setq org-capture-templates |
美中不足的是,CUSTOM_ID
和ID
的值是不同的,因为uuidgen-4
每次都会返回不同的字符串。有没有什么办法能够让它们一样的呢?答案是肯定的。
一式两份
既然两次调用uuidgen-4
的结果不同,那么就将第一次调用后的结果保存起来,然后重复使用即可。思路很简单,实现代码也很直白
1 | (let (lt-org-capture--uuid) |
capture-template也是水到渠成的
1 | (setq org-capture-templates |
后记
在上面的函数定义中,我试图利用词法作用域特性,使得lt-org-capture--uuid
只能被lt-org-capture-uuidgen
和lt-org-capture-uuidclr
读写。遗憾的是,Elisp并不支持词法作用域,lt-org-capture--uuid
实际上是一个全局变量——完全可以用C-h v
来审视它。
全文完。