Commit 64c57303 authored by Alan Mackenzie's avatar Alan Mackenzie

First commit to scratch/follow. Make Isearch work with Follow Mode, etc.

doc/lispref/window.texi (Basic Windows): Add paragraph defining "Group of
Windows" and new @defun selected-window-group.
(Window Start and End): Describe new &optional parameter GROUP and
...-group-function for window-start, window-end, set-window-start, and
pos-visible-in-window-p.
(Textual Scrolling) Describe the same for recenter.
doc/lispref/positions.texi (Screen Lines): Describe the same for
move-to-window-line.

src/window.c (Fwindow_start, Fwindow_end, Fset_window_start)
(Fpos_visible_in_window_p, Frecenter, Fmove_to_window_line): To each, add ar
new optional parameter "group".  At the beginning of each, check whether the
corresponding ...-group-function is set to a function, and if so execute this
function in place of the normal processing.
(syms_of_window): Define symbols for the six new variables below.
(window-start-group-function, window-end-group-function)
(set-window-start-group-function, recenter-group-function)
(pos-visible-in-window-p-group-function, move-to-window-line-group-function):
New permanent local buffer local variables.
src/keyboard.c (Fposn_at_point): Add extra parameter in call to
Fpos_visible_in_window_p.

lisp/window.el (selected-window-group-function): New permanent local buffer
local variable.
(selected-window-group): New function.

lisp/follow.el (follow-mode): Set the ...-group-function variables at mode
enable, kill them at mode disable.  Add/remove follow-after-change to/from
after-change-functions.
(follow-start-end-invalid): New variable.
(follow-redisplay): Manipulate follow-start-end-invalid.
(follow-after-change, follow-window-start, follow-window-end)
(follow-set-window-start, follow-pos-visible-in-window-p)
(follow-move-to-window-line, follow-sit-for): New functions.

