Sublime Forum

ST2: How to Page Up/Down without moving the cursor

#1

I’m on OS X 10.8.4 using ST2. When I use the Home and End keys, the viewport moves and the cursor is left alone. This is standard Mac behavior, and what I’d expect.

However, when I use Page Up (pageup/pgup) and Page Down (pagedown/pgdn), the cursor moves along with the viewport. This is not how other apps on my platform behave, and it’s kind of driving me crazy.

How can I get ST2 to leave the cursor alone when using Page Up/Down?

0 Likes

Page-Up Page-Down issues
#2

I have partly figured this out. I have most of the behavior I want by adding this code to my user key bindings file (Sublime Text 2 > Preferences > Key Bindings - User):


	{ "keys": "pageup"], "command": "scroll_lines", "args" : {"amount": 30.0} },
	{ "keys": "pagedown"], "command": "scroll_lines", "args" : {"amount": -30.0} }
]

Obviously, that’s not a complete solution, since it doesn’t move the viewport by a page at a time. Is there some way to get the number of lines displayed per page, and pass that into the “scroll_lines” command? Thanks in advance!

0 Likes

#3

You’d probably have to write a plugin… Here:

lines_in_view = len(view.lines(view.visible_region())) first_line,_ = view.rowcol(view.visible_region().begin()) view.show(view.text_point(first_line + lines_in_view, 0))

0 Likes

#4

Beware that scroll_lines move the cursor along the viewport when you have only one cursor without selection.
This is not the behavior that I expect and I wrote this small plugin to resolve this issue:

[code]import sublime, sublime_plugin

class ScrollLinesFixedCommand(sublime_plugin.TextCommand):
“”“Must work exactly as builtin scroll_lines command, but without moving the cursor when it goes out of the visible area.”""
def run(self, edit, amount, by=“lines”):
# only needed if one empty selection
if by != “lines” or (len(self.view.sel()) == 1 and self.view.sel()[0].empty()):
maxy = self.view.layout_extent()[1] - self.view.line_height()
curx, cury = self.view.viewport_position()
if by == “pages”:
delta = self.view.viewport_extent()[1]
else:
delta = self.view.line_height()
nexty = min(max(cury - delta * amount, 0), maxy)
self.view.set_viewport_position((curx, nexty))
else:
self.view.run_command(“scroll_lines”, {“amount”: amount})

{ “keys”: “ctrl+up”], “command”: “scroll_lines_fixed”, “args”: {“amount”: 1.0 } },

{ “keys”: “ctrl+down”], “command”: “scroll_lines_fixed”, “args”: {“amount”: -1.0 } },

{ “keys”: “ctrl+pageup”], “command”: “scroll_lines_fixed”, “args”: {“by”: “pages”, “amount”: 1.0 } },

{ “keys”: “ctrl+pagedown”], “command”: “scroll_lines_fixed”, “args”: {“by”: “pages”, “amount”: -1.0 } },[/code]

1 Like

#5

Thank you, thank you! This is just what I was hoping for. I’m annoyed that this is so difficult, but now at least the next person will be able to Google for this solution.

0 Likes

#6

Thank you so much bizoo. and nk9.
It’s been a major help for me.

0 Likes

#7

The post by @bizoo has garbled up code and markup.
It looks broken, but it seems to be complete and still works.
Thanks!

This stackoverflow post links to this one and explains better how to use it:

https://stackoverflow.com/questions/17215334/sublime-text-2-how-to-page-up-down-without-moving-the-cursor

(see accepted answer: https://stackoverflow.com/a/17231875/1158769 )

I got it to work.

Also added some more shortcuts using this plugin, using alt + all the numpad keys to move around without actually moving the cursor:

    	{ "keys": ["alt+up"]          , "command": "scroll_lines_fixed", "args": {"amount": 1.0 } },
    	{ "keys": ["alt+down"]        , "command": "scroll_lines_fixed", "args": {"amount": -1.0 } },
    	{ "keys": ["alt+pageup"]      , "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": 1.0 } },
    	{ "keys": ["alt+pagedown"]    , "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": -1.0 } },
    	{ "keys": ["alt+home"]        , "command": "scroll_lines_fixed", "args" : {"to": "top" } },
    	{ "keys": ["alt+end"]         , "command": "scroll_lines_fixed", "args" : {"to": "bottom" } },
    	{ "keys": ["alt+left"]        , "command": "scroll_lines_fixed", "args" : {"to": "left", "amount": 10.0 } },
    	{ "keys": ["alt+right"]       , "command": "scroll_lines_fixed", "args" : {"to": "right", "amount": 10.0 } },
    	{ "keys": ["alt+keypad_enter"], "command": "scroll_lines_fixed", "args" : {"to": "cursor" } },
    //	{ "keys": ["alt+keypad5"]     , "command": "show_at_center" }, // only works with numlock on
    	{ "keys": ["clear"]           , "command": "show_at_center" },
    	{ "keys": ["alt+clear"]       , "command": "show_at_center" },

