/vendor/smooth-scrolling.el
Emacs Lisp | 201 lines | 101 code | 14 blank | 86 comment | 5 complexity | cff6aad284bdd5c50ab54c728d342e98 MD5 | raw file
Possible License(s): GPL-2.0
1;; smooth-scrolling.el 2;; $Id: smooth-scrolling.el,v 1.10 2009-12-19 01:45:28 adam Exp $ 3;; Adam Spiers <emacs-ss@adamspiers.org> 4;; 5;; Make emacs scroll smoothly, keeping the point away from the top and 6;; bottom of the current buffer's window in order to keep lines of 7;; context around the point visible as much as possible, whilst 8;; avoiding sudden scroll jumps which are visually confusing. 9;; 10;; This is a nice alternative to all the native scroll-* custom 11;; variables, which unfortunately cannot provide this functionality 12;; perfectly. `scroll-margin' comes closest, but has some bugs 13;; (e.g. with handling of mouse clicks). See 14;; 15;; http://www.emacswiki.org/cgi-bin/wiki/SmoothScrolling 16;; 17;; for the gory details. 18;; 19;;;_* Installation 20;; 21;; Put somewhere on your `load-path' and include 22;; 23;; (require 'smooth-scrolling) 24;; 25;; in your .emacs initialization file. 26;; 27;;;_* Notes 28;; 29;; This only affects the behaviour of the `next-line' and 30;; `previous-line' functions, usually bound to the cursor keys and 31;; C-n/C-p, and repeated isearches (`isearch-repeat'). Other methods 32;; of moving the point will behave as normal according to the standard 33;; custom variables. 34;; 35;; Prefix arguments to `next-line' and `previous-line' are honoured. 36;; The minimum number of lines are scrolled in order to keep the 37;; point outside the margin. 38;; 39;; There is one case where moving the point in this fashion may cause 40;; a jump: if the point is placed inside one of the margins by another 41;; method (e.g. left mouse click, or M-x goto-line) and then moved in 42;; the normal way, the advice code will scroll the minimum number of 43;; lines in order to keep the point outside the margin. This jump may 44;; cause some slight confusion at first, but hopefully it is justified 45;; by the benefit of automatically ensuring `smooth-scroll-margin' 46;; lines of context are visible around the point as often as possible. 47;; 48;;;_* TODO 49;; 50;; - Maybe add option to avoid scroll jumps when point is within 51;; margin. 52;; 53;;;_* Acknowledgements 54;; 55;; Thanks to Mark Hulme-Jones and consolers on #emacs for helping 56;; debug issues with line-wrapping. 57;; 58;;;_* License 59;; 60;; Released under the GNU General Public License v2 or later, with 61;; all rights assigned to the Free Software Foundation. 62;; 63 64;;;_* Code follows 65;;;_ + disable `scroll-margin' 66(setq scroll-margin 0) 67;;;_ + defcustoms 68(defcustom smooth-scroll-margin 10 69 "Number of lines of visible margin at the top and bottom of a window. 70If the point is within these margins, then scrolling will occur 71smoothly for `previous-line' at the top of the window, and for 72`next-line' at the bottom. 73 74This is very similar in its goal to `scroll-margin'. However, it 75is implemented by activating `smooth-scroll-down' and 76`smooth-scroll-up' advise via `defadvice' for `previous-line' and 77`next-line' respectively. As a result it avoids problems 78afflicting `scroll-margin', such as a sudden jump and unexpected 79highlighting of a region when the mouse is clicked in the margin. 80 81Scrolling only occurs when the point is closer to the window 82boundary it is heading for (top or bottom) than the middle of the 83window. This is to intelligently handle the case where the 84margins cover the whole buffer (e.g. `smooth-scroll-margin' set 85to 5 and `window-height' returning 10 or less). 86 87See also `smooth-scroll-strict-margins'." 88 :type 'integer 89 :group 'windows) 90 91(defcustom smooth-scroll-strict-margins t 92 "If true, the advice code supporting `smooth-scroll-margin' 93will use `count-screen-lines' to determine the number of 94*visible* lines between the point and the window top/bottom, 95rather than `count-lines' which obtains the number of actual 96newlines. This is because there might be extra newlines hidden 97by a mode such as folding-mode, outline-mode, org-mode etc., or 98fewer due to very long lines being displayed wrapped when 99`truncate-lines' is nil. 100 101However, using `count-screen-lines' can supposedly cause 102performance issues in buffers with extremely long lines. Setting 103`cache-long-line-scans' may be able to address this; 104alternatively you can set this variable to nil so that the advice 105code uses `count-lines', and put up with the fact that sometimes 106the point will be allowed to stray into the margin." 107 :type 'boolean 108 :group 'windows) 109;;;_ + helper functions 110(defun smooth-scroll-lines-from-window-top () 111 "Work out, using the function indicated by 112`smooth-scroll-strict-margins', what the current screen line is, 113relative to the top of the window. Counting starts with 1 referring 114to the top line in the window." 115 (interactive) 116 (cond ((= (window-start) (point)) 117 ;; In this case, count-screen-lines would return 0, so we override. 118 1) 119 (smooth-scroll-strict-margins 120 (count-screen-lines (window-start) (point) 'count-final-newline)) 121 (t 122 (count-lines (window-start) (point))))) 123 124(defun smooth-scroll-lines-from-window-bottom () 125 "Work out, using the function indicated by 126`smooth-scroll-strict-margins', how many screen lines there are 127between the point and the bottom of the window. Counting starts 128with 1 referring to the bottom line in the window." 129 (interactive) 130 (if smooth-scroll-strict-margins 131 (count-screen-lines (point) (window-end)) 132 (count-lines (point) (window-end)))) 133;;;_ + after advice 134 135(defun smooth-scroll-down () 136 "Scroll down smoothly if cursor is within `smooth-scroll-margin' 137lines of the top of the window." 138 (and 139 ;; Only scroll down if there is buffer above the start of the window. 140 (> (line-number-at-pos (window-start)) 1) 141 (let ((lines-from-window-top 142 (smooth-scroll-lines-from-window-top))) 143 (and 144 ;; Only scroll down if we're within the top margin 145 (<= lines-from-window-top smooth-scroll-margin) 146 ;; Only scroll down if we're in the top half of the window 147 (<= lines-from-window-top 148 ;; N.B. `window-height' includes modeline, so if it returned 21, 149 ;; that would mean exactly 10 lines in the top half and 10 in 150 ;; the bottom. 22 (or any even number) means there's one in the 151 ;; middle. In both cases the following expression will 152 ;; yield 10: 153 (/ (1- (window-height)) 2)) 154 (save-excursion 155 (scroll-down 156 (1+ (- smooth-scroll-margin lines-from-window-top)))))))) 157 158(defun smooth-scroll-up () 159 "Scroll up smoothly if cursor is within `smooth-scroll-margin' 160lines of the bottom of the window." 161 (and 162 ;; Only scroll up if there is buffer below the end of the window. 163 (< (window-end) (buffer-end 1)) 164 (let ((lines-from-window-bottom 165 (smooth-scroll-lines-from-window-bottom))) 166 (and 167 ;; Only scroll up if we're within the bottom margin 168 (<= lines-from-window-bottom smooth-scroll-margin) 169 ;; Only scroll up if we're in the bottom half of the window. 170 (<= lines-from-window-bottom 171 ;; See above notes on `window-height'. 172 (/ (1- (window-height)) 2)) 173 (save-excursion 174 (scroll-up 175 (1+ (- smooth-scroll-margin lines-from-window-bottom)))))))) 176 177(defadvice previous-line (after smooth-scroll-down 178 (&optional arg try-vscroll) 179 activate) 180 (smooth-scroll-down)) 181(defadvice next-line (after smooth-scroll-up 182 (&optional arg try-vscroll) 183 activate) 184 (smooth-scroll-up)) 185 186(defadvice isearch-repeat (after isearch-smooth-scroll 187 (direction) 188 activate) 189 (if (eq direction 'forward) 190 (smooth-scroll-up) 191 (smooth-scroll-down))) 192 193;;;_ + provide 194(provide 'smooth-scrolling) 195 196;;;_* Local emacs variables 197 198;;Local variables: 199;;allout-layout: (0 : -1 0) 200;;mode: allout 201;;End: