The way I typically work is, I start the editor from the Terminal using lein run
.
cd editor
lein run
Then load a project as usual in the editor. If for example I wanted to explore the Sprite component, I'd create a new Sprite somewhere in the project and open it.
From Cursive, I have a Remote REPL run configuration set up, as detailed in our Cursive Setup Guide. When I run it, it will connect to the running editor process I started from the Terminal.
I'd switch the namespace of the REPL to sprite.clj
, so I have access to all the sprite-related functions from that namespace. I switch namespaces a lot, so I have this configured as a keyboard shortcut, as detailed in our Cursive Setup Guide.
I can now evaluate expressions in the sprite
namespace from the REPL window. Or I can put a (comment ...)
block at the bottom of sprite.clj
where I experiment, so I can save my in-progress experiments in case I have to restart later.
First, I'd require the dev
module so I can conveniently get the currently open resource or the current selection, things like that. I can then use (dev/active-resource)
to get the node-id of the open resource (my sprite), or I can use (dev/sel)
to get the first selected node-id within a resource that has structure, such as an Atlas or Game Object.
Here, I've put in a comment block where I require the dev module and evaluate the default-image
property on the currently open .sprite
resource.
(comment
(require 'dev)
(let [sprite (dev/active-resource)]
(g/node-value sprite :default-image)))
I'd execute the Send Top Form to REPL command on each expression in turn. I do this a lot, so I have this configured as a keyboard shortcut, as detailed in our Cursive Setup Guide.
At any time, I can redefine a function inside sprite.clj
, replacing its implementation in the running editor. This is great when iterating over a fix.
Be warned however that the editor caches node values aggressively. You might need to run (g/clear-system-cache!)
before you re-run (g/node-value ...)
to see the changes from redefined functions, etc. Also, if you redefine a g/defnk
expression, you also need to redefine the g/defnode
expression that uses it, since it is inlined.
You can inspect the type of a node-id using (g/node-type* node-id)
. And also list its inputs, outputs, and properties using (dev/node-labels node-id)
. You can inspect connections between nodes using (g/sources-of node-id output-label)
, (g/targets-of node-id input-label)
, and the convenience function (g/node-feeding-into node-id input-label)
when you just want the node-id that's connected and don't care which output label.
For example, here I'm getting all the attribute
lines from the Vertex Shader that is referenced from the Material that is assigned to the currently open Sprite.
(comment
(require '[clojure.string :as string])
(require 'dev)
(let [sprite (dev/active-resource)
material (g/node-feeding-into sprite :material-resource)
vertex-shader (g/node-feeding-into material :vertex-resource)
vertex-shader-lines (g/node-value vertex-shader :lines)
attribute-lines (filter #(string/starts-with? % "attribute")
vertex-shader-lines)]
attribute-lines))
There's lots more you can do of course, but I hope this has provided some idea of how you can work with a REPL.
Reloading files that redefine Java classes can lead to annoying schema-validation
errors since the types don't match anymore. You can temporarily disable schema checks by merging the no-schemas
profile into the default dev
profile when you start your session:
cd editor
lein with-profile +no-schemas run