I'm writing some commands for myself to help manage lists of things. I will
bind the commands to keystrokes which I will be able to use to quickly add
new items and highlight which items I've recently worked on. These commands
will also create clones inside "@day <date>" (and automatically create
the @day node if hasn't already been created). These details are not
important for the point of this post. I just wanted to give a sense of
context. The important part is that I have commands which will be making
several modifications to the outline structure.

In the long past, I wrote a much simpler command which required multiple
outline changes, I gave up trying to write position code directly and
instead just used the 'executeMinibuffer' method. That way I was able to
leave the handling of positions (and the invalidations thereof) for Leo to
take care of.

But this new functionality seemed way too complicated to achieve using
minibuffer commands, so I decided to give positions a try again. Based on
problems I had in the past with positions, I decided to take the most
conservative approach I could think of. Here is the "rule of thumb" I
adopted:

   - After making an outline modification, don't use any position variables
   from before the modification.
   - The only position which can be trusted is the position returned by the
   outline modification method call.
   - If the old positions are still needed to complete the rest of the
   functionality of the command, then they need to be "re-queried" using the
   position returned by the outline modification.

In my case, the outline modifications were in "close" proximity to each
other, so with some hard thinking I was able to figure out how to re-query
the other positions. I'm not sure that will always be the case.

Using the above "rule of thumb", I have my code *working* (lightly tested).
I write this email to find out if I've gone overboard and this rule is way
more restrictive than necessary. Like maybe only a subset of outline
modifications require such strict handling? If that is the case, what are
the rules which can be followed in order to safely deal with positions
during outline modification. *Edward or Vitalije or anyone else, do you
know?*

Here is my working code (there is a lot of it) to show examples of what I
mean. Search for comments with the word "trusted" to see how I'm
re-querying positions. I made the "interesting" parts bold to make them
easier to find:

import timedef cloneToSiblingTodayNode(p):
    """    Clone the node identified by p and move that clone to a
child of a    sibling "@day %Y-%m-%d" node. If the @day node doesn't
exist yet,    create it. If p already has a clone as a child of @day,
then do nothing.        The position pointing at the original node is
returned as output    (warning:  actually the first child of the
parent of p is returned...the     assumption is that will be the same
node as pointed to by the original p).    """
    today = "@day " +  time.strftime("%Y-%m-%d", time.gmtime())
    def findTodayNode(p):
        day = (
            p1
            for p1 in p.self_and_siblings()
            if p1.h == today
        )
        return next(day, None)
    day = findTodayNode(p)
    if day:
        # The @day node for today already exists
        cloneAlreadyUnderDay = (
            p1
            for p1 in day.children()
            if p1.v == p.v
        )
        if next(cloneAlreadyUnderDay, None):
            # A clone is already child of @day, leave it where it is
(should it also move to the top?)
            p2 = p
        else:
            # p doesn't have a clone in @day, so create one*
 p2 = p.clone()   # Outline modification!
*            # With the outline modification, the day position can't
be *trusted*, so query it again*            day = findTodayNode(p2)
*            p2.moveToFirstChildOf(day) # Outline modification!
            p2 = p2.parent().parent().moveToFirstChild()
    else:*        p2 = p.clone()  # Outline modification!
        day = p2.insertAfter() # Outline modification!
*        day.h = today
        # With the outline modification, the p2 position can't be
*trusted*, so query it again*        p2 = day.copy().moveToBack()
*        p2.moveToFirstChildOf(day)
        p2 = p2.parent().parent().moveToFirstChild()
    return p2
def findFtlistAncestor(p):
    ftlist = (
        p1
        for p1 in p.parents()
        if p1.h.startswith("@ftlist")
    )
    return next(ftlist, None)
def moveOrCloneToTop(p, ftlist):
    """        If p or a clone of p is already a direct child of
ftlist, then move the node        to the first child of ftlist.
Otherwise, create a clone of p and move the clone    """
    if ftlist.v != p.parent().v:
        # Node is  not a child of ftlist...it is a deeper descendant,
so clone instead of move
        # But only add the clone if the node doesn't already have a
direct clone child in @ftlist
        cloneIsAlreadyFtlistChild = (
            p1
            for p1 in ftlist.children()
            if p1.v == p.v
        )
        child = next(cloneIsAlreadyFtlistChild, None)
        if not child:*            p = p.clone() # Outline modification!
*            # Since the outline changed, the ftlist position can't be
*trusted*, so query it again*            ftlist =
findFtlistAncestor(p)
*        else:
            p = child
    return p, ftlist
# TODO: what about undo?def moveToTopAndCloneToAtDay(c, p):
    """        Move the node identified by position p to the first
child of        an ancestor @ftlist node and also clone it to a
sibling @day        node with date matching today's date    """
    ftlist = findFtlistAncestor(p)
    if not ftlist:
        g.es("Not in ftlist tree")
        return*    p, ftlist = moveOrCloneToTop(p, ftlist) # Outline
modification!
    p.moveToFirstChildOf(ftlist) # Outline modification!
    p2 = cloneToSiblingTodayNode(p) # Outline modification!
*    c.redraw(p2)
def insertToTopAndCloneToAtDay(c, p):
    """        Insert a new node as the first child of        an
ancestor @ftlist node and also clone it to a sibling @day        node
with date matching today's date                Place the headline of
the new node in edit mode    """
    ftlist = findFtlistAncestor(p)
    if not ftlist:
        if p.h.startswith("@ftlist"):
            ftlist = p
        else:
            g.es("Not in ftlist tree")
            return*    p = ftlist.insertAsNthChild(0) # Outline modification!
    p2 = cloneToSiblingTodayNode(p) # Outline modification!
*    c.redraw(p2)
    c.editHeadline()
@g.command('ftlist-insert-node')def ftlistInsertNodeCommand(event):
    c = event['c']
    insertToTopAndCloneToAtDay(c,c.p)
@g.command('ftlist-move-to-top')def ftlistMoveToTopCommand(event):
    c = event['c']
    moveToTopAndCloneToAtDay(c, c.p)

-- 
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/CAO5X8CwmrJL2679MXWBxF4A43TW_vaFYeFRwfHQW_0hPG_pOWg%40mail.gmail.com.

Reply via email to