From 2c56fc2a3f106a1286ad793eed9bfaafd09a7411 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Wed, 11 Nov 2015 12:02:48 +0000 Subject: 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. --- doc/lispref/positions.texi | 13 +- doc/lispref/windows.texi | 87 ++++- lisp/follow.el | 193 +++++++++++- lisp/isearch.el | 133 ++++---- lisp/window.el | 11 + src/keyboard.c | 2 +- src/window.c | 772 +++++++++++++++++++++++++++------------------ 7 files changed, 838 insertions(+), 373 deletions(-) diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index 72b76ce5c8..e0496e3084 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi @@ -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 diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 357247ef43..832ced719c 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -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 @@ -3064,7 +3088,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 @@ -3080,10 +3105,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. @@ -3106,9 +3141,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}. @@ -3169,9 +3214,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 @@ -3210,6 +3266,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 @@ -3427,7 +3495,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 @@ -3444,6 +3513,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 diff --git a/lisp/follow.el b/lisp/follow.el index 938c59e850..f2427374f2 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -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. @@ -1446,6 +1474,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 diff --git a/lisp/isearch.el b/lisp/isearch.el index 9f8ba8d8d7..92d7894d2e 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -171,6 +171,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, @@ -969,12 +974,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) @@ -995,7 +998,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 @@ -1049,7 +1052,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) @@ -1059,7 +1062,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 @@ -1282,13 +1285,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 @@ -1301,7 +1297,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)) @@ -1895,6 +1901,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, @@ -2071,6 +2078,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 @@ -2237,10 +2245,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)) @@ -2267,15 +2277,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) @@ -2422,6 +2432,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)) @@ -2494,6 +2505,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))) @@ -2680,9 +2698,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))) @@ -2980,6 +2995,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) @@ -3012,7 +3028,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 @@ -3021,8 +3051,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 @@ -3033,9 +3063,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)) @@ -3043,16 +3073,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) @@ -3070,10 +3099,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. @@ -3096,13 +3123,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)))))) + (window-start nil t)))))) ;; Use a loop like in `isearch-search'. (while retry (setq success (isearch-search-string @@ -3126,7 +3153,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 @@ -3146,12 +3173,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-end nil nil t))) (setq found nil) (forward-char 1)) (if (= mb (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-end - (window-start))) + (window-start nil t))) (setq found nil) (forward-char -1))) @@ -3161,8 +3188,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. @@ -3178,12 +3205,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-start nil t)) (goto-char (max (or isearch-lazy-highlight-start-limit (point-min)) - (window-start)))) - (setq isearch-lazy-highlight-start (window-end)) + (window-start nil t)))) + (setq isearch-lazy-highlight-start (window-end nil nil t)) (goto-char (min (or isearch-lazy-highlight-end-limit (point-max)) - (window-end)))))))) + (window-end nil nil t)))))))) (unless nomore (setq isearch-lazy-highlight-timer (run-at-time lazy-highlight-interval nil diff --git a/lisp/window.el b/lisp/window.el index 6d189055c1..0c403eb184 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -28,6 +28,17 @@ ;;; Code: +(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)))) + (defun internal--before-save-selected-window () (cons (selected-window) ;; We save and restore all frames' selected windows, because diff --git a/src/keyboard.c b/src/keyboard.c index 851207874d..c7733568e7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10663,7 +10663,7 @@ The `posn-' functions access elements of such lists. */) if (NILP (window)) window = selected_window; - tem = Fpos_visible_in_window_p (pos, window, Qt); + tem = Fpos_visible_in_window_p (pos, window, Qt, Qnil); if (!NILP (tem)) { Lisp_Object x = XCAR (tem); diff --git a/src/window.c b/src/window.c index 0ac76d4186..efb4c9b4f8 100644 --- a/src/window.c +++ b/src/window.c @@ -1540,13 +1540,23 @@ WINDOW must be a live window and defaults to the selected one. */) return Fmarker_position (decode_live_window (window)->old_pointm); } -DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, +DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 2, 0, doc: /* Return position at which display currently starts in WINDOW. WINDOW must be a live window and defaults to the selected one. -This is updated by redisplay or by calling `set-window-start'. */) - (Lisp_Object window) +This is updated by redisplay or by calling `set-window-start'. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), return the start position of +the group rather than of the individual WINDOW. This condition holds when +`window-start-group-function' is set to a function, in which case +`window-start' calls the function with the argument WINDOW, then returns its +result, instead of doing its normal processing. */) + (Lisp_Object window, Lisp_Object group) { - return Fmarker_position (decode_live_window (window)->start); + return (!NILP (group) + && FUNCTIONP (Vwindow_start_group_function)) + ? call1 (Vwindow_start_group_function, window) + : Fmarker_position (decode_live_window (window)->start); } /* This is text temporarily removed from the doc string below. @@ -1560,7 +1570,7 @@ have been if redisplay had finished, do this: (vertical-motion (1- (window-height window)) window) (point))") */ -DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0, +DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 3, 0, doc: /* Return position at which display currently ends in WINDOW. WINDOW must be a live window and defaults to the selected one. This is updated by redisplay, when it runs to completion. @@ -1569,65 +1579,77 @@ 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. */) - (Lisp_Object window, Lisp_Object update) -{ - Lisp_Object value; - struct window *w = decode_live_window (window); - Lisp_Object buf; - struct buffer *b; - - buf = w->contents; - CHECK_BUFFER (buf); - b = XBUFFER (buf); - - if (! NILP (update) - && (windows_or_buffers_changed - || !w->window_end_valid - || b->clip_changed - || b->prevent_redisplay_optimizations_p - || window_outdated (w)) - /* Don't call display routines if we didn't yet create any real - frames, because the glyph matrices are not yet allocated in - that case. This could happen in some code that runs in the - daemon during initialization (e.g., see bug#20565). */ - && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) - { - struct text_pos startp; - struct it it; - struct buffer *old_buffer = NULL; - void *itdata = NULL; - - /* Cannot use Fvertical_motion because that function doesn't - cope with variable-height lines. */ - if (b != current_buffer) - { - old_buffer = current_buffer; - set_buffer_internal (b); - } - - /* In case W->start is out of the range, use something - reasonable. This situation occurred when loading a file with - `-l' containing a call to `rmail' with subsequent other - commands. At the end, W->start happened to be BEG, while - rmail had already narrowed the buffer. */ - CLIP_TEXT_POS_FROM_MARKER (startp, w->start); - - itdata = bidi_shelve_cache (); - start_display (&it, w, startp); - move_it_vertically (&it, window_box_height (w)); - if (it.current_y < it.last_visible_y) - move_it_past_eol (&it); - value = make_number (IT_CHARPOS (it)); - bidi_unshelve_cache (itdata, false); - - if (old_buffer) - set_buffer_internal (old_buffer); - } - else - XSETINT (value, BUF_Z (b) - w->window_end_pos); +if it isn't already recorded. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), return the end position of +the group rather than of the individual WINDOW. This condition holds when +`window-end-group-function' is set to a function, in which case `window-end' +calls the function with the two arguments WINDOW and UPDATE, then returns its +result, instead of doing its normal processing. */) + (Lisp_Object window, Lisp_Object update, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vwindow_end_group_function)) + return call2 (Vwindow_end_group_function, window, update); + { + Lisp_Object value; + struct window *w = decode_live_window (window); + Lisp_Object buf; + struct buffer *b; + + buf = w->contents; + CHECK_BUFFER (buf); + b = XBUFFER (buf); + + if (! NILP (update) + && (windows_or_buffers_changed + || !w->window_end_valid + || b->clip_changed + || b->prevent_redisplay_optimizations_p + || window_outdated (w)) + /* Don't call display routines if we didn't yet create any real + frames, because the glyph matrices are not yet allocated in + that case. This could happen in some code that runs in the + daemon during initialization (e.g., see bug#20565). */ + && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) + { + struct text_pos startp; + struct it it; + struct buffer *old_buffer = NULL; + void *itdata = NULL; + + /* Cannot use Fvertical_motion because that function doesn't + cope with variable-height lines. */ + if (b != current_buffer) + { + old_buffer = current_buffer; + set_buffer_internal (b); + } + + /* In case W->start is out of the range, use something + reasonable. This situation occurred when loading a file with + `-l' containing a call to `rmail' with subsequent other + commands. At the end, W->start happened to be BEG, while + rmail had already narrowed the buffer. */ + CLIP_TEXT_POS_FROM_MARKER (startp, w->start); + + itdata = bidi_shelve_cache (); + start_display (&it, w, startp); + move_it_vertically (&it, window_box_height (w)); + if (it.current_y < it.last_visible_y) + move_it_past_eol (&it); + value = make_number (IT_CHARPOS (it)); + bidi_unshelve_cache (itdata, false); + + if (old_buffer) + set_buffer_internal (old_buffer); + } + else + XSETINT (value, BUF_Z (b) - w->window_end_pos); - return value; + return value; + } } DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0, @@ -1666,30 +1688,43 @@ Return POS. */) return pos; } -DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, +DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0, doc: /* Make display in 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. */) - (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) -{ - register struct window *w = decode_live_window (window); +overriding motion of point in order to display at this exact start. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), set the start position of +the group rather than of the individual WINDOW. This condition holds when +`set-window-start-group-function' is set to a function, in which case +`set-window-start' calls the function with the three arguments WINDOW, POS, +and NOFORCE, then returns its result, instead of doing its normal +processing. */) + (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vset_window_start_group_function)) + return call3 (Vset_window_start_group_function, window, pos, noforce); + { + register struct window *w = decode_live_window (window); - set_marker_restricted (w->start, pos, w->contents); - /* This is not right, but much easier than doing what is right. */ - w->start_at_line_beg = false; - if (NILP (noforce)) - w->force_start = true; - wset_update_mode_line (w); - /* Bug#15957. */ - w->window_end_valid = false; - wset_redisplay (w); + set_marker_restricted (w->start, pos, w->contents); + /* This is not right, but much easier than doing what is right. */ + w->start_at_line_beg = false; + if (NILP (noforce)) + w->force_start = true; + wset_update_mode_line (w); + /* Bug#15957. */ + w->window_end_valid = false; + wset_redisplay (w); - return pos; + return pos; + } } DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, - Spos_visible_in_window_p, 0, 3, 0, + Spos_visible_in_window_p, 0, 4, 0, doc: /* Return non-nil if position POS is currently on the frame in WINDOW. WINDOW must be a live window and defaults to the selected one. @@ -1709,9 +1744,21 @@ 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). */) - (Lisp_Object pos, Lisp_Object window, Lisp_Object partially) -{ +(zero-based). + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), test whether POS is visible +in the group of windows rather than in the individual WINDOW. This +condition holds when `pos-visible-in-window-p-function' is set to a +function, in which case `pos-visible-in-window-p' calls the function with +the three arguments POS, WINDOW, and PARTIALLY, then returns its result, +instead of doing its normal processing. */) + (Lisp_Object pos, Lisp_Object window, Lisp_Object partially, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vpos_visible_in_window_p_group_function)) + return call3 (Vpos_visible_in_window_p_group_function, pos, window, partially); + { struct window *w; EMACS_INT posint; struct buffer *buf; @@ -1760,6 +1807,7 @@ POS, ROWH is the visible height of that row, and VPOS is the row number } return in_window; + } } DEFUN ("window-line-height", Fwindow_line_height, @@ -5185,7 +5233,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) } XSETFASTINT (tem, PT); - tem = Fpos_visible_in_window_p (tem, window, Qnil); + tem = Fpos_visible_in_window_p (tem, window, Qnil, Qnil); if (NILP (tem)) { @@ -5574,7 +5622,7 @@ displayed_window_lines (struct window *w) } -DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", +DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "P\ni", doc: /* Center point in selected window and maybe redisplay frame. With a numeric prefix argument ARG, recenter putting point on screen line ARG relative to the selected window. If ARG is negative, it counts up from the @@ -5588,208 +5636,221 @@ 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. */) - (register Lisp_Object arg) -{ - struct window *w = XWINDOW (selected_window); - struct buffer *buf = XBUFFER (w->contents); - bool center_p = false; - ptrdiff_t charpos, bytepos; - EMACS_INT iarg IF_LINT (= 0); - int this_scroll_margin; - - if (buf != current_buffer) - error ("`recenter'ing a window that does not display current-buffer."); +and redisplay normally--don't erase and redraw the frame. + +When `recenter' is called from a program, GROUP is non-nil, and WINDOW is +part of a group of windows collectively displaying a buffer (such as with +Follow Mode), perform `recenter''s actions on the group rather than on the +individual WINDOW. This condition holds when `recenter-group-function' is +set to a function, in which case `recenter' calls the function with the +argument ARG, then returns its value, instead of doing its normal +processing. */) + (register Lisp_Object arg, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vrecenter_group_function)) + return call1 (Vrecenter_group_function, arg); + { + struct window *w = XWINDOW (selected_window); + struct buffer *buf = XBUFFER (w->contents); + bool center_p = false; + ptrdiff_t charpos, bytepos; + EMACS_INT iarg IF_LINT (= 0); + int this_scroll_margin; - /* If redisplay is suppressed due to an error, try again. */ - buf->display_error_modiff = 0; + if (buf != current_buffer) + error ("`recenter'ing a window that does not display current-buffer."); - if (NILP (arg)) - { - if (!NILP (Vrecenter_redisplay) - && (!EQ (Vrecenter_redisplay, Qtty) - || !NILP (Ftty_type (selected_frame)))) - { - ptrdiff_t i; + /* If redisplay is suppressed due to an error, try again. */ + buf->display_error_modiff = 0; - /* Invalidate pixel data calculated for all compositions. */ - for (i = 0; i < n_compositions; i++) - composition_table[i]->font = NULL; + if (NILP (arg)) + { + if (!NILP (Vrecenter_redisplay) + && (!EQ (Vrecenter_redisplay, Qtty) + || !NILP (Ftty_type (selected_frame)))) + { + ptrdiff_t i; + + /* Invalidate pixel data calculated for all compositions. */ + for (i = 0; i < n_compositions; i++) + composition_table[i]->font = NULL; #if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) - WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; + WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; #endif - Fredraw_frame (WINDOW_FRAME (w)); - SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); - } + Fredraw_frame (WINDOW_FRAME (w)); + SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); + } + center_p = true; + } + else if (CONSP (arg)) /* Just C-u. */ center_p = true; - } - else if (CONSP (arg)) /* Just C-u. */ - center_p = true; - else - { - arg = Fprefix_numeric_value (arg); - CHECK_NUMBER (arg); - iarg = XINT (arg); - } - - /* Do this after making BUF current - in case scroll_margin is buffer-local. */ - this_scroll_margin - = max (0, min (scroll_margin, w->total_lines / 4)); - - /* Don't use redisplay code for initial frames, as the necessary - data structures might not be set up yet then. */ - if (!FRAME_INITIAL_P (XFRAME (w->frame))) - { - if (center_p) - { - struct it it; - struct text_pos pt; - void *itdata = bidi_shelve_cache (); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - move_it_vertically_backward (&it, window_box_height (w) / 2); - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - bidi_unshelve_cache (itdata, false); - } - else if (iarg < 0) - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); - int extra_line_spacing; - int h = window_box_height (w); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin + 1, nlines, - ht - this_scroll_margin); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Be sure we have the exact height of the full line containing PT. */ - move_it_by_lines (&it, 0); - - /* The amount of pixels we have to move back is the window - height minus what's displayed in the line containing PT, - and the lines below. */ - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, nlines); - - if (it.vpos == nlines) - h -= it.current_y; - else - { - /* Last line has no newline. */ - h -= line_bottom_y (&it); - it.vpos++; - } - - /* Don't reserve space for extra line spacing of last line. */ - extra_line_spacing = it.max_extra_line_spacing; - - /* If we can't move down NLINES lines because we hit - the end of the buffer, count in some empty lines. */ - if (it.vpos < nlines) - { - nlines -= it.vpos; - extra_line_spacing = it.extra_line_spacing; - h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); - } - if (h <= 0) - { - bidi_unshelve_cache (itdata, false); - return Qnil; - } - - /* Now find the new top line (starting position) of the window. */ - start_display (&it, w, pt); - it.current_y = 0; - move_it_vertically_backward (&it, h); - - /* If extra line spacing is present, we may move too far - back. This causes the last line to be only partially - visible (which triggers redisplay to recenter that line - in the middle), so move forward. - But ignore extra line spacing on last line, as it is not - considered to be part of the visible height of the line. - */ - h += extra_line_spacing; - while (-it.current_y > h) - move_it_by_lines (&it, 1); - - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - - bidi_unshelve_cache (itdata, false); - } - else - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin, nlines, - ht - this_scroll_margin - 1); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Move to the beginning of screen line containing PT. */ - move_it_by_lines (&it, 0); - - /* Move back to find the point which is ARG screen lines above PT. */ - if (nlines > 0) - { - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, -nlines); - } + else + { + arg = Fprefix_numeric_value (arg); + CHECK_NUMBER (arg); + iarg = XINT (arg); + } - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); + /* Do this after making BUF current + in case scroll_margin is buffer-local. */ + this_scroll_margin + = max (0, min (scroll_margin, w->total_lines / 4)); - bidi_unshelve_cache (itdata, false); - } - } - else - { - struct position pos; - int ht = window_internal_height (w); + /* Don't use redisplay code for initial frames, as the necessary + data structures might not be set up yet then. */ + if (!FRAME_INITIAL_P (XFRAME (w->frame))) + { + if (center_p) + { + struct it it; + struct text_pos pt; + void *itdata = bidi_shelve_cache (); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + move_it_vertically_backward (&it, window_box_height (w) / 2); + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + bidi_unshelve_cache (itdata, false); + } + else if (iarg < 0) + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); + int extra_line_spacing; + int h = window_box_height (w); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); + + nlines = clip_to_bounds (this_scroll_margin + 1, nlines, + ht - this_scroll_margin); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Be sure we have the exact height of the full line containing PT. */ + move_it_by_lines (&it, 0); + + /* The amount of pixels we have to move back is the window + height minus what's displayed in the line containing PT, + and the lines below. */ + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, nlines); + + if (it.vpos == nlines) + h -= it.current_y; + else + { + /* Last line has no newline. */ + h -= line_bottom_y (&it); + it.vpos++; + } + + /* Don't reserve space for extra line spacing of last line. */ + extra_line_spacing = it.max_extra_line_spacing; + + /* If we can't move down NLINES lines because we hit + the end of the buffer, count in some empty lines. */ + if (it.vpos < nlines) + { + nlines -= it.vpos; + extra_line_spacing = it.extra_line_spacing; + h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); + } + if (h <= 0) + { + bidi_unshelve_cache (itdata, false); + return Qnil; + } + + /* Now find the new top line (starting position) of the window. */ + start_display (&it, w, pt); + it.current_y = 0; + move_it_vertically_backward (&it, h); + + /* If extra line spacing is present, we may move too far + back. This causes the last line to be only partially + visible (which triggers redisplay to recenter that line + in the middle), so move forward. + But ignore extra line spacing on last line, as it is not + considered to be part of the visible height of the line. + */ + h += extra_line_spacing; + while (-it.current_y > h) + move_it_by_lines (&it, 1); + + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + bidi_unshelve_cache (itdata, false); + } + else + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); + + nlines = clip_to_bounds (this_scroll_margin, nlines, + ht - this_scroll_margin - 1); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Move to the beginning of screen line containing PT. */ + move_it_by_lines (&it, 0); + + /* Move back to find the point which is ARG screen lines above PT. */ + if (nlines > 0) + { + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, -nlines); + } + + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + bidi_unshelve_cache (itdata, false); + } + } + else + { + struct position pos; + int ht = window_internal_height (w); - if (center_p) - iarg = ht / 2; - else if (iarg < 0) - iarg += ht; + if (center_p) + iarg = ht / 2; + else if (iarg < 0) + iarg += ht; - /* Don't let it get into the margin at either top or bottom. */ - iarg = clip_to_bounds (this_scroll_margin, iarg, - ht - this_scroll_margin - 1); + /* Don't let it get into the margin at either top or bottom. */ + iarg = clip_to_bounds (this_scroll_margin, iarg, + ht - this_scroll_margin - 1); - pos = *vmotion (PT, PT_BYTE, - iarg, w); - charpos = pos.bufpos; - bytepos = pos.bytepos; - } + pos = *vmotion (PT, PT_BYTE, - iarg, w); + charpos = pos.bufpos; + bytepos = pos.bytepos; + } - /* Set the new window start. */ - set_marker_both (w->start, w->contents, charpos, bytepos); - w->window_end_valid = false; + /* Set the new window start. */ + set_marker_both (w->start, w->contents, charpos, bytepos); + w->window_end_valid = false; - w->optional_new_start = true; + w->optional_new_start = true; - w->start_at_line_beg = (bytepos == BEGV_BYTE - || FETCH_BYTE (bytepos - 1) == '\n'); + w->start_at_line_beg = (bytepos == BEGV_BYTE + || FETCH_BYTE (bytepos - 1) == '\n'); - wset_redisplay (w); + wset_redisplay (w); - return Qnil; + return Qnil; + } } DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width, @@ -5836,52 +5897,68 @@ pixels. */) } DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, - 1, 1, "P", + 1, 2, "P\ni", doc: /* Position point relative to window. ARG nil means position point at center of window. Else, ARG specifies vertical position within the window; -zero means top of window, negative means relative to bottom of window. */) - (Lisp_Object arg) -{ - struct window *w = XWINDOW (selected_window); - int lines, start; - Lisp_Object window; +zero means top of window, negative means relative to bottom of window. + +When GROUP is non-nil, and `move-to-window-line-group-function' is set to a +function, then instead of the above, that function is called with the +single argument ARG, and its result returned. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), position point relative to +the group of windows as a whole rather than the individual WINDOW. This +condition holds when `move-to-window-line-group-function' is set to a +function, in which case `move-to-window-line' calls the function with the +argument ARG, then returns its result, instead of doing its normal +processing. */) + (Lisp_Object arg, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vmove_to_window_line_group_function)) + return call1 (Vmove_to_window_line_group_function, arg); + { + struct window *w = XWINDOW (selected_window); + int lines, start; + Lisp_Object window; #if false - int this_scroll_margin; + int this_scroll_margin; #endif - if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) - /* This test is needed to make sure PT/PT_BYTE make sense in w->contents - when passed below to set_marker_both. */ - error ("move-to-window-line called from unrelated buffer"); + if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) + /* This test is needed to make sure PT/PT_BYTE make sense in w->contents + when passed below to set_marker_both. */ + error ("move-to-window-line called from unrelated buffer"); - window = selected_window; - start = marker_position (w->start); - if (start < BEGV || start > ZV) - { - int height = window_internal_height (w); - Fvertical_motion (make_number (- (height / 2)), window, Qnil); - set_marker_both (w->start, w->contents, PT, PT_BYTE); - w->start_at_line_beg = !NILP (Fbolp ()); - w->force_start = true; - } - else - Fgoto_char (w->start); + window = selected_window; + start = marker_position (w->start); + if (start < BEGV || start > ZV) + { + int height = window_internal_height (w); + Fvertical_motion (make_number (- (height / 2)), window, Qnil); + set_marker_both (w->start, w->contents, PT, PT_BYTE); + w->start_at_line_beg = !NILP (Fbolp ()); + w->force_start = true; + } + else + Fgoto_char (w->start); - lines = displayed_window_lines (w); + lines = displayed_window_lines (w); #if false - this_scroll_margin = max (0, min (scroll_margin, lines / 4)); + this_scroll_margin = max (0, min (scroll_margin, lines / 4)); #endif - if (NILP (arg)) - XSETFASTINT (arg, lines / 2); - else - { - EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); + if (NILP (arg)) + XSETFASTINT (arg, lines / 2); + else + { + EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); - if (iarg < 0) - iarg = iarg + lines; + if (iarg < 0) + iarg = iarg + lines; #if false /* This code would prevent move-to-window-line from moving point to a place inside the scroll margins (which would cause the @@ -5889,19 +5966,20 @@ zero means top of window, negative means relative to bottom of window. */) it is probably better not to install it. However, it is here inside #if false so as not to lose it. -- rms. */ - /* Don't let it get into the margin at either top or bottom. */ - iarg = max (iarg, this_scroll_margin); - iarg = min (iarg, lines - this_scroll_margin - 1); + /* Don't let it get into the margin at either top or bottom. */ + iarg = max (iarg, this_scroll_margin); + iarg = min (iarg, lines - this_scroll_margin - 1); #endif - arg = make_number (iarg); - } + arg = make_number (iarg); + } - /* Skip past a partially visible first line. */ - if (w->vscroll) - XSETINT (arg, XINT (arg) + 1); + /* Skip past a partially visible first line. */ + if (w->vscroll) + XSETINT (arg, XINT (arg) + 1); - return Fvertical_motion (arg, window, Qnil); + return Fvertical_motion (arg, window, Qnil); + } } @@ -7175,6 +7253,12 @@ syms_of_window (void) DEFSYM (Qclone_of, "clone-of"); DEFSYM (Qfloor, "floor"); DEFSYM (Qceiling, "ceiling"); + DEFSYM (Qwindow_start_group_function, "window-start-group-function"); + DEFSYM (Qwindow_end_group_function, "window-end-group-function"); + DEFSYM (Qset_window_start_group_function, "set-window-start-group-function"); + DEFSYM (Qrecenter_group_function, "recenter-group-function"); + DEFSYM (Qpos_visible_in_window_p_group_function, "pos-visible-in-window-p-group-function"); + DEFSYM (Qmove_to_window_line_group_function, "move-to-window-line-group-function"); staticpro (&Vwindow_list); @@ -7346,6 +7430,72 @@ Note that this optimization can cause the portion of the buffer displayed after a scrolling operation to be somewhat inaccurate. */); Vfast_but_imprecise_scrolling = false; + DEFVAR_LISP ("window-start-group-function", Vwindow_start_group_function, + doc: /* Function to call for `window-start' when its GROUP parameter is non-nil. +When this variable contains a function, and `window-start' is called with a +non-nil GROUP parameter, the function is called instead of `window-start''s +normal action. `window-start' passes the function its argument WINDOW, which +might be nil. The function may recursively call `window-start'. */); + Vwindow_start_group_function = Qnil; + Fmake_variable_buffer_local (Qwindow_start_group_function); + Fput (Qwindow_start_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("window-end-group-function", Vwindow_end_group_function, + doc: /* Function to call for `window-end' if its GROUP parameter is non-nil. +When this variable contains a function, and `window-end' is called with a +non-nil GROUP parameter, the function is called instead of `window-end''s +normal action. `window-end' passes the function its two parameters WINDOW, +and UPDATE. The function may call `window-end' recursively. */); + Vwindow_end_group_function = Qnil; + Fmake_variable_buffer_local (Qwindow_end_group_function); + Fput (Qwindow_end_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("set-window-start-group-function", + Vset_window_start_group_function, + doc: /* The function to call for `set-window-start' when its GROUP parameter is non-nil. +When this variable contains a function, and `set-window-start' is called +with a non-nil GROUP parameter, the function is called instead of +`set-window-start''s normal action. `set-window-start' passes the function +its three parameters WINDOW, POS, and NOFORCE. The function may call +`set-window-start' recursively. */); + Vset_window_start_group_function = Qnil; + Fmake_variable_buffer_local (Qset_window_start_group_function); + Fput (Qset_window_start_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("recenter-group-function", Vrecenter_group_function, + doc: /* Function to call for `recenter' when its GROUP parameter is non-nil. +When this variable contains a function, and `recenter' is called with a +non-nil GROUP parameter, the function is called instead of `recenter''s +normal action. `recenter' passes the function its parameter ARG. The +function may call `recenter' recursively. */); + Vrecenter_group_function = Qnil; + Fmake_variable_buffer_local (Qrecenter_group_function); + Fput (Qrecenter_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("pos-visible-in-window-p-group-function", + Vpos_visible_in_window_p_group_function, + doc: /* Function for `pos-visible-in-window-p' when its GROUP parameter is non-nil. +When this variable contains a function, and `pos-visible-in-window-p' is +called with a non-nil GROUP parameter, the function is called instead of +`pos-visible-in-window-p''s normal action. `pos-visible-in-window-p' +passes the function its three parameters POS, WINDOW, and PARTIALLY. The +function may call `pos-visible-in-window-p' recursively. */); + Vpos_visible_in_window_p_group_function = Qnil; + Fmake_variable_buffer_local (Qpos_visible_in_window_p_group_function); + Fput (Qpos_visible_in_window_p_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("move-to-window-line-group-function", + Vmove_to_window_line_group_function, + doc: /* Function for `move-to-window-line' when its GROUP parameter is non-nil. +When this variable contains a function, and `move-to-window-line' is +called with a non-nil GROUP parameter, the function is called instead of +`move-to-window-line''s normal action. `move-to-window-line' passes the +function its parameter ARG. The function may call `move-to-window-line' +recursively. */); + Vmove_to_window_line_group_function = Qnil; + Fmake_variable_buffer_local (Qmove_to_window_line_group_function); + Fput (Qmove_to_window_line_group_function, Qpermanent_local, Qt); + defsubr (&Sselected_window); defsubr (&Sminibuffer_window); defsubr (&Swindow_minibuffer_p); -- cgit v1.2.3 From b442b94858c68c412324ff72da1e4c4c725b16df Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Wed, 18 Nov 2015 21:14:30 +0000 Subject: lisp/isearch.el: Eliminate macro isearch-call-message, replacing with funcall. --- lisp/isearch.el | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lisp/isearch.el b/lisp/isearch.el index 92d7894d2e..dc2dca1253 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -171,11 +171,6 @@ 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, @@ -974,7 +969,7 @@ The last thing it does is to run `isearch-update-post-hook'." (null executing-kbd-macro)) (progn (if (not (input-pending-p)) - (isearch-call-message)) + (funcall (or isearch-message-function #'isearch-message))) (if (and isearch-slow-terminal-mode (not (or isearch-small-window (pos-visible-in-window-p nil nil nil t)))) @@ -1300,7 +1295,7 @@ You can update the global isearch variables by setting new values to isearch-case-fold-search isearch-new-case-fold) ;; Restore the minibuffer message before moving point. - (isearch-call-message nil t) + (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) @@ -1901,7 +1896,8 @@ 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. + ;; 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, @@ -2078,7 +2074,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. - (isearch-call-message nil t) ; Do this before moving 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 @@ -2432,7 +2429,7 @@ Search is updated accordingly." (isearch-ring-adjust1 advance) (if search-ring-update (progn - (isearch-call-message nil t) + (funcall (or isearch-message-function #'isearch-message) nil t) (isearch-search) (isearch-push-state) (isearch-update)) -- cgit v1.2.3 From 64c57303658f69b019c4599f8c960a5623855410 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Wed, 11 Nov 2015 12:02:48 +0000 Subject: 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. --- doc/lispref/positions.texi | 13 +- doc/lispref/windows.texi | 87 ++++- lisp/follow.el | 193 +++++++++++- lisp/isearch.el | 133 ++++---- lisp/window.el | 11 + src/keyboard.c | 2 +- src/window.c | 772 +++++++++++++++++++++++++++------------------ 7 files changed, 838 insertions(+), 373 deletions(-) diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index 72b76ce5c8..e0496e3084 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi @@ -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 diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 5c7947eeca..f92289f51a 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -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 diff --git a/lisp/follow.el b/lisp/follow.el index 37de923e6a..2cbf0f2b93 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -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 diff --git a/lisp/isearch.el b/lisp/isearch.el index 8c98d36f4a..39d573cc01 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -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)))))) + (window-start nil t)))))) ;; Use a loop like in `isearch-search'. (while retry (setq success (isearch-search-string @@ -3169,7 +3196,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 +3216,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-end nil nil t))) (setq found nil) (forward-char 1)) (if (= mb (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-end - (window-start))) + (window-start nil t))) (setq found nil) (forward-char -1))) @@ -3204,8 +3231,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 +3248,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-start nil t)) (goto-char (max (or isearch-lazy-highlight-start-limit (point-min)) - (window-start)))) - (setq isearch-lazy-highlight-start (window-end)) + (window-start nil t)))) + (setq isearch-lazy-highlight-start (window-end nil nil t)) (goto-char (min (or isearch-lazy-highlight-end-limit (point-max)) - (window-end)))))))) + (window-end nil nil t)))))))) (unless nomore (setq isearch-lazy-highlight-timer (run-at-time lazy-highlight-interval nil diff --git a/lisp/window.el b/lisp/window.el index 54ac811572..c5a1019372 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -28,6 +28,17 @@ ;;; Code: +(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)))) + (defun internal--before-save-selected-window () (cons (selected-window) ;; We save and restore all frames' selected windows, because diff --git a/src/keyboard.c b/src/keyboard.c index 02bc7d2a0b..725c324eb7 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10658,7 +10658,7 @@ The `posn-' functions access elements of such lists. */) if (NILP (window)) window = selected_window; - tem = Fpos_visible_in_window_p (pos, window, Qt); + tem = Fpos_visible_in_window_p (pos, window, Qt, Qnil); if (!NILP (tem)) { Lisp_Object x = XCAR (tem); diff --git a/src/window.c b/src/window.c index 9f6b489e74..1a0163c02e 100644 --- a/src/window.c +++ b/src/window.c @@ -1540,13 +1540,23 @@ WINDOW must be a live window and defaults to the selected one. */) return Fmarker_position (decode_live_window (window)->old_pointm); } -DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, +DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 2, 0, doc: /* Return position at which display currently starts in WINDOW. WINDOW must be a live window and defaults to the selected one. -This is updated by redisplay or by calling `set-window-start'. */) - (Lisp_Object window) +This is updated by redisplay or by calling `set-window-start'. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), return the start position of +the group rather than of the individual WINDOW. This condition holds when +`window-start-group-function' is set to a function, in which case +`window-start' calls the function with the argument WINDOW, then returns its +result, instead of doing its normal processing. */) + (Lisp_Object window, Lisp_Object group) { - return Fmarker_position (decode_live_window (window)->start); + return (!NILP (group) + && FUNCTIONP (Vwindow_start_group_function)) + ? call1 (Vwindow_start_group_function, window) + : Fmarker_position (decode_live_window (window)->start); } /* This is text temporarily removed from the doc string below. @@ -1560,7 +1570,7 @@ have been if redisplay had finished, do this: (vertical-motion (1- (window-height window)) window) (point))") */ -DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0, +DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 3, 0, doc: /* Return position at which display currently ends in WINDOW. WINDOW must be a live window and defaults to the selected one. This is updated by redisplay, when it runs to completion. @@ -1569,65 +1579,77 @@ 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. */) - (Lisp_Object window, Lisp_Object update) -{ - Lisp_Object value; - struct window *w = decode_live_window (window); - Lisp_Object buf; - struct buffer *b; - - buf = w->contents; - CHECK_BUFFER (buf); - b = XBUFFER (buf); - - if (! NILP (update) - && (windows_or_buffers_changed - || !w->window_end_valid - || b->clip_changed - || b->prevent_redisplay_optimizations_p - || window_outdated (w)) - /* Don't call display routines if we didn't yet create any real - frames, because the glyph matrices are not yet allocated in - that case. This could happen in some code that runs in the - daemon during initialization (e.g., see bug#20565). */ - && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) - { - struct text_pos startp; - struct it it; - struct buffer *old_buffer = NULL; - void *itdata = NULL; - - /* Cannot use Fvertical_motion because that function doesn't - cope with variable-height lines. */ - if (b != current_buffer) - { - old_buffer = current_buffer; - set_buffer_internal (b); - } - - /* In case W->start is out of the range, use something - reasonable. This situation occurred when loading a file with - `-l' containing a call to `rmail' with subsequent other - commands. At the end, W->start happened to be BEG, while - rmail had already narrowed the buffer. */ - CLIP_TEXT_POS_FROM_MARKER (startp, w->start); - - itdata = bidi_shelve_cache (); - start_display (&it, w, startp); - move_it_vertically (&it, window_box_height (w)); - if (it.current_y < it.last_visible_y) - move_it_past_eol (&it); - value = make_number (IT_CHARPOS (it)); - bidi_unshelve_cache (itdata, false); - - if (old_buffer) - set_buffer_internal (old_buffer); - } - else - XSETINT (value, BUF_Z (b) - w->window_end_pos); +if it isn't already recorded. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), return the end position of +the group rather than of the individual WINDOW. This condition holds when +`window-end-group-function' is set to a function, in which case `window-end' +calls the function with the two arguments WINDOW and UPDATE, then returns its +result, instead of doing its normal processing. */) + (Lisp_Object window, Lisp_Object update, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vwindow_end_group_function)) + return call2 (Vwindow_end_group_function, window, update); + { + Lisp_Object value; + struct window *w = decode_live_window (window); + Lisp_Object buf; + struct buffer *b; + + buf = w->contents; + CHECK_BUFFER (buf); + b = XBUFFER (buf); + + if (! NILP (update) + && (windows_or_buffers_changed + || !w->window_end_valid + || b->clip_changed + || b->prevent_redisplay_optimizations_p + || window_outdated (w)) + /* Don't call display routines if we didn't yet create any real + frames, because the glyph matrices are not yet allocated in + that case. This could happen in some code that runs in the + daemon during initialization (e.g., see bug#20565). */ + && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) + { + struct text_pos startp; + struct it it; + struct buffer *old_buffer = NULL; + void *itdata = NULL; + + /* Cannot use Fvertical_motion because that function doesn't + cope with variable-height lines. */ + if (b != current_buffer) + { + old_buffer = current_buffer; + set_buffer_internal (b); + } + + /* In case W->start is out of the range, use something + reasonable. This situation occurred when loading a file with + `-l' containing a call to `rmail' with subsequent other + commands. At the end, W->start happened to be BEG, while + rmail had already narrowed the buffer. */ + CLIP_TEXT_POS_FROM_MARKER (startp, w->start); + + itdata = bidi_shelve_cache (); + start_display (&it, w, startp); + move_it_vertically (&it, window_box_height (w)); + if (it.current_y < it.last_visible_y) + move_it_past_eol (&it); + value = make_number (IT_CHARPOS (it)); + bidi_unshelve_cache (itdata, false); + + if (old_buffer) + set_buffer_internal (old_buffer); + } + else + XSETINT (value, BUF_Z (b) - w->window_end_pos); - return value; + return value; + } } DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0, @@ -1666,30 +1688,43 @@ Return POS. */) return pos; } -DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, +DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0, doc: /* Make display in 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. */) - (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) -{ - register struct window *w = decode_live_window (window); +overriding motion of point in order to display at this exact start. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), set the start position of +the group rather than of the individual WINDOW. This condition holds when +`set-window-start-group-function' is set to a function, in which case +`set-window-start' calls the function with the three arguments WINDOW, POS, +and NOFORCE, then returns its result, instead of doing its normal +processing. */) + (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vset_window_start_group_function)) + return call3 (Vset_window_start_group_function, window, pos, noforce); + { + register struct window *w = decode_live_window (window); - set_marker_restricted (w->start, pos, w->contents); - /* This is not right, but much easier than doing what is right. */ - w->start_at_line_beg = false; - if (NILP (noforce)) - w->force_start = true; - wset_update_mode_line (w); - /* Bug#15957. */ - w->window_end_valid = false; - wset_redisplay (w); + set_marker_restricted (w->start, pos, w->contents); + /* This is not right, but much easier than doing what is right. */ + w->start_at_line_beg = false; + if (NILP (noforce)) + w->force_start = true; + wset_update_mode_line (w); + /* Bug#15957. */ + w->window_end_valid = false; + wset_redisplay (w); - return pos; + return pos; + } } DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, - Spos_visible_in_window_p, 0, 3, 0, + Spos_visible_in_window_p, 0, 4, 0, doc: /* Return non-nil if position POS is currently on the frame in WINDOW. WINDOW must be a live window and defaults to the selected one. @@ -1709,9 +1744,21 @@ 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). */) - (Lisp_Object pos, Lisp_Object window, Lisp_Object partially) -{ +(zero-based). + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), test whether POS is visible +in the group of windows rather than in the individual WINDOW. This +condition holds when `pos-visible-in-window-p-function' is set to a +function, in which case `pos-visible-in-window-p' calls the function with +the three arguments POS, WINDOW, and PARTIALLY, then returns its result, +instead of doing its normal processing. */) + (Lisp_Object pos, Lisp_Object window, Lisp_Object partially, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vpos_visible_in_window_p_group_function)) + return call3 (Vpos_visible_in_window_p_group_function, pos, window, partially); + { struct window *w; EMACS_INT posint; struct buffer *buf; @@ -1760,6 +1807,7 @@ POS, ROWH is the visible height of that row, and VPOS is the row number } return in_window; + } } DEFUN ("window-line-height", Fwindow_line_height, @@ -5157,7 +5205,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) } XSETFASTINT (tem, PT); - tem = Fpos_visible_in_window_p (tem, window, Qnil); + tem = Fpos_visible_in_window_p (tem, window, Qnil, Qnil); if (NILP (tem)) { @@ -5546,7 +5594,7 @@ displayed_window_lines (struct window *w) } -DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", +DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "P\ni", doc: /* Center point in selected window and maybe redisplay frame. With a numeric prefix argument ARG, recenter putting point on screen line ARG relative to the selected window. If ARG is negative, it counts up from the @@ -5560,208 +5608,221 @@ 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. */) - (register Lisp_Object arg) -{ - struct window *w = XWINDOW (selected_window); - struct buffer *buf = XBUFFER (w->contents); - bool center_p = false; - ptrdiff_t charpos, bytepos; - EMACS_INT iarg IF_LINT (= 0); - int this_scroll_margin; - - if (buf != current_buffer) - error ("`recenter'ing a window that does not display current-buffer."); +and redisplay normally--don't erase and redraw the frame. + +When `recenter' is called from a program, GROUP is non-nil, and WINDOW is +part of a group of windows collectively displaying a buffer (such as with +Follow Mode), perform `recenter''s actions on the group rather than on the +individual WINDOW. This condition holds when `recenter-group-function' is +set to a function, in which case `recenter' calls the function with the +argument ARG, then returns its value, instead of doing its normal +processing. */) + (register Lisp_Object arg, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vrecenter_group_function)) + return call1 (Vrecenter_group_function, arg); + { + struct window *w = XWINDOW (selected_window); + struct buffer *buf = XBUFFER (w->contents); + bool center_p = false; + ptrdiff_t charpos, bytepos; + EMACS_INT iarg IF_LINT (= 0); + int this_scroll_margin; - /* If redisplay is suppressed due to an error, try again. */ - buf->display_error_modiff = 0; + if (buf != current_buffer) + error ("`recenter'ing a window that does not display current-buffer."); - if (NILP (arg)) - { - if (!NILP (Vrecenter_redisplay) - && (!EQ (Vrecenter_redisplay, Qtty) - || !NILP (Ftty_type (selected_frame)))) - { - ptrdiff_t i; + /* If redisplay is suppressed due to an error, try again. */ + buf->display_error_modiff = 0; - /* Invalidate pixel data calculated for all compositions. */ - for (i = 0; i < n_compositions; i++) - composition_table[i]->font = NULL; + if (NILP (arg)) + { + if (!NILP (Vrecenter_redisplay) + && (!EQ (Vrecenter_redisplay, Qtty) + || !NILP (Ftty_type (selected_frame)))) + { + ptrdiff_t i; + + /* Invalidate pixel data calculated for all compositions. */ + for (i = 0; i < n_compositions; i++) + composition_table[i]->font = NULL; #if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) - WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; + WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; #endif - Fredraw_frame (WINDOW_FRAME (w)); - SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); - } + Fredraw_frame (WINDOW_FRAME (w)); + SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); + } + center_p = true; + } + else if (CONSP (arg)) /* Just C-u. */ center_p = true; - } - else if (CONSP (arg)) /* Just C-u. */ - center_p = true; - else - { - arg = Fprefix_numeric_value (arg); - CHECK_NUMBER (arg); - iarg = XINT (arg); - } - - /* Do this after making BUF current - in case scroll_margin is buffer-local. */ - this_scroll_margin - = max (0, min (scroll_margin, w->total_lines / 4)); - - /* Don't use redisplay code for initial frames, as the necessary - data structures might not be set up yet then. */ - if (!FRAME_INITIAL_P (XFRAME (w->frame))) - { - if (center_p) - { - struct it it; - struct text_pos pt; - void *itdata = bidi_shelve_cache (); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - move_it_vertically_backward (&it, window_box_height (w) / 2); - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - bidi_unshelve_cache (itdata, false); - } - else if (iarg < 0) - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); - int extra_line_spacing; - int h = window_box_height (w); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin + 1, nlines, - ht - this_scroll_margin); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Be sure we have the exact height of the full line containing PT. */ - move_it_by_lines (&it, 0); - - /* The amount of pixels we have to move back is the window - height minus what's displayed in the line containing PT, - and the lines below. */ - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, nlines); - - if (it.vpos == nlines) - h -= it.current_y; - else - { - /* Last line has no newline. */ - h -= line_bottom_y (&it); - it.vpos++; - } - - /* Don't reserve space for extra line spacing of last line. */ - extra_line_spacing = it.max_extra_line_spacing; - - /* If we can't move down NLINES lines because we hit - the end of the buffer, count in some empty lines. */ - if (it.vpos < nlines) - { - nlines -= it.vpos; - extra_line_spacing = it.extra_line_spacing; - h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); - } - if (h <= 0) - { - bidi_unshelve_cache (itdata, false); - return Qnil; - } - - /* Now find the new top line (starting position) of the window. */ - start_display (&it, w, pt); - it.current_y = 0; - move_it_vertically_backward (&it, h); - - /* If extra line spacing is present, we may move too far - back. This causes the last line to be only partially - visible (which triggers redisplay to recenter that line - in the middle), so move forward. - But ignore extra line spacing on last line, as it is not - considered to be part of the visible height of the line. - */ - h += extra_line_spacing; - while (-it.current_y > h) - move_it_by_lines (&it, 1); - - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - - bidi_unshelve_cache (itdata, false); - } - else - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin, nlines, - ht - this_scroll_margin - 1); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Move to the beginning of screen line containing PT. */ - move_it_by_lines (&it, 0); - - /* Move back to find the point which is ARG screen lines above PT. */ - if (nlines > 0) - { - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, -nlines); - } + else + { + arg = Fprefix_numeric_value (arg); + CHECK_NUMBER (arg); + iarg = XINT (arg); + } - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); + /* Do this after making BUF current + in case scroll_margin is buffer-local. */ + this_scroll_margin + = max (0, min (scroll_margin, w->total_lines / 4)); - bidi_unshelve_cache (itdata, false); - } - } - else - { - struct position pos; - int ht = window_internal_height (w); + /* Don't use redisplay code for initial frames, as the necessary + data structures might not be set up yet then. */ + if (!FRAME_INITIAL_P (XFRAME (w->frame))) + { + if (center_p) + { + struct it it; + struct text_pos pt; + void *itdata = bidi_shelve_cache (); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + move_it_vertically_backward (&it, window_box_height (w) / 2); + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + bidi_unshelve_cache (itdata, false); + } + else if (iarg < 0) + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); + int extra_line_spacing; + int h = window_box_height (w); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); + + nlines = clip_to_bounds (this_scroll_margin + 1, nlines, + ht - this_scroll_margin); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Be sure we have the exact height of the full line containing PT. */ + move_it_by_lines (&it, 0); + + /* The amount of pixels we have to move back is the window + height minus what's displayed in the line containing PT, + and the lines below. */ + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, nlines); + + if (it.vpos == nlines) + h -= it.current_y; + else + { + /* Last line has no newline. */ + h -= line_bottom_y (&it); + it.vpos++; + } + + /* Don't reserve space for extra line spacing of last line. */ + extra_line_spacing = it.max_extra_line_spacing; + + /* If we can't move down NLINES lines because we hit + the end of the buffer, count in some empty lines. */ + if (it.vpos < nlines) + { + nlines -= it.vpos; + extra_line_spacing = it.extra_line_spacing; + h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); + } + if (h <= 0) + { + bidi_unshelve_cache (itdata, false); + return Qnil; + } + + /* Now find the new top line (starting position) of the window. */ + start_display (&it, w, pt); + it.current_y = 0; + move_it_vertically_backward (&it, h); + + /* If extra line spacing is present, we may move too far + back. This causes the last line to be only partially + visible (which triggers redisplay to recenter that line + in the middle), so move forward. + But ignore extra line spacing on last line, as it is not + considered to be part of the visible height of the line. + */ + h += extra_line_spacing; + while (-it.current_y > h) + move_it_by_lines (&it, 1); + + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + bidi_unshelve_cache (itdata, false); + } + else + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); + + nlines = clip_to_bounds (this_scroll_margin, nlines, + ht - this_scroll_margin - 1); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Move to the beginning of screen line containing PT. */ + move_it_by_lines (&it, 0); + + /* Move back to find the point which is ARG screen lines above PT. */ + if (nlines > 0) + { + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, -nlines); + } + + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + bidi_unshelve_cache (itdata, false); + } + } + else + { + struct position pos; + int ht = window_internal_height (w); - if (center_p) - iarg = ht / 2; - else if (iarg < 0) - iarg += ht; + if (center_p) + iarg = ht / 2; + else if (iarg < 0) + iarg += ht; - /* Don't let it get into the margin at either top or bottom. */ - iarg = clip_to_bounds (this_scroll_margin, iarg, - ht - this_scroll_margin - 1); + /* Don't let it get into the margin at either top or bottom. */ + iarg = clip_to_bounds (this_scroll_margin, iarg, + ht - this_scroll_margin - 1); - pos = *vmotion (PT, PT_BYTE, - iarg, w); - charpos = pos.bufpos; - bytepos = pos.bytepos; - } + pos = *vmotion (PT, PT_BYTE, - iarg, w); + charpos = pos.bufpos; + bytepos = pos.bytepos; + } - /* Set the new window start. */ - set_marker_both (w->start, w->contents, charpos, bytepos); - w->window_end_valid = false; + /* Set the new window start. */ + set_marker_both (w->start, w->contents, charpos, bytepos); + w->window_end_valid = false; - w->optional_new_start = true; + w->optional_new_start = true; - w->start_at_line_beg = (bytepos == BEGV_BYTE - || FETCH_BYTE (bytepos - 1) == '\n'); + w->start_at_line_beg = (bytepos == BEGV_BYTE + || FETCH_BYTE (bytepos - 1) == '\n'); - wset_redisplay (w); + wset_redisplay (w); - return Qnil; + return Qnil; + } } DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width, @@ -5808,52 +5869,68 @@ pixels. */) } DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, - 1, 1, "P", + 1, 2, "P\ni", doc: /* Position point relative to window. ARG nil means position point at center of window. Else, ARG specifies vertical position within the window; -zero means top of window, negative means relative to bottom of window. */) - (Lisp_Object arg) -{ - struct window *w = XWINDOW (selected_window); - int lines, start; - Lisp_Object window; +zero means top of window, negative means relative to bottom of window. + +When GROUP is non-nil, and `move-to-window-line-group-function' is set to a +function, then instead of the above, that function is called with the +single argument ARG, and its result returned. + +If GROUP is non-nil, and WINDOW is part of a group of windows collectively +displaying a buffer (such as with Follow Mode), position point relative to +the group of windows as a whole rather than the individual WINDOW. This +condition holds when `move-to-window-line-group-function' is set to a +function, in which case `move-to-window-line' calls the function with the +argument ARG, then returns its result, instead of doing its normal +processing. */) + (Lisp_Object arg, Lisp_Object group) +{ + if (!NILP (group) + && FUNCTIONP (Vmove_to_window_line_group_function)) + return call1 (Vmove_to_window_line_group_function, arg); + { + struct window *w = XWINDOW (selected_window); + int lines, start; + Lisp_Object window; #if false - int this_scroll_margin; + int this_scroll_margin; #endif - if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) - /* This test is needed to make sure PT/PT_BYTE make sense in w->contents - when passed below to set_marker_both. */ - error ("move-to-window-line called from unrelated buffer"); + if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) + /* This test is needed to make sure PT/PT_BYTE make sense in w->contents + when passed below to set_marker_both. */ + error ("move-to-window-line called from unrelated buffer"); - window = selected_window; - start = marker_position (w->start); - if (start < BEGV || start > ZV) - { - int height = window_internal_height (w); - Fvertical_motion (make_number (- (height / 2)), window, Qnil); - set_marker_both (w->start, w->contents, PT, PT_BYTE); - w->start_at_line_beg = !NILP (Fbolp ()); - w->force_start = true; - } - else - Fgoto_char (w->start); + window = selected_window; + start = marker_position (w->start); + if (start < BEGV || start > ZV) + { + int height = window_internal_height (w); + Fvertical_motion (make_number (- (height / 2)), window, Qnil); + set_marker_both (w->start, w->contents, PT, PT_BYTE); + w->start_at_line_beg = !NILP (Fbolp ()); + w->force_start = true; + } + else + Fgoto_char (w->start); - lines = displayed_window_lines (w); + lines = displayed_window_lines (w); #if false - this_scroll_margin = max (0, min (scroll_margin, lines / 4)); + this_scroll_margin = max (0, min (scroll_margin, lines / 4)); #endif - if (NILP (arg)) - XSETFASTINT (arg, lines / 2); - else - { - EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); + if (NILP (arg)) + XSETFASTINT (arg, lines / 2); + else + { + EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); - if (iarg < 0) - iarg = iarg + lines; + if (iarg < 0) + iarg = iarg + lines; #if false /* This code would prevent move-to-window-line from moving point to a place inside the scroll margins (which would cause the @@ -5861,19 +5938,20 @@ zero means top of window, negative means relative to bottom of window. */) it is probably better not to install it. However, it is here inside #if false so as not to lose it. -- rms. */ - /* Don't let it get into the margin at either top or bottom. */ - iarg = max (iarg, this_scroll_margin); - iarg = min (iarg, lines - this_scroll_margin - 1); + /* Don't let it get into the margin at either top or bottom. */ + iarg = max (iarg, this_scroll_margin); + iarg = min (iarg, lines - this_scroll_margin - 1); #endif - arg = make_number (iarg); - } + arg = make_number (iarg); + } - /* Skip past a partially visible first line. */ - if (w->vscroll) - XSETINT (arg, XINT (arg) + 1); + /* Skip past a partially visible first line. */ + if (w->vscroll) + XSETINT (arg, XINT (arg) + 1); - return Fvertical_motion (arg, window, Qnil); + return Fvertical_motion (arg, window, Qnil); + } } @@ -7147,6 +7225,12 @@ syms_of_window (void) DEFSYM (Qclone_of, "clone-of"); DEFSYM (Qfloor, "floor"); DEFSYM (Qceiling, "ceiling"); + DEFSYM (Qwindow_start_group_function, "window-start-group-function"); + DEFSYM (Qwindow_end_group_function, "window-end-group-function"); + DEFSYM (Qset_window_start_group_function, "set-window-start-group-function"); + DEFSYM (Qrecenter_group_function, "recenter-group-function"); + DEFSYM (Qpos_visible_in_window_p_group_function, "pos-visible-in-window-p-group-function"); + DEFSYM (Qmove_to_window_line_group_function, "move-to-window-line-group-function"); staticpro (&Vwindow_list); @@ -7318,6 +7402,72 @@ Note that this optimization can cause the portion of the buffer displayed after a scrolling operation to be somewhat inaccurate. */); Vfast_but_imprecise_scrolling = false; + DEFVAR_LISP ("window-start-group-function", Vwindow_start_group_function, + doc: /* Function to call for `window-start' when its GROUP parameter is non-nil. +When this variable contains a function, and `window-start' is called with a +non-nil GROUP parameter, the function is called instead of `window-start''s +normal action. `window-start' passes the function its argument WINDOW, which +might be nil. The function may recursively call `window-start'. */); + Vwindow_start_group_function = Qnil; + Fmake_variable_buffer_local (Qwindow_start_group_function); + Fput (Qwindow_start_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("window-end-group-function", Vwindow_end_group_function, + doc: /* Function to call for `window-end' if its GROUP parameter is non-nil. +When this variable contains a function, and `window-end' is called with a +non-nil GROUP parameter, the function is called instead of `window-end''s +normal action. `window-end' passes the function its two parameters WINDOW, +and UPDATE. The function may call `window-end' recursively. */); + Vwindow_end_group_function = Qnil; + Fmake_variable_buffer_local (Qwindow_end_group_function); + Fput (Qwindow_end_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("set-window-start-group-function", + Vset_window_start_group_function, + doc: /* The function to call for `set-window-start' when its GROUP parameter is non-nil. +When this variable contains a function, and `set-window-start' is called +with a non-nil GROUP parameter, the function is called instead of +`set-window-start''s normal action. `set-window-start' passes the function +its three parameters WINDOW, POS, and NOFORCE. The function may call +`set-window-start' recursively. */); + Vset_window_start_group_function = Qnil; + Fmake_variable_buffer_local (Qset_window_start_group_function); + Fput (Qset_window_start_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("recenter-group-function", Vrecenter_group_function, + doc: /* Function to call for `recenter' when its GROUP parameter is non-nil. +When this variable contains a function, and `recenter' is called with a +non-nil GROUP parameter, the function is called instead of `recenter''s +normal action. `recenter' passes the function its parameter ARG. The +function may call `recenter' recursively. */); + Vrecenter_group_function = Qnil; + Fmake_variable_buffer_local (Qrecenter_group_function); + Fput (Qrecenter_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("pos-visible-in-window-p-group-function", + Vpos_visible_in_window_p_group_function, + doc: /* Function for `pos-visible-in-window-p' when its GROUP parameter is non-nil. +When this variable contains a function, and `pos-visible-in-window-p' is +called with a non-nil GROUP parameter, the function is called instead of +`pos-visible-in-window-p''s normal action. `pos-visible-in-window-p' +passes the function its three parameters POS, WINDOW, and PARTIALLY. The +function may call `pos-visible-in-window-p' recursively. */); + Vpos_visible_in_window_p_group_function = Qnil; + Fmake_variable_buffer_local (Qpos_visible_in_window_p_group_function); + Fput (Qpos_visible_in_window_p_group_function, Qpermanent_local, Qt); + + DEFVAR_LISP ("move-to-window-line-group-function", + Vmove_to_window_line_group_function, + doc: /* Function for `move-to-window-line' when its GROUP parameter is non-nil. +When this variable contains a function, and `move-to-window-line' is +called with a non-nil GROUP parameter, the function is called instead of +`move-to-window-line''s normal action. `move-to-window-line' passes the +function its parameter ARG. The function may call `move-to-window-line' +recursively. */); + Vmove_to_window_line_group_function = Qnil; + Fmake_variable_buffer_local (Qmove_to_window_line_group_function); + Fput (Qmove_to_window_line_group_function, Qpermanent_local, Qt); + defsubr (&Sselected_window); defsubr (&Sminibuffer_window); defsubr (&Swindow_minibuffer_p); -- cgit v1.2.3 From bf510d8ab047fc44fe7ed3521944c23b9476375b Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Wed, 18 Nov 2015 21:14:30 +0000 Subject: lisp/isearch.el: Eliminate macro isearch-call-message, replacing with funcall. --- lisp/isearch.el | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) diff --git a/lisp/isearch.el b/lisp/isearch.el index 39d573cc01..12ded02345 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -177,11 +177,6 @@ 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, @@ -983,7 +978,7 @@ The last thing it does is to run `isearch-update-post-hook'." (null executing-kbd-macro)) (progn (if (not (input-pending-p)) - (isearch-call-message)) + (funcall (or isearch-message-function #'isearch-message))) (if (and isearch-slow-terminal-mode (not (or isearch-small-window (pos-visible-in-window-p nil nil nil t)))) @@ -1317,7 +1312,7 @@ You can update the global isearch variables by setting new values to isearch-case-fold-search isearch-new-case-fold) ;; Restore the minibuffer message before moving point. - (isearch-call-message nil t) + (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) @@ -1937,7 +1932,8 @@ 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. + ;; 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, @@ -2114,7 +2110,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. - (isearch-call-message nil t) ; Do this before moving 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 @@ -2468,7 +2465,7 @@ Search is updated accordingly." (isearch-ring-adjust1 advance) (if search-ring-update (progn - (isearch-call-message nil t) + (funcall (or isearch-message-function #'isearch-message) nil t) (isearch-search) (isearch-push-state) (isearch-update)) -- cgit v1.2.3 From f5c403d269f4f5079dc47853f16a51e9542ea238 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 7 Dec 2015 10:39:07 +0000 Subject: Amend doc of `mapconcat': it can take sequences, not merely strings. * doc/lispref/functions.texi (Mapping Functions): Amend the doc of `mapconcat' to say that SEPARATOR and the results from FUNCTION may be any character sequences, not just strings. Add an @xref to "Sequences Arrays Vectors". --- doc/lispref/functions.texi | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/doc/lispref/functions.texi b/doc/lispref/functions.texi index 8835667b82..7cc041fa77 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 -@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 -- cgit v1.2.3 From 3194f1ccd99cfd13ddaf621d6e7f1e1aa1645165 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 7 Dec 2015 15:12:15 +0000 Subject: Further progress making Isearch, Ispell, Replace work with Follow Mode. * lisp/follow.el: (follow-mode): Remove references to sit*-for-function, which no longer exists. Add follow-post-command-hook to three special purpose hooks at setup, and remove them at tear down. * lisp/isearch.el: (isearch-update): invoke isearch-update-post-hook before isearch-lazy-highlight-new-loop. (isearch-lazy-highlight-new-loop): Restore this function to what it previously was, merging the functionality of isearch-lazy-highlight-maybe-new-loop into it. (isearch-lazy-highlight-maybe-new-loop): function removed. * lisp/replace.el: (replace-update-post-hook): New hook variable. (perform-replace): Add second (nil) argument to looking-back. Invoke replace-update-post-hook before calling replace-highlight. * lisp/textmodes/ispell.el: (ispell-update-post-hook): New hook variable. (ispell-command-loop): invoke ispell-update-post-hook. Add GROUP argument to call of pos-visible-in-window-p. (ispell-display-buffer): Place *Choices* window at the top of the last window in a window group. --- lisp/follow.el | 10 +++++++--- lisp/isearch.el | 31 +++++++++++-------------------- lisp/replace.el | 8 ++++++-- lisp/textmodes/ispell.el | 20 +++++++++++++++++--- 4 files changed, 41 insertions(+), 28 deletions(-) diff --git a/lisp/follow.el b/lisp/follow.el index 2cbf0f2b93..609b29f7cc 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -423,6 +423,9 @@ Keys specific to Follow mode: (add-hook 'post-command-hook 'follow-post-command-hook 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-start-group-function 'follow-window-start) (setq window-end-group-function 'follow-window-end) @@ -431,8 +434,7 @@ Keys specific to Follow mode: (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)) + (setq move-to-window-line-group-function 'follow-move-to-window-line)) ;; Remove globally-installed hook functions only if there is no ;; other Follow mode buffer. @@ -445,7 +447,6 @@ Keys specific to Follow mode: (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) @@ -454,6 +455,9 @@ Keys specific to Follow mode: (kill-local-variable 'window-end-group-function) (kill-local-variable 'window-start-group-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))) diff --git a/lisp/isearch.el b/lisp/isearch.el index 12ded02345..8e9a686dca 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 @@ -1018,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. @@ -3068,21 +3069,7 @@ is nil. This function is called when exiting an incremental search if "22.1") (defun isearch-lazy-highlight-new-loop (&optional beg end) - "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. + "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 @@ -3118,6 +3105,8 @@ by other Emacs features." ;; 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-group (selected-window-group) isearch-lazy-highlight-window-start (window-start nil t) @@ -3140,7 +3129,9 @@ by other Emacs features." isearch-lazy-highlight-regexp-function isearch-regexp-function isearch-lazy-highlight-forward isearch-forward) (unless (equal isearch-string "") - (isearch-lazy-highlight-update)))) + (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. diff --git a/lisp/replace.el b/lisp/replace.el index 54b3a71bda..d48f4f3fdf 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 fe27f0f158..7d5bb6dbc5 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -2248,6 +2248,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 +2320,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-p end nil nil t)) (sit-for 0)) ;; Display choices for misspelled word. @@ -2844,13 +2851,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) -- cgit v1.2.3 From e8937de5547a687b6d03199368645f168cb8ad37 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 14 Dec 2015 16:38:07 +0000 Subject: Replace GROUP argument in six window primitives by new functions. * doc/lispref/windows.texi (Window Start and End, Textual Scrolling) * doc/lispref/positions.texi (Screen Lines): Remove optional GROUP argument from description of six window functions. Add in description of new functions window-group-start, window-group-end, set-window-group-start, pos-visible-in-window-group-p, recenter-group and move-to-window-group-line, together with the six variables indirecting to the pertinent group functions. * src/window.c * src/keyboard.c: Revert the commit from 2015-11-11 12:02:48, in so far as it applies to these two files, which added the GROUP argument to six window primitives. * lisp/follow.el (follow-mode): Use updated variable names for the indirected functions. * lisp/isearch.el (isearch-update, isearch-done, isearch-string-out-of-window) (isearch-back-into-window, isearch-lazy-highlight-new-loop) (isearch-lazy-highlight-search, isearch-lazy-highlight-update): Replace calls to window primitives (e.g. window-start) with a GROUP argument by calls to new functions (e.g. window-group-start). * lisp/ispell.el (ispell-command-loop): Replace call to pos-visible-in-window-p with pos-visible-in-window-group-p. * lisp/window.el (window-group-start, window-group-end) (set-window-group-start, recenter-group, pos-visible-in-window-group-p) (selected-window-group, move-to-window-group-line): New functions. (window-group-start-function, window-group-end-function) (set-window-group-start-function, recenter-group-function) (pos-visible-in-window-group-p-function, selected-window-group-function) (move-to-window-group-line-function): New variables. --- doc/lispref/positions.texi | 26 +- doc/lispref/windows.texi | 116 +++---- lisp/follow.el | 20 +- lisp/isearch.el | 54 ++-- lisp/textmodes/ispell.el | 2 +- lisp/window.el | 157 ++++++++- src/keyboard.c | 2 +- src/window.c | 772 ++++++++++++++++++--------------------------- 8 files changed, 572 insertions(+), 577 deletions(-) diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index e0496e3084..090eb4598a 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi @@ -551,8 +551,7 @@ 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 group -@vindex move-to-window-line-group-function +@deffn Command move-to-window-line count 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 @@ -571,18 +570,21 @@ 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 +@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{Basic +Windows}), @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. The argument has +the same meaning as in @code{move-to-window-line}. +@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 diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index f92289f51a..45899588c3 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -3104,8 +3104,7 @@ 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 group -@vindex window-start-group-function +@defun window-start &optional window @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 @@ -3121,20 +3120,22 @@ 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. +@end defun -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. +@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{Basic Windows}), +@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. The argument to this +function has the same meaning as in @code{window-start}. @end defun -@vindex window-end-group-function @cindex window end position -@defun window-end &optional window update group +@defun window-end &optional window update This function returns the position where display of its buffer ends in @var{window}. The default for @var{window} is the selected window. @@ -3157,19 +3158,21 @@ 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. +@end defun -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. +@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{Basic Windows}), +@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 arguments +to this function have the same meaning as in @code{window-end}. @end defun -@vindex set-window-start-group-function -@defun set-window-start window position &optional noforce group +@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}. @@ -3230,20 +3233,22 @@ 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. +@end defun -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. +@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{Basic Windows}), +@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 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 group -@vindex pos-visible-in-window-p-group-function +@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 returns @code{nil} if @var{position} is scrolled vertically out of @@ -3282,18 +3287,19 @@ Here is an example: (recenter 0)) @end group @end example +@end defun -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. +@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{Basic Windows}), +@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. @end defun @defun window-line-height &optional line window @@ -3511,8 +3517,7 @@ 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 group -@vindex recenter-group-function +@deffn Command recenter &optional count @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 @@ -3529,14 +3534,6 @@ 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 @@ -3548,6 +3545,17 @@ the top of the window. The command @code{recenter-top-bottom} offers a more convenient way to achieve this. @end deffn +@vindex recenter-group-function +@defun recenter-group &optional count +This function is like @code{recenter}, except that when the selected +window is part of a group of windows (@pxref{Basic Windows}), +@code{recenter-group} 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-group} calls the function +with the argument @var{count}, then returns its result. The argument +has the same meaning as in @code{recenter}. +@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 diff --git a/lisp/follow.el b/lisp/follow.el index 609b29f7cc..3a876bcb21 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -427,14 +427,14 @@ Keys specific to Follow mode: (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-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 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-group-function 'follow-recenter) - (setq pos-visible-in-window-p-group-function + (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-line-group-function 'follow-move-to-window-line)) + (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. @@ -447,13 +447,13 @@ Keys specific to Follow mode: (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-line-group-function) + (kill-local-variable 'move-to-window-group-line-function) (kill-local-variable 'selected-window-group-function) - (kill-local-variable 'pos-visible-in-window-p-group-function) + (kill-local-variable 'pos-visible-in-window-group-p-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) + (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) diff --git a/lisp/isearch.el b/lisp/isearch.el index 8e9a686dca..b29e432f37 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -982,7 +982,7 @@ The last thing is to trigger a new round of lazy highlighting." (funcall (or isearch-message-function #'isearch-message))) (if (and isearch-slow-terminal-mode (not (or isearch-small-window - (pos-visible-in-window-p nil nil nil t)))) + (pos-visible-in-window-group-p)))) (let ((found-point (point))) (setq isearch-small-window t) (move-to-window-line 0) @@ -1003,10 +1003,10 @@ The last thing is to trigger a new round of lazy highlighting." (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 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))) @@ -1057,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 nil t)) + (let ((found-start (window-group-start)) (found-point (point))) (when isearch-window-configuration (set-window-configuration isearch-window-configuration) @@ -1067,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 t)))) + (set-window-group-start (selected-window) found-start t)))) (setq isearch-mode nil) (if isearch-input-method-local-p @@ -2279,12 +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 nil t)) - (w-end (window-end nil t t)) + (let ((w-start (window-group-start)) + (w-end (window-group-end nil t)) (w-L1 (save-excursion - (save-selected-window (move-to-window-line 1 t) (point)))) + (save-selected-window (move-to-window-group-line 1) (point)))) (w-L-1 (save-excursion - (save-selected-window (move-to-window-line -1 t) (point)))) + (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)) @@ -2311,15 +2311,15 @@ the bottom." (if above (progn (goto-char start) - (recenter 0 t) - (when (>= isearch-point (window-end nil t t)) + (recenter-group 0) + (when (>= isearch-point (window-group-end nil t)) (goto-char isearch-point) - (recenter -1 t))) + (recenter-group -1))) (goto-char end) - (recenter -1 t) - (when (< isearch-point (window-start nil t)) + (recenter-group -1) + (when (< isearch-point (window-group-start)) (goto-char isearch-point) - (recenter 0 t)))) + (recenter-group 0)))) (goto-char isearch-point)) (defvar isearch-pre-scroll-point nil) @@ -3090,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 nil t) + (not (= (window-group-start) isearch-lazy-highlight-window-start)) - (not (= (window-end nil nil t) ; 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)) @@ -3109,8 +3109,8 @@ by other Emacs features." isearch-lazy-highlight-end-limit end) (setq isearch-lazy-highlight-window (selected-window) 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) + 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) @@ -3154,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 nil nil t))) + (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 nil t)))))) + (window-group-start)))))) ;; Use a loop like in `isearch-search'. (while retry (setq success (isearch-search-string @@ -3204,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 nil nil t))) + (window-group-end))) (setq found nil) (forward-char 1)) (if (= mb (if isearch-lazy-highlight-wrapped isearch-lazy-highlight-end - (window-start nil t))) + (window-group-start))) (setq found nil) (forward-char -1))) @@ -3236,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 nil t)) + (setq isearch-lazy-highlight-end (window-group-start)) (goto-char (max (or isearch-lazy-highlight-start-limit (point-min)) - (window-start nil t)))) - (setq isearch-lazy-highlight-start (window-end nil nil t)) + (window-group-start)))) + (setq isearch-lazy-highlight-start (window-group-end)) (goto-char (min (or isearch-lazy-highlight-end-limit (point-max)) - (window-end nil nil t)))))))) + (window-group-end)))))))) (unless nomore (setq isearch-lazy-highlight-timer (run-at-time lazy-highlight-interval nil diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 7d5bb6dbc5..0f1806c888 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -2323,7 +2323,7 @@ Global `ispell-quit' set to start location to continue spell session." (run-hooks 'ispell-update-post-hook) ;; ensure word is visible - (if (not (pos-visible-in-window-p end nil nil t)) + (if (not (pos-visible-in-window-group-p end)) (sit-for 0)) ;; Display choices for misspelled word. diff --git a/lisp/window.el b/lisp/window.el index c5a1019372..ce0256f1ff 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -28,17 +28,6 @@ ;;; Code: -(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)))) - (defun internal--before-save-selected-window () (cons (selected-window) ;; We save and restore all frames' selected windows, because @@ -7880,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-group-function nil) +(make-variable-buffer-local 'recenter-group-function) +(put 'recenter-group-function 'permanent-local t) +(defun recenter-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-group-function) + (funcall recenter-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. diff --git a/src/keyboard.c b/src/keyboard.c index 725c324eb7..02bc7d2a0b 100644 --- a/src/keyboard.c +++ b/src/keyboard.c @@ -10658,7 +10658,7 @@ The `posn-' functions access elements of such lists. */) if (NILP (window)) window = selected_window; - tem = Fpos_visible_in_window_p (pos, window, Qt, Qnil); + tem = Fpos_visible_in_window_p (pos, window, Qt); if (!NILP (tem)) { Lisp_Object x = XCAR (tem); diff --git a/src/window.c b/src/window.c index 1a0163c02e..9f6b489e74 100644 --- a/src/window.c +++ b/src/window.c @@ -1540,23 +1540,13 @@ WINDOW must be a live window and defaults to the selected one. */) return Fmarker_position (decode_live_window (window)->old_pointm); } -DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 2, 0, +DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, doc: /* Return position at which display currently starts in WINDOW. WINDOW must be a live window and defaults to the selected one. -This is updated by redisplay or by calling `set-window-start'. - -If GROUP is non-nil, and WINDOW is part of a group of windows collectively -displaying a buffer (such as with Follow Mode), return the start position of -the group rather than of the individual WINDOW. This condition holds when -`window-start-group-function' is set to a function, in which case -`window-start' calls the function with the argument WINDOW, then returns its -result, instead of doing its normal processing. */) - (Lisp_Object window, Lisp_Object group) +This is updated by redisplay or by calling `set-window-start'. */) + (Lisp_Object window) { - return (!NILP (group) - && FUNCTIONP (Vwindow_start_group_function)) - ? call1 (Vwindow_start_group_function, window) - : Fmarker_position (decode_live_window (window)->start); + return Fmarker_position (decode_live_window (window)->start); } /* This is text temporarily removed from the doc string below. @@ -1570,7 +1560,7 @@ have been if redisplay had finished, do this: (vertical-motion (1- (window-height window)) window) (point))") */ -DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 3, 0, +DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0, doc: /* Return position at which display currently ends in WINDOW. WINDOW must be a live window and defaults to the selected one. This is updated by redisplay, when it runs to completion. @@ -1579,77 +1569,65 @@ 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 GROUP is non-nil, and WINDOW is part of a group of windows collectively -displaying a buffer (such as with Follow Mode), return the end position of -the group rather than of the individual WINDOW. This condition holds when -`window-end-group-function' is set to a function, in which case `window-end' -calls the function with the two arguments WINDOW and UPDATE, then returns its -result, instead of doing its normal processing. */) - (Lisp_Object window, Lisp_Object update, Lisp_Object group) -{ - if (!NILP (group) - && FUNCTIONP (Vwindow_end_group_function)) - return call2 (Vwindow_end_group_function, window, update); - { - Lisp_Object value; - struct window *w = decode_live_window (window); - Lisp_Object buf; - struct buffer *b; - - buf = w->contents; - CHECK_BUFFER (buf); - b = XBUFFER (buf); - - if (! NILP (update) - && (windows_or_buffers_changed - || !w->window_end_valid - || b->clip_changed - || b->prevent_redisplay_optimizations_p - || window_outdated (w)) - /* Don't call display routines if we didn't yet create any real - frames, because the glyph matrices are not yet allocated in - that case. This could happen in some code that runs in the - daemon during initialization (e.g., see bug#20565). */ - && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) - { - struct text_pos startp; - struct it it; - struct buffer *old_buffer = NULL; - void *itdata = NULL; - - /* Cannot use Fvertical_motion because that function doesn't - cope with variable-height lines. */ - if (b != current_buffer) - { - old_buffer = current_buffer; - set_buffer_internal (b); - } - - /* In case W->start is out of the range, use something - reasonable. This situation occurred when loading a file with - `-l' containing a call to `rmail' with subsequent other - commands. At the end, W->start happened to be BEG, while - rmail had already narrowed the buffer. */ - CLIP_TEXT_POS_FROM_MARKER (startp, w->start); - - itdata = bidi_shelve_cache (); - start_display (&it, w, startp); - move_it_vertically (&it, window_box_height (w)); - if (it.current_y < it.last_visible_y) - move_it_past_eol (&it); - value = make_number (IT_CHARPOS (it)); - bidi_unshelve_cache (itdata, false); - - if (old_buffer) - set_buffer_internal (old_buffer); - } - else - XSETINT (value, BUF_Z (b) - w->window_end_pos); +if it isn't already recorded. */) + (Lisp_Object window, Lisp_Object update) +{ + Lisp_Object value; + struct window *w = decode_live_window (window); + Lisp_Object buf; + struct buffer *b; + + buf = w->contents; + CHECK_BUFFER (buf); + b = XBUFFER (buf); + + if (! NILP (update) + && (windows_or_buffers_changed + || !w->window_end_valid + || b->clip_changed + || b->prevent_redisplay_optimizations_p + || window_outdated (w)) + /* Don't call display routines if we didn't yet create any real + frames, because the glyph matrices are not yet allocated in + that case. This could happen in some code that runs in the + daemon during initialization (e.g., see bug#20565). */ + && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) + { + struct text_pos startp; + struct it it; + struct buffer *old_buffer = NULL; + void *itdata = NULL; + + /* Cannot use Fvertical_motion because that function doesn't + cope with variable-height lines. */ + if (b != current_buffer) + { + old_buffer = current_buffer; + set_buffer_internal (b); + } - return value; - } + /* In case W->start is out of the range, use something + reasonable. This situation occurred when loading a file with + `-l' containing a call to `rmail' with subsequent other + commands. At the end, W->start happened to be BEG, while + rmail had already narrowed the buffer. */ + CLIP_TEXT_POS_FROM_MARKER (startp, w->start); + + itdata = bidi_shelve_cache (); + start_display (&it, w, startp); + move_it_vertically (&it, window_box_height (w)); + if (it.current_y < it.last_visible_y) + move_it_past_eol (&it); + value = make_number (IT_CHARPOS (it)); + bidi_unshelve_cache (itdata, false); + + if (old_buffer) + set_buffer_internal (old_buffer); + } + else + XSETINT (value, BUF_Z (b) - w->window_end_pos); + + return value; } DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0, @@ -1688,43 +1666,30 @@ Return POS. */) return pos; } -DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0, +DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, doc: /* Make display in 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. - -If GROUP is non-nil, and WINDOW is part of a group of windows collectively -displaying a buffer (such as with Follow Mode), set the start position of -the group rather than of the individual WINDOW. This condition holds when -`set-window-start-group-function' is set to a function, in which case -`set-window-start' calls the function with the three arguments WINDOW, POS, -and NOFORCE, then returns its result, instead of doing its normal -processing. */) - (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object group) -{ - if (!NILP (group) - && FUNCTIONP (Vset_window_start_group_function)) - return call3 (Vset_window_start_group_function, window, pos, noforce); - { - register struct window *w = decode_live_window (window); +overriding motion of point in order to display at this exact start. */) + (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) +{ + register struct window *w = decode_live_window (window); - set_marker_restricted (w->start, pos, w->contents); - /* This is not right, but much easier than doing what is right. */ - w->start_at_line_beg = false; - if (NILP (noforce)) - w->force_start = true; - wset_update_mode_line (w); - /* Bug#15957. */ - w->window_end_valid = false; - wset_redisplay (w); + set_marker_restricted (w->start, pos, w->contents); + /* This is not right, but much easier than doing what is right. */ + w->start_at_line_beg = false; + if (NILP (noforce)) + w->force_start = true; + wset_update_mode_line (w); + /* Bug#15957. */ + w->window_end_valid = false; + wset_redisplay (w); - return pos; - } + return pos; } DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, - Spos_visible_in_window_p, 0, 4, 0, + Spos_visible_in_window_p, 0, 3, 0, doc: /* Return non-nil if position POS is currently on the frame in WINDOW. WINDOW must be a live window and defaults to the selected one. @@ -1744,21 +1709,9 @@ 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 GROUP is non-nil, and WINDOW is part of a group of windows collectively -displaying a buffer (such as with Follow Mode), test whether POS is visible -in the group of windows rather than in the individual WINDOW. This -condition holds when `pos-visible-in-window-p-function' is set to a -function, in which case `pos-visible-in-window-p' calls the function with -the three arguments POS, WINDOW, and PARTIALLY, then returns its result, -instead of doing its normal processing. */) - (Lisp_Object pos, Lisp_Object window, Lisp_Object partially, Lisp_Object group) -{ - if (!NILP (group) - && FUNCTIONP (Vpos_visible_in_window_p_group_function)) - return call3 (Vpos_visible_in_window_p_group_function, pos, window, partially); - { +(zero-based). */) + (Lisp_Object pos, Lisp_Object window, Lisp_Object partially) +{ struct window *w; EMACS_INT posint; struct buffer *buf; @@ -1807,7 +1760,6 @@ instead of doing its normal processing. */) } return in_window; - } } DEFUN ("window-line-height", Fwindow_line_height, @@ -5205,7 +5157,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror) } XSETFASTINT (tem, PT); - tem = Fpos_visible_in_window_p (tem, window, Qnil, Qnil); + tem = Fpos_visible_in_window_p (tem, window, Qnil); if (NILP (tem)) { @@ -5594,7 +5546,7 @@ displayed_window_lines (struct window *w) } -DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "P\ni", +DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", doc: /* Center point in selected window and maybe redisplay frame. With a numeric prefix argument ARG, recenter putting point on screen line ARG relative to the selected window. If ARG is negative, it counts up from the @@ -5608,221 +5560,208 @@ 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. - -When `recenter' is called from a program, GROUP is non-nil, and WINDOW is -part of a group of windows collectively displaying a buffer (such as with -Follow Mode), perform `recenter''s actions on the group rather than on the -individual WINDOW. This condition holds when `recenter-group-function' is -set to a function, in which case `recenter' calls the function with the -argument ARG, then returns its value, instead of doing its normal -processing. */) - (register Lisp_Object arg, Lisp_Object group) -{ - if (!NILP (group) - && FUNCTIONP (Vrecenter_group_function)) - return call1 (Vrecenter_group_function, arg); - { - struct window *w = XWINDOW (selected_window); - struct buffer *buf = XBUFFER (w->contents); - bool center_p = false; - ptrdiff_t charpos, bytepos; - EMACS_INT iarg IF_LINT (= 0); - int this_scroll_margin; +and redisplay normally--don't erase and redraw the frame. */) + (register Lisp_Object arg) +{ + struct window *w = XWINDOW (selected_window); + struct buffer *buf = XBUFFER (w->contents); + bool center_p = false; + ptrdiff_t charpos, bytepos; + EMACS_INT iarg IF_LINT (= 0); + int this_scroll_margin; - if (buf != current_buffer) - error ("`recenter'ing a window that does not display current-buffer."); + if (buf != current_buffer) + error ("`recenter'ing a window that does not display current-buffer."); - /* If redisplay is suppressed due to an error, try again. */ - buf->display_error_modiff = 0; + /* If redisplay is suppressed due to an error, try again. */ + buf->display_error_modiff = 0; - if (NILP (arg)) - { - if (!NILP (Vrecenter_redisplay) - && (!EQ (Vrecenter_redisplay, Qtty) - || !NILP (Ftty_type (selected_frame)))) - { - ptrdiff_t i; - - /* Invalidate pixel data calculated for all compositions. */ - for (i = 0; i < n_compositions; i++) - composition_table[i]->font = NULL; + if (NILP (arg)) + { + if (!NILP (Vrecenter_redisplay) + && (!EQ (Vrecenter_redisplay, Qtty) + || !NILP (Ftty_type (selected_frame)))) + { + ptrdiff_t i; + + /* Invalidate pixel data calculated for all compositions. */ + for (i = 0; i < n_compositions; i++) + composition_table[i]->font = NULL; #if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) - WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; + WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; #endif - Fredraw_frame (WINDOW_FRAME (w)); - SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); - } + Fredraw_frame (WINDOW_FRAME (w)); + SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); + } - center_p = true; - } - else if (CONSP (arg)) /* Just C-u. */ center_p = true; - else - { - arg = Fprefix_numeric_value (arg); - CHECK_NUMBER (arg); - iarg = XINT (arg); - } + } + else if (CONSP (arg)) /* Just C-u. */ + center_p = true; + else + { + arg = Fprefix_numeric_value (arg); + CHECK_NUMBER (arg); + iarg = XINT (arg); + } - /* Do this after making BUF current - in case scroll_margin is buffer-local. */ - this_scroll_margin - = max (0, min (scroll_margin, w->total_lines / 4)); + /* Do this after making BUF current + in case scroll_margin is buffer-local. */ + this_scroll_margin + = max (0, min (scroll_margin, w->total_lines / 4)); - /* Don't use redisplay code for initial frames, as the necessary - data structures might not be set up yet then. */ - if (!FRAME_INITIAL_P (XFRAME (w->frame))) - { - if (center_p) - { - struct it it; - struct text_pos pt; - void *itdata = bidi_shelve_cache (); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - move_it_vertically_backward (&it, window_box_height (w) / 2); - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - bidi_unshelve_cache (itdata, false); - } - else if (iarg < 0) - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); - int extra_line_spacing; - int h = window_box_height (w); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin + 1, nlines, - ht - this_scroll_margin); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Be sure we have the exact height of the full line containing PT. */ - move_it_by_lines (&it, 0); - - /* The amount of pixels we have to move back is the window - height minus what's displayed in the line containing PT, - and the lines below. */ - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, nlines); - - if (it.vpos == nlines) - h -= it.current_y; - else - { - /* Last line has no newline. */ - h -= line_bottom_y (&it); - it.vpos++; - } - - /* Don't reserve space for extra line spacing of last line. */ - extra_line_spacing = it.max_extra_line_spacing; - - /* If we can't move down NLINES lines because we hit - the end of the buffer, count in some empty lines. */ - if (it.vpos < nlines) - { - nlines -= it.vpos; - extra_line_spacing = it.extra_line_spacing; - h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); - } - if (h <= 0) - { - bidi_unshelve_cache (itdata, false); - return Qnil; - } - - /* Now find the new top line (starting position) of the window. */ - start_display (&it, w, pt); - it.current_y = 0; - move_it_vertically_backward (&it, h); - - /* If extra line spacing is present, we may move too far - back. This causes the last line to be only partially - visible (which triggers redisplay to recenter that line - in the middle), so move forward. - But ignore extra line spacing on last line, as it is not - considered to be part of the visible height of the line. - */ - h += extra_line_spacing; - while (-it.current_y > h) - move_it_by_lines (&it, 1); - - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - - bidi_unshelve_cache (itdata, false); - } - else - { - struct it it; - struct text_pos pt; - ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); - int ht = window_internal_height (w); - void *itdata = bidi_shelve_cache (); - - nlines = clip_to_bounds (this_scroll_margin, nlines, - ht - this_scroll_margin - 1); - - SET_TEXT_POS (pt, PT, PT_BYTE); - start_display (&it, w, pt); - - /* Move to the beginning of screen line containing PT. */ - move_it_by_lines (&it, 0); - - /* Move back to find the point which is ARG screen lines above PT. */ - if (nlines > 0) - { - it.current_y = 0; - it.vpos = 0; - move_it_by_lines (&it, -nlines); - } - - charpos = IT_CHARPOS (it); - bytepos = IT_BYTEPOS (it); - - bidi_unshelve_cache (itdata, false); - } - } - else - { - struct position pos; - int ht = window_internal_height (w); + /* Don't use redisplay code for initial frames, as the necessary + data structures might not be set up yet then. */ + if (!FRAME_INITIAL_P (XFRAME (w->frame))) + { + if (center_p) + { + struct it it; + struct text_pos pt; + void *itdata = bidi_shelve_cache (); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + move_it_vertically_backward (&it, window_box_height (w) / 2); + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + bidi_unshelve_cache (itdata, false); + } + else if (iarg < 0) + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg); + int extra_line_spacing; + int h = window_box_height (w); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); + + nlines = clip_to_bounds (this_scroll_margin + 1, nlines, + ht - this_scroll_margin); + + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Be sure we have the exact height of the full line containing PT. */ + move_it_by_lines (&it, 0); + + /* The amount of pixels we have to move back is the window + height minus what's displayed in the line containing PT, + and the lines below. */ + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, nlines); - if (center_p) - iarg = ht / 2; - else if (iarg < 0) - iarg += ht; + if (it.vpos == nlines) + h -= it.current_y; + else + { + /* Last line has no newline. */ + h -= line_bottom_y (&it); + it.vpos++; + } - /* Don't let it get into the margin at either top or bottom. */ - iarg = clip_to_bounds (this_scroll_margin, iarg, - ht - this_scroll_margin - 1); + /* Don't reserve space for extra line spacing of last line. */ + extra_line_spacing = it.max_extra_line_spacing; - pos = *vmotion (PT, PT_BYTE, - iarg, w); - charpos = pos.bufpos; - bytepos = pos.bytepos; - } + /* If we can't move down NLINES lines because we hit + the end of the buffer, count in some empty lines. */ + if (it.vpos < nlines) + { + nlines -= it.vpos; + extra_line_spacing = it.extra_line_spacing; + h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing); + } + if (h <= 0) + { + bidi_unshelve_cache (itdata, false); + return Qnil; + } - /* Set the new window start. */ - set_marker_both (w->start, w->contents, charpos, bytepos); - w->window_end_valid = false; + /* Now find the new top line (starting position) of the window. */ + start_display (&it, w, pt); + it.current_y = 0; + move_it_vertically_backward (&it, h); + + /* If extra line spacing is present, we may move too far + back. This causes the last line to be only partially + visible (which triggers redisplay to recenter that line + in the middle), so move forward. + But ignore extra line spacing on last line, as it is not + considered to be part of the visible height of the line. + */ + h += extra_line_spacing; + while (-it.current_y > h) + move_it_by_lines (&it, 1); - w->optional_new_start = true; + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); - w->start_at_line_beg = (bytepos == BEGV_BYTE - || FETCH_BYTE (bytepos - 1) == '\n'); + bidi_unshelve_cache (itdata, false); + } + else + { + struct it it; + struct text_pos pt; + ptrdiff_t nlines = min (PTRDIFF_MAX, iarg); + int ht = window_internal_height (w); + void *itdata = bidi_shelve_cache (); - wset_redisplay (w); + nlines = clip_to_bounds (this_scroll_margin, nlines, + ht - this_scroll_margin - 1); - return Qnil; - } + SET_TEXT_POS (pt, PT, PT_BYTE); + start_display (&it, w, pt); + + /* Move to the beginning of screen line containing PT. */ + move_it_by_lines (&it, 0); + + /* Move back to find the point which is ARG screen lines above PT. */ + if (nlines > 0) + { + it.current_y = 0; + it.vpos = 0; + move_it_by_lines (&it, -nlines); + } + + charpos = IT_CHARPOS (it); + bytepos = IT_BYTEPOS (it); + + bidi_unshelve_cache (itdata, false); + } + } + else + { + struct position pos; + int ht = window_internal_height (w); + + if (center_p) + iarg = ht / 2; + else if (iarg < 0) + iarg += ht; + + /* Don't let it get into the margin at either top or bottom. */ + iarg = clip_to_bounds (this_scroll_margin, iarg, + ht - this_scroll_margin - 1); + + pos = *vmotion (PT, PT_BYTE, - iarg, w); + charpos = pos.bufpos; + bytepos = pos.bytepos; + } + + /* Set the new window start. */ + set_marker_both (w->start, w->contents, charpos, bytepos); + w->window_end_valid = false; + + w->optional_new_start = true; + + w->start_at_line_beg = (bytepos == BEGV_BYTE + || FETCH_BYTE (bytepos - 1) == '\n'); + + wset_redisplay (w); + + return Qnil; } DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width, @@ -5869,68 +5808,52 @@ pixels. */) } DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, - 1, 2, "P\ni", + 1, 1, "P", doc: /* Position point relative to window. ARG nil means position point at center of window. Else, ARG specifies vertical position within the window; -zero means top of window, negative means relative to bottom of window. - -When GROUP is non-nil, and `move-to-window-line-group-function' is set to a -function, then instead of the above, that function is called with the -single argument ARG, and its result returned. - -If GROUP is non-nil, and WINDOW is part of a group of windows collectively -displaying a buffer (such as with Follow Mode), position point relative to -the group of windows as a whole rather than the individual WINDOW. This -condition holds when `move-to-window-line-group-function' is set to a -function, in which case `move-to-window-line' calls the function with the -argument ARG, then returns its result, instead of doing its normal -processing. */) - (Lisp_Object arg, Lisp_Object group) -{ - if (!NILP (group) - && FUNCTIONP (Vmove_to_window_line_group_function)) - return call1 (Vmove_to_window_line_group_function, arg); - { - struct window *w = XWINDOW (selected_window); - int lines, start; - Lisp_Object window; +zero means top of window, negative means relative to bottom of window. */) + (Lisp_Object arg) +{ + struct window *w = XWINDOW (selected_window); + int lines, start; + Lisp_Object window; #if false - int this_scroll_margin; + int this_scroll_margin; #endif - if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) - /* This test is needed to make sure PT/PT_BYTE make sense in w->contents - when passed below to set_marker_both. */ - error ("move-to-window-line called from unrelated buffer"); + if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) + /* This test is needed to make sure PT/PT_BYTE make sense in w->contents + when passed below to set_marker_both. */ + error ("move-to-window-line called from unrelated buffer"); - window = selected_window; - start = marker_position (w->start); - if (start < BEGV || start > ZV) - { - int height = window_internal_height (w); - Fvertical_motion (make_number (- (height / 2)), window, Qnil); - set_marker_both (w->start, w->contents, PT, PT_BYTE); - w->start_at_line_beg = !NILP (Fbolp ()); - w->force_start = true; - } - else - Fgoto_char (w->start); + window = selected_window; + start = marker_position (w->start); + if (start < BEGV || start > ZV) + { + int height = window_internal_height (w); + Fvertical_motion (make_number (- (height / 2)), window, Qnil); + set_marker_both (w->start, w->contents, PT, PT_BYTE); + w->start_at_line_beg = !NILP (Fbolp ()); + w->force_start = true; + } + else + Fgoto_char (w->start); - lines = displayed_window_lines (w); + lines = displayed_window_lines (w); #if false - this_scroll_margin = max (0, min (scroll_margin, lines / 4)); + this_scroll_margin = max (0, min (scroll_margin, lines / 4)); #endif - if (NILP (arg)) - XSETFASTINT (arg, lines / 2); - else - { - EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); + if (NILP (arg)) + XSETFASTINT (arg, lines / 2); + else + { + EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); - if (iarg < 0) - iarg = iarg + lines; + if (iarg < 0) + iarg = iarg + lines; #if false /* This code would prevent move-to-window-line from moving point to a place inside the scroll margins (which would cause the @@ -5938,20 +5861,19 @@ processing. */) it is probably better not to install it. However, it is here inside #if false so as not to lose it. -- rms. */ - /* Don't let it get into the margin at either top or bottom. */ - iarg = max (iarg, this_scroll_margin); - iarg = min (iarg, lines - this_scroll_margin - 1); + /* Don't let it get into the margin at either top or bottom. */ + iarg = max (iarg, this_scroll_margin); + iarg = min (iarg, lines - this_scroll_margin - 1); #endif - arg = make_number (iarg); - } + arg = make_number (iarg); + } - /* Skip past a partially visible first line. */ - if (w->vscroll) - XSETINT (arg, XINT (arg) + 1); + /* Skip past a partially visible first line. */ + if (w->vscroll) + XSETINT (arg, XINT (arg) + 1); - return Fvertical_motion (arg, window, Qnil); - } + return Fvertical_motion (arg, window, Qnil); } @@ -7225,12 +7147,6 @@ syms_of_window (void) DEFSYM (Qclone_of, "clone-of"); DEFSYM (Qfloor, "floor"); DEFSYM (Qceiling, "ceiling"); - DEFSYM (Qwindow_start_group_function, "window-start-group-function"); - DEFSYM (Qwindow_end_group_function, "window-end-group-function"); - DEFSYM (Qset_window_start_group_function, "set-window-start-group-function"); - DEFSYM (Qrecenter_group_function, "recenter-group-function"); - DEFSYM (Qpos_visible_in_window_p_group_function, "pos-visible-in-window-p-group-function"); - DEFSYM (Qmove_to_window_line_group_function, "move-to-window-line-group-function"); staticpro (&Vwindow_list); @@ -7402,72 +7318,6 @@ Note that this optimization can cause the portion of the buffer displayed after a scrolling operation to be somewhat inaccurate. */); Vfast_but_imprecise_scrolling = false; - DEFVAR_LISP ("window-start-group-function", Vwindow_start_group_function, - doc: /* Function to call for `window-start' when its GROUP parameter is non-nil. -When this variable contains a function, and `window-start' is called with a -non-nil GROUP parameter, the function is called instead of `window-start''s -normal action. `window-start' passes the function its argument WINDOW, which -might be nil. The function may recursively call `window-start'. */); - Vwindow_start_group_function = Qnil; - Fmake_variable_buffer_local (Qwindow_start_group_function); - Fput (Qwindow_start_group_function, Qpermanent_local, Qt); - - DEFVAR_LISP ("window-end-group-function", Vwindow_end_group_function, - doc: /* Function to call for `window-end' if its GROUP parameter is non-nil. -When this variable contains a function, and `window-end' is called with a -non-nil GROUP parameter, the function is called instead of `window-end''s -normal action. `window-end' passes the function its two parameters WINDOW, -and UPDATE. The function may call `window-end' recursively. */); - Vwindow_end_group_function = Qnil; - Fmake_variable_buffer_local (Qwindow_end_group_function); - Fput (Qwindow_end_group_function, Qpermanent_local, Qt); - - DEFVAR_LISP ("set-window-start-group-function", - Vset_window_start_group_function, - doc: /* The function to call for `set-window-start' when its GROUP parameter is non-nil. -When this variable contains a function, and `set-window-start' is called -with a non-nil GROUP parameter, the function is called instead of -`set-window-start''s normal action. `set-window-start' passes the function -its three parameters WINDOW, POS, and NOFORCE. The function may call -`set-window-start' recursively. */); - Vset_window_start_group_function = Qnil; - Fmake_variable_buffer_local (Qset_window_start_group_function); - Fput (Qset_window_start_group_function, Qpermanent_local, Qt); - - DEFVAR_LISP ("recenter-group-function", Vrecenter_group_function, - doc: /* Function to call for `recenter' when its GROUP parameter is non-nil. -When this variable contains a function, and `recenter' is called with a -non-nil GROUP parameter, the function is called instead of `recenter''s -normal action. `recenter' passes the function its parameter ARG. The -function may call `recenter' recursively. */); - Vrecenter_group_function = Qnil; - Fmake_variable_buffer_local (Qrecenter_group_function); - Fput (Qrecenter_group_function, Qpermanent_local, Qt); - - DEFVAR_LISP ("pos-visible-in-window-p-group-function", - Vpos_visible_in_window_p_group_function, - doc: /* Function for `pos-visible-in-window-p' when its GROUP parameter is non-nil. -When this variable contains a function, and `pos-visible-in-window-p' is -called with a non-nil GROUP parameter, the function is called instead of -`pos-visible-in-window-p''s normal action. `pos-visible-in-window-p' -passes the function its three parameters POS, WINDOW, and PARTIALLY. The -function may call `pos-visible-in-window-p' recursively. */); - Vpos_visible_in_window_p_group_function = Qnil; - Fmake_variable_buffer_local (Qpos_visible_in_window_p_group_function); - Fput (Qpos_visible_in_window_p_group_function, Qpermanent_local, Qt); - - DEFVAR_LISP ("move-to-window-line-group-function", - Vmove_to_window_line_group_function, - doc: /* Function for `move-to-window-line' when its GROUP parameter is non-nil. -When this variable contains a function, and `move-to-window-line' is -called with a non-nil GROUP parameter, the function is called instead of -`move-to-window-line''s normal action. `move-to-window-line' passes the -function its parameter ARG. The function may call `move-to-window-line' -recursively. */); - Vmove_to_window_line_group_function = Qnil; - Fmake_variable_buffer_local (Qmove_to_window_line_group_function); - Fput (Qmove_to_window_line_group_function, Qpermanent_local, Qt); - defsubr (&Sselected_window); defsubr (&Sminibuffer_window); defsubr (&Swindow_minibuffer_p); -- cgit v1.2.3 From 9ae19d2f0d757db73dee35b7e675810dab7bcb37 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 14 Dec 2015 17:17:31 +0000 Subject: Enhance ispell-skip-region-alist by generating part of it at runtime. * lisp/textmodes/ispell.el (ispell--\\w-filter, ispell--make-\\w-expression) (ispell--make-filename-or-URL-re): New functions which generate a regexp. (ispell-skip-region-alist): Remove the bit that matches a filename/URL, etc. (ispell-begin-skip-region-regexp, ispell-skip-region-list, ispell-message): Include the result of ispell--make-filename-or-URL-re in regexps. --- lisp/textmodes/ispell.el | 59 ++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 54 insertions(+), 5 deletions(-) diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 0f1806c888..1b26b4905a 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: @@ -3387,7 +3432,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))) "\\|")) @@ -3426,6 +3472,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 @@ -4119,9 +4167,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)) -- cgit v1.2.3 From 16dac3f9af08ba5591f7dd06efe99a3f6b2d3965 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Mon, 14 Dec 2015 17:29:45 +0000 Subject: Ispell: Bind isearch-regexp-function to nil around call to isearch..-new-loop * lisp/textmodes/ispell.el (ispell-highlight-spelling-error-overlay): bind isearch-regexp-function to nil around call to isearch-lazy-highligh-new-loop. --- lisp/textmodes/ispell.el | 1 + 1 file changed, 1 insertion(+) diff --git a/lisp/textmodes/ispell.el b/lisp/textmodes/ispell.el index 1b26b4905a..3327028894 100644 --- a/lisp/textmodes/ispell.el +++ b/lisp/textmodes/ispell.el @@ -2861,6 +2861,7 @@ The variable `ispell-highlight-face' selects the face to use for highlighting." (regexp-quote (buffer-substring-no-properties start end)) "\\b")) (isearch-regexp t) + (isearch-regexp-function nil) (isearch-case-fold-search nil) (isearch-forward t) (isearch-other-end start) -- cgit v1.2.3 From 64ad54734dc720b4e14fe75475cc767f2e421e06 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Tue, 15 Dec 2015 11:41:54 +0000 Subject: Tidy up documentation associated with window groups. * doc/lispref/windows.texi (Basic Windows): Add an @anchor for "Window Groups". Correct example function to `window-group-start'. (Window Start and End, Textual scrolling): Point to the new anchor. State that (most of) the args in window group functions have the same meaning as for the corresponding window primitives. * doc/lispref/positions.texi (Screen Lines). Same as above. --- doc/lispref/positions.texi | 9 ++++----- doc/lispref/windows.texi | 44 +++++++++++++++++++++++--------------------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi index 090eb4598a..9daf5cef05 100644 --- a/doc/lispref/positions.texi +++ b/doc/lispref/positions.texi @@ -575,14 +575,13 @@ the top line in the window numbered 0. @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{Basic -Windows}), @code{move-to-window-group-line} will move to a position -with respect to the entire group, not just the single window. This +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. The argument has -the same meaning as in @code{move-to-window-line}. +the argument @var{count}, then returns its result. @end defun @defun compute-motion from frompos to topos width offsets window diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 45899588c3..0c0c1bce3b 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -133,14 +133,14 @@ 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. +@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 @@ -3125,13 +3125,12 @@ position to change in response until after the next redisplay. @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{Basic Windows}), +@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. The argument to this -function has the same meaning as in @code{window-start}. +argument @var{window}, then returns its result. @end defun @cindex window end position @@ -3163,13 +3162,13 @@ text will end if scrolling is not required. @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{Basic Windows}), +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 arguments -to this function have the same meaning as in @code{window-end}. +@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 @@ -3238,14 +3237,14 @@ position that works well with point, and thus @var{position} is not used. @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{Basic Windows}), +@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 in this function have the same -meaning as in @code{set-window-start}. +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 @@ -3292,14 +3291,16 @@ Here is an example: @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{Basic Windows}), +@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. +@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 @@ -3548,12 +3549,13 @@ a more convenient way to achieve this. @vindex recenter-group-function @defun recenter-group &optional count This function is like @code{recenter}, except that when the selected -window is part of a group of windows (@pxref{Basic Windows}), +window is part of a group of windows (@pxref{Window Group}), @code{recenter-group} 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-group} calls the function with the argument @var{count}, then returns its result. The argument -has the same meaning as in @code{recenter}. +@var{count} has the same meaning as in @code{recenter}, but with +respect to the entire window group. @end defun @defopt recenter-redisplay -- cgit v1.2.3 From f3f0d12cafb89b68bb5ccee0c6e2ced80d204336 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Thu, 17 Dec 2015 21:06:39 +0000 Subject: * lisp/follow.el (follow-sit-for): Remove (it's redundant). --- lisp/follow.el | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/lisp/follow.el b/lisp/follow.el index 3a876bcb21..dc525315b8 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -1479,9 +1479,9 @@ non-first windows in Follow mode." ;; 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. +;; 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'." @@ -1615,27 +1615,6 @@ zero means top of the first window in the group, negative means 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 -- cgit v1.2.3 From a72a9fbbbc9cd5f5933719b11489c2578eb0aa59 Mon Sep 17 00:00:00 2001 From: Alan Mackenzie Date: Fri, 18 Dec 2015 10:40:49 +0000 Subject: Rename `recenter-group' to `recenter-window-group' * doc/lispref/windows.texi (Textual Scrolling) * lisp/window.el (top level, recenter-group) * lisp/follow.el (follow-mode) * lisp/isearch.el (isearch-back-into-window): Rename `recenter-group' to `recenter-window-group' and `recenter-group-function' to `recenter-window-group-function'. --- doc/lispref/windows.texi | 13 +++++++------ lisp/follow.el | 4 ++-- lisp/isearch.el | 8 ++++---- lisp/window.el | 12 ++++++------ 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi index 0c0c1bce3b..e45201b057 100644 --- a/doc/lispref/windows.texi +++ b/doc/lispref/windows.texi @@ -3546,14 +3546,15 @@ the top of the window. The command @code{recenter-top-bottom} offers a more convenient way to achieve this. @end deffn -@vindex recenter-group-function -@defun recenter-group &optional count +@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-group} 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-group} calls the function -with the argument @var{count}, then returns its result. The argument +@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 diff --git a/lisp/follow.el b/lisp/follow.el index dc525315b8..71e8824947 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -430,7 +430,7 @@ Keys specific to Follow mode: (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-group-function 'follow-recenter) + (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) @@ -450,7 +450,7 @@ Keys specific to Follow mode: (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-group-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) diff --git a/lisp/isearch.el b/lisp/isearch.el index b29e432f37..05dc2931d9 100644 --- a/lisp/isearch.el +++ b/lisp/isearch.el @@ -2311,15 +2311,15 @@ the bottom." (if above (progn (goto-char start) - (recenter-group 0) + (recenter-window-group 0) (when (>= isearch-point (window-group-end nil t)) (goto-char isearch-point) - (recenter-group -1))) + (recenter-window-group -1))) (goto-char end) - (recenter-group -1) + (recenter-window-group -1) (when (< isearch-point (window-group-start)) (goto-char isearch-point) - (recenter-group 0)))) + (recenter-window-group 0)))) (goto-char isearch-point)) (defvar isearch-pre-scroll-point nil) diff --git a/lisp/window.el b/lisp/window.el index ce0256f1ff..c57fef441f 100644 --- a/lisp/window.el +++ b/lisp/window.el @@ -7930,10 +7930,10 @@ overriding motion of point in order to display at this exact start." (funcall set-window-group-start-function window pos noforce) (set-window-start window pos noforce))) -(defvar recenter-group-function nil) -(make-variable-buffer-local 'recenter-group-function) -(put 'recenter-group-function 'permanent-local t) -(defun recenter-group (&optional arg) +(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'. @@ -7953,8 +7953,8 @@ 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-group-function) - (funcall recenter-group-function arg) + (if (functionp recenter-window-group-function) + (funcall recenter-window-group-function arg) (recenter arg))) (defvar pos-visible-in-window-group-p-function nil) -- cgit v1.2.3