OMG: It's easy to copy outlines!

43 views
Skip to first unread message

Edward K. Ream

unread,
May 19, 2022, 11:17:59 AM5/19/22
to leo-editor
PR #2655 shows how to copy an outline iteratively. Just use p.moveToThreadNext as a template! Like this:

p = c.rootPosition()
for p2 in c2.all_positions():
    p.h = p2.h
    p.b = p2.b
    p.u = p2.u
    if not p2.hasThreadNext():
        break
    # Like p.moveToThreadNext.
    if p2.hasChildren():
        p = p.insertAsLastChild()
    elif p2.hasNext():
        p = p.insertAfter()
    else:
        while p2:
            p2 = p2.parent()
            p = p.parent()
            if p2.hasNext():
                p = p.insertAfter()
                break

Unlike all my recursive attempts, this iterative solution effortlessly inserts new nodes.

Summary

It was only by chance that I stumbled across this iterative solution. This discovery was shocking, disorienting, and upsetting. How could I have missed this solution all these years?

I'll soon submit a PR for a slightly more general utility method.

Edward

P. S. For reference, here is p. moveToThreadNext:

def moveToThreadNext(self) -> "Position":
    """Move a position to threadNext position."""
    p = self
    if p.v:
        if p.v.children:
            p.moveToFirstChild()
        elif p.hasNext():
            p.moveToNext()
        else:
            p.moveToParent()
            while p:
                if p.hasNext():
                    p.moveToNext()
                    break  #found
                p.moveToParent()
            # not found.
    return p

EKR

Edward K. Ream

unread,
May 19, 2022, 12:07:33 PM5/19/22
to leo-editor
On Thursday, May 19, 2022 at 10:17:59 AM UTC-5 Edward K. Ream wrote:

PR #2655 shows how to copy an outline iteratively. Just use p.moveToThreadNext as a template!

Some notes:

Félix, the upcoming PR will also rewrite server.copy_node. The new code should be about 10x faster. Otoh, most of the real cost is in transmission time...

Don't confuse the new utility with the code that implements the copy-node command. The PR will rename the existing c.copyOutline to c.copyNode to reduce potential confusion.

Several existing methods use vnodes instead of positions. However, the new position-oriented helper should be more flexible.

Edward

tbp1...@gmail.com

unread,
May 19, 2022, 1:24:01 PM5/19/22
to leo-editor
wouldn't a deep copy of the root position do it?

Edward K. Ream

unread,
May 19, 2022, 2:12:52 PM5/19/22
to leo-editor
On Thu, May 19, 2022 at 12:24 PM tbp1...@gmail.com <tbp1...@gmail.com> wrote:
wouldn't a deep copy of the root position do it?

A deep copy of the root vnode would copy only that subtree. Worse, vnodes have state that depends on the entire outline.

There are, I think, valid vnode-based solutions already. However, I think the code I discussed is (in some loose sense) the simplest and most intuitive.  It's certainly a new idea. I'll remember it for the rest of my life. Hehe, scroll down.

Edward

Edward K. Ream

unread,
May 19, 2022, 5:35:52 PM5/19/22
to leo-editor
On Thursday, May 19, 2022 at 10:17:59 AM UTC-5 Edward K. Ream wrote:

PR #2655 shows how to copy an outline iteratively. Just use p.moveToThreadNext as a template!

Alas not. The pattern doesn't handle clones properly!  One fix: the algorithm could enter a "skipping" mode when seeing an already-cloned mode. I'll continue my investigations...

Happily, LM.openEmptyWorkBook can avoid copying trees entirely. Doh! Just open CheatSheet.leo, then do save-as. Coming soon.

Edward

Edward K. Ream

unread,
May 19, 2022, 6:06:12 PM5/19/22
to leo-editor
On Thursday, May 19, 2022 at 4:35:52 PM UTC-5 Edward K. Ream wrote:

Happily, LM.openEmptyWorkBook can avoid copying trees entirely. Doh! Just open CheatSheet.leo, then do save-as.

Done in devel at rev dd9d1. This should finally put #2645 to bed.

Edward

vitalije

unread,
May 20, 2022, 4:34:39 AM5/20/22
to leo-editor

Unlike all my recursive attempts, this iterative solution effortlessly inserts new nodes.


This solution may look like iterative, but it just hides the recursion inside moveToThreadNextEffectively the recursion is implemented by hand using p.stack instead of the true recursion implemented by using the Python stack.

Have you tested it for speed? I really doubt that it would gain any speed improvements.  I don't know against which other implementation you compare the speed, but I am almost 100% certain that it would be far slower than using recursion directly on v-nodes.

I would expect this script to be slower than the naive recursive implementation using recursive calls. The solution implemented by using Python generators directly on v-nodes (at least in my experience) is the fastest.

Edward K. Ream

unread,
May 20, 2022, 6:58:16 AM5/20/22
to leo-editor
On Fri, May 20, 2022 at 3:34 AM vitalije <vita...@gmail.com> wrote:

This solution may look like iterative, but it just hides the recursion inside moveToThreadNext

Imo, the "solution" is iterative, regardless of the nature of moveToThreadNext.

Alas, the whole idea appears doomed because there is no easy, obvious way to handle clones.

Using copy/paste is likely to be the best general way to copy trees. Indeed, copy/paste of xml involves Leo's general read/write logic, which will handle clones correctly if anything does.

For #2645, the original code opened CheatSheet.leo visibly, which caused an annoying flash. Trying to open CheatSheet.leo in a hidden commander had unexpected problems. So the final fix was to use the save-as logic.

Edward
Reply all
Reply to author
Forward
0 new messages