Sublime Forum

Newbie personal plugin guidance needed

#1

I have zero experience with Python but I would like to create, what I hope will be, a fairly simple personal plugin.

When editing text and markdown files, I would like to be able to skip from sentence to sentence. This is table-stakes stuff for emacs/Vim but I miss it in Sublime.

I have been watching most of Odatnurd’s Youtube playlist on plugin dev so now have some basic understanding of how plugins work. What I am struggling with, besides trying to understand how Python does things, is knowing what the appropriate methods are from the API for what I want to achieve. I’ll admit to being a little overwhelmed!

My intention is two have two commands: move_to_character and move_back_to_character, with the idea being that when you set the keymap, you can pass the character (maybe regex pattern as a string??) you are interested in moving forwards/backwards to.

My humble beginnings look like this:

def run(self, edit):
    sels = self.view.sel()
    for sel in sels:
        if self.view.substr(sel).find('.') != -1:
            foundForward = True

What methods exist to move backwards through the buffer? Is rfind with the cursor pos as the index the way to go? Something like character_behind = self.view.substr(sel).rfind('.', 0, cursorPos) ??

Once I have moved to the location, what methods should I be using to set the new cursor position?

Thanks for any pointers!

0 Likes

#2

Something that you’re possibly missing is that there’s no difference between a selection and a cursor/caret. The typical cursor that’s simply a vertical bar and nothing more is just considered to be a selection with size zero.

The string being returned by self.view.substr(sel) in your example is probably a lot shorter than you’re expecting. Rather than extracting text using that method (based on a selection), you probably want to use some combination of view.find(...), view.find_all(...) and view.find_by_class(...) instead.

When you’ve decided on the ultimate cursor position and want to set it, you’d call some methods on sels in your example, which is of type sublime.Selection in the API. The naming in the API is a bit confusing regarding singular/multiple. The hierarchy that’s often more natural to refer to informally as selections (multiple) and selection (singular) is actually called Selection (multiple) and Region (singular).

p.s. at least for now, it would probably be easier to start your run method with the following and not think about the multiple-selection scenario.

if len(sels) != 1:
	return
sel = sels[0]
0 Likes

#3

Could be something like this:

import sublime
import sublime_plugin


class MoveToCharacterCommand(sublime_plugin.TextCommand):
    def run(self, edit, **args):
        selections = self.view.sel()
        if len(selections) != 1:
            return
        pt = selections[0].b
        forward = args["forward"] if "forward" in args else True
        character = args.get("character") or "."
        regex = args["regex"] if "regex" in args else False
        flags = 0 if regex else sublime.LITERAL
        regions = self.view.find_all(character, flags)
        if forward:
            for region in regions:
                if region.begin() > pt:
                    selections.clear()
                    selections.add(region.begin())
                    return
        else:
            for region in reversed(regions):
                if region.begin() < pt:
                    selections.clear()
                    selections.add(region.begin())
                    return

Then you can use the "args" in the keybinding to customize the direction and content to search for. For example:

[
    { "keys": ["alt+pageup"], "command": "move_to_character", "args": {"forward": false, "character": "."} },
    { "keys": ["alt+pagedown"], "command": "move_to_character", "args": {"forward": true, "character": "."} },
]
0 Likes

#4

You may or may not find this useful; the code is commented to describe what it’s doing:

0 Likes

#5

Thanks so much for info and solutions here. Looking at them, it’s clear I’ll need to get a little more familiar with Python before I can do anything meaningful but being able to work through how you have solved it is very informative.

0 Likes

#6

Just to add that I’ve been able to use @OdatNurd file and get pretty much what I’m after passing in a regex:

{
    "keys": ["alt+pagedown"],
    "command": "pattern_navigate",
    "args": {
        "pattern": "[.?!]\\s|\\n|\\Z",
        "forward": true,
        "ignorecase": false,
        "literal": false,
    }
    },
    {
    "keys": ["alt+pageup"],
    "command": "pattern_navigate",
    "args": {
        "pattern": "[.?!]\\s|\\n|\\Z",
        "forward": false,
        "ignorecase": false,
        "literal": false,
    }
    },

If was being picky, I’d look to get the cursor at the beginning of the next sentence going forward, and the beginning of the prior sentence going back but for now this is a great aid when skipping through prose etc. Thanks again all.

1 Like