lisp/isearch.el (isearch-call-message): New macro.
(isearch-update, with-isearch-suspended, isearch-del-char)
(isearch-search-and-update, isearch-ring-adjust): Invoke above new macro.
(with-isearch-suspended): Rearrange code such that isearch-call-message is
invoked before point is moved.
(isearch-message): Add comment about where point must be at function call.
(isearch-search): Remove call to isearch-message.
(isearch-lazy-highlight-window-group): New variable.
(isearch-lazy-highlight-new-loop): Unconditionally start idle timer.  Move
the battery of tests to ...
(isearch-lazy-highlight-maybe-new-loop): New function, started by idle timer.
Note: (sit-for 0) is still called.
(isearch-lazy-highlight-update): Check membership of
isearch-lazy-highlight-window-group.  Don't set the `window' overlay
property.
(isearch-update, isearch-done, isearch-string-out-of-window)
(isearch-back-into-window, isearch-lazy-highlight-maybe-new-loop)
(isearch-lazy-highlight-search, isearch-lazy-highlight-update)
(isearch-lazy-highlight-update): Call the six amended primitives (see
src/window.c above) with the new `group' argument set to t, to cooperate
with Follow Mode.
parent 30f3432e
......@@ -551,7 +551,8 @@ current buffer, regardless of which buffer is displayed in
any buffer, whether or not it is currently displayed in some window.
@end defun
@deffn Command move-to-window-line count
@deffn Command move-to-window-line count group
@vindex move-to-window-line-group-function
This function moves point with respect to the text currently displayed
in the selected window. It moves point to the beginning of the screen
line @var{count} screen lines from the top of the window. If
......@@ -570,6 +571,16 @@ In an interactive call, @var{count} is the numeric prefix argument.
The value returned is the window line number point has moved to, with
the top line in the window numbered 0.
If @var{group} is non-@code{nil}, and the selected window is a part of
a group of windows (@pxref{Basic Windows}), @code{move-to-window-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
@code{move-to-window-line-group-function} is set to a function. In
this case, @code{move-to-window-line} calls the function with the
argument @var{count}, then returns its result, instead of performing
the actions described above. Typically, the function will call
@code{move-to-window-line} recursively.
@end deffn
@defun compute-motion from frompos to topos width offsets window
......
......@@ -131,6 +131,30 @@ frame is ever selected. @xref{Selecting Windows}.
@defun selected-window
This function returns the selected window (which is always a live
window).
@end defun
Sometimes several windows collectively and cooperatively display a
buffer, for example, under the management of Follow Mode, 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{group
of windows} as a single entity. Several functions such as
@code{window-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.
@defun selected-window-group
@vindex 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).
@end defun
@node Windows and Frames
......@@ -3080,7 +3104,8 @@ using the commands of Lisp mode, because they trigger this
readjustment. To test such code, put it into a command and bind the
command to a key.
@defun window-start &optional window
@defun window-start &optional window group
@vindex window-start-group-function
@cindex window top line
This function returns the display-start position of window
@var{window}. If @var{window} is @code{nil}, the selected window is
......@@ -3096,10 +3121,20 @@ it explicitly since the previous redisplay)---to make sure point appears
on the screen. Nothing except redisplay automatically changes the
window-start position; if you move point, do not expect the window-start
position to change in response until after the next redisplay.
If @var{group} is non-@code{nil}, and @var{window} is a part of a
group of windows (@pxref{Basic Windows}), @code{window-start} returns
the start position of the entire group. This condition holds when the
buffer local variable @code{window-start-group-function} is set to a
function. In this case, @code{window-start} calls the function with
the single argument @var{window}, then returns its result, instead of
performing the actions described above. Typically, the function will
call @code{window-start} recursively.
@end defun
@vindex window-end-group-function
@cindex window end position
@defun window-end &optional window update
@defun window-end &optional window update group
This function returns the position where display of its buffer ends in
@var{window}. The default for @var{window} is the selected window.
......@@ -3122,9 +3157,19 @@ attempt to scroll the display if point has moved off the screen, the
way real redisplay would do. It does not alter the
@code{window-start} value. In effect, it reports where the displayed
text will end if scrolling is not required.
If @var{group} is non-@code{nil}, and @var{window} is a part of a
group of windows (@pxref{Basic Windows}), `window-end' returns the end
position of the entire group. This condition holds when the buffer
local variable @code{window-end-group-function} is set to a function.
In this case, @code{window-end} calls the function with the two
arguments @var{window} and @var{update}, then returns its result,
instead of performing the actions described above. Typically, the
function will call @code{window-end} recursively.
@end defun
@defun set-window-start window position &optional noforce
@vindex set-window-start-group-function
@defun set-window-start window position &optional noforce group
This function sets the display-start position of @var{window} to
@var{position} in @var{window}'s buffer. It returns @var{position}.
......@@ -3185,9 +3230,20 @@ it is still 1 when redisplay occurs. Here is an example:
If @var{noforce} is non-@code{nil}, and @var{position} would place point
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.
If @var{group} is non-@code{nil}, and @var{window} is a part of a
group of windows (@pxref{Basic Windows}), @code{set-window-start} sets
the start position of the entire group. This condition holds when the
buffer local variable @code{set-window-start-group-function} is set to
a function. In this case, @code{set-window-start} calls the function
with the three arguments @var{window}, @var{position}, and
@var{noforce}, then returns its result, instead of performing the
actions described above. Typically, the function will call
@code{set-window-start} recursively.
@end defun
@defun pos-visible-in-window-p &optional position window partially
@defun pos-visible-in-window-p &optional position window partially group
@vindex pos-visible-in-window-p-group-function
This function returns non-@code{nil} if @var{position} is within the
range of text currently visible on the screen in @var{window}. It
returns @code{nil} if @var{position} is scrolled vertically out of
......@@ -3226,6 +3282,18 @@ Here is an example:
(recenter 0))
@end group
@end example
If @var{group} is non-@code{nil}, and @var{window} is a part of a
group of windows (@pxref{Basic Windows}),
@code{pos-visible-in-window-p} 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
@code{pos-visible-in-window-p-group-function} is set to a function.
In this case @code{pos-visible-in-window-p} calls the function with
the three arguments @var{position}, @var{window}, and @var{partially},
then returns its result, instead of performing the actions described
above. Typically, the function will call
@code{pos-visible-in-window-p} recursively.
@end defun
@defun window-line-height &optional line window
......@@ -3443,7 +3511,8 @@ beginning or end of the buffer (depending on scrolling direction);
only if point is already on that position do they signal an error.
@end defopt
@deffn Command recenter &optional count
@deffn Command recenter &optional count group
@vindex recenter-group-function
@cindex centering point
This function scrolls the text in the selected window so that point is
displayed at a specified vertical position within the window. It does
......@@ -3460,6 +3529,14 @@ If @var{count} is @code{nil} (or a non-@code{nil} list),
window. If @var{count} is @code{nil}, this function may redraw the
frame, according to the value of @code{recenter-redisplay}.
If @var{group} is non-@code{nil}, and the selected window is part of a
group of windows (@pxref{Basic Windows}), @code{recenter} scrolls the
entire group. This condition holds when the buffer local variable
@code{recenter-group-function} is set to a function. In this case,
@code{recenter} calls the function with the argument @var{count}, then
returns its result, instead of performing the actions described above.
Typically, the function will call @code{recenter} recursively.
When @code{recenter} is called interactively, @var{count} is the raw
prefix argument. Thus, typing @kbd{C-u} as the prefix sets the
@var{count} to a non-@code{nil} list, while typing @kbd{C-u 4} sets
......
......@@ -421,7 +421,19 @@ 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)
(setq window-start-group-function 'follow-window-start)
(setq window-end-group-function 'follow-window-end)
(setq set-window-start-group-function 'follow-set-window-start)
(setq recenter-group-function 'follow-recenter)
(setq pos-visible-in-window-p-group-function
'follow-pos-visible-in-window-p)
(setq selected-window-group-function 'follow-all-followers)
(setq move-to-window-line-group-function 'follow-move-to-window-line)
(setq sit*-for-function 'follow-sit-for))
;; Remove globally-installed hook functions only if there is no
;; other Follow mode buffer.
(let ((buffers (buffer-list))
......@@ -432,6 +444,17 @@ 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 'sit*-for-function)
(kill-local-variable 'move-to-window-line-group-function)
(kill-local-variable 'selected-window-group-function)
(kill-local-variable 'pos-visible-in-window-p-group-function)
(kill-local-variable 'recenter-group-function)
(kill-local-variable 'set-window-start-group-function)
(kill-local-variable 'window-end-group-function)
(kill-local-variable 'window-start-group-function)
(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 +1038,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 +1074,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 +1471,167 @@ 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*-start'. The aim is to be able to handle
;; Follow Mode windows by replacing `window-start' by `window*-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))))
(defun follow-sit-for (seconds &optional nodisp)
"Redisplay, then wait for SECONDS seconds. Stop when input is available.
Before redisplaying, synchronise all Follow windows.
SECONDS may be a floating-point value.
\(On operating systems that do not support waiting for fractions of a
second, floating-point values are rounded down to the nearest integer.)
Redisplay does not happen if input is available before it starts.
If optional arg NODISP is t, don't synchronise or redisplay, just
wait for input.
Value is t if waited the full time with no input arriving, and nil
otherwise.
The functionality is intended to be the same as `sit-for''s."
(when (and (not (input-pending-p t))
(not nodisp))
(follow-adjust-window (selected-window)))
(sit-for seconds nodisp))
;;; Profile support
;; The following (non-evaluated) section can be used to
......
......@@ -177,6 +177,11 @@ is non-nil if the user quits the search.")
"Function to call to display the search prompt.
If nil, use function `isearch-message'.")
(defmacro isearch-call-message (&optional cqh ellip)
`(if isearch-message-function
(funcall isearch-message-function ,cqh ,ellip)
(isearch-message ,cqh ,ellip)))
(defvar isearch-wrap-function nil
"Function to call to wrap the search when search is failed.
If nil, move point to the beginning of the buffer for a forward search,
......@@ -978,12 +983,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)))
(isearch-call-message))
(if (and isearch-slow-terminal-mode
(not (or isearch-small-window
(pos-visible-in-window-p))))
(pos-visible-in-window-p nil nil nil t))))
(let ((found-point (point)))
(setq isearch-small-window t)
(move-to-window-line 0)
......@@ -1004,7 +1007,7 @@ 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-p nil nil t t))
(if (or (not visible-p)
;; When point is not visible because of hscroll,
;; pos-visible-in-window-p returns non-nil, but
......@@ -1058,7 +1061,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-start nil t))
(found-point (point)))
(when isearch-window-configuration
(set-window-configuration isearch-window-configuration)
......@@ -1068,7 +1071,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-start (selected-window) found-start t t))))
(setq isearch-mode nil)
(if isearch-input-method-local-p
......@@ -1299,13 +1302,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 +1314,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.
(isearch-call-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 +1937,7 @@ If search string is empty, just beep."
(length isearch-string))))
isearch-message (mapconcat 'isearch-text-char-description
isearch-string "")))
(isearch-call-message nil t) ; Do this before moving point.
;; 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 +2114,7 @@ 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.
(isearch-call-message nil t) ; Do this before moving point.
(if (and isearch-other-end (not isearch-adjusted))
(goto-char (if isearch-forward isearch-other-end
(min isearch-opoint
......@@ -2273,10 +2281,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-start nil t))
(w-end (window-end nil t t))
(w-L1 (save-excursion
(save-selected-window (move-to-window-line 1 t) (point))))
(w-L-1 (save-excursion
(save-selected-window (move-to-window-line -1 t) (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 +2313,15 @@ the bottom."
(if above
(progn
(goto-char start)
(recenter 0)
(when (>= isearch-point (window-end nil t))
(recenter 0 t)
(when (>= isearch-point (window-end nil t t))
(goto-char isearch-point)
(recenter -1)))
(recenter -1 t)))
(goto-char end)
(recenter -1)
(when (< isearch-point (window-start))
(recenter -1 t)
(when (< isearch-point (window-start nil t))
(goto-char isearch-point)
(recenter 0))))
(recenter 0 t))))
(goto-char isearch-point))
(defvar isearch-pre-scroll-point nil)
......@@ -2458,6 +2468,7 @@ Search is updated accordingly."
(isearch-ring-adjust1 advance)
(if search-ring-update
(progn
(isearch-call-message nil t)
(isearch-search)
(isearch-push-state)
(isearch-update))
......@@ -2530,6 +2541,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 +2741,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 +3038,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)
......@@ -3055,7 +3071,21 @@ is nil. This function is called when exiting an incremental search if
"22.1")
(defun isearch-lazy-highlight-new-loop (&optional beg end)
"Cleanup any previous `lazy-highlight' loop and begin a new one.
"Set an idle timer, which will trigger a new `lazy-highlight' loop.
BEG and END specify the bounds within which highlighting should
occur. This is called when `isearch-update' is invoked (which
can cause the search string to change or the window(s) to
scroll). It is also used by other Emacs features. Do not start
the loop when we are executing a keyboard macro."
(setq isearch-lazy-highlight-start-limit beg
isearch-lazy-highlight-end-limit end)
(when (null executing-kbd-macro)
(setq isearch-lazy-highlight-timer
(run-with-idle-timer lazy-highlight-initial-delay nil
'isearch-lazy-highlight-maybe-new-loop))))
(defun isearch-lazy-highlight-maybe-new-loop ()
"If needed cleanup any previous `lazy-highlight' loop and begin a new one.
BEG and END specify the bounds within which highlighting should occur.
This is called when `isearch-update' is invoked (which can cause the
search string to change or the window to scroll). It is also used
......@@ -3064,8 +3094,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 +3106,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-start nil t)
isearch-lazy-highlight-window-start))
(not (= (window-end) ; Window may have been split/joined.
(not (= (window-end nil nil t) ; Window may have been split/joined.
isearch-lazy-highlight-window-end))
(not (eq isearch-forward
isearch-lazy-highlight-forward))
......@@ -3086,16 +3116,15 @@ 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
;; `isearch-error' is non-nil. (Bug#9918)
(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-start nil t)
isearch-lazy-highlight-window-end (window-end nil nil t)
;; 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 +3142,8 @@ 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 "")
(isearch-lazy-highlight-update))))
(defun isearch-lazy-highlight-search ()
"Search ahead for the next or previous match, for lazy highlighting.
......@@ -3139,13 +3166,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-end nil nil t)))
(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))))))