]> code.delx.au - gnu-emacs/blobdiff - lisp/follow.el
(dabbrev--search): Don't downcase the result here.
[gnu-emacs] / lisp / follow.el
index 91a2e1a1ee2bd7865f3e0606e7c0d0fc5ded1aec..cae16af116e2c03e48d124c4e8ed968fe7706ff0 100644 (file)
@@ -5,23 +5,26 @@
 ;; Author: Anders Lindgren <andersl@csd.uu.se>
 ;; Maintainer: Anders Lindgren <andersl@csd.uu.se>
 ;; Created: 25 May 1995
-;; Version: 1.5
+;; Version: 1.6
 ;; Keywords: display, window, minor-mode
-;; Date: 22 Jan 1996
+;; Date: 20 Feb 1996
 
-;; This program is free software; you can redistribute it and/or modify
+;; This file is part of GNU Emacs.
+
+;; GNU Emacs is free software; you can redistribute it and/or modify
 ;; it under the terms of the GNU General Public License as published by
 ;; the Free Software Foundation; either version 2, or (at your option)
 ;; any later version.
 
-;; This program is distributed in the hope that it will be useful,
+;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
 ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with this program; see the file COPYING.  If not, write to
-;; the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+;; along with GNU Emacs; see the file COPYING.  If not, write to the
+;; Free Software Foundation, Inc., 59 Temple Place - Suite 330,
+;; Boston, MA 02111-1307, USA.
 
 ;;; Commentary:
 
 
 ;; There exists two system variables which controls the appearence of
 ;; lines which are wider than the window containing them.  The default
-;; is to truncate long lines if a window isn't as wide as the frame.
+;; is to truncate long lines whenever a window isn't as wide as the
+;; frame.
 ;;
 ;; To make sure lines are never truncated, please place the following
 ;; lines in your init file:
 ;; (setq truncate-partial-width-windows nil)
 
 
+;; Since the display of XEmacs is pixel-oriented, a line could be
+;; clipped in half at the bottom of the window.
+;;
+;; To make XEmacs avoid clipping (normal) lines, please place the
+;; following line in your init-file:
+;;
+;; (setq pixel-vertical-clip-threshold 30)
+
+
 ;; The correct way to cofigurate Follow mode, or any other mode for
 ;; that matter, is to create one (or more) function which does
 ;; whatever you would like to do.  The function is then added to
 ;;                           Code in post hook removed.
 ;;                         * XEmacs: Post hook is always executed
 ;;                          after a mouse button event.
-;;      22-Jan-95 andersl  * 1.5 released.
+;;      22-Jan-96 andersl  * 1.5 released.
 ;;
 
 ;;}}}
 ;;; LCD Archive Entry:
 ;; follow|Anders Lindgren|andersl@csd.uu.se|
 ;; Combines windows into tall virtual window, minor mode.
-;; 22-Jan-1996|1.5|~/modes/follow.el.Z|
+;; 20-Feb-1996|1.6|~/modes/follow.el.Z|
 
 ;;}}}
 
 (defvar follow-mode-off-hook nil
   "*Hooks to run when follow-mode is turned off.")
 
-(defvar follow-mode-version "follow.el (Release 1.5)"
+(defvar follow-mode-version "follow.el (Release 1.6)"
   "The current version of Follow mode.")
 
 (defvar follow-mode-map nil
-  "Minor mode keymap for Follow mode.")
+  "*Minor mode keymap for Follow mode.")
 
 (defvar follow-mode-line-text " Follow"
   "*Text shown in the mode line when Follow mode is active.  
@@ -400,7 +413,7 @@ the buffer.  Normally it is practical for the user that empty
 windows are recentered automatically.  However, when using 
 Follow Mode it breaks the display when the end is displayed 
 in a window \"above\" the last window.  This is for 
-example the case when displaying short files.
+example the case when displaying a short page in info.
 
 Must be set before Follow Mode is loaded.
 
@@ -410,11 +423,24 @@ situation in which Emacs recenters empty windows.
 
 XEmacs, as of 19.12, does not recenter windows, good!")
 
+(defvar follow-cache-command-list
+  '(next-line previous-line forward-char backward-char)
+  "List of commands which don't require recalculation.
+
+In order to be able to use the cache, a command should not change the
+contents of the buffer, nor should it change selected window or current
+buffer.
+
+The commands in this list are checked at load time.
+
+To mark other commands as suitable for caching, set the symbol
+property `follow-mode-use-cache' to non-nil.")
+
 (defvar follow-debug nil
   "*Non-nil when debugging Follow mode.")
 
 
-;;; Internal variables
+;; Internal variables:
 
 (defvar follow-internal-force-redisplay nil
   "True when Follow mode should redisplay the windows.")
@@ -432,6 +458,9 @@ XEmacs, as of 19.12, does not recenter windows, good!")
   "Non-nil when inside Follow modes `post-command-hook'. 
 Used by `follow-window-size-change'.")
 
+(defvar follow-windows-start-end-cache nil
+  "Cache used by `follow-window-start-end'.")
+
 ;;}}}
 ;;{{{ Bug report
 
@@ -654,6 +683,14 @@ Used by `follow-window-size-change'.")
     (setq minor-mode-map-alist
          (cons (cons 'follow-mode follow-mode-map) minor-mode-map-alist)))
 
+;;}}}
+;;{{{ Cache
+
+(let ((cmds follow-cache-command-list))
+  (while cmds
+    (put (car cmds) 'follow-mode-use-cache t)
+    (setq cmds (cdr cmds))))
+
 ;;}}}
 
 ;;{{{ The mode
@@ -739,9 +776,13 @@ Keys specific to Follow mode:
 
 ;; Register follow-mode as a minor mode.
 
-(or (assq 'follow-mode minor-mode-alist)
-    (setq minor-mode-alist
-         (cons '(follow-mode follow-mode-line-text) minor-mode-alist)))
+(if (fboundp 'add-minor-mode)
+    ;; XEmacs
+    (funcall (symbol-function 'add-minor-mode)
+            'follow-mode 'follow-mode-line-text)
+  (or (assq 'follow-mode minor-mode-alist)
+      (setq minor-mode-alist
+           (cons '(follow-mode follow-mode-line-text) minor-mode-alist))))
 
 ;;}}}
 ;;{{{ Find file hook
@@ -1123,6 +1164,29 @@ If WIN is nil the point below all windows is returned."
     pos))
 
 
+;; The result from `follow-windows-start-end' is cached when using
+;; a handful simple commands, like cursor movement commands.
+
+(defsubst follow-cache-valid-p (windows)
+  "Test if the cached value of `follow-windows-start-end' can be used.
+Note that this handles the case when the cache has been set to nil."
+  (let ((res t)
+       (cache follow-windows-start-end-cache))
+    (while (and res windows cache)
+      (setq res (and (eq (car windows)
+                        (car (car cache)))
+                    (eq (window-start (car windows))
+                        (car (cdr (car cache))))))
+      (setq windows (cdr windows))
+      (setq cache (cdr cache)))
+    (and res (null windows) (null cache))))
+
+
+(defsubst follow-invalidate-cache ()
+  "Force `follow-windows-start-end' to recalculate the end of the window."
+  (setq follow-windows-start-end-cache nil))
+
+
 ;; Build a list of windows and their start and end positions.
 ;; Useful to avoid calculating start/end position whenever they are needed.
 ;; The list has the format:
@@ -1134,21 +1198,24 @@ If WIN is nil the point below all windows is returned."
 
 (defun follow-windows-start-end (windows)
   "Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS."
-  (let ((win-start-end '())
-       (orig-win (selected-window)))
-    (while windows
-      (select-window (car windows))
-      (setq win-start-end 
-           (cons (cons (car windows) 
-                       (cons (window-start)
-                             (follow-calc-win-end)))
-            win-start-end))
-      (setq windows (cdr windows)))
-    (select-window orig-win)
-    (nreverse win-start-end)))
-
-
-(defun follow-pos-visible (pos win win-start-end)
+  (if (follow-cache-valid-p windows)
+      follow-windows-start-end-cache
+    (let ((win-start-end '())
+         (orig-win (selected-window)))
+      (while windows
+       (select-window (car windows))
+       (setq win-start-end 
+             (cons (cons (car windows) 
+                         (cons (window-start)
+                               (follow-calc-win-end)))
+                   win-start-end))
+       (setq windows (cdr windows)))
+      (select-window orig-win)
+      (setq follow-windows-start-end-cache (nreverse win-start-end))
+      follow-windows-start-end-cache)))
+
+
+(defsubst follow-pos-visible (pos win win-start-end)
   "Non-nil when POS is visible in WIN."
   (let ((wstart-wend-bend (cdr (assq win win-start-end))))
     (and (>= pos (car wstart-wend-bend))
@@ -1160,7 +1227,7 @@ If WIN is nil the point below all windows is returned."
 ;; first is equal with the start of the successor.  The first window
 ;; should start at a full screen line.
 
-(defun follow-windows-aligned-p (win-start-end)
+(defsubst follow-windows-aligned-p (win-start-end)
   "Non-nil if the follower WINDOWS are alinged."
   (let ((res t)) 
     (save-excursion
@@ -1171,8 +1238,8 @@ If WIN is nil the point below all windows is returned."
         (setq res (eq (point) (window-start (car (car win-start-end)))))))
     (while (and res (cdr win-start-end))
       ;; At least two followers left
-      (setq res (eq (nth 2 (car win-start-end))
-                   (nth 1 (car (cdr win-start-end)))))
+      (setq res (eq (car (cdr (cdr (car win-start-end))))
+                   (car (cdr (car (cdr win-start-end))))))
       (setq win-start-end (cdr win-start-end)))
     res))
 
@@ -1184,10 +1251,9 @@ If WIN is nil the point below all windows is returned."
   "Non-nil when the window-point is visible in all windows."
   (let ((res t))
     (while (and res win-start-end)
-      (setq res (inline 
-                 (follow-pos-visible (window-point (car (car win-start-end)))
-                                     (car (car win-start-end))
-                                     win-start-end)))
+      (setq res (follow-pos-visible (window-point (car (car win-start-end)))
+                                   (car (car win-start-end))
+                                   win-start-end))
       (setq win-start-end (cdr win-start-end)))
     res))
 
@@ -1495,26 +1561,30 @@ This is done by reading and rewriting the start positon of
 non-first windows in Follow Mode."
   (if follow-avoid-tail-recenter-p
       (let* ((orig-buffer (current-buffer))
-            (top (frame-first-window (selected-frame)))
-            (win top)
-            (who '())                  ; list of (buffer . frame)
-            start
-            pair)                      ; (buffer . frame)
-       (while  ;; look, no body!
-           (progn
-             (setq start (window-start win))
-             (set-buffer (window-buffer win))
-             (setq pair (cons (window-buffer win) (window-frame win)))
-             (if (member pair who)
-                 (if (and (boundp 'follow-mode) follow-mode 
-                          (eq (point-max) start))
-                     ;; Write the same window start back, but don't
-                     ;; set the NOFORCE flag.
-                     (set-window-start win start))
-               (setq who (cons pair who)))
-             (setq win (next-window win 'not t))
-             (not (eq win top))))  ;; Loop while this is true.
-       (set-buffer orig-buffer))))
+           (top (frame-first-window (selected-frame)))
+           (win top)
+           (who '())                   ; list of (buffer . frame)
+           start
+           pair)                       ; (buffer . frame)
+       ;; If the only window in the frame is a minibuffer
+       ;; window, `next-window' will never find it again...
+       (if (window-minibuffer-p top)
+           nil
+         (while  ;; look, no body!
+             (progn
+               (setq start (window-start win))
+               (set-buffer (window-buffer win))
+               (setq pair (cons (window-buffer win) (window-frame win)))
+               (if (member pair who)
+                   (if (and (boundp 'follow-mode) follow-mode 
+                            (eq (point-max) start))
+                       ;; Write the same window start back, but don't
+                       ;; set the NOFORCE flag.
+                       (set-window-start win start))
+                 (setq who (cons pair who)))
+               (setq win (next-window win 'not t))
+               (not (eq win top))))  ;; Loop while this is true.
+         (set-buffer orig-buffer)))))
 
 ;;}}}
 
@@ -1547,6 +1617,9 @@ non-first windows in Follow Mode."
       (let ((orig-buffer (current-buffer))
            (win (selected-window)))
        (set-buffer (window-buffer win))
+       (or (and (symbolp this-command) 
+                (get this-command 'follow-mode-use-cache))
+           (follow-invalidate-cache))
        (if (and (boundp 'follow-mode) follow-mode
                 (not (window-minibuffer-p win)))
            ;; The buffer shown in the selected window is in follow
@@ -1554,12 +1627,14 @@ non-first windows in Follow Mode."
            ;; cache the result for speed (i.e. `aligned' and `visible'.)
            (let* ((windows (inline (follow-all-followers win)))
                   (dest (point))
-                  (win-start-end (progn
+                  (win-start-end (inline
                                    (follow-update-window-start (car windows))
                                    (follow-windows-start-end windows)))
                   (aligned (follow-windows-aligned-p win-start-end))
                   (visible (follow-pos-visible dest win win-start-end)))
-             (follow-avoid-tail-recenter)
+             (if (not (and aligned visible))
+                 (follow-invalidate-cache))
+             (inline (follow-avoid-tail-recenter))
              ;; Select a window to display the point.
              (or follow-internal-force-redisplay
                  (progn
@@ -1629,6 +1704,7 @@ non-first windows in Follow Mode."
                        (goto-char dest)
                        (set-window-start (selected-window) (point-min))
                        (setq win-start-end (follow-windows-start-end windows))
+                       (follow-invalidate-cache)
                        (setq visible t)
                        (setq aligned nil))
                       ;; If we can position the cursor without moving the first
@@ -1661,6 +1737,7 @@ non-first windows in Follow Mode."
                (sit-for 0)
                (follow-avoid-tail-recenter)
                (setq win-start-end (follow-windows-start-end windows))
+               (follow-invalidate-cache)
                (setq aligned nil))
              ;; Redraw the windows whenever needed.
              (if (or follow-internal-force-redisplay
@@ -1672,6 +1749,7 @@ non-first windows in Follow Mode."
                    (setq follow-internal-force-redisplay nil)
                    (follow-redisplay windows (selected-window))
                    (setq win-start-end (follow-windows-start-end windows))
+                   (follow-invalidate-cache)
                    ;; When the point ends up in another window. This
                    ;; happends when dest is in the beginning of the
                    ;; file and the selected window is not the first.
@@ -1694,7 +1772,7 @@ non-first windows in Follow Mode."
                  (follow-maximize-region 
                   (selected-window) windows win-start-end))
 
-             (follow-avoid-tail-recenter)            
+             (inline (follow-avoid-tail-recenter))
              ;; DEBUG
              ;;(if (not (follow-windows-aligned-p 
              ;;           (follow-windows-start-end windows)))
@@ -2129,18 +2207,20 @@ report this using the `follow-submit-feedback' function."
     (if return-to-orig-win
        (select-window orig-win))
     (set-buffer old-buffer))
-
-    ;; Normally, if the display has been changed, it is redrawn.  All
-    ;; windows showing only the end of a buffer is unconditionally
-    ;; recentered, we can't prevent it by calling
-    ;; `follow-avoid-tail-recenter'.
-    ;;
-    ;; By performing a redisplay on our own, Emacs need not perform
-    ;; the above described redisplay.  (However, bu performing it when
-    ;; there are input available just seems to make things worse.)
-    (if (and follow-avoid-tail-recenter-p
-            (not (input-pending-p)))
-       (sit-for 0)))
+  
+  (follow-invalidate-cache)
+
+  ;; Normally, if the display has been changed, it is redrawn.  All
+  ;; windows showing only the end of a buffer is unconditionally
+  ;; recentered, we can't prevent it by calling
+  ;; `follow-avoid-tail-recenter'.
+  ;;
+  ;; By performing a redisplay on our own, Emacs need not perform
+  ;; the above described redisplay.  (However, bu performing it when
+  ;; there are input available just seems to make things worse.)
+  (if (and follow-avoid-tail-recenter-p
+          (not (input-pending-p)))
+      (sit-for 0)))
 
 ;;}}}
 
@@ -2316,6 +2396,7 @@ messing things up."
        follow-calc-win-start
        follow-pos-visible
        follow-windows-start-end
+       follow-cache-valid-p
        follow-select-if-visible
        follow-select-if-visible-from-first
        follow-windows-aligned-p