My org-refile workflow
I use Org files as my Calendar/Notes/Time-Tracking tool. I try to use Org-Mode to organize my life with text files. So naturally, I have a lot of Org headings.
And I mainly use org-refile
to rearrange these headings and search
them.
Here are my org-refile
configurations:
Refile
org-refile-targets
(defun +org/opened-buffer-files () "Return the list of files currently opened in emacs" (delq nil (mapcar (lambda (x) (if (and (buffer-file-name x) (string-match "\\.org$" (buffer-file-name x))) (buffer-file-name x))) (buffer-list)))) (setq org-refile-targets '((+org/opened-buffer-files :maxlevel . 9)))
This config lets
org-refile
searches all the opened org mode files and fetches all the headings in them.Use outline path
(setq org-refile-use-outline-path 'file) ;; makes org-refile outline working with helm/ivy (setq org-outline-path-complete-in-steps nil) (setq org-refile-allow-creating-parent-nodes 'confirm)
It tells org refile to use the outline path (like
emacs.org/Packages/Org Mode
) for the query.It's really powerful since we can leverage the powerful org-mode hierarchy and we just need to
- Give this heading a meaningful, easy to remember name (like
refile
) - Categorize it to a parent heading (like
Org-Mode
)
Then we can get this heading in the
org-refile
interface easily (org mode refile
).- Give this heading a meaningful, easy to remember name (like
You can also read this post1 for more info about these configurations.
Search
With the powerful outline filtering, I wondered if org-refile
can
also be used in searching headings.
I believe this is the most natural way to search org-mode headings. Since our org-mode files are already organized in this tree-like hierarchy.
And it's well supported by the org-refile
function itself. When
passed in an universal-argument
('(4)
), org-refile
will just
take you to the target heading you select instead of refiling current
heading to the target heading.
(defun +org-search () (interactive) (org-refile '(4)))
Caching
When you have so many org-mode headings (e.g. I have more than 10,000 headings), it's really slow for
org-refile
to fetch all the headings every time. So I enabled caching fororg-refile
as well.(setq org-refile-use-cache t)
But then comes the hardest problem with caches: when to invalidate them.
I set a timer to regenerate this cache automatically every time my Emacs has been idled for 5 mins:
(run-with-idle-timer 300 t (lambda () (org-refile-cache-clear) (org-refile-get-targets)))
It's been working pretty well for some time. The only drawbacks are:
- The cache is not always up-to-date. But it's acceptable since I mostly only jump/refile to old headings.
- Emacs may hang for several seconds when I switch back to it because it's regenerating the cache.
Potential Improvements
- Incrementally updating cache table
My org files change a lot everyday but it's incremental, mostly it's just adding new headings and refile them to an existing heading.
So I really hope the cache can be smarter to just updates the cache when a heading is added and moved.
But it's pretty hard to do it in code. (I guess that's why
caching
is hard.)- Updating cache table in another thread
- I tried to run
(org-refile-get-targets)
in a new thread in Emacs 26. But Emacs crashed, I guess the new threading mechanism in Emacs 26 is not strong enough to support this. - Improve
org-refile
to speed up org-refile
is a 175-LOC method and it's quite complicated. I tried to refactor it but failed.I also tried to use some other similar functions (like
imenu-anywhere
2) to replace it, butimenu-anywhere
can only jump to children headings (leaves) but not parent headings (nodes).