Fork me on GitHub
#cider
<
2023-01-23
>
Carsten Behring15:01:41

I made a proof-of-concept to combine the cider-inspector and Clerk and quite like it. The idea is that while in the cider-inspector I can press a key, and the current value gets then send to Clerk and is rendered in browser via Clerks tap inspector. So I can still navigate complex data structures with the cider-inspector as usual, and at any given point in time render the current value with Clerk.

cider 6
Carsten Behring15:01:49

As I have little clue on Cider and EmacsLisp I just hacked the existing cider-inspector-def-current-val and added a "tapping" of the value to it. (just to see f it works)

(defun cider-inspector-def-current-val (var-name ns)
  "Defines a var with VAR-NAME in current namespace.

Doesn't modify current page.  When called interactively NS defaults to
current-namespace."
  (interactive (let ((ns (cider-current-ns)))
                 (list (read-from-minibuffer (concat "Var name: " ns "/"))
                       ns)))
  (setq cider-inspector--current-repl (cider-current-repl))
  (when-let* ((value (cider-sync-request:inspect-def-current-val ns var-name)))
    (cider-interactive-eval (concat "(tap> " ns "/" var-name ")"))
    (cider-inspector--render-value value)
    (message "%s#'%s/%s = %s" cider-eval-result-prefix ns var-name value)
    ))

Carsten Behring15:01:07

Then I can setup the Clerk tap inspector:

(nextjournal.clerk/show! 'nextjournal.clerk.tap)
Now I can call cider-inspector-def-current-value while in cider-inspector and it will render the current value in Clerk.

Carsten Behring15:01:22

So I think it would be cool, if the cider inspector could gain a new function to tap the current value. cider-inspector-tap-current-value

Carsten Behring15:01:32

To have a "full integration" of Cider and Clerk, one piece is still missing: When "render" a value like above in clerk, we need to have a way to "choose a Clerk viewer". In Clerk itself this goes via code, using specific functions. I made an other proof of concept, in which Emacs asks the user to select a viewer from a list and wrap the value with the needed metadata before tapping, and then Clerk would use the selected viewer in the tap inspector:

(setq clerk-viewer-list '("default"
                          ":html"
                          ":latex"
                          ":table"
                          "nextjournal.clerk.viewer/html-viewer"
                          "nextjournal.clerk.viewer/vega-lite-viewer"
                          "nextjournal.clerk.viewer/map-viewer"
                          "nextjournal.clerk.viewer/markdown-viewer"
                          "nextjournal.clerk.viewer/katex-viewer"
                          "nextjournal.clerk.viewer/fallback-viewer"
                          "nextjournal.clerk.viewer/string-viewer"))

(defun clerk-tap-last-sexp-with-viewer (viewer)
  (interactive
   (list (completing-read "Choose viewer: " clerk-viewer-list nil t)))

  (let ((tapped-form (concat "(clojure.core/->> "
                             (cider-last-sexp)
                             (if (equal "default" viewer)
                                 (concat " (nextjournal.clerk/with-viewer {:transform-fn identity})")
                               (if (string-prefix-p ":" viewer)
                                   (concat " (nextjournal.clerk/with-viewer " "(keyword \"" (substring viewer 1) "\")" ")")
                                   (concat " (nextjournal.clerk/with-viewer " "(symbol \"" viewer "\")" ")"))
                               )

                             " (clojure.core/tap>))")))
    (cider-interactive-eval tapped-form
                            nil
                            nil
                            (cider--nrepl-pr-request-map))))

Carsten Behring15:01:51

This code is Clerk specific, so should probably not be in Cider. But Cider would need to "call back" as part of the cider-inspector to ask the user to select the viewer, to get this nicely working....

Carsten Behring15:01:13

But maybe we could start by adding a function cider-inspector-tap-current-value to Cider ? Then we could make a basic integration with Clerk's tap inspector (without viewer selection) outside cider

Carsten Behring16:01:58

If somebody want to play with it, the following emacs lisp function can be used while in cider-inspector without changing any cider code:

(defun cider-inspector-tap-current-val ()
  (interactive)
  (setq cider-inspector--current-repl (cider-current-repl))
  (when-let* ((ns "user")
              (var-name "cider-inspector-temp-hdhsad")
              (value (cider-sync-request:inspect-def-current-val ns var-name)))
    (cider-interactive-eval (concat "(tap> " ns "/" var-name ")"))
    (cider-inspector--render-value value)
    (message "%s#'%s/%s = %s" cider-eval-result-prefix ns var-name value)
    ))
It will def the current value of the cider-inspector (using a fixed var name) and then tap> it. If the Clerk tap inspector is open, it will then render it. Other Clojure value inspectors, like Portal and Reveal can as well be setup as tap> targets. So it would work the same way for those, not only Clerk.

Carsten Behring17:01:10

The following add the interactive viewer selection to it:

(setq clerk-viewer-list '("default"
                          ":html"
                          ":latex"
                          ":table"
                          "nextjournal.clerk.viewer/html-viewer"
                          "nextjournal.clerk.viewer/vega-lite-viewer"
                          "nextjournal.clerk.viewer/map-viewer"
                          "nextjournal.clerk.viewer/markdown-viewer"
                          "nextjournal.clerk.viewer/katex-viewer"
                          "nextjournal.clerk.viewer/fallback-viewer"
                          "nextjournal.clerk.viewer/string-viewer"))

(defun cider-inspector-tap-current-val-with-viewer (viewer)
  (interactive
   (list (completing-read "Choose viewer: " clerk-viewer-list nil t)))

  (setq cider-inspector--current-repl (cider-current-repl))
  (when-let* ((ns "user")
              (var-name "cider-inspector-temp-hdhsad")
              (value (cider-sync-request:inspect-def-current-val ns var-name)))

    (let ((tapped-form (concat "(clojure.core/->> "
                             (concat ns "/" var-name)  
                             (if (equal "default" viewer)
                                 (concat " (nextjournal.clerk/with-viewer {:transform-fn identity})")
                               (if (string-prefix-p ":" viewer)
                                   (concat " (nextjournal.clerk/with-viewer " "(keyword \"" (substring viewer 1) "\")" ")")
                                   (concat " (nextjournal.clerk/with-viewer " "(symbol \"" viewer "\")" ")"))
                               )

                             " (clojure.core/tap>))")))
    (cider-interactive-eval tapped-form
                            nil
                            nil
                            (cider--nrepl-pr-request-map)))
    
    
    (cider-inspector--render-value value)
    (message "%s#'%s/%s = %s" cider-eval-result-prefix ns var-name value)
    ))
Calling cider-inspector-tap-current-val-with-viewer while in cider-inspector will first ask for a viewer to pick (:table for table viewer) and then it tap> the current inspector value wrapped in Clerk metadata which chooses the viewer . Clerk tap inspector will then show it as table (or in any other viewer selected before)

Carsten Behring09:01:44

The Portal docu assumes as well that the "correct viewer" can be selected automatically by "looking at the value". In my view this does not work "in general", for all cases. To detect that a "map" is "vega lite" is not easy and will not work for "all graphic formats". So the "user" need to be "asked" about the "viewer to use". (or we assume "code")