Yes, the thing is that with the current "interactive results" plug-in customizations can be first-class
It means that you can evaluate anything, and when you get the result the user can decide how to render that result - even if it involves further interactions with the REPL
So, if there's a better way to make it easier for the user to write these interactions... maybe we can see some interesting developments
Will there be a way to trigger additional evaluates from the Reagent component? And just so I understand the machinery: the text block passed to the evaluate_interactive
API is passed to the REPL and evaluated and then passed back to Chlorine and rendered -- it's a one-way valve: evaluate in the REPL, if it produces :html
etc, create an inline renderer?
I think it would be very interesting to be able to trigger additional evaluate/render cycles from the renderer: that would allow something close to REBL's functionality where you can render, then navigate and render, then navigate and render, etc.
Yes, it is possible today - every "callback" function that you add on :fns
is evaluated on the REPL
For example, on the code below:
'{:html [:div.rows
[:div [:input {:type "text" :on-change (?hello "Hello") :value (:in ?state)}]]
[:div (:msg ?state)]]
:state {:in "" :msg "Type Something..."}
:fns {:hello (fn [e s prefix] (assoc s
:in (:value e)
:msg (str prefix ", " (:value e))))}}
This :hello
handler is evaluated on the REPL side. The way it currently works is that, on Chlorine's side, the "event handler" (the e
on the function) is converted to EDN and sent to REPL. The REPL then will evaluate something like:
((fn [e s prefix] (assoc s
:in (:value e)
:msg (str prefix ", " (:value e))))
{:value "World"}
{:in "" :msg "Type Something..."}
"Hello")
Of course, not every attribute from the "event" on React / Reagent's side is serialized (mostly because React adds infinite cycles and other strange things on its side...)
The idea for the "interactive renderer" is to allow people to customize the plug-in as far as they want, even to the point of hooking new libraries, without the need to recompile it.
@mauricio.szabo Oh wow, I didn't realize that's how it works! That's awesome. So a datafy
/`nav` UI would be possible to build. I'll have to give that some thought!
Since the callbacks work on event, state, and data and they return new state, I sort of assumed they were executing on the client side (and I was wondering how, since they are Clojure/Script) 🙂 That's really nice.
@seancorfield well, the way that everything fit together is quite tricky, so if you find any strange thing, please ask here! This "Clojure/ClojureScript dance" is quite interesting, but it can become confusing really fast...
For a considerably more complex example of what the renderer can do, look at this file: https://github.com/mauricioszabo/repl-tooling/blob/master/resources/orchard-cmds.clj#L2
Thanks.
I have a very clunky, bare bones inline mini-REBL starting to come together -- The new renderer stuff is amazing @mauricio.szabo !
Thanks! Let me know if you can thing on ways to improve it 🙂
I just ran across a repl-tooling.editor-helpers.Browseable
which seems to be a wrapper for a value?
Yes, it's a wrapper
Sometimes, these results are a little crude (that's one of the reasons I want to get rid of UNREPL)
Probably an exception of some kind
No, it's wrapping a hash map that has metadata.
Oh... that should not be wrapped 😞
I have my inline REBL working for basic hash maps and vectors so I thought I'd try it on a next.jdbc
result set
I can unwrap it for display, but now I can't seem to get my button to fire (and there's no exception being displayed so it's hard to debug).
Even on devtools, no exception appears?
Maybe I can print uncaugh exceptions on code and print then somehow on the inline result, I'll see if I can come up with something
Found it in the console... yeah... Uncaught in promise, :ex #unrepl/browsable
OK, I'm stuck at this point. I can't tell where it's blowing up. It isn't invoking my button callback when I click the button.
Here's more of that exception {:ex #unrepl/browsable [#error {:via [{:type clojure.lang.LispReader$ReaderException, :message "java.lang.ClassNotFoundException: repl-tooling.editor-helpers.Browseable", :data {:clojure.error/line 91, :clojure.error/column 2812}, :at [clojure.lang.LispReader read "LispReader.java" 314]} {:type java.lang.ClassNotFoundException, :message "repl-tooling.editor-helpers.Browseable", :at [unrepl.repl$i9hjMxfOQ2IzbCA5TVia2QQEJNg$classloader$fn__13265 invoke "NO_SOURCE_FILE" 486]}] ...
Ah... okay, that's not good. I didn't anticipate it. The problem is that the REPL is sending to Chlorine a code that it can't undestand.
The REPL will evaluate a code, and Chlorine will answer with a repl-tooling.editor-helpers.Browseable
object. When this code is sent to the interactive renderer, it will not undestand that repl-tooling.editor-helpers.Browseable
is
So, it can't de-serialize, and the whole thing becomes stuck 😞
I was a bit surprised that was even in the expression... it should just be a vector of hash maps.
UNREPL issues. Sometimes it does wrap "normal Clojure" objects inside a strange format that Chlorine wraps inside this Browsable
What (type <your-object>)
or (type (first your-object))
prints?
(I had this issue with Datomic Datons, for example)
I'm not sure what you're asking.
The result of next.jdbc/execute!
is a vector of hash maps.
Is it perhaps because the metadata on the rows includes a function value?
I just want to see what object next.jdbc/execute!
returns - what's the exact class name of the vector, or the hash-maps
To see why it's being interpreted as a Browsable
The rows are being interpreted as Browsable. Not the whole result.
It's a clojure.lang.PersistentVector containing clojure.lang.PersistentHashMap objects.
And those PersistentHashMap objects have metadata, which includes the key clojure.datafy/nav
(a symbol), whose value is a function object.
I assume those function objects are not going to survive the round-trip...
Okay, that can be the issue.
(no, probably they won't survive)
Yeah, that means I can't do this client-side... I hadn't thought too deeply about the protocol metadata stuff breaking if you render it over the wire. Oh well.
Well, I also hadn't thought that Chlorine could send Browsable
s over the wire, then break everything on the process. Well, one more reason to re-think these wrappers
Probably will be fixed on future versions. Just need some "hammock time" too :hammock:
Sure... It works really well for pure data!
Initial view...
After clicking the a
key...
If you click one of the idx values, it drills down into the value. You can click <
to go back to the previous view.
Wow, interesting! Great 👏
I added this to my init.coffee
so I had a hook into the new interactive renderer stuff
# Hook to test interactive rendering in Chlorine 0.6.0.
atom.commands.add 'atom-text-editor', 'sean:interactive-block', ->
result = Cl.ext.get_block()
if result.text
cmd = wrap_in_rebl_submit result.text
Cl.ext.evaluate_interactive cmd, result.range
and then bound that to ctrl-; i
(for i
nteractive 🙂 )Yes, I did the same to be able to test things 🙂 I'm also talking with @hoertlehner to allow plots (using the same structure for the interactive renderer), and probably "custom UIs" so it becomes easier to make these fancy interfaces
I'll think about some of the workflows I use and see what I can come up with for interesting renderers 🙂
(He's working on Pink Gorilla notebooks)
Those "browsable" wrappers definitely complicate things since they have to be unwrapped by the renderer...
Yes, but I think there's a workaround. Probably not too difficult either, just need to think a little bit and then do some experiments.