Wrapping Emacs Functions to Configure CIDER
I do Clojure development in Emacs using CIDER. A nice way to get a CIDER repl started in a clojure project is to run ~cider-jack-in~. CIDER will drops you in a default repl that is configured for your environment, but I've found that it's sometimes useful to pass additional parameters when I am are starting a repl. I'd like to be prompted after ~cider-jack-in~ for additional options that will be passed to the ~clj~ process, for instance to enable certain classpath aliases like ~:test~.
Emacs supports wrapping existing functions with a feature called Advice.
Here was my first attempt at wrapping a function:
(defun cider-prompt-for-jack-in-options () (interactive) (message "Fake Prompt")) (advice-add 'cider-jack-in-global-options :before #'cider-prompt-for-jack-in-options)
We can test that our advice is actually applied to the function by running ~C-h f cider-jack-in-global-function~ and checking for the ~:before :advice~ section in the doc output.
#+BEGIN_SRC cider-jack-in-global-options is a compiled Lisp function in cider.el.
:before advice: cider-prompt-for-jack-in-options #+END_SRC
I tested to make sure that the ~Fake Prompt~ did indeed print when I ran ~cider-jack-in~.
Here's my first stab at taking options from the minibuffer with a default value.
(defun cider-prompt-for-jack-in-options () (interactive) (message "Options are %s" (read-string "Enter global-options: " "-A:ok")))
It has an initial value of ~-A:ok~ in the minibuffer, and then allows you to edit that to whatever you want.
But my goal is to take the current value returned by ~cider-jack-in-global-options~ and allow the user to modify it, so I'll edit my ~advice-add~ function to use an ~:around:~ wrapper instead of a ~:before~ wrapper.
(defun cider-prompt-for-jack-in-options (orig-fn project-type) (interactive) (let ((res (funcall orig-fn project-type))) (read-string "Enter global-options: " res))) (advice-add 'cider-jack-in-global-options :around #'cider-prompt-for-jack-in-options)
Now my function takes the original function as an argument along with the parameter passed to that argument. Then we can call the original function ourselves with ~funcall~, which we have to use because elisp is a Lisp-2.
The result of the cider-jack-in-global-options is set as the initial value of the ~read-string~, allowing you the user to change the value, or quickly hit the enter key and be on your way.
I learned while writing this that there are multiple overlapping ways to advise a function in Emacs. There is a macro ~defadvice~ which according to this page in the Emacs manual is "old" and "largely obselete". Associated obselete functions from that era include ~ad-activate~ and ~ad-deactivate~ which turn on and off all advice for a function. And there is ~ad-enable-advice~ and ~ad-disable-advice~ which will enable or disable a particular piece of advice. I belive all of those functions should be considered deprecated.
- Adam Frey, August 2019