在 org-mode 中快速插入交叉引用
本文主要介绍了 org-mode 中的各种内部链接和如何快速插入内部链接,并通过 consult 和 embark 来扩展其功能。
org-mode 内部链接简介
内部链接(Interanl Links)类似于交叉引用,是对同一文档中其他内容的引用。在 org-mode 中,内部链接的使用方法和常规链接一样,都可以通过 C-c C-c 来跳转到目标位置。
常见的内部链接对象
- Headines
- 有三种链接方式可以指向一个标题:
- CUSTOM_ID
- 通过设置
CUSTOM_ID
属性 ,可以创建形如[[#my-custom-id]]
的链接。 - ID
- 通过设置
ID
属性,可以创建形如[[id:xxxx-xxxx]]
的链接。 - fuzzy
- 可以直接在链接中使用目标标题作为链接内容,如
[[*Some section]]
或[[Some section]]
。
- Dedicated targets
- 形如
<<some target>>
的文本在 org syntax 中被称为dedicated target
,可以使用[[some target]]
的链接来指向该文本。 - Radio targets
- 形如
<<<some target>>>
的文本在 org syntax 中被称为radio target
,在常规的 org 文本中,任何与radio target
的内容相同的文本都会自动转为radio link
。 - Images and source blocks
图片和代码块(或者其他元素)如果带有
NAME
关键字,则可以通过内部链接来跳转。比如说有如下代码块:#+CAPTION: example src block #+NAME: src-1 #+begin_src emacs-lisp (message "test") #+end_src
则可以用
[[src-1]]
来引用。- Coderef
- 在 org 代码块中,可以使用形如
(ref:name)
的文本来标记当前行的代码(在使用org-edit-src-code
编辑时,可以使用 C-c l(org-store-link) 来快速创建),然后在常规文本中使用形如[[(name)]]
的链接来引用。 - Noweb references
- 这个严格来说不是内部链接的一部分,但是我经常用它,就一起说一下吧。在 org 代码块中,可以使用形如
<<CODE-BLOCK-ID>>
的文本来引用其他代码块。
内部链接跳转的优先级
从上一节我们可以看到,多个对象可以有相同的链接格式,比如说 Headlines 、Dedicated
targets 、图片和代码块都可以用 [[xxx]]
来链接。那么当这种情况真的发生时,点击
[[xxx]]
会跳转到哪里呢?这就涉及到内部链接的优先级问题了,一般来说,org-mode 会首先查找 Dedicated targets ;如果没找到,则继续找对应的 NAME
关键字;如果还没找到,则会在 Headlines 中查找。
快速插入内部链接
针对这么多的内部链接格式,org-mode 提供了一系列快捷键来帮助我们设置相关标记,但是在输入链接方面,默认好像没有什么简易的方式,基本上只能手动输入。但手动是不可能手动的,这辈子都不可能手动。我理想中的方式应该有如下效果:
- 提供一个包含所有内部链接对象的界面让我选择,选中后插入相应的内部链接。
- 每个内部链接对象要有一定的上下文(如所在标题等信息)。
- 能够对所有对象按照其类别进行分组,并可以 narrow 。
- 支持 embark 相关操作,如可以跳转到对应对象的位置等。
现有插件的情况
于是基于上述标准,我大致调研了一下我找到的两个相关插件:org-ref 和 oxr 。但是发现它们各有各的不足:
- org-ref
- org-ref 非常成熟,提供了一站式服务,但是对于我的需求来说,我只用得上 org-ref-ref-links.el 中的部分内容。另外它好像自己定义了一套链接格式,我希望使用 org-mode 默认的格式,而且它目前还不支持 consult ,所以 narrow 这一功能可能比较难实现。
- oxr
- oxr 的 UI 我非常喜欢(如图 1 所示),基本上就是我需要的样子。但是他支持的对象类型有点少,另外同样不支持 consult 。
所以我最终还是决定基于前人的基础上自己动手实现。
org-simple-ref
org-simple-ref 的代码的代码逻辑很简单,主要可以分为两个部分:收集当前 buffer 所有的引用、选择引用并插入链接。另外 org-simple-ref 还集成了 consult 和 embark ,方便过滤和跳转。
选择并插入引用
Play by play
- M-x org-simple-ref-insert-ref-link RET
- 通过以下 narrow key 来缩小范围:
- c
- coderef
- s
- 代码块
- i
- ID 或 CUSTOM_ID
- t
- radio target 或 dedicated target
- p
- 图片或其他元素
- h
- 当前标题中的引用
org-simple-ref 只有一个命令: org-simple-ref-insert-ref-link
。其界面如下图所示:
这个命令是我最近用着最爽的一个命令,也是我写这篇文章的动力。
快速跳转
插入的问题解决了,还剩下最后一个问题:如何快速跳转到目标引用上面。这在引用和其他内容非常多的事很有用。我的方案是使用embark 来实现,这样可以维持一个统一的操作入口,减少心智负担。
Play by play
- M-x org-simple-ref-insert-ref-link RET
embark-act
->embark-org-simple-ref-goto-location
总结
以上就是我个人在 org-mode 中插入交叉引用的大致流程,基本上满足了我所有的需求。如果您有更好的实现方案或相关心得,欢迎在评论区中分享。
Footnotes:
具体可以看 org manual 的 Internal Links 一节。
默认快捷键: C-c C-x p(org-set-property) 。
具体可以看 org manual 的 Literal Examples 一节。
详见 org manual 的 Noweb Reference Syntax 一节。
这两个插件我并没有深入使用过,所以如果我有说错的地方,欢迎在评论区中指出。
此部分的代码主要来自 org-ref-ref-links.el