lang/org: refactor & expand org-capture-templates

+ Adds three new default org-capture templates, for todo, notes and
  changelogs. It will use the first {todo,notes,changelog}.org file
  found up the file heirarchy from the current file, or will use
  {project-root}/X.org.
+ Variables in org-capture-templates are now resolved relative to
  org-directory, if they aren't absolute.
+ Display target file in org capture window header-line.

Mentioned in #886.
This commit is contained in:
Henrik Lissner 2018-09-19 23:26:53 -04:00
parent 1e710e94e3
commit 77255a63e0
No known key found for this signature in database
GPG key ID: 5F6C0EA160557395
2 changed files with 103 additions and 22 deletions

View file

@ -2,43 +2,91 @@
(add-hook 'org-load-hook #'+org|init-capture) (add-hook 'org-load-hook #'+org|init-capture)
;; Sets up two `org-capture' workflows that I like: ;; Sets up some reasonable defaults, as well as two `org-capture' workflows that
;; ;; I like:
;; 1. The traditional way: invoking `org-capture' directly (or through a
;; command, like :org).
;; ;;
;; 1. The traditional way: invoking `org-capture' directly, via SPC X, or
;; through the :cap ex command.
;; 2. Through a org-capture popup frame that is invoked from outside Emacs (the ;; 2. Through a org-capture popup frame that is invoked from outside Emacs (the
;; script is in ~/.emacs.d/bin). This lets me open an org-capture box ;; ~/.emacs.d/bin/org-capture script). This can be invoked from qutebrowser,
;; anywhere I can call org-capture (whether or not Emacs is open/running), ;; vimperator, dmenu or a global keybinding.
;; like, say, from qutebrowser, vimperator, dmenu or a global keybinding.
(defvar +org-default-todo-file "todo.org" (defvar +org-capture-todo-file "todo.org"
"TODO") "The path to your personal todo file.
(defvaralias '+org-default-notes-file 'org-default-notes-file) Is relative to `org-directory', unless it is absolute. Is used in Doom's default
`org-capture-templates'.")
(defvar +org-capture-notes-file "notes.org"
"The path to your personal notes file.
Is relative to `org-directory', unless it is absolute. Is used in Doom's default
`org-capture-templates'.")
(defvar +org-capture-changelog-file "changelog.org"
"The filename to use for project changelog files.
It is used in Doom's default `org-capture-templates'.")
(defvar org-capture-templates (defvar org-capture-templates
'(("t" "Todo" entry '(("t" "Personal todo" entry
(file+headline +org-default-todo-file "Inbox") (file+headline +org-capture-todo-file "Inbox")
"* [ ] %?\n%i" :prepend t :kill-buffer t) "* [ ] %?\n%i" :prepend t :kill-buffer t)
("n" "Personal notes" entry
(file+headline +org-capture-notes-file "Inbox")
"* %u %?\n%i" :prepend t :kill-buffer t)
;; Will use {project-root}/{todo,notes,changelog}.org, unless a
;; {todo,notes,changelog}.org file is found in a parent directory.
("p" "Templates for projects")
("pt" "Project todo" entry ; {project-root}/todo.org
(file+headline +org-capture-project-todo-file "Inbox")
"* [ ] %?\n%i" :prepend t :kill-buffer t)
("pn" "Project notes" entry ; {project-root}/notes.org
(file+headline +org-capture-project-notes-file "Inbox")
"* [ ] %?\n%i" :prepend t :kill-buffer t)
("pc" "Project changelog" entry ; {project-root}/changelog.org
(file+headline +org-capture-project-notes-file "Unreleased")
"* [ ] %?\n%i" :prepend t :kill-buffer t)))
(defvar org-default-notes-file nil) ; defined in org.el
("n" "Notes" entry
(file+headline org-default-notes-file "Inbox")
"* %u %?\n%i" :prepend t :kill-buffer t)))
;; ;;
(defun +org|init-capture () (defun +org|init-capture ()
(setq org-default-notes-file (expand-file-name org-default-notes-file org-directory) (dolist (var '(+org-capture-todo-file
+org-default-todo-file (expand-file-name +org-default-todo-file org-directory)) +org-capture-notes-file))
(set var (expand-file-name (symbol-value var) org-directory)))
(unless org-default-notes-file
(setq org-default-notes-file +org-capture-notes-file))
(add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame) (add-hook 'org-capture-after-finalize-hook #'+org-capture|cleanup-frame)
;; fix #462: when refiling from org-capture, Emacs prompts to kill the (defun +org*expand-variable-paths (file)
;; underlying, modified buffer. This fixes that. "If a variable is used for a file path in `org-capture-template', it is used
(defun +org-capture*refile (&rest _) as is, and expanded relative to `default-directory'. This changes it to be
relative to `org-directory', unless it is an absolute path."
(if (and (symbolp file) (boundp file))
(expand-file-name (symbol-value file) org-directory)
file))
(advice-add #'org-capture-expand-file :filter-args #'+org*expand-variable-paths)
(defun +org*prevent-save-prompts-when-refiling (&rest _)
"Fix #462: when refiling from org-capture, Emacs prompts to kill the
underlying, modified buffer. This fixes that."
(when (bound-and-true-p org-capture-is-refiling) (when (bound-and-true-p org-capture-is-refiling)
(org-save-all-org-buffers))) (org-save-all-org-buffers)))
(advice-add 'org-refile :after #'+org-capture*refile) (advice-add 'org-refile :after #'+org*prevent-save-prompts-when-refiling)
(defun +org|show-target-in-capture-header ()
(setq header-line-format
(format "%s%s%s"
(propertize (abbreviate-file-name (buffer-file-name (buffer-base-buffer)))
'face 'font-lock-string-face)
org-eldoc-breadcrumb-separator
header-line-format)))
(add-hook 'org-capture-mode-hook #'+org|show-target-in-capture-header)
(when (featurep! :feature evil) (when (featurep! :feature evil)
(add-hook 'org-capture-mode-hook #'evil-insert-state)) (add-hook 'org-capture-mode-hook #'evil-insert-state))

View file

@ -3,7 +3,8 @@
(defvar org-capture-initial) (defvar org-capture-initial)
;; --- External frame --------------------- ;;
;; External frame
;;;###autoload ;;;###autoload
(defvar +org-capture-frame-parameters (defvar +org-capture-frame-parameters
@ -70,3 +71,35 @@ you're done. This can be called from an external shell script."
(defun +org-capture-available-keys () (defun +org-capture-available-keys ()
"TODO" "TODO"
(string-join (mapcar #'car org-capture-templates) "")) (string-join (mapcar #'car org-capture-templates) ""))
;;
;; Capture targets
(defun +org--capture-root (path)
(let ((filename (file-name-nondirectory path)))
(expand-file-name
filename
(or (locate-dominating-file (file-truename default-directory)
filename)
(if (doom-project-p 'nocache) (doom-project-root 'nocache))
(user-error "Couldn't detect a project")))))
;;;###autoload
(defun +org-capture-project-todo-file ()
"Find the nearest `+org-capture-todo-file' in a parent directory, otherwise,
opens a blank one at the project root. Throws an error if not in a project."
(+org--capture-root +org-capture-todo-file))
;;;###autoload
(defun +org-capture-project-notes-file ()
"Find the nearest `+org-capture-notes-file' in a parent directory, otherwise,
opens a blank one at the project root. Throws an error if not in a project."
(+org--capture-root +org-capture-notes-file))
;;;###autoload
(defun +org-capture-project-changelog-file ()
"Find the nearest `+org-capture-changelog-file' in a parent directory,
otherwise, opens a blank one at the project root. Throws an error if not in a
project."
(+org--capture-root +org-capture-changelog-file))