Hydra for quickly refiling Org mode entries
;; Adapted from https://emacs.stackexchange.com/questions/8045/org-refile-to-a-known-fixed-location | |
(defun my/refile (file headline &optional arg) | |
"Refile to a specific location. | |
With a 'C-u' ARG argument, we jump to that location (see | |
`org-refile'). | |
Use `org-agenda-refile' in `org-agenda' mode." | |
(let* ((pos (with-current-buffer (or (get-buffer file) ;Is the file open in a buffer already? | |
(find-file-noselect file)) ;Otherwise, try to find the file by name (Note, default-directory matters here if it isn't absolute) | |
(or (org-find-exact-headline-in-buffer headline) | |
(error "Can't find headline `%s'" headline)))) | |
(filepath (buffer-file-name (marker-buffer pos)));If we're given a relative name, find absolute path | |
(rfloc (list headline filepath nil pos))) | |
(if (and (eq major-mode 'org-agenda-mode) (not (and arg (listp arg)))) ;Don't use org-agenda-refile if we're just jumping | |
(org-agenda-refile nil rfloc) | |
(org-refile arg nil rfloc)))) | |
(defun josh/refile (file headline &optional arg) | |
"Refile to HEADLINE in FILE. Clean up org-capture if it's activated. | |
With a `C-u` ARG, just jump to the headline." | |
(interactive "P") | |
(let ((is-capturing (and (boundp 'org-capture-mode) org-capture-mode))) | |
(cond | |
((and arg (listp arg)) ;Are we jumping? | |
(my/refile file headline arg)) | |
;; Are we in org-capture-mode? | |
(is-capturing ;Minor mode variable that's defined when capturing | |
(josh/org-capture-refile-but-with-args file headline arg)) | |
(t | |
(my/refile file headline arg))) | |
(when (or arg is-capturing) | |
(setq hydra-deactivate t)))) | |
(defun josh/org-capture-refile-but-with-args (file headline &optional arg) | |
"Copied from `org-capture-refile' since it doesn't allow passing arguments. This does." | |
(unless (eq (org-capture-get :type 'local) 'entry) | |
(error | |
"Refiling from a capture buffer makes only sense for `entry'-type templates")) | |
(let ((pos (point)) | |
(base (buffer-base-buffer (current-buffer))) | |
(org-capture-is-refiling t) | |
(kill-buffer (org-capture-get :kill-buffer 'local))) | |
(org-capture-put :kill-buffer nil) | |
(org-capture-finalize) | |
(save-window-excursion | |
(with-current-buffer (or base (current-buffer)) | |
(org-with-wide-buffer | |
(goto-char pos) | |
(my/refile file headline arg)))) | |
(when kill-buffer (kill-buffer base)))) | |
(defmacro josh/make-org-refile-hydra (hydraname file keyandheadline) | |
"Make a hydra named HYDRANAME with refile targets to FILE. | |
KEYANDHEADLINE should be a list of cons cells of the form (\"key\" . \"headline\")" | |
`(defhydra ,hydraname (:color blue :after-exit (unless (or hydra-deactivate | |
current-prefix-arg) ;If we're just jumping to a location, quit the hydra | |
(josh/org-refile-hydra/body))) | |
,file | |
,@(cl-loop for kv in keyandheadline | |
collect (list (car kv) (list 'josh/refile file (cdr kv) 'current-prefix-arg) (cdr kv))) | |
("q" nil "cancel"))) | |
;;;;;;;;;; | |
;; Here we'll define our refile headlines | |
;;;;;;;;;; | |
(josh/make-org-refile-hydra josh/org-refile-hydra-file-a | |
"file-a.org" | |
(("1" . "Headline 1") | |
("2" . "Headline 2"))) | |
(josh/make-org-refile-hydra josh/org-refile-hydra-file-b | |
"file-b.org" | |
(("1" . "One") | |
("2" . "Two"))) | |
(josh/make-org-refile-hydra josh/org-refile-hydra-file-c | |
"file-c.org" | |
(("1" . "1") | |
("2" . "2"))) | |
(defhydra josh/org-refile-hydra (:foreign-keys run) | |
"Refile" | |
("a" josh/org-refile-hydra-file-a/body "File A" :exit t) | |
("b" josh/org-refile-hydra-file-b/body "File B" :exit t) | |
("c" josh/org-refile-hydra-file-c/body "File C" :exit t) | |
("j" org-refile-goto-last-stored "Jump to last refile" :exit t) | |
("q" nil "cancel")) | |
(global-set-key (kbd "<f9> r") 'josh/org-refile-hydra/body) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment