如何在 agenda 任务视图中实现 “隐藏所有的父任务” / “仅显示最后一级的子任务” ?

我是一个刚接触 emacs 的新手,接触目的主要是为了实现任务管理。

现在的问题是:

我在进行任务管理的过程中,尽量让每个任务都有自己的 SCHEDULEDDEADLINE ,即使它们彼此互为父子任务。因此,许多父子任务都会带着不同的 SCHEDULEDDEADLINE 同时出现在 agenda 任务视图中。

有时,当我想完成其中一个任务时,我会发现它是一个父任务,然后我不得不找到它的最后一级子任务,然后再完成这些处于最后一级的子任务。父任务在任务视图中的出现并不能让我直接完成它,只会让我感到混乱。

所以,我想让任务视图只显示处于最后一级的子任务,它们是树枝的末端,是迷宫的外部,是积木的第一层,我想直接看到并处理它们。

我了解到 emacs 是非常自由的软件,在 Google 一番过后,我没有找到已有的解决方案,所以我在此寻求帮助。

期望各位大佬能随意丢出任何可能有用的信息,从一个链接到已有的解决方案,都提前在此表示感谢!

org-agenda-skip-function 应该可以,如果方便的话最好提供一个简单的 org 示例文本。

这是org 示例文本

* TODO task1
** TODO task1.1
*** TODO task1.1.1
*** TODO task1.1.2
** TODO task1.2
* TODO task2
** TODO task2.1
** TODO task2.2

理想效果是,只显示
TODO task1.1.1
TODO task1.1.2
TODO task1.2
TODO task2.1
TODO task2.2
这些都是最后一级的子任务

主要是现在相关知识不足,不知道在 org-mode 中该怎么识别哪些任务是最后一级的子任务,也就是如何过滤筛选。

大佬说的org-agenda-skip-function我去了解一下,如果有更详细的信息,欢迎大佬发出来,感谢

你对于任务的划分有点问题,应当是所有子任务全部完成后,父任务应当自动被标记为完成状态。

没有必要为每个任务设置 schedule 和 deadline.

另外可以看看 GitHub - Trevoke/org-gtd.el: A package for using GTD with org-mode 这个包,我认为是较好实现了 David Allen 的 GTD 流程的包。

1 个赞

大佬你可以当作,我想得到一个只显示最后一级子任务的视图,就算是只给子任务设定 schedule 和 deadline ,有一个只显示最后一级子任务的视图也是很方便的,我可以看到最后一级子任务都有哪些,是否有哪些忘记设置schedule 和 deadline 了,这是一个自动化的过程。

谢谢大佬推荐的包,我会去尝试用用,不过 “只显示最后一级子任务的视图” 我还是想要的。

(defun my/skip-parent-todo-entries ()
  "Skip TODOs that are not the last level."
  (if (save-excursion (org-goto-first-child))
      (or (outline-next-heading) (point-max))
    (if (org-entry-is-todo-p)
        nil
      (org-end-of-subtree t))))

