1
0
mirror of https://git.savannah.gnu.org/git/emacs.git synced 2025-01-05 11:45:45 +00:00

Merge branch 'scratch/follow' into emacs-25

This allows Isearch, etc., to work well when Follow Mode is active.
This commit is contained in:
Alan Mackenzie 2015-12-20 12:33:30 +00:00
commit df32db2f5f
8 changed files with 577 additions and 74 deletions

View File

@ -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
@var{sequence}: the results, which must be strings, are concatenated.
Between each pair of result strings, @code{mapconcat} inserts the string
@var{separator}. Usually @var{separator} contains a space or comma or
other suitable punctuation.
@var{sequence}; 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

View File

@ -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
@vindex move-to-window-group-line-function
@defun 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
@code{move-to-window-group-line-function} 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.
@end 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

View File

@ -133,6 +133,30 @@ This function returns the selected window (which is always a live
window).
@end defun
@anchor{Window 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.
@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
@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
@defun window-group-start &optional window
@vindex window-group-start-function
This function is like @code{window-start}, except that when
@var{window} is a part of a group of windows (@pxref{Window Group}),
@code{window-group-start} returns the start position of the entire
group. This condition holds when the buffer local variable
@code{window-group-start-function} 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.
@end 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
@vindex window-group-end-function
@defun 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}),
@code{window-group-end} returns the end position of the entire group.
This condition holds when the buffer local variable
@code{window-group-end-function} is set to a function. In this case,
@code{window-group-end} calls the function with the two arguments
@var{window} and @var{update}, then returns its result. The argument
@var{update} has the same meaning as in @code{window-end}.
@end 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
@vindex set-window-group-start-function
@defun set-window-group-start window position &optional noforce
This function is like @code{set-window-start}, except that when
@var{window} is a part of a group of windows (@pxref{Window Group}),
@code{set-window-group-start} sets the start position of the entire
group. This condition holds when the buffer local variable
@code{set-window-group-start-function} 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}.
@end defun
@defun pos-visible-in-window-p &optional position window partially
This function returns non-@code{nil} 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
@vindex pos-visible-in-window-group-p-function
@defun pos-visible-in-window-group-p &optional position window partially
This function is like @code{pos-visible-in-window-p}, except that when
@var{window} is a part of a group of windows (@pxref{Window Group}),
@code{pos-visible-in-window-group-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-group-p-function} 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
@var{partially}, then returns its result. The arguments
@var{position} and @var{partially} have the same meaning as in
@code{pos-visible-in-window-p}.
@end 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
@vindex recenter-window-group-function
@defun 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}),
@code{recenter-window-group} scrolls the entire group. This condition
holds when the buffer local variable
@code{recenter-window-group-function} 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
@var{count} has the same meaning as in @code{recenter}, but with
respect to the entire window group.
@end defun
@defopt recenter-redisplay
If this variable is non-@code{nil}, calling @code{recenter} with a
@code{nil} argument redraws the frame. The default value is

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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))

View File

@ -7869,6 +7869,152 @@ Return non-nil if the window was shrunk, nil otherwise."
(with-current-buffer buffer-to-kill
(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.