1 ;;; follow.el --- synchronize windows showing the same buffer
3 ;; Copyright (C) 1995-1997, 1999, 2001-2015 Free Software Foundation,
6 ;; Author: Anders Lindgren <andersl@andersl.com>
7 ;; Maintainer: emacs-devel@gnu.org (Anders' email bounces, Sep 2005)
9 ;; Keywords: display, window, minor-mode, convenience
11 ;; This file is part of GNU Emacs.
13 ;; GNU Emacs is free software: you can redistribute it and/or modify
14 ;; it under the terms of the GNU General Public License as published by
15 ;; the Free Software Foundation, either version 3 of the License, or
16 ;; (at your option) any later version.
18 ;; GNU Emacs is distributed in the hope that it will be useful,
19 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
20 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 ;; GNU General Public License for more details.
23 ;; You should have received a copy of the GNU General Public License
24 ;; along with GNU Emacs. If not, see <http://www.gnu.org/licenses/>.
28 ;; `Follow mode' is a minor mode for Emacs and XEmacs that
29 ;; combines windows into one tall virtual window.
31 ;; The feeling of a "virtual window" has been accomplished by the use
32 ;; of two major techniques:
34 ;; * The windows always display adjacent sections of the buffer.
35 ;; This means that whenever one window is moved, all the
36 ;; others will follow. (Hence the name Follow mode.)
38 ;; * Should point (cursor) end up outside a window, another
39 ;; window displaying that point is selected, if possible. This
40 ;; makes it possible to walk between windows using normal cursor
43 ;; Follow mode comes to its prime when a large screen and two
44 ;; side-by-side window are used. The user can, with the help of Follow
45 ;; mode, use two full-height windows as though they are one.
46 ;; Imagine yourself editing a large function, or section of text,
47 ;; and being able to use 144 lines instead of the normal 72... (your
50 ;; To test this package, make sure `follow' is loaded, or will be
51 ;; autoloaded when activated (see below). Then do the following:
53 ;; * Find your favorite file (preferably a long one).
55 ;; * Resize Emacs so that it will be wide enough for two full size
56 ;; columns. Delete the other windows and split the window with
57 ;; the commands `C-x 1 C-x 3'.
59 ;; * Give the command:
60 ;; M-x follow-mode <RETURN>
62 ;; * Now the display should look something like (assuming the text "71"
65 ;; +----------+----------+
72 ;; +----------+----------+
74 ;; As you can see, the right-hand window starts at line 73, the line
75 ;; immediately below the end of the left-hand window. As long as
76 ;; `follow-mode' is active, the two windows will follow each other!
78 ;; * Play around and enjoy! Scroll one window and watch the other.
79 ;; Jump to the beginning or end. Press `Cursor down' at the last
80 ;; line of the left-hand window. Enter new lines into the
81 ;; text. Enter long lines spanning several lines, or several
84 ;; * Should you find `Follow' mode annoying, just type
85 ;; M-x follow-mode <RETURN>
89 ;; The command `follow-delete-other-windows-and-split' maximizes the
90 ;; visible area of the current buffer.
92 ;; I recommend adding it, and `follow-mode', to hotkeys in the global
93 ;; key map. To do so, add the following lines (replacing `[f7]' and
94 ;; `[f8]' with your favorite keys) to the init file:
96 ;; (global-set-key [f8] 'follow-mode)
97 ;; (global-set-key [f7] 'follow-delete-other-windows-and-split)
100 ;; There exist two system variables that control the appearance of
101 ;; lines wider than the window containing them. The default is to
102 ;; truncate long lines whenever a window isn't as wide as the frame.
104 ;; To make sure lines are never truncated, please place the following
105 ;; lines in your init file:
107 ;; (setq truncate-lines nil)
108 ;; (setq truncate-partial-width-windows nil)
111 ;; The correct way to configure Follow mode, or any other mode for
112 ;; that matter, is to create one or more functions that do
113 ;; whatever you would like to do. These functions are then added to
116 ;; The keymap `follow-key-map' contains key bindings activated by
120 ;; (add-hook 'follow-mode-hook 'my-follow-mode-hook)
122 ;; (defun my-follow-mode-hook ()
123 ;; (define-key follow-mode-map "\C-ca" 'your-favorite-function)
124 ;; (define-key follow-mode-map "\C-cb" 'another-function))
129 ;; To activate, issue the command "M-x follow-mode"
130 ;; and press Return. To deactivate, do it again.
132 ;; The following is a list of commands useful when follow-mode is active.
134 ;; follow-scroll-up C-c . C-v
135 ;; Scroll text in a Follow mode window chain up.
137 ;; follow-scroll-down C-c . v
138 ;; Like `follow-scroll-up', but in the other direction.
140 ;; follow-delete-other-windows-and-split C-c . 1
141 ;; Maximize the visible area of the current buffer,
142 ;; and enter Follow mode. This is a very convenient
143 ;; way to start Follow mode, hence we recommend that
144 ;; this command be added to the global keymap.
146 ;; follow-recenter C-c . C-l
147 ;; Place point in the center of the middle window,
148 ;; or a specified number of lines from either top or bottom.
150 ;; follow-switch-to-buffer C-c . b
151 ;; Switch buffer in all windows displaying the current buffer
154 ;; follow-switch-to-buffer-all C-c . C-b
155 ;; Switch buffer in all windows in the selected frame.
157 ;; follow-switch-to-current-buffer-all
158 ;; Show the current buffer in all windows on the current
159 ;; frame and turn on `follow-mode'.
161 ;; follow-first-window C-c . <
162 ;; Select the first window in the frame showing the same buffer.
164 ;; follow-last-window C-c . >
165 ;; Select the last window in the frame showing the same buffer.
167 ;; follow-next-window C-c . n
168 ;; Select the next window in the frame showing the same buffer.
170 ;; follow-previous-window C-c . p
171 ;; Select the previous window showing the same buffer.
174 ;; Well, it seems ok, but what if I really want to look at two different
175 ;; positions in the text? Here are two simple methods to use:
177 ;; 1) Use multiple frames; `follow' mode only affects windows displayed
178 ;; in the same frame. (My apologies to you who can't use frames.)
180 ;; 2) Bind `follow-mode' to key so you can turn it off whenever
181 ;; you want to view two locations. Of course, `follow' mode can
182 ;; be reactivated by hitting the same key again.
184 ;; Example from my ~/.emacs:
185 ;; (global-set-key [f8] 'follow-mode)
189 ;; The main method by which Follow mode aligns windows is via the
190 ;; function `follow-post-command-hook', which is run after each
191 ;; command. This "fixes up" the alignment of other windows which are
192 ;; showing the same Follow mode buffer, on the same frame as the
193 ;; selected window. It does not try to deal with buffers other than
194 ;; the buffer of the selected frame, or windows on other frames.
196 ;; Comint mode specially calls `follow-comint-scroll-to-bottom' on
197 ;; Follow mode buffers. This function scrolls the bottom-most window
198 ;; in a window chain and aligns the other windows accordingly. Follow
199 ;; mode adds a function to `compilation-filter-hook' to align
200 ;; compilation buffers.
205 (eval-when-compile (require 'cl-lib
))
210 "Synchronize windows showing the same buffer."
214 (defcustom follow-mode-hook nil
215 "Normal hook run by `follow-mode'."
221 ;; Define keys for the follow-mode minor mode map and replace some
222 ;; functions in the global map. All Follow mode special functions can
223 ;; be found on the `C-c .' prefix key.
225 ;; To change the prefix, redefine `follow-mode-prefix' before `follow'
226 ;; is loaded, or see the section on `follow-mode-hook' above for an
227 ;; example of how to bind the keys the way you like.
229 (defcustom follow-mode-prefix
"\C-c."
230 "Prefix key to use for follow commands in Follow mode.
231 The value of this variable is checked as part of loading Follow mode.
232 After that, changing the prefix key requires manipulating keymaps."
236 (defvar follow-mode-map
237 (let ((mainmap (make-sparse-keymap))
238 (map (make-sparse-keymap)))
239 (define-key map
"\C-v" 'follow-scroll-up
)
240 (define-key map
"\M-v" 'follow-scroll-down
)
241 (define-key map
"v" 'follow-scroll-down
)
242 (define-key map
"1" 'follow-delete-other-windows-and-split
)
243 (define-key map
"b" 'follow-switch-to-buffer
)
244 (define-key map
"\C-b" 'follow-switch-to-buffer-all
)
245 (define-key map
"\C-l" 'follow-recenter
)
246 (define-key map
"<" 'follow-first-window
)
247 (define-key map
">" 'follow-last-window
)
248 (define-key map
"n" 'follow-next-window
)
249 (define-key map
"p" 'follow-previous-window
)
251 (define-key mainmap follow-mode-prefix map
)
253 ;; Replace the standard `end-of-buffer', when in Follow mode. (I
254 ;; don't see the point in trying to replace every function that
255 ;; could be enhanced in Follow mode. End-of-buffer is a special
256 ;; case since it is very simple to define and it greatly enhances
257 ;; the look and feel of Follow mode.)
258 (define-key mainmap
[remap end-of-buffer
] 'follow-end-of-buffer
)
260 (define-key mainmap
[remap scroll-bar-toolkit-scroll
] 'follow-scroll-bar-toolkit-scroll
)
261 (define-key mainmap
[remap scroll-bar-drag
] 'follow-scroll-bar-drag
)
262 (define-key mainmap
[remap scroll-bar-scroll-up
] 'follow-scroll-bar-scroll-up
)
263 (define-key mainmap
[remap scroll-bar-scroll-down
] 'follow-scroll-bar-scroll-down
)
264 (define-key mainmap
[remap mwheel-scroll
] 'follow-mwheel-scroll
)
267 "Minor mode keymap for Follow mode.")
269 ;; When the mode is not activated, only one item is visible to activate
271 (defun follow-menu-filter (menu)
272 (if (bound-and-true-p follow-mode
)
274 '(["Follow mode" follow-mode
275 :style toggle
:selected follow-mode
])))
277 (easy-menu-add-item nil
'("Tools")
279 :filter follow-menu-filter
280 ["Scroll Up" follow-scroll-up follow-mode
]
281 ["Scroll Down" follow-scroll-down follow-mode
]
283 ["Delete Other Windows and Split" follow-delete-other-windows-and-split follow-mode
]
285 ["Switch To Buffer" follow-switch-to-buffer follow-mode
]
286 ["Switch To Buffer (all windows)" follow-switch-to-buffer-all follow-mode
]
288 ["First Window" follow-first-window follow-mode
]
289 ["Last Window" follow-last-window follow-mode
]
290 ["Next Window" follow-next-window follow-mode
]
291 ["Previous Window" follow-previous-window follow-mode
]
293 ["Recenter" follow-recenter follow-mode
]
295 ["Follow mode" follow-mode
:style toggle
:selected follow-mode
]))
297 (defcustom follow-mode-line-text
" Follow"
298 "Text shown in the mode line when Follow mode is active.
299 Defaults to \" Follow\". Examples of other values
300 are \" Fw\", or simply \"\"."
304 (defcustom follow-auto nil
305 "Non-nil activates Follow mode whenever a file is loaded."
308 :set
(lambda (symbol value
)
310 (add-hook 'find-file-hook
'follow-find-file-hook t
)
311 (remove-hook 'find-file-hook
'follow-find-file-hook
))
312 (set-default symbol value
)))
314 (defvar follow-cache-command-list
315 '(next-line previous-line forward-char backward-char right-char left-char
)
316 "List of commands that don't require recalculation.
318 In order to be able to use the cache, a command should not change the
319 contents of the buffer, nor should it change selected window or current
322 The commands in this list are checked at load time.
324 To mark other commands as suitable for caching, set the symbol
325 property `follow-mode-use-cache' to non-nil.")
327 (defcustom follow-debug nil
328 "If non-nil, emit Follow mode debugging messages."
332 ;; Internal variables:
334 (defvar follow-internal-force-redisplay nil
335 "True when Follow mode should redisplay the windows.")
337 (defvar follow-active-menu nil
338 "The menu visible when Follow mode is active.")
340 (defvar follow-inactive-menu nil
341 "The menu visible when Follow mode is inactive.")
343 (defvar follow-inside-post-command-hook nil
344 "Non-nil when inside Follow modes `post-command-hook'.
345 Used by `follow-window-size-change'.")
347 (defvar follow-windows-start-end-cache nil
348 "Cache used by `follow-window-start-end'.")
350 (defvar follow-fixed-window nil
351 "If non-nil, the current window must not be scrolled.
352 This is typically set by explicit scrolling commands.")
355 ;; This inline function must be as small as possible!
356 ;; Maybe we should define a macro that expands to nil if
357 ;; the variable is not set.
359 (defsubst follow-debug-message
(&rest args
)
360 "Like `message', but only active when `follow-debug' is non-nil."
361 (if (and (boundp 'follow-debug
) follow-debug
)
362 (apply 'message args
)))
366 (dolist (cmd follow-cache-command-list
)
367 (put cmd
'follow-mode-use-cache t
))
372 (defun turn-on-follow-mode ()
373 "Turn on Follow mode. Please see the function `follow-mode'."
378 (defun turn-off-follow-mode ()
379 "Turn off Follow mode. Please see the function `follow-mode'."
382 (put 'follow-mode
'permanent-local t
)
384 (define-minor-mode follow-mode
386 With a prefix argument ARG, enable Follow mode if ARG is
387 positive, and disable it otherwise. If called from Lisp, enable
388 the mode if ARG is omitted or nil.
390 Follow mode is a minor mode that combines windows into one tall
391 virtual window. This is accomplished by two main techniques:
393 * The windows always displays adjacent sections of the buffer.
394 This means that whenever one window is moved, all the
395 others will follow. (Hence the name Follow mode.)
397 * Should point (cursor) end up outside a window, another
398 window displaying that point is selected, if possible. This
399 makes it possible to walk between windows using normal cursor
402 Follow mode comes to its prime when used on a large screen and two
403 side-by-side windows are used. The user can, with the help of Follow
404 mode, use two full-height windows as though they would have been
405 one. Imagine yourself editing a large function, or section of text,
406 and being able to use 144 lines instead of the normal 72... (your
409 To split one large window into two side-by-side windows, the commands
410 `\\[split-window-right]' or \
411 `\\[follow-delete-other-windows-and-split]' can be used.
413 Only windows displayed in the same frame follow each other.
415 This command runs the normal hook `follow-mode-hook'.
417 Keys specific to Follow mode:
419 :keymap follow-mode-map
422 (add-hook 'compilation-filter-hook
'follow-align-compilation-windows t t
)
423 (add-hook 'post-command-hook
'follow-post-command-hook t
)
424 (add-hook 'window-size-change-functions
'follow-window-size-change t
))
425 ;; Remove globally-installed hook functions only if there is no
426 ;; other Follow mode buffer.
427 (let ((buffers (buffer-list))
429 (while (and (not following
) buffers
)
430 (setq following
(buffer-local-value 'follow-mode
(car buffers
))
431 buffers
(cdr buffers
)))
433 (remove-hook 'post-command-hook
'follow-post-command-hook
)
434 (remove-hook 'window-size-change-functions
'follow-window-size-change
)))
435 (remove-hook 'compilation-filter-hook
'follow-align-compilation-windows t
)))
437 (defun follow-find-file-hook ()
438 "Find-file hook for Follow mode. See the variable `follow-auto'."
439 (if follow-auto
(follow-mode 1)))
445 (defun follow-get-scrolled-point (dest windows
)
446 "Calculate the correct value for point after a scrolling operation.
448 DEST is our default position, typically where point was before the scroll.
449 If `scroll-preserve-screen-position' is non-nil and active, DEST will be
450 in the same screen position as before the scroll. WINDOWS is the list of
451 windows in the follow chain.
453 This function attempts to duplicate the point placing from
454 `window_scroll_line_based' in the Emacs core source window.c.
456 Return the new position."
457 (if (and scroll-preserve-screen-position
458 (get this-command
'scroll-command
))
464 (progn (vertical-motion 0) (current-column)))))
466 (with-selected-window (car windows
)
468 (goto-char (window-start))
472 (with-selected-window (car (reverse windows
))
474 (goto-char (window-end nil t
))
475 (if (pos-visible-in-window-p)
476 (point) ; i.e. (point-max)
480 (with-selected-window (car windows
)
483 (vertical-motion (cons dest-column
0))
486 (with-selected-window (car (reverse windows
))
489 (vertical-motion (cons dest-column
0))
493 ;; `scroll-up' and `-down', but for windows in Follow mode.
495 ;; Almost like the real thing, except when the cursor ends up outside
496 ;; the top or bottom... In our case however, we end up outside the
497 ;; window and hence we are recentered. Should we let `recenter' handle
498 ;; the point position we would never leave the selected window. To do
499 ;; it ourselves we would need to do our own redisplay, which is easier
500 ;; said than done. (Why didn't I do a real display abstraction from
503 ;; We must sometimes set `follow-internal-force-redisplay', otherwise
504 ;; our post-command-hook will move our windows back into the old
505 ;; position... (This would also be corrected if we would have had a
506 ;; good redisplay abstraction.)
509 (defun follow-scroll-up (&optional arg
)
510 "Scroll text in a Follow mode window chain up.
512 If called with no ARG, the `next-screen-context-lines' last lines of
513 the bottom window in the chain will be visible in the top window.
515 If called with an argument, scroll ARG lines up.
516 Negative ARG means scroll downward.
518 Works like `scroll-up' when not in Follow mode."
520 (cond ((not follow-mode
)
523 (follow-scroll-down))
525 (let ((opoint (point)) (owin (selected-window)))
527 ;; If we are too near EOB, try scrolling the previous window.
528 (condition-case nil
(progn (scroll-up arg
) nil
)
530 (condition-case nil
(progn (follow-previous-window) t
)
534 (signal 'end-of-buffer nil
))))))
535 (unless (and scroll-preserve-screen-position
536 (get this-command
'scroll-command
))
538 (setq follow-fixed-window t
)))))
539 (put 'follow-scroll-up
'scroll-command t
)
542 (defun follow-scroll-down (&optional arg
)
543 "Scroll text in a Follow mode window chain down.
545 If called with no ARG, the `next-screen-context-lines' top lines of
546 the top window in the chain will be visible in the bottom window.
548 If called with an argument, scroll ARG lines down.
549 Negative ARG means scroll upward.
551 Works like `scroll-down' when not in Follow mode."
553 (cond ((not follow-mode
)
558 (let ((opoint (point)))
560 (unless (and scroll-preserve-screen-position
561 (get this-command
'scroll-command
))
563 (setq follow-fixed-window t
)))))
564 (put 'follow-scroll-down
'scroll-command t
)
566 (declare-function comint-adjust-point
"comint" (window))
567 (defvar comint-scroll-show-maximum-output
)
569 (defun follow-comint-scroll-to-bottom (&optional _window
)
570 "Scroll the bottom-most window in the current Follow chain.
571 This is to be called by `comint-postoutput-scroll-to-bottom'."
572 (let* ((buffer (current-buffer))
573 (selected (selected-window))
574 (is-selected (eq (window-buffer) buffer
))
576 (when (or is-selected
577 (setq some-window
(get-buffer-window)))
578 (let* ((pos (progn (comint-adjust-point nil
) (point)))
581 (car (last (follow-all-followers some-window
))))))
584 (setq follow-windows-start-end-cache nil
)
585 (follow-adjust-window win
)
587 (select-window selected
)
588 (set-buffer buffer
))))))
590 (defun follow-align-compilation-windows ()
591 "Align the windows of the current Follow mode buffer.
592 This is to be called from `compilation-filter-hook'."
593 (let ((buffer (current-buffer))
594 (win (get-buffer-window))
595 (selected (selected-window)))
596 (when (and follow-mode
(waiting-for-user-input-p) win
)
597 (let ((windows (follow-all-followers win
)))
598 (unless (eq (window-buffer selected
) buffer
)
599 (setq win
(car windows
))
601 (follow-redisplay windows win t
)
602 (setq follow-windows-start-end-cache nil
)
603 (unless (eq selected win
)
604 (select-window selected
)
605 (set-buffer buffer
))))))
610 (defun follow-delete-other-windows-and-split (&optional arg
)
611 "Create two side by side windows and enter Follow mode.
613 Execute this command to display as much as possible of the text
614 in the selected window. All other windows, in the current
615 frame, are deleted and the selected window is split in two
616 side-by-side windows. Follow mode is activated, hence the
617 two windows always will display two successive pages.
618 \(If one window is moved, the other one will follow.)
620 If ARG is positive, the leftmost window is selected. If negative,
621 the rightmost is selected. If ARG is nil, the leftmost window is
622 selected if the original window is the first one in the frame."
624 (let ((other (or (and (null arg
)
625 (not (eq (selected-window)
626 (frame-first-window))))
628 (< (prefix-numeric-value arg
) 0))))
629 (start (window-start)))
630 (delete-other-windows)
635 (set-window-start (selected-window) start
)
636 (setq follow-internal-force-redisplay t
)))
639 (defun follow-switch-to-buffer (buffer)
640 "Show BUFFER in all windows in the current Follow mode window chain."
641 (interactive "BSwitch to Buffer: ")
642 (let ((orig-window (selected-window))
643 (windows (follow-all-followers)))
645 (select-window (car windows
))
646 (switch-to-buffer buffer
)
647 (setq windows
(cdr windows
)))
648 (select-window orig-window
)))
651 (defun follow-switch-to-buffer-all (&optional buffer
)
652 "Show BUFFER in all windows on this frame.
653 Defaults to current buffer."
654 (interactive (list (read-buffer "Switch to Buffer: "
656 (or buffer
(setq buffer
(current-buffer)))
657 (let ((orig-window (selected-window)))
658 (walk-windows (lambda (win)
660 (switch-to-buffer buffer
))
662 (select-window orig-window
)
666 (defun follow-switch-to-current-buffer-all ()
667 "Show current buffer in all windows on this frame, and enter Follow mode."
671 (follow-switch-to-buffer-all))
675 ;; Note, these functions are not very useful, at least not unless you
676 ;; rebind the rather cumbersome key sequence `C-c . p'.
678 (defun follow-next-window ()
679 "Select the next window showing the same buffer."
681 (let ((succ (cdr (follow-split-followers (follow-all-followers)))))
683 (select-window (car succ
))
684 (error "%s" "No more windows"))))
687 (defun follow-previous-window ()
688 "Select the previous window showing the same buffer."
690 (let ((pred (car (follow-split-followers (follow-all-followers)))))
692 (select-window (car pred
))
693 (error "%s" "No more windows"))))
696 (defun follow-first-window ()
697 "Select the first window in the frame showing the same buffer."
699 (select-window (car (follow-all-followers))))
702 (defun follow-last-window ()
703 "Select the last window in the frame showing the same buffer."
705 (select-window (car (reverse (follow-all-followers)))))
709 (defun follow-recenter (&optional arg
)
710 "Recenter the middle window around point.
711 Rearrange all other windows around the middle window.
713 With a positive argument, place the current line ARG lines
714 from the top. With a negative argument, place it -ARG lines
719 (arg (prefix-numeric-value arg
)))
721 ;; Recenter relative to the top.
723 (follow-first-window)
726 ;; Recenter relative to the bottom.
730 ;; Otherwise, our post-command-hook will move the window
732 (setq follow-internal-force-redisplay t
)))
733 ;; Recenter in the middle.
734 (let* ((dest (point))
735 (windows (follow-all-followers))
736 (win (nth (/ (- (length windows
) 1) 2) windows
)))
742 (defun follow-redraw ()
743 "Arrange windows displaying the same buffer in successor order.
744 This function can be called even if the buffer is not in Follow mode.
746 Hopefully, there should be no reason to call this function when in
747 Follow mode since the windows should always be aligned."
754 (defun follow-end-of-buffer (&optional arg
)
755 "Move point to the end of the buffer, Follow mode style.
757 If the end is not visible, it will be displayed in the last possible
758 window in the Follow mode window chain.
760 The mark is left at the previous position. With arg N, put point N/10
761 of the way from the true end."
763 (let ((followers (follow-all-followers))
766 (select-window (car (reverse followers
))))
767 ((follow-select-if-end-visible
768 (follow-windows-start-end followers
)))
770 (select-window (car (reverse followers
)))))
773 (end-of-buffer arg
))))
777 (defun follow--window-sorter (w1 w2
)
778 "Sorting function for W1 and W2 based on their positions.
779 Return non-nil if W1 is above W2; if their top-lines
780 are at the same position, return non-nil if W1 is to the
782 (let* ((edge-1 (window-pixel-edges w1
))
783 (edge-2 (window-pixel-edges w2
))
787 (< (car edge-1
) (car edge-2
))
790 (defun follow-all-followers (&optional win
)
791 "Return all windows displaying the same buffer as the WIN.
792 The list is sorted with topmost and leftmost windows first, and
793 contains only windows in the same frame as WIN. If WIN is nil,
794 it defaults to the selected window."
795 (unless (window-live-p win
)
796 (setq win
(selected-window)))
797 (let ((windows (get-buffer-window-list
798 (window-buffer win
) 'no-minibuf
(window-frame win
))))
799 (sort windows
#'follow--window-sorter
)))
801 (defun follow-split-followers (windows &optional win
)
802 "Split WINDOWS into two sets: predecessors and successors.
803 Return `(PRED . SUCC)' where `PRED' and `SUCC' are ordered starting
804 from the selected window."
806 (setq win
(selected-window)))
808 (while (not (eq (car windows
) win
))
809 (setq pred
(cons (car windows
) pred
))
810 (setq windows
(cdr windows
)))
811 (cons pred
(cdr windows
))))
813 (defun follow-calc-win-end (&optional win
)
814 "Calculate the end position for window WIN.
815 Return (END-POS END-OF-BUFFER).
817 Actually, the position returned is the start of the line after
818 the last fully-visible line in WIN. END-OF-BUFFER is t when EOB
819 is fully-visible in WIN. If WIN is nil, the selected window is
821 (let* ((win (or win
(selected-window)))
822 (edges (window-inside-pixel-edges win
))
823 (ht (- (nth 3 edges
) (nth 1 edges
)))
824 (last-line-pos (posn-point (posn-at-x-y 0 (1- ht
) win
))))
825 (if (pos-visible-in-window-p last-line-pos win
)
826 (let ((end (window-end win t
)))
827 (list end
(pos-visible-in-window-p (point-max) win
)))
828 (list last-line-pos nil
))))
830 (defun follow-calc-win-start (windows pos win
)
831 "Determine the start of window WIN in a Follow mode window chain.
832 WINDOWS is a list of chained windows, and POS is the starting
833 position for the first window in the list. If WIN is nil, return
834 the point below all windows."
835 (while (and windows
(not (eq (car windows
) win
)))
836 (let ((old-start (window-start (car windows
))))
837 ;; Can't use `save-window-excursion' since it triggers a redraw.
838 (set-window-start (car windows
) pos
'noforce
)
839 (setq pos
(car (follow-calc-win-end (car windows
))))
840 (set-window-start (car windows
) old-start
'noforce
)
841 (setq windows
(cdr windows
))))
844 ;; The result from `follow-windows-start-end' is cached when using
845 ;; a handful simple commands, like cursor movement commands.
847 (defsubst follow-cache-valid-p
(windows)
848 "Test if the cached value of `follow-windows-start-end' can be used.
849 Note that this handles the case when the cache has been set to nil."
851 (cache follow-windows-start-end-cache
))
852 (while (and res windows cache
)
853 (setq res
(and (eq (car windows
)
855 (eq (window-start (car windows
))
856 (car (cdr (car cache
))))))
857 (setq windows
(cdr windows
))
858 (setq cache
(cdr cache
)))
859 (and res
(null windows
) (null cache
))))
861 (defun follow-windows-start-end (windows)
862 "Return a list of (WIN START END BUFFER-END-P) for window list WINDOWS."
863 (if (follow-cache-valid-p windows
)
864 follow-windows-start-end-cache
865 (let ((orig-win (selected-window))
869 (push (cons w
(cons (window-start) (follow-calc-win-end)))
871 (select-window orig-win
)
872 (setq follow-windows-start-end-cache
(nreverse win-start-end
)))))
874 (defsubst follow-pos-visible
(pos win win-start-end
)
875 "Non-nil when POS is visible in WIN."
876 (let ((wstart-wend-bend (cdr (assq win win-start-end
))))
877 (and (>= pos
(car wstart-wend-bend
))
878 (or (< pos
(cadr wstart-wend-bend
))
879 (nth 2 wstart-wend-bend
)))))
882 ;; By `aligned' we mean that for all adjacent windows, the end of the
883 ;; first is equal with the start of the successor. The first window
884 ;; should start at a full screen line.
886 (defsubst follow-windows-aligned-p
(win-start-end)
887 "Non-nil if the follower windows are aligned.
888 The argument, WIN-START-END, should be a list of the form
889 returned by `follow-windows-start-end'."
891 (while (and win-start-end result
)
892 (if (cdr win-start-end
)
893 (setq result
(eq (nth 2 (car win-start-end
))
894 (nth 1 (cadr win-start-end
)))))
895 (setq win-start-end
(cdr win-start-end
)))
898 ;; Check if point is visible in all windows. (So that
899 ;; no one will be recentered.)
901 (defun follow-point-visible-all-windows-p (win-start-end)
902 "Non-nil when the `window-point' is visible in all windows."
904 (while (and res win-start-end
)
905 (setq res
(follow-pos-visible (window-point (car (car win-start-end
)))
906 (car (car win-start-end
))
908 (setq win-start-end
(cdr win-start-end
)))
912 ;; Make sure WIN always starts at the beginning of a whole screen
913 ;; line. If WIN is not aligned the start is updated which probably
914 ;; will lead to a redisplay of the screen later on.
916 ;; This is used with the first window in a follow chain. The reason
917 ;; is that we want to detect that point is outside the window.
918 ;; (Without the update, the start of the window will move as the
919 ;; user presses BackSpace, and the other window redisplay routines
920 ;; will move the start of the window in the wrong direction.)
922 (defun follow-update-window-start (win)
923 "Make sure that the start of WIN starts at a full screen line."
925 (goto-char (window-start win
))
927 (vertical-motion 0 win
)
928 (unless (eq (point) (window-start win
))
929 (vertical-motion 1 win
)
930 (set-window-start win
(point) 'noforce
)))))
932 (defun follow-select-if-visible (dest win-start-end
)
933 "Select and return a window, if DEST is visible in it.
934 Return the selected window."
936 (while (and (not win
) win-start-end
)
937 ;; Don't select a window that was just moved. This makes it
938 ;; possible to later select the last window after a
939 ;; `end-of-buffer' command.
940 (setq wse
(car win-start-end
))
941 (when (follow-pos-visible dest
(car wse
) win-start-end
)
944 (setq win-start-end
(cdr win-start-end
)))
947 ;; Lets select a window showing the end. Make sure we only select it if
948 ;; it wasn't just moved here. (I.e. M-> shall not unconditionally place
949 ;; point in the selected window.)
951 ;; (Compatibility kludge: in Emacs `window-end' is equal to `point-max';
952 ;; in XEmacs, it is equal to `point-max + 1'. Should I really bother
953 ;; checking `window-end' now when I check `end-of-buffer' explicitly?)
955 (defun follow-select-if-end-visible (win-start-end)
956 "Select and return a window, if end is visible in it."
958 (while (and (not win
) win-start-end
)
959 ;; Don't select a window that was just moved. This makes it
960 ;; possible to later select the last window after a `end-of-buffer'
962 (if (and (eq (point-max) (nth 2 (car win-start-end
)))
963 (nth 3 (car win-start-end
))
964 ;; `window-end' might return nil.
965 (let ((end (window-end (car (car win-start-end
)))))
967 (eq (point-max) (min (point-max) end
)))))
969 (setq win
(car (car win-start-end
)))
970 (select-window win
)))
971 (setq win-start-end
(cdr win-start-end
)))
975 ;; Select a window that will display point if the windows would
976 ;; be redisplayed with the first window fixed. This is useful for
977 ;; example when the user has pressed return at the bottom of a window
978 ;; as point is not visible in any window.
980 (defun follow-select-if-visible-from-first (dest windows
)
981 "Try to select one of WINDOWS without repositioning the topmost window.
982 If one of the windows in WINDOWS contains DEST, select it, call
983 `follow-redisplay', move point to DEST, and return that window.
984 Otherwise, return nil."
985 (let (win end-pos-end-p
)
987 (goto-char (window-start (car windows
)))
988 ;; Make sure the line start in the beginning of a real screen
990 (vertical-motion 0 (car windows
))
991 (when (>= dest
(point))
992 ;; At or below the start. Check the windows.
993 (save-window-excursion
994 (let ((windows windows
))
995 (while (and (not win
) windows
)
996 (set-window-start (car windows
) (point) 'noforce
)
997 (setq end-pos-end-p
(follow-calc-win-end (car windows
)))
998 (goto-char (car end-pos-end-p
))
999 ;; Visible, if dest above end, or if eob is visible
1000 ;; inside the window.
1001 (if (or (car (cdr end-pos-end-p
))
1003 (setq win
(car windows
))
1004 (setq windows
(cdr windows
))))))))
1007 (follow-redisplay windows
(car windows
))
1013 ;; Redraw all the windows on the screen, starting with the top window.
1014 ;; The window used as as marker is WIN, or the selected window if WIN
1015 ;; is nil. Start every window directly after the end of the previous
1016 ;; window, to make sure long lines are displayed correctly.
1018 (defun follow-redisplay (&optional windows win preserve-win
)
1019 "Reposition the WINDOWS around WIN.
1020 Should point be too close to the roof we redisplay everything
1021 from the top. WINDOWS should contain a list of windows to
1022 redisplay; it is assumed that WIN is a member of the list.
1023 Should WINDOWS be nil, the windows displaying the
1024 same buffer as WIN, in the current frame, are used.
1025 Should WIN be nil, the selected window is used.
1026 If PRESERVE-WIN is non-nil, keep WIN itself unchanged while
1027 repositioning the other windows."
1028 (or win
(setq win
(selected-window)))
1029 (or windows
(setq windows
(follow-all-followers win
)))
1030 ;; Calculate the start of the first window.
1031 (let* ((old-win-start (window-start win
))
1032 (try-first-start (follow-estimate-first-window-start
1033 windows win old-win-start
))
1034 (try-win-start (follow-calc-win-start
1035 windows try-first-start win
))
1036 (start (cond ((= try-win-start old-win-start
)
1037 (follow-debug-message "exact")
1039 ((< try-win-start old-win-start
)
1040 (follow-debug-message "above")
1041 (follow-calculate-first-window-start-from-above
1042 windows try-first-start win old-win-start
))
1044 (follow-debug-message "below")
1045 (follow-calculate-first-window-start-from-below
1046 windows try-first-start win old-win-start
)))))
1048 (unless (and preserve-win
(eq w win
))
1049 (set-window-start w start
))
1050 (setq start
(car (follow-calc-win-end w
))))))
1052 (defun follow-estimate-first-window-start (windows win start
)
1053 "Estimate the position of the first window.
1054 The estimate is computed by assuming that the window WIN, which
1055 should be a member of WINDOWS, starts at position START."
1056 (let ((windows-before (car (follow-split-followers windows win
))))
1059 (vertical-motion 0 win
)
1060 (dolist (w windows-before
)
1061 (vertical-motion (- (window-text-height w
)) w
))
1065 ;; Find the starting point, start at GUESS and search downward.
1066 ;; The returned point is always a point below GUESS.
1068 (defun follow-calculate-first-window-start-from-above
1069 (windows guess win start
)
1076 (if (not (= (vertical-motion 1 (car windows
)) 1))
1077 ;; Hit bottom! (Can we really do this?)
1078 ;; We'll keep it, since it ensures termination.
1081 (setq res
(point-max)))
1082 (setq win-start
(follow-calc-win-start windows
(point) win
))
1083 (if (>= win-start start
)
1084 (setq done t res
(point)))))
1088 ;; Find the starting point, start at GUESS and search upward. Return
1089 ;; a point on the same line as GUESS, or above.
1091 (defun follow-calculate-first-window-start-from-below
1092 (windows guess
&optional win start
)
1093 (setq win
(or win
(selected-window)))
1094 (setq start
(or start
(window-start win
)))
1096 (let (done win-start res opoint
)
1097 ;; Always calculate what happens when no line is displayed in the first
1098 ;; window. (The `previous' res is needed below!)
1100 (vertical-motion 0 (car windows
))
1103 (setq opoint
(point))
1104 (if (not (= (vertical-motion -
1 (car windows
)) -
1))
1106 (setq done t res
(point-min))
1107 (setq win-start
(follow-calc-win-start windows
(point) win
))
1108 (cond ((>= (point) opoint
)
1109 ;; In some pathological cases, vertical-motion may
1110 ;; return -1 even though point has not decreased. In
1111 ;; that case, avoid looping forever.
1112 (setq done t res
(point)))
1113 ((= win-start start
) ; Perfect match, use this value
1114 (setq done t res
(point)))
1115 ((< win-start start
) ; Walked to far, use previous result
1117 (t ; Store result for next iteration
1118 (setq res
(point))))))
1121 ;;; Avoid tail recenter
1123 ;; This sets the window internal flag `force_start'. The effect is
1124 ;; that windows only displaying the tail aren't recentered.
1126 ;; A window displaying only the tail, is a window whose window-start
1127 ;; position is equal to (point-max) of the buffer it displays.
1129 (defun follow-avoid-tail-recenter (&rest _rest
)
1130 "Make sure windows displaying the end of a buffer aren't recentered.
1131 This is done by reading and rewriting the start position of
1132 non-first windows in Follow mode."
1133 (let* ((orig-buffer (current-buffer))
1134 (top (frame-first-window))
1136 who
) ; list of (buffer . frame)
1137 ;; If the only window in the frame is a minibuffer
1138 ;; window, `next-window' will never find it again...
1139 (unless (window-minibuffer-p top
)
1140 (while ;; look, no body!
1141 (let ((start (window-start win
))
1142 (pair (cons (window-buffer win
) (window-frame win
))))
1143 (set-buffer (window-buffer win
))
1144 (cond ((null (member pair who
))
1145 (setq who
(cons pair who
)))
1146 ((and follow-mode
(eq (point-max) start
))
1147 ;; Write the same window start back, but don't
1148 ;; set the NOFORCE flag.
1149 (set-window-start win start
)))
1150 (setq win
(next-window win
'not t
))
1151 (not (eq win top
)))) ;; Loop while this is true.
1152 (set-buffer orig-buffer
))))
1154 ;;; Post Command Hook
1156 ;; The magic little box. This function is called after every command.
1158 ;; This is not as complicated as it seems. It is simply a list of common
1159 ;; display situations and the actions to take, plus commands for redrawing
1160 ;; the screen if it should be unaligned.
1162 ;; We divide the check into two parts; whether we are at the end or not.
1163 ;; This is due to the fact that the end can actually be visible
1164 ;; in several window even though they are aligned.
1166 (defun follow-post-command-hook ()
1167 "Ensure that the windows in Follow mode are adjacent after each command."
1168 (unless (input-pending-p)
1169 (let ((follow-inside-post-command-hook t
)
1170 (win (selected-window)))
1171 ;; Work in the selected window, not in the current buffer.
1172 (with-current-buffer (window-buffer win
)
1173 (unless (and (symbolp this-command
)
1174 (get this-command
'follow-mode-use-cache
))
1175 (setq follow-windows-start-end-cache nil
))
1176 (follow-adjust-window win
)))))
1178 (defun follow-adjust-window (win)
1179 ;; Adjust the window WIN and its followers.
1180 (cl-assert (eq (window-buffer win
) (current-buffer)))
1181 (when (and follow-mode
1182 (not (window-minibuffer-p win
)))
1183 (let ((windows (follow-all-followers win
)))
1184 ;; If we've explicitly scrolled, align the windows first.
1185 (when follow-fixed-window
1186 (follow-debug-message "fixed")
1187 (follow-redisplay windows win
)
1188 (goto-char (follow-get-scrolled-point (point) windows
))
1189 (setq follow-fixed-window nil
))
1190 (let* ((dest (point))
1191 (win-start-end (progn
1192 (follow-update-window-start (car windows
))
1193 (follow-windows-start-end windows
)))
1194 (aligned (follow-windows-aligned-p win-start-end
))
1195 (visible (follow-pos-visible dest win win-start-end
))
1196 selected-window-up-to-date
)
1197 (unless (and aligned visible
)
1198 (setq follow-windows-start-end-cache nil
))
1200 ;; Select a window to display point.
1201 (unless follow-internal-force-redisplay
1202 (if (eq dest
(point-max))
1203 ;; Be careful at point-max: the display can be aligned
1204 ;; while DEST can be visible in several windows.
1206 ;; Select the current window, but only when the display
1207 ;; is correct. (When inserting characters in a tail
1208 ;; window, the display is not correct, as they are
1211 ;; Never stick to the current window after a deletion.
1212 ;; Otherwise, when typing `DEL' in a window showing
1213 ;; only the end of the file, a character would be
1214 ;; removed from the window above, which is very
1218 (not (memq this-command
1219 '(backward-delete-char
1220 delete-backward-char
1221 backward-delete-char-untabify
1223 (follow-debug-message "Max: same"))
1224 ;; If the end is visible, and the window doesn't
1225 ;; seems like it just has been moved, select it.
1226 ((follow-select-if-end-visible win-start-end
)
1227 (follow-debug-message "Max: end visible")
1228 (setq visible t aligned nil
)
1230 ;; Just show the end...
1232 (follow-debug-message "Max: default")
1233 (select-window (car (last windows
)))
1235 (setq visible nil aligned nil
)))
1237 ;; We're not at the end, here life is much simpler.
1239 ;; This is the normal case!
1240 ;; It should be optimized for speed.
1241 ((and visible aligned
)
1242 (follow-debug-message "same"))
1243 ;; Pick a position in any window. If the display is ok,
1244 ;; this picks the `correct' window.
1245 ((follow-select-if-visible dest win-start-end
)
1246 (follow-debug-message "visible")
1248 ;; Perform redisplay, in case line is partially visible.
1250 ;; Not visible anywhere else, lets pick this one.
1252 (follow-debug-message "visible in selected."))
1253 ;; If DEST is before the first window start, select the
1255 ((< dest
(nth 1 (car win-start-end
)))
1256 (follow-debug-message "before first")
1257 (select-window (car windows
))
1259 (setq visible nil aligned nil
))
1260 ;; If we can position the cursor without moving the first
1261 ;; window, do it. This is the case that catches `RET' at
1262 ;; the bottom of a window.
1263 ((follow-select-if-visible-from-first dest windows
)
1264 (follow-debug-message "Below first")
1265 (setq visible t aligned t
))
1266 ;; None of the above. Stick to the selected window.
1268 (follow-debug-message "None")
1269 (setq visible nil aligned nil
))))
1271 ;; If a new window was selected, make sure that the old is
1272 ;; not scrolled when point is outside the window.
1273 (unless (eq win
(selected-window))
1274 (let ((p (window-point win
)))
1275 (set-window-start win
(window-start win
) nil
)
1276 (set-window-point win p
))))
1279 ;; If point may not be visible in the selected window,
1280 ;; perform a redisplay; this ensures scrolling.
1281 (let ((opoint (point)))
1283 ;; If this `redisplay' moved point, we got clobbered by a
1284 ;; previous call to `set-window-start'. Try again.
1285 (when (/= (point) opoint
)
1289 (setq selected-window-up-to-date t
)
1290 (follow-avoid-tail-recenter)
1291 (setq win-start-end
(follow-windows-start-end windows
)
1292 follow-windows-start-end-cache nil
1295 ;; Now redraw the windows around the selected window.
1296 (unless (and (not follow-internal-force-redisplay
)
1298 (follow-windows-aligned-p win-start-end
))
1299 (follow-point-visible-all-windows-p win-start-end
))
1300 (setq follow-internal-force-redisplay nil
)
1301 (follow-redisplay windows
(selected-window)
1302 selected-window-up-to-date
)
1303 (setq win-start-end
(follow-windows-start-end windows
)
1304 follow-windows-start-end-cache nil
)
1305 ;; Point can end up in another window when DEST is at
1306 ;; the beginning of the buffer and the selected window is
1307 ;; not the first. It can also happen when long lines are
1308 ;; used and there is a big difference between the width of
1309 ;; the windows. (When scrolling one line in a wide window
1310 ;; which will cause a move larger that an entire small
1312 (unless (follow-pos-visible dest win win-start-end
)
1313 (follow-select-if-visible dest win-start-end
)
1316 ;; If the region is visible, make it look good when spanning
1317 ;; multiple windows.
1318 (when (region-active-p)
1319 (follow-maximize-region
1320 (selected-window) windows win-start-end
))))
1322 ;; Whether or not the buffer was in follow mode, update windows
1323 ;; displaying the tail so that Emacs won't recenter them.
1324 (follow-avoid-tail-recenter)))
1328 ;; Tries to make the highlighted area representing the region look
1329 ;; good when spanning several windows.
1331 ;; Not perfect, as point can't be placed at window end, only at
1332 ;; end-1. This will highlight a little bit in windows above
1335 (defun follow-maximize-region (win windows win-start-end
)
1336 "Make a highlighted region stretching multiple windows look good."
1337 (let* ((all (follow-split-followers windows win
))
1342 (setq data
(assq (car pred
) win-start-end
))
1343 (set-window-point (car pred
) (max (nth 1 data
) (- (nth 2 data
) 1)))
1344 (setq pred
(cdr pred
)))
1346 (set-window-point (car succ
) (nth 1 (assq (car succ
) win-start-end
)))
1347 (setq succ
(cdr succ
)))))
1351 ;;;; Scroll-bar support code.
1353 ;; This handles the case where the user drags the scroll bar of a
1354 ;; non-selected window whose buffer is in Follow mode.
1356 (declare-function scroll-bar-toolkit-scroll
"scroll-bar" (event))
1357 (declare-function scroll-bar-drag
"scroll-bar" (event))
1358 (declare-function scroll-bar-scroll-up
"scroll-bar" (event))
1359 (declare-function scroll-bar-scroll-down
"scroll-bar" (event))
1360 (declare-function mwheel-scroll
"mwheel" (event))
1362 (defun follow-scroll-bar-toolkit-scroll (event)
1364 (scroll-bar-toolkit-scroll event
)
1365 (follow-redraw-after-event event
))
1367 (defun follow-scroll-bar-drag (event)
1369 (scroll-bar-drag event
)
1370 (follow-redraw-after-event event
))
1372 (defun follow-scroll-bar-scroll-up (event)
1374 (scroll-bar-scroll-up event
)
1375 (follow-redraw-after-event event
))
1377 (defun follow-scroll-bar-scroll-down (event)
1379 (scroll-bar-scroll-down event
)
1380 (follow-redraw-after-event event
))
1382 (defun follow-mwheel-scroll (event)
1384 (mwheel-scroll event
)
1385 (follow-redraw-after-event event
))
1387 (defun follow-redraw-after-event (event)
1388 "Re-align the Follow mode windows affected by EVENT."
1389 (let* ((window (nth 0 (event-end event
)))
1390 (buffer (window-buffer window
))
1391 (orig-win (selected-window)))
1392 (when (and (buffer-local-value 'follow-mode buffer
)
1393 ;; Ignore the case where we scroll the selected window;
1394 ;; that is handled by the post-command hook function.
1395 (not (eq window
(selected-window))))
1396 (select-window window
)
1398 (unless (eq (window-buffer orig-win
) buffer
)
1399 (select-window orig-win
)))))
1401 ;;; Window size change
1403 ;; The functions in `window-size-change-functions' are called every
1404 ;; time a window in a frame changes size, most notably after the frame
1405 ;; has been resized. We call `follow-post-command-hook' for every
1406 ;; Follow mode buffer visible in any window in the resized frame.
1408 ;; Since `follow-window-size-change' can be called indirectly from
1409 ;; `follow-post-command-hook' we have a potential infinite loop. To
1410 ;; avoid this, we simply do not do anything in this situation. The
1411 ;; variable `follow-inside-post-command-hook' contains information
1412 ;; about whether the execution actually is inside the
1413 ;; post-command-hook or not.
1415 (defun follow-window-size-change (frame)
1416 "Redraw all windows in FRAME, when in Follow mode."
1417 ;; Below, we call `post-command-hook'. Avoid an infloop.
1418 (unless follow-inside-post-command-hook
1420 (orig-window (selected-window))
1421 (orig-buffer (current-buffer))
1422 (orig-frame (selected-frame))
1425 (select-frame frame
)
1429 (setq buf
(window-buffer win
))
1430 (unless (memq buf buffers
)
1433 (setq windows
(follow-all-followers win
))
1434 (if (not (memq orig-window windows
))
1435 (follow-redisplay windows win
)
1436 ;; Make sure we're redrawing around the selected
1438 (select-window orig-window
)
1439 (follow-post-command-hook)
1440 (setq orig-window
(selected-window)))
1441 (setq buffers
(cons buf buffers
)))))
1443 (select-frame orig-frame
)
1444 (set-buffer orig-buffer
)
1445 (select-window orig-window
)))))
1447 (add-hook 'window-scroll-functions
'follow-avoid-tail-recenter t
)
1451 ;; The following (non-evaluated) section can be used to
1452 ;; profile this package using `elp'.
1454 ;; Invalid indentation on purpose!
1456 ;; (setq elp-function-list
1460 ;; follow-all-followers
1461 ;; follow-split-followers
1463 ;; follow-estimate-first-window-start
1464 ;; follow-calculate-first-window-start-from-above
1465 ;; follow-calculate-first-window-start-from-below
1466 ;; follow-calc-win-end
1467 ;; follow-calc-win-start
1468 ;; follow-pos-visible
1469 ;; follow-windows-start-end
1470 ;; follow-cache-valid-p
1471 ;; follow-select-if-visible
1472 ;; follow-select-if-visible-from-first
1473 ;; follow-windows-aligned-p
1474 ;; follow-point-visible-all-windows-p
1475 ;; follow-avoid-tail-recenter
1476 ;; follow-update-window-start
1477 ;; follow-post-command-hook))
1481 ;; /------------------------------------------------------------------------\
1482 ;; | "I [..] am rarely happier then when spending an entire day programming |
1483 ;; | my computer to perform automatically a task that it would otherwise |
1484 ;; | take me a good ten seconds to do by hand. Ten seconds, I tell myself, |
1485 ;; | is ten seconds. Time is valuable and ten seconds' worth of it is well |
1486 ;; | worth the investment of a day's happy activity working out a way to |
1487 ;; | save it". -- Douglas Adams, "Last Chance to See" |
1488 ;; \------------------------------------------------------------------------/
1490 ;;; follow.el ends here