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

481
conf.org
View File

@ -417,7 +417,7 @@ vim is all about escape, not...ctrl+g???
("DONE" ("WAITING") ("CANCELLED") ("HOLD"))))) ("DONE" ("WAITING") ("CANCELLED") ("HOLD")))))
#+END_SRC #+END_SRC
*** subtask autocomplete *** subtask autocomplete
#+BEGIN_SRC emacs-lisp +BEGIN_SRC emacs-lisp
(defun org-summary-todo (n-done n-not-done) (defun org-summary-todo (n-done n-not-done)
"Switch entry to DONE when all subentries are done, to TODO otherwise." "Switch entry to DONE when all subentries are done, to TODO otherwise."
(let (org-log-done org-log-states) ; turn off logging (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))) (setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags)) (org-agenda-align-tags))
#+END_SRC #+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 *** auto exclusion
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/org-auto-exclude-function (tag) (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) (setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
#+END_SRC #+END_SRC
*** filtering functions *** task helper functions
some definitions: These are the building blocks for skip functions.
- 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
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defun nd/is-todoitem-p () (defun nd/is-todoitem-p ()
"return todo keyword if present in headline "return todo keyword if present in headline (which defines the heading as a todoitem)
(which defines the heading as a todoitem) this is used to both test if a heading is a todoitem and retrieving the keyword"
this is used to both test if a heading is a todoitem
and retrieve the keyword"
(let ((keyword (nth 2 (org-heading-components)))) (let ((keyword (nth 2 (org-heading-components))))
(if (member keyword org-todo-keywords-1) (if (member keyword org-todo-keywords-1)
keyword))) 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 () (defun nd/is-project-p ()
"return todo keyword if heading is todoitem and has children" "return todo keyword if heading is todoitem and has children"
(and (nd/heading-has-children) (nd/is-todoitem-p))) (and (nd/heading-has-children) (nd/is-todoitem-p)))
@ -667,8 +569,30 @@ some definitions:
"return keyword if task is WAITING" "return keyword if task is WAITING"
(equal (nd/is-task-p) "WAITING")) (equal (nd/is-task-p) "WAITING"))
;; org-forward-heading-same-level (defconst nd/project-invalid-todostates
;; project level testing '("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 () (defun nd/test-first-order-project ()
"tests the state of a project assuming first order. "tests the state of a project assuming first order.
if not first order, this function will iterate to the next project 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))) (org-forward-heading-same-level 1 t)))
found-active)) found-active))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
;; project level testing ;; project level testing
;; TODO: is there a better way to handle statuscodes like this??? (array like thingy)
(defun nd/descend-into-project () (defun nd/descend-into-project ()
"returns numeric value according to state of project: "returns statuscode according to state of project:
0: stuck 0: complete
1: held 10: stuck
2: waiting 20: held
3: active 30: waiting
40: active
Larger values have precedence over smaller (eg a NEXT This function works on an assumed order of precendence:
keyword will override any other WAITING or HELD task present" - 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) (let ((project-state 0)
(previous-point)) (previous-point))
(save-excursion (save-excursion
(setq previous-point (point)) (setq previous-point (point))
(outline-next-heading) (outline-next-heading)
(while (and (< project-state 3) (while (and (< project-state 40)
(> (point) previous-point)) (> (point) previous-point))
(let ((keyword (nd/is-todoitem-p)) (let ((keyword (nd/is-todoitem-p))
(has-children (nd/heading-has-children))) (has-children (nd/heading-has-children)))
(if keyword (if keyword
(let ((cur-state (let ((cur-state
(if has-children (if has-children
(cond ((equal keyword "HOLD") 1) (cond ((equal keyword "HOLD") 20)
((equal keyword "TODO") (nd/descend-into-project)) ((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)) (t 0))
(cond ((equal keyword "HOLD") 1) (cond ((equal keyword "TODO") 10)
((equal keyword "WAITING") 2) ((equal keyword "HOLD") 20)
((equal keyword "NEXT") 3) ((equal keyword "WAITING") 30)
((nd/is-scheduled-heading-p) 3) ((equal keyword "NEXT") 40)
((nd/is-scheduled-heading-p) 40)
(t 0))))) (t 0)))))
(if (> cur-state project-state) (if (> cur-state project-state)
(setq project-state cur-state))))) (setq project-state cur-state)))))
@ -733,11 +663,14 @@ some definitions:
(let ((keyword (nd/is-project-p))) (let ((keyword (nd/is-project-p)))
(if keyword (if keyword
(cond ((member keyword nd/project-invalid-todostates) nil) (cond ((member keyword nd/project-invalid-todostates) nil)
((and (equal keyword "HOLD") (= statuscode 1)) keyword) ((and (equal keyword "HOLD") (= statuscode 20)) keyword)
((and (equal keyword "HOLD") (/= statuscode 1)) nil) ((and (equal keyword "HOLD") (/= statuscode 20)) nil)
((= statuscode (nd/descend-into-project)) keyword))))) ((= statuscode (nd/descend-into-project)) keyword)))))
#+END_SRC
;; task skip functions *** 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 ;; NOTE: use save-restriction and widen if we ever actually use narrowing
(defun nd/skip-non-atomic-tasks () (defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p)) (if (not (nd/is-atomic-task-p))
@ -756,224 +689,116 @@ some definitions:
(save-excursion (or (outline-next-heading) (point-max))))) (save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-stuck-projects () (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))))) (save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-projects () (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))))) (save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-projects () (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))))) (save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-active-projects () (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))))) (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 () (defun nd/toggle-project-display ()
;; (interactive) (interactive)
;; (setq nd/hide-scheduled-and-waiting-next-tasks (not nd/hide-scheduled-and-waiting-next-tasks)) (setq nd/agenda-project-view (not nd/agenda-project-view))
;; (when (equal major-mode 'org-agenda-mode) (when (equal major-mode 'org-agenda-mode)
;; (org-agenda-redo)) (org-agenda-redo))
;; (message "%s WAITING and SCHEDULED NEXT Tasks" (if nd/hide-scheduled-and-waiting-next-tasks "Hide" "Show"))) (message "%s project view in agenda" (if nd/agenda-project-view "Showing" "Hiding")))
;; (defun nd/skip-non-stuck-projects () (defun nd/agenda-base-task-command (header skip-fun)
;; "Skip trees that are not stuck projects" ;;(quote
;; (save-restriction (tags-todo "-NA-REFILE/!"
;; (widen) ((org-agenda-overriding-header header)
;; (let ((next-headline (save-excursion (or (outline-next-heading) (point-max))))) (org-agenda-skip-function skip-fun)
;; (if (nd/is-project-p) (org-agenda-todo-ignore-with-date 'all)
;; (let* ((subtree-end (save-excursion (org-end-of-subtree t))) (org-agenda-sorting-strategy
;; (has-next )) '(category-keep)))))
;; (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))))
;; project test functions ;; (defvar nd/agenda-task-commands
;; is state ;; (quote
;; if project ;; (tags-todo "-NA-REFILE/!"
;; if order = 1 ;; ((org-agenda-overriding-header (concat "Project Next Tasks"))
;; return (state is true) ;; (org-agenda-skip-function 'nd/skip-non-next-project-tasks)
;; else order > 1 ;; (org-agenda-todo-ignore-with-date 'all)
;; call is state (recursive) ;; (org-agenda-sorting-strategy
;; else if task ;; '(category-keep))))
;; return (state is true) ;; (tags-todo "-NA-REFILE/!"
;; note: this needs to iterate through lines ;; ((org-agenda-overriding-header (concat "Project Waiting Tasks"))
;; (defun nd/is-active-project-p () ;; (org-agenda-skip-function 'nd/skip-non-waiting-project-tasks)
;; "return true if project has at least one ;; (org-agenda-todo-ignore-with-date 'all)
;; NEXT/scheduled task or active subproject" ;; (org-agenda-sorting-strategy
;; ;; if not a project then don't bother ;; '(category-keep))))
;; (if (nd/is-project-p) ;; (tags-todo "-NA-REFILE/!"
;; (let (((subtree-end (save-excursion (org-end-of-subtree t)))) ;; ((org-agenda-overriding-header (concat "Project Held Tasks"))
;; (is-active)) ;; (org-agenda-skip-function 'nd/skip-non-held-project-tasks)
;; (save-excursion ;; (org-agenda-todo-ignore-with-date 'all)
;; (while (and (not is-active) ;; (org-agenda-sorting-strategy
;; (< (point) subtree-end)) ;; '(category-keep))))
;; (outline-heading-next) ;; (tags-todo "-NA-REFILE/!"
;; (cond ((nd/is-active-task-p) (setq is-active t)) ;; ((org-agenda-overriding-header (concat "Atomic Tasks"))
;; ((nd/is-active-project-p) (setq is-active)))))))) ;; (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 () ;; (defvar nd/agenda-project-commands
;; goto next headline ;; (quote
;; if project ;; ((tags-todo "-NA-REFILE/!"
;; if project order 1 ;; ((org-agenda-overriding-header "Stuck Projects")
;; if it has NEXT, WAITING, HOLD, or a scheduled task ;; (org-agenda-skip-function 'nd/skip-non-stuck-projects)
;; then skip (return end of subtree) ;; (org-agenda-sorting-strategy
;; else stuck project, return nil ;; '(category-keep))))
;; else (order > 1) ;; (tags-todo "-NA-REFILE/!"
;; descend into project (recursion) ;; ((org-agenda-overriding-header "Held Projects")
;; skip (either an atomic task or non-todo, return next heading) ;; (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 #+END_SRC
** ui ** ui
*** bullets *** bullets