Org-mode 只使用所有子 checkbox 来表示完成度?

目前的效果

- [-] Project [4/6]
  - [-] Sub Task
    - [X] Task
    - [ ] Task
  - [X] Task 1.2
    - [X] Task
    - [X] Task

但是我个人觉得只有子项目才是真正计算在进度条中的内容,而 Sub Task 并不能计算在里面。因为我认为这样获得的进度才是最真实。

所以我想能否完成这种递归查询子项目的效果?

期望的效果

- [-] Project [4/5]
  - [-] Sub Task
    - [ ] Task
    - [X] Task
      - [X] Task 1.1
      - [X] Task 1.2
  - [X] Task 1.2
    - [X] Task
    - [x] Task

我觉得虽然使用最底层子项目来做进度标识很精确,但是这个概念就是完全的树结构。

而我认为真正的项目进度是单位是不分子树结构的,也就是说一个节点包括的所有节点都是平权的,结构只是一种意识,而并不是现实结果。

所以任务单位任然是以所有子节点来算的。

据我所知,Org mode 不支持你想要的效果。要想支持的话,第一步是把你的需求提交给开发者:M-x org-submit-bug-report

另外你提到的「目前的效果」不是 Org 预设的效果,参考选项 org-checkbox-hierarchical-statistics(org) Checkboxes

哈哈,这个概念很有意思啊。想了解下,是否有出处。

按我的理解, TODO 表达的意思是待办事项,是一种行为,而我们在分解任务的时候就是将其转化为每一个可执行的步骤,因此当我们拆分到最后的时候,也只有最深层的子节点才能代表一个真正的行动。而所有这些行为构成了整个项目。

嗯,确实不是预设的。这个当前效果是我设置的结果。Org mode 自带的确实只能支持到这一步了,想请问下是否有什么样的函数能实现这个功能?

Org 只支持这两种效果,在命令 org-update-checkbox-count 中是 hard-coded 了的,理论上你可以修改这个函数,但会很复杂,不如问问 Org mode 的开发者。

一个不成熟的实现:可以利用 org-list 的 indentation 来判断不是叶的项。

条件有

  1. org-list-indent-offset 不能小于 -1 (正常情况不会设置这么极端,默认值即可);
  2. org-checkbox-hierarchical-statistics 设为 nil, 或者 headline 的 :COOKIE_DATA: 属性设为 recursive, 来开启递归;
  3. 自己定义一个变量 org-checkbox-statistics-only-leaves 并设为 t
  4. 改动函数 org-update-checkbox-count (在文件 org-list.el 中)如下
        (item (org-list-get-children item s par))
        (t (org-list-get-all-items
            (org-list-get-top-point s) s pre))))
-			(cookies (delq nil (mapcar
-					    (lambda (e)
-					      (org-list-get-checkbox e s))
-					    items))))
+			(origin-cookies (mapcar
+					 (lambda (e)
+					   (org-list-get-checkbox e s))
+					 items))
+			(cookies (remq nil origin-cookies)))
       (cl-incf c-all (length cookies))
-		   (cl-incf c-on (cl-count "[X]" cookies :test #'equal)))))))
+		   (cl-incf c-on (cl-count "[X]" cookies :test #'equal))
+		   (when org-checkbox-statistics-only-leaves
+		     (let* ((inds (mapcar (lambda (e)
+					    (org-list-get-ind e s))
+					  items))
+			    (deltas (cl-loop for x in inds
+					     for y in (cdr inds)
+					     collect (- x y)))
+			    (sub-item (cl-loop for delta  in deltas
+					       for cookie in origin-cookies
+					       count (and (< delta 0) cookie)))
+			    (sub-item-on (cl-loop for delta  in deltas
+						  for cookie in origin-cookies
+						  count (and (< delta 0)
+							     (equal cookie "[X]")))))
+		       (cl-decf c-all sub-item)
+		       (cl-decf c-on sub-item-on))))))))
    cookies-list cache)
      ;; Move to start.
      (cond (all (goto-char (point-min)))

测试 (本来录了动图,太大了传不上来)

- test [9/10]
  + task1
    1. [X] task1.1
    2. [X] task1.2
    4. [@4] [X] task1.4
       + [X] task1.4.1
       + [X] task1.4.2
  + [-] task2 [5/6]
    - [X] task2.1
    - not a task
    - [-] task2.2
      + [ ] task2.2.1
      + [X] task2.2.2
      + [X] task2.2.3
    - [X] task2.3
    - [X] task2.4
1 个赞

org-list-get-subtree 貌似可以,返回 nil 则满足条件。把 org-checkbox-hierarchical-statistics 设改成 nil,配合下面的修改,试了下上面的两个例子,能达到目的,但是如果其它地方出了问题我也不会意外。

diff --git a/lisp/org-list.el b/lisp/org-list.el
index b5c911e1b..35b2abbc5 100644
--- a/lisp/org-list.el
+++ b/lisp/org-list.el
@@ -2493,7 +2493,9 @@ With optional prefix argument ALL, do this for the whole buffer."
 			(par (org-list-parents-alist s))
 			(items
 			 (cond
-			  ((and recursivep item) (org-list-get-subtree item s))
+			  ((and recursivep item)
+			   (--remove (org-list-get-subtree it s)
+				     (org-list-get-subtree item s)))
 			  (recursivep (mapcar #'car s))
 			  (item (org-list-get-children item s par))
 			  (t (org-list-get-all-items

无论如何,这个需求用户端这里解决一点儿都不现实,还是得报告给上游。

2 个赞
** Plan [80%]
- [ ] HtDP [0% ] 
- [X] JSGP [100%] 
  - [X] Objects
  - [X] Functions
  - [X] Inheritance

存在一个小问题,此时 Plan 应该是 [75%]。

这是一个特殊的个人需求啦,回头我会将这个需求报告给上游,问问 Org-mode 的开发者是否合适。

1 个赞

的确有这个问题,我之前没试过 Heading 的情况,只试用 List。

学习了,果然还是对这部分代码不太熟悉,这个思路应该是正确的,之前没往这方面想。

就是 org-list-get-subtree 有点过 “重” 了,性能可能会有影响。 看了一下似乎 org-list-has-child-p 更适合。

应该是下一个条件,(recursivep (mapcar #'car s)) 那里也加上移除符合 org-list-has-child-p 语句。