.

0 Likes

#8

Glad it worked for you! I am also still using this on ST4. Would love some SO upvotes if this helped you out. :slight_smile:

0 Likes

#9

@nk9 already done :slight_smile:

I used that link years ago, and over the years made some modifications too it seems (I forgot I did).

I wonder if we should make this a plugin so everyone can benefit and find it on packagecontrol.io .
Ideally bizoo does so, but I doubt bizoo is still around. And bizoo will need a mention in the credits.

I have no idea how to go create and publish a plugin out of this though.

I see my previous post had some key commands that are not supported by the original plugin code, I’ll post my version of scroll_lines_fixed.py below that does support them:


    import sublime
    import sublime_plugin

    # source original idea:
    # https://stackoverflow.com/questions/17215334/sublime-text-2-how-to-page-up-down-without-moving-the-cursor
    # https://forum.sublimetext.com/t/st2-how-to-page-up-down-without-moving-the-cursor/10434/4

    # The idea is to only scroll the viewport and leave the cursor in place.
    class ScrollLinesFixedCommand(sublime_plugin.TextCommand):
        def run(self, edit, amount=1.0, to="", by="lines"):
            if to == "cursor":
                # scroll so cursor is at center of viewport, this command already exists:
                self.view.run_command("show_at_center")

            elif to == "top":
                # scroll so first line of file is the top line in viewport:
                curx, cury = self.view.viewport_position()
                self.view.set_viewport_position((curx, 0))

            elif to == "bottom":
                # scroll so last line of file is the bottom line in viewport:
                # - scroll to end.
                # - scroll up a page so the entire last page is visible.
                # - optional(?): scroll down 1 line so you see one empty line so that it is clear you are at the end?
                #   Could also be annoying waste of screen estate. Should be non-default, or optional. Or just dont.
                curx, cury = self.view.viewport_position()
                maxx, maxy = self.view.layout_extent()
                pagewidth, pageheight = self.view.viewport_extent()
                # optional 1 extra line:
                # newy = maxy - pageheight + self.view.line_height()
                newy = maxy - pageheight
                self.view.set_viewport_position((curx, newy))

            elif to == "linebegin":
                curx, cury = self.view.viewport_position()
                self.view.set_viewport_position((0, cury))

            elif to == "lineend":
                curx, cury = self.view.viewport_position()
                curwidth, curheight = self.view.viewport_extent()
                maxx, maxy = self.view.layout_extent()
                self.view.set_viewport_position((maxx - curwidth, cury))

            elif to == "left" or to == "right":
                curx, cury = self.view.viewport_position()
                curwidth, curheight = self.view.viewport_extent()
                maxx, maxy = self.view.layout_extent()

                if by == "page":
                    delta = curwidth * amount
                else:
                    # TODO: make charwidth not hardcoded. 9.3 seems to work for me, with current (monospace) font.
                    charwidth = 9.3
                    delta = charwidth * amount
                if to == "left":
                     delta *= -1
                newx = curx + delta

                # Works without min/max below, but if you go left or right too much
                # and then go back you notice it takes a few extra moves to start scrolling again.
                # It seems to keep scrolling even though it visually sticks within min max.
                # hard to explain, but if you comment 2 lines below out you can see what I mean.
                newx = max(newx, 0)
                newx = min(newx, maxx - curwidth)

                self.view.set_viewport_position((newx, cury))

            # below: as builtin scroll_lines command, but without moving the cursor when it goes out of the visible area.
            elif by != "lines" or (len(self.view.sel()) == 1 and self.view.sel()[0].empty()):
                maxy = self.view.layout_extent()[1] - self.view.line_height()
                curx, cury = self.view.viewport_position()
                if by == "pages":
                    delta = self.view.viewport_extent()[1]
                else:
                    delta = self.view.line_height()
                nexty = cury - delta * amount
                nexty = min(max(nexty, 0), maxy)
                self.view.set_viewport_position((curx, nexty))
            else:
                self.view.run_command("scroll_lines", {"amount": amount})

