@button for displaying all parents of a clone to log pane

85 views
Skip to first unread message

Brian Theado

unread,
Oct 6, 2019, 10:15:58 PM10/6/19
to leo-editor
I'm aware of the clone-find-all-parents command, but usually I want to display the other parents of a given clone in order to find one of them in particular. I don't want the new nodes which clone-find-all-parents create, because I don't want the extra work later of deleting these extra nodes. I want something more transient.

I'm also aware of the transient nature of the goto-clone command.  It moves the position selection to the next cloned instance of the current position. However, searching through the clones only one-at-a-time feels uncomfortable and slow to me.

I wanted to be able to see all the clones together at once like clone-find-all-parents and at the same time have nothing leftover to cleanup like the goto-clone command.

My answer is this small script button which will display links to all the clones of the current selected position. The headline of the parent and the clone is displayed:

@button show clones
for clone in c.vnode2allPositions(p.v):
    parent = clone.parent()
    if parent:
        g.es_clickable_link(c, clone, 1, f"{parent.h }->{clone.h}\n")

Thanks to the c.vnode2allPositions and g.es_clickable_link methods, the code is easy!

Brian

Edward K. Ream

unread,
Oct 7, 2019, 4:41:26 AM10/7/19
to leo-editor
On Sun, Oct 6, 2019 at 9:15 PM Brian Theado <brian....@gmail.com> wrote:

the [goto-next-clone] command...feels uncomfortable and slow to me.
... 
this small script button [displays] links to all the clones of the current selected position. The headline of the parent and the clone is displayed:

@button show clones
for clone in c.vnode2allPositions(p.v):
    parent = clone.parent()
    if parent:
        g.es_clickable_link(c, clone, 1,
                       f"{parent.h} -> {clone.h}\n")

Elegant.  Thanks for sharing this with us.

Edward

Edward K. Ream

unread,
Oct 7, 2019, 4:52:44 AM10/7/19
to leo-editor
On Monday, October 7, 2019 at 3:41:26 AM UTC-5, Edward K. Ream wrote:

> Elegant.  Thanks for sharing this with us.

Rev cb630a3 in devel adds the show-clones command.  The definition is in leo/commands/editCommands.py

Edward

SegundoBob

unread,
Oct 7, 2019, 2:05:50 PM10/7/19
to leo-editor
btheado,

Thanks, but your code doesn't work if several positions have the same parent.  In this case, multiple links for the positions are displayed, but they all go to only one of the positions.  I did not figure out what determines which of the positions wins.

SegundoBob

Brian Theado

unread,
Oct 7, 2019, 4:42:17 PM10/7/19
to leo-editor
SegundoBob,

I don't ever have clones with the same parent in my workflow and I didn't think to test that case. It looks like the g.es_clickable_link method is making use of UNL rather than positions. It looks to me like the leo getUNL functionality handles multiple clones with the same parent.  Maybe the g.handleUnl method (which is the callback when a UNL link is clicked) is doing something wrong.

The short answer is that this is standard, core Leo code I'm calling. The long answer is that it will take some investigation.

Brian

--
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 leo-editor+...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/leo-editor/daac2df5-8483-4952-9145-78a76d3eaffa%40googlegroups.com.

Edward K. Ream

unread,
Oct 7, 2019, 4:58:18 PM10/7/19
to leo-editor


On Monday, October 7, 2019 at 3:42:17 PM UTC-5, btheado wrote:

The short answer is that this is standard, core Leo code I'm calling. The long answer is that it will take some investigation.

Here is the fix, at rev cc6c47b in devel:

@g.command('show-clones')
def show_clones(event=None):
   
"""Display links to all parent nodes of the node c.p."""
    c
= event.get('c')
   
if not c:
       
return
    seen
= []
   
for clone in c.vnode2allPositions(c.p.v):
        parent
= clone.parent()
       
if parent and parent not in seen:
            seen
.append(parent)

            g
.es_clickable_link(c, clone, 1, f"{parent.h} -> {clone.h}\n")

Note that the more pythonic: `for clone in list(set(c.vnode2allPositions(c.p.v)))` fails because positions are (on purpose) not hashable.

Edward

Brian Theado

