Command To Convert Selection Or Body To Title Case

122 views
Skip to first unread message

Thomas Passin

unread,
Jan 2, 2023, 12:14:47 AM1/2/23
to leo-editor
Leo has commands to capitalize, upcase, and lowercase the body text or selections of the body text.  I could not find a command to convert to title case (first letter of every word capitalized).  So here is a script to do so.

The script does not take into account any tricky edge cases - it just uses the Python string.title() method.  The command is undoable.

"""Convert selection or body to title case."""
w = c.frame.body.wrapper
p = c.p
s = p.b
u = c.undoer

start, end = w.getSelectionRange()
use_entire = start == end  # no selection, convert entire body

undoType = 'title-case-body-selection'
undoData = u.beforeChangeNodeContents(p)

if use_entire:
    p.b = s.title()
else:
    sel = s[start:end]
    head, tail = s[:start], s[end:]
    p.b = head + sel.title() + tail

c.setChanged()
p.setDirty()
u.afterChangeNodeContents(p, undoType, undoData)
c.redraw()

jkn

unread,
Jan 2, 2023, 6:17:38 PM1/2/23
to leo-editor
It would be good to have a miscellany of little script examples like this easily
available, to give people assistance with learning Leo scripting. I know
a few have been published as Github gists in the past, and there are some clues
in the Leoeditor.com documentation, but a few dozen snippets on one place,
with some annotation, would be very useful, I think. I might even have a couple
of my own to contribute...

J^n

Thomas Passin

unread,
Jan 2, 2023, 6:47:36 PM1/2/23
to leo-editor
Theoretically they can go into scripts.leo, but I always forget about that outline and a lot of its scripts are so long unmaintained that they don't even work.  That, or they are so specialized that it's hard to even know what they do.

I do agree that a collection of small utility scripts would be great for people trying to learn how to program for Leo - IF they are well documented, and clear and simple.

jkn

unread,
Jan 3, 2023, 5:23:47 AM1/3/23
to leo-editor

Yes ... 'many' of the scripts I come across, eg. on this newsgroup, need a bit of tweaking to run with more modern Leo. But I think a collection with some of the concepts 'curated' and commented on would be useful.

Like you, I always forget about scripts.leo; not sure that is the right place.

Thomas Passin

unread,
Jan 3, 2023, 9:19:08 AM1/3/23
to leo-editor
Where do you think a collection like that should live, bearing in mind that it should be easy for a newbie to learn about it?

Edward K. Ream

unread,
Jan 3, 2023, 10:20:59 AM1/3/23
to leo-e...@googlegroups.com
On Tue, Jan 3, 2023 at 4:23 AM jkn <jkn...@nicorp.f9.co.uk> wrote:

Yes ... 'many' of the scripts I come across, eg. on this newsgroup, need a bit of tweaking to run with more modern Leo. But I think a collection with some of the concepts 'curated' and commented on would be useful.

Like you, I always forget about scripts.leo; not sure that is the right place.

I think scripts.leo is the best of several not-very-good alternatives.

Edward

jkn

unread,
Jan 3, 2023, 3:58:23 PM1/3/23
to leo-editor
Well, maybe we should have a push to improve the 'visibility' of scripts.leo. I can't remember how it is 'promoted', perhaps some work in that are would be worthwhile.

I see there is no introduction to itself inside the file, so it's not very clear what it's supposed to represent.

Would calling it 'example_scripts.leo', and having a preamble in the file, help any?

Edward K. Ream

unread,
Jan 3, 2023, 4:28:42 PM1/3/23
to leo-e...@googlegroups.com
On Tue, Jan 3, 2023 at 2:58 PM jkn <jkn...@nicorp.f9.co.uk> wrote:
Well, maybe we should have a push to improve the 'visibility' of scripts.leo. I can't remember how it is 'promoted', perhaps some work in that are would be worthwhile.

I see there is no introduction to itself inside the file, so it's not very clear what it's supposed to represent.

Would calling it 'example_scripts.leo', and having a preamble in the file, help any?

I doubt it :-)

Edward

jkn

unread,
Jan 4, 2023, 3:56:30 AM1/4/23
to leo-editor
This is the other candidate place, of course:

Edward K. Ream

unread,
Jan 4, 2023, 4:06:31 AM1/4/23
to leo-e...@googlegroups.com
On Wed, Jan 4, 2023 at 2:56 AM jkn <jkn...@nicorp.f9.co.uk> wrote:
This is the other candidate place, of course:


Heh. I had forgotten about this. Thanks for the reminder.

Edward

jkn

unread,
Jan 4, 2023, 6:28:31 AM1/4/23
to leo-editor
It is/was my starting-off place re. scripting; especially when I forget about scripts.leo ...

Thomas Passin

unread,
Jan 4, 2023, 9:22:17 AM1/4/23
to leo-editor
Hah! That's what I was trying to remember.  If we forget  about these places, how is a newbie going to find them?  Isn't there a link in the cheatsheet stuff that Leo puts into the default workbook when it is run for the first time?

Edward K. Ream

unread,
Jan 4, 2023, 11:18:53 AM1/4/23
to leo-e...@googlegroups.com
On Wed, Jan 4, 2023 at 8:22 AM Thomas Passin <tbp1...@gmail.com> wrote:
Hah! That's what I was trying to remember.  If we forget  about these places, how is a newbie going to find them? 