(setq org-agenda-custom-commands '(("t" "Show last level TODOs"
                                    ((todo ""
                                           ((org-agenda-skip-function #'my/skip-parent-todo-entries)))))))

你可以试试,应该没什么问题。

这里用了 org-agenda-custom-commands 相关用法可以看 Custom Agenda Views (The Org Manual)

1 个赞

感谢大佬!很好用,完美符合预期效果

给其他有需要的人:搭配 [%] 和 以下配置 体验更好

;; 强制TODO的状态切换依赖(父任务必须等子任务完成才能完成)
(setq org-enforce-todo-dependencies t)

;; 设置父任务自动完成,当子任务全部完成
(defun org-summary-todo (n-done n-not-done)
  "Switch entry to DONE when all subentries are done, to TODO otherwise."
  (let (org-log-done org-log-states) ; turn off logging
    (org-todo (if (= n-not-done 0) "DONE" "TODO"))))
(add-hook 'org-after-todo-statistics-hook 'org-summary-todo)

再次感谢!

org-gtd 就可以只显示一个project 下的第一个子任务,标记完成后自动显示下一个子任务,直到所有子任务完成。整个项目从org-agenda 中就不显示了。

哈哈,这样感觉是我想法在另一个方向的完善版本,都是专注于最外层的子任务。

感谢大佬,我都会试试看的。

大佬我又遇到个小问题,还是与这个视图有关。

我想把org-goto-first-child替换成org-goto-first-todo-child,当然此处的org-goto-first-todo-child很大可能是不存在的,我想自定义出来这个函数,想看看替换后是什么效果。或者说,我预想了一个效果,想自定义这个函数来实现。

这是示例文本

* TODO 测试1
** TODO 测试1.1
*** DONE 测试1.1.1
CLOSED: [2024-02-25 Sun 08:34]
*** DONE 测试1.1.2
CLOSED: [2024-02-25 Sun 08:34]
** TODO 测试1.2
*** TODO 测试1.2.1
*** DONE 测试1.2.2
CLOSED: [2024-02-25 Sun 08:46]

我想显示出TODO 测试1.1,虽然这个任务已经没有ToDo子任务了,但它本身有时会是一个长期的ToDo父任务,会时不时往下面添加ToDo子任务,所以它不会被完成,并且我希望它在没有子任务时显示在任务视图中。

我预想了这个思路,不过对语法不太熟悉,短时间查不到直接相关的内容,所以希望大佬提点一下。

那你可以直接抄 org-goto-first-child ,中间修改下正则就好。

这是我现在满意的配置

;; 强制TODO的状态切换依赖(父任务必须等子任务完成才能完成)
(setq org-enforce-todo-dependencies t)

(defun my/org-goto-first-todo-child (&optional element)
  "Goto the first child with a TODO status, even if it is invisible.
Return t when a child was found.  Otherwise don't move point and
return nil."
  (if (org-element--cache-active-p)
      (let ((heading (org-element-lineage
                      (or element (org-element-at-point))
                      '(headline inlinetask org-data)
                      t)))
        (when heading
          (unless (or (eq 'inlinetask (org-element-type heading))
                      (not (org-element-property :contents-begin heading)))
            (let ((pos (point)))
              (goto-char (org-element-property :contents-begin heading))
              (if (re-search-forward
                   (concat org-outline-regexp-bol "\\(TODO\\|SCH\\|DOING\\|WAIT\\)")
                   (org-element-property :end heading)
                   t)
                  (progn (goto-char (match-beginning 0)) t)
                (goto-char pos) nil)))))
    (let (level (pos (point)) (re (concat org-outline-regexp-bol "\\(TODO\\|SCH\\|DOING\\|WAIT\\)")))
      (when (org-back-to-heading-or-point-min t)
        (setq level (org-outline-level))
        (forward-char 1)
        (if (and (re-search-forward re nil t) (> (org-outline-level) level))
	    (progn (goto-char (match-beginning 0)) t)
	  (goto-char pos) nil)))))

(defun my/skip-parent-todo-entries ()
  "Skip TODOs that are not the last level."
  (if (save-excursion (my/org-goto-first-todo-child))
      (or (outline-next-heading) (point-max))
    (if (org-entry-is-todo-p)
        nil
      (org-end-of-subtree t))))

(setq org-agenda-custom-commands
      '(("q" "Agenda for the next 31 days - Show last level TODOs"
         ((agenda ""
		  ((org-agenda-start-day "-6d")
                      (org-agenda-span 37)
                      (org-agenda-show-all-dates nil)
                      (org-agenda-skip-function #'my/skip-parent-todo-entries)))))
        ("w" "Show last level TODOs and sort by scheduled-up"
         ((todo ""
                ((org-agenda-skip-function #'my/skip-parent-todo-entries)
                 (org-agenda-sorting-strategy '(scheduled-up))))))))

不再使用"自动完成父任务",因为喜欢手动标注DONE,不喜欢任务不经过我同意就自己变成DONE。使用过程中,发现"自动完成父任务"的配置需要每一个父子任务都加上[/][%]才能自动更新状态,这一点需要注意。

配置中的\\(TODO\\|SCH\\|DOING\\|WAIT\\)是我个人的TODO类任务状态,可根据自己的实际情况自行调整。

感谢 @VagrantJoker 大佬提点,让我能找到解决问题的正确思路。非常感谢!