When scripting with Leo, how to call a command by name?

42 views
Skip to first unread message

Félix

unread,
Sep 3, 2023, 8:41:54 PM9/3/23
to leo-editor
Making script in Leo is great, with the globally defined vars g, c and p anything is possible. 

But what is the recommended way of doing a simple command by name in a script?

The c.doCommandByName method exists, but it insists on having an event as a second parameter. 

I discovered that I can make it work by passing a fake event such as : {"c": c}, or even a better one: g.app.gui.create_key_event(c),  but this is quite unintuitive. Could it not default to a valid default event if the event is not passed?

Félix

Jacob Peck

unread,
Sep 3, 2023, 9:08:31 PM9/3/23
to leo-e...@googlegroups.com
I tend to use c.executeMinibufferCommand('name-of-command') -- doesn't need any extra parameters, and Just Works TM.

Jake

--
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/68b44f92-c2fd-403b-97aa-58fba041d366n%40googlegroups.com.

Thomas Passin

unread,
Sep 3, 2023, 9:13:39 PM9/3/23
to leo-editor
There's also c.k.simulateCommand('name-of-command').  I'm not sure why there are both, since they seem to do the same thing.  With either one, you don't need to supply a fake event.  The method takes care of that. I use whichever one I remember first.

Félix

unread,
Sep 3, 2023, 9:24:35 PM9/3/23
to leo-editor
Having in mind a fresh new user's perspective, I wonder if doCommandByName, the method with the most intuitive name to use for such a task to perform, could not be relatively easily modified to support not having an 'event' passed to it? 

...I'm not familiar with those 'events' concepts in python so I'm curious about Edwards thought on this matter. 

Hoping it can be changed easily ! :)

Félix

Thomas Passin

unread,
Sep 3, 2023, 11:06:54 PM9/3/23
to leo-editor
I've never encountered or used that method.  But it's another case where I would resist changing the signature of an existing command.  If it's only a matter of making and argument optional, that would be more palatable.  The "events" in question here are not Python things but Leo objects.  They often carry the "c" parameter, for example, so the command can access it.

Félix

unread,
Sep 3, 2023, 11:34:01 PM9/3/23
to leo-editor
I agree, respecting existing parameter orders and signatures in general is very important. But making it optional does not interfere with normal usage. 

jkn

unread,
Sep 4, 2023, 2:37:43 AM9/4/23
to leo-editor
FWIW the current options for doing this have always seemed a bit sub-optimal to me, due to the reasons you suggest. c.executeMinibufferCommand('name-of-command') seem like the wrong level to me, I should be able to call something 'lower down' than this...

Edward K. Ream

unread,
Sep 4, 2023, 6:01:51 AM9/4/23
to leo-e...@googlegroups.com
On Sun, Sep 3, 2023 at 7:41 PM Félix <felix...@gmail.com> wrote:

The c.doCommandByName method exists, but it insists on having an event as a second parameter. 

Hah. I had completely forgotten about c.doCommandByName. Furthermore, I had completely mis-remembered the complexity of k.simulateCommand. Maybe somebody (me?) refactored simulateCommand.

I discovered that I can make it work by passing a fake event such as : {"c": c}, or even a better one: g.app.gui.create_key_event(c),  but this is quite unintuitive. Could it not default to a valid default event if the event is not passed?

Imo, the answer to your question is obvious: Create the default event in c.doCommandByName instead of k.sumulateCommand:

1. Define k.simulateCommand as:

def simulateCommand(self, commandName: str, event: Event = None) -> None:
    """Execute a Leo command by name."""
    c = self.c
    c.doCommandByName(commandName, event)

2. Change the signature of c.doCommandByName to:

def doCommandByName(self, command_name: Any, event: Event = None) -> Any:

As you say, the new signature is in no way a breaking change.

3. At the start of c.doCommandByName, create an event if necessary:

    if not event:
        # Create a default key event.
        event = g.app.gui.create_key_event(c)

4. Update Leo's docs, especially the cheat sheet to mention c.doCommandByName instead of k.simulateCommand.

Obviously, k.simulateCommand must remain for compatibility.

5. Update Leo's unit tests to use c.doCommandByName instead of k.simulateCommand.

I'll create a PR immediately.

Edward

Edward K. Ream

unread,
Sep 4, 2023, 6:38:47 AM9/4/23
to leo-editor
On Monday, September 4, 2023 at 5:01:51 AM UTC-5 Edward K. Ream wrote:
On Sun, Sep 3, 2023 at 7:41 PM Félix wrote:

The c.doCommandByName method exists, but it insists on having an event as a second parameter. 
...
I discovered that I can make it work by passing a fake event such as : {"c": c}, or even a better one: g.app.gui.create_key_event(c),  but this is quite unintuitive. Could it not default to a valid default event if the event is not passed?
 
PR #3541 has been merged into devel.

k.simulateCommand is deprecated, but will remain for compatibility.

The PR changes k.simulateCommand to c.doCommandByName everywhere, including in plugins and unit tests.

Edward
Reply all
Reply to author
Forward
0 new messages