I've been looking at the shrubbery and Rhombus specs and trying to assess how suitable they are for allowing the structural navigation/editing commands that are typically available in lisps.
There are 2 big concerns I have that tie together:
If we only rely on shrubbery without enforestation, AFAIK you can't tell how a group breaks down into typical expressions. To an extent, this might actually be a boon from a user interface perspective. Users who are looking at an expression like a * b + c can't see the AST structure (+ (* a b) c) so if navigation acted as if that was the ground truth it would probably be confusing. So treating that as one big expression could be fine, as long as we are only talking about chains of infix operators. However, I think it would be very surprising for users to discover that a + if b > 0 | c | d is being treated as one expression instead of two, and we would typically expect that if the cursor were in front of the if that moving forward would skip over the entire expression unless we chose to move "in" to it at which point we would expect to be able to visit the then and else branches. IIUC, from a shrubbery POV we would end up with a group containing a,+,if,>,0 which mixes expressions.
If we try to make structural navigation/editing based on enforestation, then it's not clear how to treat code that is being passed into macro invocations. The code isn't enforested yet, so just by looking at the macro call site you can't be sure where expression boundaries are. If you see a + if b > 0 | c | d you don't know if you should navigate it as if it were normal Rhombus code or that's a coincidentally similar series of tokens actually expressing something in a totally different language. If the navigation/editing were shrubbery based, we wouldn't have this problem because any code being fed into the macro must still be valid shrubbery.
So it seems fraught either way. This is one of the major advantages of sexp notation though and it would be nice to preserve it. It's possible I don't understand the enforestation/macro protocols in enough detail yet to see an obvious answer, has anyone dug into this?
The DrRacket support for shrubbery notation (via #lang shrubbery or #lang rhombus) includes shrubbery navigation analogous to S-expression navigation. For example, M-right for "forward-sexp" is implemented as moving forward over a term. But it really is shrubbery navigation at the level of shrubbery terms and groups, and not expression navigation.
Of course, S-expression navigation also doesn't navigate expressions at the level of Racket, either. For let and cond, navigation at points will be with respect to clauses, and for a weird macro that rewrites (m (+ x y) z) to (- (* z x) (+ 2 y)), the connection between s-expressions and expressions becomes tenuous. But, partly by convention, the gap is not as large between S-expression navigation and expression navigation.
It would be possible for a tool to take advantage of post-expansion information to refine navigation, similar to the way binding arrows work in DrRacket, but I don't know whether that would be useful enough.
I just want to add that, something that is easier to navigate probably also means that it’s easier to understand. a + if b > 0 | c | d is difficult to navigate and to understand due to the lack of parenthesization; realistically, you should probably write a + (if b > 0 | c | d), or even extract if b > 0 | c | d into a separate variable. (Moreover, it really just happens to work in this order; if b > 0 | c | d + a wouldn’t mean what you want.) Badly-formatted Lisp is likewise difficult to understand, even with structural navigation.
I think my overall experience with shrubbery notation is that the indentation and conventional grouping make everything quite easy to navigate and understand, just like how well-formatted Lisp is easy to understand. In occasions when I do want structural navigation, I think navigation by shrubbery terms is the correct thing to do (but we could probably add ways to navigate by shrubbery groups as well?).
The distinction you're drawing is unclear to me because the docs here make it sound like a term is either an atom or a group, so IIUC manipulating terms already lets you manipulate groups?
Yeah my confusion is that it's circular. terms are sequences of groups, and groups are ()/[]/{}'ified terms, so what's an example of a group-level navigation action that isn't also a term level navigation action or vice versa? The only thing I can think of is groups that exclusively contain atoms, but then you can just use ctrl+right/left.