Easy way to "archive" nodes

98 views
Skip to first unread message

Yaseen Mowzer

unread,
Jun 23, 2025, 10:36:47 AMJun 23
to leo-editor
Hi all

I'm using leo to manage a TODO list. I have two nodes "Next Actions" and "Done". When I complete a task I want to move it from "Next Actions" to "Done". Currently I'm doing this by holding Shift and then using the arrow keys to move the task to Done. But this takes a lot of keypresses. Is there a faster way to achieve something like this?

I used to use org-mode and it had an "archive" feature, which allowed you to move an outline to an archive file with a single chord. It also had a "refile" feature, which allowed you to move an outline to be under any other outline by searching for it's name.

Are there any similar features in Leo?

Kind regards

Yaseen Mowzer

Edward K. Ream

unread,
Jun 23, 2025, 11:23:00 AMJun 23
to leo-e...@googlegroups.com
On Mon, Jun 23, 2025 at 9:36 AM 'Yaseen Mowzer' via leo-editor <leo-e...@googlegroups.com> wrote:
Hi all

I'm using leo to manage a TODO list. I have two nodes "Next Actions" and "Done". When I complete a task I want to move it from "Next Actions" to "Done". Currently I'm doing this by holding Shift and then using the arrow keys to move the task to Done. But this takes a lot of keypresses. Is there a faster way to achieve something like this?

Sure. Write an `@button` or `@command` script to do exactly what you want. You can bind keys to the script.

Here is tested code for an undoable "archive" command bound to F1:

Headline: @button archive @key=F1
Body:

u, undoType = c.undoer, 'Archive Node'
done = g.findNodeAnywhere(c, 'Done')
next_actions = g.findNodeAnywhere(c, 'Next Actions')
p = c.p
if not done:
    g.es_print('"Done" node not found')
elif p == done:
    g.es_print("Can't move 'Done'")
elif p == next_actions:
    g.es_print("Can't move 'Next Actions'")
elif p.parent() == done:
    g.es_print(f"Already archived: '{p.h}'")
else:
    bunch = u.beforeMoveNode(p)
    p.moveToLastChildOf(done)
    g.es_print(f"Node '{p.h}' moved to 'Done'")
    if not next_actions:
        g.es_print('"Next Actions" node not found')
    next_task = next_actions.lastChild() if next_actions else None
    u.afterMoveNode(p, undoType, bunch)
    c.selectPosition(next_task or next_actions)
    c.redraw()

Welcome to the world of Leo scripting!

Edward

Edward K. Ream

unread,
Jun 23, 2025, 11:30:14 AMJun 23
to leo-editor
On Monday, June 23, 2025 at 10:23:00 AM UTC-5 Edward K. Ream wrote:

Welcome to the world of Leo scripting!

The original script always moves c.p (the presently selected node). But this might be a dubious choice if c.p.parent() isn't the 'Next Actions' node. In that case, one might prefer to clone c.p and then move the clone. The undo code would be more complicated in that case.

I'll leave this improvement as an exercise, but I'll be glad to help you you wish.

Edward

Edward K. Ream

unread,
Jun 23, 2025, 11:45:08 AMJun 23
to leo-editor
On Monday, June 23, 2025 at 10:30:14 AM UTC-5 Edward K. Ream wrote:

The original script always moves c.p (the presently selected node). But this might be a dubious choice if c.p.parent() isn't the 'Next Actions' node.

We can sidestep this case with another @button node:

Headline: @button next-task @key=f2

Body node:

next_actions = g.findNodeAnywhere(c, 'Next Actions')
if next_actions:
    next_action = next_actions.firstChild()
    if next_action:
        c.selectPosition(next_action)
        c.redraw()
    else:
        g.es_print('No next action')
else:

    g.es_print('"Next Actions" node not found')


In other words: F2 finds the next action. F1 archives it. This keeps the workflow simple.

And perhaps the F1 script should do nothing if c.p isn't a child of the 'Next Actions' node. Your choice.

Edward

Yaseen Mowzer

unread,
Jun 23, 2025, 12:15:05 PMJun 23
to leo-editor
On Monday, 23 June 2025 at 17:23:00 UTC+2 Edward K. Ream wrote:
Sure. Write an `@button` or `@command` script to do exactly what you want. You can bind keys to the script.

Here is tested code for an undoable "archive" command bound to F1:


Thanks! That works like a charm. I did not know about @key, and findNodeAnywhere seems extremely useful. Thanks a ton!

Yaseen 

Edward K. Ream

unread,
Jun 23, 2025, 2:15:34 PMJun 23
to leo-e...@googlegroups.com
You're welcome!

Edward

Yaseen Mowzer

unread,
Jul 2, 2025, 4:30:52 AMJul 2
to leo-editor
Hi

I'm just looking at the code you wrote and I see you did

p = c.p

I'm curious as to why this was necessary? I thought all scripts come with c, p and g defined?

Thanks again.

Edward K. Ream

unread,
Jul 2, 2025, 6:33:21 AMJul 2
to leo-e...@googlegroups.com
On Wed, Jul 2, 2025 at 3:30 AM 'Yaseen Mowzer' via leo-editor <leo-e...@googlegroups.com> wrote:
Hi

I'm just looking at the code you wrote and I see you did

p = c.p

I'm curious as to why this was necessary? I thought all scripts come with c, p and g defined?

Good question!

p is predefined as c.p at the start of the script. But I wanted to emphasize that c.p itself might change during the script.

Edward

Thomas Passin

unread,
Jul 2, 2025, 9:01:04 AMJul 2
to leo-editor
Or you could select the target node, then

<CTRL-SHIFT-X>
<Select final location, such as the last node under "Done">
<CTRL-SHIFT-V>

The scripts are slick to use and good introductions to Leo scripting; this way is simple, and doesn't use up shortcut keys.   

Another scripting way, possibly simpler.  In your myLeoSettings.leo outline, create a new @command node, let's say "archive-node".  In the body of the node, borrowing bits of Edward's code (untested):

# Archive selected node
c.doCommandByName('cut-node')
done = g.findNodeAnywhere(c, 'Done')
if done:
     p.moveToLastChildOf(done)
     c.doCommandByName('paste-node')
else:
    g.es_print('"Done" node not found')

You can create a shortcut key for this new command. Note that the @command node should be under an @commands node, which must be under the file's @settings node. If you like, you can add undo capability following the example in Edward's code. The command will be available after a restart of Leo.

Thomas Passin

unread,
Jul 2, 2025, 10:01:59 AMJul 2
to leo-editor
You can archive to another file by opening it and getting its commander:

target_c = g.openWithFileName(FILE)

Then you can use code similar to the example scripts but using target_c instead of c.

Yaseen Mowzer

unread,
Jul 3, 2025, 3:43:16 AMJul 3
to leo-editor
On Wednesday, 2 July 2025 at 16:01:59 UTC+2 Thomas Passin wrote:
You can archive to another file by opening it and getting its commander:

target_c = g.openWithFileName(FILE)

Then you can use code similar to the example scripts but using target_c instead of c.

Oh that's Interesting! If I archive to another file, I can keep the file that I use regularly very lean. 
Reply all
Reply to author
Forward
0 new messages