Where parallels cross

Interesting bits of life

Make adding a Clojure require more interactive with Cider and Cljr

This is mostly a thank you to the Cider, clj-refactor and YASnippet maintainers: it is amazing to develop Clojure in Emacs!

With the occasion, let me share an improvement I just added to my configuration. I have the clj-refactor package installed from a long time. I don't use it super-often, but when I do it saves me time. For example, I extract definitions and functions. One refactor I felt always uncomfortable with is adding a require. This expands a YASnippet asking you for the library you want to require. The problem though is that it doesn't help at all at finding it!

I struggle at remembering Clojure namespaces (that usually look like this.special.thing.you-cannot-remember)! So I decided to fix that for myself.

The amazing discovery is that clj-refactor developers made this refactor easy to extend. Again that uses a YASnippet defined as follows:

(defvar cljr--add-use-snippet "[$1 :refer ${2:[$3]}]"

When you call cljr-add-require-to-ns this snippet gets expanded. The bit that concerns me is $1, which asks for a namespace (I cannot remember).

For editing Clojure I use cider which has a function called cider-find-ns. This allows you to search a Clojure namespace instead of file names. And I know that YasSnippet has a fantastic feature to provide multiple choices to complete a snippet.

These give us all the bits we need. The shortest snippet to get a list of all namespaces from Cider is (cider-sync-request:ns-list). The YASnippet we want to use for choosing the namespace to insert then is: "[${1:$$(yas-choose-value (ignore-errors (cider-sync-request:ns-list)))} :refer ${2:[$3]}]".

Since cljr could work also without Cider, I decided to set the cljr--add-use-snippet variable only if Cider is active. I do this with hooks:

(defun my/make-cljr-add-use-snippet-interactive ()
  (setq-local cljr--add-use-snippet "[${1:$$(yas-choose-value (ignore-errors (cider-sync-request:ns-list)))} :refer ${2:[$3]}]"))

(add-hook 'cider-mode-hook 'my/make-cljr-add-use-snippet-interactive)

This sets the variable only for the current file, given cider-mode-hook was called (which happens only for buffer that have Cider active).

And that's it! Please appreciate how programmable each of these Emacs libraries is: it made me so happy that I could inject this use case by making use of the extension points that others made available for me. Thank you very much kind Emacs users I never met!

Happy Clojuring!

Comments