Fix clj-refactor Libspec Stealing
(This post is regarding a very particular issue at the intersection of Emacs, Clojure, and CIDER, so most readers can probably skip it, but I figured the solution deserved to be somewhere on the internet.)
CIDER has this awesome feature via clj-refactor where it can
automatically add missing libspecs to your ns
form as soon as you
type an aliased var name using an :as
alias you have previously used
for that namespace:
You can define a preferred set of (:as
) aliases via
cljr-magic-require-namespaces
in your Emacs config, and, whether
you’ve done that or not, CIDER tries hard to be smart, offering you
choices if you’ve previously used the same alias for different
namespaces in different places in your project (e.g. s/
for
clojure.spec
some places and the same for clojure.string
in other
places).
Moreover, CIDER evaluates the namespace form, bringing in the new requires, allowing you to just keep typing and coding, without breaking your flow.
This is all fantastic, but I ran across one particular scenario where
this kept biting me: my user
namespace.
In my dev/user.clj
file, I had a number of commands for managing the
state of my program during development (viz. reloaded.repl and
friends) that I used constantly, including while testing out various
functions I’d just written in the current namespace (e.g.
(new-db-query (user/db))
). But I also happen to have another
(rarely-used) namespace in my app which was, somewhere, aliased as
user
. Something like app.ui.views.user
.
Since references to the (top-level) user
namespace never occur in
the (production-shared) broader codebase, every time I typed user/
,
CIDER immediately added a require for the namespace I didn’t want
(the only usage of the alias in the codebase) and loaded it.
Meaning I not only had to remove it, but also undefine the alias (C-c C-u
)—and hopefully not accidentally trigger the same thing yet
again though muscle memory….
I tried everything I could think of to fix this, including just
specifying via cljr-magic-require-namespaces
that user
should
expand to user
, but nothing worked, so I suspect a proper solution
is going to require an upstream patch.
In the meantime, a quick and dirty solution is just to use advice in Emacs:
(use-package clj-refactor
:defer t
:config
;; Don't auto add `require' form for `user' namespace.
(defun cljr--unresolved-alias-ref--unless-user (alias-ref)
(unless (string= "user" alias-ref)
alias-ref))
(advice-add 'cljr--unresolved-alias-ref :before-while
#'cljr--unresolved-alias-ref--unless-user))
If the advice function here
(cljr--unresolved-alias-ref--unless-user
) returns nil
, it
essentially tells clj-refactor that an alias is already handled and it
doesn’t need to worry about adding an import for it.