Friday, January 28, 2011

slime clojure hack

hello, world

Came up with a functional little hack for slime and clojure today, currently called Slimer:
By defining a hook inside the clojure swank function listener-eval and a custom repl shortcut
on the emacs side, we can have the repl run temporarily through a function that replaces
the repl input with it's results, and evaluates them -- effectively acting as a macro.

2 files:

slimer.clj

(in-ns 'swank.commands.basic)

(def **f nil)

(defslimefn listener-eval [form]
  (if **f
    (let [f  (read-string form)
          f2 (list 'quote f)
          r  (eval (list **f f2))]
      (send-repl-results-to-emacs r))
    (with-emacs-package
      (with-package-tracking
 (let [[value last-form] (eval-region form)]
   (when (and last-form (not (one-of? last-form '*1 '*2 '*3 '*e)))
     (set! *3 *2)
     (set! *2 *1)
     (set! *1 value))
   (send-repl-results-to-emacs value))))))


slimer.el

(defun slimer-pkg ()
  (let ((p (slime-current-package)))
    (if p (concat p "/") nil)))

(defslime-repl-shortcut nil  ("dsl")
  (:handler
   (lambda (str)
     (interactive (list (slime-read-from-minibuffer
    "function:" (or (slimer-pkg) ""))))
     (let ((sym (if (or (string-equal str (slimer-pkg))
   (string-equal str ""))
      "nil" str)))
       (slime-interactive-eval
 (concat "(do (in-ns 'swank.commands.basic) (def **f " sym " ))"))
       (setf (slime-lisp-package-prompt-string)
      (if (string-equal sym "nil")
   (slime-current-package)
        (concat sym " $$"))))
     (slime-repl-insert-prompt))))

(provide 'slimer)


I warned you it was ugly.

drop the .el into your emacs load path, and the .clj in the /src of your lein project,
and then you can just (require 'slimer) on both sides, and you're ready to go!


now, the code is ugly, and may only work with my version of slime, swank, and swank-clojure etc,
so YMMV. It's a useful for me as-is, but it would be neat to see this functionality made generally
available instead of as a hack.

current setup is to provide the repl shortcut ( comma command )  "dsl" which will prompt for a
symbol in the current namespace, evaluate it, and attempt to apply it's value as a function to
subsequent forms entered at the repl, evaluating the results. If you accept the default at
the prompt i.e. just press enter -- it will return the repl to it's original behavior.

The repl prompt is also changed to the string of the expression that yielded the current function + "  $$"
during dsl mode to make it easier to use. As it is, the entered code is read and then passed, as that seems
more useful, but by removing the call to read-string in line 3 of listener-eval it could be used to operate on raw strings, which may be useful it you're attempting to use syntax clojure does not understand.

 ...anyway, the code's really ugly but it works for now. If there's something better, please let me know!

No comments:

Post a Comment