42 KiB
- include-file-as-forms
- use-package
- My Customization Variables
- Bindings
- Org Variable Customization
- Config
- Miscellaneous Functions
- Reschedule Past Items to Today
- Miscellaneous
- Hooks
- Modules
- Assignee/Completer
- Agenda
- Archive
- Capture
- Babel
- frame-mode handling
- Disable yasnippet in org-mode
- Set Background Color of Source Blocks for Export
- Use my own default naming scheme for org-headings
- Add link icons in headings that lead to themselves
- Allow with query params in image extentions
- Use org-tempo to allow inserting templates using e.g. <s
- Packages
include-file-as-forms
We're going to use this to write separate parts of our config to different sections that will then be provided to our use package statment
(defun imalison:include-file-as-forms (filename)
"Include the contents of FILENAME into the source as s-expressions."
(with-temp-buffer
(insert-file-contents-literally filename)
(goto-char (point-min))
(let (forms form)
(condition-case nil
(while t
(setq form (read (current-buffer)))
(push form forms))
(end-of-file nil))
`,@(nreverse forms))))
use-package
(defmacro imalison:org-config ()
`(use-package org
:ensure nil
:commands (org-mode org org-mobile-push org-mobile-pull org-agenda)
:mode ("\\.org\\'" . org-mode)
:preface (progn ,@(imalison:include-file-as-forms (imalison:join-paths user-emacs-directory "org-config-preface.el")))
:custom ,(imalison:include-file-as-forms (imalison:join-paths user-emacs-directory "org-config-custom.el"))
:config (progn ,@(imalison:include-file-as-forms (imalison:join-paths user-emacs-directory "org-config-config.el")))
:bind (,(imalison:include-file-as-forms (imalison:join-paths user-emacs-directory "org-config-bind.el")))))
(imalison:org-config)
My Customization Variables
;; Load org-config-preface when org-config.el is loaded directly.
(let ((preface (expand-file-name "org-config-preface.el" user-emacs-directory)))
(when (file-exists-p preface)
(load-file preface)))
;; Fallbacks in case preface isn't available yet.
(unless (boundp 'imalison:org-dir)
(defvar imalison:org-dir "~/org"))
(unless (boundp 'imalison:created-property-string)
(defvar imalison:created-property-string "
:PROPERTIES:
:ID: %(org-id-new)
:CREATED: %U
:END:"))
(defvar imalison:org-dir "~/org")
(defvar imalison:shared-org-dir "~/org")
(defvar imalison:org-whoami "Ivan Malison")
(defvar imalison:org-people (list nil "Ivan Malison" "Kat Huang"))
(defvar imalison:org-people-associated-states
'(("Ivan Malison" . ("IVANDONE"))
("Kat Huang" . ("KATDONE"))))
(defvar imalison:org-default-initial-state "TODO")
(defvar imalison:org-gtd-file
(imalison:join-paths imalison:org-dir "gtd.org"))
(defvar imalison:org-habits-file
(imalison:join-paths imalison:org-dir "habits.org"))
(defvar imalison:org-calendar-file
(imalison:join-paths imalison:org-dir "calendar.org"))
(defvar imalison:org-inbox-file
(imalison:join-paths imalison:org-dir "inbox.org"))
(defvar imalison:shared-org-gtd-file
(imalison:join-paths imalison:shared-org-dir "shared_gtd.org"))
(defvar imalison:shared-habits-file
(imalison:join-paths imalison:shared-org-dir "shared_habits.org"))
(defvar imalison:shared-calendar-file
(imalison:join-paths imalison:shared-org-dir "shared_calendar.org"))
(defvar imalison:shared-shopping-file
(imalison:join-paths imalison:shared-org-dir "shared_shopping.org"))
(defvar imalison:shared-repeating-file
(imalison:join-paths imalison:shared-org-dir "repeating.org"))
(defvar imalison:orgzly-files
(list (imalison:join-paths imalison:shared-org-dir "kat_orgzly.org")
(imalison:join-paths imalison:shared-org-dir "ivan_orgzly.org")
(imalison:join-paths imalison:org-dir "orgzly.org")))
(defvar imalison:repeating-org-files
(list imalison:org-habits-file imalison:shared-habits-file imalison:shared-repeating-file))
(defvar imalison:include-repeating-in-agenda t)
(defun imalison:add-to-org-agenda-files (incoming-files)
(setq org-agenda-files
(delete-dups
(cl-loop for filepath in (append org-agenda-files incoming-files)
when (and filepath (file-exists-p (file-truename filepath)))
collect (file-truename filepath)))))
(defvar imalison:created-property-string "
:PROPERTIES:
:ID: %(org-id-new)
:CREATED: %U
:END:")
Bindings
("C-c c" . org-capture)
:map org-mode-map
(("C-e" . end-of-visual-line)
("C-c n t" . org-insert-todo-heading)
("C-c n s" . org-insert-todo-subheading)
("C-c n h" . org-insert-habit)
("C-c n m" . org-make-habit)
("C-c n l" . org-store-link)
("C-c n i" . org-insert-link)
("C-c C-t" . org-todo)
("C-c C-S-t" . org-todo-force-notes)
("M-." . elisp-slime-nav-find-elisp-thing-at-point))
Org Variable Customization
(org-agenda-log-mode-items '(closed state))
(org-agenda-skip-deadline-if-done t)
(org-agenda-skip-scheduled-if-done t)
(org-agenda-span 10)
(org-agenda-start-day "-0d")
(org-agenda-timegrid-use-ampm 1)
(org-agenda-window-setup 'other-window)
(org-columns-default-format "%80ITEM(Task) %10Effort(Effort){:} %10CLOCKSUM")
(org-deadline-warning-days 0)
(org-default-priority ?C)
(org-edit-src-content-indentation 0)
(org-enforce-todo-dependencies t)
(org-export-headline-levels 3)
(org-fold-catch-invisible-edits 'show)
(org-goto-interface 'outline-path-completion)
(org-goto-max-level 10)
(org-habit-graph-column 60)
(org-habit-show-all-today nil)
(org-habit-show-habits-only-for-today t)
(org-imenu-depth 10)
(org-log-into-drawer t)
(org-log-redeadline nil)
(org-log-reschedule nil)
(org-lowest-priority 69) ;; The character E
(org-outline-path-complete-in-steps nil)
(org-refile-allow-creating-parent-nodes t)
(org-refile-use-outline-path 'file)
(org-src-fontify-natively t)
(org-src-preserve-indentation t)
(org-startup-folded t)
(org-startup-indented nil)
(org-todo-repeat-to-state "TODO")
(org-treat-insert-todo-heading-as-state-change t)
(org-refile-targets '((org-agenda-files . (:maxlevel . 2))
(org-agenda-files . (:level . 0))))
(org-global-properties
'(quote (("Effort_ALL" . "0:15 0:30 0:45 1:00 2:00 3:00 4:00 5:00 6:00 0:00")
("STYLE_ALL" . "habit"))))
(org-todo-keywords
'((sequence "INBOX(i!)" "TODO(t!)" "NEXT(n!)" "STARTED(s!)" "WAIT(w!)"
"BACKLOG(b!)" "|" "DONE(d!)" "HANDLED(h!)" "EXPIRED(e!)"
"CANCELED(c!)")))
Skip showing deadline when we are scheduled
(org-agenda-skip-deadline-prewarning-if-scheduled t)
Config
Miscellaneous Functions
(require 'cl-lib)
(require 'org-habit)
(defun imalison:maybe-symbol-name (arg)
(if (symbolp arg)
(symbol-name arg)
arg))
(defun imalison:set-display-custom-times ()
(setq org-display-custom-times nil))
(when (not (fboundp 'org-is-habit-p))
(defun org-is-habit-p ()
(string-equal (org-entry-get nil "STYLE") "habit")))
(defun org-todo-force-notes ()
(interactive)
(let ((org-todo-log-states
(mapcar (lambda (state)
(list state 'note 'time))
(apply 'append org-todo-sets))))
(cond ((eq major-mode 'org-mode) (org-todo))
((eq major-mode 'org-agenda-mode) (org-agenda-todo)))))
(defun org-make-habit ()
(interactive)
(org-set-property "STYLE" "habit"))
(defun org-insert-habit ()
(interactive)
(org-insert-todo-heading nil)
(org-make-habit))
(defun org-todo-at-date (date)
(interactive (list (org-time-string-to-time (org-read-date))))
(flet ((org-current-effective-time (&rest r) date)
(org-today (&rest r) (time-to-days date)))
(cond ((eq major-mode 'org-mode) (org-todo))
((eq major-mode 'org-agenda-mode) (org-agenda-todo)))))
(defun imalison:lower-todo-priorities ()
"Lower the priority of each TODO heading in all org-agenda files by 1."
(interactive)
(org-map-entries
(lambda ()
(when (org-entry-is-todo-p)
(org-priority-down)))
nil
'agenda))
(cl-defun imalison:org-at-time (&key time (fn 'org-wild-notifier-check))
(interactive)
(setq time (or time (time-convert (encode-time (parse-time-string (org-read-date))) 'list)))
(message "%s" time)
(flet
((current-time (&rest args) time))
(funcall fn)))
Reschedule Past Items to Today
This function finds all incomplete items in the agenda files that are scheduled in the past and reschedules them to today. Useful for catching up on tasks that have slipped past their original scheduled date.
(defun imalison:reschedule-past-to-today ()
"Reschedule all incomplete items scheduled in the past to today.
Iterates through all org-agenda files and reschedules any TODO items
that have a SCHEDULED date before today to the current date."
(interactive)
(let* ((today-time (org-time-string-to-time (format-time-string "<%Y-%m-%d>")))
(rescheduled-count 0))
(org-map-entries
(lambda ()
(let* ((scheduled (org-entry-get nil "SCHEDULED"))
(scheduled-time (when scheduled (org-time-string-to-time scheduled))))
(when (and scheduled-time
(not (org-entry-is-done-p))
(time-less-p scheduled-time today-time))
(org-schedule nil ".")
(cl-incf rescheduled-count))))
nil
'agenda)
(message "Rescheduled %d items to today" rescheduled-count)))
imalison:reschedule-past-to-today
Miscellaneous
(add-to-list 'org-show-context-detail '(org-goto . lineage))
(add-to-list 'org-src-lang-modes '("plantuml" . plantuml))
(define-key mode-specific-map [?a] 'org-agenda)
(unbind-key "C-j" org-mode-map)
Hooks
;; TODO why is this set
(add-hook 'org-mode-hook (lambda () (setq org-todo-key-trigger t)))
Disables
(add-hook 'org-agenda-mode-hook 'imalison:set-display-custom-times)
(add-hook 'org-agenda-mode-hook (lambda () (eldoc-mode -1)))
auto-revert-mode
(add-hook 'org-mode-hook (lambda () (auto-revert-mode +1)))
(add-to-list 'revert-without-query "\\.org\\'")
Modules
(add-to-list 'org-modules 'org-habit)
Assignee/Completer
(defmacro imalison:def-agenda-pred (&rest forms)
`(lambda ()
(unless ,@forms
(or (outline-next-heading)
(point-max)))))
(defun imalison:org-known-assignees ()
imalison:org-people)
(defun imalison:set-person-for-prop (&rest args)
(interactive)
(if (eq major-mode 'org-agenda-mode)
(org-agenda-with-point-at-orig-entry nil
(apply 'imalison--set-person-for-prop args))
(apply 'imalison--set-person-for-prop args)))
(cl-defun imalison--set-person-for-prop (&key assignee (override t) (property "ASSIGNEE"))
(interactive)
(let ((chosen-person
(if (null assignee)
(completing-read "Choose person: "
(imalison:org-known-assignees)
nil t)
(or assignee imalison:org-whoami))))
(when (or override (not (org-entry-get nil property)))
(org-set-property property chosen-person))))
(defalias 'imalison:set-assignee 'imalison:set-person-for-prop)
(defun imalison:set-completer (&rest args)
(interactive)
(apply 'imalison:set-person-for-prop :property "COMPLETER" args))
(defun imalison:assign-to-self-if-unassigned ()
(interactive)
(imalison:set-assignee :assignee imalison:org-whoami :override nil))
(defun imalison:shared-org-file-p ()
(string-prefix-p (file-truename imalison:shared-org-dir)
(file-truename default-directory)))
(defun imalison:habit-or-repeating-heading ()
(org-is-habit-p))
(defun imalison:shared-non-habit-p ()
(and (not (imalison:habit-or-repeating-heading))
(imalison:shared-org-file-p)))
(defvar imalison:auto-assign-to-self-predicates
(list 'imalison:shared-non-habit-p))
(defun imalison:auto-assign-to-self-when ()
(cl-loop for pred in imalison:auto-assign-to-self-predicates
when (funcall pred)
return t
finally return nil))
(defun imalison:maybe-auto-assign-to-self (&rest args)
(when (imalison:auto-assign-to-self-when)
(imalison:assign-to-self-if-unassigned)))
(advice-add 'org-schedule :after 'imalison:maybe-auto-assign-to-self)
(defun imalison:relevant-to (person)
(let ((assignee (org-entry-get nil "ASSIGNEE"))
(completer (org-entry-get nil "COMPLETER")))
(or
(null person)
(string-equal assignee person)
(string-equal completer person)
(and
(null (or assignee completer))
(or
(imalison:shared-org-file-p)
(equal person imalison:org-whoami))))))
(defmacro imalison:assigned-to-me ()
`(let ((assignee (org-entry-get nil "ASSIGNEE")))
(or (string-equal assignee imalison:org-whoami)
(null assignee))))
(defalias 'imalison:assigned-to-me-agenda-pred
(imalison:def-agenda-pred
(imalison:assigned-to-me)))
Add a hook to automatically set completer
(defun imalison:maybe-add-completer (state-change)
(when
(and (eq (plist-get state-change :type) 'todo-state-change)
(member (plist-get state-change :to) org-done-keywords)
(imalison:shared-non-habit-p)
(null (org-entry-get nil "COMPLETER")))
(org-set-property "COMPLETER" imalison:org-whoami)))
(add-hook 'org-trigger-hook 'imalison:maybe-add-completer)
Tools for controlling the log book
(defvar imalison:org-agenda-filter-to nil)
(defun imalison:org-agenda-filter-log-to-completions-for (person)
(interactive (list (completing-read "Choose a person" imalison:org-people)))
(when (string-equal person "nil")
(setq person nil))
(setq imalison:org-agenda-filter-to person)
(when (eq major-mode 'org-agenda-mode)
(org-agenda-redo)))
(defun imalison:state-belongs-to (state person)
(member
state (cdr (assoc person imalison:org-people-associated-states))))
(defun imalison:state-relevant-to (state person)
(not
(member
state
(--mapcat it (--filter (not (equal (car it) person))
imalison:org-people-associated-states)))))
(defun org-agenda-states-filter (state &optional marker)
(and (member state org-done-keywords-for-agenda)
(or (null imalison:org-agenda-filter-to)
(save-excursion
(set-buffer (marker-buffer marker))
(goto-char (marker-position marker))
(or
(imalison:state-belongs-to state imalison:org-agenda-filter-to)
(and
(imalison:relevant-to imalison:org-agenda-filter-to)
(imalison:state-relevant-to state imalison:org-agenda-filter-to)))))))
Agenda
(require 'org-agenda)
Agenda Files
(imalison:add-to-org-agenda-files
(nconc (list imalison:org-gtd-file imalison:org-calendar-file
imalison:org-inbox-file imalison:shared-org-gtd-file
imalison:shared-calendar-file imalison:shared-shopping-file)
imalison:orgzly-files))
(when imalison:include-repeating-in-agenda
(imalison:add-to-org-agenda-files imalison:repeating-org-files))
Predicates
(defun org-get-priority-at-point ()
(save-excursion
(beginning-of-line)
(org-back-to-heading t)
(when (looking-at org-priority-regexp)
(let ((ms (match-string 2)))
(org-priority-to-value ms)))))
(defmacro imalison:def-agenda-priority-pred (priority)
`(imalison:def-agenda-pred
(>= (org-get-priority-at-point) ,priority)))
(cl-defun imalison:org-time-condition-met-p (&key (property "CREATED") (days 30) (future nil))
(let* ((property-value (org-entry-get (point) property))
(comparison-time
(if future
(time-add (current-time) (days-to-time days))
(time-subtract (current-time) (days-to-time days))))
(formatted-time-string (format-time-string "<%Y-%m-%d %H:%M>" comparison-time))
(compare-time (org-time-string-to-time formatted-time-string))
(node-time (when property-value (org-time-string-to-time property-value))))
(when node-time
(if future
(time-less-p node-time compare-time)
(time-less-p compare-time node-time)))))
(defun org-cmp-creation-times (a b)
(let ((a-created (get-date-created-from-agenda-entry a))
(b-created (get-date-created-from-agenda-entry b)))
(imalison:compare-int-list a-created b-created)))
Transient support
(when (fboundp 'org-agenda-transient)
(bind-key "C-c a" 'org-agenda-transient))
Agenda Commands (Views)
(let ((high-priority
;; The < in the following line has behavior that is opposite
;; to what one might expect.
`(tags-todo
"+PRIORITY<\"C\""
((org-agenda-overriding-header "High priority tasks:")
(org-agenda-skip-function
,(imalison:def-agenda-pred
(not (org-is-habit-p))))
)))
(due-today
`(alltodo
""
((org-agenda-overriding-header "Due today:")
(org-agenda-skip-function
,(imalison:def-agenda-pred
(or
(imalison:org-time-condition-met-p
:property "DEADLINE" :days 0 :future t)
(imalison:org-time-condition-met-p
:property "SCHEDULED" :days 0 :future t)))))))
(all-habits
`(agenda
""
((org-agenda-overriding-header "Habits:")
(org-agenda-ndays 1)
(org-agenda-span 1)
(org-agenda-use-time-grid nil)
(org-agenda-skip-function
,(imalison:def-agenda-pred
(org-is-habit-p)))
(org-habit-show-all-today t))))
(recently-created
`(alltodo
""
((org-agenda-overriding-header "Recently Created:")
(org-agenda-skip-function
,(imalison:def-agenda-pred
(imalison:org-time-condition-met-p :days 10)))
(org-agenda-cmp-user-defined 'org-cmp-creation-times)
(org-agenda-sorting-strategy '(user-defined-down)))))
(next '(todo "NEXT"))
(started '(todo "STARTED"))
(inbox '(todo "INBOX"))
(missing-deadline
'(tags-todo "-DEADLINE={.}/!"
((org-agenda-overriding-header
"These don't have deadlines:"))))
(missing-priority
'(tags-todo "-PRIORITY={.}/!"
((org-agenda-overriding-header
"These don't have priorities:"))))
(high-priority-unscheduled
'(tags-todo "+PRIORITY<\"C\"-SCHEDULED={.}-DEADLINE={.}/!"
((org-agenda-overriding-header
"High priority without scheduled/deadline:")))))
(setq org-agenda-custom-commands
`(("M" "Main agenda view"
((agenda ""
((org-agenda-overriding-header "Agenda:")
(org-agenda-ndays 5)
(org-deadline-warning-days 0)
(org-agenda-skip-function 'imalison:assigned-to-me-agenda-pred)))
,high-priority
,all-habits
,next
,recently-created
,inbox)
nil nil)
,(cons "A" (cons "High priority" high-priority))
,(cons "d" (cons "Overdue tasks and due today" due-today))
,(cons "r" (cons "Recently created" recently-created))
("h" "A, B priority:" tags-todo "+PRIORITY<\"C\""
((org-agenda-overriding-header
"High Priority:")))
("c" "At least priority C:" tags-todo "+PRIORITY<\"D\""
((org-agenda-overriding-header
"At least priority C:")))
("l" "Completions"
. (agenda ""
((org-agenda-overriding-header "Completion History:")
(org-agenda-ndays 5)
(org-agenda-entry-types '(:none))
(org-agenda-start-with-log-mode '(state)))))
,(cons "i" (cons "Inbox" inbox))
,(cons "n" (cons "Next" next))
("w" "Wait" todo "WAIT"
((org-agenda-overriding-header "Waiting:")))
,(cons "u" (cons "High priority unscheduled" high-priority-unscheduled)))))
Filters
Regexp Presets
(defvar imalison:org-agenda-regexp-presets
`(("incomplete" . (lambda ()
(concat "-"
(rx--to-expr (cons 'or org-done-keywords-for-agenda)))))))
(defun imalison:org-agenda-filter-by-regexp-preset ()
(interactive)
(let* ((regex-fn
(cdr (assoc (completing-read "Select a preset:"
imalison:org-agenda-regexp-presets)
imalison:org-agenda-regexp-presets)))
(new-regex (funcall regex-fn)))
(push new-regex org-agenda-regexp-filter)
(org-agenda-filter-apply org-agenda-regexp-filter 'regexp)))
Binding
:map org-agenda-mode-map
(("p" . imalison:org-agenda-filter-by-regexp-preset))
Sorting
I don't want habits to be sorted separately. If they are scheduled for a specific time, they should appear in the agenda at that time!
(let ((entry (assoc 'agenda org-agenda-sorting-strategy)))
(setf (cdr entry) (remove 'habit-down (cdr entry))))
(let ((entry (assoc 'agenda org-agenda-sorting-strategy)))
(setf (cdr entry) (remove 'habit-up (cdr entry))))
Archive
(defun org-archive-if (condition-function)
(if (funcall condition-function)
(let ((next-point-marker
(save-excursion (org-forward-heading-same-level 1) (point-marker))))
(org-archive-subtree)
(setq org-map-continue-from (marker-position next-point-marker)))))
(defun org-archive-if-completed ()
(interactive)
(org-archive-if 'org-entry-is-done-p))
(defun org-archive-completed-in-buffer ()
(interactive)
(org-map-entries 'org-archive-if-completed))
(defun org-archive-all-in-buffer ()
(interactive)
(org-map-entries 'org-archive-subtree))
Capture
Helper Functions
(cl-defun imalison:make-org-template (&key (content "%?"))
(with-temp-buffer
(org-mode)
(insert content)
(org-set-property "ID" (org-id-new))
(org-set-property "CREATED"
(with-temp-buffer
(org-insert-time-stamp
(org-current-effective-time) t t)))
(buffer-substring-no-properties (point-min) (point-max))))
(defun imalison:make-org-template-from-file (filename)
(imalison:make-org-template (imalison:get-string-from-file filename)))
(cl-defun imalison:make-org-todo-template
(&key (content "%?") (creation-state imalison:org-default-initial-state))
(with-temp-buffer
(org-mode)
(org-insert-heading)
(insert content)
(org-todo creation-state)
(org-set-property "ID" (org-id-new))
(org-set-property "CREATED"
(with-temp-buffer
(org-insert-time-stamp
(org-current-effective-time) t t)))
(remove-hook 'post-command-hook 'org-add-log-note)
(let ((org-log-note-purpose 'state)
(org-log-note-return-to (point-marker))
(org-log-note-marker (progn (goto-char (org-log-beginning t))
(point-marker)))
(org-log-note-state creation-state))
(org-add-log-note))
(buffer-substring-no-properties (point-min) (point-max))))
(defun imalison:make-org-linked-todo-template ()
(imalison:make-org-todo-template "[#C] %? %A"))
Journal
(defun imalison:journal-filepath-for-date (&optional date)
(interactive (list (org-read-date)))
(let ((date-str (or date (format-time-string "%Y-%m-%d"))))
(imalison:join-paths
org-directory "journal" (concat date-str ".org"))))
(defun imalison:open-todays-org-journal ()
(interactive)
(imalison:open-org-journal (format-time-string "%Y-%m-%d")))
(defvar imalison:journal-template-filepath
(imalison:join-paths org-directory "capture-templates" "journal.org"))
(defun imalison:get-journal-template ()
(with-temp-buffer
(insert-file-contents imalison:journal-template-filepath)
(buffer-string)))
(defun imalison:open-org-journal (&optional date)
(interactive (list (org-read-date nil nil nil "Select a date:")))
(let* ((filepath (imalison:journal-filepath-for-date date))
(file-existed (file-exists-p filepath))
(date-str (or date (format-time-string "%Y-%m-%d")))
(time-vals (append '(0 0 0) (nthcdr 3 (parse-time-string date-str))))
(original-format-time-string (symbol-function 'format-time-string)))
(find-file filepath)
(when (not file-existed)
(cl-letf (((symbol-function 'format-time-string)
(lambda (format-string &optional _time _universal)
(funcall original-format-time-string format-string (apply #'encode-time time-vals)))))
(insert (org-capture-fill-template (imalison:get-journal-template)))))))
(bind-key "C-c j" 'imalison:open-todays-org-journal)
Templates
(use-package org-capture
:ensure nil
:config
(add-to-list 'org-capture-templates
`("t" "GTD Todo (Linked)" entry (file ,imalison:org-gtd-file)
(function imalison:make-org-linked-todo-template)))
(add-to-list 'org-capture-templates
`("g" "GTD Todo" entry (file ,imalison:org-gtd-file)
(function imalison:make-org-todo-template)))
(add-to-list 'org-capture-templates
`("s" "Shared GTD Todo" entry (file ,imalison:shared-org-gtd-file)
(function imalison:make-org-todo-template)))
(add-to-list 'org-capture-templates
`("y" "Calendar entry (Linked)" entry
(file ,imalison:org-calendar-file)
,(format "%s%s\n%s" "* %? %A" imalison:created-property-string "%^T")))
(add-to-list 'org-capture-templates
`("c" "Calendar entry" entry
(file ,imalison:org-calendar-file)
,(format "%s\n%s\n%s" "* %?" imalison:created-property-string "%^T")))
(add-to-list 'org-capture-templates
`("z" "Shopping Todo" entry (file ,imalison:shared-shopping-file)
(function (lambda (&rest args) (imalison:make-org-todo-template :creation-state "TODO")))))
(add-to-list 'org-capture-templates
`("h" "Habit" entry (file ,imalison:org-habits-file)
"* TODO
SCHEDULED: %^t
:PROPERTIES:
:ID: %(org-id-new)
:CREATED: %U
:STYLE: habit
:END:"))
(add-to-list 'org-capture-templates
'("w" "Weekly Planning and Self Assesment"
plain
(function (lambda ()
(find-file (let ((date (format-time-string "%Y-%m-%d")))
(expand-file-name (concat date ".org")
"~/org/weekly")))))
(file "~/org/capture-templates/weekly.org")))
(add-to-list 'org-capture-templates
`("n" "Next (Scheduled Today)" entry (file ,imalison:org-gtd-file)
,(format "%s%s\n%s" "* NEXT %?" imalison:created-property-string
"SCHEDULED: %(format-time-string \"<%Y-%m-%d %a>\")")))
(add-to-list 'org-capture-templates
`("i" "Inbox" entry (file ,imalison:org-gtd-file)
(function (lambda (&rest args) (imalison:make-org-todo-template :creation-state "INBOX"))))))
Babel
(add-hook 'org-mode-hook 'imalison:load-babel-languages)
(defun imalison:load-babel-languages ()
(let* ((loaded-ob (or (require 'ob-sh nil t) (require 'ob-shell nil t)))
(ob-shell-name
(when loaded-ob
(intern (substring-no-properties (imalison:maybe-symbol-name loaded-ob) 3))))
(added-modes (when ob-shell-name `((,ob-shell-name . t)))))
(org-babel-do-load-languages
'org-babel-load-languages
`((python . t)
(ruby . t)
(octave . t)
(plantuml . t)
(js . t)
,@added-modes))))
(use-package ob-typescript
:config
(progn
(org-babel-do-load-languages
'org-babel-load-languages '((typescript . t)))))
(use-package ob-mermaid
:config
(org-babel-do-load-languages
'org-babel-load-languages '((mermaid . t))))
frame-mode handling
This block is part of the runtime config. It is tangled via the default `header-args:emacs-lisp` for this file (see the `#+PROPERTY` near the top).
(use-package org
:ensure nil
:after frame-mode
:config
(progn
(defun imalison:org-frame-mode-hook ()
(setq org-src-window-setup 'current-window)
(when frame-mode
(progn
(setcdr (assoc 'file org-link-frame-setup) 'find-file-other-frame))))
(add-hook 'frame-mode-hook 'imalison:org-frame-mode-hook)))
Disable yasnippet in org-mode
(use-package org
:ensure nil
:config
(progn
(defun imalison:disable-yas ()
(when (fboundp 'yas-minor-mode)
(yas-minor-mode -1)))
(add-hook 'org-mode-hook 'imalison:disable-yas)))
Set Background Color of Source Blocks for Export
This was taken from here.
(use-package org
:ensure nil
:config
(progn
(defun imalison:org-inline-css-hook (exporter)
"Insert custom inline css to automatically set the
background of code to whatever theme I'm using's background"
(when (eq exporter 'html)
(let* ((my-pre-bg (face-background 'default))
(my-pre-fg (face-foreground 'default)))
(setq
org-html-head-extra
(concat
org-html-head-extra
(format "<style type=\"text/css\">\n pre.src {background-color: %s; color: %s;}</style>\n"
my-pre-bg my-pre-fg))))))
(add-hook 'org-export-before-processing-hook 'imalison:org-inline-css-hook)))
Use my own default naming scheme for org-headings
First we define a function that will generate a sanitized version of the heading as its link target.
(defun imalison:org-get-raw-value (item)
(when (listp item)
(let* ((property-list (cadr item)))
(when property-list (plist-get property-list :raw-value)))))
(defun imalison:sanitize-name (name)
(replace-regexp-in-string "[^[:alpha:]]" "" (s-downcase name)))
(defun imalison:generate-name (datum cache)
(let ((raw-value (imalison:org-get-raw-value datum)))
(if raw-value
(imalison:sanitize-name raw-value)
;; This is the default implementation from org
(let ((type (org-element-type datum)))
(format "org%s%d"
(if type
(replace-regexp-in-string "-" "" (symbol-name type))
"secondarystring")
(incf (gethash type cache 0)))))))
This function replaces the default naming scheme with a call to
imalison:generate-name, and uses a slightly different uniquify approach.
(use-package ox
:defer t
:ensure nil
:config
(defun org-export-get-reference (datum info)
"Return a unique reference for DATUM, as a string.
DATUM is either an element or an object. INFO is the current
export state, as a plist. Returned reference consists of
alphanumeric characters only."
(let ((type (org-element-type datum))
(cache (or (plist-get info :internal-references)
(let ((h (make-hash-table :test #'eq)))
(plist-put info :internal-references h)
h)))
(reverse-cache (or (plist-get info :taken-internal-references)
(let ((h (make-hash-table :test 'equal)))
(plist-put info :taken-internal-references h)
h))))
(or (gethash datum cache)
(let* ((name (imalison:generate-name datum cache))
(number (+ 1 (gethash name reverse-cache -1)))
(new-name (format "%s%s" name (if (< 0 number) number ""))))
(puthash name number reverse-cache)
(puthash datum new-name cache)
new-name)))))
Add link icons in headings that lead to themselves
(use-package ox-html
:commands (org-html-export-as-html org-html-export-as-html)
:ensure nil
:preface
(progn
(defvar imalison:link-svg-html
"<svg aria-hidden=\"true\" class=\"octicon octicon-link\" height=\"16\" version=\"1.1\" viewBox=\"0 0 16 16\" width=\"16\"><path fill-rule=\"evenodd\" d=\"M4 9h1v1H4c-1.5 0-3-1.69-3-3.5S2.55 3 4 3h4c1.45 0 3 1.69 3 3.5 0 1.41-.91 2.72-2 3.25V8.59c.58-.45 1-1.27 1-2.09C10 5.22 8.98 4 8 4H4c-.98 0-2 1.22-2 2.5S3 9 4 9zm9-3h-1v1h1c1 0 2 1.22 2 2.5S13.98 12 13 12H9c-.98 0-2-1.22-2-2.5 0-.83.42-1.64 1-2.09V6.25c-1.09.53-2 1.84-2 3.25C6 11.31 7.55 13 9 13h4c1.45 0 3-1.69 3-3.5S14.5 6 13 6z\"></path></svg>")
(defvar imalison:current-html-headline)
(defun imalison:set-current-html-headline (headline &rest args)
(setq imalison:current-html-headline headline))
(defun imalison:clear-current-html-headline (&rest args)
(setq imalison:current-html-headline nil))
(defun imalison:org-html-format-heading-function (todo todo-type priority text tags info)
(let* ((reference (when imalison:current-html-headline
(org-export-get-reference imalison:current-html-headline info)))
;; Don't do anything special if the current headline is not set
(new-text (if reference
(format "%s <a href=\"#%s\">%s</a>" text reference imalison:link-svg-html)
text)))
(org-html-format-headline-default-function
todo todo-type priority new-text tags info))))
:config
(progn
;; This is set before and cleared afterwards, so that we know when we are
;; generating the text for the headline itself and when we are not.
(advice-add 'org-html-headline :before 'imalison:set-current-html-headline)
(advice-add 'org-html-headline :after 'imalison:clear-current-html-headline)
(setq org-html-format-headline-function
'imalison:org-html-format-heading-function)))
Allow with query params in image extentions
(use-package ox-html
:defer t
:ensure nil
:config
(setq org-html-inline-image-rules
'(("file" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\)\\(\\?.*?\\)?\\'")
("http" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\)\\(\\?.*?\\)?\\'")
("https" . "\\.\\(jpeg\\|jpg\\|png\\|gif\\|svg\\)\\(\\?.*?\\)?\\'"))))
Use org-tempo to allow inserting templates using e.g. <s
(use-package org-tempo
:ensure nil
:after org)
Packages
org-present
(use-package org-present
:after org
:commands org-present)
org-bullets
(use-package org-bullets
:commands org-bullets-mode
:after org
:preface
(add-hook 'org-mode-hook (lambda () (org-bullets-mode 1))))
org-ehtml
(use-package org-ehtml
:after web-server
:disabled t
:config
(progn
(setq org-ehtml-allow-agenda t)
(setq org-ehtml-editable-headlines t)
(setq org-ehtml-everything-editable t)
(ws-start org-ehtml-handler 8888)))
org-modern
(use-package org-modern
:disabled t
:after org
:hook (org-mode . org-modern-mode))
org-project-capture
(use-package org-project-capture
:after org
:bind ("C-c o p" . org-project-capture-project-todo-completing-read)
;; We want this to load somewhat quickly because we need to update the list of agenda files
:defer 2
:config
(progn
(use-package org-projectile
:demand t
:config
(setq org-project-capture-default-backend
(make-instance 'org-project-capture-projectile-backend)))
(setq org-project-capture-strategy
(make-instance 'org-project-capture-combine-strategies
:strategies (list (make-instance 'org-project-capture-single-file-strategy)
(make-instance 'org-project-capture-per-project-strategy))))
(setq org-project-capture-projects-file
(imalison:join-paths imalison:org-dir "projects.org")
org-project-capture-capture-template
(format "%s%s" "* TODO %?" imalison:created-property-string)
occ-auto-insert-category-heading t)
(add-to-list 'org-capture-templates
(org-project-capture-project-todo-entry
:capture-character "l"
:capture-heading "Linked Project TODO"))
(add-to-list 'org-capture-templates
(org-project-capture-project-todo-entry
:capture-character "p"))
(setq org-confirm-elisp-link-function nil)
(imalison:add-to-org-agenda-files (org-project-capture-todo-files))))
org-pomodoro
(use-package org-pomodoro
:after org)
org-roam
(use-package org-roam
:after org
:defer 1
:preface
(defun imalison:org-roam-browse-backlink ()
(interactive)
(let* ((node-alist
(cl-loop for backlink in (org-roam-backlinks-get (org-roam-node-at-point))
for node = (org-roam-backlink-source-node backlink)
collect `(,(org-roam-node-title node) . ,node)))
(selected-name (completing-read "Select a backlink to visit: " node-alist))
(selected-node (alist-get selected-name node-alist nil nil 'string-equal)))
(org-roam-node-visit selected-node)))
:bind
(("C-c r f" . org-roam-node-find)
("C-c r i" . org-roam-node-insert)
("C-c r b" . imalison:org-roam-browse-backlink)
("C-c r t" . org-roam-buffer-toggle))
:config
(progn
(when (version<= "29.0" emacs-version)
(setq org-roam-database-connector 'sqlite-builtin))
(org-roam-db-autosync-mode +1)
(defun imalison:frames-displaying-buffer (buf)
"Return a list of frames in which BUF is displayed."
(let ((target-buffer (if (bufferp buf) buf (get-buffer buf))))
(if target-buffer
(delq nil
(mapcar (lambda (frame)
(if (get-buffer-window target-buffer frame)
frame))
(frame-list)))
nil)))
(defun imalison:org-roam-frame-based-buffer-visibility-fn ()
(cond
((--any (funcall frame-mode-is-frame-viewable-fn it)
(imalison:frames-displaying-buffer org-roam-buffer)) 'visible)
((get-buffer org-roam-buffer) 'exists)
(t 'none)))
;; org-roam-buffer-visibility-fn was removed in newer org-roam versions
(when (and imalison:use-frame-mode (boundp 'org-roam-buffer-visibility-fn))
(require 'frame-mode)
(emit-variable-set-mode
imalison:org-roam-set-frame-visibility-mode
org-roam-buffer-visibility-fn 'imalison:org-roam-frame-based-buffer-visibility-fn)
(emit-make-mode-dependent imalison:org-roam-set-frame-visibility-mode frame-mode)))
:custom
(org-roam-directory (file-truename "~/org/roam/")))
ui
(use-package simple-httpd
:ensure (:host github :repo "skeeto/emacs-web-server"
:local-repo "simple-httpd"
:files ("simple-httpd.el")))
(use-package org-roam-ui
:after org-roam
:custom
(org-roam-ui-sync-theme t)
(org-roam-ui-follow t)
(org-roam-ui-update-on-save t)
(org-roam-ui-open-on-start nil))
org-wild-notifier
(use-package org-wild-notifier
:after org
:custom
((org-wild-notifier-keyword-whitelist nil)
(org-wild-notifier-tags-blacklist '("nonotify"))
(org-wild-notifier-alert-time '(30 5 0))
(org-wild-notifier-extra-alert-plist '(:persistent t))
(org-wild-notifier-additional-environment-regexes
(list (rx string-start "imalison:org-whoami")))
(org-wild-notifier-predicate-whitelist
`((lambda (marker)
(save-excursion
(set-buffer (marker-buffer marker))
(goto-char (marker-position marker))
,(macroexpand '(imalison:assigned-to-me))))))
(org-wild-notifier-predicate-blacklist
'(org-wild-notifier-done-keywords-predicate))
(org-wild-notifier-show-any-overdue-with-day-wide-alerts t)
(org-wild-notifier-day-wide-alert-times '("10pm")))
:config
(org-wild-notifier-mode +1)
(defun org-wild-notify-check-at-time ()
(interactive)
(imalison:org-at-time
:fn (lambda ()
(org-wild-notifier--check-events (funcall (org-wild-notifier--retrieve-events))))))
(defun org-wild-notify-list-at-time ()
(interactive)
(imalison:org-at-time
:fn (lambda ()
(message "%s"
(->> (funcall (org-wild-notifier--retrieve-events))
(-map 'org-wild-notifier--check-event)
(-flatten)
(-uniq)))))))
org-reveal
(use-package ox-reveal
:defer t
:commands org-reveal
:config
(setq org-reveal-root
(imalison:join-paths "file://" imalison:projects-directory "reveal.js")))
org-fc
(use-package org-fc
:after org
:bind ("C-c 9" . org-fc-hydra/body)
:config
(progn
(require 'org-fc-hydra))
:ensure (:host github :repo "l3kn/org-fc"
:files ("*.el" "awk" "demo.org")))
org-ql
(use-package org-ql
:bind ("C-c o s" . org-ql-find-in-agenda)
:commands org-ql-find-in-agenda)
org-window-habit
(use-package org-window-habit
:demand t
:ensure
(:repo "colonelpanic8/org-window-habit"
:host github
:files (:defaults "*.el"))
:custom
((org-window-habit-property-prefix nil)
(org-window-habit-repeat-to-scheduled t)
(org-window-habit-preceding-intervals 21)
(org-window-habit-following-days 2))
:config
(progn
(org-window-habit-mode +1)))