unread,
Oct 7, 2019, 6:23:57 PM10/7/19
to leo-editor
Thanks, Edward for putting this code into the core as a command.

You interpreted SegundoBob's request differently, than I did. I was thinking he didn't mind that the duplicates are there (and maybe prefers it?), just that he wanted the displayed links go to the separate instances of the clones.

SegundoBob, could you clarify?

--
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 leo-editor+...@googlegroups.com.

Segundo Bob

unread,
Oct 7, 2019, 6:49:07 PM10/7/19
to leo-e...@googlegroups.com
On 10/7/19 3:23 PM, Brian Theado wrote:
> You interpreted SegundoBob's request differently, than I did. I was
> thinking he didn't mind that the duplicates are there (and maybe prefers
> it?), just that he wanted the displayed links go to the separate
> instances of the clones.
>

Brian is right. Edward, I think your fix does not fix the problem.

The title given the code is misleading. Yes, it displays the parents of
all the occurrences of the selected cloned node, but the clickable links
are intended to jump to each occurrence of the cloned node.

I think this is the most reasonable and useful design. The user is
interested in all the occurrences of a cloned node, not in the parents
of these occurrences. The parent headlines are displayed because these
are the most likely to be distinct identifiers of the occurrences available.

--
Segundo Bob
Segun...@gmail.com

Edward K. Ream

unread,
Oct 8, 2019, 3:59:04 AM10/8/19
to leo-editor
On Mon, Oct 7, 2019 at 5:49 PM Segundo Bob <segun...@gmail.com> wrote:

Brian is right.  Edward, I think your fix does not fix the problem.

Please let me know what code you want, and i'll put it in.  Better yet, you could do it yourself.

Edward

Brian Theado

unread,
Oct 8, 2019, 8:57:12 AM10/8/19
to leo-editor
In my previous email I was pointing the finger at g.handleUnl doing something wrong for SegundoBob's use case. However, I played with that a little bit and it seems to handle links to clones with the same parent just fine. Now I'm suspecting c.vnode2allPositions is not appropriate for the use case.

Try pasting these nodes and use ctrl-b on either of the 'child' clones. Then change which line is commented out and use ctrl-b again. In both cases the same position is selected. IOW, it looks to me like even though c.vnode2allPositions is returning two positions, they both point to the same position in the outline. The purpose of vnode2allPositions must be different than I expect.

<?xml version="1.0" encoding="utf-8"?>
<!-- Created by Leo: http://leoeditor.com/leo_toc.html -->
<leo_header file_format="2"/>
<vnodes>
<v t="btheado.20191008074512.1"><vh>test</vh>
<v t="btheado.20191008074524.1"><vh>child</vh></v>
<v t="btheado.20191008074524.1"></v>
</v>
</vnodes>
<tnodes>
<t tx="btheado.20191008074512.1">@language python</t>
<t tx="btheado.20191008074524.1"># Try ctrl-b with each of these lines and it will select the same position
c.selectPosition(c.vnode2allPositions(p.v)[0])
#c.selectPosition(c.vnode2allPositions(p.v)[1])
</t>
</tnodes>
</leo_file>

--
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 leo-editor+...@googlegroups.com.

Brian Theado

unread,
Oct 10, 2019, 7:08:06 AM10/10/19
to leo-editor
I've been thinking about this a lot and I've come to the conclusion that there should be a separate command that will display all the clone positions of a given node. Maybe the original could be renamed from 'show-clones' to 'show-clone-parents' and this one could be called 'show-clone-ancestors'

g.es(f"Ancestors of '{p.h}':")
for clone in c.all_positions():
    if clone.v == p.v:
        unl = clone.get_UNL(with_file=False, with_index=False)
        runl = " <- ".join(unl.split("-->")[::-1][1:]) # reverse and drop first
        g.es("  ", newline = False)
        g.es_clickable_link(c, clone, 1, runl + "\n")

SegundoBob, this one should work better for your clone structure, though in general there will be more output from this one than the other one.

Edward K. Ream

unread,
Oct 10, 2019, 12:36:52 PM10/10/19
to leo-editor
On Thu, Oct 10, 2019 at 6:08 AM Brian Theado <brian....@gmail.com> wrote:
I've been thinking about this a lot and I've come to the conclusion that there should be a separate command that will display all the clone positions of a given node. Maybe the original could be renamed from 'show-clones' to 'show-clone-parents' and this one could be called 'show-clone-ancestors'

