emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Emacs-diffs] emacs-25 df32db2: Merge branch 'scratch/follow' into emacs


From: Alan Mackenzie
Subject: [Emacs-diffs] emacs-25 df32db2: Merge branch 'scratch/follow' into emacs-25
Date: Sun, 20 Dec 2015 12:50:19 +0000

branch: emacs-25
commit df32db2f5f3dcad4b2b16fd52e51e1c7bd846609
Merge: 6a8a41c a72a9fb
Author: Alan Mackenzie <address@hidden>
Commit: Alan Mackenzie <address@hidden>

    Merge branch 'scratch/follow' into emacs-25
    
    This allows Isearch, etc., to work well when Follow Mode is active.
---
 doc/lispref/functions.texi |   17 +++--
 doc/lispref/positions.texi |   12 +++
 doc/lispref/windows.texi   |   88 ++++++++++++++++++++++
 lisp/follow.el             |  176 +++++++++++++++++++++++++++++++++++++++++++-
 lisp/isearch.el            |  125 +++++++++++++++++--------------
 lisp/replace.el            |    8 ++-
 lisp/textmodes/ispell.el   |   79 ++++++++++++++++++--
 lisp/window.el             |  146 ++++++++++++++++++++++++++++++++++++
 8 files changed, 577 insertions(+), 74 deletions(-)

diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi
index 8835667..7cc041fa7 100644
--- a/doc/lispref/functions.texi
+++ b/doc/lispref/functions.texi
@@ -861,15 +861,18 @@ into a list.  @code{mapc} always returns @var{sequence}.
 
 @defun mapconcat function sequence separator
 @code{mapconcat} applies @var{function} to each element of
address@hidden: the results, which must be strings, are concatenated.
-Between each pair of result strings, @code{mapconcat} inserts the string
address@hidden  Usually @var{separator} contains a space or comma or
-other suitable punctuation.
address@hidden; the results, which must be sequences of characters
+(strings, vectors, or lists), are concatenated into a single string
+return value.  Between each pair of result sequences, @code{mapconcat}
+inserts the characters from @var{separator}, which also must be a
+string, or a vector or list of characters.  @xref{Sequences Arrays
+Vectors}.
 
 The argument @var{function} must be a function that can take one
-argument and return a string.  The argument @var{sequence} can be any
-kind of sequence except a char-table; that is, a list, a vector, a
-bool-vector, or a string.
+argument and returns a sequence of characters: a string, a vector, or
+a list.  The argument @var{sequence} can be any kind of sequence
+except a char-table; that is, a list, a vector, a bool-vector, or a
+string.
 
 @example
 @group
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index 72b76ce..9daf5ce 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -572,6 +572,18 @@ The value returned is the window line number point has 
moved to, with
 the top line in the window numbered 0.
 @end deffn
 
address@hidden move-to-window-group-line-function
address@hidden move-to-window-group-line count
+This function is like @code{move-to-window-line}, except that when the
+selected window is a part of a group of windows (@pxref{Window
+Group}), @code{move-to-window-group-line} will move to a position with
+respect to the entire group, not just the single window.  This
+condition holds when the buffer local variable
address@hidden is set to a function.  In
+this case, @code{move-to-window-group-line} calls the function with
+the argument @var{count}, then returns its result.
address@hidden defun
+
 @defun compute-motion from frompos to topos width offsets window
 This function scans the current buffer, calculating screen positions.
 It scans the buffer forward from position @var{from}, assuming that is
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 5c7947e..e45201b 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -133,6 +133,30 @@ This function returns the selected window (which is always 
a live
 window).
 @end defun
 
address@hidden Group}Sometimes several windows collectively and
+cooperatively display a buffer, for example, under the management of
+Follow Mode (@pxref{Follow Mode,,, emacs}), where the windows together
+display a bigger portion of the buffer than one window could alone.
+It is often useful to consider such a @dfn{window group} as a single
+entity.  Several functions such as @code{window-group-start}
+(@pxref{Window Start and End}) allow you to do this by supplying, as
+an argument, one of the windows as a stand in for the whole group.
+
address@hidden selected-window-group
address@hidden selected-window-group-function
+When the selected window is a member of a group of windows, this
+function returns a list of the windows in the group, ordered such that
+the first window in the list is displaying the earliest part of the
+buffer, and so on.  Otherwise the function returns a list containing
+just the selected window.
+
+The selected window is considered part of a group when the buffer
+local variable @code{selected-window-group-function} is set to a
+function.  In this case, @code{selected-window-group} calls it with no
+arguments and returns its result (which should be the list of windows
+in the group).
address@hidden defun
+
 @node Windows and Frames
 @section Windows and Frames
 
@@ -3098,6 +3122,17 @@ window-start position; if you move point, do not expect 
the window-start
 position to change in response until after the next redisplay.
 @end defun
 
address@hidden window-group-start &optional window
address@hidden window-group-start-function
+This function is like @code{window-start}, except that when
address@hidden is a part of a group of windows (@pxref{Window Group}),
address@hidden returns the start position of the entire
+group.  This condition holds when the buffer local variable
address@hidden is set to a function.  In this
+case, @code{window-group-start} calls the function with the single
+argument @var{window}, then returns its result.
address@hidden defun
+
 @cindex window end position
 @defun window-end &optional window update
 This function returns the position where display of its buffer ends in
@@ -3124,6 +3159,18 @@ way real redisplay would do.  It does not alter the
 text will end if scrolling is not required.
 @end defun
 
address@hidden window-group-end-function
address@hidden window-group-end window update
+This function is like @code{window-end}, except that when @var{window}
+is a part of a group of windows (@pxref{Window Group}),
address@hidden returns the end position of the entire group.
+This condition holds when the buffer local variable
address@hidden is set to a function.  In this case,
address@hidden calls the function with the two arguments
address@hidden and @var{update}, then returns its result.  The argument
address@hidden has the same meaning as in @code{window-end}.
address@hidden defun
+
 @defun set-window-start window position &optional noforce
 This function sets the display-start position of @var{window} to
 @var{position} in @var{window}'s buffer.  It returns @var{position}.
@@ -3187,6 +3234,19 @@ off screen at the next redisplay, then redisplay 
computes a new window-start
 position that works well with point, and thus @var{position} is not used.
 @end defun
 
