/vendor/smooth-scrolling.el

http://github.com/rejeep/emacs · Emacs Lisp · 201 lines · 101 code · 14 blank · 86 comment · 5 complexity · cff6aad284bdd5c50ab54c728d342e98 MD5 · raw file

  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. ;;;_* Code follows
  64. ;;;_ + disable `scroll-margin'
  65. (setq scroll-margin 0)
  66. ;;;_ + defcustoms
  67. (defcustom smooth-scroll-margin 10
  68. "Number of lines of visible margin at the top and bottom of a window.
  69. If the point is within these margins, then scrolling will occur
  70. smoothly for `previous-line' at the top of the window, and for
  71. `next-line' at the bottom.
  72. This is very similar in its goal to `scroll-margin'. However, it
  73. is implemented by activating `smooth-scroll-down' and
  74. `smooth-scroll-up' advise via `defadvice' for `previous-line' and
  75. `next-line' respectively. As a result it avoids problems
  76. afflicting `scroll-margin', such as a sudden jump and unexpected
  77. highlighting of a region when the mouse is clicked in the margin.
  78. Scrolling only occurs when the point is closer to the window
  79. boundary it is heading for (top or bottom) than the middle of the
  80. window. This is to intelligently handle the case where the
  81. margins cover the whole buffer (e.g. `smooth-scroll-margin' set
  82. to 5 and `window-height' returning 10 or less).
  83. See also `smooth-scroll-strict-margins'."
  84. :type 'integer
  85. :group 'windows)
  86. (defcustom smooth-scroll-strict-margins t
  87. "If true, the advice code supporting `smooth-scroll-margin'
  88. will use `count-screen-lines' to determine the number of
  89. *visible* lines between the point and the window top/bottom,
  90. rather than `count-lines' which obtains the number of actual
  91. newlines. This is because there might be extra newlines hidden
  92. by a mode such as folding-mode, outline-mode, org-mode etc., or
  93. fewer due to very long lines being displayed wrapped when
  94. `truncate-lines' is nil.
  95. However, using `count-screen-lines' can supposedly cause
  96. performance issues in buffers with extremely long lines. Setting
  97. `cache-long-line-scans' may be able to address this;
  98. alternatively you can set this variable to nil so that the advice
  99. code uses `count-lines', and put up with the fact that sometimes
  100. the point will be allowed to stray into the margin."
  101. :type 'boolean
  102. :group 'windows)
  103. ;;;_ + helper functions
  104. (defun smooth-scroll-lines-from-window-top ()
  105. "Work out, using the function indicated by
  106. `smooth-scroll-strict-margins', what the current screen line is,
  107. relative to the top of the window. Counting starts with 1 referring
  108. to the top line in the window."
  109. (interactive)
  110. (cond ((= (window-start) (point))
  111. ;; In this case, count-screen-lines would return 0, so we override.
  112. 1)
  113. (smooth-scroll-strict-margins
  114. (count-screen-lines (window-start) (point) 'count-final-newline))
  115. (t
  116. (count-lines (window-start) (point)))))
  117. (defun smooth-scroll-lines-from-window-bottom ()
  118. "Work out, using the function indicated by
  119. `smooth-scroll-strict-margins', how many screen lines there are
  120. between the point and the bottom of the window. Counting starts
  121. with 1 referring to the bottom line in the window."
  122. (interactive)
  123. (if smooth-scroll-strict-margins
  124. (count-screen-lines (point) (window-end))
  125. (count-lines (point) (window-end))))
  126. ;;;_ + after advice
  127. (defun smooth-scroll-down ()
  128. "Scroll down smoothly if cursor is within `smooth-scroll-margin'
  129. lines of the top of the window."
  130. (and
  131. ;; Only scroll down if there is buffer above the start of the window.
  132. (> (line-number-at-pos (window-start)) 1)
  133. (let ((lines-from-window-top
  134. (smooth-scroll-lines-from-window-top)))
  135. (and
  136. ;; Only scroll down if we're within the top margin
  137. (<= lines-from-window-top smooth-scroll-margin)
  138. ;; Only scroll down if we're in the top half of the window
  139. (<= lines-from-window-top
  140. ;; N.B. `window-height' includes modeline, so if it returned 21,
  141. ;; that would mean exactly 10 lines in the top half and 10 in
  142. ;; the bottom. 22 (or any even number) means there's one in the
  143. ;; middle. In both cases the following expression will
  144. ;; yield 10:
  145. (/ (1- (window-height)) 2))
  146. (save-excursion
  147. (scroll-down
  148. (1+ (- smooth-scroll-margin lines-from-window-top))))))))
  149. (defun smooth-scroll-up ()
  150. "Scroll up smoothly if cursor is within `smooth-scroll-margin'
  151. lines of the bottom of the window."
  152. (and
  153. ;; Only scroll up if there is buffer below the end of the window.
  154. (< (window-end) (buffer-end 1))
  155. (let ((lines-from-window-bottom
  156. (smooth-scroll-lines-from-window-bottom)))
  157. (and
  158. ;; Only scroll up if we're within the bottom margin
  159. (<= lines-from-window-bottom smooth-scroll-margin)
  160. ;; Only scroll up if we're in the bottom half of the window.
  161. (<= lines-from-window-bottom
  162. ;; See above notes on `window-height'.
  163. (/ (1- (window-height)) 2))
  164. (save-excursion
  165. (scroll-up
  166. (1+ (- smooth-scroll-margin lines-from-window-bottom))))))))
  167. (defadvice previous-line (after smooth-scroll-down
  168. (&optional arg try-vscroll)
  169. activate)
  170. (smooth-scroll-down))
  171. (defadvice next-line (after smooth-scroll-up
  172. (&optional arg try-vscroll)
  173. activate)
  174. (smooth-scroll-up))
  175. (defadvice isearch-repeat (after isearch-smooth-scroll
  176. (direction)
  177. activate)
  178. (if (eq direction 'forward)
  179. (smooth-scroll-up)
  180. (smooth-scroll-down)))
  181. ;;;_ + provide
  182. (provide 'smooth-scrolling)
  183. ;;;_* Local emacs variables
  184. ;;Local variables:
  185. ;;allout-layout: (0 : -1 0)
  186. ;;mode: allout
  187. ;;End: