Telling Leo about the current selection in the body pane

62 views
Skip to first unread message

Félix

unread,
Sep 27, 2020, 1:45:19 AM9/27/20
to leo-editor
I'm trying to tell Leo to 'fake' what it's currently selected text is, in it's currently selected body, while running Leo in a leobridge. 

I'v lookedin leo's code: This is set when creating a Leo bridge : "inBridge" 

Obviously, It's checked by leo in some commands that would  make no sense if it was not running in a gui. So it makes sure "inBridge" is false.

"g.app.inBridge" is the way it's usually checked...

I'm trying to integrate 'selection state' by overriding the 'wrapper' object ( in c.frame.body ) which would be useful for commands such as 'execute selected script, extract node, etc... 

Problem is, in Leo, when about to run 'getScript' or other stuff like the most important method i'm overriding : "getSelectedText", Leo checks first if c.frame.body.wrapper exists, which makes sense, and would be enough, but then it also checks if g.app.inBridge is true, which prevents the call I was expecting Leo to make to the methods in the new 'wrapper' I provided for my commander's frame's body...

I dont wanna force "inBridge" to be false, it's gonna break everything else I presume!

Any thoughts or pointers that could shed some more light on this aspect of Leo's handling of it's gui wrapper interactions when opened via leoBridge? 

--
Félix
 

Edward K. Ream

unread,
Sep 27, 2020, 2:18:06 PM9/27/20
to leo-editor
On Sun, Sep 27, 2020 at 12:45 AM Félix <felix...@gmail.com> wrote:

I'm trying to integrate 'selection state' by overriding the 'wrapper' object ( in c.frame.body ) which would be useful for commands such as 'execute selected script, extract node, etc...
 
[snip]

I dont wanna force "inBridge" to be false, it's gonna break everything else I presume!

You should be able to override any commander method from inside the LeoBridgeIntegController (BIC) class. This is tricky. Here is tested code:

class Test:
    """
    An example patching a method in Leo's QTextMixin class.
    The Test class represents the LeoBridgeIntegController class.
    """
   
    def __init__(self, c):
        self.commander = c
        self.old_getSelectedText = c.frame.body.wrapper.getSelectedText
        g.funcToMethod(
            self.patched_getSelectedText,
            c.frame.body.wrapper,
            name='getSelectedText',
        )
        g.trace('Patched!')

    def patched_getSelectedText(self):
        """
        Call the original getSelectedText with g.app.inBridge = False.
        """
        old_in_bridge = g.app.inBridge
        try:
            g.app.inBridge = False
            func = self.old_getSelectedText
            g.trace('Calling', func)
            return func()
        except Exception:
            g.app.inBridge = old_in_bridge
            g.es_exception()
            return ''
           
Test(c)

To test:

- Run the script above.
- Create a node containing leading spaces. Select all the body text of that node.
- Run the tabify command.

The output will be something like:

__init__ Patched!
patched_getSelectedText Calling <bound method QTextMixin.getSelectedText of  <QTextEditWrapper: 2633857821064 body>>
getSelectedText ===== QTextMixin  <QTextEditWrapper: 2633857821064 body>

Notes:

- The patch should be applied to all commanders. Test by opening a new outline.
- This is just a start. If you are going to wrap lots of methods it might be useful to use __getattr__ in a generic wrapper.
  Having said that, I never like to use __getattr__ :-)

HTH.  I'm happy to help you on this project, so feel free to ask more questions.

Edward

Félix

unread,
Sep 27, 2020, 4:15:28 PM9/27/20
to leo-editor
Thanks for your reply ! 

I don't think the problem is overriding getSelectedText in the wrapper, - or overriding the whole wrapper itself like I successfully did anyways. 

It's that methods in Leo (such as getScript in leoGlobals.py) consider important to check if they're in leoBridge (!), even if they also check if the wrapper is available on the frame.body ! (see line 14 of getScript in leoGlobals.py)

