Where parallels cross

Interesting bits of life

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!

Comments