address@hidden set-window-group-start-function
address@hidden set-window-group-start window position &optional noforce
+This function is like @code{set-window-start}, except that when
address@hidden is a part of a group of windows (@pxref{Window Group}),
address@hidden sets the start position of the entire
+group.  This condition holds when the buffer local variable
address@hidden is set to a function.  In this
+case, @code{set-window-group-start} calls the function with the three
+arguments @var{window}, @var{position}, and @var{noforce}, then
+returns its result.  The arguments @var{position} and @var{noforce} in
+this function have the same meaning as in @code{set-window-start}.
address@hidden defun
+
 @defun pos-visible-in-window-p &optional position window partially
 This function returns address@hidden if @var{position} is within the
 range of text currently visible on the screen in @var{window}.  It
@@ -3228,6 +3288,21 @@ Here is an example:
 @end example
 @end defun
 
address@hidden pos-visible-in-window-group-p-function
address@hidden pos-visible-in-window-group-p &optional position window partially
+This function is like @code{pos-visible-in-window-p}, except that when
address@hidden is a part of a group of windows (@pxref{Window Group}),
address@hidden tests the visibility of @var{pos}
+in the entire group, not just in the single @var{window}.  This
+condition holds when the buffer local variable
address@hidden is set to a function.
+In this case @code{pos-visible-in-window-group-p} calls the function
+with the three arguments @var{position}, @var{window}, and
address@hidden, then returns its result.  The arguments
address@hidden and @var{partially} have the same meaning as in
address@hidden
address@hidden defun
+
 @defun window-line-height &optional line window
 This function returns the height of text line @var{line} in
 @var{window}.  If @var{line} is one of @code{header-line} or
@@ -3471,6 +3546,19 @@ the top of the window.  The command 
@code{recenter-top-bottom} offers
 a more convenient way to achieve this.
 @end deffn
 
address@hidden recenter-window-group-function
address@hidden recenter-window-group &optional count
+This function is like @code{recenter}, except that when the selected
+window is part of a group of windows (@pxref{Window Group}),
address@hidden scrolls the entire group.  This condition
+holds when the buffer local variable
address@hidden is set to a function.  In this
+case, @code{recenter-window-group} calls the function with the
+argument @var{count}, then returns its result.  The argument
address@hidden has the same meaning as in @code{recenter}, but with
+respect to the entire window group.
address@hidden defun
+
 @defopt recenter-redisplay
 If this variable is address@hidden, calling @code{recenter} with a
 @code{nil} argument redraws the frame.  The default value is
