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!