By default Emacs scrolls nearly full screenfuls with scroll-up and scroll-down. I personally would like it to scroll half a page at a time, but unfortunately haven’t been able to figure out how to do it cleanly.

There is a next-screen-context-lines variable which controls how many lines of continuity should be preserved when scrolling by screenfuls. The problem at hand could be solved by setting this variable to window-height/2, but obviously it should be calculated every time the next-screen-context-lines is used because window-height is not constant.

The only working solution I came up with is the following sucky hack. It’s still better than nothing, so here it comes:

   (defun window-half-height ()
     (max 1 (/ (1- (window-height (selected-window))) 2)))
   (defun scroll-up-half ()
     (scroll-up (window-half-height)))
   (defun scroll-down-half ()         
     (scroll-down (window-half-height)))
   (global-set-key [next] 'scroll-up-half)
   (global-set-key [prior] 'scroll-down-half)

Otherwise this would work well, but when scroll-preserve-screen-position is true, it doesn’t actually preserve the point screen position. It would preserve it if next-screen-context-lines was set to (window-half-height) and no arguments were given to scroll-up/down.

Hopefully someone who actually knows (e)lisp (I don’t) comes up with a better solution.

Solution With defadvice

Such a problem could be solved using defadvice but I think that is even uglier than the previous approach. The trick is to recalculate the value of next-screen-context-lines before actually performing the function.

    (defadvice scroll-up (around half-window activate)
      (setq next-screen-context-lines
            (max 1 (/ (1- (window-height (selected-window))) 2)))

and the appropriate for scroll-down…

(please note that I did not test this thoroughly…please remove this note whenever this code was tested)

Another Possibile Solution

I bind this to shift-space. It’s main feature is that your attention always goes to two fixed locations on the screen so as to avoid dizzy feelings (exceptions when you are at the top or bottom of buffer). – MaverickWoo

  (defvar scroll-half-window-was-up nil "Was the last half-scrolling going up?")
  (defun scroll-half-window ()
    "By default, attention go forward by half a window as follows:
  If point is in upper half of window, then point jumps to lower half.
  (current line will change)
  If point is in lower half, the scroll up to make point in upper half.
  (current line will not change)
  But if the last command is a member of `up-command-list', then reverse.
  Consecutive invocation maintains in the same travel direction.
  Works especially well with modes that highlight the current line.
  The actual destinations of the point are controlled by `upper-precent' and
    (let* ( ;;manually set these personal preferences
           (upper-percent 0.15)
           (lower-percent 0.85)
           (up-command-list '(previous-line
           ;; number of lines shown in selected window, one less due to mode line
           (winlines (1- (window-height (selected-window))))
           ;; number of lines above and below point
           (above (+ (cdr (nth 6 (posn-at-point)))
                     -1 (if (= (current-column) 0) 1 0)))
           (below (- winlines above 1)))
      ;; compute if we should go up or not
      (let ((should-up (or (and scroll-half-window-was-up
                                (eq last-command 'scroll-half-window))
                           (memq last-command up-command-list))))
        ;; dispatch depending on whether point is in upper half or lower half
        (if (< above below)
          (apply (if should-up 'scroll-down 'next-line) ;upper
                 (list (- (floor (* winlines lower-percent)) above)))
          (apply (if should-up 'next-line 'scroll-down) ;lower
                 (list (- (floor (* winlines upper-percent)) above))))
        ;; done, remember last direction before exit
        (setq scroll-half-window-was-up should-up))))

Solution using built-in commands

I’ve used:

  move-to-window-line (moves the point to center by default)
  recenter-top-bottom (second invocation scrolls line with point to top)

which in most cases is bound to M-r C-l C-l

(note, recenter-top-bottom seems only to exhibit the desired behavior when invoked using a key binding)

Half page scrolling à la Vim

Coming from vim and still evil-mode not having a decent half scroll page function, based on above commands I came up with this:

  (defun zz-scroll-half-page (direction)
    "Scrolls half page up if `direction' is non-nil, otherwise will scroll half page down."
    (let ((opos (cdr (nth 6 (posn-at-point)))))
      ;; opos = original position line relative to window
      (move-to-window-line nil)  ;; Move cursor to middle line
      (if direction
          (recenter-top-bottom -1)  ;; Current line becomes last
        (recenter-top-bottom 0))  ;; Current line becomes first
      (move-to-window-line opos)))  ;; Restore cursor/point position
  (defun zz-scroll-half-page-down ()
    "Scrolls exactly half page down keeping cursor/point position."
    (zz-scroll-half-page nil))
  (defun zz-scroll-half-page-up ()
    "Scrolls exactly half page up keeping cursor/point position."
    (zz-scroll-half-page t))


This seems to be built-in. Maybe it wasn’t in the past. I use View-scroll-half-page-backward and View-scroll-half-page-forward from view.el