Home GitHub Patreon
Discussions RSS Twitter

Refiling hydra with pre-defined targets

I'm a heavy org-capture user and I use about 10 templates to save the ideas/tasks to appropriate places (work / life / emacs / other projects / reading…). Sometimes, however, it is quite difficult to determine at the time of capture where to put the note, or it would take a lot of time to categorize properly… or sometimes I'm just lazy. For these situations I use a general refile.org file. Anything I don't want to deal with right now goes there.

Then I often end up with 200+ notes in this file and I have to deal with it somehow during my weekly reviews. Many items I simply delete, but some I refine and then refile away to where they belong.

I use about 10 huge org files to store my data and simply calling org-refile is very slow and the number of targets grows into tens of thousands which makes the experience sub-optimal.

I've written a simple Elisp defmacro to generate specialized versions of org-refile where I can limit the targets to one file or a subset of files. This is done by let-binding org-refine-targets variable and then calling org-refile—it will pick up the new setting. I also automatically clear the cache because during this process I often add or move headlines around and the cache is most of the time stale. In practice it's not a problem because refiling to just one file is fast-enough to rebuild the cache on-the-go.

(defmacro my-org-make-refile-command (fn-suffix refile-targets)
  "Generate a command to call `org-refile' with modified targets."
  `(defun ,(intern (concat "my-org-refile-" (symbol-name fn-suffix))) ()
     ,(format "`org-refile' to %S" refile-targets)
     (interactive)
     (org-refile-cache-clear)
     (let ((org-refile-target-verify-function nil)
           (org-refile-targets ,refile-targets))
       (call-interactively 'org-refile))))

It's quite straight-forward, we have a defun skeleton and we splice the name and the target there. The expansion looks like this

(my-org-make-refile-command kb '(("~/data/documents/kb.org" :maxlevel . 9)))

;; expands to

(defun my-org-refile-kb nil
  "`org-refile' to (quote ((\"~/data/documents/kb.org\" :maxlevel . 9)))"
  (interactive)
  (org-refile-cache-clear)
  (let
      ((org-refile-target-verify-function nil)
       (org-refile-targets
        '(("~/data/documents/kb.org" :maxlevel . 9))))
    (call-interactively 'org-refile)))

Throw in a cool hydra and you're all set!

(my-org-make-refile-command kb '(("~/data/documents/kb.org" :maxlevel . 9)))
(my-org-make-refile-command reading '(("~/org/reading.org" :maxlevel . 9)))
(my-org-make-refile-command this-file `((,(buffer-file-name) :maxlevel . 9)))

(defhydra my-org-refile-hydra (:color blue :hint nil)
  "
_t_his file

Special files:
---------------------
_k_b.org    _r_eading.org"
  ("k" my-org-refile-kb)
  ("r" my-org-refile-reading)
  ("t" my-org-refile-this-file))

(bind-key "C-c r" #'my-org-refile-hydra/body org-mode-map)

Published at: 2019-02-10 13:52 Last updated at: 2023-02-08 15:59
Found a typo? Edit on GitHub!