home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / languages / elisp / misc / scroll-in-place.el < prev    next >
Encoding:
Text File  |  1993-03-20  |  58.0 KB  |  1,257 lines

  1. ;;;; -*-Emacs-Lisp-*- Improved Vertical Scrolling Commands
  2. ;;;; Written by Eric Eide, last modified on February 26, 1993.
  3. ;;;; (C) Copyright 1993, Eric Eide and the University of Utah
  4. ;;;;
  5. ;;;; COPYRIGHT NOTICE
  6. ;;;;
  7. ;;;; This program is free software; you can redistribute it and/or modify it
  8. ;;;; under the terms of the GNU General Public License as published by the Free
  9. ;;;; Software Foundation; either version 1, or (at your option) any later
  10. ;;;; version.
  11. ;;;;
  12. ;;;; This program is distributed in the hope that it will be useful, but 
  13. ;;;; WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
  14. ;;;; or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  15. ;;;; for more details.
  16. ;;;;
  17. ;;;; You should have received a copy of the GNU General Public License along
  18. ;;;; with this program; if not, write to the Free Software Foundation, Inc.,
  19. ;;;; 675 Mass Ave, Cambridge, MA 02139, USA.
  20.  
  21. ;;;; AUTHORS
  22. ;;;;
  23. ;;;; This package was written by Eric Eide (eeide@cs.utah.edu) and was based on
  24. ;;;; a very similar package ("scroll-fix") by Joe Wells.  Almost all of the
  25. ;;;; code in this file is original, but I owe a great debt to Mr. Wells for his
  26. ;;;; ideas and his original implementation.
  27. ;;;;
  28. ;;;;   Eric Eide (eeide@cs.utah.edu)
  29. ;;;;   University of Utah
  30. ;;;;   3190 Merrill Engineering Building
  31. ;;;;   Salt Lake City, Utah  84112
  32. ;;;;
  33. ;;;;   Joe Wells (jbw@cs.bu.edu)
  34. ;;;;
  35. ;;;; Joe Wells' "scroll-fix" package is Copyright (C) 1988, 1989, and 1991 by
  36. ;;;; the Free Software Foundation.  It is distributed under the terms of the
  37. ;;;; GNU General Public License.
  38.  
  39. ;;;; LISP CODE DIRECTORY INFORMATION
  40. ;;;;
  41. ;;;; LCD Archive Entry:
  42. ;;;; scroll-in-place|Eric Eide|eeide@cs.utah.edu|
  43. ;;;; Improved vertical scrolling commands|
  44. ;;;; 26-Feb-1993||~/misc/scroll-in-place.el.Z|
  45.  
  46. ;;;; SUMMARY
  47. ;;;;
  48. ;;;; This package provides improved vertical scrolling commands for GNU Emacs.
  49. ;;;; These new commands offer the following features:
  50. ;;;;
  51. ;;;; + When a scrolling command is executed, GNU Emacs tries to keep point as
  52. ;;;;   close as possible to its original window position (window line and
  53. ;;;;   column).  This is what "scroll in place" means: point stays "in place"
  54. ;;;;   within the window.  (There are times when point must be moved from its
  55. ;;;;   original window position in order to execute the scroll; see below.)
  56. ;;;;
  57. ;;;;   The variable scroll-in-place, which is true by default, determines
  58. ;;;;   whether or not the standard GNU Emacs scrolling commands (scroll-down,
  59. ;;;;   scroll-up, and scroll-other-window) use the "in place" features listed
  60. ;;;;   here.  When scroll-in-place is nil, the standard GNU Emacs scrolling
  61. ;;;;   commands essentially just call the original versions of themselves.
  62. ;;;;   (Note that even when scroll-in-place is nil, the new versions of scroll-
  63. ;;;;   down and scroll-up have slightly different behavior when the minibuffer
  64. ;;;;   window is the selected window.  See below.)
  65. ;;;;
  66. ;;;;   It is possible to turn off (or turn on) "in place" scrolling for certain
  67. ;;;;   buffers by making buffer-local bindings of the variable scroll-in-place
  68. ;;;;   for those buffers.  The variable scroll-in-place is not usually buffer-
  69. ;;;;   local, but you can make it so if you desire.
  70. ;;;;
  71. ;;;; + Because the improved scrolling commands keep point at its original
  72. ;;;;   window position, these scrolling commands are "reversible."  The
  73. ;;;;   scroll-up command undoes the effect of the immediately previous
  74. ;;;;   scroll-down command (if any) and vice versa.  In other words, if you
  75. ;;;;   scroll up and then immediately scroll back down, the window
  76. ;;;;   configuration is restored to its exact original state.  This allows you
  77. ;;;;   to browse through a buffer more easily, as you can always get back to
  78. ;;;;   the original configuration.
  79. ;;;;
  80. ;;;;   Note, however, that the improved scrolling commands are guaranteed to be
  81. ;;;;   reversible only if there are no intervening non-scrolling commands.
  82. ;;;;   Also, if you give a prefix argument to a scrolling command (in order to
  83. ;;;;   specify the number of lines to scroll by), previous scrolling commands
  84. ;;;;   may no longer be reversible.  More specifically, if the new prefix
  85. ;;;;   argument has a different magnitude than the previous scrolling distance,
  86. ;;;;   then any previous scrolling commands are not reversible.  The new prefix
  87. ;;;;   argument takes precedence.
  88. ;;;;
  89. ;;;;   You might find it useful to think of the scrolling commands as forming
  90. ;;;;   "chains."  A scrolling command either starts or continues a chain.  By
  91. ;;;;   issuing a non-scrolling command or by changing the number of lines to be
  92. ;;;;   scrolled, you break the chain.  (Note that simply changing the scrolling
  93. ;;;;   direction won't break the chain; changing the absolute number of lines
  94. ;;;;   to be scrolled is what breaks the chain.)  Scrolling commands are
  95. ;;;;   guaranteed to be reversible only within the current chain.  Hopefully
  96. ;;;;   that's clear enough.
  97. ;;;;
  98. ;;;; + When a scrolling command is given a prefix argument (which specifies the
  99. ;;;;   number of lines to scroll by), then that argument becomes the default
  100. ;;;;   scrolling distance for all immediately subsequent scrolling commands.
  101. ;;;;   This means that you can easily set the scrolling distance for a chain
  102. ;;;;   of scrolling commands.  Note that a new prefix argument or any non-
  103. ;;;;   scrolling command breaks the chain (as described above), and any further
  104. ;;;;   scrolling commands will use the usual defaults (or the prefix argument
  105. ;;;;   you specify at that time, of course).
  106. ;;;;
  107. ;;;;   However, there are cases in which one doesn't want the current scrolling
  108. ;;;;   command to use the default scrolling distance that was set by the
  109. ;;;;   previous scrolling command.  For example, suppose that you had special
  110. ;;;;   commands that scrolled one line up and one line down.  When you invoke
  111. ;;;;   one of these commands, the "in place" scrolling routines set the default
  112. ;;;;   scrolling distance to be just one line.  Now suppose that you use one of
  113. ;;;;   your special commands and then immediately invoke scroll-up (C-v),
  114. ;;;;   expecting it to scroll by a near windowful of text.  You would be
  115. ;;;;   disappointed -- because the previous command set the default scrolling
  116. ;;;;   distance to be just one line, scroll-up just scrolls by one line.
  117. ;;;;
  118. ;;;;   To solve this problem, "scroll-in-place" allows you to divide scrolling
  119. ;;;;   commands into separate "groups."  Commands in a group can only form
  120. ;;;;   chains with (and therefore, inherit defaults from) commands in the same
  121. ;;;;   group.  (Note that no command can be in more than one group.)  If you
  122. ;;;;   invoke a scrolling command that is not in the same group as that of the
  123. ;;;;   immediately previous scrolling command, then the previous chain is
  124. ;;;;   broken and you start a new chain -- with a new set of defaults.
  125. ;;;;
  126. ;;;;   So to solve the problem described above, you could put your one-line
  127. ;;;;   scrolling commands in their own group.  Once that is done, the standard
  128. ;;;;   scrolling commands will not form chains with your one-line scrolling
  129. ;;;;   commands, and therefore will not use the default scrolling distance set
  130. ;;;;   by those commands.  Problem solved!
  131. ;;;;
  132. ;;;;   By default, all "in place" scrolling commands are in a single group.  If
  133. ;;;;   you want to partition some commands into separate groups, you must do
  134. ;;;;   that yourself *before* any "in place" commands are invoked.  For more
  135. ;;;;   information about grouping commands, see the documentation for variables
  136. ;;;;   scroll-command-groups and scroll-default-command-group.
  137. ;;;;
  138. ;;;; + The improved scrolling commands will avoid displaying empty lines past
  139. ;;;;   the end of the buffer when possible.  In other words, just as you can't
  140. ;;;;   see "dead space" before the beginning of the buffer text, the new
  141. ;;;;   scrolling commands try to avoid displaying "dead space" past the end of
  142. ;;;;   the buffer text.  This behavior is somewhat configurable; see the
  143. ;;;;   documentation for the variable scroll-allow-blank-lines-past-eob.
  144. ;;;;
  145. ;;;;   Dead space will be displayed if it is necessary in order to make a
  146. ;;;;   previous scrolling action reversible, however.
  147. ;;;;
  148. ;;;; + If the scrolling commands cannot keep point at its initial window
  149. ;;;;   position (because a buffer boundary is on screen and the window can't be
  150. ;;;;   scrolled as far as necessary to keep point at the right place), point is
  151. ;;;;   allowed to temporarily stray from its initial window position.  That is,
  152. ;;;;   point moves the correct number of window lines, even if it means that it
  153. ;;;;   has to stray from its desired window position.  This straying is undone
  154. ;;;;   when (and if) the scrolling action is reversed.
  155. ;;;;
  156. ;;;; + If a scrolling command tries to move point past a buffer boundary, point
  157. ;;;;   is instead moved to the boundary (the beginning or the end of the buffer
  158. ;;;;   as appropriate) and an appropriate message is displayed.  This motion is
  159. ;;;;   reversible, of course.
  160. ;;;;
  161. ;;;;   However, if point was already at the buffer boundary when the scrolling
  162. ;;;;   command was invoked, the command signals an appropriate error instead.
  163. ;;;;
  164. ;;;; + When the minibuffer window is the selected window, the new versions of
  165. ;;;;   scroll-up and scroll-down either scroll the minibuffer-scroll-window
  166. ;;;;   (which is usually the window of completions) or the next-window if there
  167. ;;;;   is no minibuffer-scroll-window.  This is usually much more useful than
  168. ;;;;   scrolling the minibuffer itself.  (Note that this feature is available
  169. ;;;;   even when the variable scroll-in-place is nil.)
  170. ;;;;
  171. ;;;; + When a scrolling command is scrolling a window other than the selected
  172. ;;;;   window, it will signal an appropriate buffer boundary error if the
  173. ;;;;   window cannot be scrolled (because the appropriate buffer boundary is
  174. ;;;;   already visible).  This means that an error is signalled even in cases
  175. ;;;;   that would be allowed (by "straying" point or by moving it to the buffer
  176. ;;;;   boundary) if the window were selected.
  177. ;;;;
  178. ;;;;   (If an error were not signalled in these cases, then there would be many
  179. ;;;;   cases in which the last scroll in a particular direction would appear to
  180. ;;;;   do nothing because only the point position would change -- the displayed
  181. ;;;;   text would stay the same!  To avoid these cases the scrolling commands
  182. ;;;;   signal boundary errors "prematurely" when the window to be scrolled is
  183. ;;;;   not selected.)
  184. ;;;;
  185. ;;;; So how is this package different than Joe Wells' "scroll-fix" package?
  186. ;;;;
  187. ;;;; + This package provides "in place" behavior for the standard GNU Emacs
  188. ;;;;   commands by default; "scroll-fix" does not.
  189. ;;;;
  190. ;;;; + "scroll-fix" behaves differently when the window is near a buffer
  191. ;;;;   boundary.  Instead of allowing point to stray, "scroll-fix" first does
  192. ;;;;   an incomplete scroll (i.e., moves point less than the full distance in
  193. ;;;;   order to keep point at the desired window position) and then pops point
  194. ;;;;   to the buffer boundary.  I think that the behavior of this package is
  195. ;;;;   somewhat move intuitive, especially for small scrolling distances.
  196. ;;;;
  197. ;;;; + The scrolling commands in this package will appropriately signal buffer
  198. ;;;;   boundary errors; the commands in "scroll-fix" never signal boundary
  199. ;;;;   errors.  This makes it difficult to allow "scroll-fix" to replace the
  200. ;;;;   standard scroll-down and scroll-up commands because some other packages
  201. ;;;;   (e.g., VM and GNUS) expect the scrolling commands to signal these errors
  202. ;;;;   as necessary.
  203. ;;;;
  204. ;;;; + This package handles long lines correctly.  (But see PROBLEMS, below.)
  205. ;;;;
  206. ;;;; + "scroll-fix" handles prefix arguments differently.  In "scroll-fix", a
  207. ;;;;   number-containing prefix argument always breaks any running chain of
  208. ;;;;   scrolling commands.  The prefix argument - (the symbol minus, generated
  209. ;;;;   by C-u -) causes a temporary change in direction (a change for only the
  210. ;;;;   current command).  In this package, a number-containing prefix argument
  211. ;;;;   only breaks a running chain if it has a different magnitude than the
  212. ;;;;   default scrolling distance, and the prefix argument - causes a permanent
  213. ;;;;   change in the sign of the default scrolling distance (a change visible
  214. ;;;;   to immediately subsequent scrolling commands).
  215. ;;;;
  216. ;;;; + This package keeps track of the set of "in place" scrolling commands
  217. ;;;;   dynamically, in order to detect "chains" of scrolling commands.
  218. ;;;;   "scroll-fix" has a fixed list of scrolling commands, so "scroll-fix"
  219. ;;;;   cannot keep track of some chains.  (Again, "scroll-fix" interacts badly
  220. ;;;;   with VM and GNUS.)  And because "scroll-fix" keeps a static list of
  221. ;;;;   scrolling commands, it is a bad idea to call its "in place" commands
  222. ;;;;   from a program.  This package, because it maintains the information
  223. ;;;;   dynamically, has no such problems.
  224. ;;;;
  225. ;;;; + This package allows one to divide the "in place" scrolling commands into
  226. ;;;;   groups; a command in a group only forms chains with the members of its
  227. ;;;;   group.  "scroll-fix" has no notion of command groups.
  228. ;;;;
  229. ;;;; + This package provides an "in place" version of the standard GNU Emacs
  230. ;;;;   command scroll-other-window (and a replacement for scroll-other-window,
  231. ;;;;   too).
  232. ;;;;
  233. ;;;; + This package will refuse to scroll non-selected windows (by signalling
  234. ;;;;   an error) when the displayed text would not change, as described in the
  235. ;;;;   feature list above.
  236. ;;;;
  237. ;;;; + When the minibuffer is selected, this package always scrolls a window
  238. ;;;;   other than the minibuffer.  "scroll-fix" will scroll another window only
  239. ;;;;   if the entire minibuffer contents are visible.
  240. ;;;;
  241. ;;;; + "scroll-fix" provides a command to toggle the "in place" behavior of the
  242. ;;;;   standard GNU Emacs commands.  This package doesn't; you'll have to set
  243. ;;;;   the option manually with set-variable.
  244. ;;;;
  245. ;;;; + This package has gratuitous variable renaming (insert smile here!):
  246. ;;;;
  247. ;;;;   "scroll-fix" user variable            Equivalent in this package
  248. ;;;;   -----------------------------------------------------------------------
  249. ;;;;   scroll-in-place                       (none)
  250. ;;;;   scroll-in-place-replace-original      scroll-in-place
  251. ;;;;   scroll-in-place-eob-blank-allowed     scroll-allow-blank-lines-past-eob
  252. ;;;;
  253. ;;;; + This package allows programmers to specify the default scrolling
  254. ;;;;   distance (i.e., the default distance used when starting a new chain of
  255. ;;;;   scrolling commands) for custom scrolling commands.
  256.  
  257. ;;;; COMMANDS AND FUNCTIONS
  258. ;;;;
  259. ;;;; This package provides the following "in place" versions of GNU Emacs'
  260. ;;;; standard vertical scrolling commands:
  261. ;;;;
  262. ;;;;   scroll-down-in-place
  263. ;;;;   scroll-up-in-place
  264. ;;;;   scroll-other-window-in-place
  265. ;;;;
  266. ;;;; The variable scroll-in-place, which is true by default, determines whether
  267. ;;;; or not the new versions of the standard GNU Emacs scrolling commands
  268. ;;;; (scroll-down, scroll-up, and scroll-other-window) use the "in place"
  269. ;;;; features listed above.  When scroll-in-place is nil, the standard GNU
  270. ;;;; Emacs scrolling commands essentially just call the original versions of
  271. ;;;; themselves.  (Note that even when scroll-in-place is nil, the new versions
  272. ;;;; of scroll-down and scroll-up have slightly different behavior when the
  273. ;;;; minibuffer window is the selected window.  See the feature list above.)
  274. ;;;;
  275. ;;;; NOTE that this package redefines the standard GNU Emacs commands scroll-
  276. ;;;; down, scroll-up, and scroll-other-window (in order to check the variable
  277. ;;;; scroll-in-place, as described above).
  278. ;;;;
  279. ;;;; This package also provides the following functions and variables which are
  280. ;;;; of use to programmers:
  281. ;;;;
  282. ;;;;   scroll-window
  283. ;;;;   scroll-window-in-place
  284. ;;;;   scroll-window-in-place-continue-sequence
  285. ;;;;   scroll-default-lines (variable)
  286. ;;;;   scroll-command-groups (variable)
  287. ;;;;
  288. ;;;; scroll-window-in-place is the heart of the "in place" scrolling commands.
  289. ;;;; scroll-window is a function that checks the variable scroll-in-place and
  290. ;;;; calls the appropriate scrolling function (either scroll-window-in-place or
  291. ;;;; one of the original versions of scroll-down and scroll-up).  The function
  292. ;;;; scroll-window-in-place-continue-sequence is provided in order to preserve
  293. ;;;; running "chains" of scrolling commands as described above.
  294. ;;;;
  295. ;;;; The variable scroll-default-lines determines the default scrolling
  296. ;;;; distance when a new chain of "in place" scrolling commands begins.  If
  297. ;;;; this variable is not a number, then the default distance is the height of
  298. ;;;; the window to be scrolled minus next-screen-context-lines.  The variable
  299. ;;;; scroll-command-groups contains the explicit groups of "in place" scrolling
  300. ;;;; commands; for more information read the variable documentation.
  301.  
  302. ;;;; YOUR .EMACS FILE
  303. ;;;;
  304. ;;;; To use this package, you simply need to load it from within your ".emacs"
  305. ;;;; file:
  306. ;;;;
  307. ;;;;   (require 'scroll-in-place)
  308. ;;;;
  309. ;;;; By default, this package provides for the standard GNU Emacs vertical
  310. ;;;; scrolling commands (scroll-down, scroll-up, and scroll-other-window) to
  311. ;;;; use the "in place" features.  If you would rather not have this, set the
  312. ;;;; variable scroll-in-place to nil:
  313. ;;;;
  314. ;;;;   (setq scroll-in-place nil)
  315. ;;;;
  316. ;;;; When scroll-in-place is nil, you will have to bind keys in order to call
  317. ;;;; the "in place" scrolling commands.  For example, you might want to do the
  318. ;;;; following:
  319. ;;;;
  320. ;;;;   (global-set-key "\M-v" 'scroll-down-in-place)
  321. ;;;;   (global-set-key "\C-v" 'scroll-up-in-place)
  322. ;;;;
  323. ;;;; Sun users should also read the PROBLEMS section, below.
  324. ;;;;
  325. ;;;; ADVANCED CUSTOMIZATION
  326. ;;;;
  327. ;;;; If you want to partition certain "in place" scrolling commands into
  328. ;;;; separate groups, you should do something like the following:
  329. ;;;;
  330. ;;;;   ;; Make one group containing the commands scroll-down-one-line and
  331. ;;;;   ;; scroll-up-one-line.  (These are not standard GNU Emacs commands.)
  332. ;;;;   (setq scroll-command-groups
  333. ;;;;         (list '(scroll-down-one-line scroll-up-one-line)))
  334. ;;;;
  335. ;;;; If you want to disable "in place" scrolling for windows that display a
  336. ;;;; particular buffer (while leaving it available in other windows), you can
  337. ;;;; make scroll-in-place a buffer-local variable for that buffer and then bind
  338. ;;;; that local copy of scroll-in-place to nil.  This is the kind of thing that
  339. ;;;; one generally does in a major mode hook.  For example, you can disable "in
  340. ;;;; place" scrolling of GNUS article windows with the following code:
  341. ;;;;
  342. ;;;;   (setq gnus-Article-mode-hook
  343. ;;;;         (function (lambda ()
  344. ;;;;                     (make-local-variable 'scroll-in-place)
  345. ;;;;                     (setq scroll-in-place nil))))
  346. ;;;;
  347. ;;;; The variable scroll-allow-blank-lines-past-eob can also be made local to
  348. ;;;; particular buffers, if you desire.  (But why would you want to do that?)
  349.  
  350. ;;;; PROBLEMS
  351. ;;;;
  352. ;;;; + It is sometimes difficult for one's eyes to follow an incomplete scroll
  353. ;;;;   (i.e., a scroll in which the text doesn't move as far as one expected),
  354. ;;;;   especially when the scrolled window is not selected (and therefore that
  355. ;;;;   window's point is not highlighted).  One can lose one's place in the
  356. ;;;;   text.
  357. ;;;;
  358. ;;;; + The names scroll-down-in-place and scroll-up-in-place conflict with two
  359. ;;;;   commands in the GNU Emacs terminal-specific file "term/sun.el".  This
  360. ;;;;   means that in order to load this package correctly, Sunterm users will
  361. ;;;;   have to use the hook term-setup-hook.  For example, you might put the
  362. ;;;;   following form in your ".emacs" file:
  363. ;;;;
  364. ;;;;   (setq term-setup-hook (function (lambda () (require 'scroll-in-place))))
  365. ;;;;
  366. ;;;;   If this is confusing, get help from your local GNU Emacs guru.
  367. ;;;;
  368. ;;;; + scroll-determine-goal-column tries to honor the variable track-eol if it
  369. ;;;;   is set.  But when lines are being wrapped we can't move point past the
  370. ;;;;   wrap -- or else it is possible that scrolling won't work correctly.  In
  371. ;;;;   short, this package honors track-eol as best it can.
  372. ;;;;
  373. ;;;; + scroll-window-in-place can get confused when something changes the
  374. ;;;;   window "out from under it."  By "confused" I mean that it is possible
  375. ;;;;   for scroll-window-in-place to think that it should continue the running
  376. ;;;;   sequence of "in place" scrolls when it should really probably start a
  377. ;;;;   new sequence.  For example, if a process filter inserts text into the
  378. ;;;;   buffer and moves point, scroll-window-in-place loses track of where
  379. ;;;;   point should be and where the window should start.  Commands that call a
  380. ;;;;   "scroll in place" function and then subsequently move point can also
  381. ;;;;   confuse scroll-window-in-place.
  382. ;;;;
  383. ;;;;   scroll-window-in-place is not confused by VM 4.41's message scrolling
  384. ;;;;   commands, which do renarrowing when an end-of-buffer error is signalled.
  385. ;;;;   (I don't use VM 5.32 yet, but a quick look at the code doesn't reveal
  386. ;;;;   any obvious problems.)  scroll-window-in-place can be confused by GNUS
  387. ;;;;   3.13's and 3.14's article scrolling commands because they move point to
  388. ;;;;   the last line of the article window and then scroll the text.
  389. ;;;;
  390. ;;;;   To correct this confusion, scroll-window-in-place would have to keep
  391. ;;;;   track of the final positions of window-start and window-point, possibly
  392. ;;;;   with both markers and character positions.  In my experience the "in
  393. ;;;;   place" scrolling commands are almost never confused, so the extra sanity
  394. ;;;;   checking isn't worth the effort.  If your mileage varies, let me know.
  395. ;;;;
  396. ;;;; + Process filters that call scrolling functions can cause confusion.  They
  397. ;;;;   may break running chains of "in place" scrolling commands and they may
  398. ;;;;   set up inappropriate defaults for future scrolling commands.  Maybe this
  399. ;;;;   is a moot problem, as I am unaware of any process filters that invoke
  400. ;;;;   scrolling commands.
  401.  
  402. (provide 'scroll-in-place)
  403.  
  404.  
  405. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  406. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  407. ;;;;
  408. ;;;; Here are the variable declarations, both user options and internal
  409. ;;;; variables.
  410. ;;;;
  411. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  412. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  413.  
  414. (defvar scroll-in-place t
  415.   "*When this variable is true (i.e., non-nil), the standard GNU Emacs vertical
  416. scrolling commands scroll-down, scroll-up, and scroll-other-window will attempt
  417. to keep point at its current position in the window (window line and column).
  418. In other words, point stays \"in place\" within the window.
  419.  
  420. When this variable is nil, the standard GNU Emacs vertical scrolling commands
  421. behave as usual.  The \"in place\" equivalents, however, are still available as
  422. separate commands.
  423.  
  424. This variable may be made buffer-local in order to disable (or enable) \"in
  425. place\" scrolling in particular buffers."
  426.   ;; I have thought about dividing scroll-in-place into three variables: a list
  427.   ;; of commands that always scroll in place, a list of commands that never
  428.   ;; scroll in place, and a flag that determines the default behavior of other
  429.   ;; scrolling commands.  This could make it easier to make "in place"
  430.   ;; scrolling the default because one could single out certain ill-behaved
  431.   ;; commands.  But as of now I'm sure that the added complexity would really
  432.   ;; be worth it.
  433.   )
  434.  
  435. (defvar scroll-allow-blank-lines-past-eob nil
  436.   "*When this variable is nil, the \"in place\" scrolling commands will avoid
  437. displaying empty lines past the end of the buffer text.  In other words, just
  438. as you can't see \"dead space\" before the beginning of the buffer text, the
  439. \"in place\" scrolling commands try to avoid displaying \"dead space\" past the
  440. end of the buffer text.  This helps make the most of window real estate.
  441.  
  442. Note that sometimes it is necessary to display \"dead space\" in order to make
  443. a previous scrolling action reversible.
  444.  
  445. When this variable is non-nil, the \"in place\" scrolling commands will always
  446. allow blank lines to be shown past the end of the buffer.")
  447.  
  448. ;;;;
  449. ;;;; The following variables are not user options, but are intended to be set
  450. ;;;; by code outside this package.
  451. ;;;;
  452.  
  453. (defvar scroll-default-lines nil
  454.   "The default number of lines to be scrolled by when a new sequence of \"in
  455. place\" scrolling commands begins.  Of course, when an explicit number of lines
  456. is specified, that explicit number takes precedence.  See the documentation for
  457. function scroll-window-in-place for more information.
  458.  
  459. If this variable is not bound to a number, then the default number of lines is
  460. the height of the window to be scrolled minus next-screen-context-lines.
  461.  
  462. This variable should not be set globally!  Commands that want to specify a
  463. default scrolling distance should just bind scroll-default-lines temporarily.")
  464.  
  465. (defvar scroll-command-groups nil
  466.   "The explicitly specified \"groups\" of \"in place\" scrolling commands.
  467. This variable should be set before or immediately after the \"in place\"
  468. scrolling package is loaded, and then not changed after that.
  469.  
  470. Usually, \"in place\" scrolling commands share state (e.g., the number of lines
  471. to scroll by) with any and all immediately previous \"in place\" scrolling
  472. commands.  Sometimes, however, this is undesirable.  In these cases the \"in
  473. place\" scrolling commands can be divided into groups.  A command in a group
  474. only shares state with members of its group.
  475.  
  476. Each element of scroll-command-groups is a list that contains all of the
  477. members of a unique command group.  For example, if there were only one
  478. explicit group and that group contained the commands `scroll-down-one-line' and
  479. `scroll-up-one-line', then scroll-command-groups would be set to:
  480.  
  481.   ((scroll-down-one-line scroll-up-one-line))
  482.  
  483. Commands that are not in any explicitly specified group are added to a default
  484. group.  That group is stored in the variable scroll-default-command-group.
  485.  
  486. The \"in place\" scrolling functions assume that all of the scrolling command
  487. groups are nonintersecting (i.e., no command is in more than one group) and
  488. only contain \"in place\" scrolling commands.")
  489.  
  490. ;;;;
  491. ;;;; The variables below this point are internal to this package.
  492. ;;;;
  493.  
  494. (defvar scroll-default-command-group nil
  495.   "The set of \"in place\" scrolling commands that are not members of any
  496. explicitly defined group of commands.  This set of commands is an implicitly
  497. defined group, constructed as \"in place\" commands are invoked, and members of
  498. this group share state among themselves.  See the documentation for variable
  499. scroll-command-groups for more information.")
  500.  
  501. (defvar scroll-initially-displayed-lines 0
  502.   "The number of window lines that contained buffer text when the current
  503. sequence of \"in place\" scrolling commands started.  Unless the variable
  504. scroll-in-place-allow-blank-lines-past-eob is true, the \"in place\" scrolling
  505. commands ensure that at least this many text lines are visible at all times.")
  506.  
  507. (defvar scroll-previous-window nil
  508.   "The window that was most recently scrolled by an \"in place\" scrolling
  509. command.")
  510.  
  511. (defvar scroll-previous-lines 0
  512.   "The number of window lines that the previous \"in place\" scrolling command
  513. attempted to scroll.")
  514.  
  515. (defvar scroll-goal-column 0
  516.   "The desired horizontal window position for point, used by the \"in place\"
  517. scrolling commands.")
  518.  
  519. (defvar scroll-boundary-previous-point nil
  520.   "The value of point before point was moved to a buffer boundary.")
  521.  
  522. (defvar scroll-boundary-previous-lines 0
  523.   "The number of lines that point moved when it moved to a buffer boundary.")
  524.  
  525. (defvar scroll-boundary-error-command nil
  526.   "The value of this-command when an \"in place\" scrolling command signalled a
  527. buffer boundary error.  This is used to decide how subsequent scrolling
  528. commands should recover from the error.")
  529.  
  530. (defvar scroll-boundary-error-point nil
  531.   "The value of point when an \"in place\" scrolling command signalled a buffer
  532. boundary error.  This is used to decide how subsequent scrolling commands
  533. should recover from the error."
  534.   ;; This variable is used as a flag, indicating whether or not the previous
  535.   ;; "in place" scrolling command signalled an error.
  536.   )
  537.  
  538. (defvar scroll-window-debt 0
  539.   "The difference between the number of lines an \"in place\" scrolling command
  540. tried to scroll a window and the number of lines that the window actually
  541. scrolled.  This difference is the \"debt\" in the window's starting position.
  542. Subsequent \"in place\" scrolling commands try to make up this debt.")
  543.  
  544. (defconst scroll-pos-visible-bug-p
  545.   ;; As of this writing, the most recent version of Epoch is 4.2.  This test
  546.   ;; will probably have to be rewritten when a newer version of Epoch becomes
  547.   ;; available.
  548.   (and (boundp 'epoch::version)
  549.        (let ((old-match-data (match-data)))
  550.      (unwind-protect
  551.          (if (string-match "\\`4\\." emacs-version) t nil)
  552.        (store-match-data old-match-data))))
  553.   "A flag, set when this version of GNU Emacs has a buggy version of the
  554. function pos-visible-in-window-p that returns nil when given (point-max) and
  555. \(point-max) is on the last line of the window.  Currently, this flag is set
  556. for all versions of Epoch 4.")
  557.  
  558.  
  559. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  560. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  561. ;;;;
  562. ;;;; Here are the "in place" scrolling commands (interactive functions) and the
  563. ;;;; replacements for the standard GNU Emacs vertical scrolling commands.
  564. ;;;;
  565. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  566. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  567.  
  568. ;;;;
  569. ;;;; But first, an auxiliary function.
  570. ;;;;
  571.  
  572. (defun scroll-choose-window ()
  573.   "Choose the window to be scrolled by the commands scroll-down, scroll-up,
  574. scroll-down-in-place, and scroll-up-in-place.
  575.  
  576. The rules are simple.  If the selected window is not the minibuffer window,
  577. then scroll the selected window.  When the minibuffer window is selected,
  578. either scroll the minibuffer-scroll-window (if it exists) or scroll the next
  579. window (otherwise).  The minibuffer-scroll-window is usually the window that
  580. displays completions."
  581.   (let ((selected-window (selected-window)))
  582.     (if (eq selected-window (minibuffer-window))
  583.     (if (and minibuffer-scroll-window
  584.          ;; window-point is nil if the window has been deleted.
  585.          (window-point minibuffer-scroll-window))
  586.         minibuffer-scroll-window
  587.       (next-window selected-window))
  588.       selected-window)))
  589.  
  590. ;;;;
  591. ;;;; Here are the new scroll "in place" commands.
  592. ;;;;
  593.  
  594. (defun scroll-down-in-place (&optional lines)
  595.   "Scroll the text of the current window downward by LINES lines, leaving point
  596. as close as possible to its current window position (window line and column).
  597. In other words, point is left \"in place\" within the window.  As a special
  598. case, when the current window is the minibuffer window, this command scrolls
  599. the minibuffer-scroll-window (which is usually the list of completions) if it
  600. exists, or otherwise the next window in the canonical ordering of windows.
  601.  
  602. If LINES is nil, scroll the window by the same amount it was moved by the
  603. immediately previous \"in place\" scrolling command, or by scroll-default-lines
  604. \(usually almost a windowful) if the previous command was not an \"in place\"
  605. scrolling command (or when that previous command scrolled some other window, or
  606. when other circumstances prevent the previous scrolling distance from being
  607. used).  If LINES is the symbol -, then the scrolling distance is determined as
  608. if LINES had been nil, and then that distance is multiplied by -1.
  609.  
  610. If the window cannot be scrolled by the full distance, point is allowed to
  611. stray from its initial position so that it can move the full number of lines.
  612. If point cannot move the full number of lines, point is moved to the buffer
  613. boundary.  Any immediately subsequent \"in place\" scrolling commands will try
  614. to restore point to its initial window position."
  615.   (interactive "P")
  616.   (scroll-window-in-place (scroll-choose-window) lines -1))
  617.  
  618. ;;;
  619. ;;;
  620. ;;;
  621.  
  622. (defun scroll-up-in-place (&optional lines)
  623.   "Scroll the text of the current window upward by LINES lines, leaving point
  624. as close as possible to its current window position (window line and column).
  625. In other words, point is left \"in place\" within the window.  As a special
  626. case, when the current window is the minibuffer window, this command scrolls
  627. the minibuffer-scroll-window (which is usually the list of completions) if it
  628. exists, or otherwise the next window in the canonical ordering of windows.
  629.  
  630. If LINES is nil, scroll the window by the same amount it was moved by the
  631. immediately previous \"in place\" scrolling command, or by scroll-default-lines
  632. \(usually almost a windowful) if the previous command was not an \"in place\"
  633. scrolling command (or when that previous command scrolled some other window, or
  634. when other circumstances prevent the previous scrolling distance from being
  635. used).  If LINES is the symbol -, then the scrolling distance is determined as
  636. if LINES had been nil, and then that distance is multiplied by -1.
  637.  
  638. If the window cannot be scrolled by the full distance, point is allowed to
  639. stray from its initial position so that it can move the full number of lines.
  640. If point cannot move the full number of lines, point is moved to the buffer
  641. boundary.  Any immediately subsequent \"in place\" scrolling commands will try
  642. to restore point to its initial window position."
  643.   (interactive "P")
  644.   (scroll-window-in-place (scroll-choose-window) lines 1))
  645.  
  646. ;;;
  647. ;;;
  648. ;;;
  649.  
  650. (defun scroll-other-window-in-place (&optional lines)
  651.   "Scroll the text of the next window upward by LINES lines, leaving point in
  652. that window as close as possible to its current window position (window line
  653. and column).  In other words, point is left \"in place\" within the window.
  654. The next window is the one below the current one, or the one at the top of the
  655. screen if the current window is at the bottom of the screen.
  656.  
  657. If LINES is nil, scroll the window by the same amount it was moved by the
  658. immediately previous \"in place\" scrolling command, or by scroll-default-lines
  659. \(usually almost a windowful) if the previous command was not an \"in place\"
  660. scrolling command (or when that previous command scrolled some other window, or
  661. when other circumstances prevent the previous scrolling distance from being
  662. used).  If LINES is the symbol -, then the scrolling distance is determined as
  663. if LINES had been nil, and then that distance is multiplied by -1.
  664.  
  665. If the window cannot be scrolled by the full distance, point is allowed to
  666. stray from its initial position so that it can move the full number of lines.
  667. If point cannot move the full number of lines, point is moved to the buffer
  668. boundary.  Any immediately subsequent \"in place\" scrolling commands will try
  669. to restore point to its initial window position.
  670.  
  671. If it is impossible to scroll the text of the window at all (because a buffer
  672. boundary is already visible), this command signals a buffer boundary error.
  673. The error is signalled even if point could otherwise move the full number of
  674. lines."
  675.   (interactive "P")
  676.   (let* ((selected-window (selected-window))
  677.      (other-window (if (and (eq selected-window (minibuffer-window))
  678.                 minibuffer-scroll-window
  679.                 ;; window-point is nil if the window has been
  680.                 ;; deleted.
  681.                 (window-point minibuffer-scroll-window))
  682.                minibuffer-scroll-window
  683.              (next-window selected-window))))
  684.     (if (eq selected-window other-window)
  685.     (error "There is no other window."))
  686.     (scroll-window-in-place other-window lines 1)))
  687.  
  688. ;;;;
  689. ;;;; Here are the replacements for GNU Emacs' standard vertical scrolling
  690. ;;;; commands.
  691. ;;;;
  692.  
  693. (or (fboundp 'original-scroll-down)
  694.     (fset 'original-scroll-down (symbol-function 'scroll-down)))
  695. (or (fboundp 'original-scroll-up)
  696.     (fset 'original-scroll-up (symbol-function 'scroll-up)))
  697. (or (fboundp 'original-scroll-other-window)
  698.     (fset 'original-scroll-other-window (symbol-function 'scroll-other-window))
  699.     )
  700.  
  701. ;;;
  702. ;;;
  703. ;;;
  704.  
  705. (defun scroll-down (&optional lines)
  706.   "Scroll the text of the current window downward by LINES lines.  As a special
  707. case, when the current window is the minibuffer window, this command scrolls
  708. the minibuffer-scroll-window (which is usually the list of completions) if it
  709. exists, or otherwise the next window in the canonical ordering of windows.
  710.  
  711. When the variable scroll-in-place is true, this command works just like the
  712. command scroll-down-in-place, scrolling the current window and leaving point
  713. \"in place\" within the window.  See the documentation for scroll-down-in-place
  714. for more information.
  715.  
  716. When the variable scroll-in-place is nil, this command invokes the standard GNU
  717. Emacs version of scroll-down.  In that case, when LINES is nil the current
  718. window is scrolled by nearly a complete windowful of text.
  719.  
  720. Note that this command correctly handles cases in which scroll-in-place has a
  721. buffer-local value in the window to be scrolled.  That value is honored."
  722.   (interactive "P")
  723.   (scroll-window (scroll-choose-window) lines -1))
  724.  
  725. ;;;
  726. ;;;
  727. ;;;
  728.  
  729. (defun scroll-up (&optional lines)
  730.   "Scroll the text of the current window upward by LINES lines.  As a special
  731. case, when the current window is the minibuffer window, this command scrolls
  732. the minibuffer-scroll-window (which is usually the list of completions) if it
  733. exists, or otherwise the next window in the canonical ordering of windows.
  734.  
  735. When the variable scroll-in-place is true, this command works just like the
  736. command scroll-up-in-place, scrolling the current window and leaving point \"in
  737. place\" within the window.  See the documentation for scroll-up-in-place for
  738. more information.
  739.  
  740. When the variable scroll-in-place is nil, this command invokes the standard GNU
  741. Emacs version of scroll-up.  In that case, when LINES is nil the current window
  742. is scrolled by nearly a complete windowful of text.
  743.  
  744. Note that this command correctly handles cases in which scroll-in-place has a
  745. buffer-local value in the window to be scrolled.  That value is honored."
  746.   (interactive "P")
  747.   (scroll-window (scroll-choose-window) lines 1))
  748.  
  749. ;;;
  750. ;;;
  751. ;;;
  752.  
  753. (defun scroll-other-window (&optional lines)
  754.   "Scroll the text of the next window upward by LINES lines.  The next window
  755. is the one below the current one, or the one at the top of the screen if the
  756. current window is at the bottom of the screen.
  757.  
  758. When the variable scroll-in-place is true, this command invokes the command
  759. scroll-other-window-in-place to scroll the next window and leave point \"in
  760. place\" within that window.  See the documentation for scroll-other-window-in-
  761. place for more information.
  762.  
  763. When the variable scroll-in-place is nil, this command invokes the standard GNU
  764. Emacs version of scroll-other-window.  In that case, when LINES is nil the next
  765. window is scrolled by nearly a complete windowful of text.
  766.  
  767. Note that this command correctly handles cases in which scroll-in-place has a
  768. buffer-local value in the window to be scrolled.  That value is honored."
  769.   (interactive "P")
  770.   ;; This code is similar to the body of scroll-window, below.
  771.   (let* ((selected-window (selected-window))
  772.      (other-window (if (and (eq selected-window (minibuffer-window))
  773.                 minibuffer-scroll-window
  774.                 ;; window-point is nil if the window has been
  775.                 ;; deleted.
  776.                 (window-point minibuffer-scroll-window))
  777.                minibuffer-scroll-window
  778.              (next-window selected-window)))
  779.      (other-window-buffer (window-buffer other-window)))
  780.     (if (eq selected-window other-window)
  781.     (error "There is no other window."))
  782.     (if ;; Allow scroll-in-place to be a buffer-local variable.
  783.     (save-excursion (set-buffer other-window-buffer) scroll-in-place)
  784.     (scroll-window-in-place other-window lines 1)
  785.       
  786.       ;; Paranoid, we forcibly break any running sequence of "in place"
  787.       ;; scrolling commands.
  788.       (setq scroll-previous-window nil)
  789.       (original-scroll-other-window lines))
  790.     ))
  791.  
  792.  
  793. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  794. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  795. ;;;;
  796. ;;;; Here are the new functions scroll-window-in-place, scroll-window, and
  797. ;;;; scroll-window-in-place-continue-sequence.  These functions are intended to
  798. ;;;; be available to programs outside this package.
  799. ;;;;
  800. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  801. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  802.  
  803. (defun scroll-window-in-place (window lines direction)
  804.   "Scroll WINDOW vertically by the given number of window LINES in the given
  805. DIRECTION, leaving the window's point as close as possible to its original
  806. window position (window line and column).  In other words, the window's point
  807. is left \"in place\" within the window.
  808.  
  809. Note that the window to be scrolled does not have to be the selected window,
  810. and that this function does not change which window is selected.
  811.  
  812. LINES specifies the number of window lines to scroll and is interpreted as if
  813. it were a raw prefix argument.  If LINES is nil, the window is scrolled by the
  814. amount it was moved by the immediately previous \"in place\" scrolling command,
  815. or by scroll-default-lines (by default, almost a windowful) if the previous
  816. command was not an \"in place\" scrolling command (or when WINDOW is not the
  817. previously scrolled window, or when this-command and the previous scrolling
  818. command are not in the same group of scrolling commands (see the documentation
  819. for variable scroll-command-groups), or when other circumstances prevent the
  820. previous scrolling distance from being used).  If LINES is the symbol -, then
  821. the scrolling distance is determined as if LINES had been nil, and then that
  822. distance is multiplied by -1.
  823.  
  824. DIRECTION determines the direction of the scrolling motion.  The values -1 and
  825. 'down indicate downward motion; the values 1 and 'up indicate upward motion.
  826. Any other value causes an error.
  827.  
  828. If the window cannot be scrolled by the full distance (because the window hits
  829. the boundary of its buffer), the window's point is allowed to stray from its
  830. initial position so that it can move the full number of lines.  If point cannot
  831. move the full number of lines, point is moved to the buffer boundary (unless it
  832. was already there, in which case a buffer boundary error is signalled instead).
  833. Any immediately subsequent \"in place\" scrolling commands will try to restore
  834. point to its initial window position.
  835.  
  836. Unless the variable scroll-allow-blank-lines-past-eob is true, this function
  837. avoids displaying blank lines past the end of the buffer except as necessary to
  838. make a previous \"in place\" scrolling action reversible.  Effectively, this
  839. means that this function will not display any more past-end-of-buffer blank
  840. lines than were visible when the current sequence of \"in place\" scrolling
  841. commands started.  When the variable scroll-allow-blank-lines-past-eob is true,
  842. this function will display as many blank lines as is necessary to keep point
  843. \"in place\" in the window.
  844.  
  845. Note that if WINDOW is not the selected window and it is impossible to scroll
  846. the text of WINDOW at all (because a buffer boundary is already visible), then
  847. this function signals a buffer boundary error.  The error is signalled even if
  848. point could otherwise move the full number of lines."
  849.   (let* (;; Make sure that the user doesn't quit in the middle and leave us
  850.      ;; with our variables out of sync.
  851.      (inhibit-quit t)
  852.      (original-window (selected-window))
  853.      (original-buffer (current-buffer))
  854.      (window-height (- (window-height window)
  855.                (if (eq window (minibuffer-window)) 0 1)))
  856.      (this-command-group (scroll-get-command-group this-command))
  857.      (continue-scroll-p
  858.       (and ;; We're scrolling the previously scrolled window...
  859.            (windowp scroll-previous-window)
  860.            (eq window scroll-previous-window)
  861.            ;; ...and the last command was an "in place" scrolling command
  862.            ;; that can be continued by this command.
  863.            (if (eq last-command t)
  864.            ;; If the previous command signalled an error, last-command
  865.            ;; is t.  Try to see if we signalled the error and if point
  866.            ;; is where we left it.
  867.            (and    scroll-boundary-error-point
  868.             (eq (window-point window) scroll-boundary-error-point)
  869.             (memq scroll-boundary-error-command this-command-group)
  870.             )
  871.          ;; Otherwise...
  872.          (memq last-command this-command-group))
  873.            ))
  874.      (lines-value (prefix-numeric-value lines))
  875.      )
  876.     
  877.     ;; Parse the direction into a unit distance (1 or -1).
  878.     (setq direction (scroll-parse-direction direction))
  879.     
  880.     (setq scroll-previous-window window
  881.       ;; scroll-boundary-error-command nil not necessary.
  882.       scroll-boundary-error-point nil)
  883.     (unwind-protect
  884.     (progn
  885.       ;; select-window does an implicit set-buffer.
  886.       (select-window window)
  887.       
  888.       (if (or ;; The current command is not a continuation of a running
  889.           ;; sequence of "in place" scrolling commands...
  890.           (not continue-scroll-p)
  891.           ;; ...or we were given an explicit number of lines to scroll,
  892.           ;; and that number has a different magnitude than the last
  893.           ;; number of lines we scrolled...
  894.               (and (or (numberp lines) (consp lines))
  895.                (/= scroll-previous-lines lines-value)
  896.                (/= scroll-previous-lines (- lines-value)))
  897.           ;; ...or the last successful scrolling command moved to a
  898.           ;; buffer boundary, but the buffer is no longer in the state
  899.           ;; we left it.  (This can occur if, for example, we signal an
  900.           ;; end-of-buffer error and something catches it and moves
  901.           ;; point or renarrows.  VM, for example, does this.)
  902.           (and scroll-boundary-previous-point
  903.                (or (not (or (bobp) (eobp)))
  904.                (< scroll-boundary-previous-point (point-min))
  905.                (> scroll-boundary-previous-point (point-max))
  906.                (eq scroll-boundary-previous-point (point)))))
  907.           
  908.           ;; We're starting a new sequence of scrolling commands.
  909.           (setq lines (if (or (numberp lines) (consp lines))
  910.                   lines-value
  911.                 ;; The default number of lines...
  912.                 (* (if (eq lines '-) -1 1)
  913.                    (if (numberp scroll-default-lines)
  914.                    scroll-default-lines
  915.                  (max (- window-height
  916.                      next-screen-context-lines)
  917.                       1))))
  918.             scroll-previous-lines lines
  919.             scroll-goal-column (scroll-determine-goal-column window)
  920.             scroll-boundary-previous-point nil
  921.             ;; scroll-boundary-previous-lines 0 not necessary.
  922.             scroll-window-debt 0
  923.             scroll-initially-displayed-lines
  924.             (if scroll-allow-blank-lines-past-eob
  925.             0
  926.               (save-excursion
  927.             (goto-char (window-start window))
  928.             (vertical-motion (1- window-height)))))
  929.         
  930.         ;; Otherwise we want to scroll by the same number of lines (but
  931.         ;; possibly in a different direction) that we scrolled in previous
  932.         ;; invocations of this function.
  933.         (cond ((null lines)
  934.            (setq lines scroll-previous-lines))
  935.           ((eq lines '-)
  936.            (setq lines (- scroll-previous-lines)
  937.              scroll-previous-lines lines))
  938.           (t
  939.            (setq lines lines-value
  940.              scroll-previous-lines lines)))
  941.         )
  942.       
  943.       (setq lines (* direction lines))
  944.       
  945.       ;; If point is not in the window, center window around point.  We try
  946.       ;; to account for a bug in pos-visible-in-window-p in some versions
  947.       ;; of Emacs (namely, Epoch 4.x).
  948.       (save-excursion
  949.         (if (pos-visible-in-window-p (let ((point (point)))
  950.                        (if (and scroll-pos-visible-bug-p
  951.                             (= point (point-max)))
  952.                            (max (1- point) (point-min))
  953.                          point))
  954.                      window)
  955.         nil
  956.           (vertical-motion (/ (- window-height) 2))
  957.           (set-window-start window (point))))
  958.       
  959.       (cond ((and scroll-boundary-previous-point
  960.               ;; lines is the same sign as the direction from point to
  961.               ;; the scroll-boundary-previous-point.
  962.               (cond ((> lines 0)
  963.                  (> (- scroll-boundary-previous-point (point)) 0))
  964.                 ((< lines 0)
  965.                  (< (- scroll-boundary-previous-point (point)) 0))
  966.                 (t nil)))
  967.          ;; We're moving away from the buffer boundary.
  968.          (goto-char scroll-boundary-previous-point)
  969.          ;; Always move here (i.e., don't reject cases in which the
  970.          ;; window doesn't move).
  971.          (scroll-set-window-start window
  972.                       (- scroll-boundary-previous-lines))
  973.          ;; (message "Back, window debt is %s." scroll-window-debt)
  974.          (setq scroll-boundary-previous-point nil))
  975.  
  976.         ((= lines 0)
  977.          ;; We're going nowhere, so save ourselves some work.
  978.          ;; (message "Scrolled zero lines.")
  979.          )
  980.         
  981.         (t
  982.          ;; Perform the scrolling motion.
  983.          (let ((initial-point (point))
  984.                (moved nil))
  985.            ;; First move point and see how far it goes.
  986.            (setq moved (vertical-motion lines))
  987.            (if (= moved lines)
  988.                (progn
  989.              ;; Point moved the full distance.  Move to the desired
  990.              ;; column and then try to move the window the full
  991.              ;; distance, too.
  992.              (move-to-column (+ (current-column)
  993.                         scroll-goal-column))
  994.              (or (scroll-set-window-start window moved
  995.                               original-window)
  996.                  (scroll-signal-boundary-error initial-point
  997.                                lines))
  998.              ;; (message "Normal, window debt is %s."
  999.              ;;          scroll-window-debt)
  1000.              )
  1001.              ;; Point couldn't move all the way.  Move to the buffer
  1002.              ;; boundary if we're not already there, or signal a buffer
  1003.              ;; boundary error otherwise.
  1004.              (let ((boundary-point (if (< lines 0)
  1005.                            (point-min)
  1006.                          (point-max)))
  1007.                (boundary-symbol (if (< lines 0)
  1008.                         'beginning-of-buffer
  1009.                           'end-of-buffer)))
  1010.                (if (= initial-point boundary-point)
  1011.                (scroll-signal-boundary-error initial-point lines)
  1012.              ;; Scroll the window by as many lines as point could
  1013.              ;; move.
  1014.              (or (scroll-set-window-start window moved
  1015.                               original-window)
  1016.                  (scroll-signal-boundary-error initial-point
  1017.                                lines))
  1018.              (message "%s" (get boundary-symbol 'error-message))
  1019.              ;; (message "Boundary, window debt is %s."
  1020.              ;;          scroll-window-debt)
  1021.              (setq scroll-boundary-previous-lines moved)
  1022.              (setq scroll-boundary-previous-point initial-point)
  1023.              (goto-char boundary-point))
  1024.                )))
  1025.          )))
  1026.       
  1027.       ;; The unwind forms of the unwind-protect, above.  Restore the originally
  1028.       ;; selected window and current buffer.
  1029.       (select-window original-window)
  1030.       (set-buffer original-buffer)))
  1031.   
  1032.   ;; The standard GNU Emacs scrolling commands return nil, so we do, too.
  1033.   nil)
  1034.  
  1035. ;;;
  1036. ;;;
  1037. ;;;
  1038.  
  1039. (defun scroll-window (window lines direction)
  1040.   "Scroll WINDOW vertically by the given number of window LINES in the given
  1041. DIRECTION.  Note that the window to be scrolled does not have to be the
  1042. selected window, and that this function does not change which window is
  1043. selected.
  1044.  
  1045. When the variable scroll-in-place is true, this function simply invokes the
  1046. function scroll-window-in-place to scroll the window and leave point \"in
  1047. place\" within that window.  See the documentation for scroll-window-in-place
  1048. for more information.
  1049.  
  1050. When the variable scroll-in-place is nil, this function invokes the original
  1051. version of the standard GNU Emacs command scroll-down or scroll-up, as
  1052. determined by DIRECTION, to scroll the window.  If DIRECTION is -1 or 'down,
  1053. the original scroll-down is called; if DIRECTION is 1 or 'up, the original
  1054. scroll-up is called.  Any other DIRECTION is an error.  LINES is interpreted as
  1055. if it were a raw prefix argument.  If LINES is nil, the window is scrolled by
  1056. almost a complete windowful.  If LINES is the symbol -, the window is scrolled
  1057. by almost a complete windowful in the opposite direction.
  1058.  
  1059. Note that this function correctly handles cases in which scroll-in-place has a
  1060. buffer-local value in the WINDOW's buffer.  That value is honored."
  1061.   (let ((current-buffer (current-buffer))
  1062.     (selected-window (selected-window))
  1063.     (window-buffer (window-buffer window)))
  1064.     (if ;; Allow scroll-in-place to be a buffer-local variable.
  1065.     (if (eq current-buffer window-buffer)
  1066.         scroll-in-place
  1067.       (save-excursion (set-buffer window-buffer) scroll-in-place))
  1068.     (scroll-window-in-place window lines direction)
  1069.       
  1070.       (unwind-protect
  1071.       (progn
  1072.         ;; Paranoid, we forcibly break any running sequence of "in place"
  1073.         ;; scrolling commands.
  1074.         (setq scroll-previous-window nil)
  1075.         (select-window window)
  1076.         (if (= (scroll-parse-direction direction) 1)
  1077.         (original-scroll-up lines)
  1078.           (original-scroll-down lines)))
  1079.     (select-window selected-window)
  1080.     (set-buffer current-buffer))
  1081.       )))
  1082.  
  1083. ;;;
  1084. ;;; The following function is sometimes useful.  For example, I call it from
  1085. ;;; functions that are invoked by certain mouse button down events in order to
  1086. ;;; preserve any running chain of "in place" scrolling commands.  This lets me
  1087. ;;; continue the sequence from my mouse button up functions.
  1088. ;;;
  1089. ;;; I haven't yet needed a function to purposely break a running sequence of
  1090. ;;; "in place" scrolling commands.  Such a function would be easy to write,
  1091. ;;; however; just set scroll-previous-window to nil.
  1092. ;;;
  1093.  
  1094. (defun scroll-window-in-place-continue-sequence ()
  1095.   "If the previous command was a \"scroll in place\" command, set this-command
  1096. to the name of that previous command.  This ensures that any running sequence
  1097. of \"in place\" scrolling commands will not be broken by the current command.
  1098. See the documentation for the commands scroll-down-in-place and scroll-down-in-
  1099. place for more information about \"in place\" scrolling.
  1100.  
  1101. NOTE that you don't need to call this function if the current command scrolls
  1102. in place!  You only need to call this function when the current command is not
  1103. a \"scroll in place\" command but you still want to preserve any running
  1104. sequence of \"in place\" commands.  Such situations are rare.
  1105.  
  1106. NOTE that this function sets this-command in order to trick the \"in place\"
  1107. scrolling commands.  If something else subsequently sets this-command, any
  1108. running sequence of scrolling commands will probably be broken anyway."
  1109.   (if (if (eq last-command t)
  1110.       ;; If last-command is t, then the previous command signalled an
  1111.       ;; error.  See if the last invocation of scroll-window-in-place
  1112.       ;; signalled an error.
  1113.       scroll-boundary-error-point
  1114.     ;; Otherwise, last-command must belong to some group of "in place"
  1115.     ;; scrolling commands.
  1116.     (or (memq last-command scroll-default-command-group)
  1117.         (let ((groups scroll-command-groups)
  1118.           (found nil))
  1119.           (while (and groups (not found))
  1120.         (if (memq last-command (car groups))
  1121.             (setq found t)
  1122.           (setq groups (cdr groups)))
  1123.         )
  1124.           found)))
  1125.       (setq this-command last-command)))
  1126.  
  1127.  
  1128. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1129. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1130. ;;;;
  1131. ;;;; Here are the various auxiliary functions called by scroll-window-in-place.
  1132. ;;;; None of the functions are intended to be called from outside this package.
  1133. ;;;;
  1134. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1135. ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
  1136.  
  1137. (defun scroll-get-command-group (command)
  1138.   "Return the group of \"in place\" scrolling commands that contains the given
  1139. COMMAND.  This is the list of commands with which the given command may share
  1140. state and form \"chains.\"
  1141.  
  1142. This function is an auxiliary for the function scroll-window-in-place.  Don't
  1143. call this function from other code."
  1144.   ;; This function assumes that the given command is an "in place" scrolling
  1145.   ;; command.
  1146.   (let ((groups scroll-command-groups)
  1147.     (found nil))
  1148.     (while (and groups (not found))
  1149.       (if (memq command (car groups))
  1150.       (setq found t)
  1151.     (setq groups (cdr groups)))
  1152.       )
  1153.     (if groups
  1154.     (car groups)
  1155.       ;; Otherwise return the default command group.  If necessary, add the
  1156.       ;; given command to the default command group.
  1157.       (or (memq command scroll-default-command-group)
  1158.       (setq scroll-default-command-group
  1159.         (cons command scroll-default-command-group)))
  1160.       scroll-default-command-group)
  1161.     ))
  1162.  
  1163. ;;;
  1164. ;;;
  1165. ;;;
  1166.  
  1167. (defun scroll-parse-direction (direction)
  1168.   "Return the signed unit distance for the given DIRECTION.  If DIRECTION is
  1169. unacceptable, signal an error."
  1170.   (cond ((or (eq direction 1) (eq direction -1)) direction)
  1171.     ((eq direction 'up) 1)
  1172.     ((eq direction 'down) -1)
  1173.     (t (signal 'args-out-of-range (list 'direction direction)))
  1174.     ))
  1175.  
  1176. ;;;
  1177. ;;;
  1178. ;;;
  1179.  
  1180. (defun scroll-determine-goal-column (window)
  1181.   "Return the goal column for the \"in place\" vertical scrolling commands.
  1182. This is the horizontal window position at which these commands try to keep
  1183. point.
  1184.  
  1185. This function is an auxiliary for the function scroll-window-in-place.  Don't
  1186. call this function from other code."
  1187.   ;; NOTE that window must be the selected window!  scroll-window-in-place
  1188.   ;; ensures that this is so.
  1189.   (cond ((or truncate-lines
  1190.          (and truncate-partial-width-windows
  1191.           (< (window-width window) (screen-width)))
  1192.          (> (window-hscroll window) 0))
  1193.      ;; Lines in this window are being truncated.
  1194.      (if (and track-eol (eolp))
  1195.          9999
  1196.        (current-column)))
  1197.     ((and track-eol (eolp))
  1198.      ;; In some ways this isn't quite right, as point doesn't track the
  1199.      ;; ends of wrapped lines.  But if it did so, point would be on the
  1200.      ;; wrong window line.  This is the best we can do.
  1201.      (1- (window-width window)))
  1202.     (t (% (current-column) (1- (window-width window))))
  1203.     ))
  1204.  
  1205. ;;;
  1206. ;;;
  1207. ;;;
  1208.  
  1209. (defun scroll-set-window-start (window lines &optional original-window)
  1210.   "Move the window-start of the given window, which must be the selected
  1211. window.  If the window was successfully scrolled, update the scroll-window-debt
  1212. and return t.  Otherwise return nil.
  1213.  
  1214. This function is an auxiliary for the function scroll-window-in-place.  Don't
  1215. call this function from other code."
  1216.   (save-excursion
  1217.     (goto-char (window-start window))
  1218.     ;; Try to move the window start by the specified number of lines.  In
  1219.     ;; addition, try to make up any existing debt in the window start's
  1220.     ;; position and make sure that we don't move too close to the end of the
  1221.     ;; buffer.
  1222.     (let ((moved (+ (vertical-motion (+ lines
  1223.                     scroll-window-debt
  1224.                     scroll-initially-displayed-lines))
  1225.             (vertical-motion (- scroll-initially-displayed-lines)))))
  1226.       ;; If we're not scrolling the original-window (i.e., the originally
  1227.       ;; selected window), punt if we didn't move the window start at all.
  1228.       (if (and original-window
  1229.            (not (eq window original-window))
  1230.            (= moved 0))
  1231.       nil
  1232.     ;; Otherwise update the window start and keep track of the debt in our
  1233.     ;; position.  Return t to indicate success.
  1234.     (set-window-start window (point))
  1235.     (setq scroll-window-debt (- (+ lines scroll-window-debt) moved))
  1236.     t))
  1237.     ))
  1238.  
  1239. ;;;
  1240. ;;;
  1241. ;;;
  1242.  
  1243. (defun scroll-signal-boundary-error (initial-point lines)
  1244.   "Move point to its initial location and signal an appropriate buffer boundary
  1245. error.  This function is an auxiliary for the function scroll-window-in-place.
  1246. Don't call this function from other code."
  1247.   (goto-char initial-point)
  1248.   ;; Remember what we were doing and where point was when we signalled the
  1249.   ;; error so that subsequent "in place" scrolling commands can decide how to
  1250.   ;; recover.
  1251.   (setq scroll-boundary-error-command this-command
  1252.     scroll-boundary-error-point initial-point)
  1253.   (signal (if (< lines 0) 'beginning-of-buffer 'end-of-buffer)
  1254.       nil))
  1255.  
  1256. ;; End of file.
  1257.