An Helm source for Org Roam v2
Too long; didn't read
How I wrote a Helm source for Org Roam.
The problem
Org Roam is fantastic for my note taking. Still there is an ever returning bug with Helm: all of a sudden the formatting of your completion buffer would change. This makes me aesthetically cringe.
Since I have been playing around with Helm lately, I thought why not writing an Helm source for Org Roam? One with actions. And that fits perfectly with my little escalator mode!
And there is a solution
It wasn't hard with the amazing community that Emacs pulled together. I started at peeking at helm-org-roam, which is a functioning source for Org Roam v1. Then through browsing I got to a nice post by John Kitchin, which nudged me into understanding how to define Helm actions.
Anyway I started small:
(defun helm-org-roam (&optional input candidates) (interactive) (require 'org-roam) (helm :input input :sources (list (helm-build-sync-source "Roam: " :must-match nil :fuzzy-match t :candidates (or candidates (org-roam--get-titles)) :action '(("Find File" . (lambda (x) (--> x org-roam-node-from-title-or-alias (org-roam-node-visit it t)))) )))))
The candidate lists is the titles of my notes. The only action is to open a file. The code is copied from Org Roam, naturally.
But then I thought, I may also want to insert a link. And so I added another action using Org Roam's API:
("Insert link" . (lambda (x) (--> x org-roam-node-from-title-or-alias (insert (format "[[id:%s][%s]]" (org-roam-node-id it) (org-roam-node-title it))))))
That is pretty easy and smooth. Only thing, you need to use TAB to pick the right action (or use the F2 key). I still didn't learn how to assign keybindings for helm actions.
It was at this point that I remembered my useful function to navigate backlinks. Knowing Helm, I believed I could make it extremely more compact as an action:
("Follow backlinks" . (lambda (x) (let ((candidates (--> x org-roam-node-from-title-or-alias org-roam-backlinks-get (--map (org-roam-node-title (org-roam-backlink-source-node it)) it)))) (helm-org-roam nil (or candidates (list x))))))
How cool is that? An huge improvement over my many lines function that
needed to implement recursion. Helm is my recursion with
(helm-org-roam nil (or candidates (list x)))
, where I start it with
a new list of candidates.
The only bit missing at this point is to create a note, if it does not exist already. So I discovered that Helm allows multiple sources for a searcher and so I wrote my dummy source to create a note:
(helm-build-dummy-source "Create note" :action '(("Capture note" . (lambda (candidate) (org-roam-capture- :node (org-roam-node-create :title candidate) :props '(:finalize find-file))))))
The result is beautiful! I finally can compose navigation (of backlinks), insertion and opening notes as I want: Helm is really powerful.
I am now thinking about how to make moldable-emacs molds available to use as actions!
The overall code is in my escalator.el code.
Conclusion
Writing Helm searchers is simpler than I thought! And the modular actions mixed with recursion provide a powerful engine to search and act. If you are an Org Roam user, you may really like to try this version of helm-org-roam: load it and invoke it.
Happy searching!