Summary

This is an exhibit of one mechanism for interactively running Clojure functions with ease from emacs.

Similar mechanisms exist for running tests, too, as I will demonstrate.

Demo

When I want to have on-hand a bunch of Clojure functions I can run at any time, I don’t want to have to add input handlers for the variables. I just want to run it, interactively.

1
2
(defn pen-test-interactive-clj [a b c]
  (sh "pen-tv" :in a))

Define the key binding:

1
(define-key lispy-mode-map (kbd "E") 'special-pen-lispy-eval-eval)

As soon as I press E while on the left parenthesis, I am greeted with the following:

1
2
(1/1) a:
hello (nil) [=> #'khala.dev/pen-test-interactive-clj]

This means, the function was evaluated, and it is now asking for a value for a, and provides the history of the last value I gave a, (hello).

As you can see a vim window appeared with How are you? inside of it.

That is because, the function runs pen-tv, with the stdin = “How are you?”.

pen-tv is a script for viewing the stdin in a vim window.

Source

Check out the interactive lambda ;).

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
(defun pen-clojure-eval-eval ()
  (interactive)
  (if (derived-mode-p 'clojure-mode)
      (progn
        (call-interactively 'pen-lisp-e)
        (let* ((sexp (sexp-at-point))
               ;; cadr = second
               (symstr (symbol-name (cadr sexp)))
               ;; caddr = third
               (argvec (third sexp))
               (arglist (pen-vector2list argvec))
               (iarglist (mapcar
                          (lambda (e) `(read-string-hist ,(concat (str e) ": ")))
                          arglist))
               (argrepr (str (third sexp)))
               (argstr (s-substring "\\[\\(.*\\)\\]" argrepr)))
          (if (re-match-p "^\\[.*\\]$" argrepr)
              (progn
                (eval
                 `(call-interactively
                   (lambda (,@arglist)
                     (interactive (list ,@iarglist))
                     (let* ((valstr (pen-cmd ,@arglist))
                            (clj (concat "(" symstr " " valstr ")")))
                       (cider-nrepl-sync-request:eval clj nil))))))
            (cider-nrepl-request:eval (concat "(" symstr ")") nil))))))

(defun pen-lispy-eval-eval ()
  "Evaluate sexp at point and then evaluate the result as a function."
  (interactive)

  (let ((cpa current-prefix-arg)
        (cgpa current-global-prefix-arg))
    (setq current-prefix-arg nil)
    (setq current-global-prefix-arg nil)
    (if (lispy-left-p)
        (cond
         ((derived-mode-p 'emacs-lisp-mode)
          (let* ((result)
                 (resultsym (save-excursion
                              (lispy-different)
                              (call-interactively 'eval-last-sexp))))
            (if (and (fboundp resultsym) (commandp resultsym))
                (let ((current-prefix-arg cpa)
                      ;; Propagating current-global-prefix-arg doesn't actually work with certain interactive, such as (interactive (list (read-string "kjlfdskf")))
                      (current-global-prefix-arg cgpa))
                  (call-interactively resultsym))
              (progn
                (if (or (functionp resultsym)
                        (macrop resultsym))
                    (setq result
                          (str (eval `(,resultsym))))
                  (setq result (eval-string result)))
                (new-buffer-from-string result)))))
         ((derived-mode-p 'clojure-mode)
          (call-interactively 'pen-clojure-eval-eval))
         (t (error (concat "No eval-eval handler for " (symbol-name major-mode))))))))

Thank you

I thought I’d demonstrate this as I’m trying to make the best Clojure IDE ever, in emacs.