Treefrog: A code editor that uses both AST and text editing commands
treefrog-editor.com
treefrog-editor.com
The main idea is to design all of the editing and navigation commands around the thought processes (instead of designing around a data structure, which I think is where traditional editors have gone wrong), so that it feels natural and intuitive to use - like something that's been designed for what editing code actually involves, as opposed to a text editor that's had lots of code intelligence stuff added on top of it.
The editor operates on the AST down to the lowest level, with careful design of keyboard input and cursor feedback to make that not too painful for things like infix operators. The language is described by a grammar, and there is an AST macro language.
My conclusion afterwards was that the shift at that time away from keyboard input towards editing with a mouse was negative for editing in terms of the AST (rather than text), since with AST editing there's a less direct connection between seeing and pointing and what you're editing. I got interested in other things.
But perhaps that was a premature conclusion.
(TeXmacs is not based on TeX nor emacs but is inspired by both.)
A full s-expression based grammar for mathematics is unfortunately not something that you can write on paper and requires white space delineation to be readable. On a screen with something like emacs paredit both of those are a breeze however. The fact that higher order functions don't need a special notation is really freeing.
How would Polish notation help, for instance, with things like describing set membership or logical relations? How is this more readable (using ASCII):
-> a b
Than the current approach: a -> b
Which provides a convention sense of the relationship that the former does not? The second one can be "read" in order: a implies b. The first cannot.The same reason why geometers needed to be told to start using Arabic numerals by algebraists: mathematics is asking new questions and the old tools aren't good enough. That we even need to make a notational distinction between operators like addition and operators like high order partial derivatives should show that clearly enough.
>How would Polish notation help, for instance, with things like describing set membership or logical relations? How is this more readable (using ASCII):
Start working on longer expressions and the lack of parenthesis becomes a godsend.
-> -> a b c and -> a -> b c can be parsed in only one way, by comparison with infix notation you need miscellaneous parenthesis to show the way you want a -> b -> c to be parsed: (a -> b) -> c and a -> (b -> c). Or you need to memorize an arbitrary number of rules which increase exponentially with each new operator you add.
> Which provides a convention sense of the relationship that the former does not? The second one can be "read" in order: a implies b. The first cannot.
In English and only for binary operators, and even then you can just as easily understand "add 5 and 6" as you can "5 plus 6". How would you use infix notation to express a ternary or higher operator? The notation for the definite integral needs a super and sub script as well as a dummy variable for an operator that is fundamentally a ternary one. By comparison in prefix notation it becomes definite_integral upper_limit lower_limit function.
At least those were other mathematicians who could understand the domain and the importance of notations for the work.
Hah, no, "-> -> a b c" is not easy to parse, except for a computer. going are people not to read easily. It's a silly argument that prefix is fundamentally easier for humans when it disrupts the flow even in your example.
Even using the words, "implies implies a b c", does not parse well as a reader, even if it can only be parsed one way. If you go to a higher arity operation, switch to function (which is prefix, like polish) notation if it makes sense. But even then, we have things like the integral which uses physical placement to take (typically) up to 3 arguments:
b
∫ f(x) dx
a
With other similar operations. And if order of operations is not clear from an expression, then yes, use parentheses. That's what they have been used for for quite a while.The only argument I'm seeing here is that you can't parse prefix notation and won't put the effort into trying it, which is the same argument people used against Arabic numerals in the middle ages.
A prefix only language is fundamentally easier to read and write to anyone who hasn't spend two decades learning the mishmash of pre, in, post, and upside-down fix that mathematics currently uses.
>But even then, we have things like the integral which uses physical placement to take (typically) up to 3 arguments:
Using four variables. It's amazing to me people defend this type of mess as something meaningful instead of a terrible historical contingency which we should get rid of as quickly as we can. In prefix notation you can just use "integral upper lower function" to convey the same information in pure text without the need for inventing TeX and vector displays.
= 1 + square sin x square cos x -- a trig identity
/ + - b √ - square b * * 4 a c * 2 a
^ oops, ambiguous, you need a new symbol for negation
Note as well, in that second example, the great distance between the division operation and the divisor. This does not lend itself to readability by people, they have to keep the entire stack of operations in their head. Ok, so you switch the notation to be more tree-like and use indentation (how I write complex Lisp math expressions): v still ambiguous, what alternate symbol can we use?
/ + - b
√ - square b
* * 4 a c
* 2 a
Ok, somewhat clearer, but hardly "easy" to read for a person. At this point you may as well just put back the parentheses (and be back at Lisp s-exprs, which as you noted earlier is not well-suited to handwriting). The mathematical expression is clearer for people. It requires them to keep less in their head at any one point in the reading. _________
-b + √b^2 - 4ac
---------------
2a
I'm not saying this is perfect, there is room for improvement in this form, but it is going to be better for the average math literate person (by which I mean, someone who has at least covered high school algebra). Even if you taught prefix notation from the start, the user of that notation still has to keep more in their head as they try to parse it.It's perfectly legible to me and I use it daily in my (mathematical) work.
Again all you're saying is that you're not used to it and it must be a bad notation. Just like how people who were shown Arabic numerals defended Roman numerals.
I'd do you one better and point out that the example should be = 1 λ x square sin x square cos x since having unbound variables is poor form after someone invented lambda calculus.
And yes, you need a symbol for negation which is a step up since a-b is also ambiguous in the same sense.
For the second one you easily see why words are superior to symbols when you stop trying to use hieroglyphs and start using letters instead: div add neg b sqrt sub pow b 2 sub times 4 times a c times 2 a. I can define new functions with well known meaning without having to copy special characters or invent type setting systems.
+ and - are bad to use, but λ is ok to use? Words are the thing to use for "add" and "sub" and "div" (why not the whole word?), but not "equals" or "define function"? And the words you choose are strictly English, which makes your mathematical notation no longer mutually comprehensible across most of the globe? Those are all positives to you? That's how math notation will be dragged into the 20th century?
And, what, we now teach lambda calculus to elementary school kids once they start learning about variables? At least try to come up with a plausible argument.
> I use it daily in my (mathematical) work.
I find this highly unlikely, but sure, if you're not a troll, you've found a notation that (apparently) works for you (what kind of math?) and you think it's better but haven't bothered to think it though.
And you still haven't answered the question on the second example, you just made it more verbose. The reader still has to remember (with zero punctuation or other indicators in your example) that the dividend for the div is the next 14 tokens and the divisor the last 3. Because that's totally readable and comprehensible.
If you're not a troll, your idea isn't even half-baked.
Well, your judging that would be easier if you could use it, rather than just read about it... I still have the source code, but since it was designed to work with a vector graphics display attached to a PDP-11, getting it to work in a modern environment would take some work. We had only one vector graphics display, so there was no chance then to have a significant user community, which was another problem.
I don't how language parsing should be exposed to make it easy to have parsing in editors. But I believe some efforts like tree-sitter are most welcome and also more inline with what we expect editors to be.
I hacked up a plug-in yesterday to let me run tree sitter queries over my codebase. I’d upgraded a dependency and some patterns were no longer valid so I was able to track them down in a way that would never have been possible with regex etc (certain function called with more than one argument chained with one of several other functions).
Perhaps you need to allow the developers to reach invalid states, because the editors I tried just refused entries which would result in a syntax violation. This is harder to implement though. :)
I feel that because no one uses AST aware editors no one understands what that would bring to the table. A counter example I've used CAD programs. And all of them store the design files as databases internally. Which means you can and people do preform database operations on them. Generate reports and perform update operations just like you would with an SQL type database.
(https://ieeexplore.ieee.org/book/6267325)
showed that it wasn't practical to build this kind of editor. Perhaps that was not at all the point; it was a long time ago.
I wonder, though, can anyone explain 1) if Reps' thesis does bear on the Treefrog work 2) what it was in Reps' thesis that bears on the difficulty of making syntax-tree based code development tools?
There's some existing work in this area you might find interesting as well:
https://twitter.com/dm_0ney/status/1414742742530498566
https://hazel.org/build/dev/ (expand the left side pane by clicking the (?) to see the valid operations)
Is it fair to think of IDEs that include language-specific refactoring capabilities as a providing a developer abstraction over AST editing?
https://atariwiki.org/wiki/Wiki.jsp?page=Lisp
Source is here: https://atariwiki.org/wiki/Wiki.jsp?page=LispEditor
If you really wanted a vi-like experience for editing lisp, you could figure out how to make an editor like that full-screen and interactive.
(defun double-all-the-things (things)
(mapcar (lambda (x) (* 2 x)) things))
would be straightforward to navigate through with an s-expr aware editor.- I cannot reach inside if and while statements using tree mode
- It is not clear which lines are available for refactorings
- dragging a line reveals "+ If" and "+ else if" under an if statement. Dropping the line onto it does not work.
- tree based moving does not move the viewport
About the business side of it:
I think it will be hard to create a whole editor just for these features. I know that Jetbrains editors also have ast aware refactorings and I often use moveBySymbol in sublime text which acts a lot like your tree navigation. I think it makes more sense to create this as a plugin, but it will be difficult to monetize that.
Anyway I applaud you for creating something that actually works!
- can you move into an if/while if it's not already selected? For already selected blocks I've had it both ways and found it better to be able to drag from anywhere in the block - you can select a child by clicking it. Both ways have their advantages and disadvantages though. "d" should also work to navigate to a block's first child.
- yes maybe some kind of visual indicator would be good, but if required after the first few uses then the interaction would need to be redesigned I think, as there shouldn't be any unexpected or unintuitive behaviour
- hmm, that's working for me, does anything at all happen when you drop it?
- yeah, haven't implemented that yet
I thought about doing it as a plugin as well, but I think trying to add something genuinely new to an existing editor would be possibly harder than just making one (I've been working on Treefrog for about 7 months full time and using it as my only editor for the last 2), and would end up either not fitting in well with the existing UI or having clunky UX to switch between modes etc. Designing new modes from the ground up (and being able to prototype them quickly with web tech and Electron) allows for much easier experimentation.
Sorry I meant the condition of while, for and if.
> hmm, that's working for me, does anything at all happen when you drop it?
Now that I try it again it seems to be working. Before it would just drop the line bellow or above the "+ if".
In many cases it probably requires awareness of packaging in a way that normal AST manipulation does not, at least to be particularly useful it would.
(This is on a 4K display running Win11 with UI scale set to 200%)
Anyway I am receiving an error on page load: Uncaught TypeError: Cannot read properties of undefined (reading 'watch') under platform.jsonStore.watch
Semantic merging would also be a nice feature.
Other cases are similar, e.g. being able to just drag and drop a div into another div without thinking about the exact text selection - the benefit is having to think slightly less about the mechanics of your editor or the particular data structure (text or ASTs).
I think for the first case there is a ‘wrap in if-else’ refactoring in Webstorm/Intellij, but I rarely use it.
Second case is interesting, I would be excited to have this for React components.
I've not hear many success stories about building companies around single not-yet-established pieces of software. But I've had similar dreams and wish you luck.
* ctrl-home/end do not go to very top/bottom
* python tree-mode does not do anything sensible