Skip to content

Instantly share code, notes, and snippets.

@kingtim
Last active December 10, 2015 23:09
Show Gist options
  • Save kingtim/4507824 to your computer and use it in GitHub Desktop.
Save kingtim/4507824 to your computer and use it in GitHub Desktop.
Prototype nrepl middleware for eval-in-emacs.
(ns org.kingtim.eval-in-emacs
(:require
[clojure.tools.nrepl.transport :as transport]
[clojure.tools.nrepl.middleware :refer [set-descriptor!]]
[clojure.tools.nrepl.middleware.session :refer [session]]
[clojure.tools.nrepl.misc :refer [response-for]])
(:import clojure.tools.nrepl.transport.Transport))
(def transports (atom {}))
(defn eval-in-emacs [form]
(if-let [t (first @transports)]
(do
(clojure.tools.nrepl.transport/send (val t) {:eval form})
true)))
(defn init-eval-in-emacs [{:keys [session transport] :as msg}]
(let [id (-> session meta :id)]
(swap! transports assoc id transport)
(transport/send transport (response-for msg :status :done))))
(defn eval-result [{:keys [session transport] :as msg}]
;; TODO: not implemented yet
(transport/send transport (response-for msg :status :done)))
(defn add-eval-in-emacs
"eval-in-emacs middleware. Returns a handler that supports eval-in-emacs."
[h]
(fn [{:keys [op] :as msg}]
(cond
(= op "init-eval")
(init-eval-in-emacs msg)
(= op "eval-result")
(eval-result msg)
:else
(h msg))))
(set-descriptor! #'add-eval-in-emacs
{:requires #{#'session}
:handles {"init-eval" {:doc "Initialize eval in emacs"}
"eval-result" {:doc "Result from eval (not implemented yet)"}}})
1. Use nrepl.el from "eval-in-emacs" branch published on github.
2. Build the above middleware add add it into your local repository.
3. Add the middleware into you project dependencies like so:
...
:repl-options {:nrepl-middleware
[org.kingtim.eval-in-emacs/add-eval-in-emacs]}
:profiles {:dev {:dependencies [[org.kingtim/eval-in-emacs "0.1.0-SNAPSHOT"]]}}
...
4. Connect up to your nrepl server using nrepl-jack-in or your preferred method.
The server should now have the eval-in-emacs middleware.
You can verify this by evalulating in emacs (nrepl-op-supported-p "init-eval"). Should return t.
4. From emacs connected to nrepl mode: M-x nrepl-init-eval-in-emacs
This initializes the middleware with the correct transport for the current connection. This was the best way I could find
to get ahold of this with the current nrepl server.
5. Invoke (org.kingtim.eval-in-emacs/eval-in-emacs form) which will send the form to emacs to be evaled.
e.g. (org.kingtim.eval-in-emacs/eval-in-emacs "(with-current-buffer nrepl-nrepl-buffer (insert \"hi\"))")
TODO:
- Should be controlled on the client side by a variable e.g. nrepl-enable-evaluate-in-emacs
- Need to complete round trip so that server side can get results of the eval. I was thinking this could be modeled
like the need-input/stdin interactions that currently exist in the nrepl server, with some kind of promise/future that
that is fulfilled when the result is delivered. Reference current eval in emacs implementation in swank-clojure and
stdin handler in nrepl server. Depends on your use cases.
- Possibly need synchronous version as well on the client side. Again, not sure exactly what your use cases require.
- Would like a better way of getting a handle on the transport, but that may require changes to the core nrepl server.
The other way I was able to do it was by getting a handle on the server and grabbing the transport from there, which
I can provide an example of if you are interested.
@kingtim
Copy link
Author

kingtim commented Feb 1, 2013

@samaaron Sorry, missed your comment.

I haven't found a good way to inject from nrepl.el yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment