added error detection...untested

This commit is contained in:
petrucci4prez 2018-04-08 21:54:20 -04:00
parent 241197b670
commit 518a18fb40
2 changed files with 293 additions and 226 deletions

242
conf.el
View File

@ -230,7 +230,7 @@
(load "ess-site") (load "ess-site")
(setq ess-history-file "session.Rhistory") (setq ess-history-file "session.Rhistory")
(setq ess-history-directory (setq ess-history-directory
(substitute-in-file-name "${XDG_CONFIG_HOME}/r/")) (substitute-in-file-name "${XDG_CONFIG_HOME}/r/"))
(setq org-log-done t) (setq org-log-done t)
(setq org-src-window-setup 'current-window) (setq org-src-window-setup 'current-window)
@ -317,27 +317,93 @@
(setq org-agenda-dim-blocked-tasks nil) (setq org-agenda-dim-blocked-tasks nil)
(setq org-agenda-compact-blocks t) (setq org-agenda-compact-blocks t)
(evil-define-key 'motion org-agenda-mode-map "T" 'nd/toggle-project-toplevel-display) (setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
`(("t"
"Task view"
((agenda "" nil)
,(macroexpand '(nd/agenda-base-task-command "Next Project" 'nd/skip-non-next-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Waiting Project" 'nd/skip-non-waiting-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Atomic" 'nd/skip-non-atomic-tasks))
,(macroexpand '(nd/agenda-base-task-command "Held Project" 'nd/skip-non-held-project-tasks))))
("o"
"Project Overview"
(,(macroexpand '(nd/agenda-base-project-command "Stuck" 10))
,(macroexpand '(nd/agenda-base-project-command "Waiting" 20))
,(macroexpand '(nd/agenda-base-project-command "Active" 40))
,(macroexpand '(nd/agenda-base-project-command "Held" 30))))
("r"
"Refile and errors"
;; TODO add error detection here
((tags "REFILE" ((org-agenda-overriding-header "Tasks to Refile")) (org-tags-match-list-sublevels nil))
,(macroexpand '(nd/agenda-base-task-command "Discontinous Project" 'nd/skip-non-discontinuous-project-tasks))
,(macroexpand '(nd/agenda-base-project-command "Invalid" 50))))))
(setq org-agenda-span 'day) (defvar nd/agenda-limit-project-toplevel t
(setq org-agenda-time-grid (quote ((daily today remove-match) "used to filter projects by all levels or top-level only")
#("----------------" 0 16 (org-heading t))
(0900 1100 1300 1500 1700))))
(add-hook 'org-finalize-agenda-hook 'place-agenda-tags) (defun nd/toggle-project-toplevel-display ()
(defun place-agenda-tags () (interactive)
"Put the agenda tags by the right border of the agenda window." (setq nd/agenda-limit-project-toplevel (not nd/agenda-limit-project-toplevel))
(setq org-agenda-tags-column (- 4 (window-width))) (when (equal major-mode 'org-agenda-mode)
(org-agenda-align-tags)) (org-agenda-redo))
(message "Showing %s project view in agenda" (if nd/agenda-limit-project-toplevel "toplevel" "complete")))
(defun nd/org-auto-exclude-function (tag) (defmacro nd/agenda-base-task-command (keyword skip-fun)
"Automatic task exclusion in the agenda with / RET" "shorter syntax to define task agenda commands"
(and (cond `(tags-todo
((string= tag "hold") "-NA-REFILE/!"
t)) ((org-agenda-overriding-header (concat ,keyword " Tasks"))
(concat "-" tag))) (org-agenda-skip-function ,skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy '(category-keep)))))
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function) (defmacro nd/agenda-base-project-command (keyword statuscode)
"shorter syntax to define project agenda commands"
`(tags-todo
"-NA-REFILE-ATOMIC/!"
((org-agenda-overriding-header (concat
(and nd/agenda-limit-project-toplevel "Toplevel ")
,keyword
" Projects"))
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
'(nd/skip-subprojects-without-statuscode ,statuscode)
'(nd/skip-projects-without-statuscode ,statuscode)))
(org-agenda-sorting-strategy '(category-keep)))))
;; NOTE: use save-restriction and widen if we ever actually use narrowing
;; tasks
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-next-project-tasks ()
(if (not (equal (nd/is-project-task-p) "NEXT"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-project-tasks ()
(if (not (equal (nd/is-project-task-p) "WAITING"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-project-tasks ()
(if (not (equal (nd/is-project-task-p) "HOLD"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-discontinous-project-tasks ()
(if (not (nd/is-discontinous-project-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
;; projects
;; TODO skip entire subtree if we don't need to evaluate anything inside
;; otherwise (for example) a held project will still have it's subtasks show up
(defun nd/skip-projects-without-statuscode (statuscode)
(if (not (nd/is-project-status-p statuscode))
(save-excursion (or (outline-next-heading) (point-max)))))
;; top-level projects
(defun nd/skip-subprojects-without-statuscode (statuscode)
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/is-todoitem-p () (defun nd/is-todoitem-p ()
"return todo keyword if present in headline (which defines the heading as a todoitem) "return todo keyword if present in headline (which defines the heading as a todoitem)
@ -376,10 +442,6 @@ this is used to both test if a heading is a todoitem and retrieving 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"))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
(defun nd/heading-has-children () (defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree" "returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing ;; TODO make this more efficient (and accurate) by only testing
@ -400,6 +462,19 @@ this is used to both test if a heading is a todoitem and retrieving the keyword"
"returns parent keyword if heading is in the immediate subtree of a todoitem" "returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p)))) (save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/is-discontinuous-project-task-p ()
"detects todoitems that are children of non-todoitems
that in turn are children of todoitems (discontinous project)"
(let ((has-todoitem-parent)
(has-non-todoitem-parent))
(save-excursion
(while (and (org-up-heading-safe)
has-todoitem-parent)
(if (nd/is-todoitem-p)
(setq has-todoitem-parent t)
(setq has-non-todoitem-parent t))))
(and has-todoitem-parent has-non-todoitem-parent)))
(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
@ -420,6 +495,10 @@ 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) ;; TODO: is there a better way to handle statuscodes like this??? (array like thingy)
(defun nd/descend-into-project () (defun nd/descend-into-project ()
@ -436,7 +515,9 @@ This function works on an assumed order of precendence:
- if project has any TODO (regardless of DONE or CANCELLED) it is stuck - 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 - 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 WAITING means waiting project
- in the same manner, NEXT means active. NEXT overrides all - in the same manner, NEXT or scheduled means active.
- can also detect errors which override all
- anything higher than active breaks the recursion/tree walk
Using this scheme, we simply compare the magnitude of the statuscodes" Using this scheme, we simply compare the magnitude of the statuscodes"
(let ((project-state 0) (let ((project-state 0)
@ -451,15 +532,23 @@ Using this scheme, we simply compare the magnitude of the statuscodes"
(if keyword (if keyword
(let ((cur-state (let ((cur-state
(if has-children (if has-children
(cond ((equal keyword "HOLD") 20) (cond ((member keyword nd/project-invalid-todostates) 50)
((equal keyword "TODO") (nd/descend-into-project)) ((nd/is-scheduled-heading-p) 50)
;; NOTE: all projects are assumed to only have TODO, HOLD, CANCELLED, or DONE, hence the three possible statuscodes ;; cancelled and hold work independent of everything underneath
(t 0)) ((equal keyword "CANCELLED") 0)
((equal keyword "HOLD") 20)
;; all other tests require a descent into the child project hence let form
(t (let ((child-statuscode (nd/descend-into-project)))
;; projects marked TODO should not be complete
(cond ((equal keyword "TODO") (if (> child-statuscode 0) child-statuscode 50))
;; projects marked DONE should have all subtasks/projects marked DONE/CANCELLED
(t (if (= child-statuscode 0) 0 50))))))
(cond ((equal keyword "HOLD") 20) (cond ((equal keyword "HOLD") 20)
((equal keyword "WAITING") 30) ((equal keyword "WAITING") 30)
((equal keyword "NEXT") 40) ((equal keyword "NEXT") 40)
((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) 40) ((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) 40)
((equal keyword "TODO") 10) ((equal keyword "TODO") 10)
;; catchall means CANCELLED or DONE (complete)
(t 0))))) (t 0)))))
(if (> cur-state project-state) (if (> cur-state project-state)
(setq project-state cur-state))))) (setq project-state cur-state)))))
@ -470,88 +559,33 @@ Using this scheme, we simply compare the magnitude of the statuscodes"
(defun nd/is-project-status-p (statuscode) (defun nd/is-project-status-p (statuscode)
(let ((keyword (nd/is-project-p))) (let ((keyword (nd/is-project-p)))
(if keyword (if keyword
(cond ((member keyword nd/project-invalid-todostates) nil) (if (member keyword nd/project-invalid-todostates)
((and (equal keyword "HOLD") (= statuscode 20)) keyword) (if (= statuscode 50) keyword)
((and (equal keyword "HOLD") (/= statuscode 20)) nil) (if (equal keyword "HOLD")
((= statuscode (nd/descend-into-project)) keyword))))) (if (= statuscode 20) keyword)
(if (= statuscode (nd/descend-into-project)) keyword))))))
;; NOTE: use save-restriction and widen if we ever actually use narrowing (evil-define-key 'motion org-agenda-mode-map "T" 'nd/toggle-project-toplevel-display)
;; tasks
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-next-project-tasks () (setq org-agenda-span 'day)
(if (not (equal (nd/is-project-task-p) "NEXT")) (setq org-agenda-time-grid (quote ((daily today remove-match)
(save-excursion (or (outline-next-heading) (point-max))))) #("----------------" 0 16 (org-heading t))
(0900 1100 1300 1500 1700))))
(defun nd/skip-non-waiting-project-tasks () (add-hook 'org-finalize-agenda-hook 'place-agenda-tags)
(if (not (equal (nd/is-project-task-p) "WAITING")) (defun place-agenda-tags ()
(save-excursion (or (outline-next-heading) (point-max))))) "Put the agenda tags by the right border of the agenda window."
(setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags))
(defun nd/skip-non-held-project-tasks () (defun nd/org-auto-exclude-function (tag)
(if (not (equal (nd/is-project-task-p) "HOLD")) "Automatic task exclusion in the agenda with / RET"
(save-excursion (or (outline-next-heading) (point-max))))) (and (cond
((string= tag "hold")
t))
(concat "-" tag)))
;; projects (setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
(defun nd/skip-projects-without-statuscode (statuscode)
(if (not (nd/is-project-status-p statuscode))
(save-excursion (or (outline-next-heading) (point-max)))))
;; top-level projects
(defun nd/skip-subprojects-without-statuscode (statuscode)
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
(save-excursion (or (outline-next-heading) (point-max)))))
(defvar nd/agenda-limit-project-toplevel t
"used to filter projects by all levels or top-level only")
(defun nd/toggle-project-toplevel-display ()
(interactive)
(setq nd/agenda-limit-project-toplevel (not nd/agenda-limit-project-toplevel))
(when (equal major-mode 'org-agenda-mode)
(org-agenda-redo))
(message "Showing %s project view in agenda" (if nd/agenda-limit-project-toplevel "toplevel" "complete")))
(defmacro nd/agenda-base-task-command (keyword skip-fun)
"shorter syntax to define task agenda commands"
`(tags-todo
"-NA-REFILE/!"
((org-agenda-overriding-header (concat ,keyword " Tasks"))
(org-agenda-skip-function ,skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy '(category-keep)))))
(defmacro nd/agenda-base-project-command (keyword statuscode)
"shorter syntax to define project agenda commands"
`(tags-todo
"-NA-REFILE-ATOMIC/!"
((org-agenda-overriding-header (concat
(and nd/agenda-limit-project-toplevel "Toplevel ")
,keyword
" Projects"))
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
'(nd/skip-subprojects-without-statuscode ,statuscode)
'(nd/skip-projects-without-statuscode ,statuscode)))
(org-agenda-sorting-strategy '(category-keep)))))
(setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
`(("t" "Task view"
((agenda "" nil)
,(macroexpand '(nd/agenda-base-task-command "Next Project" 'nd/skip-non-next-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Waiting Project" 'nd/skip-non-waiting-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Atomic" 'nd/skip-non-atomic-tasks))
,(macroexpand '(nd/agenda-base-task-command "Held Project" 'nd/skip-non-held-project-tasks))))
("o" "Project Overview"
(,(macroexpand '(nd/agenda-base-project-command "Stuck" 10))
,(macroexpand '(nd/agenda-base-project-command "Waiting" 20))
,(macroexpand '(nd/agenda-base-project-command "Active" 40))
,(macroexpand '(nd/agenda-base-project-command "Held" 30))))
("r" "Refile and errors"
((tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile"))
(org-tags-match-list-sublevels nil))))))
(use-package org-bullets (use-package org-bullets
:ensure t :ensure t

275
conf.org
View File

@ -480,37 +480,102 @@ I use tags for filtering in the agenda view to narrow down tasks by project/cont
(setq org-agenda-dim-blocked-tasks nil) (setq org-agenda-dim-blocked-tasks nil)
(setq org-agenda-compact-blocks t) (setq org-agenda-compact-blocks t)
#+END_SRC #+END_SRC
*** keymap *** custom commands
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(evil-define-key 'motion org-agenda-mode-map "T" 'nd/toggle-project-toplevel-display) (setq org-agenda-tags-todo-honor-ignore-options t)
(setq org-agenda-custom-commands
`(("t"
"Task view"
((agenda "" nil)
,(macroexpand '(nd/agenda-base-task-command "Next Project" 'nd/skip-non-next-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Waiting Project" 'nd/skip-non-waiting-project-tasks))
,(macroexpand '(nd/agenda-base-task-command "Atomic" 'nd/skip-non-atomic-tasks))
,(macroexpand '(nd/agenda-base-task-command "Held Project" 'nd/skip-non-held-project-tasks))))
("o"
"Project Overview"
(,(macroexpand '(nd/agenda-base-project-command "Stuck" 10))
,(macroexpand '(nd/agenda-base-project-command "Waiting" 20))
,(macroexpand '(nd/agenda-base-project-command "Active" 40))
,(macroexpand '(nd/agenda-base-project-command "Held" 30))))
("r"
"Refile and errors"
;; TODO add error detection here
((tags "REFILE" ((org-agenda-overriding-header "Tasks to Refile")) (org-tags-match-list-sublevels nil))
,(macroexpand '(nd/agenda-base-task-command "Discontinous Project" 'nd/skip-non-discontinuous-project-tasks))
,(macroexpand '(nd/agenda-base-project-command "Invalid" 50))))))
#+END_SRC #+END_SRC
*** views *** interactive view functions
**** calendar display
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(setq org-agenda-span 'day) (defvar nd/agenda-limit-project-toplevel t
(setq org-agenda-time-grid (quote ((daily today remove-match) "used to filter projects by all levels or top-level only")
#("----------------" 0 16 (org-heading t))
(0900 1100 1300 1500 1700))))
#+End_src
**** right align tags
the agenda does not do this by default...it's annoying
#+BEGIN_SRC emacs-lisp
(add-hook 'org-finalize-agenda-hook 'place-agenda-tags)
(defun place-agenda-tags ()
"Put the agenda tags by the right border of the agenda window."
(setq org-agenda-tags-column (- 4 (window-width)))
(org-agenda-align-tags))
#+END_SRC
*** auto exclusion
#+BEGIN_SRC emacs-lisp
(defun nd/org-auto-exclude-function (tag)
"Automatic task exclusion in the agenda with / RET"
(and (cond
((string= tag "hold")
t))
(concat "-" tag)))
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function) (defun nd/toggle-project-toplevel-display ()
(interactive)
(setq nd/agenda-limit-project-toplevel (not nd/agenda-limit-project-toplevel))
(when (equal major-mode 'org-agenda-mode)
(org-agenda-redo))
(message "Showing %s project view in agenda" (if nd/agenda-limit-project-toplevel "toplevel" "complete")))
(defmacro nd/agenda-base-task-command (keyword skip-fun)
"shorter syntax to define task agenda commands"
`(tags-todo
"-NA-REFILE/!"
((org-agenda-overriding-header (concat ,keyword " Tasks"))
(org-agenda-skip-function ,skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy '(category-keep)))))
(defmacro nd/agenda-base-project-command (keyword statuscode)
"shorter syntax to define project agenda commands"
`(tags-todo
"-NA-REFILE-ATOMIC/!"
((org-agenda-overriding-header (concat
(and nd/agenda-limit-project-toplevel "Toplevel ")
,keyword
" Projects"))
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
'(nd/skip-subprojects-without-statuscode ,statuscode)
'(nd/skip-projects-without-statuscode ,statuscode)))
(org-agenda-sorting-strategy '(category-keep)))))
#+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
;; tasks
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-next-project-tasks ()
(if (not (equal (nd/is-project-task-p) "NEXT"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-project-tasks ()
(if (not (equal (nd/is-project-task-p) "WAITING"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-project-tasks ()
(if (not (equal (nd/is-project-task-p) "HOLD"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-discontinous-project-tasks ()
(if (not (nd/is-discontinous-project-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
;; projects
;; TODO skip entire subtree if we don't need to evaluate anything inside
;; otherwise (for example) a held project will still have it's subtasks show up
(defun nd/skip-projects-without-statuscode (statuscode)
(if (not (nd/is-project-status-p statuscode))
(save-excursion (or (outline-next-heading) (point-max)))))
;; top-level projects
(defun nd/skip-subprojects-without-statuscode (statuscode)
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
(save-excursion (or (outline-next-heading) (point-max)))))
#+END_SRC #+END_SRC
*** task helper functions *** task helper functions
These are the building blocks for skip functions. These are the building blocks for skip functions.
@ -552,10 +617,6 @@ These are the building blocks for skip functions.
"return keyword if task is WAITING" "return keyword if task is WAITING"
(equal (nd/is-task-p) "WAITING")) (equal (nd/is-task-p) "WAITING"))
(defconst nd/project-invalid-todostates
'("WAITING" "NEXT")
"projects cannot have these todostates")
(defun nd/heading-has-children () (defun nd/heading-has-children ()
"returns t if heading has todoitems in its immediate subtree" "returns t if heading has todoitems in its immediate subtree"
;; TODO make this more efficient (and accurate) by only testing ;; TODO make this more efficient (and accurate) by only testing
@ -576,6 +637,19 @@ These are the building blocks for skip functions.
"returns parent keyword if heading is in the immediate subtree of a todoitem" "returns parent keyword if heading is in the immediate subtree of a todoitem"
(save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p)))) (save-excursion (and (org-up-heading-safe) (nd/is-todoitem-p))))
(defun nd/is-discontinuous-project-task-p ()
"detects todoitems that are children of non-todoitems
that in turn are children of todoitems (discontinous project)"
(let ((has-todoitem-parent)
(has-non-todoitem-parent))
(save-excursion
(while (and (org-up-heading-safe)
has-todoitem-parent)
(if (nd/is-todoitem-p)
(setq has-todoitem-parent t)
(setq has-non-todoitem-parent t))))
(and has-todoitem-parent has-non-todoitem-parent)))
(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
@ -596,6 +670,10 @@ These are the building blocks for skip functions.
(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) ;; TODO: is there a better way to handle statuscodes like this??? (array like thingy)
(defun nd/descend-into-project () (defun nd/descend-into-project ()
@ -612,7 +690,9 @@ These are the building blocks for skip functions.
- if project has any TODO (regardless of DONE or CANCELLED) it is stuck - 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 - 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 WAITING means waiting project
- in the same manner, NEXT means active. NEXT overrides all - in the same manner, NEXT or scheduled means active.
- can also detect errors which override all
- anything higher than active breaks the recursion/tree walk
Using this scheme, we simply compare the magnitude of the statuscodes" Using this scheme, we simply compare the magnitude of the statuscodes"
(let ((project-state 0) (let ((project-state 0)
@ -627,15 +707,23 @@ These are the building blocks for skip functions.
(if keyword (if keyword
(let ((cur-state (let ((cur-state
(if has-children (if has-children
(cond ((equal keyword "HOLD") 20) (cond ((member keyword nd/project-invalid-todostates) 50)
((equal keyword "TODO") (nd/descend-into-project)) ((nd/is-scheduled-heading-p) 50)
;; NOTE: all projects are assumed to only have TODO, HOLD, CANCELLED, or DONE, hence the three possible statuscodes ;; cancelled and hold work independent of everything underneath
(t 0)) ((equal keyword "CANCELLED") 0)
((equal keyword "HOLD") 20)
;; all other tests require a descent into the child project hence let form
(t (let ((child-statuscode (nd/descend-into-project)))
;; projects marked TODO should not be complete
(cond ((equal keyword "TODO") (if (> child-statuscode 0) child-statuscode 50))
;; projects marked DONE should have all subtasks/projects marked DONE/CANCELLED
(t (if (= child-statuscode 0) 0 50))))))
(cond ((equal keyword "HOLD") 20) (cond ((equal keyword "HOLD") 20)
((equal keyword "WAITING") 30) ((equal keyword "WAITING") 30)
((equal keyword "NEXT") 40) ((equal keyword "NEXT") 40)
((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) 40) ((and (equal keyword "TODO") (nd/is-scheduled-heading-p)) 40)
((equal keyword "TODO") 10) ((equal keyword "TODO") 10)
;; catchall means CANCELLED or DONE (complete)
(t 0))))) (t 0)))))
(if (> cur-state project-state) (if (> cur-state project-state)
(setq project-state cur-state))))) (setq project-state cur-state)))))
@ -646,98 +734,43 @@ These are the building blocks for skip functions.
(defun nd/is-project-status-p (statuscode) (defun nd/is-project-status-p (statuscode)
(let ((keyword (nd/is-project-p))) (let ((keyword (nd/is-project-p)))
(if keyword (if keyword
(cond ((member keyword nd/project-invalid-todostates) nil) (if (member keyword nd/project-invalid-todostates)
((and (equal keyword "HOLD") (= statuscode 20)) keyword) (if (= statuscode 50) keyword)
((and (equal keyword "HOLD") (/= statuscode 20)) nil) (if (equal keyword "HOLD")
((= statuscode (nd/descend-into-project)) keyword))))) (if (= statuscode 20) keyword)
(if (= statuscode (nd/descend-into-project)) keyword))))))
#+END_SRC #+END_SRC
*** skip functions *** keymap
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 #+BEGIN_SRC emacs-lisp
;; NOTE: use save-restriction and widen if we ever actually use narrowing (evil-define-key 'motion org-agenda-mode-map "T" 'nd/toggle-project-toplevel-display)
;; tasks
(defun nd/skip-non-atomic-tasks ()
(if (not (nd/is-atomic-task-p))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-next-project-tasks ()
(if (not (equal (nd/is-project-task-p) "NEXT"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-waiting-project-tasks ()
(if (not (equal (nd/is-project-task-p) "WAITING"))
(save-excursion (or (outline-next-heading) (point-max)))))
(defun nd/skip-non-held-project-tasks ()
(if (not (equal (nd/is-project-task-p) "HOLD"))
(save-excursion (or (outline-next-heading) (point-max)))))
;; projects
(defun nd/skip-projects-without-statuscode (statuscode)
(if (not (nd/is-project-status-p statuscode))
(save-excursion (or (outline-next-heading) (point-max)))))
;; top-level projects
(defun nd/skip-subprojects-without-statuscode (statuscode)
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
(save-excursion (or (outline-next-heading) (point-max)))))
#+END_SRC #+END_SRC
*** interactive view functions *** views
**** calendar display
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(defvar nd/agenda-limit-project-toplevel t (setq org-agenda-span 'day)
"used to filter projects by all levels or top-level only") (setq org-agenda-time-grid (quote ((daily today remove-match)
#("----------------" 0 16 (org-heading t))
(defun nd/toggle-project-toplevel-display () (0900 1100 1300 1500 1700))))
(interactive) #+End_src
(setq nd/agenda-limit-project-toplevel (not nd/agenda-limit-project-toplevel)) **** right align tags
(when (equal major-mode 'org-agenda-mode) the agenda does not do this by default...it's annoying
(org-agenda-redo)) #+BEGIN_SRC emacs-lisp
(message "Showing %s project view in agenda" (if nd/agenda-limit-project-toplevel "toplevel" "complete"))) (add-hook 'org-finalize-agenda-hook 'place-agenda-tags)
(defun place-agenda-tags ()
(defmacro nd/agenda-base-task-command (keyword skip-fun) "Put the agenda tags by the right border of the agenda window."
"shorter syntax to define task agenda commands" (setq org-agenda-tags-column (- 4 (window-width)))
`(tags-todo (org-agenda-align-tags))
"-NA-REFILE/!"
((org-agenda-overriding-header (concat ,keyword " Tasks"))
(org-agenda-skip-function ,skip-fun)
(org-agenda-todo-ignore-with-date 'all)
(org-agenda-sorting-strategy '(category-keep)))))
(defmacro nd/agenda-base-project-command (keyword statuscode)
"shorter syntax to define project agenda commands"
`(tags-todo
"-NA-REFILE-ATOMIC/!"
((org-agenda-overriding-header (concat
(and nd/agenda-limit-project-toplevel "Toplevel ")
,keyword
" Projects"))
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
'(nd/skip-subprojects-without-statuscode ,statuscode)
'(nd/skip-projects-without-statuscode ,statuscode)))
(org-agenda-sorting-strategy '(category-keep)))))
#+END_SRC #+END_SRC
*** custom commands *** auto exclusion
#+BEGIN_SRC emacs-lisp #+BEGIN_SRC emacs-lisp
(setq org-agenda-tags-todo-honor-ignore-options t) (defun nd/org-auto-exclude-function (tag)
(setq org-agenda-custom-commands "Automatic task exclusion in the agenda with / RET"
`(("t" "Task view" (and (cond
((agenda "" nil) ((string= tag "hold")
,(macroexpand '(nd/agenda-base-task-command "Next Project" 'nd/skip-non-next-project-tasks)) t))
,(macroexpand '(nd/agenda-base-task-command "Waiting Project" 'nd/skip-non-waiting-project-tasks)) (concat "-" tag)))
,(macroexpand '(nd/agenda-base-task-command "Atomic" 'nd/skip-non-atomic-tasks))
,(macroexpand '(nd/agenda-base-task-command "Held Project" 'nd/skip-non-held-project-tasks))))
("o" "Project Overview"
(,(macroexpand '(nd/agenda-base-project-command "Stuck" 10))
,(macroexpand '(nd/agenda-base-project-command "Waiting" 20))
,(macroexpand '(nd/agenda-base-project-command "Active" 40))
,(macroexpand '(nd/agenda-base-project-command "Held" 30))))
("r" "Refile and errors"
((tags "REFILE"
((org-agenda-overriding-header "Tasks to Refile"))
(org-tags-match-list-sublevels nil))))))
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
#+END_SRC #+END_SRC
** ui ** ui
*** bullets *** bullets