It's in Leo's table of contents.  I've just added this entry (online and in devel).

Edward

Edward K. Ream

unread,
Jan 4, 2023, 11:32:09 AM1/4/23
to leo-editor
On Wednesday, January 4, 2023 at 10:18:53 AM UTC-6 Edward K. Ream wrote:

> I've just added this entry (online and in devel).

And I've just updated this page in Leo's Reference Guide.

Edward

Thomas Passin

unread,
Jan 4, 2023, 1:27:17 PM1/4/23
to leo-editor
Let's add the scripting-miscellany link to Leo's Help menu.

Edward K. Ream

unread,
Jan 4, 2023, 2:03:51 PM1/4/23
to leo-e...@googlegroups.com
On Wed, Jan 4, 2023 at 12:27 PM Thomas Passin <tbp1...@gmail.com> wrote:
Let's add the scripting-miscellany link to Leo's Help menu.

Good idea.

Edward

Edward K. Ream

unread,
Jan 4, 2023, 2:20:57 PM1/4/23
to leo-editor
> Thomas: Let's add the scripting-miscellany link to Leo's Help menu.
> EKR: Good idea.

Done via PR #3030. See also #3029: Add undoable check-nodes command.

Edward

Kevin Henderson

unread,
Jan 10, 2023, 4:11:17 PM1/10/23
to leo-editor
This is great timing Thomas, thank you!
I've been away from Leo for a while, but making another attempt to get back into it.

I've had a _heck_ of time figuring out how to do what showed, in my case I wanted a 'duplicate-line' command.
It really is tremendously difficult to figure out what to do or where to start.

a couple of observations:
- what got me the furthest (but not all the way there) is/was the `scripts.leo` file (i.e. the 'Text' section).
- most of the scripts & guides seem to be around working with nodes (which seems easier) rather than body text.
- the body text seems very challenging/manual to work with, perhaps some utility functions would make this easier
  - e.g. for my duplicate line command, i'll have repeat most of Thomas's code above, there isn't a simple utility function
  - there are already commands such as 'extend-to-line'/extendToLine, but do I have access to these or do i have to reproduce them?
- it would be nice to group these docs & content by context, e.g. the guides with the examples

Anyway, it's good to say hello again, glad to see things are still plugging.

Cheers & Happy 2023,
Kevin

Thomas Passin

unread,
Jan 10, 2023, 5:30:56 PM1/10/23
to leo-editor
Yes, it can be hard to find out how to do things you want in Leo.  The good news is that you can do most anything.  Remember to undo/redo operations that change a node or the tree - my title-case script shows how to do that for node content.

Leo's code base is in the PyLeoRef.leo outline, as I imagine you already know.  IMHO, the best way to find code that might be related to what you want is the Nav tab, along with tab completion of minibuffer commands.  When you type (or paste) into the search box in the Nav tab, it shows you node headlines that match.  After you hit ENTER, it also shows you body matches.

For example, you write that you want to duplicate a line.  So you have to select or at least find a line to copy.  I remembered that there is a command to move a line up.  I typed "move" into the minibuffer and then TAB to get a list of all matching command.  There it was - "move-lines-up".  I copied that and pasted it into the search box in the Nav tab.  This showed a match for "@cmd('move-lines-up')".  Clicking on that took me to where the command is defined.  Right there it shows how to get the selection range, and how to get the line from the selection.

The other excellent feature is the ability to CTRL-click on a function or method call and (usually) get taken to its definition.

Forgive me if you already know these things. They have been really helpful for me.  And, as Edward would advise, judicious use of clones can be very helpful.  Also, you might want to check out the Freewin plugin - it opens a stand-alone window that shows a single node in a basic editor, which you can keep open alongside Leo's window for reference while you navigate to some other node.  That's probably new since you were last working with Leo.

Kevin Henderson

unread,
Jan 11, 2023, 10:31:52 AM1/11/23
to leo-editor
I'll check out that Freewin plugin, thank you. I'm not sure how to actually execute the functions, even if i've found the definition.
Recently I made this (which is obviously very ugly for multiple reasons) - for a duplicate-line command.
I started with your example, Tom, then added/modified the built-in command for `extend-to-line` (which has a bug if on the last character of the line, but i haven't had time to look at that yet). I wasn't sure how to call these without rewriting them from scratch.


```
@language python

# duplicate-line copies the selected line in the body pane
# and preserves the insertion point on the new line as well
# todo: incorrect if at last character in line

w = c.frame.body.wrapper #wrapper
p = c.p #position
s = p.b #string of body
u = c.undoer #undo

orig_ip = w.getInsertPoint()

max_chars = len(s)


start = orig_ip
while 0 <= start < max_chars and not s[start] == '\n':
    start -= 1
start += 1


end = orig_ip
while 0 <= end < max_chars and not s[end] == '\n':
    end += 1


line_text = s[start:end]
new_line_text = '\n' + line_text


column_offset = max(0, orig_ip - start)
new_ip_offset = len(new_line_text)
new_ip = orig_ip + new_ip_offset


head, tail = s[:end], s[end:]

undoType = 'duplicate-line'
undoData = u.beforeChangeNodeContents(p)

p.b = head + new_line_text + tail

w.setInsertPoint(new_ip)


c.setChanged()
p.setDirty()
u.afterChangeNodeContents(p, undoType, undoData)
c.redraw()    


# g.es('---')
```
Reply all
Reply to author
Forward
0 new messages