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

#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)

0 Likes