broke down agenda commands

This commit is contained in:
petrucci4prez 2018-04-02 00:40:42 -04:00
parent a14650fc16
commit ed94b385ae
2 changed files with 296 additions and 650 deletions

457
conf.el
View File

@ -271,13 +271,6 @@
("NEXT" ("WAITING") ("CANCELLED") ("HOLD"))
("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
(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)
(setq org-tag-alist (quote ((:startgroup)
("@errand" . ?e)
("@work" . ?o)
@ -343,69 +336,6 @@
(setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags))
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
(quote ((" " "Agenda"
((agenda "" nil)
(tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")
(org-tags-match-list-sublevels nil)))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Next Tasks"))
(org-agenda-skip-function 'nd/skip-non-next-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Waiting Tasks"))
(org-agenda-skip-function 'nd/skip-non-waiting-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Held Tasks"))
(org-agenda-skip-function 'nd/skip-non-held-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Atomic Tasks"))
(org-agenda-skip-function 'nd/skip-non-atomic-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Stuck Projects")
(org-agenda-skip-function 'nd/skip-non-stuck-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Held Projects")
(org-agenda-skip-function 'nd/skip-non-held-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Waiting Projects")
(org-agenda-skip-function 'nd/skip-non-waiting-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Active Projects")
(org-agenda-skip-function 'nd/skip-non-active-projects)
(org-agenda-sorting-strategy
'(category-keep)))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Projects")
;; (org-agenda-skip-function 'nd/skip-non-projects)
;; (org-tags-match-list-sublevels 'indented)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags "-NA-REFILE/"
;; ((org-agenda-overriding-header "Tasks to Archive")
;; (org-agenda-skip-function 'nd/skip-non-archivable-tasks)
;; (org-tags-match-list-sublevels nil))))
nil))))
(defun nd/org-auto-exclude-function (tag)
"Automatic task exclusion in the agenda with / RET"
(and (cond
@ -416,34 +346,12 @@
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
(defun nd/is-todoitem-p ()
"return todo keyword if present in headline
(which defines the heading as a todoitem)
this is used to both test if a heading is a todoitem
and retrieve the keyword"
"return todo keyword if present in headline (which defines the heading as a todoitem)
this is used to both test if a heading is a todoitem and retrieving the keyword"
(let ((keyword (nth 2 (org-heading-components))))
(if (member keyword org-todo-keywords-1)
keyword)))
(defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing
;; the level immediately below (if it exists)
(let ((has-children)
(subtree-end (save-excursion (org-end-of-subtree t))))
(save-excursion
(outline-next-heading)
(while (and (not has-children)
(< (point) subtree-end))
(when (nd/is-todoitem-p)
(setq has-children t))
;; (org-forward-heading-same-level 1 t)))
(outline-next-heading)))
has-children))
(defun nd/heading-has-parent ()
"returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/is-project-p ()
"return todo keyword if heading is todoitem and has children"
(and (nd/heading-has-children) (nd/is-todoitem-p)))
@ -474,8 +382,30 @@ and retrieve the keyword"
"return keyword if task is WAITING"
(equal (nd/is-task-p) "WAITING"))
;; org-forward-heading-same-level
;; project level testing
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
(defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing
;; the level immediately below (if it exists)
(let ((has-children)
(subtree-end (save-excursion (org-end-of-subtree t))))
(save-excursion
(outline-next-heading)
(while (and (not has-children)
(< (point) subtree-end))
(when (nd/is-todoitem-p)
(setq has-children t))
;; (org-forward-heading-same-level 1 t)))
(outline-next-heading)))
has-children))
(defun nd/heading-has-parent ()
"returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/test-first-order-project ()
"tests the state of a project assuming first order.
if not first order, this function will iterate to the next project
@ -496,39 +426,45 @@ function is not meant to be called independently."
(org-forward-heading-same-level 1 t)))
found-active))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
;; project level testing
;; TODO: is there a better way to handle statuscodes like this??? (array like thingy)
(defun nd/descend-into-project ()
"returns numeric value according to state of project:
0: stuck
1: held
2: waiting
3: active
"returns statuscode according to state of project:
0: complete
10: stuck
20: held
30: waiting
40: active
Larger values have precedence over smaller (eg a NEXT
keyword will override any other WAITING or HELD task present"
This function works on an assumed order of precendence:
- we start by assuming all projects as complete (eg only DONE and CANCELLED)
- if project has any TODO (regardless of DONE or CANCELLED) it is stuck
- if project has any HOLD (regardless of DONE, CANCELLED, or TODO) it is held
- in the same manner WAITING means waiting project
- in the same manner, NEXT means active. NEXT overrides all
Using this scheme, we simply compare the magnitude of the statuscodes"
(let ((project-state 0)
(previous-point))
(save-excursion
(setq previous-point (point))
(outline-next-heading)
(while (and (< project-state 3)
(while (and (< project-state 40)
(> (point) previous-point))
(let ((keyword (nd/is-todoitem-p))
(has-children (nd/heading-has-children)))
(if keyword
(let ((cur-state
(if has-children
(cond ((equal keyword "HOLD") 1)
(cond ((equal keyword "HOLD") 20)
((equal keyword "TODO") (nd/descend-into-project))
;; NOTE: all projects are assumed to only have TODO, HOLD, CANCELLED, or DONE, hence the three possible statuscodes
(t 0))
(cond ((equal keyword "HOLD") 1)
((equal keyword "WAITING") 2)
((equal keyword "NEXT") 3)
((nd/is-scheduled-heading-p) 3)
(cond ((equal keyword "TODO") 10)
((equal keyword "HOLD") 20)
((equal keyword "WAITING") 30)
((equal keyword "NEXT") 40)
((nd/is-scheduled-heading-p) 40)
(t 0)))))
(if (> cur-state project-state)
(setq project-state cur-state)))))
@ -540,11 +476,10 @@ keyword will override any other WAITING or HELD task present"
(let ((keyword (nd/is-project-p)))
(if keyword
(cond ((member keyword nd/project-invalid-todostates) nil)
((and (equal keyword "HOLD") (= statuscode 1)) keyword)
((and (equal keyword "HOLD") (/= statuscode 1)) nil)
((and (equal keyword "HOLD") (= statuscode 20)) keyword)
((and (equal keyword "HOLD") (/= statuscode 20)) nil)
((= statuscode (nd/descend-into-project)) keyword)))))
;; task skip functions
;; NOTE: use save-restriction and widen if we ever actually use narrowing
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
@ -563,224 +498,110 @@ keyword will override any other WAITING or HELD task present"
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-stuck-projects ()
(if (not (nd/is-project-status-p 0))
(if (not (nd/is-project-status-p 10))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-projects ()
(if (not (nd/is-project-status-p 1))
(if (not (nd/is-project-status-p 20))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-projects ()
(if (not (nd/is-project-status-p 2))
(if (not (nd/is-project-status-p 30))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-active-projects ()
(if (not (nd/is-project-status-p 3))
(if (not (nd/is-project-status-p 40))
(save-excursion (or (outline-next-heading) (point-max)))))
;; (defvar nd/hide-scheduled-and-waiting-next-tasks t)
(defvar nd/agenda-project-view t)
;; (defun nd/toggle-next-task-display ()
;; (interactive)
;; (setq nd/hide-scheduled-and-waiting-next-tasks (not nd/hide-scheduled-and-waiting-next-tasks))
;; (when (equal major-mode 'org-agenda-mode)
;; (org-agenda-redo))
;; (message "%s WAITING and SCHEDULED NEXT Tasks" (if nd/hide-scheduled-and-waiting-next-tasks "Hide" "Show")))
(defun nd/toggle-project-display ()
(interactive)
(setq nd/agenda-project-view (not nd/agenda-project-view))
(when (equal major-mode 'org-agenda-mode)
(org-agenda-redo))
(message "%s project view in agenda" (if nd/agenda-project-view "Showing" "Hiding")))
;; (defun nd/skip-non-stuck-projects ()
;; "Skip trees that are not stuck projects"
;; (save-restriction
;; (widen)
;; (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
;; (if (nd/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 nd/agenda-base-task-command (header skip-fun)
;;(quote
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header header)
(org-agenda-skip-function skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep)))))
;; project test functions
;; is state
;; if project
;; if order = 1
;; return (state is true)
;; else order > 1
;; call is state (recursive)
;; else if task
;; return (state is true)
;; note: this needs to iterate through lines
;; (defun nd/is-active-project-p ()
;; "return true if project has at least one
;; NEXT/scheduled task or active subproject"
;; ;; if not a project then don't bother
;; (if (nd/is-project-p)
;; (let (((subtree-end (save-excursion (org-end-of-subtree t))))
;; (is-active))
;; (save-excursion
;; (while (and (not is-active)
;; (< (point) subtree-end))
;; (outline-heading-next)
;; (cond ((nd/is-active-task-p) (setq is-active t))
;; ((nd/is-active-project-p) (setq is-active))))))))
;; (defvar nd/agenda-task-commands
;; (quote
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Next Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-next-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Waiting Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-waiting-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Held Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-held-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Atomic Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-atomic-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))))
;; (defun nd/skip-non-stuck-projects ()
;; goto next headline
;; if project
;; if project order 1
;; if it has NEXT, WAITING, HOLD, or a scheduled task
;; then skip (return end of subtree)
;; else stuck project, return nil
;; else (order > 1)
;; descend into project (recursion)
;; skip (either an atomic task or non-todo, return next heading)
;; )
;; (defvar nd/agenda-project-commands
;; (quote
;; ((tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Stuck Projects")
;; (org-agenda-skip-function 'nd/skip-non-stuck-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Held Projects")
;; (org-agenda-skip-function 'nd/skip-non-held-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Waiting Projects")
;; (org-agenda-skip-function 'nd/skip-non-waiting-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Active
;; (org-agenda-skip-function 'nd/skip-non-active-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep)))))))
;; (defun nd/skip-non-projects ()
;; "Skip trees that are not projects"
;; ;; (nd/list-sublevels-for-projects-indented)
;; (if (save-excursion (nd/skip-non-stuck-projects))
;; (save-restriction
;; (widen)
;; (let ((subtree-end (save-excursion (org-end-of-subtree t))))
;; (cond
;; ((nd/is-project-p)
;; nil)
;; ((and (nd/is-subtask-p) (not (nd/is-atomic-p)))
;; nil)
;; (t
;; subtree-end))))
;; (save-excursion (org-end-of-subtree t))))
(defun nd/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
((nd/is-atomic-p)
nil)
(t
next-headline)))))
(defun nd/skip-project-trees-and-habits ()
"Skip trees that are projects"
(save-restriction
(widen)
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
(t
nil)))))
(defun nd/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 nd/hide-scheduled-and-waiting-next-tasks
(member "WAITING" (org-get-tags-at)))
next-headline)
((nd/is-project-p)
next-headline)
((and (nd/is-atomic-p) (not (nd/is-subtask-p)))
next-headline)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
next-headline)
;; ((org-is-habit-p)
;; subtree-end)
((and (not limit-to-project)
(nd/is-subtask-p))
subtree-end)
((and limit-to-project
(nd/is-subtask-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
((nd/is-subtask-p)
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
next-headline)
;; ((org-is-habit-p)
;; subtree-end)
((and (nd/is-subtask-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
((not (nd/is-subtask-p))
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
(t
nil)))))
;; (defun nd/skip-non-subprojects ()
;; "Skip trees that are not projects"
;; (let ((next-headline (save-excursion (outline-next-heading))))
;; (if (nd/is-subproject-p)
;; nil
;; next-headline)))
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
(quote ((" " "Agenda"
(;;(agenda "" nil)
(tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")
(org-tags-match-list-sublevels nil)))
nd/agenda-base-task-command)
;;(nd/agenda-base-task-command "Project next tasks" 'nd/skip-non-next-project-tasks))
;;(if nd/agenda-project-view nd/agenda-project-commands nd/agenda-task-commands))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Projects")
;; (org-agenda-skip-function 'nd/skip-non-projects)
;; (org-tags-match-list-sublevels 'indented)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags "-NA-REFILE/"
;; ((org-agenda-overriding-header "Tasks to Archive")
;; (org-agenda-skip-function 'nd/skip-non-archivable-tasks)
;; (org-tags-match-list-sublevels nil))))
nil))))
(use-package org-bullets
:ensure t

481
conf.org
View File

@ -417,7 +417,7 @@ vim is all about escape, not...ctrl+g???
("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
#+END_SRC
*** subtask autocomplete
#+BEGIN_SRC emacs-lisp
+BEGIN_SRC emacs-lisp
(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
@ -518,72 +518,6 @@ TODO: add meeting template as scheduled+action item thing
(setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags))
#+END_SRC
*** custom commands
#+BEGIN_SRC emacs-lisp
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
(quote ((" " "Agenda"
((agenda "" nil)
(tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")
(org-tags-match-list-sublevels nil)))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Next Tasks"))
(org-agenda-skip-function 'nd/skip-non-next-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Waiting Tasks"))
(org-agenda-skip-function 'nd/skip-non-waiting-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Project Held Tasks"))
(org-agenda-skip-function 'nd/skip-non-held-project-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header (concat "Atomic Tasks"))
(org-agenda-skip-function 'nd/skip-non-atomic-tasks)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Stuck Projects")
(org-agenda-skip-function 'nd/skip-non-stuck-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Held Projects")
(org-agenda-skip-function 'nd/skip-non-held-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Waiting Projects")
(org-agenda-skip-function 'nd/skip-non-waiting-projects)
(org-agenda-sorting-strategy
'(category-keep))))
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header "Active Projects")
(org-agenda-skip-function 'nd/skip-non-active-projects)
(org-agenda-sorting-strategy
'(category-keep)))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Projects")
;; (org-agenda-skip-function 'nd/skip-non-projects)
;; (org-tags-match-list-sublevels 'indented)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags "-NA-REFILE/"
;; ((org-agenda-overriding-header "Tasks to Archive")
;; (org-agenda-skip-function 'nd/skip-non-archivable-tasks)
;; (org-tags-match-list-sublevels nil))))
nil))))
#+END_SRC
*** auto exclusion
#+BEGIN_SRC emacs-lisp
(defun nd/org-auto-exclude-function (tag)
@ -595,48 +529,16 @@ TODO: add meeting template as scheduled+action item thing
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
#+END_SRC
*** filtering functions
some definitions:
- todoitem: heading with todo state
- task: todo heading with no todo headings beneath it
- project: todo heading with tasks or other project in subtree
- subtask: task that is part of a project
- subproject: project that is part of another project
- atomic task: task that is not part of a project
- project order: quantifies the degree of project nesting.
- First order projects have only subtasks
- Second order projects have subtasks or first order subprojects
- etc
*** task helper functions
These are the building blocks for skip functions.
#+BEGIN_SRC emacs-lisp
(defun nd/is-todoitem-p ()
"return todo keyword if present in headline
(which defines the heading as a todoitem)
this is used to both test if a heading is a todoitem
and retrieve the keyword"
"return todo keyword if present in headline (which defines the heading as a todoitem)
this is used to both test if a heading is a todoitem and retrieving the keyword"
(let ((keyword (nth 2 (org-heading-components))))
(if (member keyword org-todo-keywords-1)
keyword)))
(defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing
;; the level immediately below (if it exists)
(let ((has-children)
(subtree-end (save-excursion (org-end-of-subtree t))))
(save-excursion
(outline-next-heading)
(while (and (not has-children)
(< (point) subtree-end))
(when (nd/is-todoitem-p)
(setq has-children t))
;; (org-forward-heading-same-level 1 t)))
(outline-next-heading)))
has-children))
(defun nd/heading-has-parent ()
"returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/is-project-p ()
"return todo keyword if heading is todoitem and has children"
(and (nd/heading-has-children) (nd/is-todoitem-p)))
@ -667,8 +569,30 @@ some definitions:
"return keyword if task is WAITING"
(equal (nd/is-task-p) "WAITING"))
;; org-forward-heading-same-level
;; project level testing
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
(defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing
;; the level immediately below (if it exists)
(let ((has-children)
(subtree-end (save-excursion (org-end-of-subtree t))))
(save-excursion
(outline-next-heading)
(while (and (not has-children)
(< (point) subtree-end))
(when (nd/is-todoitem-p)
(setq has-children t))
;; (org-forward-heading-same-level 1 t)))
(outline-next-heading)))
has-children))
(defun nd/heading-has-parent ()
"returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/test-first-order-project ()
"tests the state of a project assuming first order.
if not first order, this function will iterate to the next project
@ -689,39 +613,45 @@ some definitions:
(org-forward-heading-same-level 1 t)))
found-active))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
;; project level testing
;; TODO: is there a better way to handle statuscodes like this??? (array like thingy)
(defun nd/descend-into-project ()
"returns numeric value according to state of project:
0: stuck
1: held
2: waiting
3: active
"returns statuscode according to state of project:
0: complete
10: stuck
20: held
30: waiting
40: active
Larger values have precedence over smaller (eg a NEXT
keyword will override any other WAITING or HELD task present"
This function works on an assumed order of precendence:
- we start by assuming all projects as complete (eg only DONE and CANCELLED)
- if project has any TODO (regardless of DONE or CANCELLED) it is stuck
- if project has any HOLD (regardless of DONE, CANCELLED, or TODO) it is held
- in the same manner WAITING means waiting project
- in the same manner, NEXT means active. NEXT overrides all
Using this scheme, we simply compare the magnitude of the statuscodes"
(let ((project-state 0)
(previous-point))
(save-excursion
(setq previous-point (point))
(outline-next-heading)
(while (and (< project-state 3)
(while (and (< project-state 40)
(> (point) previous-point))
(let ((keyword (nd/is-todoitem-p))
(has-children (nd/heading-has-children)))
(if keyword
(let ((cur-state
(if has-children
(cond ((equal keyword "HOLD") 1)
(cond ((equal keyword "HOLD") 20)
((equal keyword "TODO") (nd/descend-into-project))
;; NOTE: all projects are assumed to only have TODO, HOLD, CANCELLED, or DONE, hence the three possible statuscodes
(t 0))
(cond ((equal keyword "HOLD") 1)
((equal keyword "WAITING") 2)
((equal keyword "NEXT") 3)
((nd/is-scheduled-heading-p) 3)
(cond ((equal keyword "TODO") 10)
((equal keyword "HOLD") 20)
((equal keyword "WAITING") 30)
((equal keyword "NEXT") 40)
((nd/is-scheduled-heading-p) 40)
(t 0)))))
(if (> cur-state project-state)
(setq project-state cur-state)))))
@ -733,11 +663,14 @@ some definitions:
(let ((keyword (nd/is-project-p)))
(if keyword
(cond ((member keyword nd/project-invalid-todostates) nil)
((and (equal keyword "HOLD") (= statuscode 1)) keyword)
((and (equal keyword "HOLD") (/= statuscode 1)) nil)
((and (equal keyword "HOLD") (= statuscode 20)) keyword)
((and (equal keyword "HOLD") (/= statuscode 20)) nil)
((= statuscode (nd/descend-into-project)) keyword)))))
;; task skip functions
#+END_SRC
*** skip functions
These are the primary means we use to sort through tasks. Note that we could do this with
tags in the custom commands section but I find this easier to maintain and possibly faster.
#+BEGIN_SRC emacs-lisp
;; NOTE: use save-restriction and widen if we ever actually use narrowing
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
@ -756,224 +689,116 @@ some definitions:
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-stuck-projects ()
(if (not (nd/is-project-status-p 0))
(if (not (nd/is-project-status-p 10))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-projects ()
(if (not (nd/is-project-status-p 1))
(if (not (nd/is-project-status-p 20))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-projects ()
(if (not (nd/is-project-status-p 2))
(if (not (nd/is-project-status-p 30))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-active-projects ()
(if (not (nd/is-project-status-p 3))
(if (not (nd/is-project-status-p 40))
(save-excursion (or (outline-next-heading) (point-max)))))
;; (defvar nd/hide-scheduled-and-waiting-next-tasks t)
#+END_SRC
*** interactive view functions
#+BEGIN_SRC emacs-lisp
(defvar nd/agenda-project-view t)
;; (defun nd/toggle-next-task-display ()
;; (interactive)
;; (setq nd/hide-scheduled-and-waiting-next-tasks (not nd/hide-scheduled-and-waiting-next-tasks))
;; (when (equal major-mode 'org-agenda-mode)
;; (org-agenda-redo))
;; (message "%s WAITING and SCHEDULED NEXT Tasks" (if nd/hide-scheduled-and-waiting-next-tasks "Hide" "Show")))
(defun nd/toggle-project-display ()
(interactive)
(setq nd/agenda-project-view (not nd/agenda-project-view))
(when (equal major-mode 'org-agenda-mode)
(org-agenda-redo))
(message "%s project view in agenda" (if nd/agenda-project-view "Showing" "Hiding")))
;; (defun nd/skip-non-stuck-projects ()
;; "Skip trees that are not stuck projects"
;; (save-restriction
;; (widen)
;; (let ((next-headline (save-excursion (or (outline-next-heading) (point-max)))))
;; (if (nd/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 nd/agenda-base-task-command (header skip-fun)
;;(quote
(tags-todo "-NA-REFILE/!"
((org-agenda-overriding-header header)
(org-agenda-skip-function skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy
'(category-keep)))))
;; project test functions
;; is state
;; if project
;; if order = 1
;; return (state is true)
;; else order > 1
;; call is state (recursive)
;; else if task
;; return (state is true)
;; note: this needs to iterate through lines
;; (defun nd/is-active-project-p ()
;; "return true if project has at least one
;; NEXT/scheduled task or active subproject"
;; ;; if not a project then don't bother
;; (if (nd/is-project-p)
;; (let (((subtree-end (save-excursion (org-end-of-subtree t))))
;; (is-active))
;; (save-excursion
;; (while (and (not is-active)
;; (< (point) subtree-end))
;; (outline-heading-next)
;; (cond ((nd/is-active-task-p) (setq is-active t))
;; ((nd/is-active-project-p) (setq is-active))))))))
;; (defvar nd/agenda-task-commands
;; (quote
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Next Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-next-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Waiting Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-waiting-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Project Held Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-held-project-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header (concat "Atomic Tasks"))
;; (org-agenda-skip-function 'nd/skip-non-atomic-tasks)
;; (org-agenda-todo-ignore-with-date 'all)
;; (org-agenda-sorting-strategy
;; '(category-keep))))))
;; (defun nd/skip-non-stuck-projects ()
;; goto next headline
;; if project
;; if project order 1
;; if it has NEXT, WAITING, HOLD, or a scheduled task
;; then skip (return end of subtree)
;; else stuck project, return nil
;; else (order > 1)
;; descend into project (recursion)
;; skip (either an atomic task or non-todo, return next heading)
;; )
;; (defvar nd/agenda-project-commands
;; (quote
;; ((tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Stuck Projects")
;; (org-agenda-skip-function 'nd/skip-non-stuck-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Held Projects")
;; (org-agenda-skip-function 'nd/skip-non-held-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Waiting Projects")
;; (org-agenda-skip-function 'nd/skip-non-waiting-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Active
;; (org-agenda-skip-function 'nd/skip-non-active-projects)
;; (org-agenda-sorting-strategy
;; '(category-keep)))))))
#+END_SRC
*** custom commands
#+BEGIN_SRC emacs-lisp
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
(quote ((" " "Agenda"
(;;(agenda "" nil)
(tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile")
(org-tags-match-list-sublevels nil)))
nd/agenda-base-task-command)
;;(nd/agenda-base-task-command "Project next tasks" 'nd/skip-non-next-project-tasks))
;;(if nd/agenda-project-view nd/agenda-project-commands nd/agenda-task-commands))
;; (tags-todo "-NA-REFILE/!"
;; ((org-agenda-overriding-header "Projects")
;; (org-agenda-skip-function 'nd/skip-non-projects)
;; (org-tags-match-list-sublevels 'indented)
;; (org-agenda-sorting-strategy
;; '(category-keep))))
;; (tags "-NA-REFILE/"
;; ((org-agenda-overriding-header "Tasks to Archive")
;; (org-agenda-skip-function 'nd/skip-non-archivable-tasks)
;; (org-tags-match-list-sublevels nil))))
nil))))
;; (defun nd/skip-non-projects ()
;; "Skip trees that are not projects"
;; ;; (nd/list-sublevels-for-projects-indented)
;; (if (save-excursion (nd/skip-non-stuck-projects))
;; (save-restriction
;; (widen)
;; (let ((subtree-end (save-excursion (org-end-of-subtree t))))
;; (cond
;; ((nd/is-project-p)
;; nil)
;; ((and (nd/is-subtask-p) (not (nd/is-atomic-p)))
;; nil)
;; (t
;; subtree-end))))
;; (save-excursion (org-end-of-subtree t))))
(defun nd/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
((nd/is-atomic-p)
nil)
(t
next-headline)))))
(defun nd/skip-project-trees-and-habits ()
"Skip trees that are projects"
(save-restriction
(widen)
(let ((subtree-end (save-excursion (org-end-of-subtree t))))
(cond
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
(t
nil)))))
(defun nd/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 nd/hide-scheduled-and-waiting-next-tasks
(member "WAITING" (org-get-tags-at)))
next-headline)
((nd/is-project-p)
next-headline)
((and (nd/is-atomic-p) (not (nd/is-subtask-p)))
next-headline)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
next-headline)
;; ((org-is-habit-p)
;; subtree-end)
((and (not limit-to-project)
(nd/is-subtask-p))
subtree-end)
((and limit-to-project
(nd/is-subtask-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
((nd/is-subtask-p)
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
next-headline)
;; ((org-is-habit-p)
;; subtree-end)
((and (nd/is-subtask-p)
(member (org-get-todo-state) (list "NEXT")))
subtree-end)
((not (nd/is-subtask-p))
subtree-end)
(t
nil)))))
(defun nd/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
((nd/is-project-p)
subtree-end)
;; ((org-is-habit-p)
;; subtree-end)
(t
nil)))))
;; (defun nd/skip-non-subprojects ()
;; "Skip trees that are not projects"
;; (let ((next-headline (save-excursion (outline-next-heading))))
;; (if (nd/is-subproject-p)
;; nil
;; next-headline)))
#+END_SRC
** ui
*** bullets