This is #1539 <https://github.com/leo-editor/leo-editor/issues/1539>. I 
assigned this issue to 6.3 because because of this method's long history of 
failure.

Yesterday I saw a new approach. If this new approach is sound, and I think 
it is, I am willing to consider putting the new code in 6.2.

*Thought experiment*

I considered the possibility the following approach might be the only way:

1. Generate a list of positions meeting some criterion.
2. Delete one of the positions.
3. Regenerate the list of positions meeting the same criterion.

This surely must work. However, c.deletePositionsInList is not privy to the 
criterion, so a practical way forward is required.


*Aha: p.position_after_deleting(p2)*

The essence of our woes is that deleting *any *position P in the outline 
may affect all the other positions in the list passed to 
c.deletePositionsInList. Rather than guessing, we must *compute *the effect 
of deleting P on all positions in this list. 

p.position_after_deleting(p2) will return None if p will not exist after 
deleting p2. Otherwise, it will return the *adjusted* value of p.

Calculating this adjusted value is non-trivial, but surely it is possible. 
Something like this:

- If p2 is p or an ancestor of p, p will no longer exist. Return None.
- Otherwise, len(p.stack) will remain unchanged, as will parent.v for 
parents in p.stack.
- If p2 is a previous sibling of p_i, (p or one of p's parents), then 
p_i.childIndex will decrease by 1.

*A sound algorithm*

The following is untested. Imo, it's ideas are most likely sound.

def deletePositionsInList(self, aList):
    """Delete all positions in the given list."""
    c = self
    while aList:
        # del_p is the next position to be deleted.
        del_p = aList.pop()
        # Calculate new_positions.
        new_positions = []
        for p in aList:
            new_p = p.position_after_deleting(del_p)
            if new_p:
                new_positions.append(new_p)
        # Delete del_p.
        p.doDelete()
        # Check the new positions.
        for p in new_positions:
            assert c.positionExists(p)
        # Update aList.
        aList = new_positions
    # Make sure c.hiddenRootNode always has at least one child.
    if not c.hiddenRootNode.children:
        v = leoNodes.VNode(context=c)
        v._addCopiedLink(childIndex=0, parent_v=c.hiddenRootNode)
    # Redraw.
    c.selectPosition(c.rootPosition())

Notes:

- aList.pop() ensures that the loop terminates.

- p.position_after_deleting makes it possible to call p.doDelete rather 
than vnode-based code.

*Summary*

Since day 1, c.deletePositionsInList has been based on guesswork and 
wishful thinking. I am completely responsible for this.

p.position_after_deleting promises to put c.deletePositionsInList on a 
solid foundation. I am willing to consider putting the new code in Leo 6.2, 
provided a substantial set of unit tests for p.position_after_deleting 
exists.

I'll attempt p.position_after_deleting later today. It's time for bed.

All comments welcome.

Edward

-- 
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/c835d23d-8e54-49fd-94e2-d770d3b89379%40googlegroups.com.

Reply via email to