Replace "Maintainer: FSF" with the emacs-devel mailing address
[bpt/emacs.git] / lisp / follow.el
index ef26788..ddb25be 100644 (file)
@@ -1,9 +1,10 @@
 ;;; follow.el --- synchronize windows showing the same buffer
 ;;; follow.el --- synchronize windows showing the same buffer
-;; Copyright (C) 1995-1997, 1999, 2001-2013 Free Software Foundation,
+
+;; Copyright (C) 1995-1997, 1999, 2001-2014 Free Software Foundation,
 ;; Inc.
 
 ;; Author: Anders Lindgren <andersl@andersl.com>
 ;; Inc.
 
 ;; Author: Anders Lindgren <andersl@andersl.com>
-;; Maintainer: FSF (Anders' email bounces, Sep 2005)
+;; Maintainer: emacs-devel@gnu.org (Anders' email bounces, Sep 2005)
 ;; Created: 1995-05-25
 ;; Keywords: display, window, minor-mode, convenience
 
 ;; Created: 1995-05-25
 ;; Keywords: display, window, minor-mode, convenience
 
@@ -34,7 +35,7 @@
 ;;   This means that whenever one window is moved, all the
 ;;   others will follow.  (Hence the name Follow mode.)
 ;;
 ;;   This means that whenever one window is moved, all the
 ;;   others will follow.  (Hence the name Follow mode.)
 ;;
-;; * Should the point (cursor) end up outside a window, another
+;; * Should point (cursor) end up outside a window, another
 ;;   window displaying that point is selected, if possible.  This
 ;;   makes it possible to walk between windows using normal cursor
 ;;   movement commands.
 ;;   window displaying that point is selected, if possible.  This
 ;;   makes it possible to walk between windows using normal cursor
 ;;   movement commands.
 ;;             this command be added to the global keymap.
 ;;
 ;;     follow-recenter                          C-c . C-l
 ;;             this command be added to the global keymap.
 ;;
 ;;     follow-recenter                          C-c . C-l
-;;             Place the point in the center of the middle window,
+;;             Place point in the center of the middle window,
 ;;             or a specified number of lines from either top or bottom.
 ;;
 ;;     follow-switch-to-buffer                  C-c . b
 ;;             or a specified number of lines from either top or bottom.
 ;;
 ;;     follow-switch-to-buffer                  C-c . b
 ;;; Code:
 
 (require 'easymenu)
 ;;; Code:
 
 (require 'easymenu)
+(eval-when-compile (require 'cl-lib))
 
 ;;; Variables
 
 
 ;;; Variables
 
@@ -310,7 +312,7 @@ are \" Fw\", or simply \"\"."
         (set-default symbol value)))
 
 (defvar follow-cache-command-list
         (set-default symbol value)))
 
 (defvar follow-cache-command-list
-  '(next-line previous-line forward-char backward-char)
+  '(next-line previous-line forward-char backward-char right-char left-char)
   "List of commands that don't require recalculation.
 
 In order to be able to use the cache, a command should not change the
   "List of commands that don't require recalculation.
 
 In order to be able to use the cache, a command should not change the
@@ -389,7 +391,7 @@ virtual window.  This is accomplished by two main techniques:
   This means that whenever one window is moved, all the
   others will follow.  (Hence the name Follow mode.)
 
   This means that whenever one window is moved, all the
   others will follow.  (Hence the name Follow mode.)
 
-* Should the point (cursor) end up outside a window, another
+* Should point (cursor) end up outside a window, another
   window displaying that point is selected, if possible.  This
   makes it possible to walk between windows using normal cursor
   movement commands.
   window displaying that point is selected, if possible.  This
   makes it possible to walk between windows using normal cursor
   movement commands.
@@ -515,7 +517,7 @@ Works like `scroll-up' when not in Follow mode."
 (declare-function comint-adjust-point "comint" (window))
 (defvar comint-scroll-show-maximum-output)
 
 (declare-function comint-adjust-point "comint" (window))
 (defvar comint-scroll-show-maximum-output)
 
-(defun follow-comint-scroll-to-bottom (&optional window)
+(defun follow-comint-scroll-to-bottom (&optional _window)
   "Scroll the bottom-most window in the current Follow chain.
 This is to be called by `comint-postoutput-scroll-to-bottom'."
   (let* ((buffer (current-buffer))
   "Scroll the bottom-most window in the current Follow chain.
 This is to be called by `comint-postoutput-scroll-to-bottom'."
   (let* ((buffer (current-buffer))
@@ -531,7 +533,7 @@ This is to be called by `comint-postoutput-scroll-to-bottom'."
        (select-window win)
        (goto-char pos)
        (setq follow-windows-start-end-cache nil)
        (select-window win)
        (goto-char pos)
        (setq follow-windows-start-end-cache nil)
-       (follow-adjust-window win pos)
+       (follow-adjust-window win)
        (unless is-selected
          (select-window selected)
          (set-buffer buffer))))))
        (unless is-selected
          (select-window selected)
          (set-buffer buffer))))))