Removing this check on line 14 would still check if the wrapper exists, and use for example, the default return value of the default wrapper method (that just returns empty string). This would enable replacing the wrapper itself, or only the methods inside it like you proposed, to be called. 

Actually, the behavior is that Leo checks if he's running via leoBridge and if so, decides in getscript that it's going to be the whole body text that is going to be the script because no text selection could exist if running via leoBridge, which is what I'm trying to override.

getScript should use the whole body if there's no selection, indeed, not by checking g.app.inBridge, but by calling the "hasSelection" of the wrapper, (would get the 'false' result of the WrapperAPI default class in leoFrame.py... if ran via leoBridge without wrapper replacements like I do, obviously)   

wadaya think?
--
Félix
p.s. maybe I misunderstood something crucial so just go ahead and correct me if its the case.

Edward K. Ream

unread,
Sep 27, 2020, 4:54:05 PM9/27/20
to leo-editor
On Sun, Sep 27, 2020 at 3:15 PM Félix <felix...@gmail.com> wrote:

[The problem is] that methods in Leo (such as getScript in leoGlobals.py) consider important to check if they're in leoBridge (!), even if they also check if the wrapper is available on the frame.body ! (see line 14 of getScript in leoGlobals.py)

It's possible that g.getScript has this weird check because of the console gui.

wadaya think?

Perhaps I misunderstood you.  The general idea was to leave Leo's core unchanged, but call it with g.app.inBridge = False. That's all the wrapper does.

How general is the problem? If it's only a few methods/functions, the patching technique I discussed could be used to patch g.getScript (and any other pesky methods/functions) to your liking.

Edward

Félix

unread,
Sep 27, 2020, 5:33:04 PM9/27/20
to leo-editor
Allright, good point,

I've just took 30 seconds to make sure of how much does the 'inBridge' is checked in all of leo, seems to be relevant to LeoInteg only in getScript. (Doh! Why didnt I check that before?!?)

So i guess your suggestion totally makes sense : I'm going to override getScript in leoGlobals on top of overriding the wrapper (I re-use the WrapperApi class as-is and replace some of its members in the most easy and lazy way possible : the standard programmer's way )

Thanks for those helpful tips ! 
--
Félix

Edward K. Ream

unread,
Sep 27, 2020, 5:37:37 PM9/27/20
to leo-editor
On Sun, Sep 27, 2020 at 4:33 PM Félix <felix...@gmail.com> wrote:

I've just took 30 seconds to make sure of how much does the 'inBridge' is checked in all of leo, seems to be relevant to LeoInteg only in getScript.

Oh good.

So i guess your suggestion totally makes sense : I'm going to override getScript in leoGlobals on top of overriding the wrapper (I re-use the WrapperApi class as-is and replace some of its members in the most easy and lazy way possible : the standard programmer's way )

Thanks for those helpful tips ! 

You're welcome.

Edward

Félix

unread,
Sep 27, 2020, 9:47:00 PM9/27/20
to leo-editor
I got it to work :) yay!

But not sure if I did something stupid, as I just assign instead of using funcToMethod ... I'll flesh out all the rest to finish issue #39, push it, and i'll come back to this assignment later tomorrow 

 (I lack python knowledge to know why it still works and if/why your suggestion is better)

So thanks again anyways I just wanted to share this thought ! :D
--
Félix

Edward K. Ream

unread,
Sep 28, 2020, 9:44:19 AM9/28/20
to leo-editor
On Sun, Sep 27, 2020 at 8:47 PM Félix <felix...@gmail.com> wrote:
I got it to work :) yay!

:-)

I just assign instead of using funcToMethod ... I'll flesh out all the rest to finish issue #39, push it, and i'll come back to this assignment later tomorrow 

Assignment to an instance should work. funcToMethod patches the class, but you probably don't need that.

Edward
Reply all
Reply to author
Forward
0 new messages