Sublime Forum

Move cursor up or down a few lines at a time from the keyboard

#1

Hi, I’m wondering how to create a keyboard shortcut to move the caret up down a few lines at a time. Say:

alt+up —> moves cursor up by 10 lines (or to beginning of file, if that comes first)
alt+down —> moves cursor down by 10 lines (or to end of file, if that comes first)

I tried a few things but failed. Couldn’t find a default key binding that moves the cursor up/down a line to model myself on.

Thanks!

0 Likes

#2

Pg up and pg down?

0 Likes

#3

save this as plugin in your sublime user folder

    import sublime
    import sublime_plugin


    class PlusLineCommand(sublime_plugin.TextCommand):
    	def run(self, edit, lines = 10):
    		(row,col) = self.view.rowcol(self.view.sel()[0].begin())
    		self.view.run_command("goto_line", {"line": row+1 + lines})

    class MinusLineCommand(sublime_plugin.TextCommand):
    	def run(self, edit, lines = 10):
    		(row,col) = self.view.rowcol(self.view.sel()[0].begin())
    		self.view.run_command("goto_line", {"line": row+1 - lines})

	{ "keys": ["alt+down"], "command": "plus_line", "args": {"lines": 10}},
	{ "keys": ["alt+up"], "command": "minus_line", "args": {"lines": 10}},

you can also use ctrl+g to go any line or use vi mode vi already have this future
j and k move down and up one line, so 10j and 10k move down and up ten lines. You can repeat any motion by putting a number before it. (if you in vi mode)

1 Like

#4
0 Likes

#5

Thanks, looks just like what I want, but I can’t get it to work! (Warning: I am a newbie at this.)

So far I copy-pasted the plugin part into a file that I called “updown”, placed in ~/Library/…/Sublime Text 3/Packages/User and I have copy-pasted the two user keybindings into my keybindings file. Nothing happens. I have tried to restart sublime text as well.

0 Likes

#6

I think it should be updown.py and check sublime console for any errors (ctrl+`)

0 Likes

#7

Thanks, changing to updown.py did it.

There was a slight bug: moving up past top of file had a “wraparound” effect. I changed that by putting

max(1, row+1-lines)

in the second function.

In passing, I don’t understand how sublime text knows that “plus_line” refers to a function named “PlusLineCommand”… I guess that’s some sort of convention?

0 Likes

#8

Yes, command names are converted to snake_case internally with the “command” suffix stripped.

0 Likes

#9

Hi unknown_user (or whoever else is still listening). I realized that I would also like

shift+alt+up
shift+alt+down

to do the same thing, while extending the current selection or starting a new selection. Can your code be modified to do that? (I tried and failed.)

Thanks!

0 Likes

#10

The code in the forum post I wrote that @kingkeith linked above is a drop in replacement for the move command that takes the same arguments and also takes an extra argument that allows you to specify a movement amount.

If you use that, you can replicate the key bindings used for Shift+Up and Shift+Down using that command and a repeat amount of your choosing to do this,

0 Likes

#11

Hi, thanks for the pointer. It worked! For reference, here are my key bindings, using your code:

{ "keys": ["alt+up"], "command": "move_amount", "args": { "by": "lines", "forward": false, "amount": 7 } },
{ "keys": ["alt+down"], "command": "move_amount", "args": { "by": "lines", "forward": true, "amount": 7 } },
{ "keys": ["shift+alt+up"], "command": "move_amount", "args": { "by": "lines", "forward": false, "amount": 7, "extend": true } },
{ "keys": ["shift+alt+down"], "command": "move_amount", "args": { "by": "lines", "forward": true, "amount": 7, "extend": true } },

And for even more reference, here is the code question (courtesy OdatNurd):

import sublime
import sublime_plugin

class MoveAmountCommand(sublime_plugin.TextCommand):
	def run(self, edit, amount=1, **kwargs):
    	for _ in range(amount):
        		self.view.run_command("move", args=kwargs)

Thanks again!

1 Like

#12

I’m using vintage mode where I start my selection using ‘v’. This version based on the version from @youlam detects that a selection has started and if so maintains it.

import sublime_plugin

class MoveAmountCommand(sublime_plugin.TextCommand):

    def run(self, edit, amount=10, **kwargs):
        for region in self.view.sel():
            if len(region) > 0:
                kwargs['extend'] = True
        for _ in range(amount):
            self.view.run_command("move", args=kwargs)
0 Likes

#13

The plugins above mess up folded regions which is extremely annoying. This one skips folded regions (and automatically centers the view). Full credits to claude (I’m speechless):

import sublime
import sublime_plugin

class PlusLineCommand(sublime_plugin.TextCommand):
    def run(self, edit, lines=5):
        # Get current cursor position
        cursor_pos = self.view.sel()[0].begin()
        (row, col) = self.view.rowcol(cursor_pos)
        
        # Get the target row
        target_row = row + lines
        
        # Get all folded regions
        folded_regions = self.view.folded_regions()
        
        # Convert target row to text point
        target_point = self.view.text_point(target_row, 0)
        
        # Check if target point is within any folded region
        for region in folded_regions:
            if region.contains(target_point):
                # If target is in folded region, jump to the end of the fold
                target_point = region.end()
                break
        
        # Move to the target point
        self.view.sel().clear()
        self.view.sel().add(sublime.Region(target_point))
        self.view.show_at_center(target_point)


class MinusLineCommand(sublime_plugin.TextCommand):
    def run(self, edit, lines=5):
        # Get current cursor position
        cursor_pos = self.view.sel()[0].begin()
        (row, col) = self.view.rowcol(cursor_pos)
        
        # Get the target row
        target_row = row - lines
        
        # Get all folded regions
        folded_regions = self.view.folded_regions()
        
        # Convert target row to text point
        target_point = self.view.text_point(target_row, 0)
        
        # Check if target point is within any folded region
        for region in folded_regions:
            if region.contains(target_point):
                # If target is in folded region, jump to the start of the fold
                target_point = region.begin()
                break
        
        # Move to the target point
        self.view.sel().clear()
        self.view.sel().add(sublime.Region(target_point))
        self.view.show_at_center(target_point)
'''
import sublime
import sublime_plugin

class PlusLineCommand(sublime_plugin.TextCommand):
    def run(self, edit, lines = 5):
        (row,col) = self.view.rowcol(self.view.sel()[0].begin())
        self.view.run_command("goto_line", {"line": row+1 + lines})
        self.view.run_command("show_at_center")

class MinusLineCommand(sublime_plugin.TextCommand):
    def run(self, edit, lines = 5):
        (row,col) = self.view.rowcol(self.view.sel()[0].begin())
        self.view.run_command("goto_line", {"line": row+1 - lines})
        self.view.run_command("show_at_center")
'''
0 Likes

#14

Any1 know if i could combine this with text selection (shift)? these r my key bindings:

{ "keys": ["alt+down"], "command": "plus_line", "args": {"lines": 4}},
    { "keys": ["alt+up"], "command": "minus_line", "args": {"lines": 4}},
0 Likes