Skip to content

Instantly share code, notes, and snippets.

@mm--
Created March 9, 2017 21:52
Show Gist options
  • Star 9 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save mm--/60e0790bcbf8447160cc87a66dc949ab to your computer and use it in GitHub Desktop.
Save mm--/60e0790bcbf8447160cc87a66dc949ab to your computer and use it in GitHub Desktop.
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