@@ -572,7 +574,7 @@ selected if the original window is the first one in the frame."
   (interactive "P")
   (let ((other (or (and (null arg)
                        (not (eq (selected-window)
   (interactive "P")
   (let ((other (or (and (null arg)
                        (not (eq (selected-window)
-                                (frame-first-window (selected-frame)))))
+                                (frame-first-window))))
                   (and arg
                        (< (prefix-numeric-value arg) 0))))
        (start (window-start)))
                   (and arg
                        (< (prefix-numeric-value arg) 0))))
        (start (window-start)))
@@ -743,12 +745,9 @@ contains only windows in the same frame as WIN.  If WIN is nil,
 it defaults to the selected window."
   (unless (window-live-p win)
     (setq win (selected-window)))
 it defaults to the selected window."
   (unless (window-live-p win)
     (setq win (selected-window)))
-  (let ((buffer (window-buffer win))
-       windows)
-    (dolist (w (window-list (window-frame win) 'no-minibuf win))
-      (if (eq (window-buffer w) buffer)
-         (push w windows)))
-    (sort windows 'follow--window-sorter)))
+  (let ((windows (get-buffer-window-list
+                  (window-buffer win) 'no-minibuf (window-frame win))))
+    (sort windows #'follow--window-sorter)))
 
 (defun follow-split-followers (windows &optional win)
   "Split WINDOWS into two sets: predecessors and successors.
 
 (defun follow-split-followers (windows &optional win)
   "Split WINDOWS into two sets: predecessors and successors.
@@ -846,7 +845,7 @@ returned by `follow-windows-start-end'."
       (setq win-start-end (cdr win-start-end)))
     result))
 
       (setq win-start-end (cdr win-start-end)))
     result))
 
-;; Check if the point is visible in all windows. (So that
+;; Check if point is visible in all windows. (So that
 ;; no one will be recentered.)
 
 (defun follow-point-visible-all-windows-p (win-start-end)
 ;; no one will be recentered.)
 
 (defun follow-point-visible-all-windows-p (win-start-end)
@@ -865,7 +864,7 @@ returned by `follow-windows-start-end'."
 ;; will lead to a redisplay of the screen later on.
 ;;
 ;; This is used with the first window in a follow chain.  The reason
 ;; will lead to a redisplay of the screen later on.
 ;;
 ;; This is used with the first window in a follow chain.  The reason
-;; is that we want to detect that the point is outside the window.
+;; is that we want to detect that point is outside the window.
 ;; (Without the update, the start of the window will move as the
 ;; user presses BackSpace, and the other window redisplay routines
 ;; will move the start of the window in the wrong direction.)
 ;; (Without the update, the start of the window will move as the
 ;; user presses BackSpace, and the other window redisplay routines
 ;; will move the start of the window in the wrong direction.)
@@ -883,22 +882,21 @@ returned by `follow-windows-start-end'."
 (defun follow-select-if-visible (dest win-start-end)
   "Select and return a window, if DEST is visible in it.
 Return the selected window."
 (defun follow-select-if-visible (dest win-start-end)
   "Select and return a window, if DEST is visible in it.
 Return the selected window."
-  (let (win win-end wse)
+  (let (win wse)
     (while (and (not win) win-start-end)
       ;; Don't select a window that was just moved. This makes it
       ;; possible to later select the last window after a
       ;; `end-of-buffer' command.
       (setq wse (car win-start-end))
       (when (follow-pos-visible dest (car wse) win-start-end)
     (while (and (not win) win-start-end)
       ;; Don't select a window that was just moved. This makes it
       ;; possible to later select the last window after a
       ;; `end-of-buffer' command.
       (setq wse (car win-start-end))
       (when (follow-pos-visible dest (car wse) win-start-end)
-       (setq win (car wse)
-             win-end (nth 2 wse))
+       (setq win (car wse))
        (select-window win))
       (setq win-start-end (cdr win-start-end)))
     win))
 
 ;; Lets select a window showing the end. Make sure we only select it if
 ;; it wasn't just moved here. (I.e. M-> shall not unconditionally place
        (select-window win))
       (setq win-start-end (cdr win-start-end)))
     win))
 
 ;; Lets select a window showing the end. Make sure we only select it if
 ;; it wasn't just moved here. (I.e. M-> shall not unconditionally place
-;; the point in the selected window.)
+;; point in the selected window.)
 ;;
 ;; (Compatibility kludge: in Emacs `window-end' is equal to `point-max';
 ;; in XEmacs, it is equal to `point-max + 1'. Should I really bother
 ;;
 ;; (Compatibility kludge: in Emacs `window-end' is equal to `point-max';
 ;; in XEmacs, it is equal to `point-max + 1'. Should I really bother
@@ -924,10 +922,10 @@ Return the selected window."
     win))
 
 
     win))
 
 
-;; Select a window that will display the point if the windows would
+;; Select a window that will display point if the windows would
 ;; be redisplayed with the first window fixed. This is useful for
 ;; example when the user has pressed return at the bottom of a window
 ;; be redisplayed with the first window fixed. This is useful for
 ;; example when the user has pressed return at the bottom of a window
-;; as the point is not visible in any window.
+;; as point is not visible in any window.
 
 (defun follow-select-if-visible-from-first (dest windows)
   "Try to select one of WINDOWS without repositioning the topmost window.
 
 (defun follow-select-if-visible-from-first (dest windows)
   "Try to select one of WINDOWS without repositioning the topmost window.
@@ -969,7 +967,7 @@ Otherwise, return nil."
 
 (defun follow-redisplay (&optional windows win preserve-win)
   "Reposition the WINDOWS around WIN.
 
 (defun follow-redisplay (&optional windows win preserve-win)
   "Reposition the WINDOWS around WIN.
-Should the point be too close to the roof we redisplay everything
+Should point be too close to the roof we redisplay everything
 from the top.  WINDOWS should contain a list of windows to
 redisplay; it is assumed that WIN is a member of the list.
 Should WINDOWS be nil, the windows displaying the
 from the top.  WINDOWS should contain a list of windows to
 redisplay; it is assumed that WIN is a member of the list.
 Should WINDOWS be nil, the windows displaying the
@@ -1083,7 +1081,7 @@ should be a member of WINDOWS, starts at position START."
 This is done by reading and rewriting the start position of
 non-first windows in Follow mode."
   (let* ((orig-buffer (current-buffer))
 This is done by reading and rewriting the start position of
 non-first windows in Follow mode."
   (let* ((orig-buffer (current-buffer))
-        (top (frame-first-window (selected-frame)))
+        (top (frame-first-window))
         (win top)
         who) ; list of (buffer . frame)
     ;; If the only window in the frame is a minibuffer
         (win top)
         who) ; list of (buffer . frame)
     ;; If the only window in the frame is a minibuffer
@@ -1124,156 +1122,157 @@ non-first windows in Follow mode."
       (with-current-buffer (window-buffer win)
        (unless (and (symbolp this-command)
                     (get this-command 'follow-mode-use-cache))
       (with-current-buffer (window-buffer win)
        (unless (and (symbolp this-command)
                     (get this-command 'follow-mode-use-cache))
-         (setq follow-windows-start-end-cache nil)))
-      (follow-adjust-window win (point)))))
-
-(defun follow-adjust-window (win dest)
-  ;; Adjust the window WIN and its followers.
-  (with-current-buffer (window-buffer win)
-    (when (and follow-mode
-              (not (window-minibuffer-p win)))
-      (let* ((windows (follow-all-followers win))
-            (win-start-end (progn
-                             (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))
-            selected-window-up-to-date)
-       (unless (and aligned visible)
          (setq follow-windows-start-end-cache nil))
          (setq follow-windows-start-end-cache nil))
+        (follow-adjust-window win)))))
 
 
-       ;; Select a window to display point.
-       (unless follow-internal-force-redisplay
-         (if (eq dest (point-max))
-             ;; Be careful at point-max: the display can be aligned
-             ;; while DEST can be visible in several windows.
-             (cond
-              ;; Select the current window, but only when the display
-              ;; is correct. (When inserting characters in a tail
-              ;; window, the display is not correct, as they are
-              ;; shown twice.)
-              ;;
-              ;; Never stick to the current window after a deletion.
-              ;; Otherwise, when typing `DEL' in a window showing
-              ;; only the end of the file, a character would be
-              ;; removed from the window above, which is very
-              ;; unintuitive.
-              ((and visible
-                    aligned
-                    (not (memq this-command
-                               '(backward-delete-char
-                                 delete-backward-char
-                                 backward-delete-char-untabify
-                                 kill-region))))
-               (follow-debug-message "Max: same"))
-              ;; If the end is visible, and the window doesn't
-              ;; seems like it just has been moved, select it.
-              ((follow-select-if-end-visible win-start-end)
-               (follow-debug-message "Max: end visible")
-               (setq visible t aligned nil)
-               (goto-char dest))
-              ;; Just show the end...
-              (t
-               (follow-debug-message "Max: default")
-               (select-window (car (last windows)))
-               (goto-char dest)
-               (setq visible nil aligned nil)))
-
-           ;; We're not at the end, here life is much simpler.
-           (cond
-            ;; This is the normal case!
-            ;; It should be optimized for speed.
-            ((and visible aligned)
-             (follow-debug-message "same"))
-            ;; Pick a position in any window.  If the display is ok,
-            ;; this picks the `correct' window.
-            ((follow-select-if-visible dest win-start-end)
-             (follow-debug-message "visible")
-             (goto-char dest)
-             ;; Perform redisplay, in case line is partially visible.
-             (setq visible nil))
-            ;; Not visible anywhere else, lets pick this one.
-            (visible
-             (follow-debug-message "visible in selected."))
-            ;; If DEST is before the first window start, select the
-            ;; first window.
-            ((< dest (nth 1 (car win-start-end)))
-             (follow-debug-message "before first")
-             (select-window (car windows))
-             (goto-char dest)
-             (setq visible nil aligned nil))
-            ;; If we can position the cursor without moving the first
-            ;; window, do it. This is the case that catches `RET' at
-            ;; the bottom of a window.
-            ((follow-select-if-visible-from-first dest windows)
-             (follow-debug-message "Below first")
-             (setq visible t aligned t))
-            ;; None of the above.  Stick to the selected window.
-            (t
-             (follow-debug-message "None")
-             (setq visible nil aligned nil))))
-
-         ;; If a new window was selected, make sure that the old is
-         ;; not scrolled when the point is outside the window.
-         (unless (eq win (selected-window))
-           (let ((p (window-point win)))
-             (set-window-start win (window-start win) nil)
-             (set-window-point win p))))
-
-       (unless visible
-         ;; If point may not be visible in the selected window,
-         ;; perform a redisplay; this ensures scrolling.
-         (let ((opoint (point)))
-           (redisplay)
-           ;; If this `redisplay' moved point, we got clobbered by a
-           ;; previous call to `set-window-start'.  Try again.
-           (when (/= (point) opoint)
-             (goto-char opoint)
-             (redisplay)))
-
-         (setq selected-window-up-to-date t)
-         (follow-avoid-tail-recenter)
-         (setq win-start-end (follow-windows-start-end windows)
-               follow-windows-start-end-cache nil
-               aligned nil))
-
-       ;; Now redraw the windows around the selected window.
-       (unless (and (not follow-internal-force-redisplay)
-                    (or aligned
-                        (follow-windows-aligned-p win-start-end))
-                    (follow-point-visible-all-windows-p win-start-end))
-         (setq follow-internal-force-redisplay nil)
-         (follow-redisplay windows (selected-window)
-                           selected-window-up-to-date)
-         (setq win-start-end (follow-windows-start-end windows)
-               follow-windows-start-end-cache nil)
-         ;; The point can ends up in another window when DEST is at
-         ;; the beginning of the buffer and the selected window is
-         ;; not the first.  It can also happen when long lines are
-         ;; used and there is a big difference between the width of
-         ;; the windows.  (When scrolling one line in a wide window
-         ;; which will cause a move larger that an entire small
-         ;; window.)
-         (unless (follow-pos-visible dest win win-start-end)
-           (follow-select-if-visible dest win-start-end)
-           (goto-char dest)))
-
-       ;; If the region is visible, make it look good when spanning
-       ;; multiple windows.
-       (when (region-active-p)
-         (follow-maximize-region
-          (selected-window) windows win-start-end)))
-
-      ;; Whether or not the buffer was in follow mode, update windows
-      ;; displaying the tail so that Emacs won't recenter them.
-      (follow-avoid-tail-recenter))))
+(defun follow-adjust-window (win)
+  ;; Adjust the window WIN and its followers.
+  (cl-assert (eq (window-buffer win) (current-buffer)))
+  (when (and follow-mode
+             (not (window-minibuffer-p win)))
+    (let* ((dest (point))
+           (windows (follow-all-followers win))
+           (win-start-end (progn
+                            (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))
+           selected-window-up-to-date)
+      (unless (and aligned visible)
+        (setq follow-windows-start-end-cache nil))
+
+      ;; Select a window to display point.
+      (unless follow-internal-force-redisplay
+        (if (eq dest (point-max))
+            ;; Be careful at point-max: the display can be aligned
+            ;; while DEST can be visible in several windows.
+            (cond
+             ;; Select the current window, but only when the display
+             ;; is correct. (When inserting characters in a tail
+             ;; window, the display is not correct, as they are
+             ;; shown twice.)
+             ;;
+             ;; Never stick to the current window after a deletion.
+             ;; Otherwise, when typing `DEL' in a window showing
+             ;; only the end of the file, a character would be
+             ;; removed from the window above, which is very
+             ;; unintuitive.
+             ((and visible
+                   aligned
+                   (not (memq this-command
+                              '(backward-delete-char
+                                delete-backward-char
+                                backward-delete-char-untabify
+                                kill-region))))
+              (follow-debug-message "Max: same"))
+             ;; If the end is visible, and the window doesn't
+             ;; seems like it just has been moved, select it.
+             ((follow-select-if-end-visible win-start-end)
+              (follow-debug-message "Max: end visible")
+              (setq visible t aligned nil)
+              (goto-char dest))
+             ;; Just show the end...
+             (t
+              (follow-debug-message "Max: default")
+              (select-window (car (last windows)))
+              (goto-char dest)
+              (setq visible nil aligned nil)))
+
+          ;; We're not at the end, here life is much simpler.
+          (cond
+           ;; This is the normal case!
+           ;; It should be optimized for speed.
+           ((and visible aligned)
+            (follow-debug-message "same"))
+           ;; Pick a position in any window.  If the display is ok,
+           ;; this picks the `correct' window.
+           ((follow-select-if-visible dest win-start-end)
+            (follow-debug-message "visible")
+            (goto-char dest)
+            ;; Perform redisplay, in case line is partially visible.
+            (setq visible nil))
+           ;; Not visible anywhere else, lets pick this one.
+           (visible
+            (follow-debug-message "visible in selected."))
+           ;; If DEST is before the first window start, select the
+           ;; first window.
+           ((< dest (nth 1 (car win-start-end)))
+            (follow-debug-message "before first")
+            (select-window (car windows))
+            (goto-char dest)
+            (setq visible nil aligned nil))
+           ;; If we can position the cursor without moving the first
+           ;; window, do it. This is the case that catches `RET' at
+           ;; the bottom of a window.
+           ((follow-select-if-visible-from-first dest windows)
+            (follow-debug-message "Below first")
+            (setq visible t aligned t))
+           ;; None of the above.  Stick to the selected window.
+           (t
+            (follow-debug-message "None")
+            (setq visible nil aligned nil))))
+
+        ;; If a new window was selected, make sure that the old is
+        ;; not scrolled when point is outside the window.
+        (unless (eq win (selected-window))
+          (let ((p (window-point win)))
+            (set-window-start win (window-start win) nil)
+            (set-window-point win p))))
+
+      (unless visible
+        ;; If point may not be visible in the selected window,
+        ;; perform a redisplay; this ensures scrolling.
+        (let ((opoint (point)))
+          (redisplay)
+          ;; If this `redisplay' moved point, we got clobbered by a
+          ;; previous call to `set-window-start'.  Try again.
+          (when (/= (point) opoint)
+            (goto-char opoint)
+            (redisplay)))
+
+        (setq selected-window-up-to-date t)
+        (follow-avoid-tail-recenter)
+        (setq win-start-end (follow-windows-start-end windows)
+              follow-windows-start-end-cache nil
+              aligned nil))
+
+      ;; Now redraw the windows around the selected window.
+      (unless (and (not follow-internal-force-redisplay)
+                   (or aligned
+                       (follow-windows-aligned-p win-start-end))
+                   (follow-point-visible-all-windows-p win-start-end))
+        (setq follow-internal-force-redisplay nil)
+        (follow-redisplay windows (selected-window)
+                          selected-window-up-to-date)
+        (setq win-start-end (follow-windows-start-end windows)
+              follow-windows-start-end-cache nil)
+        ;; Point can end up in another window when DEST is at
+        ;; the beginning of the buffer and the selected window is
+        ;; not the first.  It can also happen when long lines are
+        ;; used and there is a big difference between the width of
+        ;; the windows.  (When scrolling one line in a wide window
+        ;; which will cause a move larger that an entire small
+        ;; window.)
+        (unless (follow-pos-visible dest win win-start-end)
+          (follow-select-if-visible dest win-start-end)
+          (goto-char dest)))
+
+      ;; If the region is visible, make it look good when spanning
+      ;; multiple windows.
+      (when (region-active-p)
+        (follow-maximize-region
+         (selected-window) windows win-start-end)))
+
+    ;; Whether or not the buffer was in follow mode, update windows
+    ;; displaying the tail so that Emacs won't recenter them.
+    (follow-avoid-tail-recenter)))
 
 ;;; The region
 
 ;; Tries to make the highlighted area representing the region look
 ;; good when spanning several windows.
 ;;
 
 ;;; The region
 
 ;; Tries to make the highlighted area representing the region look
 ;; good when spanning several windows.
 ;;
-;; Not perfect, as the point can't be placed at window end, only at
+;; Not perfect, as point can't be placed at window end, only at
 ;; end-1.  This will highlight a little bit in windows above
 ;; the current.
 
 ;; end-1.  This will highlight a little bit in windows above
 ;; the current.
 
@@ -1298,6 +1297,12 @@ non-first windows in Follow mode."
 ;; This handles the case where the user drags the scroll bar of a
 ;; non-selected window whose buffer is in Follow mode.
 
 ;; This handles the case where the user drags the scroll bar of a
 ;; non-selected window whose buffer is in Follow mode.
 
+(declare-function scroll-bar-toolkit-scroll "scroll-bar" (event))
+(declare-function scroll-bar-drag "scroll-bar" (event))
+(declare-function scroll-bar-scroll-up "scroll-bar" (event))
+(declare-function scroll-bar-scroll-down "scroll-bar" (event))
+(declare-function mwheel-scroll "mwheel" (event))
+
 (defun follow-scroll-bar-toolkit-scroll (event)
   (interactive "e")
   (scroll-bar-toolkit-scroll event)
 (defun follow-scroll-bar-toolkit-scroll (event)
   (interactive "e")
   (scroll-bar-toolkit-scroll event)