We need keys to be fixed as we add/delete nodes because we're going to
start recording them next inside Nodes to encode edges.
Since there isn't a clear name for nodes in this app, I came up with a
way to autogenerate keys.
This is a long-standing problem in driver.love as well:
* each node requires an edit buffer and various decorations
* while the mouse is down during a move/resize it's really helpful to
see the decorations change
* but to change decorations we have to go all the way back to A(),
which is very slow if there's lots of nodes on the surface
Solution: create a variant of A called A1 that only does the work of A
for a single node id (index) into Nodes.
This requires tracking for every shape we render in Surface, which id it
is part of. That way we can selectively delete just shapes belonging to
a single id.
Caveat: id is a scalar, so this approach can't handle any nesting, only
a flat array of objects. But that's good enough for both driver.love and
this app.
Until now the hover actions were happening in surface coordinates sx/sy
but the mouse press action (done so far) was happening in viewport
coordinates vx/vy. Now it's consistent. Surface coordinates make more
sense since most data in memory uses them.
1. No more version history, now we have just the contents of the current
version.
2. Editing a definition no longer changes the order in which definitions
load.
This should make repos easier to browse, and more amenable to modify.
You don't need driver.love anymore. And a stable order eliminates some
gotchas. For example:
using driver.love, define `Foo = 3` in a definition
define `Bar = Foo + 1`
edit and redefine `Foo = 4`
Before this commit, you'd get an error when you restart the app.
Definitions used to be loaded in version order, and editing a definition
would move it to the end of the load order, potentially after
definitions using it. I mostly avoided this by keeping top-level
definitions independent. It's fine to refer to any definition inside a
function body, we only need to be careful with initializers for global
variables which run immediately while loading.
After this commit you can still end up in a weird state if you modify a
definition that other later definitions use. In the above example, you
will now see Foo = 4 and Bar = 4. But when you restart, Foo = 4 and Bar
= 5. But that's no more confusing than Emacs's C-x C-e. It's still
a good idea to keep top-level definitions order-independent. It's just
confusing in a similar way to existing tools if you fail to do so. And
your tools won't tend to break as badly.
Why did I ever do my weird version history thing? I think it's my deep
aversion to risking losing any data entered. (Even though the app
currently will seem to lose data in those situations. You'd need to
leave your tools to find the data.) Now I rely on driver.love's undo to
avoid data loss, but once you shut it down you're stuck with what you
have on disk. Or in git.
I also wasn't aware for a long time of any primitives for deleting
files. This might have colored my choices a lot.
Functions can refer to each other, but global variable initializers
shouldn't.
But this doesn't work. That comment keeps growing to capture more corner
cases.
Step back. What am I trying to achieve?
I'm not trying to create a better abstraction for programming with. I'm
trying to use an existing abstraction (LÖVE) without needing additional
tools.
I'm not supporting end-user programming, only end-programmer
programming. What happens in a regular LÖVE program if you use a global
before it's defined? You get an error, and you're on the hook to fix it.
But it's obvious what's going on because a file has an obvious sequence
of definitions. But what if you have multiple files? It's easy to lose
track of order and we mostly don't care.
The important property existing dev environments care about: merely
editing a definition doesn't _change_ the order of top-level
definitions. Let's just provide this guarantee.
We'll no longer load definitions in order of their version. Just load
definitions in the order they were created. Editing a definition doesn't
change this order. Deleting and recreating a definition puts it at the
end.