Done at 5e0117d, if I understand you correctly.

I confess that I find both commands confusing, but I'll leave them in.

Edward

Brian Theado

unread,
Oct 10, 2019, 1:19:56 PM10/10/19
to leo-editor
Edward,

On Thu, Oct 10, 2019 at 12:36 PM Edward K. Ream <edre...@gmail.com> wrote:
[...]
Done at 5e0117d, if I understand you correctly.

Thanks! 
 
I confess that I find both commands confusing, but I'll leave them in.

Thanks for the feedback. Do you mean you find the output confusing or how to use it confusing or confusing why it would be useful? 

Edward K. Ream

unread,
Oct 10, 2019, 1:36:46 PM10/10/19
to leo-editor
On Thu, Oct 10, 2019 at 12:19 PM Brian Theado <brian....@gmail.com> wrote:

I confess that I find both commands confusing, but I'll leave them in.

Thanks for the feedback. Do you mean you find the output confusing or how to use it confusing or confusing why it would be useful? 

All of the above.  For me, Alt-N is good enough.

Edward

SegundoBob

unread,
Oct 11, 2019, 6:57:04 PM10/11/19
to leo-editor

On Thursday, October 10, 2019 at 4:08:06 AM UTC-7, btheado wrote:
I've been thinking about this a lot and I've come to the conclusion that there should be a separate command that will display all the clone positions of a given node. Maybe the original could be renamed from 'show-clones' to 'show-clone-parents' and this one could be called 'show-clone-ancestors'

g.es(f"Ancestors of '{p.h}':")
for clone in c.all_positions():
    if clone.v == p.v:
        unl = clone.get_UNL(with_file=False, with_index=False)
        runl = " <- ".join(unl.split("-->")[::-1][1:]) # reverse and drop first
        g.es("  ", newline = False)
        g.es_clickable_link(c, clone, 1, runl + "\n")

SegundoBob, this one should work better for your clone structure, though in general there will be more output from this one than the other one.

 Brian,

You give a correct implementation of vnode2allPositions().

Based on Brian's code, here is the code I suggest for replacing leoCommands.vnode2allPositions():
```
def vnode2allPositions(self, v):
    """Given a VNode v, find all valid positions p such that p.v = v.

    """
    c = self
    context = v.context # v's commander.
    assert(c == context)
    return [posX.copy() for posX in c.all_positions() if posX.v == v]
```

Edward,

The implementation of vnode2allPositions() in leoCommand.py is seriously flawed. Correcting it would probably change it extensively.

You can see the problem by considering the case of one parent node A with one position X, one clone node B with with positions Y and Z that are immediate children of X.  Node B.parents has two members both of which are A.  Hence leoCommands. vnode2allPositions() returns 2 positions because the outermost loop is on the members of B.parents.  But, both positions returned position Y because A.children.index(B) is used to determine the index for each position and the index(self, z) is always the first occurrence of z in the list self.

My best guess is that the implementation in leoCommands.py is complicated by trying to be efficient.  But vnode2allPositions() is not used by Leo-Editor core, so there is probably no need for it to be particularly efficient.

Brian,

So far all Leo-Editor UNL's have been presented in left-to-right (root to target position) order.  Like you I have written code that presents the UNL is right-to-left (target position to root) order because usually the target position is of much more interest than the root and the right-to-left order always places the the target position at the same column (the left side of the screen).

