链接子树
回到我们开头遇到的问题,为避免将节点拷贝到不同视图的节点中,我们需要定义一种超越大纲结构的关系。一种方案如下:
节点的链接子树:
* 节点A
:PROPERTIES:
:ID: xxx
:END:
* 节点B
** [[id:xxx][节点A]]
如上述 Org 片段所示,节点A 以链接的形式作为 节点B 的子树。借此定义, B 的子节点除了物理意义上存于其下的 Org entry 外,还包括这些——被链接所指的 Org entries——链接子树:
属性: #'org-T.peers.linktree, 链接子树。
#+name: 2025-08-29-20-18
#+header: :lexical t :eval yes :results silent
#+begin_src emacs-lisp
(!def 'org-T.peers.linktree
(!let (org-T.peers.linktree linktree linknode)
(!def org-T.peers.linktree
(lambda (&optional node)
(org-referent-get node linktree :no-cache t)))
(!def linktree
(lambda nil
(remq nil (org-map-entries
linknode nil 'tree))))
(!def linknode
(lambda nil
(and-let*
((title (org-entry-get nil "ITEM"))
(link (with-temp-buffer
(save-excursion (insert title))
(org-element-link-parser)))
(_ (org-element-type-p link '(link)))
(link (org-element-property :raw-link link))
(_ (format "[[%s]]" link))))))
org-T.peers.linktree))
#+end_src
在上述定义的链接子树关系的基础上,我们将先前的任务节点重新组织成如下的 Org 片段。任务节点物理上依旧存储于“项目”节点中,并以链接子树的形式,在逻辑上作为“日期”节点的子节点:
* 项目
** 项目A
*** 编码a
:LOGBOOK:
CLOCK: [2025-08-18 Mon 09:30]--[2025-08-18 Mon 11:30] => 2:00
:END:
*** 测试a
:LOGBOOK:
CLOCK: [2025-08-20 Wed 14:00]--[2025-08-20 Wed 18:00] => 4:00
:END:
** 项目B
*** 分析b
:LOGBOOK:
CLOCK: [2025-08-22 Fri 10:25]--[2025-08-22 Fri 11:50] => 1:25
:END:
*** 编码b
:LOGBOOK:
CLOCK: [2025-08-25 Mon 09:45]--[2025-08-25 Mon 11:50] => 2:05
:END:
* 日期
** 32周投入
*** [[*编码a][编码a]]
*** [[*测试a][测试a]]
*** [[*分析b][分析b]]
** 33周投入
*** [[*编码b][编码b]]
基于上述 Org 片段,我们可通过如下代码生成不同视角的报表:
#+name: 2025-08-29-20-19
#+header: :lexical t :eval no
#+begin_src emacs-lisp :var node="[[*项目]]"
(let* ((n node)
;; 一种新的 clock-sum 属性。
(clock-sum
(lambda (n)
(+
;; 除了整棵 tree 的 clock 数据外,
(org-T.clock-sum n)
;; 还包括所有 linktree 的 clock 数据。
(apply
#'+
(mapcar
#'org-T.clock-sum
(org-T.peers.linktree n))))))
(formatter
(lambda (mins)
(format "%d:%02d"
(/ mins 60) (% mins 60))))
(N (org-T.peers.tree n))
(item (seq-mapn
(lambda (i l)
(if (= l 1) i
(concat
"\\_"
(make-string (* 2 (1- l)) ? ) i
(make-string (1- l) ?|))))
(mapcar #'org-E.title N)
(mapcar #'org-E.level N)))
(clock (mapcar clock-sum N))
(clock (mapcar formatter clock)))
(seq-mapn #'list item clock))
#+end_src
项目视角:
#+call: 2025-08-29-20-19(node="[[*项目]]") :eval no
#+RESULTS:
| 项目 | 9:30 | | |
| \_ 项目A | | 6:00 | |
| \_ 编码a | | | 2:00 |
| \_ 测试a | | | 4:00 |
| \_ 项目B | | 3:30 | |
| \_ 分析b | | | 1:25 |
| \_ 编码b | | | 2:05 |
日期视角:
#+call: 2025-08-29-20-19(node="[[*日期]]") :eval no
#+RESULTS:
| 日期 | 9:30 | | |
| \_ 32周投入 | | 7:25 | |
| \_ 编码a | | | 2:00 |
| \_ 测试a | | | 4:00 |
| \_ 分析b | | | 1:25 |
| \_ 33周投入 | | 2:05 | |
| \_ 编码b | | | 2:05 |