Sublime Forum

Keep the cursor after insert text

#1
class HelloCommand(sublime_plugin.TextCommand):
def run(self, edit):
    self.view.insert(edit, 0, "Hello, World!\n")

How do I keep the cursor after ‘\n’ in the ‘Hello, World!\n’? Currently, ST3 will move the cursor to previous cursor position before running the “hello” command.

0 Likes

#2

So the trick is to clear the previous selection (the previous cursor location) and then add a new region based on the text insertion point & the length of the text you are inserting.

class HelloWorldCommand(sublime_plugin.TextCommand):

    def run(self, edit):
        self.view.insert(edit, 0, "Hello, World\n")
        # This will clear existing selections (the previous cursor location) in the buffer.
        self.view.sel().clear()
        # Add the cursor after the text inserted. For flexibility, you may
        # want to set them to some variables.        
        self.view.sel().add(0 + len("Hello, World\n"))
1 Like

#3

Sublime isn’t moving the cursor to a previous position; the view.insert() method is specifying the exact position in the file (here the 0 means “start of the file”) that the insert should happen in. This just inserts the text directly into the buffer at the position you say, regardless of where the cursor might be, if any text is selected, etc.

To move the cursor too, you want to do what @UltraInstinct05 says here; modify the selection after the insert to put the cursor where you expect it to be. You might want to capture the return value of view.insert(), which tells you how many characters it actually inserted and use that instead of taking the length of the text, if there’s a possibility that the text might contain a tab character though.

If you happen to be in a situation where you want to insert text at the position the cursor is currently sitting at, you can also do self.view.run_command("insert", {"characters": "Hello, World\n"}) to have the text insert and move the cursor for you.

In doing so you lose the ability to specify the insertion point of the text that you get via view.insert() though, so this isn’t always a handy trick as it would require you to modify the selection anyway to move the cursor first.

On the other hand, running the insert command will take the same action as would happen if the user typed text, which includes inserting text at every cursor, replacing selected text with the new text, etc. So in that regard this is a good thing to keep in mind. You can also do it from any command and not just a TextCommand, which can be handy.

0 Likes

#4

Super great. this works.

0 Likes

#5

Correct me If I am wrong, but I don’t think you should do it from any command. Ideally TextCommand since that gives you a valid edit instance and technically view.run_command("insert", ...) is modifying the buffer in some fashion.

0 Likes

#6

The rule is that only a TextCommand can modify a file because only a TextCommand is given an edit object with which to do so; however it’s also valid for any command to execute any other type of command, regardless of the type of either kind of command.

In this case insert is the command that’s modifying the buffer, and that command is a TextCommand, which gets the edit object and makes the change (though it’s in the core, so the implementation is only logically what we’d expect and not physically so, being in C++ and not Python).

What’s extremely ill advised is something along the lines of this plugin, which is here only for explanatory purposes and most definitely is not a thing that you should ever do outside of explorations, lest the bags under your eyes grow so big that your head falls right into them due to loss of sleep from all of the problems it will inevitably cause. :smiley:

import sublime
import sublime_plugin


class ExampleCommand(sublime_plugin.WindowCommand):
    def run_(self, edit_token, args):
        self.token = edit_token
        super().run_(edit_token, args)

    def run(self):
        view = self.window.active_view()

        edit = view.begin_edit(self.token, self.name())
        view.insert(edit, 0, "This tops the list of things to never do.")
        view.end_edit(edit)
0 Likes

#7

God save the one who resorts to this tomfoolery :slight_smile:

1 Like

#8

Side note: Although the sel() method is available on any view, which technically allows you to modify the selection of any view that you happen to get access to, you should never mutate the selection of a view outside of a TextCommand.

Although it technically works, it tends to cause rendering issues wherein the selection is not rendered properly until other actions are taken that cause the window to redraw.

See the link referenced below for more info (I originally attributed this to Will, but it seems like it was Jon; sorry!)

Doing something like this ends in a situation where the selection seems visibly wrong (i.e. unchanged) in the view until something else does something that causes an update.

I suspect that the core expects that only TextCommand is going to do anything that would modify anything, so it doesn’t schedule a render update for a command sequence that doesn’t include a TextCommand.

I also strongly suspect that this at least one of the potential reasons that occasionally you run into a situation where soft undo doesn’t move the selection the way you expect, but that’s only a suspicion.

1 Like