Here is a function that guarantees positions Y and Z display as different UNL's by appending/prepending the child index for Y or Z (child index in brackets)  to the headline of X.
```
def unlpc(posX, r2l=False):
    """ Append/Prepend the child index to the parent of the last node
    in the UNL.

    Arguments:
        posX:  Target position
        r2l: Right to Left
            False --> UNL in root to posX direction.
            True --> UNL in posX to root direction.

    Returns:
        unlpc: A UNL with the child index of the last node
            in the UNL appended in brackets to the parent's headline.
            If the parent is the hidden root node, then
            the bracketed child index is appended to the
            empty string.

    Suppose a cloned-node has several positions that are
    immediate children of one node.  This function produces
    unique UNL's for each of these positions.

    This function can produce equal UNL's for several cloned-node
    positions if several nodes have the same headline.
    """

    unl = posX.get_UNL(with_file=False, with_index=False)
    unlList = unl.split("-->")
    uLen = len(unlList)
    if uLen == 1:
        parent = ''
        uBefore = list()
    else:
        parent = unlList[-2]
        uBefore = unlList[:-2]
    if r2l:
        unlList = uBefore + [f'[{posX.childIndex()}]' + parent, unlList[-1]]
        unlList.reverse()
        arrow = '<--'
    else:
        unlList = uBefore + [parent + f'[{posX.childIndex()}]', unlList[-1]]
        arrow = '-->'
    return arrow.join(unlList)
```

I hope this helps,
SegundoBob

Brian Theado

unread,
Oct 12, 2019, 9:53:41 AM10/12/19
to leo-editor
SegundoBob,

> You give a correct implementation of vnode2allPositions().
[...]

> The implementation of vnode2allPositions() in leoCommand.py is seriously flawed
[...]

> But vnode2allPositions() is not used by Leo-Editor core

But vnode2allPositions is used in the leo-editor by the method c.cloneFindParents. The vnode2allPositions method has this in its doc string:

"Not really all, just all for each of v's distinct immediate parents."

I'm guessing it does exactly what Edward wants it to because he uses the clone-find-x commands all the time. The clone-find-all-parents command helps you quickly see the context of other occurences of the clone. But sometimes the volume of other clones is very high due to distant ancestors themselves having clone instances. Seeing so many repeated instances of similar tree structure often becomes too much noise.

 So probably your quibble with vnode2allPositions is that it doesn't do what you expect it to do based on the name?

For my initial 'show-clones' command (now called 'show-clone-parents') I wanted the same "reduced noise" approach as clone-find-all-parents and so I re-used the vnode2allPositions method. Then based on your feedback, I wrote the show-clone-ancestors which shows all the paths to the clone. Two different use cases and I thought the latter would fit your use case. Does it?

> Here is a function that guarantees positions Y and Z display as different UNL's
> by appending/prepending the child index for Y or Z (child index in brackets) to
> the headline of X
[...]

Have you seen the with_index and with_count parameters for get_UNL?

def get_UNL(self, with_file=True, with_proto=False, with_index=True, with_count=False):
    """
    with_file=True - include path to Leo file
    with_proto=False - include 'file://'
    with_index - include ',x' at end where x is child index in parent
    with_count - include ',x,y' at end where y zero based count of same headlines
    """

I didn't take a close look at your code, but is it any different than just using those options? I didn't include those options in my show-clone-ancestors, but that is easy to change. I figured the important part is that if you click on the same-looking links, it will still take you to the distinct instances of that position

Brian

SegundoBob

unread,
Oct 12, 2019, 12:38:58 PM10/12/19
to leo-editor


On Saturday, October 12, 2019 at 6:53:41 AM UTC-7, btheado wrote:

But vnode2allPositions is used in the leo-editor by the method c.cloneFindParents.

I missed the restructuring of the Leo-Editor core code that created the separate commands directory.  My  copy of LeoPyRef.leo was out of date.



 So probably your quibble with vnode2allPositions is that it doesn't do what you expect it to do based on the name?

 
Exactly.

Have you seen the with_index and with_count parameters for get_UNL?

Yes, I' m aware of those options. get_UNL() does not have a "target position to root" option. 

I have no more interest in this topic and nothing to contribute to it.

Thanks for your help.

SegundoBob

Edward K. Ream

unread,
Oct 12, 2019, 6:24:15 PM10/12/19
to leo-editor
On Sat, Oct 12, 2019 at 8:53 AM Brian Theado <brian....@gmail.com> wrote:

But vnode2allPositions is used in the leo-editor by the method c.cloneFindParents. The vnode2allPositions method has this in its doc string:

"Not really all, just all for each of v's distinct immediate parents."

I'm guessing it does exactly what Edward wants it to because he uses the clone-find-x commands all the time.

I don't remember anything about this method, and I doubt that I wrote the part of the docstring you quote.  In any case, please do not change this method.  If necessary, write another method.

Edward
Reply all
Reply to author
Forward
0 new messages