diff --git a/lisp/follow.el b/lisp/follow.el
index 37de923..71e8824 100644
--- a/lisp/follow.el
+++ b/lisp/follow.el
@@ -421,7 +421,21 @@ Keys specific to Follow mode:
       (progn
        (add-hook 'compilation-filter-hook 'follow-align-compilation-windows t 
t)
        (add-hook 'post-command-hook 'follow-post-command-hook t)
-       (add-hook 'window-size-change-functions 'follow-window-size-change t))
+       (add-hook 'window-size-change-functions 'follow-window-size-change t)
+        (add-hook 'after-change-functions 'follow-after-change nil t)
+        (add-hook 'isearch-update-post-hook 'follow-post-command-hook nil t)
+        (add-hook 'replace-update-post-hook 'follow-post-command-hook nil t)
+        (add-hook 'ispell-update-post-hook 'follow-post-command-hook nil t)
+
+        (setq window-group-start-function 'follow-window-start)
+        (setq window-group-end-function 'follow-window-end)
+        (setq set-window-group-start-function 'follow-set-window-start)
+        (setq recenter-window-group-function 'follow-recenter)
+        (setq pos-visible-in-window-group-p-function
+              'follow-pos-visible-in-window-p)
+        (setq selected-window-group-function 'follow-all-followers)
+        (setq move-to-window-group-line-function 'follow-move-to-window-line))
+
     ;; Remove globally-installed hook functions only if there is no
     ;; other Follow mode buffer.
     (let ((buffers (buffer-list))
@@ -432,6 +446,19 @@ Keys specific to Follow mode:
       (unless following
        (remove-hook 'post-command-hook 'follow-post-command-hook)
        (remove-hook 'window-size-change-functions 'follow-window-size-change)))
+
+    (kill-local-variable 'move-to-window-group-line-function)
+    (kill-local-variable 'selected-window-group-function)
+    (kill-local-variable 'pos-visible-in-window-group-p-function)
+    (kill-local-variable 'recenter-window-group-function)
+    (kill-local-variable 'set-window-group-start-function)
+    (kill-local-variable 'window-group-end-function)
+    (kill-local-variable 'window-group-start-function)
+
+    (remove-hook 'ispell-update-post-hook 'follow-post-command-hook t)
+    (remove-hook 'replace-update-post-hook 'follow-post-command-hook t)
+    (remove-hook 'isearch-update-post-hook 'follow-post-command-hook t)
+    (remove-hook 'after-change-functions 'follow-after-change t)
     (remove-hook 'compilation-filter-hook 'follow-align-compilation-windows 
t)))
 
 (defun follow-find-file-hook ()
@@ -1015,6 +1042,10 @@ Otherwise, return nil."
 ;; is nil.  Start every window directly after the end of the previous
 ;; window, to make sure long lines are displayed correctly.
 
+(defvar follow-start-end-invalid t
+  "When non-nil, indicates `follow-windows-start-end-cache' is invalid.")
+(make-variable-buffer-local 'follow-start-end-invalid)
+
 (defun follow-redisplay (&optional windows win preserve-win)
   "Reposition the WINDOWS around WIN.
 Should point be too close to the roof we redisplay everything
@@ -1047,7 +1078,8 @@ repositioning the other windows."
     (dolist (w windows)
       (unless (and preserve-win (eq w win))
        (set-window-start w start))
-      (setq start (car (follow-calc-win-end w))))))
+      (setq start (car (follow-calc-win-end w))))
+    (setq follow-start-end-invalid nil)))
 
 (defun follow-estimate-first-window-start (windows win start)
   "Estimate the position of the first window.
@@ -1443,6 +1475,146 @@ non-first windows in Follow mode."
 
 (add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t)
 
+;;; Low level window start and end.
+
+;; These routines are the Follow Mode versions of the low level
+;; functions described on page "Window Start and End" of the elisp
+;; manual, e.g. `window-group-start'.  The aim is to be able to handle
+;; Follow Mode windows by replacing `window-start' by
+;; `window-group-start', etc.
+
+(defun follow-after-change (_beg _end _old-len)
+  "After change function: set `follow-start-end-invalid'."
+  (setq follow-start-end-invalid t))
+
+(defun follow-window-start (&optional window)
+  "Return position at which display currently starts in the
+Follow Mode group of windows which includes WINDOW.
+
+WINDOW must be a live window and defaults to the selected one.
+This is updated by redisplay or by calling
+`follow-set-window-start'."
+  (let ((windows (follow-all-followers window)))
+    (window-start (car windows))))
+
+(defun follow-window-end (&optional window update)
+  "Return position at which display currently ends in the Follow
+  Mode group of windows which includes WINDOW.
+
+  WINDOW must be a live window and defaults to the selected one.
+  This is updated by redisplay, when it runs to completion.
+  Simply changing the buffer text or setting `window-start' does
+  not update this value.
+
+  Return nil if there is no recorded value.  (This can happen if
+  the last redisplay of WINDOW was preempted, and did not
+  finish.)  If UPDATE is non-nil, compute the up-to-date position
+  if it isn't already recorded."
+  (let* ((windows (follow-all-followers window))
+         (last (car (last windows))))
+    (when (and update follow-start-end-invalid)
+      (follow-redisplay windows (car windows)))
+    (window-end last update)))
+
+(defun follow-set-window-start (window pos &optional noforce)
+  "Make display in the Follow Mode group of windows which includes
+WINDOW start at position POS in WINDOW's buffer.
+
+WINDOW must be a live window and defaults to the selected one.  Return
+POS.  Optional third arg NOFORCE non-nil inhibits next redisplay from
+overriding motion of point in order to display at this exact start."
+  (let ((windows (follow-all-followers window)))
+    (setq follow-start-end-invalid t)
+    (set-window-start (car windows) pos noforce)))
+
+(defun follow-pos-visible-in-window-p (&optional pos window partially)
+  "Return non-nil if position POS is currently on the frame in one of
+  the windows in the Follow Mode group which includes WINDOW.
+
+WINDOW must be a live window and defaults to the selected one.
+
+Return nil if that position is scrolled vertically out of view.  If a
+character is only partially visible, nil is returned, unless the
+optional argument PARTIALLY is non-nil.  If POS is only out of view
+because of horizontal scrolling, return non-nil.  If POS is t, it
+specifies the position of the last visible glyph in WINDOW.  POS
+defaults to point in WINDOW; WINDOW defaults to the selected window.
+
+If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil,
+the return value is a list of 2 or 6 elements (X Y [RTOP RBOT ROWH VPOS]),
+where X and Y are the pixel coordinates relative to the top left corner
+of the actual window containing it.  The remaining elements are
+omitted if the character after POS is fully visible; otherwise, RTOP
+and RBOT are the number of pixels off-window at the top and bottom of
+the screen line (\"row\") containing POS, ROWH is the visible height
+of that row, and VPOS is the row number \(zero-based)."
+  (let* ((windows (follow-all-followers window))
+         (last (car (last windows))))
+    (when follow-start-end-invalid
+      (follow-redisplay windows (car windows)))
+    (let* ((cache (follow-windows-start-end windows))
+           (last-elt (car (last cache)))
+           our-pos pertinent-elt)
+      (setq pertinent-elt
+            (if (eq pos t)
+                last-elt
+              (setq our-pos (or pos (point)))
+              (catch 'element
+                (while cache
+                  (when (< our-pos (nth 2 (car cache)))
+                    (throw 'element (car cache)))
+                  (setq cache (cdr cache)))
+                last-elt)))
+      (pos-visible-in-window-p our-pos (car pertinent-elt) partially))))
+
+(defun follow-move-to-window-line (arg)
+  "Position point relative to the Follow mode group containing the selected 
window.
+ARG nil means position point at center of the window group.
+Else, ARG specifies vertical position within the window group;
+zero means top of the first window in the group, negative means
+  relative to bottom of the last window in the group."
+  (let* ((windows (follow-all-followers))
+         (start-end (follow-windows-start-end windows))
+         (rev-start-end (reverse start-end))
+         (lines 0)
+         middle-window elt count)
+    (select-window
+     (cond
+      ((null arg)
+       (setq rev-start-end (nthcdr (/ (length windows) 2) rev-start-end))
+       (prog1 (car (car rev-start-end))
+         (while (setq rev-start-end (cdr rev-start-end))
+           (setq elt (car rev-start-end)
+                 count (count-screen-lines (cadr elt) (nth 2 elt)
+                                           nil (car elt))
+                 lines (+ lines count)))))
+      ((>= arg 0)
+       (while (and (cdr start-end)
+                   (progn
+                     (setq elt (car start-end)
+                           count (count-screen-lines (cadr elt) (nth 2 elt)
+                                                     nil (car elt)))
+                     (>= arg count)))
+         (setq arg (- arg count)
+               lines (+ lines count)
+               start-end (cdr start-end)))
+       (car (car start-end)))
+      (t                                ; (< arg 0)
+       (while (and (cadr rev-start-end)
+                   (progn
+                     (setq elt (car rev-start-end)
+                           count (count-lines (cadr elt) (nth 2 elt)))
+                     (<= arg (- count))))
+         (setq arg (+ arg count)
+               rev-start-end (cdr rev-start-end)))
+       (prog1 (car (car rev-start-end))
+         (while (setq rev-start-end (cdr rev-start-end))
+           (setq elt (car rev-start-end)
+                 count (count-screen-lines (cadr elt) (nth 2 elt)
+                                           nil (car elt))
+                 lines (+ lines count)))))))
+    (+ lines (move-to-window-line arg))))
+
 ;;; Profile support
 
 ;; The following (non-evaluated) section can be used to
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 8c98d36..05dc293 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -961,7 +961,8 @@ used to set the value of `isearch-regexp-function'."
 
 (defun isearch-update ()
   "This is called after every isearch command to update the display.
-The last thing it does is to run `isearch-update-post-hook'."
+The second last thing it does is to run `isearch-update-post-hook'.
+The last thing is to trigger a new round of lazy highlighting."
   (unless (eq (current-buffer) isearch--current-buffer)
     (when (buffer-live-p isearch--current-buffer)
       (with-current-buffer isearch--current-buffer
@@ -978,12 +979,10 @@ The last thing it does is to run 
`isearch-update-post-hook'."
           (null executing-kbd-macro))
       (progn
         (if (not (input-pending-p))
-           (if isearch-message-function
-               (funcall isearch-message-function)
-             (isearch-message)))
+          (funcall (or isearch-message-function #'isearch-message)))
         (if (and isearch-slow-terminal-mode
                  (not (or isearch-small-window
-                          (pos-visible-in-window-p))))
+                          (pos-visible-in-window-group-p))))
             (let ((found-point (point)))
               (setq isearch-small-window t)
               (move-to-window-line 0)
@@ -1004,10 +1003,10 @@ The last thing it does is to run 
`isearch-update-post-hook'."
          (let ((current-scroll (window-hscroll))
                visible-p)
            (set-window-hscroll (selected-window) isearch-start-hscroll)
-           (setq visible-p (pos-visible-in-window-p nil nil t))
+           (setq visible-p (pos-visible-in-window-group-p nil nil t))
            (if (or (not visible-p)
                    ;; When point is not visible because of hscroll,
-                   ;; pos-visible-in-window-p returns non-nil, but
+                   ;; pos-visible-in-window-group-p returns non-nil, but
                    ;; the X coordinate it returns is 1 pixel beyond
                    ;; the last visible one.
                    (>= (car visible-p) (window-body-width nil t)))
@@ -1020,12 +1019,12 @@ The last thing it does is to run 
`isearch-update-post-hook'."
   (setq ;; quit-flag nil  not for isearch-mode
    isearch-adjusted nil
    isearch-yank-flag nil)
-  (when isearch-lazy-highlight
-    (isearch-lazy-highlight-new-loop))
   ;; We must prevent the point moving to the end of composition when a
   ;; part of the composition has just been searched.
   (setq disable-point-adjustment t)
-  (run-hooks 'isearch-update-post-hook))
+  (run-hooks 'isearch-update-post-hook)
+  (when isearch-lazy-highlight
+    (isearch-lazy-highlight-new-loop)))
 
 (defun isearch-done (&optional nopush edit)
   "Exit Isearch mode.
@@ -1058,7 +1057,7 @@ NOPUSH is t and EDIT is t."
   (setq minibuffer-message-timeout isearch-original-minibuffer-message-timeout)
   (isearch-dehighlight)
   (lazy-highlight-cleanup lazy-highlight-cleanup)
-  (let ((found-start (window-start))
+  (let ((found-start (window-group-start))
        (found-point (point)))
     (when isearch-window-configuration
       (set-window-configuration isearch-window-configuration)
@@ -1068,7 +1067,7 @@ NOPUSH is t and EDIT is t."
        ;; This has an annoying side effect of clearing the last_modiff
        ;; field of the window, which can cause unwanted scrolling,
        ;; so don't do it unless truly necessary.
-       (set-window-start (selected-window) found-start t))))
+       (set-window-group-start (selected-window) found-start t))))
 
   (setq isearch-mode nil)
   (if isearch-input-method-local-p
@@ -1299,13 +1298,6 @@ You can update the global isearch variables by setting 
new values to
          (unwind-protect
              (progn ,@body)
 
-           ;; Set point at the start (end) of old match if forward (backward),
-           ;; so after exiting minibuffer isearch resumes at the start (end)
-           ;; of this match and can find it again.
-           (if (and old-other-end (eq old-point (point))
-                    (eq isearch-forward isearch-new-forward))
-               (goto-char old-other-end))
-
            ;; Always resume isearching by restarting it.
            (isearch-mode isearch-forward
                          isearch-regexp
@@ -1318,7 +1310,17 @@ You can update the global isearch variables by setting 
new values to
                  isearch-message isearch-new-message
                  isearch-forward isearch-new-forward
                  isearch-regexp-function isearch-new-regexp-function
-                 isearch-case-fold-search isearch-new-case-fold))
+                 isearch-case-fold-search isearch-new-case-fold)
+
+           ;; Restore the minibuffer message before moving point.
+            (funcall (or isearch-message-function #'isearch-message) nil t)
+
+           ;; Set point at the start (end) of old match if forward (backward),
+           ;; so after exiting minibuffer isearch resumes at the start (end)
+           ;; of this match and can find it again.
+           (if (and old-other-end (eq old-point (point))
+                    (eq isearch-forward isearch-new-forward))
+               (goto-char old-other-end)))
 
          ;; Empty isearch-string means use default.
          (when (= 0 (length isearch-string))
@@ -1931,6 +1933,8 @@ If search string is empty, just beep."
                                            (length isearch-string))))
           isearch-message (mapconcat 'isearch-text-char-description
                                      isearch-string "")))
+  ;; Do the following before moving point.
+  (funcall (or isearch-message-function #'isearch-message) nil t)
   ;; Use the isearch-other-end as new starting point to be able
   ;; to find the remaining part of the search string again.
   ;; This is like what `isearch-search-and-update' does,
@@ -2107,6 +2111,8 @@ With argument, add COUNT copies of the character."
              (setq isearch-case-fold-search
                    (isearch-no-upper-case-p isearch-string isearch-regexp))))
       ;; Not regexp, not reverse, or no match at point.
+      ;; Do the following before moving point.
+      (funcall (or isearch-message-function #'isearch-message) nil t)
       (if (and isearch-other-end (not isearch-adjusted))
          (goto-char (if isearch-forward isearch-other-end
                       (min isearch-opoint
@@ -2273,10 +2279,12 @@ Return nil if it's completely visible, or if point is 
visible,
 together with as much of the search string as will fit; the symbol
 `above' if we need to scroll the text downwards; the symbol `below',
 if upwards."
-  (let ((w-start (window-start))
-        (w-end (window-end nil t))
-        (w-L1 (save-excursion (move-to-window-line 1) (point)))
-        (w-L-1 (save-excursion (move-to-window-line -1) (point)))
+  (let ((w-start (window-group-start))
+        (w-end (window-group-end nil t))
+        (w-L1 (save-excursion
+                (save-selected-window (move-to-window-group-line 1) (point))))
+        (w-L-1 (save-excursion
+                 (save-selected-window (move-to-window-group-line -1) 
(point))))
         start end)                  ; start and end of search string in buffer
     (if isearch-forward
         (setq end isearch-point  start (or isearch-other-end isearch-point))
@@ -2303,15 +2311,15 @@ the bottom."
     (if above
         (progn
           (goto-char start)
-          (recenter 0)
-          (when (>= isearch-point (window-end nil t))
+          (recenter-window-group 0)
+          (when (>= isearch-point (window-group-end nil t))
             (goto-char isearch-point)
-            (recenter -1)))
+            (recenter-window-group -1)))
       (goto-char end)
-      (recenter -1)
-      (when (< isearch-point (window-start))
+      (recenter-window-group -1)
+      (when (< isearch-point (window-group-start))
         (goto-char isearch-point)
-        (recenter 0))))
+        (recenter-window-group 0))))
   (goto-char isearch-point))
 
 (defvar isearch-pre-scroll-point nil)
@@ -2458,6 +2466,7 @@ Search is updated accordingly."
   (isearch-ring-adjust1 advance)
   (if search-ring-update
       (progn
+        (funcall (or isearch-message-function #'isearch-message) nil t)
        (isearch-search)
        (isearch-push-state)
        (isearch-update))
@@ -2530,6 +2539,13 @@ If there is no completion possible, say so and continue 
searching."
 
 (defun isearch-message (&optional c-q-hack ellipsis)
   ;; Generate and print the message string.
+
+  ;; N.B.: This function should always be called with point at the
+  ;; search point, because in certain (rare) circumstances, undesired
+  ;; scrolling can happen when point is elsewhere.  These
+  ;; circumstances are when follow-mode is active, the search string
+  ;; spans two (or several) windows, and the message about to be
+  ;; displayed will cause the echo area to expand.
   (let ((cursor-in-echo-area ellipsis)
        (m isearch-message)
        (fail-pos (isearch-fail-pos t)))
@@ -2723,9 +2739,6 @@ update the match data, and return point."
 
 (defun isearch-search ()
   ;; Do the search with the current search string.
-  (if isearch-message-function
-      (funcall isearch-message-function nil t)
-    (isearch-message nil t))
   (if (and (eq isearch-case-fold-search t) search-upper-case)
       (setq isearch-case-fold-search
            (isearch-no-upper-case-p isearch-string isearch-regexp)))
@@ -3023,6 +3036,7 @@ since they have special meaning in a regexp."
 (defvar isearch-lazy-highlight-timer nil)
 (defvar isearch-lazy-highlight-last-string nil)
 (defvar isearch-lazy-highlight-window nil)
+(defvar isearch-lazy-highlight-window-group nil)
 (defvar isearch-lazy-highlight-window-start nil)
 (defvar isearch-lazy-highlight-window-end nil)
 (defvar isearch-lazy-highlight-case-fold-search nil)
@@ -3064,8 +3078,8 @@ by other Emacs features."
              (sit-for 0)         ;make sure (window-start) is credible
              (or (not (equal isearch-string
                              isearch-lazy-highlight-last-string))
-                 (not (eq (selected-window)
-                          isearch-lazy-highlight-window))
+                 (not (memq (selected-window)
+                            isearch-lazy-highlight-window-group))
                 (not (eq isearch-lazy-highlight-case-fold-search
                          isearch-case-fold-search))
                 (not (eq isearch-lazy-highlight-regexp
@@ -3076,9 +3090,9 @@ by other Emacs features."
                          isearch-lax-whitespace))
                 (not (eq isearch-lazy-highlight-regexp-lax-whitespace
                          isearch-regexp-lax-whitespace))
-                 (not (= (window-start)
+                 (not (= (window-group-start)
                          isearch-lazy-highlight-window-start))
-                 (not (= (window-end)   ; Window may have been split/joined.
+                 (not (= (window-group-end) ; Window may have been 
split/joined.
                         isearch-lazy-highlight-window-end))
                 (not (eq isearch-forward
                          isearch-lazy-highlight-forward))
@@ -3086,7 +3100,7 @@ by other Emacs features."
                 (not (equal isearch-error
                             isearch-lazy-highlight-error))))
     ;; something important did indeed change
-    (lazy-highlight-cleanup t) ;kill old loop & remove overlays
+    (lazy-highlight-cleanup t)        ;kill old loop & remove overlays
     (setq isearch-lazy-highlight-error isearch-error)
     ;; It used to check for `(not isearch-error)' here, but actually
     ;; lazy-highlighting might find matches to highlight even when
@@ -3094,8 +3108,9 @@ by other Emacs features."
     (setq isearch-lazy-highlight-start-limit beg
          isearch-lazy-highlight-end-limit end)
     (setq isearch-lazy-highlight-window       (selected-window)
-         isearch-lazy-highlight-window-start (window-start)
-         isearch-lazy-highlight-window-end   (window-end)
+          isearch-lazy-highlight-window-group (selected-window-group)
+         isearch-lazy-highlight-window-start (window-group-start)
+         isearch-lazy-highlight-window-end   (window-group-end)
          ;; Start lazy-highlighting at the beginning of the found
          ;; match (`isearch-other-end').  If no match, use point.
          ;; One of the next two variables (depending on search direction)
@@ -3113,10 +3128,10 @@ by other Emacs features."
          isearch-lazy-highlight-regexp-lax-whitespace 
isearch-regexp-lax-whitespace
          isearch-lazy-highlight-regexp-function  isearch-regexp-function
          isearch-lazy-highlight-forward      isearch-forward)
-      (unless (equal isearch-string "")
-       (setq isearch-lazy-highlight-timer
-             (run-with-idle-timer lazy-highlight-initial-delay nil
-                                  'isearch-lazy-highlight-update)))))
+    (unless (equal isearch-string "")
+      (setq isearch-lazy-highlight-timer
+            (run-with-idle-timer lazy-highlight-initial-delay nil
+                                 'isearch-lazy-highlight-update)))))
 
 (defun isearch-lazy-highlight-search ()
   "Search ahead for the next or previous match, for lazy highlighting.
@@ -3139,13 +3154,13 @@ Attempt to do the search exactly the way the pending 
Isearch would."
                                (+ isearch-lazy-highlight-start
                                   ;; Extend bound to match whole string at 
point
                                   (1- (length 
isearch-lazy-highlight-last-string)))
-                             (window-end)))
+                             (window-group-end)))
                     (max (or isearch-lazy-highlight-start-limit (point-min))
                          (if isearch-lazy-highlight-wrapped
                              (- isearch-lazy-highlight-end
                                 ;; Extend bound to match whole string at point
                                 (1- (length 
isearch-lazy-highlight-last-string)))
-                           (window-start))))))
+                           (window-group-start))))))
        ;; Use a loop like in `isearch-search'.
        (while retry
          (setq success (isearch-search-string
@@ -3169,7 +3184,7 @@ Attempt to do the search exactly the way the pending 
Isearch would."
     (with-local-quit
       (save-selected-window
        (if (and (window-live-p isearch-lazy-highlight-window)
-                (not (eq (selected-window) isearch-lazy-highlight-window)))
+                (not (memq (selected-window) 
isearch-lazy-highlight-window-group)))
            (select-window isearch-lazy-highlight-window))
        (save-excursion
          (save-match-data
@@ -3189,12 +3204,12 @@ Attempt to do the search exactly the way the pending 
Isearch would."
                          (if isearch-lazy-highlight-forward
                              (if (= mb (if isearch-lazy-highlight-wrapped
                                            isearch-lazy-highlight-start
-                                         (window-end)))
+                                         (window-group-end)))
                                  (setq found nil)
                                (forward-char 1))
                            (if (= mb (if isearch-lazy-highlight-wrapped
                                          isearch-lazy-highlight-end
-                                       (window-start)))
+                                       (window-group-start)))
                                (setq found nil)
                              (forward-char -1)))
 
@@ -3204,8 +3219,8 @@ Attempt to do the search exactly the way the pending 
Isearch would."
                          ;; 1000 is higher than ediff's 100+,
                          ;; but lower than isearch main overlay's 1001
                          (overlay-put ov 'priority 1000)
-                         (overlay-put ov 'face lazy-highlight-face)
-                         (overlay-put ov 'window (selected-window))))
+                         (overlay-put ov 'face lazy-highlight-face)))
+                         ;(overlay-put ov 'window (selected-window))))
                      ;; Remember the current position of point for
                      ;; the next call of `isearch-lazy-highlight-update'
                      ;; when `lazy-highlight-max-at-a-time' is too small.
@@ -3221,12 +3236,12 @@ Attempt to do the search exactly the way the pending 
Isearch would."
                      (setq isearch-lazy-highlight-wrapped t)
                      (if isearch-lazy-highlight-forward
                          (progn
-                           (setq isearch-lazy-highlight-end (window-start))
+                           (setq isearch-lazy-highlight-end 
(window-group-start))
                            (goto-char (max (or 
isearch-lazy-highlight-start-limit (point-min))
-                                           (window-start))))
-                       (setq isearch-lazy-highlight-start (window-end))
+                                           (window-group-start))))
+                       (setq isearch-lazy-highlight-start (window-group-end))
                        (goto-char (min (or isearch-lazy-highlight-end-limit 
(point-max))
-                                       (window-end))))))))
+                                       (window-group-end))))))))
            (unless nomore
              (setq isearch-lazy-highlight-timer
                    (run-at-time lazy-highlight-interval nil
diff --git a/lisp/replace.el b/lisp/replace.el
index 54b3a71..d48f4f3 100644
--- a/lisp/replace.el
+++ b/lisp/replace.el
@@ -2011,6 +2011,9 @@ passed in.  If LITERAL is set, no checking is done, 
anyway."
   (when backward (goto-char (nth 0 match-data)))
   noedit)
 
+(defvar replace-update-post-hook nil
+  "Function(s) to call after query-replace has found a match in the buffer.")
+
 (defvar replace-search-function nil
   "Function to use when searching for strings to replace.
 It is used by `query-replace' and `replace-string', and is called
@@ -2264,7 +2267,7 @@ It must return a string."
                (and nonempty-match
                     (or (not regexp-flag)
                         (and (if backward
-                                 (looking-back search-string)
+                                 (looking-back search-string nil)
                                (looking-at search-string))
                              (let ((match (match-data)))
                                (and (/= (nth 0 match) (nth 1 match))
@@ -2318,7 +2321,8 @@ It must return a string."
                ;; `real-match-data'.
                (while (not done)
                  (set-match-data real-match-data)
-                 (replace-highlight
+                  (run-hooks 'replace-update-post-hook) ; Before 
`replace-highlight'.
+                  (replace-highlight
                   (match-beginning 0) (match-end 0)
                   start end search-string
                   regexp-flag delimited-flag case-fold-search backward)
diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el
index aa51446..05a5da5 100644
--- a/lisp/textmodes/ispell.el
+++ b/lisp/textmodes/ispell.el
@@ -1782,6 +1782,51 @@ Extended character mode can be changed for this buffer 
by placing
 a `~' followed by an extended-character mode -- such as `~.tex'.
 The last occurring definition in the buffer will be used.")
 
+(defun ispell--\\w-filter (char)
+  "Return CHAR in a string when CHAR doesn't have \"word\" syntax,
+nil otherwise.  CHAR must be a character."
+  (let ((str (string char)))
+    (and
+     (not (string-match "\\w" str))
+     str)))
+
+(defun ispell--make-\\w-expression (chars)
+  "Make a regular expression like \"\\(\\w\\|[-_]\\)\".
+This (parenthesized) expression matches either a character of
+\"word\" syntax or one in CHARS.
+
+CHARS is a string of characters.  A member of CHARS is omitted
+from the expression if it already has word syntax.  (Be careful
+about special characters such as ?\\, ?^, ?], and ?- in CHARS.)
+If after this filtering there are no chars left, or only one, a
+special form of the expression is generated."
+  (let ((filtered
+        (mapconcat #'ispell--\\w-filter chars "")))
+    (concat
+     "\\(\\w"
+     (cond
+      ((equal filtered "")
+       "\\)")
+      ((eq (length filtered) 1)
+       (concat "\\|" filtered "\\)"))
+      (t
+       (concat "\\|[" filtered "]\\)"))))))
+
+(defun ispell--make-filename-or-URL-re ()
+  "Construct a regexp to match some file names or URLs or email addresses.
+The expression is crafted to match as great a variety of these
+objects as practicable, without too many false matches happening."
+  (concat ;"\\(--+\\|_+\\|"
+          "\\(/\\w\\|\\("
+          (ispell--make-\\w-expression "-_")
+          "+[.:@]\\)\\)"
+          (ispell--make-\\w-expression "-_")
+          "*\\([.:/@]+"
+          (ispell--make-\\w-expression "-_~=?&")
+          "+\\)+"
+          ;"\\)"
+          ))
+
 ;;;###autoload
 (defvar ispell-skip-region-alist
   `((ispell-words-keyword         forward-line)
@@ -1798,7 +1843,7 @@ The last occurring definition in the buffer will be 
used.")
     ;; Matches e-mail addresses, file names, http addresses, etc.  The
     ;; `-+' `_+' patterns are necessary for performance reasons when
     ;; `-' or `_' part of word syntax.
-    (,(purecopy 
"\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)"))
+;    (,(purecopy 
"\\(--+\\|_+\\|\\(/\\w\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)"))
     ;; above checks /.\w sequences
     
;;("\\(--+\\|\\(/\\|\\(\\(\\w\\|[-_]\\)+[.:@]\\)\\)\\(\\w\\|[-_]\\)*\\([.:/@]+\\(\\w\\|[-_~=?&]\\)+\\)+\\)")
     ;; This is a pretty complex regexp.  It can be simplified to the following:
@@ -2248,6 +2293,11 @@ If so, ask if it needs to be saved."
   (setq ispell-pdict-modified-p nil))
 
 
+(defvar ispell-update-post-hook nil
+  "A normal hook invoked from the ispell command loop.
+It is called once per iteration, before displaying a prompt to
+the user.")
+
 (defun ispell-command-loop (miss guess word start end)
   "Display possible corrections from list MISS.
 GUESS lists possibly valid affix construction of WORD.
@@ -2315,8 +2365,10 @@ Global `ispell-quit' set to start location to continue 
spell session."
              count (ispell-int-char (1+ count))))
       (setq count (ispell-int-char (- count ?0 skipped))))
 
+    (run-hooks 'ispell-update-post-hook)
+
     ;; ensure word is visible
-    (if (not (pos-visible-in-window-p end))
+    (if (not (pos-visible-in-window-group-p end))
        (sit-for 0))
 
     ;; Display choices for misspelled word.
@@ -2845,13 +2897,20 @@ Also position fit window to BUFFER and select it."
                     (prog1
                         (condition-case nil
                             (split-window
-                             nil (- ispell-choices-win-default-height) 'above)
+                              ;; Chose the last of a window group, since
+                              ;; otherwise, the lowering of another window's
+                              ;; TL corner would cause the logical order of
+                              ;; the windows to be changed.
+                             (car (last (selected-window-group)))
+                              (- ispell-choices-win-default-height) 'above)
                           (error nil))
                       (modify-frame-parameters frame '((unsplittable . t))))))
              (and (not unsplittable)
                   (condition-case nil
                       (split-window
-                       nil (- ispell-choices-win-default-height) 'above)
+                        ;; See comment above.
+                       (car (last (selected-window-group)))
+                        (- ispell-choices-win-default-height) 'above)
                     (error nil)))
              (display-buffer buffer))))
     (if (not window)
@@ -3374,7 +3433,8 @@ Must be called after `ispell-buffer-local-parsing' due to 
dependence on mode."
               (if (string= "" comment-end) "^" (regexp-quote comment-end)))
           (if (and (null ispell-check-comments) comment-start)
               (regexp-quote comment-start))
-          (ispell-begin-skip-region ispell-skip-region-alist)))
+          (ispell-begin-skip-region ispell-skip-region-alist)
+          (ispell--make-filename-or-URL-re)))
    "\\|"))
 
 
@@ -3413,6 +3473,8 @@ Manual checking must include comments and tib references.
 The list is of the form described by variable `ispell-skip-region-alist'.
 Must be called after `ispell-buffer-local-parsing' due to dependence on mode."
   (let ((skip-alist ispell-skip-region-alist))
+    (setq skip-alist (append (list (list (ispell--make-filename-or-URL-re)))
+                             skip-alist))
     ;; only additional explicit region definition is tex.
     (if (eq ispell-parser 'tex)
        (setq case-fold-search nil
@@ -4106,9 +4168,10 @@ You can bind this to the key C-c i in GNUS or mail by 
adding to
                      (ispell-non-empty-string vm-included-text-prefix)))
             (t default-prefix)))
           (ispell-skip-region-alist
-           (cons (list (concat "^\\(" cite-regexp "\\)")
-                       (function forward-line))
-                 ispell-skip-region-alist))
+           (cons (list (ispell--make-filename-or-URL-re))
+                  (cons (list (concat "^\\(" cite-regexp "\\)")
+                              (function forward-line))
+                        ispell-skip-region-alist)))
           (old-case-fold-search case-fold-search)
           (dictionary-alist ispell-message-dictionary-alist)
           (ispell-checking-message t))
diff --git a/lisp/window.el b/lisp/window.el
index 54ac811..c57fef4 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -7870,6 +7870,152 @@ Return non-nil if the window was shrunk, nil otherwise."
         (remove-hook 'kill-buffer-hook delete-window-hook t))))))
 
 
+;;;
+;; Groups of windows (Follow Mode).
+;;
+;; This section of functions extends the functionality of some window
+;; manipulating commands to groups of windows co-operatively
+;; displaying a buffer, typically with Follow Mode.
+;;
+;; The xxx-function variables are permanent locals so that their local
+;; status is undone only when explicitly programmed, not when a buffer
+;; is reverted or a mode function is called.
+
+(defvar window-group-start-function nil)
+(make-variable-buffer-local 'window-group-start-function)
+(put 'window-group-start-function 'permanent-local t)
+(defun window-group-start (&optional window)
+  "Return position at which display currently starts in the group of
+windows containing WINDOW.  When a grouping mode (such as Follow Mode)
+is not active, this function is identical to `window-start'.
+
+WINDOW must be a live window and defaults to the selected one.
+This is updated by redisplay or by calling `set-window*-start'."
+  (if (functionp window-group-start-function)
+      (funcall window-group-start-function window)
+    (window-start window)))
+
+(defvar window-group-end-function nil)
+(make-variable-buffer-local 'window-group-end-function)
+(put 'window-group-end-function 'permanent-local t)
+(defun window-group-end (&optional window update)
+  "Return position at which display currently ends in the group of
+windows containing WINDOW.  When a grouping mode (such as Follow Mode)
+is not active, this function is identical to `window-end'.
+
+WINDOW must be a live window and defaults to the selected one.
+This is updated by redisplay, when it runs to completion.
+Simply changing the buffer text or setting `window-group-start'
+does not update this value.
+Return nil if there is no recorded value.  (This can happen if the
+last redisplay of WINDOW was preempted, and did not finish.)
+If UPDATE is non-nil, compute the up-to-date position
+if it isn't already recorded."
+  (if (functionp window-group-end-function)
+      (funcall window-group-end-function window update)
+    (window-end window update)))
+
+(defvar set-window-group-start-function nil)
+(make-variable-buffer-local 'set-window-group-start-function)
+(put 'set-window-group-start-function 'permanent-local t)
+(defun set-window-group-start (window pos &optional noforce)
+  "Make display in the group of windows containing WINDOW start at
+position POS in WINDOW's buffer.  When a grouping mode (such as Follow
+Mode) is not active, this function is identical to `set-window-start'.
+
+WINDOW must be a live window and defaults to the selected one.  Return
+POS.  Optional third arg NOFORCE non-nil inhibits next redisplay from
+overriding motion of point in order to display at this exact start."
+  (if (functionp set-window-group-start-function)
+      (funcall set-window-group-start-function window pos noforce)
+    (set-window-start window pos noforce)))
+
+(defvar recenter-window-group-function nil)
+(make-variable-buffer-local 'recenter-window-group-function)
+(put 'recenter-window-group-function 'permanent-local t)
+(defun recenter-window-group (&optional arg)
+  "Center point in the group of windows containing the selected window
+and maybe redisplay frame.  When a grouping mode (such as Follow Mode)
+is not active, this function is identical to `recenter'.
+
+With a numeric prefix argument ARG, recenter putting point on screen line ARG
+relative to the first window in the selected window group.  If ARG is
+negative, it counts up from the bottom of the last window in the
+group.  (ARG should be less than the total height of the window group.)
+
+If ARG is omitted or nil, then recenter with point on the middle line of
+the selected window group; if the variable `recenter-redisplay' is
+non-nil, also erase the entire frame and redraw it (when
+`auto-resize-tool-bars' is set to `grow-only', this resets the
+tool-bar's height to the minimum height needed); if
+`recenter-redisplay' has the special value `tty', then only tty frames
+are redrawn.
+
+Just C-u as prefix means put point in the center of the window
+and redisplay normally--don't erase and redraw the frame."
+  (if (functionp recenter-window-group-function)
+      (funcall recenter-window-group-function arg)
+    (recenter arg)))
+
+(defvar pos-visible-in-window-group-p-function nil)
+(make-variable-buffer-local 'pos-visible-in-window-group-p-function)
+(put 'pos-visible-in-window-group-p-function 'permanent-local t)
+(defun pos-visible-in-window-group-p (&optional pos window partially)
+  "Return non-nil if position POS is currently on the frame in the
+window group containing WINDOW.  When a grouping mode (such as Follow
+Mode) is not active, this function is identical to
+`pos-visible-in-window-p'.
+
+WINDOW must be a live window and defaults to the selected one.
+
+Return nil if that position is scrolled vertically out of view.  If a
+character is only partially visible, nil is returned, unless the
+optional argument PARTIALLY is non-nil.  If POS is only out of view
+because of horizontal scrolling, return non-nil.  If POS is t, it
+specifies the position of the last visible glyph in the window group.
+POS defaults to point in WINDOW; WINDOW defaults to the selected
+window.
+
+If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil,
+the return value is a list of 2 or 6 elements (X Y [RTOP RBOT ROWH VPOS]),
+where X and Y are the pixel coordinates relative to the top left corner
+of the window.  The remaining elements are omitted if the character after
+POS is fully visible; otherwise, RTOP and RBOT are the number of pixels
+off-window at the top and bottom of the screen line (\"row\") containing
+POS, ROWH is the visible height of that row, and VPOS is the row number
+\(zero-based)."
+  (if (functionp pos-visible-in-window-group-p-function)
+      (funcall pos-visible-in-window-group-p-function pos window partially)
+    (pos-visible-in-window-p pos window partially)))
+
+(defvar selected-window-group-function nil)
+(make-variable-buffer-local 'selected-window-group-function)
+(put 'selected-window-group-function 'permanent-local t)
+(defun selected-window-group ()
+  "Return the list of windows in the group containing the selected window.
+When a grouping mode (such as Follow Mode) is not active, the
+result is a list containing only the selected window."
+  (if (functionp selected-window-group-function)
+      (funcall selected-window-group-function)
+    (list (selected-window))))
+
+(defvar move-to-window-group-line-function nil)
+(make-variable-buffer-local 'move-to-window-group-line-function)
+(put 'move-to-window-group-line-function 'permanent-local t)
+(defun move-to-window-group-line (arg)
+  "Position point relative to the the current group of windows.
+When a grouping mode (such as Follow Mode) is not active, this
+function is identical to `move-to-window-line'.
+
+ARG nil means position point at center of the window group.
+Else, ARG specifies the vertical position within the window
+group; zero means top of first window in the group, negative
+means relative to the bottom of the last window in the group."
+  (if (functionp move-to-window-group-line-function)
+      (funcall move-to-window-group-line-function arg)
+    (move-to-window-line arg)))
+
+
 (defvar recenter-last-op nil
   "Indicates the last recenter operation performed.
 Possible values: `top', `middle', `bottom', integer or float numbers.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]