本节主要介绍作者GTD流程,都是实操性很强的流程,理解流程能够更深入理解org-mode任务管理精髓。
1 GTD 相关
我每天都是截止日期/计划任务驱动的。
每天,第一件事,我会打开agenda视图然后从中选择合适任务来处理,详情参看我是如何确定下一个任务章节。
1.1 每周审查流程
每周第一天(通常周一)我会完成每周审查。 我会像这样保留一个列表来提醒我哪些需要完成。
为保证agenda够快,我设置如下参数
(setq org-agenda-span 'day)
因此默认只有今天的任务才会显示出来。只有每周评审时候我才需要周视图,这使得我的agenda 生成的非常快。
我有个重复任务,可以保证我的每周评审方便处理。每周一会弹出提醒。但是这周评审 是周二完成的因为周一放假。
* NEXT Weekly Review [0/6]
SCHEDULED: <2009-05-18 Mon ++1w>
:LOGBOOK:...
:PROPERTIES:...
What to review:
- [ ] Check follow-up folder
- [ ] Review weekly agenda =F12 a w //=
- [ ] Check clocking data for past week =v c=
- [ ] Review clock report for past week =R=
- Check where we spent time (too much or too little) and rectify this week
- [ ] Look at entire agenda for today =F12 SPC=
- [ ] Review projects =F12 SPC //= and =V= repeatedly to view each project
- start work
- daily agenda first - knock off items
- then work on NEXT tasks
第一个[]条目用来跟踪文件夹,这项任务使得我能从浩如烟海的纸质文档抽出身来-那些我 需要处理但是不是特别紧急的任务。例如收到的大量邮件情况,我暂时不想要处理。 我只要将它们放入我的 Follow-up
文件夹接下来就可以不用管了,直到每周评审时。
我会过一遍这个文件夹中任务,选出那些需要处理的任务。之后这些任务将会移到 org-mode
文件中。我会对任务设置计划,这样任务后续就会显示到agenda视图中,接下来只要根据 agenda任务列表来处理任务即可。
这个流程非常适合我,对于其他人可能需要调整。
1.2 项目定义以及找出无法推进的项目
我使用一个简单的项目定义来标记任务为一个项目。这不需要额外花经历处理。任何任务 只要有个带todo关键字的子任务,那么这个任务就是项目。
项目中如果没有任意子任务包含 NEXT
关键字,那么这个任务就是无法推进的项目。
org agenda中有单独视图来显示无法推进项目列表。当有受阻项目显示在我的agenda视图 我会将其设置为 NEXT
任务,这样就会保证无法推进列表是空的。同时可以保证项目 继续进行。
我通过如下设置禁用org-mode无法推进项目的agenda视图。
(setq org-stuck-projects (quote ("" nil nil "")))
这可以避免org-mode显示我通过 F12 #
显示的项目。我的定制化受阻项目视图 是我的agenda视图的一部分,可以通过 F12 SPC
来查看。
项目中有可能包含子项目-子项目也有可能无法推进。子项目无法推进时,也是显示在无法 推进项目列表中,因此我可以创建 NEXT
任务推进项目继续进行。
下面的例子中 Stuck Project A
是无法推进项目,因为它子任务都不在 NEXT
状态。
Project C
却不是,因为它包含 NEXT
状态任务 SubTask G
以及 = Task I= . Stuck Sub Project D
也是无法推进项目,因为 SubTask E
并没有 NEXT
状态 也没有其他可以处理的任务。
* Category
** TODO Stuck Project A
*** TODO Task B
** TODO Project C
*** TODO Stuck Sub Project D
**** TODO SubTask E
*** TODO Sub Project F
**** NEXT SubTask G
**** TODO SubTask H
*** NEXT Task I
*** TODO Task J
所有的无法推进项目以及子项目将会一直显示在无法推进列表,直到新建或者修改既有任务 状态到 NEXT
。 如果项目有任务处在 WAITING
状态,等待某些条件得到 满足。 这种情况,我会将他们一直保持在无法推进项目列表,去做其他事情,待到等待条件满足 就可以将项目从无法推进项目列表移除。无法推进项目会非常醒目,能够及时提醒我, 当我查看agenda 视图时,看到有无法推进项目,我就会一直跟踪等待任务是否完成。
下面是一些辅助函数来帮我判定任务是否是项目并用在agenda视图中。
(defun bh/is-project-p ()
"Any task with a todo keyword subtask"
(save-restriction
(widen)
(let ((has-subtask)
(subtree-end (save-excursion (org-end-of-subtree t)))
(is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
(save-excursion
(forward-line 1)
(while (and (not has-subtask)
(< (point) subtree-end)
(re-search-forward "^\*+ " subtree-end t))
(when (member (org-get-todo-state) org-todo-keywords-1)
(setq has-subtask t))))
(and is-a-task has-subtask))))
(defun bh/is-project-subtree-p ()
"Any task with a todo keyword that is in a project subtree.
Callers of this function already widen the buffer view."
(let ((task (save-excursion (org-back-to-heading 'invisible-ok)
(point))))
(save-excursion
(bh/find-project-task)
(if (equal (point) task)
nil
t))))
(defun bh/is-task-p ()
"Any task with a todo keyword and no subtask"
(save-restriction
(widen)
(let ((has-subtask)
(subtree-end (save-excursion (org-end-of-subtree t)))
(is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
(save-excursion
(forward-line 1)
(while (and (not has-subtask)
(< (point) subtree-end)
(re-search-forward "^\*+ " subtree-end t))
(when (member (org-get-todo-state) org-todo-keywords-1)
(setq has-subtask t))))
(and is-a-task (not has-subtask)))))
(defun bh/is-subproject-p ()
"Any task which is a subtask of another project"
(let ((is-subproject)
(is-a-task (member (nth 2 (org-heading-components)) org-todo-keywords-1)))
(save-excursion
(while (and (not is-subproject) (org-up-heading-safe))
(when (member (nth 2 (org-heading-components)) org-todo-keywords-1)
(setq is-subproject t))))
(and is-a-task is-subproject)))
(defun bh/list-sublevels-for-projects-indented ()
"Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
This is normally used by skipping functions where this variable is already local to the agenda."
(if (marker-buffer org-agenda-restrict-begin)
(setq org-tags-match-list-sublevels 'indented)
(setq org-tags-match-list-sublevels nil))
nil)
(defun bh/list-sublevels-for-projects ()
"Set org-tags-match-list-sublevels so when restricted to a subtree we list all subtasks.
This is normally used by skipping functions where this variable is already local to the agenda."
(if (marker-buffer org-agenda-restrict-begin)
(setq org-tags-match-list-sublevels t)
(setq org-tags-match-list-sublevels nil))
nil)
(defvar bh/hide-scheduled-and-waiting-next-tasks t)
(defun bh/toggle-next-task-display ()
(interactive)
(setq bh/hide-scheduled-and-waiting-next-tasks (not bh/hide-scheduled-and-waiting-next-tasks))
(when (equal major-mode 'org-agenda-mode)
(org-agenda-redo))
(message "%s WAITING and SCHEDULED NEXT Tasks" (if bh/hide-scheduled-and-waiting-next-tasks "Hide" "Show")))
(defun bh/skip-stuck-projects ()
"Skip trees that are not stuck projects"
(save-restriction
(widen)
(let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(if (bh/is-project-p)
(let* ((subtree-end (save-excursion (org-end-of-subtree t)))
(has-next ))
(save-excursion
(forward-line 1)
(while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t))
(unless (member "WAITING" (org-get-tags-at))
(setq has-next t))))
(if has-next
nil
next-headline)) ; a stuck project, has subtasks but no next task
nil))))
(defun bh/skip-non-stuck-projects ()
"Skip trees that are not stuck projects"
;; (bh/list-sublevels-for-projects-indented)
(save-restriction
(widen)
(let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(if (bh/is-project-p)
(let* ((subtree-end (save-excursion (org-end-of-subtree t)))
(has-next ))
(save-excursion
(forward-line 1)
(while (and (not has-next) (< (point) subtree-end) (re-search-forward "^\\*+ NEXT " subtree-end t))
(unless (member "WAITING" (org-get-tags-at))
(setq has-next t))))
(if has-next
next-headline
nil)) ; a stuck project, has subtasks but no next task
next-headline))))
(defun bh/skip-non-projects ()
"Skip trees that are not projects"
;; (bh/list-sublevels-for-projects-indented)
(if (save-excursion (bh/skip-non-stuck-projects))
(save-restriction
(widen)
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((bh/is-project-p)
nil)
((and (bh/is-project-subtree-p) (not (bh/is-task-p)))
nil)
(t
subtree-end))))
(save-excursion (org-end-of-subtree t))))
(defun bh/skip-non-tasks ()
"Show non-project tasks.
Skip project and sub-project tasks, habits, and project related tasks."
(save-restriction
(widen)
(let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(cond
((bh/is-task-p)
nil)
(t
next-headline)))))
(defun bh/skip-project-trees-and-habits ()
"Skip trees that are projects"
(save-restriction
(widen)
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((bh/is-project-p)
subtree-end)
((org-is-habit-p)
subtree-end)
(t
nil)))))
(defun bh/skip-projects-and-habits-and-single-tasks ()
"Skip trees that are projects, tasks that are habits, single non-project tasks"
(save-restriction
(widen)
(let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(cond
((org-is-habit-p)
next-headline)
((and bh/hide-scheduled-and-waiting-next-tasks
(member "WAITING" (org-get-tags-at)))
next-headline)
((bh/is-project-p)
next-headline)
((and (bh/is-task-p) (not (bh/is-project-subtree-p)))
next-headline)
(t
nil)))))
(defun bh/skip-project-tasks-maybe ()
"Show tasks related to the current restriction.
When restricted to a project, skip project and sub project tasks, habits, NEXT tasks, and loose tasks.
When not restricted, skip project and sub-project tasks, habits, and project related tasks."
(save-restriction
(widen)
(let* ((subtree-end (save-excursion (org-end-of-subtree t)))
(next-headline (save-excursion (or (outline-next-heading) (point-max))))
(limit-to-project (marker-buffer org-agenda-restrict-begin)))
(cond
((bh/is-project-p)
next-headline)
((org-is-habit-p)
subtree-end)
((and (not limit-to-project)
(bh/is-project-subtree-p))
subtree-end)
((and limit-to-project
(bh/is-project-subtree-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
(t
nil)))))
(defun bh/skip-project-tasks ()
"Show non-project tasks.
Skip project and sub-project tasks, habits, and project related tasks."
(save-restriction
(widen)
(let* ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((bh/is-project-p)
subtree-end)
((org-is-habit-p)
subtree-end)
((bh/is-project-subtree-p)
subtree-end)
(t
nil)))))
(defun bh/skip-non-project-tasks ()
"Show project tasks.
Skip project and sub-project tasks, habits, and loose non-project tasks."
(save-restriction
(widen)
(let* ((subtree-end (save-excursion (org-end-of-subtree t)))
(next-headline (save-excursion (or (outline-next-heading) (point-max)))))
(cond
((bh/is-project-p)
next-headline)
((org-is-habit-p)
subtree-end)
((and (bh/is-project-subtree-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
((not (bh/is-project-subtree-p))
subtree-end)
(t
nil)))))
(defun bh/skip-projects-and-habits ()
"Skip trees that are projects and tasks that are habits"
(save-restriction
(widen)
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((bh/is-project-p)
subtree-end)
((org-is-habit-p)
subtree-end)
(t
nil)))))
(defun bh/skip-non-subprojects ()
"Skip trees that are not projects"
(let ((next-headline (save-excursion (outline-next-heading))))
(if (bh/is-subproject-p)
nil
next-headline)))