ENB: Bugs in Leo's outline-paste commands

27 views
Skip to first unread message

Edward K. Ream

unread,
Aug 17, 2023, 7:53:00 AM8/17/23
to leo-editor

This Engineering Notebook post discusses subtle problems with Leo's paste-outline commands: paste-node, paste-retaining-clones, and paste-as-template. A new unit test (in PR #3473) revealed these problems. They may be ancient.


This post will be of interest only to hard-core Leo devs. Feel free to ignore it. Note: Remember that I'll be discussing code in the new (6.7.5) codebase. 


A subtle bug


Dumps in a new unit test reveal that c.recompute_all_parents fails:


def recompute_all_parents(self) -> None:

    """

    Recompute all v.parents arrays using neither positions

    nor v.parents ivars.

    """

    c = self

    root_v = c.hiddenRootNode


    # Clear all v.parents arrays.

    root_v.parents = []

    for v in c.alt_all_unique_nodes():

        v.parents = []


    # Loop invariant: Visit each *parent* vnode *once*.

    # Child vnodes may be visited more than once.


    # Visit the hidden root.

    for child in root_v.children:

        child.parents.append(root_v)

    # Visit all other parents.

    for parent in c.alt_all_unique_nodes():

        for child in parent.children:

            child.parents.append(parent)


How can this "obviously correct" code fail? What's going on??


Aha! c.alt_all_unique_nodes() can yield nodes with different ids than those in root_v.children. In other words, two distinct VNodes may share the same gnx. Big oops.


The cause of the bug


Leo calls c.reassignNonClonedIndices after calling c.unarchive_to_vnode. But unarchive_to_vnode itself creates (or patches) parent/child links.


In short: unarchive_to_vnode must reallocate gnxs.


fc.gnxDict


Lurking behind all this code is the infamous fc.gnxDict dictionary. Keys are gnxs; values are the corresponding VNodes. c.recreateGnxDict supposedly recreates this dict. Various read/write/paste helpers save and restore this dict for impossible-to-remember reasons.


I would love to remove this dict, but doing so probably won't work. Because of undo, Leo never deallocates VNodes! Instead, I am leaning towards replacing fc.gnxDict with, say, c.all_gnxs_dict. Leo will:


- only add keys to this dict.

- never delete keys.

never save/restore this dict.


Only a detailed examination of Leo's entire codebase will show whether this plan is feasible.


Summary


Let this post be a warning for anyone foolish enough to assert that some code is "obviously correct."


It's impossible to say whether the bugs discussed here exist in Leo's existing codebase without adding new unit tests to devel. I'm not going to do that. It's too late to change Leo 6.7.4.


All calls to c.recreateGnxDict seem dubious.  PR #3473 is an opportunity to re-engineer how Leo remembers existing VNodes.


All three paste-outline commands and their undo/redo helpers must likely confront these issues explicitly. "Clever" solutions will likely fail.


Edward

Edward K. Ream

unread,
Aug 18, 2023, 7:28:59 AM8/18/23
to leo-editor
On Thursday, August 17, 2023 at 6:53:00 AM UTC-5 Edward K. Ream wrote:

> This Engineering Notebook post discusses subtle problems with Leo's paste-outline commands: paste-node, paste-retaining-clones, and paste-as-template. A new unit test (in PR #3473) revealed these problems. They may be ancient.

There may be problems with Leo's paste-outline commands, but these issues likely arise from new code. For the record, that's good news :-)

> Lurking behind all this code is the infamous fc.gnxDict dictionary. Keys are gnxs; values are the corresponding VNodes. c.recreateGnxDict supposedly recreates this dict. Various read/write/paste helpers save and restore this dict for impossible-to-remember reasons.

A cff reveals 43 places where Leo uses this dict. It almost always works the way I wanted the new  c.all_gnxs_dict to work. Again, that's good news. There is no need for a new dict:

- Remove c.recreateGnxDict.
- Remove all save/restore logic pertaining to fc.gnxDict.
- Write new unit tests for this dict, especially during undo/redo.

Edward
Reply all
Reply to author
Forward
0 new messages