Edward,
On Sat, Aug 17, 2019 at 7:22 AM Edward K. Ream <[email protected]> wrote:
> No. See the emphasis above. Adding after *any* last sibling will leave
> all positions unchanged.
>
Got it. Thanks.
> Maybe I should start my journey on position enlightenment with
>> c.redraw(). If I see how positions are recomputed, maybe I will better
>> understand the behavior.
>>
>
> I wouldn't advise that. Instead, read << about the position class >>. If
> you want more detail, look at Position.__init__().
>
> A position consists of *a stack of ancestor vnodes*, and *a child index*.
>
Excellent, this is a huge help. I had it in my head before that there was
some kind of linked list and C-style pointer type of thing going on.
> If you move a node, both the stack and child index will change for
> descendant nodes.
>
> If you insert or delete a node, the child index of *sibling *nodes may
> change, and that will also affect the children of those siblings. But *no*
> positions will change if you only add/delete the *last* sibling. Clear?
>
Yes, very clear.
[...]
> *Important:* Usually scripts don't care much about changing positions.
> *Changing
> positions only bite you if you save positions.* Instead, it may be
> simpler to *recompute *positions of interest.
>
I think my scripts aren't usual then. Most of the (few) scripts I've
written over the years involve making multi-step modifications. Often that
means keeping track of more than one position in the outline. Knowing what
you said about "A position consists of..." is critical for correctly
writing such scripts.
Now that I understand better, I can see the rule of thumb I wrote in my
original post is far too restrictive:
- 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 (saved) 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.
The code I wrote worked, but being unnecessarily conservative made it more
complicated.
Your and Vitalije's advice that all saved positions are still safe when
operating at the last sibling, didn't really help my case because I wasn't
dealing with the last sibling much. What I didn't realize is that just
because those are the only operations in which *all* saved positions are
safe, doesn't mean there aren't *some* saved positions which will still be
safe after other operations.
Armed with the knowledge of what a position is, I came up with these
examples:
1. Position p gains a new following sibling (i.e. p2 = p.insertAfter()).
In this case both p2 and p are safe. With the more restrictive rules, I
thought only p2 would be safe, but p is still ok because its *child
index* can only go wrong if previous siblings are inserted or modified
2. Position p gains a new niece/nephew from either previous or following
siblings. Again in this case the position p remains valid
3. When changing a following sibling of position p to be a niece/nephew
of any sibling, the position p remains valid
So armed with this knowledge and the code Vitalije share, I was able to
re-write my code to be much simpler. Vitalije, using positions ended up
being sufficient and I didn't need most of your vnode based code. Here is
the revised code:
import timedef moveOrCloneToTop(p, parent):
""" If p or a clone of p is already a direct child of
parent, then move the node to the first child of parent.
Otherwise, create a clone of p and move the clone """
clone = next((p1 for p1 in parent.children() if p1.v == p.v), None)
p = clone if clone else p.clone()
p.moveToFirstChildOf(parent)
return p
def 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 """
today = "@day " + time.strftime("%Y-%m-%d", time.gmtime())
day = (
p1
for p1 in p.self_and_siblings()
if p1.h == today
)
day = next(day, None)
if not day:
# Create today's @day node
day = p.insertAfter()
day.h = today
p1 = moveOrCloneToTop(p, day)
# Since day is a sibling of p, modifying day's children don't affect
# p so it is still valid
return p
def findFtlistAncestor(p):
ftlist = (
p1
for p1 in p.parents()
if p1.h.startswith("@ftlist")
)
return next(ftlist, None)
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 = moveOrCloneToTop(p, ftlist)
p2 = cloneToSiblingTodayNode(p)
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)
p2 = cloneToSiblingTodayNode(p)
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/CAO5X8CzHYQ6UF68Ztut-a4xWQu_kdEM%3D8cnjfqVN48J9EP%2BLkQ%40mail.gmail.com.