Description
SyntaxEditor has a very cool design surface where it allows one to describe a set of changes to a tree as a series of edits (as opposed to a bottom-up tree rewrite). Those edits can be of two forms. A node->node change
(i.e. editor.ReplaceNode(oldNode, newNode)
) or a node->callback
change (i.e. editor.ReplaceNode(oldNode, currentNode => ...)
). The benefit of the latter is it lets you see what your oldNode
now looks like after previous edits have been applied. This is important in case you are making changes to nodes that contain other nodes that are being changed.
However, this functionality also comes at a cost. Because of the node->callback
form, the editor has to apply changes one-at-a-time, so that it will have the proper currentNode
s to pass to callbacks. This is because the currentNode
will not be free-floating. Instead, it is stitched into its own tree. Allowing the callback to see things like the real parent
at that point in the edits. When making many changes to a tree, this can end up becoming quite expensive as each change involves an entire tree rewrite. While the rewrite itself is still pretty efficient (usually only involving a logarithmic walk), that still makes the entire op O(n log n)
. And, even at n log n
, then constants involves are fairly high given lots of overhead in the all the subcomponents involved.
We can do a far better job here in the fairly-common case of all the edits just being simple node->node
changes. In that case, there is no need to be changing the tree serially, as there is no need to know the right currentNode
to pass to a callback. Instead, we can just to a single bottom-up rewrite that processes all the changed nodes in one pass.
--
Doing this doesn't seem like it would be very complex. We would simply need to walk the 'Change's that SyntaxEditor was storing up. If all of them were non-callback 'Replace' changes, then we could simply make a call to root.ReplaceNodes
which will do the bottom up rewrite for us, with all the provided nodes.