simplify skip functions
This commit is contained in:
parent
0ef5fe8dd1
commit
59010e234f
250
conf.el
250
conf.el
|
@ -319,9 +319,15 @@
|
||||||
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
|
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
|
||||||
(setq org-refile-target-verify-function 'nd/verify-refile-target)
|
(setq org-refile-target-verify-function 'nd/verify-refile-target)
|
||||||
|
|
||||||
(setq org-agenda-files (quote ("~/Org"
|
(use-package org-bullets
|
||||||
|
:ensure t
|
||||||
|
:config
|
||||||
|
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
|
||||||
|
|
||||||
|
(setq org-agenda-files '("~/Org"
|
||||||
"~/Org/large_projects"
|
"~/Org/large_projects"
|
||||||
"~/Org/reference")))
|
"~/Org/reference"))
|
||||||
|
;; (setq org-agenda-files '("~/Org/reference/agendatest.org"))
|
||||||
(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)
|
||||||
|
|
||||||
|
@ -345,7 +351,7 @@ this is used to both test if a heading is a todoitem and retrieving the keyword"
|
||||||
(and (not (nd/heading-has-parent)) (nd/is-task-p)))
|
(and (not (nd/heading-has-parent)) (nd/is-task-p)))
|
||||||
|
|
||||||
(defun nd/is-project-task-p ()
|
(defun nd/is-project-task-p ()
|
||||||
"return todo keyword if heading is task with no parents"
|
"return todo keyword if heading is task with parents"
|
||||||
(and (nd/heading-has-parent) (nd/is-task-p)))
|
(and (nd/heading-has-parent) (nd/is-task-p)))
|
||||||
|
|
||||||
(defun nd/is-scheduled-heading-p ()
|
(defun nd/is-scheduled-heading-p ()
|
||||||
|
@ -410,9 +416,9 @@ 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 ()
|
(defun nd/has-discontinuous-parent ()
|
||||||
"detects todoitems that are children of non-todoitems
|
"returns t if heading has a parent which is not a
|
||||||
that in turn are children of todoitems (discontinous project)"
|
todoitem which in turn has a parent which is a todoitem"
|
||||||
(let ((has-todoitem-parent)
|
(let ((has-todoitem-parent)
|
||||||
(has-non-todoitem-parent))
|
(has-non-todoitem-parent))
|
||||||
(save-excursion
|
(save-excursion
|
||||||
|
@ -509,31 +515,31 @@ down the list override higher items")
|
||||||
(org-forward-heading-same-level 1 t)))
|
(org-forward-heading-same-level 1 t)))
|
||||||
project-state))
|
project-state))
|
||||||
|
|
||||||
(defmacro nd/is-project-keyword-status-p (top-keyword operator statuscode)
|
(defmacro nd/is-project-keyword-status-p (test-keyword operator statuscode)
|
||||||
"tests if a project has toplevel heading of top-keyword and
|
"tests if a project has toplevel heading of top-keyword and
|
||||||
child status equal to status code and returns keyword if
|
child status equal to status code and returns keyword if
|
||||||
both are true"
|
both are true"
|
||||||
`(if (and (equal ,keyword ,top-keyword)
|
`(and
|
||||||
(nd/compare-statuscodes ,operator (nd/descend-into-project) statuscode))
|
(equal ,keyword ,test-keyword)
|
||||||
,keyword))
|
(nd/compare-statuscodes ,operator (nd/descend-into-project) ,statuscode)))
|
||||||
|
|
||||||
(defun nd/is-project-status-p (statuscode)
|
(defun nd/is-project-status-p (statuscode)
|
||||||
(let ((keyword (nd/is-project-p)))
|
"Returns t if project matches statuscode given.
|
||||||
(if keyword
|
Note that this assumes the headline being tested is a valid project"
|
||||||
(case statuscode
|
(case statuscode
|
||||||
;; projects closed more than 30 days ago
|
;; projects closed more than 30 days ago
|
||||||
;; note CANCELLED overrides all subtasks/projects
|
;; note CANCELLED overrides all subtasks/projects
|
||||||
(:archivable
|
(:archivable
|
||||||
(if (nd/is-archivable-heading-p)
|
(if (nd/is-archivable-heading-p)
|
||||||
(cond ((equal keyword "CANCELLED") keyword)
|
(or (equal keyword "CANCELLED")
|
||||||
(t (nd/is-project-keyword-status-p "DONE" = :archivable)))))
|
(nd/is-project-keyword-status-p "DONE" = :archivable))))
|
||||||
|
|
||||||
;; projects closed less than 30 days ago
|
;; projects closed less than 30 days ago
|
||||||
;; note CANCELLED overrides all subtasks/projects
|
;; note CANCELLED overrides all subtasks/projects
|
||||||
(:complete
|
(:complete
|
||||||
(if (not (nd/is-archivable-heading-p))
|
(if (not (nd/is-archivable-heading-p))
|
||||||
(cond ((equal keyword "CANCELLED") keyword)
|
(or (equal keyword "CANCELLED")
|
||||||
(t (nd/is-project-keyword-status-p "DONE" = :complete)))))
|
(nd/is-project-keyword-status-p "DONE" = :complete))))
|
||||||
|
|
||||||
;; projects with no waiting, held, or active components
|
;; projects with no waiting, held, or active components
|
||||||
(:stuck
|
(:stuck
|
||||||
|
@ -542,8 +548,8 @@ both are true"
|
||||||
;; held projects
|
;; held projects
|
||||||
;; note toplevel HOLD overrides all subtasks/projects
|
;; note toplevel HOLD overrides all subtasks/projects
|
||||||
(:held
|
(:held
|
||||||
(cond ((equal keyword "HOLD") keyword)
|
(or (equal keyword "HOLD")
|
||||||
(t (nd/is-project-keyword-status-p "TODO" = :stuck))))
|
(nd/is-project-keyword-status-p "TODO" = :held)))
|
||||||
|
|
||||||
;; projects with at least one waiting component
|
;; projects with at least one waiting component
|
||||||
(:waiting
|
(:waiting
|
||||||
|
@ -557,101 +563,132 @@ both are true"
|
||||||
(:done-incomplete
|
(:done-incomplete
|
||||||
(nd/is-project-keyword-status-p "DONE" > :complete))
|
(nd/is-project-keyword-status-p "DONE" > :complete))
|
||||||
|
|
||||||
;; projects not marked DONE but all subtasks are done
|
;; projects marked TODO but all subtasks are done
|
||||||
(:undone-complete
|
(:undone-complete
|
||||||
(nd/is-project-keyword-status-p "TODO" < :stuck))
|
(nd/is-project-keyword-status-p "TODO" < :stuck))
|
||||||
|
|
||||||
;; projects with invalid todo keywords
|
;; projects with invalid todo keywords
|
||||||
(:invalid-todostate
|
(:invalid-todostate
|
||||||
(if (member keyword nd/project-invalid-todostates) keyword))
|
(member keyword nd/project-invalid-todostates))
|
||||||
|
|
||||||
;; projects with scheduled heading (only subtasks should be scheduled)
|
;; projects with scheduled heading (only subtasks should be scheduled)
|
||||||
(:scheduled-project
|
(:scheduled-project
|
||||||
(if (nd/is-scheduled-heading-p) keyword))))))
|
(nd/is-scheduled-heading-p))
|
||||||
|
|
||||||
;; TODO we could clean this up with macros
|
;; error if not known
|
||||||
(defun nd/skip-non-atomic-tasks ()
|
(t (if (not (member statuscode nd/project-statuscodes))
|
||||||
|
(error "unknown statuscode")))))
|
||||||
|
|
||||||
|
;; helper functions
|
||||||
|
(defun nd/skip-item ()
|
||||||
|
(save-excursion (or (outline-next-heading) (point-max))))
|
||||||
|
|
||||||
|
(defun nd/skip-subtree ()
|
||||||
|
(save-excursion (or (org-end-of-subtree t) (point-max))))
|
||||||
|
|
||||||
|
(defconst nd/project-skip-todostates
|
||||||
|
'("HOLD" "CANCELLED")
|
||||||
|
"These keywords override all contents within their subtrees.
|
||||||
|
Currently used to tell skip functions when they can hop over
|
||||||
|
entire subtrees to save time and ignore tasks")
|
||||||
|
|
||||||
|
(defmacro nd/skip-heading-with (heading-fun test-fun)
|
||||||
|
"Skips headings accoring to certain characteristics. heading-fun
|
||||||
|
is a function that tests the heading and returns the todoitem keyword
|
||||||
|
on success. Test-fun is a function that further tests the identity of
|
||||||
|
the heading and may or may not use the keyword output supplied by
|
||||||
|
the heading-fun. This function will not skip if heading-fun and
|
||||||
|
test-fun return true"
|
||||||
|
`(save-restriction
|
||||||
|
(widen)
|
||||||
|
(let ((keyword (,heading-fun)))
|
||||||
|
(message keyword)
|
||||||
|
(if (not (and keyword ,test-fun))
|
||||||
|
(nd/skip-item)))))
|
||||||
|
|
||||||
|
;; atomic tasks
|
||||||
|
;; by definition these have no parents, so
|
||||||
|
;; we don't need to worry about skipping over projects
|
||||||
|
;; any todo state is valid and we only sort by done/cancelled
|
||||||
|
(defun nd/skip-non-unclosed-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(not (member keyword org-done-keywords))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-closed-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(and (member keyword org-done-keywords)
|
||||||
|
(not (nd/is-archivable-heading)))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-archivable-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(and (member keyword org-done-keywords)
|
||||||
|
(nd/is-archivable-heading))))
|
||||||
|
|
||||||
|
;; project tasks
|
||||||
|
;; since these are part of projects I need to assess
|
||||||
|
;; if the parent project is skippable, in which case
|
||||||
|
;; I jump to the next subtree
|
||||||
|
;; Note that I only care about the keyword in these
|
||||||
|
;; cases because I don't archive these, I archive
|
||||||
|
;; their parent projects. The keywords I care about
|
||||||
|
;; are NEXT, WAITING, and HOLD because these are
|
||||||
|
;; definitive project tasks that require/inhibit
|
||||||
|
;; futher action
|
||||||
|
(defun nd/skip-non-keyword-project-tasks (skip-keyword)
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(widen)
|
(widen)
|
||||||
(if (not (nd/is-atomic-task-p))
|
(let ((keyword (nd/is-todoitem-p)))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(if keyword
|
||||||
|
(if (nd/heading-has-children)
|
||||||
|
(if (member keyword nd/project-skip-todostates)
|
||||||
|
(nd/skip-subtree)
|
||||||
|
(nd/skip-item))
|
||||||
|
(if (not (and (nd/heading-has-parent)
|
||||||
|
(equal keyword skip-keyword)))
|
||||||
|
(nd/skip-item)))
|
||||||
|
(nd/skip-item)))))
|
||||||
|
|
||||||
(defun nd/skip-non-next-project-tasks ()
|
;; task-level errors
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "NEXT"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-waiting-project-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "WAITING"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-held-project-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "HOLD"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
;; slip functions
|
|
||||||
(defun nd/skip-non-discontinuous-project-tasks ()
|
(defun nd/skip-non-discontinuous-project-tasks ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (nd/is-discontinuous-project-task-p))
|
(nd/has-discontinuous-parent)))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-done-open-todoitems ()
|
(defun nd/skip-non-done-unclosed-todoitems ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (and (member (nd/is-todoitem-p) org-done-keywords) (not (nd/is-closed-heading-p))))
|
(and (member keyword org-done-keywords)
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(not (nd/is-closed-heading-p)))))
|
||||||
|
|
||||||
(defun nd/skip-non-undone-closed-todoitems ()
|
(defun nd/skip-non-undone-closed-todoitems ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (and (not (member (nd/is-todoitem-p)) org-done-keywords) (nd/is-closed-heading-p)))
|
(and (not (member keyword org-done-keywords))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(nd/is-closed-heading-p))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-series-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(nd/is-series-heading-p)))
|
||||||
|
|
||||||
;; projects
|
;; 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)
|
(defun nd/skip-projects-without-statuscode (statuscode)
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(widen)
|
(widen)
|
||||||
|
(let ((keyword (nd/is-project-p)))
|
||||||
|
;; TODO there may be a way to skip over skippable projects
|
||||||
|
;; and save a few cycles. Not a huge deal, but would require
|
||||||
|
;; keeping the skippable line and then skipping over the others
|
||||||
|
;; in one fell swoop, not easy to do efficiently
|
||||||
|
(if keyword
|
||||||
(if (not (nd/is-project-status-p statuscode))
|
(if (not (nd/is-project-status-p statuscode))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(if nd/agenda-limit-project-toplevel
|
||||||
|
(nd/skip-subtree)
|
||||||
;; top-level projects
|
(nd/skip-item)))
|
||||||
(defun nd/skip-subprojects-without-statuscode (statuscode)
|
(nd/skip-item)))))
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-series-projects-without-statuscode (statuscode)
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (not (and (nd/is-series-heading-p) (nd/is-project-status-p statuscode)))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
;; series projects
|
|
||||||
;; defined as project with property Project_type=series
|
|
||||||
;; must have:
|
|
||||||
;; - one level of subtasks
|
|
||||||
;; - all subtasks either TODO/scheduled, NEXT, DONE, CANCELLED
|
|
||||||
;; - at least one TODO/scheduled or NEXT (active) ..else empty
|
|
||||||
;; invalid if:
|
|
||||||
;; - project header is invalid project header (typical rules apply)
|
|
||||||
|
|
||||||
;; archiving
|
|
||||||
(defun nd/skip-non-archivable-atomic-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (not (nd/is-archivable-atomic-task-p))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defvar nd/agenda-limit-project-toplevel t
|
(defvar nd/agenda-limit-project-toplevel t
|
||||||
"used to filter projects by all levels or top-level only")
|
"used to filter projects by all levels or top-level only")
|
||||||
|
@ -665,8 +702,8 @@ both are true"
|
||||||
|
|
||||||
(defun nd/agenda-base-task-command (keyword skip-fun)
|
(defun nd/agenda-base-task-command (keyword skip-fun)
|
||||||
"shorter syntax to define task agenda commands"
|
"shorter syntax to define task agenda commands"
|
||||||
`(tags-todo
|
`(tags
|
||||||
"-NA-REFILE/!"
|
"-NA-REFILE/"
|
||||||
((org-agenda-overriding-header (concat ,keyword " Tasks"))
|
((org-agenda-overriding-header (concat ,keyword " Tasks"))
|
||||||
(org-agenda-skip-function ,skip-fun)
|
(org-agenda-skip-function ,skip-fun)
|
||||||
(org-agenda-todo-ignore-with-date 'all)
|
(org-agenda-todo-ignore-with-date 'all)
|
||||||
|
@ -680,9 +717,7 @@ both are true"
|
||||||
(and nd/agenda-limit-project-toplevel "Toplevel ")
|
(and nd/agenda-limit-project-toplevel "Toplevel ")
|
||||||
,keyword
|
,keyword
|
||||||
" Projects"))
|
" Projects"))
|
||||||
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
|
(org-agenda-skip-function '(nd/skip-projects-without-statuscode ,statuscode))
|
||||||
'(nd/skip-subprojects-without-statuscode ,statuscode)
|
|
||||||
'(nd/skip-projects-without-statuscode ,statuscode)))
|
|
||||||
(org-agenda-sorting-strategy '(category-keep)))))
|
(org-agenda-sorting-strategy '(category-keep)))))
|
||||||
|
|
||||||
(setq org-agenda-tags-todo-honor-ignore-options t)
|
(setq org-agenda-tags-todo-honor-ignore-options t)
|
||||||
|
@ -690,10 +725,10 @@ both are true"
|
||||||
`(("t"
|
`(("t"
|
||||||
"Task View"
|
"Task View"
|
||||||
((agenda "" nil)
|
((agenda "" nil)
|
||||||
,(nd/agenda-base-task-command "Next Project" ''nd/skip-non-next-project-tasks)
|
,(nd/agenda-base-task-command "Next Project" ''(nd/skip-non-keyword-project-tasks "NEXT"))
|
||||||
,(nd/agenda-base-task-command "Waiting Project" ''nd/skip-non-waiting-project-tasks)
|
,(nd/agenda-base-task-command "Waiting Project" ''(nd/skip-non-keyword-project-tasks "WAITING"))
|
||||||
,(nd/agenda-base-task-command "Atomic" ''nd/skip-non-atomic-tasks)
|
,(nd/agenda-base-task-command "Atomic" ''nd/skip-non-unclosed-atomic-tasks)
|
||||||
,(nd/agenda-base-task-command "Held Project" ''nd/skip-non-held-project-tasks)))
|
,(nd/agenda-base-task-command "Held Project" ''(nd/skip-non-keyword-project-tasks "HOLD"))))
|
||||||
("o"
|
("o"
|
||||||
"Project Overview"
|
"Project Overview"
|
||||||
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Stuck" :stuck)
|
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Stuck" :stuck)
|
||||||
|
@ -706,11 +741,11 @@ both are true"
|
||||||
((org-agenda-overriding-header "Tasks to Refile"))
|
((org-agenda-overriding-header "Tasks to Refile"))
|
||||||
(org-tags-match-list-sublevels nil))
|
(org-tags-match-list-sublevels nil))
|
||||||
,(nd/agenda-base-task-command "Discontinous Project" ''nd/skip-non-discontinuous-project-tasks)
|
,(nd/agenda-base-task-command "Discontinous Project" ''nd/skip-non-discontinuous-project-tasks)
|
||||||
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Unmarked Completed" :complete)
|
,(nd/agenda-base-task-command "Undone Closed" ''nd/skip-non-undone-closed-todoitems)
|
||||||
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Invalid" :invalid-todostate)
|
,(nd/agenda-base-task-command "Done Unclosed" ''nd/skip-non-done-unclosed-todoitems)
|
||||||
;; ,(nd/agenda-base-task-command "Done But Not Closed" ''nd/skip-non-done-open-todoitems)
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Undone Completed" :undone-complete)
|
||||||
;; ,(nd/agenda-base-task-command "Closed But Not Done" ''nd/skip-non-open-closed-todoitems)
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Done Incompleted" :done-incomplete)
|
||||||
))
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Invalid Todostate" :invalid-todostate)))
|
||||||
("s"
|
("s"
|
||||||
"Series projects"
|
"Series projects"
|
||||||
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/!" "Active Series" :active)
|
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/!" "Active Series" :active)
|
||||||
|
@ -746,11 +781,6 @@ both are true"
|
||||||
|
|
||||||
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
|
(setq org-agenda-auto-exclude-function 'nd/org-auto-exclude-function)
|
||||||
|
|
||||||
(use-package org-bullets
|
|
||||||
:ensure t
|
|
||||||
:config
|
|
||||||
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
|
|
||||||
|
|
||||||
(use-package calfw-org
|
(use-package calfw-org
|
||||||
:init
|
:init
|
||||||
:ensure t
|
:ensure t
|
||||||
|
|
255
conf.org
255
conf.org
|
@ -481,12 +481,21 @@ I use tags for contexts (mostly). The "@" represents location contexts and a mut
|
||||||
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
|
(not (member (nth 2 (org-heading-components)) org-done-keywords)))
|
||||||
(setq org-refile-target-verify-function 'nd/verify-refile-target)
|
(setq org-refile-target-verify-function 'nd/verify-refile-target)
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
** ui
|
||||||
|
*** bullets
|
||||||
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
(use-package org-bullets
|
||||||
|
:ensure t
|
||||||
|
:config
|
||||||
|
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
|
||||||
|
#+END_SRC
|
||||||
** agenda
|
** agenda
|
||||||
*** basic config
|
*** basic config
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
(setq org-agenda-files (quote ("~/Org"
|
(setq org-agenda-files '("~/Org"
|
||||||
"~/Org/large_projects"
|
"~/Org/large_projects"
|
||||||
"~/Org/reference")))
|
"~/Org/reference"))
|
||||||
|
;; (setq org-agenda-files '("~/Org/reference/agendatest.org"))
|
||||||
(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
|
||||||
|
@ -513,7 +522,7 @@ These are the building blocks for skip functions.
|
||||||
(and (not (nd/heading-has-parent)) (nd/is-task-p)))
|
(and (not (nd/heading-has-parent)) (nd/is-task-p)))
|
||||||
|
|
||||||
(defun nd/is-project-task-p ()
|
(defun nd/is-project-task-p ()
|
||||||
"return todo keyword if heading is task with no parents"
|
"return todo keyword if heading is task with parents"
|
||||||
(and (nd/heading-has-parent) (nd/is-task-p)))
|
(and (nd/heading-has-parent) (nd/is-task-p)))
|
||||||
|
|
||||||
(defun nd/is-scheduled-heading-p ()
|
(defun nd/is-scheduled-heading-p ()
|
||||||
|
@ -578,9 +587,9 @@ 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 ()
|
(defun nd/has-discontinuous-parent ()
|
||||||
"detects todoitems that are children of non-todoitems
|
"returns t if heading has a parent which is not a
|
||||||
that in turn are children of todoitems (discontinous project)"
|
todoitem which in turn has a parent which is a todoitem"
|
||||||
(let ((has-todoitem-parent)
|
(let ((has-todoitem-parent)
|
||||||
(has-non-todoitem-parent))
|
(has-non-todoitem-parent))
|
||||||
(save-excursion
|
(save-excursion
|
||||||
|
@ -677,31 +686,31 @@ These are the building blocks for skip functions.
|
||||||
(org-forward-heading-same-level 1 t)))
|
(org-forward-heading-same-level 1 t)))
|
||||||
project-state))
|
project-state))
|
||||||
|
|
||||||
(defmacro nd/is-project-keyword-status-p (top-keyword operator statuscode)
|
(defmacro nd/is-project-keyword-status-p (test-keyword operator statuscode)
|
||||||
"tests if a project has toplevel heading of top-keyword and
|
"tests if a project has toplevel heading of top-keyword and
|
||||||
child status equal to status code and returns keyword if
|
child status equal to status code and returns keyword if
|
||||||
both are true"
|
both are true"
|
||||||
`(if (and (equal ,keyword ,top-keyword)
|
`(and
|
||||||
(nd/compare-statuscodes ,operator (nd/descend-into-project) statuscode))
|
(equal ,keyword ,test-keyword)
|
||||||
,keyword))
|
(nd/compare-statuscodes ,operator (nd/descend-into-project) ,statuscode)))
|
||||||
|
|
||||||
(defun nd/is-project-status-p (statuscode)
|
(defun nd/is-project-status-p (statuscode)
|
||||||
(let ((keyword (nd/is-project-p)))
|
"Returns t if project matches statuscode given.
|
||||||
(if keyword
|
Note that this assumes the headline being tested is a valid project"
|
||||||
(case statuscode
|
(case statuscode
|
||||||
;; projects closed more than 30 days ago
|
;; projects closed more than 30 days ago
|
||||||
;; note CANCELLED overrides all subtasks/projects
|
;; note CANCELLED overrides all subtasks/projects
|
||||||
(:archivable
|
(:archivable
|
||||||
(if (nd/is-archivable-heading-p)
|
(if (nd/is-archivable-heading-p)
|
||||||
(cond ((equal keyword "CANCELLED") keyword)
|
(or (equal keyword "CANCELLED")
|
||||||
(t (nd/is-project-keyword-status-p "DONE" = :archivable)))))
|
(nd/is-project-keyword-status-p "DONE" = :archivable))))
|
||||||
|
|
||||||
;; projects closed less than 30 days ago
|
;; projects closed less than 30 days ago
|
||||||
;; note CANCELLED overrides all subtasks/projects
|
;; note CANCELLED overrides all subtasks/projects
|
||||||
(:complete
|
(:complete
|
||||||
(if (not (nd/is-archivable-heading-p))
|
(if (not (nd/is-archivable-heading-p))
|
||||||
(cond ((equal keyword "CANCELLED") keyword)
|
(or (equal keyword "CANCELLED")
|
||||||
(t (nd/is-project-keyword-status-p "DONE" = :complete)))))
|
(nd/is-project-keyword-status-p "DONE" = :complete))))
|
||||||
|
|
||||||
;; projects with no waiting, held, or active components
|
;; projects with no waiting, held, or active components
|
||||||
(:stuck
|
(:stuck
|
||||||
|
@ -710,8 +719,8 @@ These are the building blocks for skip functions.
|
||||||
;; held projects
|
;; held projects
|
||||||
;; note toplevel HOLD overrides all subtasks/projects
|
;; note toplevel HOLD overrides all subtasks/projects
|
||||||
(:held
|
(:held
|
||||||
(cond ((equal keyword "HOLD") keyword)
|
(or (equal keyword "HOLD")
|
||||||
(t (nd/is-project-keyword-status-p "TODO" = :stuck))))
|
(nd/is-project-keyword-status-p "TODO" = :held)))
|
||||||
|
|
||||||
;; projects with at least one waiting component
|
;; projects with at least one waiting component
|
||||||
(:waiting
|
(:waiting
|
||||||
|
@ -725,106 +734,136 @@ These are the building blocks for skip functions.
|
||||||
(:done-incomplete
|
(:done-incomplete
|
||||||
(nd/is-project-keyword-status-p "DONE" > :complete))
|
(nd/is-project-keyword-status-p "DONE" > :complete))
|
||||||
|
|
||||||
;; projects not marked DONE but all subtasks are done
|
;; projects marked TODO but all subtasks are done
|
||||||
(:undone-complete
|
(:undone-complete
|
||||||
(nd/is-project-keyword-status-p "TODO" < :stuck))
|
(nd/is-project-keyword-status-p "TODO" < :stuck))
|
||||||
|
|
||||||
;; projects with invalid todo keywords
|
;; projects with invalid todo keywords
|
||||||
(:invalid-todostate
|
(:invalid-todostate
|
||||||
(if (member keyword nd/project-invalid-todostates) keyword))
|
(member keyword nd/project-invalid-todostates))
|
||||||
|
|
||||||
;; projects with scheduled heading (only subtasks should be scheduled)
|
;; projects with scheduled heading (only subtasks should be scheduled)
|
||||||
(:scheduled-project
|
(:scheduled-project
|
||||||
(if (nd/is-scheduled-heading-p) keyword))))))
|
(nd/is-scheduled-heading-p))
|
||||||
|
|
||||||
|
;; error if not known
|
||||||
|
(t (if (not (member statuscode nd/project-statuscodes))
|
||||||
|
(error "unknown statuscode")))))
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** skip functions
|
*** skip functions
|
||||||
These are the primary means we use to sort through tasks. Note that we could do this with
|
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.
|
tags in the custom commands section but I find this easier to maintain and possibly faster.
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
;; TODO we could clean this up with macros
|
;; helper functions
|
||||||
(defun nd/skip-non-atomic-tasks ()
|
(defun nd/skip-item ()
|
||||||
|
(save-excursion (or (outline-next-heading) (point-max))))
|
||||||
|
|
||||||
|
(defun nd/skip-subtree ()
|
||||||
|
(save-excursion (or (org-end-of-subtree t) (point-max))))
|
||||||
|
|
||||||
|
(defconst nd/project-skip-todostates
|
||||||
|
'("HOLD" "CANCELLED")
|
||||||
|
"These keywords override all contents within their subtrees.
|
||||||
|
Currently used to tell skip functions when they can hop over
|
||||||
|
entire subtrees to save time and ignore tasks")
|
||||||
|
|
||||||
|
(defmacro nd/skip-heading-with (heading-fun test-fun)
|
||||||
|
"Skips headings accoring to certain characteristics. heading-fun
|
||||||
|
is a function that tests the heading and returns the todoitem keyword
|
||||||
|
on success. Test-fun is a function that further tests the identity of
|
||||||
|
the heading and may or may not use the keyword output supplied by
|
||||||
|
the heading-fun. This function will not skip if heading-fun and
|
||||||
|
test-fun return true"
|
||||||
|
`(save-restriction
|
||||||
|
(widen)
|
||||||
|
(let ((keyword (,heading-fun)))
|
||||||
|
(message keyword)
|
||||||
|
(if (not (and keyword ,test-fun))
|
||||||
|
(nd/skip-item)))))
|
||||||
|
|
||||||
|
;; atomic tasks
|
||||||
|
;; by definition these have no parents, so
|
||||||
|
;; we don't need to worry about skipping over projects
|
||||||
|
;; any todo state is valid and we only sort by done/cancelled
|
||||||
|
(defun nd/skip-non-unclosed-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(not (member keyword org-done-keywords))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-closed-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(and (member keyword org-done-keywords)
|
||||||
|
(not (nd/is-archivable-heading)))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-archivable-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(and (member keyword org-done-keywords)
|
||||||
|
(nd/is-archivable-heading))))
|
||||||
|
|
||||||
|
;; project tasks
|
||||||
|
;; since these are part of projects I need to assess
|
||||||
|
;; if the parent project is skippable, in which case
|
||||||
|
;; I jump to the next subtree
|
||||||
|
;; Note that I only care about the keyword in these
|
||||||
|
;; cases because I don't archive these, I archive
|
||||||
|
;; their parent projects. The keywords I care about
|
||||||
|
;; are NEXT, WAITING, and HOLD because these are
|
||||||
|
;; definitive project tasks that require/inhibit
|
||||||
|
;; futher action
|
||||||
|
(defun nd/skip-non-keyword-project-tasks (skip-keyword)
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(widen)
|
(widen)
|
||||||
(if (not (nd/is-atomic-task-p))
|
(let ((keyword (nd/is-todoitem-p)))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(if keyword
|
||||||
|
(if (nd/heading-has-children)
|
||||||
|
(if (member keyword nd/project-skip-todostates)
|
||||||
|
(nd/skip-subtree)
|
||||||
|
(nd/skip-item))
|
||||||
|
(if (not (and (nd/heading-has-parent)
|
||||||
|
(equal keyword skip-keyword)))
|
||||||
|
(nd/skip-item)))
|
||||||
|
(nd/skip-item)))))
|
||||||
|
|
||||||
(defun nd/skip-non-next-project-tasks ()
|
;; task-level errors
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "NEXT"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-waiting-project-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "WAITING"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-held-project-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
;; TODO skip over invalid and held
|
|
||||||
(if (not (equal (nd/is-project-task-p) "HOLD"))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
;; slip functions
|
|
||||||
(defun nd/skip-non-discontinuous-project-tasks ()
|
(defun nd/skip-non-discontinuous-project-tasks ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (nd/is-discontinuous-project-task-p))
|
(nd/has-discontinuous-parent)))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-non-done-open-todoitems ()
|
(defun nd/skip-non-done-unclosed-todoitems ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (and (member (nd/is-todoitem-p) org-done-keywords) (not (nd/is-closed-heading-p))))
|
(and (member keyword org-done-keywords)
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(not (nd/is-closed-heading-p)))))
|
||||||
|
|
||||||
(defun nd/skip-non-undone-closed-todoitems ()
|
(defun nd/skip-non-undone-closed-todoitems ()
|
||||||
(save-restriction
|
(nd/skip-heading-with
|
||||||
(widen)
|
nd/is-todoitem-p
|
||||||
(if (not (and (not (member (nd/is-todoitem-p)) org-done-keywords) (nd/is-closed-heading-p)))
|
(and (not (member keyword org-done-keywords))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(nd/is-closed-heading-p))))
|
||||||
|
|
||||||
|
(defun nd/skip-non-series-atomic-tasks ()
|
||||||
|
(nd/skip-heading-with
|
||||||
|
nd/is-atomic-task-p
|
||||||
|
(nd/is-series-heading-p)))
|
||||||
|
|
||||||
;; projects
|
;; 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)
|
(defun nd/skip-projects-without-statuscode (statuscode)
|
||||||
(save-restriction
|
(save-restriction
|
||||||
(widen)
|
(widen)
|
||||||
|
(let ((keyword (nd/is-project-p)))
|
||||||
|
;; TODO there may be a way to skip over skippable projects
|
||||||
|
;; and save a few cycles. Not a huge deal, but would require
|
||||||
|
;; keeping the skippable line and then skipping over the others
|
||||||
|
;; in one fell swoop, not easy to do efficiently
|
||||||
|
(if keyword
|
||||||
(if (not (nd/is-project-status-p statuscode))
|
(if (not (nd/is-project-status-p statuscode))
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
(if nd/agenda-limit-project-toplevel
|
||||||
|
(nd/skip-subtree)
|
||||||
;; top-level projects
|
(nd/skip-item)))
|
||||||
(defun nd/skip-subprojects-without-statuscode (statuscode)
|
(nd/skip-item)))))
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (or (nd/heading-has-parent) (not (nd/is-project-status-p statuscode)))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
|
|
||||||
(defun nd/skip-series-projects-without-statuscode (statuscode)
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (not (and (nd/is-series-heading-p) (nd/is-project-status-p statuscode)))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
;; series projects
|
|
||||||
;; defined as project with property Project_type=series
|
|
||||||
;; must have:
|
|
||||||
;; - one level of subtasks
|
|
||||||
;; - all subtasks either TODO/scheduled, NEXT, DONE, CANCELLED
|
|
||||||
;; - at least one TODO/scheduled or NEXT (active) ..else empty
|
|
||||||
;; invalid if:
|
|
||||||
;; - project header is invalid project header (typical rules apply)
|
|
||||||
|
|
||||||
;; archiving
|
|
||||||
(defun nd/skip-non-archivable-atomic-tasks ()
|
|
||||||
(save-restriction
|
|
||||||
(widen)
|
|
||||||
(if (not (nd/is-archivable-atomic-task-p))
|
|
||||||
(save-excursion (or (outline-next-heading) (point-max))))))
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
*** interactive view functions
|
*** interactive view functions
|
||||||
#+BEGIN_SRC emacs-lisp
|
#+BEGIN_SRC emacs-lisp
|
||||||
|
@ -840,8 +879,8 @@ tags in the custom commands section but I find this easier to maintain and possi
|
||||||
|
|
||||||
(defun nd/agenda-base-task-command (keyword skip-fun)
|
(defun nd/agenda-base-task-command (keyword skip-fun)
|
||||||
"shorter syntax to define task agenda commands"
|
"shorter syntax to define task agenda commands"
|
||||||
`(tags-todo
|
`(tags
|
||||||
"-NA-REFILE/!"
|
"-NA-REFILE/"
|
||||||
((org-agenda-overriding-header (concat ,keyword " Tasks"))
|
((org-agenda-overriding-header (concat ,keyword " Tasks"))
|
||||||
(org-agenda-skip-function ,skip-fun)
|
(org-agenda-skip-function ,skip-fun)
|
||||||
(org-agenda-todo-ignore-with-date 'all)
|
(org-agenda-todo-ignore-with-date 'all)
|
||||||
|
@ -855,9 +894,7 @@ tags in the custom commands section but I find this easier to maintain and possi
|
||||||
(and nd/agenda-limit-project-toplevel "Toplevel ")
|
(and nd/agenda-limit-project-toplevel "Toplevel ")
|
||||||
,keyword
|
,keyword
|
||||||
" Projects"))
|
" Projects"))
|
||||||
(org-agenda-skip-function (if nd/agenda-limit-project-toplevel
|
(org-agenda-skip-function '(nd/skip-projects-without-statuscode ,statuscode))
|
||||||
'(nd/skip-subprojects-without-statuscode ,statuscode)
|
|
||||||
'(nd/skip-projects-without-statuscode ,statuscode)))
|
|
||||||
(org-agenda-sorting-strategy '(category-keep)))))
|
(org-agenda-sorting-strategy '(category-keep)))))
|
||||||
|
|
||||||
#+END_SRC
|
#+END_SRC
|
||||||
|
@ -868,10 +905,10 @@ tags in the custom commands section but I find this easier to maintain and possi
|
||||||
`(("t"
|
`(("t"
|
||||||
"Task View"
|
"Task View"
|
||||||
((agenda "" nil)
|
((agenda "" nil)
|
||||||
,(nd/agenda-base-task-command "Next Project" ''nd/skip-non-next-project-tasks)
|
,(nd/agenda-base-task-command "Next Project" ''(nd/skip-non-keyword-project-tasks "NEXT"))
|
||||||
,(nd/agenda-base-task-command "Waiting Project" ''nd/skip-non-waiting-project-tasks)
|
,(nd/agenda-base-task-command "Waiting Project" ''(nd/skip-non-keyword-project-tasks "WAITING"))
|
||||||
,(nd/agenda-base-task-command "Atomic" ''nd/skip-non-atomic-tasks)
|
,(nd/agenda-base-task-command "Atomic" ''nd/skip-non-unclosed-atomic-tasks)
|
||||||
,(nd/agenda-base-task-command "Held Project" ''nd/skip-non-held-project-tasks)))
|
,(nd/agenda-base-task-command "Held Project" ''(nd/skip-non-keyword-project-tasks "HOLD"))))
|
||||||
("o"
|
("o"
|
||||||
"Project Overview"
|
"Project Overview"
|
||||||
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Stuck" :stuck)
|
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Stuck" :stuck)
|
||||||
|
@ -884,11 +921,11 @@ tags in the custom commands section but I find this easier to maintain and possi
|
||||||
((org-agenda-overriding-header "Tasks to Refile"))
|
((org-agenda-overriding-header "Tasks to Refile"))
|
||||||
(org-tags-match-list-sublevels nil))
|
(org-tags-match-list-sublevels nil))
|
||||||
,(nd/agenda-base-task-command "Discontinous Project" ''nd/skip-non-discontinuous-project-tasks)
|
,(nd/agenda-base-task-command "Discontinous Project" ''nd/skip-non-discontinuous-project-tasks)
|
||||||
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/!" "Unmarked Completed" :complete)
|
,(nd/agenda-base-task-command "Undone Closed" ''nd/skip-non-undone-closed-todoitems)
|
||||||
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Invalid" :invalid-todostate)
|
,(nd/agenda-base-task-command "Done Unclosed" ''nd/skip-non-done-unclosed-todoitems)
|
||||||
;; ,(nd/agenda-base-task-command "Done But Not Closed" ''nd/skip-non-done-open-todoitems)
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Undone Completed" :undone-complete)
|
||||||
;; ,(nd/agenda-base-task-command "Closed But Not Done" ''nd/skip-non-open-closed-todoitems)
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Done Incompleted" :done-incomplete)
|
||||||
))
|
,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC-Project_Type=\"series\"/" "Invalid Todostate" :invalid-todostate)))
|
||||||
("s"
|
("s"
|
||||||
"Series projects"
|
"Series projects"
|
||||||
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/!" "Active Series" :active)
|
(,(nd/agenda-base-project-command "-NA-REFILE-ATOMIC+Project_Type=\"series\"/!" "Active Series" :active)
|
||||||
|
@ -935,14 +972,6 @@ the agenda does not do this by default...it's annoying
|
||||||
|
|
||||||
(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
|
||||||
** ui
|
|
||||||
*** bullets
|
|
||||||
#+BEGIN_SRC emacs-lisp
|
|
||||||
(use-package org-bullets
|
|
||||||
:ensure t
|
|
||||||
:config
|
|
||||||
(add-hook 'org-mode-hook (lambda () (org-bullets-mode))))
|
|
||||||
#+END_SRC
|
|
||||||
** caldav
|
** caldav
|
||||||
+BEGIN_SRC emacs-lisp
|
+BEGIN_SRC emacs-lisp
|
||||||
(use-package org-caldav
|
(use-package org-caldav
|
||||||
|
|
Loading…
Reference in New Issue