Glad someone started prototyping something already! I think you found the boundaries of the problem here pretty quickly. Here’s a full screenshot overview of your prototype for reference:
I think your question on what to do next is hard to answer, but maybe some context on what we’re dealing with exactly will help.
Here we go!
delimiter types
A frame is obviously associated with delimiters. So if we look, Clojure(script) has three types of them—but each with their own set of possible prefixes:
-
()
lists-
#()
functions -
#?()
reader conditional -
#?@()
reader conditional splicing
-
-
[]
vectors-
#queue []
queues -
#js []
js arrays
-
-
{}
maps-
^{}
metadata -
#{}
sets -
#js {}
js objects -
#:{}
namespace maps -
#::{}
alias-namespace maps
-
Further still, most can also be prefixed with quoting characters too:
-
'
quote - ` syntax quote
-
~
unquote -
~@
unquote splice
(And I only include the following for completeness, should we want frames for strings or unreadables that can still be printed)
-
""
strings-
#""
regex
-
-
#<>
unreadable
Part of the difficulty here is that Clojure syntax may continue growing, since it is a superset of edn
afterall (e for extensible), particularly for tagged literals like #js
, #queue
, #inst
, #uuid
.
frame construction
If we construct frames from what we know in the previous section, it seems feasible to start by determining a style for the three main delimiters—as you have done. Then to deal with all the prefixes, maybe insert them as labels, at least at first.
I think that reduces the problem of constructing frames to choosing styles for each:
-
()
box -
[]
box -
{}
box - prefix label
how to style
Some methods:
-
Frame colors might be used for distinguishing
()
[]
{}
, unless it clashes with colors used for possible depth-shading or syntax-highlighting. For example, Greenfoot elides syntax colors and shades frame backgrounds instead. -
Frame corners might be shaped according to
()
[]
{}
—round, square, curly? - Frame labels might be shown as a bubble somewhere on the frame.
Some sketches using corners and labels, but not colors:
how to style nested borders
Showing code in Frames makes code more visual, but tracing lines to connect parentheses into boxes might be more overwhelming than the parens themselves—if done without some attention to aesthetic, I mean.
To avoid overwhelming, I think nested border styles are usually controlled using two variables:
- border presence: which borders do you show, and when?
- border padding: should borders be padded or collapsed, and when?
Some examples below to make this concrete.
examples
-
@jiyinyiyong’s Cirru editor only draws some of the borders on each frame according to some heuristic:
-
Matthew Chadwick’s editor prototype shows all borders by virtue of its background shading, and has slight padding:
-
A more nuanced example is Antonin Hildebrand’s prototype “Plastic” (below). If you can make out the subtle shading borders, here’s how I see them:
- borders drawn for some forms—
(defn)
,(fn)
,(let)
,#(condp)
- no borders for others—
(if)
,(log-render)
, vectors[]
, maps{}
-
└
used for subtle left border on every textual line inside a multiline delimited form
What’s also interesting is that frames are still present for every multiline delimited form, as evidenced by the dangling placement of some close-parens
)
}
]
near where the borders would be drawn if visible: - borders drawn for some forms—
-
Jack Rusher sent me a quick screenshot of a concept he was exploring. There’s a dotted left border for list forms, and full borders for vectors and maps—which I think may be distinguished by context of the form, though I’m not sure:
-
Nassi–Shneiderman diagrams from 1973, I was shown recently. The triangle at the top denotes a condition that can branch to any form below it, and flow descends vertically from there. Notice that all borders are collapsed, except for the
DO
block showing padding on left to denote a LOOP: -
I did some experiments around adding SVG borders as an annotation layer for a text editor. It drew frames that would collapse at the bottom and right sides
As we saw with Jack’s example, we can really just show the left borders, and that gives us the standard “Indent Guides”:
-
To bring up Greenfoot again, they have a notion of pinning the head of the top frame as you scroll through it. For non-top-level nested frames, sideways annotations are used instead:
conclusions
There’s a lot that we can borrow from to build a Frame-based Clojure editor. Greenfoot is a good start, but there’s some extra problems to solve that are Clojure-specific—and some people have already started laying ground work here.
I haven’t been able to read through this recent journal yet, but that might help here too.
To answer your actual question, maybe using the “prefix bubbles” on the frame would be a good next thing to try.