229 lines
8.4 KiB
EmacsLisp
229 lines
8.4 KiB
EmacsLisp
;;; gtd.el -*- lexical-binding: t; -*-
|
|
|
|
(after! org
|
|
(setq org-return-follows-link t
|
|
org-complete-tags-always-offer-all-agenda-tags t
|
|
org-agenda-files (mapcar 'file-truename
|
|
(file-expand-wildcards (concat org-directory "gtd/*.org"))))
|
|
;; org-agenda-files (append (mapcar 'file-truename
|
|
;; (file-expand-wildcards (concat org-directory "gtd/*.org")))
|
|
;; (list (expand-file-name (funcall #'gtd-get-current-journal) org-directory))))
|
|
(setq org-capture-templates
|
|
`(("i" "Inbox" entry (file "gtd/inbox.org")
|
|
,(concat "* %?\n"
|
|
":PROPERTIES:\n:CREATED: %U\n:END:") :prepend t)
|
|
;; ("j" "Journal" entry (file+datetree ,(funcall #'gtd-get-current-journal))
|
|
("j" "Journal" entry (file+datetree "gtd/journal.org")
|
|
,(concat "* %?\n"
|
|
":PROPERTIES:\n:CREATED: %U\n:END:"))
|
|
("@" "Inbox [mu4e]" entry (file "gtd/inbox.org")
|
|
;; "* Process [[mu4e:msgid:%:message-id][%:fromname - %:subject]] :email:\n%U\n"))
|
|
,(concat "* Process %a %?\n"
|
|
":PROPERTIES:\n:CREATED: %U\n:END:") :prepend t)))
|
|
|
|
(setq org-refile-allow-creating-parent-nodes 'confirm
|
|
org-refile-target-verify-function (lambda (&rest _) (null (org-get-todo-state)))
|
|
;; org-refile-target-verify-function nil
|
|
;; org-refile-targets '(("tasks.org" . (:maxlevel . 2))))
|
|
org-refile-targets '(("tasks.org" . (:maxlevel . 2))
|
|
("someday.org" . (:level . 0)))))
|
|
|
|
|
|
|
|
;; Getting the following on capture:
|
|
;;
|
|
;; Greedy org-protocol handler. Killing client.
|
|
;; No server buffers remain to edit
|
|
;;
|
|
;; See https://github.com/alphapapa/org-protocol-capture-html/issues/40
|
|
;; (use-package! org-protocol-capture-html
|
|
;; :after org-protocol
|
|
;; :config
|
|
;; (add-to-list 'org-capture-templates
|
|
;; '("w" "Website" entry (file "")
|
|
;; "* %a :website:\n\n%U %?\n\n%:initial"))
|
|
|
|
(defun gtd-capture-inbox ()
|
|
(interactive)
|
|
(org-capture nil "i"))
|
|
|
|
(map! "C-c i" #'gtd-capture-inbox)
|
|
|
|
(defun gtd-capture-journal ()
|
|
(interactive)
|
|
(org-capture nil "j"))
|
|
|
|
(map! "C-c j" #'gtd-capture-journal)
|
|
|
|
(defun gtd-capture-email ()
|
|
(interactive)
|
|
(call-interactively 'org-store-link)
|
|
(org-capture nil "@"))
|
|
|
|
(map! :after mu4e
|
|
:map (mu4e-headers-mode-map mu4e-view-mode-map)
|
|
:vne "C-c p" #'gtd-capture-email)
|
|
|
|
;; Insert CREATED property timestamps for non-captured headlines
|
|
(use-package! org-expiry
|
|
:after org
|
|
:config
|
|
(map! :map org-mode-map
|
|
"C-c c" #'org-expiry-insert-created)
|
|
(setq org-expiry-inactive-timestamps t))
|
|
|
|
;; Automatically trigger state changes in projects
|
|
(use-package! org-edna
|
|
:after org
|
|
:config
|
|
(org-edna-mode)
|
|
(setq org-edna-use-inheritance t))
|
|
|
|
(use-package! org-super-agenda
|
|
:after org
|
|
:config
|
|
(org-super-agenda-mode)
|
|
(setq org-agenda-window-setup 'current-window
|
|
org-agenda-restore-windows-after-quit t
|
|
org-agenda-block-separator nil
|
|
org-agenda-compact-blocks t
|
|
org-agenda-start-with-log-mode t
|
|
org-agenda-hide-tags-regexp "."
|
|
org-super-agenda-header-map (make-sparse-keymap)
|
|
org-agenda-custom-commands
|
|
'(("g" "Get Things Done (GTD)"
|
|
((agenda ""
|
|
((org-agenda-skip-function
|
|
'(org-agenda-skip-entry-if 'deadline))
|
|
(org-deadline-warning-days 0)))
|
|
;; TODO combine the two queries into one
|
|
(tags "inbox" ((org-agenda-overriding-header "")
|
|
(org-agenda-prefix-format " %?-14t% s")
|
|
(org-super-agenda-groups
|
|
'((:name "Inbox"
|
|
:anything t)))))
|
|
(alltodo "" ((org-agenda-overriding-header "")
|
|
(org-agenda-prefix-format " %i %-14:c ")
|
|
;; (org-agenda-sorting-strategy '(category-up))
|
|
(org-agenda-skip-function
|
|
'(org-agenda-skip-entry-if 'scheduled))
|
|
(org-super-agenda-groups
|
|
'((:name "Deadlines"
|
|
:deadline t)
|
|
(:name "Tasks"
|
|
:todo "NEXT")
|
|
(:name "Blocked/Delegated"
|
|
:todo "WAIT")
|
|
(:name "Completed today"
|
|
:log 'closed)
|
|
(:discard (:anything t)))))))))))
|
|
|
|
;; Automatically save gtd files after some actions on them
|
|
;; Source: https://www.labri.fr/perso/nrougier/GTD/index.html
|
|
(defun gtd-save-org-buffers ()
|
|
"Save `org-agenda-files' buffers without user confirmation.
|
|
See also `org-save-all-org-buffers'"
|
|
(interactive)
|
|
(message "Saving org-agenda-files buffers...")
|
|
(save-some-buffers t (lambda ()
|
|
(when (member (buffer-file-name) org-agenda-files)
|
|
t)))
|
|
(message "Saving org-agenda-files buffers... done"))
|
|
|
|
(after! org
|
|
(advice-add 'org-refile :after (lambda (&rest _) (gtd-save-org-buffers)))
|
|
(advice-add 'org-todo :after (lambda (&rest _) (gtd-save-org-buffers)))
|
|
(advice-add 'org-add-note :after (lambda (&rest _) (gtd-save-org-buffers)))
|
|
(advice-add 'org-clock-in :after (lambda (&rest _) (gtd-save-org-buffers))))
|
|
|
|
(defun gtd-agenda ()
|
|
(interactive)
|
|
(org-agenda nil "g"))
|
|
|
|
(map! :map 'override
|
|
"<f1>" #'gtd-agenda)
|
|
|
|
;;
|
|
;;; Archiving
|
|
|
|
;; Store archived entries in <current_file>.org_archive in a datetree
|
|
(after! org
|
|
(setq org-archive-location "%s_archive::datetree/"))
|
|
|
|
;;
|
|
;;; Calendar
|
|
|
|
(defun max/khalel--insert-import-file-header (sdate edate)
|
|
"Insert imported events file header information into current buffer.
|
|
SDATE and EDATE denote the start and end dates, respectively, of
|
|
the current import date range."
|
|
(when khalel-import-org-file-read-only
|
|
(insert "# -*- buffer-read-only: 1; -*-\n"))
|
|
(insert khalel-import-org-file-header)
|
|
(insert (format "*Events scheduled between %s and %s*:\n" sdate edate)))
|
|
|
|
(use-package! khalel
|
|
:after org
|
|
:config
|
|
(advice-add #'khalel--insert-import-file-header :override #'max/khalel--insert-import-file-header)
|
|
(advice-add #'khalel-import-events :around #'doom-shut-up-a)
|
|
(setq khalel-default-calendar "personal"
|
|
khalel-import-org-file-confirm-overwrite nil
|
|
khalel-import-start-date "-15d"
|
|
khalel-import-end-date "+30d"
|
|
khalel-import-org-file (expand-file-name "gtd/calendar.org" org-directory)
|
|
khalel-import-format "* {title} {cancelled} :{calendar}:\n\
|
|
:PROPERTIES:\n:CALENDAR: {calendar}\n\
|
|
:LOCATION: {location}\n\
|
|
:ID: {uid}\n\
|
|
:END:\n\
|
|
<{start-date-long} {start-time}>--<{end-date-long} {end-time}>\n\
|
|
{description}\n\
|
|
[[elisp:(khalel-edit-calendar-event)][Edit this event]]\
|
|
[[elisp:(progn (khalel-run-vdirsyncer) (khalel-import-events))]\
|
|
[Sync and update all]]\n"
|
|
khalel-import-org-file-header "#+TITLE: Calendar\n\n\
|
|
*NOTE*: this file has been generated by \
|
|
[[elisp:(khalel-import-events)][khalel-import-events]] \
|
|
and /any changes to this document will be lost on the next import/!
|
|
Instead, use =khalel-edit-calendar-event= or =khal edit= to edit the \
|
|
underlying calendar entries, then re-import them here.
|
|
|
|
You can use [[elisp:(khalel-run-vdirsyncer)][khalel-run-vdirsyncer]] \
|
|
to synchronize with remote calendars.
|
|
|
|
Consider adding this file to your list of agenda files so that events \
|
|
show up there.\n\n"))
|
|
|
|
(after! org
|
|
(khalel-add-capture-template))
|
|
|
|
(defun gtd-capture-event ()
|
|
(interactive)
|
|
(org-capture nil "e"))
|
|
|
|
(map! "C-c e" #'gtd-capture-event)
|
|
|
|
(after! org
|
|
(setq org-clock-persist t
|
|
;; Useful when clocking in on a waiting task
|
|
org-clock-in-switch-to-state "NEXT"
|
|
org-clock-out-when-done '("DONE" "CNCL" "WAIT")
|
|
org-clock-persist-query-resume nil))
|
|
|
|
(use-package! org-clock-convenience
|
|
:after org
|
|
:config
|
|
;; Fix for macos: https://github.com/dfeich/org-clock-convenience/issues/9#issuecomment-646842338
|
|
(when IS-MAC (load "org-clock-convenience"))
|
|
(map! :map org-agenda-mode-map
|
|
"<S-up>" #'org-clock-convenience-timestamp-up
|
|
"<S-down>" #'org-clock-convenience-timestamp-down
|
|
"o" #'org-clock-convenience-fill-gap
|
|
"e" #'org-clock-convenience-fill-gap-both))
|
|
|
|
(after! org-pomodoro
|
|
(setq org-pomodoro-length 25
|
|
org-pomodoro-short-break-length 5
|
|
org-pomodoro-long-break-length 20))
|