Thanks for the detailed proof. There is one thing I would like to say about 
making tree operations undoable. This might be off the topic, but since you 
mentioned making this operation undoable, I wish to share some thoughts 
about undo/redo code.

At present Leo keeps instances of vnodes in its undo data records. This is 
not a very good idea (IMHO). Undo data should be immutable, but it is not. 
If we delete some clone, the node is recorded in the undo data record. 
Later we change the other clone in the outline and it is also changed in 
the recorded undo data. It would be much better to record (gnx, h, b) 
instead of vnode instances. The v.children and v.parents can be recorded as 
a str

 '|'.join(x.gnx for x in v.children) # or v.parents 

That way undo data will be immutable and more resilient. It could be saved, 
transferred over the net, tested, examined, ... When redoing operations it 
is easy enough to turn those gnx values to real nodes. If it doesn't exists 
anymore, we can create new one with the same gnx. If it exists it can be 
reused. When using .db format, Leo stores complete outline data in sqlite3 
database using this technique. All the information necessary to recreate 
the outline can be stored as a bunch of simple immutable values.

The history_tracer plugin does this. It creates text representation of the 
complete outline and sends it to the local server which stores snapshots in 
the database. Before storing a snapshot it calculates end encodes changes 
in a very compact text form which is stored in the database. This allows 
keeping thousands of versions of a very large outline in a database which 
is maybe just a few Mb larger than the original content. For example 
LeoPy.leo.history on my computer contains 10222 recorded versions of 
LeoPy.leo. One complete snapshot of LeoPy.leo is about 5Mb. The database 
file is 8Mb.

While the saving a snapshot of the whole outline each time user presses a 
key is not very practical, especially if the outline is large, saving 
snapshot of one part of the outline is certainly doable. On my new computer 
(which is very fast) creating the snapshot of the complete LeoPy.leo takes 
about 20-25ms. On the slower machines it might take even 100-150ms. But 
creating the snapshot of the subtree with 50 nodes should not take more 
than 10-15ms on any computer. It is not often the case that user changes 
outline by more than that. So, most of the operations can have snapshot of 
all necessary data to completely restore the selected part of the outline 
within 15ms. And snapshot (in this case) is a string, but can also be a 
tuple of immutable values or something similar immutable.

I know that the undo/redo code is spread all over the Leo's code base, and 
changing it is a huge and risky task. But if you are making new operations 
undoable, consider using only immutable data. In the case of 
deletePositionsInList, I would suggest keeping track of (index, gnx) pairs, 
not (index, v) pairs. The vnode won't disappear during the execution of the 
deletePositionsInList method, but it might disappear by the time undo is 
required.

The rust compiler would not allow storing the references of vnode instances 
in the undo records and allowing vnode to change. One can have either any 
number of references with just a read access (i.e. which can't mutate the 
instance), or just a one reference with the write access (which can mutate 
instance). This principle is found to be a great help in preventing the 
bugs. So, it would be wise to follow it in Python too.

Vitalije

-- 
You received this message because you are subscribed to the Google Groups 
"leo-editor" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To view this discussion on the web visit 
https://groups.google.com/d/msgid/leo-editor/b089f0d7-65dc-47c2-91a7-d68eaf6bd576%40googlegroups.com.

Reply via email to