From 77255a63e0dd7056d15b364bcfaf9fc2dce8e0b1 Mon Sep 17 00:00:00 2001 From: Henrik Lissner Date: Wed, 19 Sep 2018 23:26:53 -0400 Subject: [PATCH] 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. --- modules/lang/org/+capture.el | 90 ++++++++++++++++++------ modules/lang/org/autoload/org-capture.el | 35 ++++++++- 2 files changed, 103 insertions(+), 22 deletions(-) diff --git a/modules/lang/org/+capture.el b/modules/lang/org/+capture.el index 6bc9881dd..03988510e 100644 --- a/modules/lang/org/+capture.el +++ b/modules/lang/org/+capture.el @@ -2,43 +2,91 @@ (add-hook 'org-load-hook #'+org|init-capture) -;; Sets up two `org-capture' workflows that I like: -;; -;; 1. The traditional way: invoking `org-capture' directly (or through a -;; command, like :org). +;; Sets up some reasonable defaults, as well as two `org-capture' workflows that +;; I like: ;; +;; 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 -;; script is in ~/.emacs.d/bin). This lets me open an org-capture box -;; anywhere I can call org-capture (whether or not Emacs is open/running), -;; like, say, from qutebrowser, vimperator, dmenu or a global keybinding. +;; ~/.emacs.d/bin/org-capture script). This can be invoked from qutebrowser, +;; vimperator, dmenu or a global keybinding. -(defvar +org-default-todo-file "todo.org" - "TODO") +(defvar +org-capture-todo-file "todo.org" + "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 - '(("t" "Todo" entry - (file+headline +org-default-todo-file "Inbox") + '(("t" "Personal todo" entry + (file+headline +org-capture-todo-file "Inbox") "* [ ] %?\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 () - (setq org-default-notes-file (expand-file-name org-default-notes-file org-directory) - +org-default-todo-file (expand-file-name +org-default-todo-file org-directory)) + (dolist (var '(+org-capture-todo-file + +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) - ;; fix #462: when refiling from org-capture, Emacs prompts to kill the - ;; underlying, modified buffer. This fixes that. - (defun +org-capture*refile (&rest _) + (defun +org*expand-variable-paths (file) + "If a variable is used for a file path in `org-capture-template', it is used +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) (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) (add-hook 'org-capture-mode-hook #'evil-insert-state)) diff --git a/modules/lang/org/autoload/org-capture.el b/modules/lang/org/autoload/org-capture.el index 4efa77780..85c701923 100644 --- a/modules/lang/org/autoload/org-capture.el +++ b/modules/lang/org/autoload/org-capture.el @@ -3,7 +3,8 @@ (defvar org-capture-initial) -;; --- External frame --------------------- +;; +;; External frame ;;;###autoload (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 () "TODO" (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))