Emacs Redux: CIDER and Projectile Meet Embark

Wait 5 sec.

Embark is one of those packages thatquietly rewires how you use Emacs. Think of it as a context-aware right-click:you invoke embark-act (I keep it on C-.) on whatever’s at point - a filename, a symbol, a URL - and Embark offers a menu of things you can do with it.The neat part is that it’s completely extensible, so you can teach it about newkinds of targets. Lately two of my projects, CIDER and Projectile, have grownEmbark integrations, in slightly different ways, and both are worth a look.Let’s start with CIDER, and teach Embark about Clojure symbols, so embark-acton one offers a bunchof CIDER commands:So d shows the documentation, . jumps to the definition, r findsreferences, i inspects the value, c opens ClojureDocs and a runs apropos -the usual CIDER commands, a keystroke away from wherever your cursor happens tobe.Here’s the setup. It’s a handful of little wrappers around the CIDER commands, akeymap that binds them, a target finder that spots a Clojure symbol at point,and the bit of wiring that ties it all together:(defun my-cider-embark-doc (sym) (interactive "sClojure symbol: ") (cider-doc-lookup sym))(defun my-cider-embark-find-def (sym) (interactive "sClojure symbol: ") (cider-find-var nil sym))(defun my-cider-embark-fn-refs (sym) (interactive "sClojure symbol: ") (cider-xref-fn-refs nil sym))(defun my-cider-embark-inspect (sym) (interactive "sClojure symbol: ") (cider-inspect-expr sym (cider-current-ns)))(defun my-cider-embark-clojuredocs (sym) (interactive "sClojure symbol: ") (cider-clojuredocs-lookup sym))(defun my-cider-embark-apropos (sym) (interactive "sClojure symbol: ") (cider-apropos sym))(defvar my-cider-embark-symbol-map (let ((map (make-sparse-keymap))) (define-key map "d" #'my-cider-embark-doc) (define-key map "." #'my-cider-embark-find-def) (define-key map "r" #'my-cider-embark-fn-refs) (define-key map "i" #'my-cider-embark-inspect) (define-key map "c" #'my-cider-embark-clojuredocs) (define-key map "a" #'my-cider-embark-apropos) map))(defun my-cider-embark-target () (when (derived-mode-p 'clojure-mode 'clojurescript-mode 'clojurec-mode 'clojure-ts-mode 'cider-repl-mode) (when-let* ((bounds (bounds-of-thing-at-point 'symbol)) (sym (cider-symbol-at-point))) (unless (string-empty-p sym) `(cider-clojure-symbol ,sym . ,bounds)))))(with-eval-after-load 'embark (add-to-list 'embark-target-finders #'my-cider-embark-target) (add-to-list 'embark-keymap-alist '(cider-clojure-symbol my-cider-embark-symbol-map)) (add-to-list 'embark-keymap-alist '(cider my-cider-embark-symbol-map)))That last cider line is my favorite bit. CIDER’s symbol prompts (things likecider-doc and cider-find-var) can read through completing-read and tagtheir candidates with the cider completion category, so pointing that categoryat the same keymap means embark-act works on the candidates too. You starttyping a symbol to read its docs, spot it in the candidate list, decide you’drather jump to its definition - C-. . and you’re there, no retyping. Symbolsin your code and candidates in the minibuffer end up sharing one set of actions.Projectile took the opposite tack and ships its Embark integration out of thebox - there’s nothing to configure, it wires itself up as soon as Embark isloaded. Two things happen. Acting on a project candidate (say, inprojectile-switch-project) gives you project-specific actions - switch to it,open its VC interface, drop into dired, or remove it from the known-projectslist - instead of the generic file actions you’d otherwise get:And when you act on a project file candidate, Projectile makes sure the pathresolves against the project root, so Embark’s file actions land on the rightfile no matter what your default-directory happens to be.You might wonder why Projectile ships this while CIDER leaves it to you. It comesdown to what each integration is. Projectile’s is mostly about making thecompletion categories it already exposes behave correctly under Embark, plus asmall and fairly obvious set of project actions - there isn’t much to bikeshedthere. CIDER’s symbol actions are a bigger, more personal grab-bag: whichcommands you want on that menu, and which keys you put them on, is very much amatter of taste. A recipe you copy and tailor felt like the better fit. Sameidea, two problems that wanted different answers.That’s all I have for you today. Keep hacking!P.S. If you’re not using Embark yet, the actions above are just the start -once you start thinking of embark-act as “do something with the thing atpoint”, it’s hard to stop.