Scrolling

Last edit

Summary: Add note about scroll-error-top-bottom variable

Added:

> [new]
> Emacs 24 has <code>(setq scroll-error-top-bottom t)</code> to approximate this behavior.


The variable ‘scroll-preserve-screen-position’ may be useful to some. When you scroll down, and up again, point should end up at the same position you started out with. The value can be toggled by the built in mode M-x scroll-lock-mode.

Using the Mouse

Besides using the scrollbar, there are other ways for scrolling with the mouse. For GnuEmacs this can be found in mouse-drag.el (which should be installed by default) and for XEmacs you might use the simple replacement TrackScroll.

Scrolling without moving the point

I like to align my window exactly to the relevant pieces of code. Being able to move the view while point stays at its place makes this easier.

    (defun gcm-scroll-down ()
      (interactive)
      (scroll-up 1))
    (defun gcm-scroll-up ()
      (interactive)
      (scroll-down 1))
    (global-set-key [(control down)] 'gcm-scroll-down)
    (global-set-key [(control up)]   'gcm-scroll-up)

scroll-1.el can bind the j/k keys for view style modes (don’t be put off by those keys being the same in the EvilOne?).

You can achieve the same effect and avoid writing those two defuns separately using the holy lambda() function. Here are my two bindings for scrolling the windows without moving the point.

    (global-set-key "\M-n"  (lambda () (interactive) (scroll-up   4)) )
    (global-set-key "\M-p"  (lambda () (interactive) (scroll-down 4)) )

– avi

But it’s usually better to use a named function: You can give it documentation, bind it to multiple keys, share it, advise it, etc. – SeanO

Point moves for me… ☹ What am I missing? (GNU Emacs 24.3.3) --mgalgs

Specific behavior with "page up" and "page down" keys

I would like to bind my page up and page down keys to move the point a window-height number of lines rather than scrolling the window (of course scrolling would automatically happen since the point would move off the top or bottom of the window). If the point was close to the top, it would move it to the beginning of the buffer. If it was close to the bottom, it would move it to the end of the buffer. I’ve tried “scroll-(down|up)-nomark” (from Pc selection mode) and “scroll-(down|up)” (the Emacs default). None of them quite have the behavior I’m looking for (they scroll the window rather than move the point).

I searched help for “pager-page-(down|up)” (using “C-h a pager-page-up” and “C-h f pager-page-up”)[1]. Doesn’t quite do what I want – it moves the point to keep it at the same position in the window when the window scrolls. It seems to provide the same functionality as setting ‘scroll-preserve-screen-position’ to non-nil (I’m using an Emacs 22 snapshot – 22.0.50.1 – in case that matters).

Further, suppose the window is 40 lines tall. I want <PgDn> to behave as if I pressed the down arrow 40 times (except near the end of the document). Likewise, I want <PgUp> to behave as if I pressed the up arrow 40 times (except near the beginning of the document). Is this possible?

I partially figured it out (adapted from some code on SmoothScrolling):

    (defun sfp-page-down ()
      (interactive)
      (next-line
       (- (window-text-height)
          next-screen-context-lines)))
    
    (defun sfp-page-up ()
      (interactive)
      (previous-line
       (- (window-text-height)
          next-screen-context-lines)))
    
    (global-set-key [next] 'sfp-page-down)
    (global-set-key [prior] 'sfp-page-up)

The only problem is that it forgets which column I’m on: If I start at column 40, then page up/down and land on a line with only 10 characters, then page up/down to a line with more than 40 characters, the point will be at column 10, not column 40. This is odd considering the up/down arrow keys don’t have this problem and they’re bound to ‘next-line’ and ‘previous-line’.

This works, instead:

    (defun sfp-page-down ()
      (interactive)
      (setq this-command 'next-line)
      (next-line
       (- (window-text-height)
          next-screen-context-lines)))
    
    (defun sfp-page-up ()
      (interactive)
      (setq this-command 'previous-line)
      (previous-line
       (- (window-text-height)
          next-screen-context-lines)))
    
    (global-set-key [next] 'sfp-page-down)
    (global-set-key [prior] 'sfp-page-up)

The functions ‘next-line’ and ‘previous-line’ use a previously-remembered column number only if the previous command was either ‘next-line’ or ‘previous-line’. Otherwise, they save the current column number to be used in future calls. Since the previous command was always either ‘sfp-page-down’ or ‘sfp-page-up’, whatever column the point landed on would be used for the next destination column. So, I set ‘this-command’ to trick them. --sfp

To make the two functions more cooperative with the ShiftSelectMode of Emacs 23, you may add an (unused) argument and specify `^’ in interactive’s argument: (&optional arg) (interactive "^P"). And for shift selection of CuaMode to work, you need to set the ‘CUA’ property. So, as a result of a discussion on Stackoverflow, these functions take the following form:

    (defun sfp-page-down (&optional arg)
      (interactive "^P")
      (setq this-command 'next-line)
      (next-line
       (- (window-text-height)
          next-screen-context-lines)))
    (put 'sfp-page-down 'isearch-scroll t)
    (put 'sfp-page-down 'CUA 'move)
    
    (defun sfp-page-up (&optional arg)
      (interactive "^P")
      (setq this-command 'previous-line)
      (previous-line
       (- (window-text-height)
          next-screen-context-lines)))
    (put 'sfp-page-up 'isearch-scroll t)
    (put 'sfp-page-up 'CUA 'move)

Modern Page-up and Page-down

The following shows how to use ‘condition-case’ as an alternative approach to extending the behavior for ‘scroll-up’ and ‘scroll-down’.

When navigating the buffer with ‘scroll-up’ and ‘scroll-down’ – from the traditional key bindings or otherwise – reaching the end of the buffer isn’t possible. In many applications, scrolling with Pgup or Pgdn would eventually get you to the top or the bottom. Emacs on the other hand will stop scrolling when the end of the buffer is reached. The philosophy (read: religion) is that scrolling commands are wholly separate from the point motion commands: Ideological purity that is imposed on the user because by the implementation. Fortunately, it doesn’t take much to customize Emacs to a more familiar behavior.

The following is from RyanBarrett’s blog.[2]

  (global-set-key [next]
    (lambda () (interactive)
      (condition-case nil (scroll-up)
        (end-of-buffer (goto-char (point-max))))))
  
  (global-set-key [prior]
    (lambda () (interactive)
      (condition-case nil (scroll-down)
        (beginning-of-buffer (goto-char (point-min))))))

Another way is to just enable cua-mode, which gives you this more modern behaviour by default.

But be advised that ‘cua-mode’ will change more behaviors than just that.

Emacs 24 has (setq scroll-error-top-bottom t) to approximate this behavior.

Other Scrolling Pages


CategoryDisplay