The interface is a bit confusing with the mix of “amount” “to” and “by” maybe not always making sense. Probably need to think about that a bit more before making it public.
(I had no experience in python or sublime and just hacked at the original, so it’s not really up to proper coding or api standards)

1 Like

#10

Hi there, I’m having this problem too. I haven’t used plug-ins before and just need it to page-up and page-down. Any chance you could you guide me through the steps I need to take?

On Windows btw.

0 Likes

#11

Well, I am on Windows, too. Maybe I have missed something until now, but according to my experience ST behaves very much like any other application with regards to pageup/pagedown.

A quick compare with Notepad, Notepad++, VS Code and LibreOffice Writer on Windows and even gedit on Debian 12 seconds it.

The caret is more or less visually kept at its relative position (e.g. in the middle of visible viewport), while content moves up or down one page until bof or eof is reached, which causes it to move to content boundaries.

0 Likes

Page-Up Page-Down issues
#12

I’ll try to give a short step by step to get it to work.

Note that you don’t even have to restart Sublime Text, as soon as you save the files it should work.


On windows, go to:
%appdata%\Sublime Text 3\Packages\User
which is the equivalent of:
C:\Users\MYUSERNAME\AppData\Roaming\Sublime Text 3\Packages\User

In this folder, create a file called: scroll_lines_fixed.py
(I think the filename needs to match the class command name inside the file, but not 100% sure)

Just to be clear, you end up with this file:

C:\Users\MYUSERNAME\AppData\Roaming\Sublime Text 3\Packages\User\scroll_lines_fixed.py

Edit the file, paste the code mentioned below (1) in it.
Save the file.

(1) The code which starts with import sublimeclass ScrollLinesFixedCommand … etc. ,
as found in this post (should show in view if you click this link): ST2: How to Page Up/Down without moving the cursor )


By itself, the above does nothing yet, we still need to assign keyboard shortcuts that use it.
Open your key bindings : Sublime Text 2 -> Preferences -> Key Bindings

On the left hand you see all default keybindings, on the right you can add your own keybindings that
override those defaults.

On the right, copy paste this:

[
    { "keys": ["pageup"]  , "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": 1.0 } },
    { "keys": ["pagedown"], "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": -1.0 } },
]

(note that the above overrides the default behaviour of pageup and pagedown)

As soon as you save both files, it should work. No need to reload Sublime Text.


In my keybinds suggested earlier I set up a theme where normal keys (as default) move the cursor,
and when I hold down alt, instead it moves the viewport without moving the cursor.
(using up/down/left/right/pgup/pgdown/home/end with or without alt)

I’ll repeat those key bindings below:

[
    	{ "keys": ["alt+up"]          , "command": "scroll_lines_fixed", "args": {"amount": 1.0 } },
    	{ "keys": ["alt+down"]        , "command": "scroll_lines_fixed", "args": {"amount": -1.0 } },
    	{ "keys": ["alt+pageup"]      , "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": 1.0 } },
    	{ "keys": ["alt+pagedown"]    , "command": "scroll_lines_fixed", "args" : {"by": "pages", "amount": -1.0 } },
    	{ "keys": ["alt+home"]        , "command": "scroll_lines_fixed", "args" : {"to": "top" } },
    	{ "keys": ["alt+end"]         , "command": "scroll_lines_fixed", "args" : {"to": "bottom" } },
    	{ "keys": ["alt+left"]        , "command": "scroll_lines_fixed", "args" : {"to": "left", "amount": 10.0 } },
    	{ "keys": ["alt+right"]       , "command": "scroll_lines_fixed", "args" : {"to": "right", "amount": 10.0 } },
    	{ "keys": ["alt+keypad_enter"], "command": "scroll_lines_fixed", "args" : {"to": "cursor" } },
    //	{ "keys": ["alt+keypad5"]     , "command": "show_at_center" }, // only works with numlock on
    	{ "keys": ["clear"]           , "command": "show_at_center" }, // clear is keypad5 without numlock
    	{ "keys": ["alt+clear"]       , "command": "show_at_center" },
]

I think you have all the information now to make it work however you prefer :slight_smile:

0 Likes