aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorAlan Mackenzie2015-11-11 12:02:48 +0000
committerAlan Mackenzie2015-11-11 12:02:48 +0000
commit2c56fc2a3f106a1286ad793eed9bfaafd09a7411 (patch)
tree6082e9df4733fb39a9d1423568a703c93f7ead62
parent25775a12c5168b0494dff15639ac25d8e1017530 (diff)
downloademacs-2c56fc2a3f106a1286ad793eed9bfaafd09a7411.tar.gz
emacs-2c56fc2a3f106a1286ad793eed9bfaafd09a7411.zip
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.
-rw-r--r--doc/lispref/positions.texi13
-rw-r--r--doc/lispref/windows.texi87
-rw-r--r--lisp/follow.el193
-rw-r--r--lisp/isearch.el133
-rw-r--r--lisp/window.el11
-rw-r--r--src/keyboard.c2
-rw-r--r--src/window.c772
7 files changed, 838 insertions, 373 deletions
diff --git a/doc/lispref/positions.texi b/doc/lispref/positions.texi
index 72b76ce5c8f..e0496e30848 100644
--- a/doc/lispref/positions.texi
+++ b/doc/lispref/positions.texi
@@ -551,7 +551,8 @@ current buffer, regardless of which buffer is displayed in
551any buffer, whether or not it is currently displayed in some window. 551any buffer, whether or not it is currently displayed in some window.
552@end defun 552@end defun
553 553
554@deffn Command move-to-window-line count 554@deffn Command move-to-window-line count group
555@vindex move-to-window-line-group-function
555This function moves point with respect to the text currently displayed 556This function moves point with respect to the text currently displayed
556in the selected window. It moves point to the beginning of the screen 557in the selected window. It moves point to the beginning of the screen
557line @var{count} screen lines from the top of the window. If 558line @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.
570 571
571The value returned is the window line number point has moved to, with 572The value returned is the window line number point has moved to, with
572the top line in the window numbered 0. 573the top line in the window numbered 0.
574
575If @var{group} is non-@code{nil}, and the selected window is a part of
576a group of windows (@pxref{Basic Windows}), @code{move-to-window-line}
577will move to a position with respect to the entire group, not just the
578single window. This condition holds when the buffer local variable
579@code{move-to-window-line-group-function} is set to a function. In
580this case, @code{move-to-window-line} calls the function with the
581argument @var{count}, then returns its result, instead of performing
582the actions described above. Typically, the function will call
583@code{move-to-window-line} recursively.
573@end deffn 584@end deffn
574 585
575@defun compute-motion from frompos to topos width offsets window 586@defun compute-motion from frompos to topos width offsets window
diff --git a/doc/lispref/windows.texi b/doc/lispref/windows.texi
index 357247ef433..832ced719cd 100644
--- a/doc/lispref/windows.texi
+++ b/doc/lispref/windows.texi
@@ -133,6 +133,30 @@ This function returns the selected window (which is always a live
133window). 133window).
134@end defun 134@end defun
135 135
136 Sometimes several windows collectively and cooperatively display a
137buffer, for example, under the management of Follow Mode, where the
138windows together display a bigger portion of the buffer than one
139window could alone. It is often useful to consider such a @dfn{group
140of windows} as a single entity. Several functions such as
141@code{window-start} (@pxref{Window Start and End}) allow you to do
142this by supplying, as an argument, one of the windows as a stand in
143for the whole group.
144
145@defun selected-window-group
146@vindex selected-window-group-function
147When the selected window is a member of a group of windows, this
148function returns a list of the windows in the group, ordered such that
149the first window in the list is displaying the earliest part of the
150buffer, and so on. Otherwise the function returns a list containing
151just the selected window.
152
153The selected window is considered part of a group when the buffer
154local variable @code{selected-window-group-function} is set to a
155function. In this case, @code{selected-window-group} calls it with no
156arguments and returns its result (which should be the list of windows
157in the group).
158@end defun
159
136@node Windows and Frames 160@node Windows and Frames
137@section Windows and Frames 161@section Windows and Frames
138 162
@@ -3064,7 +3088,8 @@ using the commands of Lisp mode, because they trigger this
3064readjustment. To test such code, put it into a command and bind the 3088readjustment. To test such code, put it into a command and bind the
3065command to a key. 3089command to a key.
3066 3090
3067@defun window-start &optional window 3091@defun window-start &optional window group
3092@vindex window-start-group-function
3068@cindex window top line 3093@cindex window top line
3069This function returns the display-start position of window 3094This function returns the display-start position of window
3070@var{window}. If @var{window} is @code{nil}, the selected window is 3095@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
3080on the screen. Nothing except redisplay automatically changes the 3105on the screen. Nothing except redisplay automatically changes the
3081window-start position; if you move point, do not expect the window-start 3106window-start position; if you move point, do not expect the window-start
3082position to change in response until after the next redisplay. 3107position to change in response until after the next redisplay.
3108
3109If @var{group} is non-@code{nil}, and @var{window} is a part of a
3110group of windows (@pxref{Basic Windows}), @code{window-start} returns
3111the start position of the entire group. This condition holds when the
3112buffer local variable @code{window-start-group-function} is set to a
3113function. In this case, @code{window-start} calls the function with
3114the single argument @var{window}, then returns its result, instead of
3115performing the actions described above. Typically, the function will
3116call @code{window-start} recursively.
3083@end defun 3117@end defun
3084 3118
3119@vindex window-end-group-function
3085@cindex window end position 3120@cindex window end position
3086@defun window-end &optional window update 3121@defun window-end &optional window update group
3087This function returns the position where display of its buffer ends in 3122This function returns the position where display of its buffer ends in
3088@var{window}. The default for @var{window} is the selected window. 3123@var{window}. The default for @var{window} is the selected window.
3089 3124
@@ -3106,9 +3141,19 @@ attempt to scroll the display if point has moved off the screen, the
3106way real redisplay would do. It does not alter the 3141way real redisplay would do. It does not alter the
3107@code{window-start} value. In effect, it reports where the displayed 3142@code{window-start} value. In effect, it reports where the displayed
3108text will end if scrolling is not required. 3143text will end if scrolling is not required.
3144
3145If @var{group} is non-@code{nil}, and @var{window} is a part of a
3146group of windows (@pxref{Basic Windows}), `window-end' returns the end
3147position of the entire group. This condition holds when the buffer
3148local variable @code{window-end-group-function} is set to a function.
3149In this case, @code{window-end} calls the function with the two
3150arguments @var{window} and @var{update}, then returns its result,
3151instead of performing the actions described above. Typically, the
3152function will call @code{window-end} recursively.
3109@end defun 3153@end defun
3110 3154
3111@defun set-window-start window position &optional noforce 3155@vindex set-window-start-group-function
3156@defun set-window-start window position &optional noforce group
3112This function sets the display-start position of @var{window} to 3157This function sets the display-start position of @var{window} to
3113@var{position} in @var{window}'s buffer. It returns @var{position}. 3158@var{position} in @var{window}'s buffer. It returns @var{position}.
3114 3159
@@ -3169,9 +3214,20 @@ it is still 1 when redisplay occurs. Here is an example:
3169If @var{noforce} is non-@code{nil}, and @var{position} would place point 3214If @var{noforce} is non-@code{nil}, and @var{position} would place point
3170off screen at the next redisplay, then redisplay computes a new window-start 3215off screen at the next redisplay, then redisplay computes a new window-start
3171position that works well with point, and thus @var{position} is not used. 3216position that works well with point, and thus @var{position} is not used.
3217
3218If @var{group} is non-@code{nil}, and @var{window} is a part of a
3219group of windows (@pxref{Basic Windows}), @code{set-window-start} sets
3220the start position of the entire group. This condition holds when the
3221buffer local variable @code{set-window-start-group-function} is set to
3222a function. In this case, @code{set-window-start} calls the function
3223with the three arguments @var{window}, @var{position}, and
3224@var{noforce}, then returns its result, instead of performing the
3225actions described above. Typically, the function will call
3226@code{set-window-start} recursively.
3172@end defun 3227@end defun
3173 3228
3174@defun pos-visible-in-window-p &optional position window partially 3229@defun pos-visible-in-window-p &optional position window partially group
3230@vindex pos-visible-in-window-p-group-function
3175This function returns non-@code{nil} if @var{position} is within the 3231This function returns non-@code{nil} if @var{position} is within the
3176range of text currently visible on the screen in @var{window}. It 3232range of text currently visible on the screen in @var{window}. It
3177returns @code{nil} if @var{position} is scrolled vertically out of 3233returns @code{nil} if @var{position} is scrolled vertically out of
@@ -3210,6 +3266,18 @@ Here is an example:
3210 (recenter 0)) 3266 (recenter 0))
3211@end group 3267@end group
3212@end example 3268@end example
3269
3270If @var{group} is non-@code{nil}, and @var{window} is a part of a
3271group of windows (@pxref{Basic Windows}),
3272@code{pos-visible-in-window-p} tests the visibility of @var{pos} in
3273the entire group, not just in the single @var{window}. This condition
3274holds when the buffer local variable
3275@code{pos-visible-in-window-p-group-function} is set to a function.
3276In this case @code{pos-visible-in-window-p} calls the function with
3277the three arguments @var{position}, @var{window}, and @var{partially},
3278then returns its result, instead of performing the actions described
3279above. Typically, the function will call
3280@code{pos-visible-in-window-p} recursively.
3213@end defun 3281@end defun
3214 3282
3215@defun window-line-height &optional line window 3283@defun window-line-height &optional line window
@@ -3427,7 +3495,8 @@ beginning or end of the buffer (depending on scrolling direction);
3427only if point is already on that position do they signal an error. 3495only if point is already on that position do they signal an error.
3428@end defopt 3496@end defopt
3429 3497
3430@deffn Command recenter &optional count 3498@deffn Command recenter &optional count group
3499@vindex recenter-group-function
3431@cindex centering point 3500@cindex centering point
3432This function scrolls the text in the selected window so that point is 3501This function scrolls the text in the selected window so that point is
3433displayed at a specified vertical position within the window. It does 3502displayed 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),
3444window. If @var{count} is @code{nil}, this function may redraw the 3513window. If @var{count} is @code{nil}, this function may redraw the
3445frame, according to the value of @code{recenter-redisplay}. 3514frame, according to the value of @code{recenter-redisplay}.
3446 3515
3516If @var{group} is non-@code{nil}, and the selected window is part of a
3517group of windows (@pxref{Basic Windows}), @code{recenter} scrolls the
3518entire group. This condition holds when the buffer local variable
3519@code{recenter-group-function} is set to a function. In this case,
3520@code{recenter} calls the function with the argument @var{count}, then
3521returns its result, instead of performing the actions described above.
3522Typically, the function will call @code{recenter} recursively.
3523
3447When @code{recenter} is called interactively, @var{count} is the raw 3524When @code{recenter} is called interactively, @var{count} is the raw
3448prefix argument. Thus, typing @kbd{C-u} as the prefix sets the 3525prefix argument. Thus, typing @kbd{C-u} as the prefix sets the
3449@var{count} to a non-@code{nil} list, while typing @kbd{C-u 4} sets 3526@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 938c59e8506..f2427374f24 100644
--- a/lisp/follow.el
+++ b/lisp/follow.el
@@ -421,7 +421,19 @@ Keys specific to Follow mode:
421 (progn 421 (progn
422 (add-hook 'compilation-filter-hook 'follow-align-compilation-windows t t) 422 (add-hook 'compilation-filter-hook 'follow-align-compilation-windows t t)
423 (add-hook 'post-command-hook 'follow-post-command-hook t) 423 (add-hook 'post-command-hook 'follow-post-command-hook t)
424 (add-hook 'window-size-change-functions 'follow-window-size-change t)) 424 (add-hook 'window-size-change-functions 'follow-window-size-change t)
425 (add-hook 'after-change-functions 'follow-after-change nil t)
426
427 (setq window-start-group-function 'follow-window-start)
428 (setq window-end-group-function 'follow-window-end)
429 (setq set-window-start-group-function 'follow-set-window-start)
430 (setq recenter-group-function 'follow-recenter)
431 (setq pos-visible-in-window-p-group-function
432 'follow-pos-visible-in-window-p)
433 (setq selected-window-group-function 'follow-all-followers)
434 (setq move-to-window-line-group-function 'follow-move-to-window-line)
435 (setq sit*-for-function 'follow-sit-for))
436
425 ;; Remove globally-installed hook functions only if there is no 437 ;; Remove globally-installed hook functions only if there is no
426 ;; other Follow mode buffer. 438 ;; other Follow mode buffer.
427 (let ((buffers (buffer-list)) 439 (let ((buffers (buffer-list))
@@ -432,6 +444,17 @@ Keys specific to Follow mode:
432 (unless following 444 (unless following
433 (remove-hook 'post-command-hook 'follow-post-command-hook) 445 (remove-hook 'post-command-hook 'follow-post-command-hook)
434 (remove-hook 'window-size-change-functions 'follow-window-size-change))) 446 (remove-hook 'window-size-change-functions 'follow-window-size-change)))
447
448 (kill-local-variable 'sit*-for-function)
449 (kill-local-variable 'move-to-window-line-group-function)
450 (kill-local-variable 'selected-window-group-function)
451 (kill-local-variable 'pos-visible-in-window-p-group-function)
452 (kill-local-variable 'recenter-group-function)
453 (kill-local-variable 'set-window-start-group-function)
454 (kill-local-variable 'window-end-group-function)
455 (kill-local-variable 'window-start-group-function)
456
457 (remove-hook 'after-change-functions 'follow-after-change t)
435 (remove-hook 'compilation-filter-hook 'follow-align-compilation-windows t))) 458 (remove-hook 'compilation-filter-hook 'follow-align-compilation-windows t)))
436 459
437(defun follow-find-file-hook () 460(defun follow-find-file-hook ()
@@ -1015,6 +1038,10 @@ Otherwise, return nil."
1015;; is nil. Start every window directly after the end of the previous 1038;; is nil. Start every window directly after the end of the previous
1016;; window, to make sure long lines are displayed correctly. 1039;; window, to make sure long lines are displayed correctly.
1017 1040
1041(defvar follow-start-end-invalid t
1042 "When non-nil, indicates `follow-windows-start-end-cache' is invalid.")
1043(make-variable-buffer-local 'follow-start-end-invalid)
1044
1018(defun follow-redisplay (&optional windows win preserve-win) 1045(defun follow-redisplay (&optional windows win preserve-win)
1019 "Reposition the WINDOWS around WIN. 1046 "Reposition the WINDOWS around WIN.
1020Should point be too close to the roof we redisplay everything 1047Should point be too close to the roof we redisplay everything
@@ -1047,7 +1074,8 @@ repositioning the other windows."
1047 (dolist (w windows) 1074 (dolist (w windows)
1048 (unless (and preserve-win (eq w win)) 1075 (unless (and preserve-win (eq w win))
1049 (set-window-start w start)) 1076 (set-window-start w start))
1050 (setq start (car (follow-calc-win-end w)))))) 1077 (setq start (car (follow-calc-win-end w))))
1078 (setq follow-start-end-invalid nil)))
1051 1079
1052(defun follow-estimate-first-window-start (windows win start) 1080(defun follow-estimate-first-window-start (windows win start)
1053 "Estimate the position of the first window. 1081 "Estimate the position of the first window.
@@ -1446,6 +1474,167 @@ non-first windows in Follow mode."
1446 1474
1447(add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t) 1475(add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t)
1448 1476
1477;;; Low level window start and end.
1478
1479;; These routines are the Follow Mode versions of the low level
1480;; functions described on page "Window Start and End" of the elisp
1481;; manual, e.g. `window*-start'. The aim is to be able to handle
1482;; Follow Mode windows by replacing `window-start' by `window*-start',
1483;; etc.
1484
1485(defun follow-after-change (_beg _end _old-len)
1486 "After change function: set `follow-start-end-invalid'."
1487 (setq follow-start-end-invalid t))
1488
1489(defun follow-window-start (&optional window)
1490 "Return position at which display currently starts in the
1491Follow Mode group of windows which includes WINDOW.
1492
1493WINDOW must be a live window and defaults to the selected one.
1494This is updated by redisplay or by calling
1495`follow-set-window-start'."
1496 (let ((windows (follow-all-followers window)))
1497 (window-start (car windows))))
1498
1499(defun follow-window-end (&optional window update)
1500 "Return position at which display currently ends in the Follow
1501 Mode group of windows which includes WINDOW.
1502
1503 WINDOW must be a live window and defaults to the selected one.
1504 This is updated by redisplay, when it runs to completion.
1505 Simply changing the buffer text or setting `window-start' does
1506 not update this value.
1507
1508 Return nil if there is no recorded value. (This can happen if
1509 the last redisplay of WINDOW was preempted, and did not
1510 finish.) If UPDATE is non-nil, compute the up-to-date position
1511 if it isn't already recorded."
1512 (let* ((windows (follow-all-followers window))
1513 (last (car (last windows))))
1514 (when (and update follow-start-end-invalid)
1515 (follow-redisplay windows (car windows)))
1516 (window-end last update)))
1517
1518(defun follow-set-window-start (window pos &optional noforce)
1519 "Make display in the Follow Mode group of windows which includes
1520WINDOW start at position POS in WINDOW's buffer.
1521
1522WINDOW must be a live window and defaults to the selected one. Return
1523POS. Optional third arg NOFORCE non-nil inhibits next redisplay from
1524overriding motion of point in order to display at this exact start."
1525 (let ((windows (follow-all-followers window)))
1526 (setq follow-start-end-invalid t)
1527 (set-window-start (car windows) pos noforce)))
1528
1529(defun follow-pos-visible-in-window-p (&optional pos window partially)
1530 "Return non-nil if position POS is currently on the frame in one of
1531 the windows in the Follow Mode group which includes WINDOW.
1532
1533WINDOW must be a live window and defaults to the selected one.
1534
1535Return nil if that position is scrolled vertically out of view. If a
1536character is only partially visible, nil is returned, unless the
1537optional argument PARTIALLY is non-nil. If POS is only out of view
1538because of horizontal scrolling, return non-nil. If POS is t, it
1539specifies the position of the last visible glyph in WINDOW. POS
1540defaults to point in WINDOW; WINDOW defaults to the selected window.
1541
1542If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil,
1543the return value is a list of 2 or 6 elements (X Y [RTOP RBOT ROWH VPOS]),
1544where X and Y are the pixel coordinates relative to the top left corner
1545of the actual window containing it. The remaining elements are
1546omitted if the character after POS is fully visible; otherwise, RTOP
1547and RBOT are the number of pixels off-window at the top and bottom of
1548the screen line (\"row\") containing POS, ROWH is the visible height
1549of that row, and VPOS is the row number \(zero-based)."
1550 (let* ((windows (follow-all-followers window))
1551 (last (car (last windows))))
1552 (when follow-start-end-invalid
1553 (follow-redisplay windows (car windows)))
1554 (let* ((cache (follow-windows-start-end windows))
1555 (last-elt (car (last cache)))
1556 our-pos pertinent-elt)
1557 (setq pertinent-elt
1558 (if (eq pos t)
1559 last-elt
1560 (setq our-pos (or pos (point)))
1561 (catch 'element
1562 (while cache
1563 (when (< our-pos (nth 2 (car cache)))
1564 (throw 'element (car cache)))
1565 (setq cache (cdr cache)))
1566 last-elt)))
1567 (pos-visible-in-window-p our-pos (car pertinent-elt) partially))))
1568
1569(defun follow-move-to-window-line (arg)
1570 "Position point relative to the Follow mode group containing the selected window.
1571ARG nil means position point at center of the window group.
1572Else, ARG specifies vertical position within the window group;
1573zero means top of the first window in the group, negative means
1574 relative to bottom of the last window in the group."
1575 (let* ((windows (follow-all-followers))
1576 (start-end (follow-windows-start-end windows))
1577 (rev-start-end (reverse start-end))
1578 (lines 0)
1579 middle-window elt count)
1580 (select-window
1581 (cond
1582 ((null arg)
1583 (setq rev-start-end (nthcdr (/ (length windows) 2) rev-start-end))
1584 (prog1 (car (car rev-start-end))
1585 (while (setq rev-start-end (cdr rev-start-end))
1586 (setq elt (car rev-start-end)
1587 count (count-screen-lines (cadr elt) (nth 2 elt)
1588 nil (car elt))
1589 lines (+ lines count)))))
1590 ((>= arg 0)
1591 (while (and (cdr start-end)
1592 (progn
1593 (setq elt (car start-end)
1594 count (count-screen-lines (cadr elt) (nth 2 elt)
1595 nil (car elt)))
1596 (>= arg count)))
1597 (setq arg (- arg count)
1598 lines (+ lines count)
1599 start-end (cdr start-end)))
1600 (car (car start-end)))
1601 (t ; (< arg 0)
1602 (while (and (cadr rev-start-end)
1603 (progn
1604 (setq elt (car rev-start-end)
1605 count (count-lines (cadr elt) (nth 2 elt)))
1606 (<= arg (- count))))
1607 (setq arg (+ arg count)
1608 rev-start-end (cdr rev-start-end)))
1609 (prog1 (car (car rev-start-end))
1610 (while (setq rev-start-end (cdr rev-start-end))
1611 (setq elt (car rev-start-end)
1612 count (count-screen-lines (cadr elt) (nth 2 elt)
1613 nil (car elt))
1614 lines (+ lines count)))))))
1615 (+ lines (move-to-window-line arg))))
1616
1617(defun follow-sit-for (seconds &optional nodisp)
1618 "Redisplay, then wait for SECONDS seconds. Stop when input is available.
1619Before redisplaying, synchronise all Follow windows.
1620
1621SECONDS may be a floating-point value.
1622\(On operating systems that do not support waiting for fractions of a
1623second, floating-point values are rounded down to the nearest integer.)
1624
1625Redisplay does not happen if input is available before it starts.
1626If optional arg NODISP is t, don't synchronise or redisplay, just
1627wait for input.
1628
1629Value is t if waited the full time with no input arriving, and nil
1630otherwise.
1631
1632The functionality is intended to be the same as `sit-for''s."
1633 (when (and (not (input-pending-p t))
1634 (not nodisp))
1635 (follow-adjust-window (selected-window)))
1636 (sit-for seconds nodisp))
1637
1449;;; Profile support 1638;;; Profile support
1450 1639
1451;; The following (non-evaluated) section can be used to 1640;; The following (non-evaluated) section can be used to
diff --git a/lisp/isearch.el b/lisp/isearch.el
index 9f8ba8d8d7b..92d7894d2e7 100644
--- a/lisp/isearch.el
+++ b/lisp/isearch.el
@@ -171,6 +171,11 @@ is non-nil if the user quits the search.")
171 "Function to call to display the search prompt. 171 "Function to call to display the search prompt.
172If nil, use function `isearch-message'.") 172If nil, use function `isearch-message'.")
173 173
174(defmacro isearch-call-message (&optional cqh ellip)
175 `(if isearch-message-function
176 (funcall isearch-message-function ,cqh ,ellip)
177 (isearch-message ,cqh ,ellip)))
178
174(defvar isearch-wrap-function nil 179(defvar isearch-wrap-function nil
175 "Function to call to wrap the search when search is failed. 180 "Function to call to wrap the search when search is failed.
176If nil, move point to the beginning of the buffer for a forward search, 181If 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'."
969 (null executing-kbd-macro)) 974 (null executing-kbd-macro))
970 (progn 975 (progn
971 (if (not (input-pending-p)) 976 (if (not (input-pending-p))
972 (if isearch-message-function 977 (isearch-call-message))
973 (funcall isearch-message-function)
974 (isearch-message)))
975 (if (and isearch-slow-terminal-mode 978 (if (and isearch-slow-terminal-mode
976 (not (or isearch-small-window 979 (not (or isearch-small-window
977 (pos-visible-in-window-p)))) 980 (pos-visible-in-window-p nil nil nil t))))
978 (let ((found-point (point))) 981 (let ((found-point (point)))
979 (setq isearch-small-window t) 982 (setq isearch-small-window t)
980 (move-to-window-line 0) 983 (move-to-window-line 0)
@@ -995,7 +998,7 @@ The last thing it does is to run `isearch-update-post-hook'."
995 (let ((current-scroll (window-hscroll)) 998 (let ((current-scroll (window-hscroll))
996 visible-p) 999 visible-p)
997 (set-window-hscroll (selected-window) isearch-start-hscroll) 1000 (set-window-hscroll (selected-window) isearch-start-hscroll)
998 (setq visible-p (pos-visible-in-window-p nil nil t)) 1001 (setq visible-p (pos-visible-in-window-p nil nil t t))
999 (if (or (not visible-p) 1002 (if (or (not visible-p)
1000 ;; When point is not visible because of hscroll, 1003 ;; When point is not visible because of hscroll,
1001 ;; pos-visible-in-window-p returns non-nil, but 1004 ;; pos-visible-in-window-p returns non-nil, but
@@ -1049,7 +1052,7 @@ NOPUSH is t and EDIT is t."
1049 (setq minibuffer-message-timeout isearch-original-minibuffer-message-timeout) 1052 (setq minibuffer-message-timeout isearch-original-minibuffer-message-timeout)
1050 (isearch-dehighlight) 1053 (isearch-dehighlight)
1051 (lazy-highlight-cleanup lazy-highlight-cleanup) 1054 (lazy-highlight-cleanup lazy-highlight-cleanup)
1052 (let ((found-start (window-start)) 1055 (let ((found-start (window-start nil t))
1053 (found-point (point))) 1056 (found-point (point)))
1054 (when isearch-window-configuration 1057 (when isearch-window-configuration
1055 (set-window-configuration isearch-window-configuration) 1058 (set-window-configuration isearch-window-configuration)
@@ -1059,7 +1062,7 @@ NOPUSH is t and EDIT is t."
1059 ;; This has an annoying side effect of clearing the last_modiff 1062 ;; This has an annoying side effect of clearing the last_modiff
1060 ;; field of the window, which can cause unwanted scrolling, 1063 ;; field of the window, which can cause unwanted scrolling,
1061 ;; so don't do it unless truly necessary. 1064 ;; so don't do it unless truly necessary.
1062 (set-window-start (selected-window) found-start t)))) 1065 (set-window-start (selected-window) found-start t t))))
1063 1066
1064 (setq isearch-mode nil) 1067 (setq isearch-mode nil)
1065 (if isearch-input-method-local-p 1068 (if isearch-input-method-local-p
@@ -1282,13 +1285,6 @@ You can update the global isearch variables by setting new values to
1282 (unwind-protect 1285 (unwind-protect
1283 (progn ,@body) 1286 (progn ,@body)
1284 1287
1285 ;; Set point at the start (end) of old match if forward (backward),
1286 ;; so after exiting minibuffer isearch resumes at the start (end)
1287 ;; of this match and can find it again.
1288 (if (and old-other-end (eq old-point (point))
1289 (eq isearch-forward isearch-new-forward))
1290 (goto-char old-other-end))
1291
1292 ;; Always resume isearching by restarting it. 1288 ;; Always resume isearching by restarting it.
1293 (isearch-mode isearch-forward 1289 (isearch-mode isearch-forward
1294 isearch-regexp 1290 isearch-regexp
@@ -1301,7 +1297,17 @@ You can update the global isearch variables by setting new values to
1301 isearch-message isearch-new-message 1297 isearch-message isearch-new-message
1302 isearch-forward isearch-new-forward 1298 isearch-forward isearch-new-forward
1303 isearch-regexp-function isearch-new-regexp-function 1299 isearch-regexp-function isearch-new-regexp-function
1304 isearch-case-fold-search isearch-new-case-fold)) 1300 isearch-case-fold-search isearch-new-case-fold)
1301
1302 ;; Restore the minibuffer message before moving point.
1303 (isearch-call-message nil t)
1304
1305 ;; Set point at the start (end) of old match if forward (backward),
1306 ;; so after exiting minibuffer isearch resumes at the start (end)
1307 ;; of this match and can find it again.
1308 (if (and old-other-end (eq old-point (point))
1309 (eq isearch-forward isearch-new-forward))
1310 (goto-char old-other-end)))
1305 1311
1306 ;; Empty isearch-string means use default. 1312 ;; Empty isearch-string means use default.
1307 (when (= 0 (length isearch-string)) 1313 (when (= 0 (length isearch-string))
@@ -1895,6 +1901,7 @@ If search string is empty, just beep."
1895 (length isearch-string)))) 1901 (length isearch-string))))
1896 isearch-message (mapconcat 'isearch-text-char-description 1902 isearch-message (mapconcat 'isearch-text-char-description
1897 isearch-string ""))) 1903 isearch-string "")))
1904 (isearch-call-message nil t) ; Do this before moving point.
1898 ;; Use the isearch-other-end as new starting point to be able 1905 ;; Use the isearch-other-end as new starting point to be able
1899 ;; to find the remaining part of the search string again. 1906 ;; to find the remaining part of the search string again.
1900 ;; This is like what `isearch-search-and-update' does, 1907 ;; This is like what `isearch-search-and-update' does,
@@ -2071,6 +2078,7 @@ With argument, add COUNT copies of the character."
2071 (setq isearch-case-fold-search 2078 (setq isearch-case-fold-search
2072 (isearch-no-upper-case-p isearch-string isearch-regexp)))) 2079 (isearch-no-upper-case-p isearch-string isearch-regexp))))
2073 ;; Not regexp, not reverse, or no match at point. 2080 ;; Not regexp, not reverse, or no match at point.
2081 (isearch-call-message nil t) ; Do this before moving point.
2074 (if (and isearch-other-end (not isearch-adjusted)) 2082 (if (and isearch-other-end (not isearch-adjusted))
2075 (goto-char (if isearch-forward isearch-other-end 2083 (goto-char (if isearch-forward isearch-other-end
2076 (min isearch-opoint 2084 (min isearch-opoint
@@ -2237,10 +2245,12 @@ Return nil if it's completely visible, or if point is visible,
2237together with as much of the search string as will fit; the symbol 2245together with as much of the search string as will fit; the symbol
2238`above' if we need to scroll the text downwards; the symbol `below', 2246`above' if we need to scroll the text downwards; the symbol `below',
2239if upwards." 2247if upwards."
2240 (let ((w-start (window-start)) 2248 (let ((w-start (window-start nil t))
2241 (w-end (window-end nil t)) 2249 (w-end (window-end nil t t))
2242 (w-L1 (save-excursion (move-to-window-line 1) (point))) 2250 (w-L1 (save-excursion
2243 (w-L-1 (save-excursion (move-to-window-line -1) (point))) 2251 (save-selected-window (move-to-window-line 1 t) (point))))
2252 (w-L-1 (save-excursion
2253 (save-selected-window (move-to-window-line -1 t) (point))))
2244 start end) ; start and end of search string in buffer 2254 start end) ; start and end of search string in buffer
2245 (if isearch-forward 2255 (if isearch-forward
2246 (setq end isearch-point start (or isearch-other-end isearch-point)) 2256 (setq end isearch-point start (or isearch-other-end isearch-point))
@@ -2267,15 +2277,15 @@ the bottom."
2267 (if above 2277 (if above
2268 (progn 2278 (progn
2269 (goto-char start) 2279 (goto-char start)
2270 (recenter 0) 2280 (recenter 0 t)
2271 (when (>= isearch-point (window-end nil t)) 2281 (when (>= isearch-point (window-end nil t t))
2272 (goto-char isearch-point) 2282 (goto-char isearch-point)
2273 (recenter -1))) 2283 (recenter -1 t)))
2274 (goto-char end) 2284 (goto-char end)
2275 (recenter -1) 2285 (recenter -1 t)
2276 (when (< isearch-point (window-start)) 2286 (when (< isearch-point (window-start nil t))
2277 (goto-char isearch-point) 2287 (goto-char isearch-point)
2278 (recenter 0)))) 2288 (recenter 0 t))))
2279 (goto-char isearch-point)) 2289 (goto-char isearch-point))
2280 2290
2281(defvar isearch-pre-scroll-point nil) 2291(defvar isearch-pre-scroll-point nil)
@@ -2422,6 +2432,7 @@ Search is updated accordingly."
2422 (isearch-ring-adjust1 advance) 2432 (isearch-ring-adjust1 advance)
2423 (if search-ring-update 2433 (if search-ring-update
2424 (progn 2434 (progn
2435 (isearch-call-message nil t)
2425 (isearch-search) 2436 (isearch-search)
2426 (isearch-push-state) 2437 (isearch-push-state)
2427 (isearch-update)) 2438 (isearch-update))
@@ -2494,6 +2505,13 @@ If there is no completion possible, say so and continue searching."
2494 2505
2495(defun isearch-message (&optional c-q-hack ellipsis) 2506(defun isearch-message (&optional c-q-hack ellipsis)
2496 ;; Generate and print the message string. 2507 ;; Generate and print the message string.
2508
2509 ;; N.B.: This function should always be called with point at the
2510 ;; search point, because in certain (rare) circumstances, undesired
2511 ;; scrolling can happen when point is elsewhere. These
2512 ;; circumstances are when follow-mode is active, the search string
2513 ;; spans two (or several) windows, and the message about to be
2514 ;; displayed will cause the echo area to expand.
2497 (let ((cursor-in-echo-area ellipsis) 2515 (let ((cursor-in-echo-area ellipsis)
2498 (m isearch-message) 2516 (m isearch-message)
2499 (fail-pos (isearch-fail-pos t))) 2517 (fail-pos (isearch-fail-pos t)))
@@ -2680,9 +2698,6 @@ update the match data, and return point."
2680 2698
2681(defun isearch-search () 2699(defun isearch-search ()
2682 ;; Do the search with the current search string. 2700 ;; Do the search with the current search string.
2683 (if isearch-message-function
2684 (funcall isearch-message-function nil t)
2685 (isearch-message nil t))
2686 (if (and (eq isearch-case-fold-search t) search-upper-case) 2701 (if (and (eq isearch-case-fold-search t) search-upper-case)
2687 (setq isearch-case-fold-search 2702 (setq isearch-case-fold-search
2688 (isearch-no-upper-case-p isearch-string isearch-regexp))) 2703 (isearch-no-upper-case-p isearch-string isearch-regexp)))
@@ -2980,6 +2995,7 @@ since they have special meaning in a regexp."
2980(defvar isearch-lazy-highlight-timer nil) 2995(defvar isearch-lazy-highlight-timer nil)
2981(defvar isearch-lazy-highlight-last-string nil) 2996(defvar isearch-lazy-highlight-last-string nil)
2982(defvar isearch-lazy-highlight-window nil) 2997(defvar isearch-lazy-highlight-window nil)
2998(defvar isearch-lazy-highlight-window-group nil)
2983(defvar isearch-lazy-highlight-window-start nil) 2999(defvar isearch-lazy-highlight-window-start nil)
2984(defvar isearch-lazy-highlight-window-end nil) 3000(defvar isearch-lazy-highlight-window-end nil)
2985(defvar isearch-lazy-highlight-case-fold-search nil) 3001(defvar isearch-lazy-highlight-case-fold-search nil)
@@ -3012,7 +3028,21 @@ is nil. This function is called when exiting an incremental search if
3012 "22.1") 3028 "22.1")
3013 3029
3014(defun isearch-lazy-highlight-new-loop (&optional beg end) 3030(defun isearch-lazy-highlight-new-loop (&optional beg end)
3015 "Cleanup any previous `lazy-highlight' loop and begin a new one. 3031 "Set an idle timer, which will trigger a new `lazy-highlight' loop.
3032BEG and END specify the bounds within which highlighting should
3033occur. This is called when `isearch-update' is invoked (which
3034can cause the search string to change or the window(s) to
3035scroll). It is also used by other Emacs features. Do not start
3036the loop when we are executing a keyboard macro."
3037 (setq isearch-lazy-highlight-start-limit beg
3038 isearch-lazy-highlight-end-limit end)
3039 (when (null executing-kbd-macro)
3040 (setq isearch-lazy-highlight-timer
3041 (run-with-idle-timer lazy-highlight-initial-delay nil
3042 'isearch-lazy-highlight-maybe-new-loop))))
3043
3044(defun isearch-lazy-highlight-maybe-new-loop ()
3045 "If needed cleanup any previous `lazy-highlight' loop and begin a new one.
3016BEG and END specify the bounds within which highlighting should occur. 3046BEG and END specify the bounds within which highlighting should occur.
3017This is called when `isearch-update' is invoked (which can cause the 3047This is called when `isearch-update' is invoked (which can cause the
3018search string to change or the window to scroll). It is also used 3048search string to change or the window to scroll). It is also used
@@ -3021,8 +3051,8 @@ by other Emacs features."
3021 (sit-for 0) ;make sure (window-start) is credible 3051 (sit-for 0) ;make sure (window-start) is credible
3022 (or (not (equal isearch-string 3052 (or (not (equal isearch-string
3023 isearch-lazy-highlight-last-string)) 3053 isearch-lazy-highlight-last-string))
3024 (not (eq (selected-window) 3054 (not (memq (selected-window)
3025 isearch-lazy-highlight-window)) 3055 isearch-lazy-highlight-window-group))
3026 (not (eq isearch-lazy-highlight-case-fold-search 3056 (not (eq isearch-lazy-highlight-case-fold-search
3027 isearch-case-fold-search)) 3057 isearch-case-fold-search))
3028 (not (eq isearch-lazy-highlight-regexp 3058 (not (eq isearch-lazy-highlight-regexp
@@ -3033,9 +3063,9 @@ by other Emacs features."
3033 isearch-lax-whitespace)) 3063 isearch-lax-whitespace))
3034 (not (eq isearch-lazy-highlight-regexp-lax-whitespace 3064 (not (eq isearch-lazy-highlight-regexp-lax-whitespace
3035 isearch-regexp-lax-whitespace)) 3065 isearch-regexp-lax-whitespace))
3036 (not (= (window-start) 3066 (not (= (window-start nil t)
3037 isearch-lazy-highlight-window-start)) 3067 isearch-lazy-highlight-window-start))
3038 (not (= (window-end) ; Window may have been split/joined. 3068 (not (= (window-end nil nil t) ; Window may have been split/joined.
3039 isearch-lazy-highlight-window-end)) 3069 isearch-lazy-highlight-window-end))
3040 (not (eq isearch-forward 3070 (not (eq isearch-forward
3041 isearch-lazy-highlight-forward)) 3071 isearch-lazy-highlight-forward))
@@ -3043,16 +3073,15 @@ by other Emacs features."
3043 (not (equal isearch-error 3073 (not (equal isearch-error
3044 isearch-lazy-highlight-error)))) 3074 isearch-lazy-highlight-error))))
3045 ;; something important did indeed change 3075 ;; something important did indeed change
3046 (lazy-highlight-cleanup t) ;kill old loop & remove overlays 3076 (lazy-highlight-cleanup t) ;kill old loop & remove overlays
3047 (setq isearch-lazy-highlight-error isearch-error) 3077 (setq isearch-lazy-highlight-error isearch-error)
3048 ;; It used to check for `(not isearch-error)' here, but actually 3078 ;; It used to check for `(not isearch-error)' here, but actually
3049 ;; lazy-highlighting might find matches to highlight even when 3079 ;; lazy-highlighting might find matches to highlight even when
3050 ;; `isearch-error' is non-nil. (Bug#9918) 3080 ;; `isearch-error' is non-nil. (Bug#9918)
3051 (setq isearch-lazy-highlight-start-limit beg
3052 isearch-lazy-highlight-end-limit end)
3053 (setq isearch-lazy-highlight-window (selected-window) 3081 (setq isearch-lazy-highlight-window (selected-window)
3054 isearch-lazy-highlight-window-start (window-start) 3082 isearch-lazy-highlight-window-group (selected-window-group)
3055 isearch-lazy-highlight-window-end (window-end) 3083 isearch-lazy-highlight-window-start (window-start nil t)
3084 isearch-lazy-highlight-window-end (window-end nil nil t)
3056 ;; Start lazy-highlighting at the beginning of the found 3085 ;; Start lazy-highlighting at the beginning of the found
3057 ;; match (`isearch-other-end'). If no match, use point. 3086 ;; match (`isearch-other-end'). If no match, use point.
3058 ;; One of the next two variables (depending on search direction) 3087 ;; One of the next two variables (depending on search direction)
@@ -3070,10 +3099,8 @@ by other Emacs features."
3070 isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace 3099 isearch-lazy-highlight-regexp-lax-whitespace isearch-regexp-lax-whitespace
3071 isearch-lazy-highlight-regexp-function isearch-regexp-function 3100 isearch-lazy-highlight-regexp-function isearch-regexp-function
3072 isearch-lazy-highlight-forward isearch-forward) 3101 isearch-lazy-highlight-forward isearch-forward)
3073 (unless (equal isearch-string "") 3102 (unless (equal isearch-string "")
3074 (setq isearch-lazy-highlight-timer 3103 (isearch-lazy-highlight-update))))
3075 (run-with-idle-timer lazy-highlight-initial-delay nil
3076 'isearch-lazy-highlight-update)))))
3077 3104
3078(defun isearch-lazy-highlight-search () 3105(defun isearch-lazy-highlight-search ()
3079 "Search ahead for the next or previous match, for lazy highlighting. 3106 "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."
3096 (+ isearch-lazy-highlight-start 3123 (+ isearch-lazy-highlight-start
3097 ;; Extend bound to match whole string at point 3124 ;; Extend bound to match whole string at point
3098 (1- (length isearch-lazy-highlight-last-string))) 3125 (1- (length isearch-lazy-highlight-last-string)))
3099 (window-end))) 3126 (window-end nil nil t)))
3100 (max (or isearch-lazy-highlight-start-limit (point-min)) 3127 (max (or isearch-lazy-highlight-start-limit (point-min))
3101 (if isearch-lazy-highlight-wrapped 3128 (if isearch-lazy-highlight-wrapped
3102 (- isearch-lazy-highlight-end 3129 (- isearch-lazy-highlight-end
3103 ;; Extend bound to match whole string at point 3130 ;; Extend bound to match whole string at point
3104 (1- (length isearch-lazy-highlight-last-string))) 3131 (1- (length isearch-lazy-highlight-last-string)))
3105 (window-start)))))) 3132 (window-start nil t))))))
3106 ;; Use a loop like in `isearch-search'. 3133 ;; Use a loop like in `isearch-search'.
3107 (while retry 3134 (while retry
3108 (setq success (isearch-search-string 3135 (setq success (isearch-search-string
@@ -3126,7 +3153,7 @@ Attempt to do the search exactly the way the pending Isearch would."
3126 (with-local-quit 3153 (with-local-quit
3127 (save-selected-window 3154 (save-selected-window
3128 (if (and (window-live-p isearch-lazy-highlight-window) 3155 (if (and (window-live-p isearch-lazy-highlight-window)
3129 (not (eq (selected-window) isearch-lazy-highlight-window))) 3156 (not (memq (selected-window) isearch-lazy-highlight-window-group)))
3130 (select-window isearch-lazy-highlight-window)) 3157 (select-window isearch-lazy-highlight-window))
3131 (save-excursion 3158 (save-excursion
3132 (save-match-data 3159 (save-match-data
@@ -3146,12 +3173,12 @@ Attempt to do the search exactly the way the pending Isearch would."
3146 (if isearch-lazy-highlight-forward 3173 (if isearch-lazy-highlight-forward
3147 (if (= mb (if isearch-lazy-highlight-wrapped 3174 (if (= mb (if isearch-lazy-highlight-wrapped
3148 isearch-lazy-highlight-start 3175 isearch-lazy-highlight-start
3149 (window-end))) 3176 (window-end nil nil t)))
3150 (setq found nil) 3177 (setq found nil)
3151 (forward-char 1)) 3178 (forward-char 1))
3152 (if (= mb (if isearch-lazy-highlight-wrapped 3179 (if (= mb (if isearch-lazy-highlight-wrapped
3153 isearch-lazy-highlight-end 3180 isearch-lazy-highlight-end
3154 (window-start))) 3181 (window-start nil t)))
3155 (setq found nil) 3182 (setq found nil)
3156 (forward-char -1))) 3183 (forward-char -1)))
3157 3184
@@ -3161,8 +3188,8 @@ Attempt to do the search exactly the way the pending Isearch would."
3161 ;; 1000 is higher than ediff's 100+, 3188 ;; 1000 is higher than ediff's 100+,
3162 ;; but lower than isearch main overlay's 1001 3189 ;; but lower than isearch main overlay's 1001
3163 (overlay-put ov 'priority 1000) 3190 (overlay-put ov 'priority 1000)
3164 (overlay-put ov 'face lazy-highlight-face) 3191 (overlay-put ov 'face lazy-highlight-face)))
3165 (overlay-put ov 'window (selected-window)))) 3192 ;(overlay-put ov 'window (selected-window))))
3166 ;; Remember the current position of point for 3193 ;; Remember the current position of point for
3167 ;; the next call of `isearch-lazy-highlight-update' 3194 ;; the next call of `isearch-lazy-highlight-update'
3168 ;; when `lazy-highlight-max-at-a-time' is too small. 3195 ;; 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."
3178 (setq isearch-lazy-highlight-wrapped t) 3205 (setq isearch-lazy-highlight-wrapped t)
3179 (if isearch-lazy-highlight-forward 3206 (if isearch-lazy-highlight-forward
3180 (progn 3207 (progn
3181 (setq isearch-lazy-highlight-end (window-start)) 3208 (setq isearch-lazy-highlight-end (window-start nil t))
3182 (goto-char (max (or isearch-lazy-highlight-start-limit (point-min)) 3209 (goto-char (max (or isearch-lazy-highlight-start-limit (point-min))
3183 (window-start)))) 3210 (window-start nil t))))
3184 (setq isearch-lazy-highlight-start (window-end)) 3211 (setq isearch-lazy-highlight-start (window-end nil nil t))
3185 (goto-char (min (or isearch-lazy-highlight-end-limit (point-max)) 3212 (goto-char (min (or isearch-lazy-highlight-end-limit (point-max))
3186 (window-end)))))))) 3213 (window-end nil nil t))))))))
3187 (unless nomore 3214 (unless nomore
3188 (setq isearch-lazy-highlight-timer 3215 (setq isearch-lazy-highlight-timer
3189 (run-at-time lazy-highlight-interval nil 3216 (run-at-time lazy-highlight-interval nil
diff --git a/lisp/window.el b/lisp/window.el
index 6d189055c15..0c403eb184a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -28,6 +28,17 @@
28 28
29;;; Code: 29;;; Code:
30 30
31(defvar selected-window-group-function nil)
32(make-variable-buffer-local 'selected-window-group-function)
33(put 'selected-window-group-function 'permanent-local t)
34(defun selected-window-group ()
35 "Return the list of windows in the group containing the selected window.
36When a grouping mode (such as Follow Mode) is not active, the
37result is a list containing only the selected window."
38 (if (functionp selected-window-group-function)
39 (funcall selected-window-group-function)
40 (list (selected-window))))
41
31(defun internal--before-save-selected-window () 42(defun internal--before-save-selected-window ()
32 (cons (selected-window) 43 (cons (selected-window)
33 ;; We save and restore all frames' selected windows, because 44 ;; We save and restore all frames' selected windows, because
diff --git a/src/keyboard.c b/src/keyboard.c
index 851207874db..c7733568e7f 100644
--- a/src/keyboard.c
+++ b/src/keyboard.c
@@ -10663,7 +10663,7 @@ The `posn-' functions access elements of such lists. */)
10663 if (NILP (window)) 10663 if (NILP (window))
10664 window = selected_window; 10664 window = selected_window;
10665 10665
10666 tem = Fpos_visible_in_window_p (pos, window, Qt); 10666 tem = Fpos_visible_in_window_p (pos, window, Qt, Qnil);
10667 if (!NILP (tem)) 10667 if (!NILP (tem))
10668 { 10668 {
10669 Lisp_Object x = XCAR (tem); 10669 Lisp_Object x = XCAR (tem);
diff --git a/src/window.c b/src/window.c
index 0ac76d41861..efb4c9b4f8c 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. */)
1540 return Fmarker_position (decode_live_window (window)->old_pointm); 1540 return Fmarker_position (decode_live_window (window)->old_pointm);
1541} 1541}
1542 1542
1543DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 1, 0, 1543DEFUN ("window-start", Fwindow_start, Swindow_start, 0, 2, 0,
1544 doc: /* Return position at which display currently starts in WINDOW. 1544 doc: /* Return position at which display currently starts in WINDOW.
1545WINDOW must be a live window and defaults to the selected one. 1545WINDOW must be a live window and defaults to the selected one.
1546This is updated by redisplay or by calling `set-window-start'. */) 1546This is updated by redisplay or by calling `set-window-start'.
1547 (Lisp_Object window) 1547
1548If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1549displaying a buffer (such as with Follow Mode), return the start position of
1550the group rather than of the individual WINDOW. This condition holds when
1551`window-start-group-function' is set to a function, in which case
1552`window-start' calls the function with the argument WINDOW, then returns its
1553result, instead of doing its normal processing. */)
1554 (Lisp_Object window, Lisp_Object group)
1548{ 1555{
1549 return Fmarker_position (decode_live_window (window)->start); 1556 return (!NILP (group)
1557 && FUNCTIONP (Vwindow_start_group_function))
1558 ? call1 (Vwindow_start_group_function, window)
1559 : Fmarker_position (decode_live_window (window)->start);
1550} 1560}
1551 1561
1552/* This is text temporarily removed from the doc string below. 1562/* This is text temporarily removed from the doc string below.
@@ -1560,7 +1570,7 @@ have been if redisplay had finished, do this:
1560 (vertical-motion (1- (window-height window)) window) 1570 (vertical-motion (1- (window-height window)) window)
1561 (point))") */ 1571 (point))") */
1562 1572
1563DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 2, 0, 1573DEFUN ("window-end", Fwindow_end, Swindow_end, 0, 3, 0,
1564 doc: /* Return position at which display currently ends in WINDOW. 1574 doc: /* Return position at which display currently ends in WINDOW.
1565WINDOW must be a live window and defaults to the selected one. 1575WINDOW must be a live window and defaults to the selected one.
1566This is updated by redisplay, when it runs to completion. 1576This is updated by redisplay, when it runs to completion.
@@ -1569,65 +1579,77 @@ does not update this value.
1569Return nil if there is no recorded value. (This can happen if the 1579Return nil if there is no recorded value. (This can happen if the
1570last redisplay of WINDOW was preempted, and did not finish.) 1580last redisplay of WINDOW was preempted, and did not finish.)
1571If UPDATE is non-nil, compute the up-to-date position 1581If UPDATE is non-nil, compute the up-to-date position
1572if it isn't already recorded. */) 1582if it isn't already recorded.
1573 (Lisp_Object window, Lisp_Object update) 1583
1574{ 1584If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1575 Lisp_Object value; 1585displaying a buffer (such as with Follow Mode), return the end position of
1576 struct window *w = decode_live_window (window); 1586the group rather than of the individual WINDOW. This condition holds when
1577 Lisp_Object buf; 1587`window-end-group-function' is set to a function, in which case `window-end'
1578 struct buffer *b; 1588calls the function with the two arguments WINDOW and UPDATE, then returns its
1579 1589result, instead of doing its normal processing. */)
1580 buf = w->contents; 1590 (Lisp_Object window, Lisp_Object update, Lisp_Object group)
1581 CHECK_BUFFER (buf); 1591{
1582 b = XBUFFER (buf); 1592 if (!NILP (group)
1583 1593 && FUNCTIONP (Vwindow_end_group_function))
1584 if (! NILP (update) 1594 return call2 (Vwindow_end_group_function, window, update);
1585 && (windows_or_buffers_changed 1595 {
1586 || !w->window_end_valid 1596 Lisp_Object value;
1587 || b->clip_changed 1597 struct window *w = decode_live_window (window);
1588 || b->prevent_redisplay_optimizations_p 1598 Lisp_Object buf;
1589 || window_outdated (w)) 1599 struct buffer *b;
1590 /* Don't call display routines if we didn't yet create any real 1600
1591 frames, because the glyph matrices are not yet allocated in 1601 buf = w->contents;
1592 that case. This could happen in some code that runs in the 1602 CHECK_BUFFER (buf);
1593 daemon during initialization (e.g., see bug#20565). */ 1603 b = XBUFFER (buf);
1594 && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w)))) 1604
1595 { 1605 if (! NILP (update)
1596 struct text_pos startp; 1606 && (windows_or_buffers_changed
1597 struct it it; 1607 || !w->window_end_valid
1598 struct buffer *old_buffer = NULL; 1608 || b->clip_changed
1599 void *itdata = NULL; 1609 || b->prevent_redisplay_optimizations_p
1600 1610 || window_outdated (w))
1601 /* Cannot use Fvertical_motion because that function doesn't 1611 /* Don't call display routines if we didn't yet create any real
1602 cope with variable-height lines. */ 1612 frames, because the glyph matrices are not yet allocated in
1603 if (b != current_buffer) 1613 that case. This could happen in some code that runs in the
1604 { 1614 daemon during initialization (e.g., see bug#20565). */
1605 old_buffer = current_buffer; 1615 && !(noninteractive || FRAME_INITIAL_P (WINDOW_XFRAME (w))))
1606 set_buffer_internal (b); 1616 {
1607 } 1617 struct text_pos startp;
1608 1618 struct it it;
1609 /* In case W->start is out of the range, use something 1619 struct buffer *old_buffer = NULL;
1610 reasonable. This situation occurred when loading a file with 1620 void *itdata = NULL;
1611 `-l' containing a call to `rmail' with subsequent other 1621
1612 commands. At the end, W->start happened to be BEG, while 1622 /* Cannot use Fvertical_motion because that function doesn't
1613 rmail had already narrowed the buffer. */ 1623 cope with variable-height lines. */
1614 CLIP_TEXT_POS_FROM_MARKER (startp, w->start); 1624 if (b != current_buffer)
1615 1625 {
1616 itdata = bidi_shelve_cache (); 1626 old_buffer = current_buffer;
1617 start_display (&it, w, startp); 1627 set_buffer_internal (b);
1618 move_it_vertically (&it, window_box_height (w)); 1628 }
1619 if (it.current_y < it.last_visible_y) 1629
1620 move_it_past_eol (&it); 1630 /* In case W->start is out of the range, use something
1621 value = make_number (IT_CHARPOS (it)); 1631 reasonable. This situation occurred when loading a file with
1622 bidi_unshelve_cache (itdata, false); 1632 `-l' containing a call to `rmail' with subsequent other
1623 1633 commands. At the end, W->start happened to be BEG, while
1624 if (old_buffer) 1634 rmail had already narrowed the buffer. */
1625 set_buffer_internal (old_buffer); 1635 CLIP_TEXT_POS_FROM_MARKER (startp, w->start);
1626 } 1636
1627 else 1637 itdata = bidi_shelve_cache ();
1628 XSETINT (value, BUF_Z (b) - w->window_end_pos); 1638 start_display (&it, w, startp);
1639 move_it_vertically (&it, window_box_height (w));
1640 if (it.current_y < it.last_visible_y)
1641 move_it_past_eol (&it);
1642 value = make_number (IT_CHARPOS (it));
1643 bidi_unshelve_cache (itdata, false);
1644
1645 if (old_buffer)
1646 set_buffer_internal (old_buffer);
1647 }
1648 else
1649 XSETINT (value, BUF_Z (b) - w->window_end_pos);
1629 1650
1630 return value; 1651 return value;
1652 }
1631} 1653}
1632 1654
1633DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0, 1655DEFUN ("set-window-point", Fset_window_point, Sset_window_point, 2, 2, 0,
@@ -1666,30 +1688,43 @@ Return POS. */)
1666 return pos; 1688 return pos;
1667} 1689}
1668 1690
1669DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 3, 0, 1691DEFUN ("set-window-start", Fset_window_start, Sset_window_start, 2, 4, 0,
1670 doc: /* Make display in WINDOW start at position POS in WINDOW's buffer. 1692 doc: /* Make display in WINDOW start at position POS in WINDOW's buffer.
1671WINDOW must be a live window and defaults to the selected one. Return 1693WINDOW must be a live window and defaults to the selected one. Return
1672POS. Optional third arg NOFORCE non-nil inhibits next redisplay from 1694POS. Optional third arg NOFORCE non-nil inhibits next redisplay from
1673overriding motion of point in order to display at this exact start. */) 1695overriding motion of point in order to display at this exact start.
1674 (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce) 1696
1675{ 1697If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1676 register struct window *w = decode_live_window (window); 1698displaying a buffer (such as with Follow Mode), set the start position of
1699the group rather than of the individual WINDOW. This condition holds when
1700`set-window-start-group-function' is set to a function, in which case
1701`set-window-start' calls the function with the three arguments WINDOW, POS,
1702and NOFORCE, then returns its result, instead of doing its normal
1703processing. */)
1704 (Lisp_Object window, Lisp_Object pos, Lisp_Object noforce, Lisp_Object group)
1705{
1706 if (!NILP (group)
1707 && FUNCTIONP (Vset_window_start_group_function))
1708 return call3 (Vset_window_start_group_function, window, pos, noforce);
1709 {
1710 register struct window *w = decode_live_window (window);
1677 1711
1678 set_marker_restricted (w->start, pos, w->contents); 1712 set_marker_restricted (w->start, pos, w->contents);
1679 /* This is not right, but much easier than doing what is right. */ 1713 /* This is not right, but much easier than doing what is right. */
1680 w->start_at_line_beg = false; 1714 w->start_at_line_beg = false;
1681 if (NILP (noforce)) 1715 if (NILP (noforce))
1682 w->force_start = true; 1716 w->force_start = true;
1683 wset_update_mode_line (w); 1717 wset_update_mode_line (w);
1684 /* Bug#15957. */ 1718 /* Bug#15957. */
1685 w->window_end_valid = false; 1719 w->window_end_valid = false;
1686 wset_redisplay (w); 1720 wset_redisplay (w);
1687 1721
1688 return pos; 1722 return pos;
1723 }
1689} 1724}
1690 1725
1691DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p, 1726DEFUN ("pos-visible-in-window-p", Fpos_visible_in_window_p,
1692 Spos_visible_in_window_p, 0, 3, 0, 1727 Spos_visible_in_window_p, 0, 4, 0,
1693 doc: /* Return non-nil if position POS is currently on the frame in WINDOW. 1728 doc: /* Return non-nil if position POS is currently on the frame in WINDOW.
1694WINDOW must be a live window and defaults to the selected one. 1729WINDOW must be a live window and defaults to the selected one.
1695 1730
@@ -1709,9 +1744,21 @@ of the window. The remaining elements are omitted if the character after
1709POS is fully visible; otherwise, RTOP and RBOT are the number of pixels 1744POS is fully visible; otherwise, RTOP and RBOT are the number of pixels
1710off-window at the top and bottom of the screen line ("row") containing 1745off-window at the top and bottom of the screen line ("row") containing
1711POS, ROWH is the visible height of that row, and VPOS is the row number 1746POS, ROWH is the visible height of that row, and VPOS is the row number
1712(zero-based). */) 1747(zero-based).
1713 (Lisp_Object pos, Lisp_Object window, Lisp_Object partially) 1748
1714{ 1749If GROUP is non-nil, and WINDOW is part of a group of windows collectively
1750displaying a buffer (such as with Follow Mode), test whether POS is visible
1751in the group of windows rather than in the individual WINDOW. This
1752condition holds when `pos-visible-in-window-p-function' is set to a
1753function, in which case `pos-visible-in-window-p' calls the function with
1754the three arguments POS, WINDOW, and PARTIALLY, then returns its result,
1755instead of doing its normal processing. */)
1756 (Lisp_Object pos, Lisp_Object window, Lisp_Object partially, Lisp_Object group)
1757{
1758 if (!NILP (group)
1759 && FUNCTIONP (Vpos_visible_in_window_p_group_function))
1760 return call3 (Vpos_visible_in_window_p_group_function, pos, window, partially);
1761 {
1715 struct window *w; 1762 struct window *w;
1716 EMACS_INT posint; 1763 EMACS_INT posint;
1717 struct buffer *buf; 1764 struct buffer *buf;
@@ -1760,6 +1807,7 @@ POS, ROWH is the visible height of that row, and VPOS is the row number
1760 } 1807 }
1761 1808
1762 return in_window; 1809 return in_window;
1810 }
1763} 1811}
1764 1812
1765DEFUN ("window-line-height", Fwindow_line_height, 1813DEFUN ("window-line-height", Fwindow_line_height,
@@ -5185,7 +5233,7 @@ window_scroll_line_based (Lisp_Object window, int n, bool whole, bool noerror)
5185 } 5233 }
5186 5234
5187 XSETFASTINT (tem, PT); 5235 XSETFASTINT (tem, PT);
5188 tem = Fpos_visible_in_window_p (tem, window, Qnil); 5236 tem = Fpos_visible_in_window_p (tem, window, Qnil, Qnil);
5189 5237
5190 if (NILP (tem)) 5238 if (NILP (tem))
5191 { 5239 {
@@ -5574,7 +5622,7 @@ displayed_window_lines (struct window *w)
5574} 5622}
5575 5623
5576 5624
5577DEFUN ("recenter", Frecenter, Srecenter, 0, 1, "P", 5625DEFUN ("recenter", Frecenter, Srecenter, 0, 2, "P\ni",
5578 doc: /* Center point in selected window and maybe redisplay frame. 5626 doc: /* Center point in selected window and maybe redisplay frame.
5579With a numeric prefix argument ARG, recenter putting point on screen line ARG 5627With a numeric prefix argument ARG, recenter putting point on screen line ARG
5580relative to the selected window. If ARG is negative, it counts up from the 5628relative 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',
5588then only tty frames are redrawn. 5636then only tty frames are redrawn.
5589 5637
5590Just C-u as prefix means put point in the center of the window 5638Just C-u as prefix means put point in the center of the window
5591and redisplay normally--don't erase and redraw the frame. */) 5639and redisplay normally--don't erase and redraw the frame.
5592 (register Lisp_Object arg) 5640
5593{ 5641When `recenter' is called from a program, GROUP is non-nil, and WINDOW is
5594 struct window *w = XWINDOW (selected_window); 5642part of a group of windows collectively displaying a buffer (such as with
5595 struct buffer *buf = XBUFFER (w->contents); 5643Follow Mode), perform `recenter''s actions on the group rather than on the
5596 bool center_p = false; 5644individual WINDOW. This condition holds when `recenter-group-function' is
5597 ptrdiff_t charpos, bytepos; 5645set to a function, in which case `recenter' calls the function with the
5598 EMACS_INT iarg IF_LINT (= 0); 5646argument ARG, then returns its value, instead of doing its normal
5599 int this_scroll_margin; 5647processing. */)
5600 5648 (register Lisp_Object arg, Lisp_Object group)
5601 if (buf != current_buffer) 5649{
5602 error ("`recenter'ing a window that does not display current-buffer."); 5650 if (!NILP (group)
5651 && FUNCTIONP (Vrecenter_group_function))
5652 return call1 (Vrecenter_group_function, arg);
5653 {
5654 struct window *w = XWINDOW (selected_window);
5655 struct buffer *buf = XBUFFER (w->contents);
5656 bool center_p = false;
5657 ptrdiff_t charpos, bytepos;
5658 EMACS_INT iarg IF_LINT (= 0);
5659 int this_scroll_margin;
5603 5660
5604 /* If redisplay is suppressed due to an error, try again. */ 5661 if (buf != current_buffer)
5605 buf->display_error_modiff = 0; 5662 error ("`recenter'ing a window that does not display current-buffer.");
5606 5663
5607 if (NILP (arg)) 5664 /* If redisplay is suppressed due to an error, try again. */
5608 { 5665 buf->display_error_modiff = 0;
5609 if (!NILP (Vrecenter_redisplay)
5610 && (!EQ (Vrecenter_redisplay, Qtty)
5611 || !NILP (Ftty_type (selected_frame))))
5612 {
5613 ptrdiff_t i;
5614 5666
5615 /* Invalidate pixel data calculated for all compositions. */ 5667 if (NILP (arg))
5616 for (i = 0; i < n_compositions; i++) 5668 {
5617 composition_table[i]->font = NULL; 5669 if (!NILP (Vrecenter_redisplay)
5670 && (!EQ (Vrecenter_redisplay, Qtty)
5671 || !NILP (Ftty_type (selected_frame))))
5672 {
5673 ptrdiff_t i;
5674
5675 /* Invalidate pixel data calculated for all compositions. */
5676 for (i = 0; i < n_compositions; i++)
5677 composition_table[i]->font = NULL;
5618#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS) 5678#if defined (HAVE_WINDOW_SYSTEM) && ! defined (USE_GTK) && ! defined (HAVE_NS)
5619 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1; 5679 WINDOW_XFRAME (w)->minimize_tool_bar_window_p = 1;
5620#endif 5680#endif
5621 Fredraw_frame (WINDOW_FRAME (w)); 5681 Fredraw_frame (WINDOW_FRAME (w));
5622 SET_FRAME_GARBAGED (WINDOW_XFRAME (w)); 5682 SET_FRAME_GARBAGED (WINDOW_XFRAME (w));
5623 } 5683 }
5624 5684
5685 center_p = true;
5686 }
5687 else if (CONSP (arg)) /* Just C-u. */
5625 center_p = true; 5688 center_p = true;
5626 } 5689 else
5627 else if (CONSP (arg)) /* Just C-u. */ 5690 {
5628 center_p = true; 5691 arg = Fprefix_numeric_value (arg);
5629 else 5692 CHECK_NUMBER (arg);
5630 { 5693 iarg = XINT (arg);
5631 arg = Fprefix_numeric_value (arg); 5694 }
5632 CHECK_NUMBER (arg);
5633 iarg = XINT (arg);
5634 }
5635
5636 /* Do this after making BUF current
5637 in case scroll_margin is buffer-local. */
5638 this_scroll_margin
5639 = max (0, min (scroll_margin, w->total_lines / 4));
5640
5641 /* Don't use redisplay code for initial frames, as the necessary
5642 data structures might not be set up yet then. */
5643 if (!FRAME_INITIAL_P (XFRAME (w->frame)))
5644 {
5645 if (center_p)
5646 {
5647 struct it it;
5648 struct text_pos pt;
5649 void *itdata = bidi_shelve_cache ();
5650
5651 SET_TEXT_POS (pt, PT, PT_BYTE);
5652 start_display (&it, w, pt);
5653 move_it_vertically_backward (&it, window_box_height (w) / 2);
5654 charpos = IT_CHARPOS (it);
5655 bytepos = IT_BYTEPOS (it);
5656 bidi_unshelve_cache (itdata, false);
5657 }
5658 else if (iarg < 0)
5659 {
5660 struct it it;
5661 struct text_pos pt;
5662 ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg);
5663 int extra_line_spacing;
5664 int h = window_box_height (w);
5665 int ht = window_internal_height (w);
5666 void *itdata = bidi_shelve_cache ();
5667
5668 nlines = clip_to_bounds (this_scroll_margin + 1, nlines,
5669 ht - this_scroll_margin);
5670
5671 SET_TEXT_POS (pt, PT, PT_BYTE);
5672 start_display (&it, w, pt);
5673
5674 /* Be sure we have the exact height of the full line containing PT. */
5675 move_it_by_lines (&it, 0);
5676
5677 /* The amount of pixels we have to move back is the window
5678 height minus what's displayed in the line containing PT,
5679 and the lines below. */
5680 it.current_y = 0;
5681 it.vpos = 0;
5682 move_it_by_lines (&it, nlines);
5683
5684 if (it.vpos == nlines)
5685 h -= it.current_y;
5686 else
5687 {
5688 /* Last line has no newline. */
5689 h -= line_bottom_y (&it);
5690 it.vpos++;
5691 }
5692
5693 /* Don't reserve space for extra line spacing of last line. */
5694 extra_line_spacing = it.max_extra_line_spacing;
5695
5696 /* If we can't move down NLINES lines because we hit
5697 the end of the buffer, count in some empty lines. */
5698 if (it.vpos < nlines)
5699 {
5700 nlines -= it.vpos;
5701 extra_line_spacing = it.extra_line_spacing;
5702 h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
5703 }
5704 if (h <= 0)
5705 {
5706 bidi_unshelve_cache (itdata, false);
5707 return Qnil;
5708 }
5709
5710 /* Now find the new top line (starting position) of the window. */
5711 start_display (&it, w, pt);
5712 it.current_y = 0;
5713 move_it_vertically_backward (&it, h);
5714
5715 /* If extra line spacing is present, we may move too far
5716 back. This causes the last line to be only partially
5717 visible (which triggers redisplay to recenter that line
5718 in the middle), so move forward.
5719 But ignore extra line spacing on last line, as it is not
5720 considered to be part of the visible height of the line.
5721 */
5722 h += extra_line_spacing;
5723 while (-it.current_y > h)
5724 move_it_by_lines (&it, 1);
5725
5726 charpos = IT_CHARPOS (it);
5727 bytepos = IT_BYTEPOS (it);
5728
5729 bidi_unshelve_cache (itdata, false);
5730 }
5731 else
5732 {
5733 struct it it;
5734 struct text_pos pt;
5735 ptrdiff_t nlines = min (PTRDIFF_MAX, iarg);
5736 int ht = window_internal_height (w);
5737 void *itdata = bidi_shelve_cache ();
5738
5739 nlines = clip_to_bounds (this_scroll_margin, nlines,
5740 ht - this_scroll_margin - 1);
5741
5742 SET_TEXT_POS (pt, PT, PT_BYTE);
5743 start_display (&it, w, pt);
5744
5745 /* Move to the beginning of screen line containing PT. */
5746 move_it_by_lines (&it, 0);
5747
5748 /* Move back to find the point which is ARG screen lines above PT. */
5749 if (nlines > 0)
5750 {
5751 it.current_y = 0;
5752 it.vpos = 0;
5753 move_it_by_lines (&it, -nlines);
5754 }
5755 5695
5756 charpos = IT_CHARPOS (it); 5696 /* Do this after making BUF current
5757 bytepos = IT_BYTEPOS (it); 5697 in case scroll_margin is buffer-local. */
5698 this_scroll_margin
5699 = max (0, min (scroll_margin, w->total_lines / 4));
5758 5700
5759 bidi_unshelve_cache (itdata, false); 5701 /* Don't use redisplay code for initial frames, as the necessary
5760 } 5702 data structures might not be set up yet then. */
5761 } 5703 if (!FRAME_INITIAL_P (XFRAME (w->frame)))
5762 else 5704 {
5763 { 5705 if (center_p)
5764 struct position pos; 5706 {
5765 int ht = window_internal_height (w); 5707 struct it it;
5708 struct text_pos pt;
5709 void *itdata = bidi_shelve_cache ();
5710
5711 SET_TEXT_POS (pt, PT, PT_BYTE);
5712 start_display (&it, w, pt);
5713 move_it_vertically_backward (&it, window_box_height (w) / 2);
5714 charpos = IT_CHARPOS (it);
5715 bytepos = IT_BYTEPOS (it);
5716 bidi_unshelve_cache (itdata, false);
5717 }
5718 else if (iarg < 0)
5719 {
5720 struct it it;
5721 struct text_pos pt;
5722 ptrdiff_t nlines = min (PTRDIFF_MAX, -iarg);
5723 int extra_line_spacing;
5724 int h = window_box_height (w);
5725 int ht = window_internal_height (w);
5726 void *itdata = bidi_shelve_cache ();
5727
5728 nlines = clip_to_bounds (this_scroll_margin + 1, nlines,
5729 ht - this_scroll_margin);
5730
5731 SET_TEXT_POS (pt, PT, PT_BYTE);
5732 start_display (&it, w, pt);
5733
5734 /* Be sure we have the exact height of the full line containing PT. */
5735 move_it_by_lines (&it, 0);
5736
5737 /* The amount of pixels we have to move back is the window
5738 height minus what's displayed in the line containing PT,
5739 and the lines below. */
5740 it.current_y = 0;
5741 it.vpos = 0;
5742 move_it_by_lines (&it, nlines);
5743
5744 if (it.vpos == nlines)
5745 h -= it.current_y;
5746 else
5747 {
5748 /* Last line has no newline. */
5749 h -= line_bottom_y (&it);
5750 it.vpos++;
5751 }
5752
5753 /* Don't reserve space for extra line spacing of last line. */
5754 extra_line_spacing = it.max_extra_line_spacing;
5755
5756 /* If we can't move down NLINES lines because we hit
5757 the end of the buffer, count in some empty lines. */
5758 if (it.vpos < nlines)
5759 {
5760 nlines -= it.vpos;
5761 extra_line_spacing = it.extra_line_spacing;
5762 h -= nlines * (FRAME_LINE_HEIGHT (it.f) + extra_line_spacing);
5763 }
5764 if (h <= 0)
5765 {
5766 bidi_unshelve_cache (itdata, false);
5767 return Qnil;
5768 }
5769
5770 /* Now find the new top line (starting position) of the window. */
5771 start_display (&it, w, pt);
5772 it.current_y = 0;
5773 move_it_vertically_backward (&it, h);
5774
5775 /* If extra line spacing is present, we may move too far
5776 back. This causes the last line to be only partially
5777 visible (which triggers redisplay to recenter that line
5778 in the middle), so move forward.
5779 But ignore extra line spacing on last line, as it is not
5780 considered to be part of the visible height of the line.
5781 */
5782 h += extra_line_spacing;
5783 while (-it.current_y > h)
5784 move_it_by_lines (&it, 1);
5785
5786 charpos = IT_CHARPOS (it);
5787 bytepos = IT_BYTEPOS (it);
5788
5789 bidi_unshelve_cache (itdata, false);
5790 }
5791 else
5792 {
5793 struct it it;
5794 struct text_pos pt;
5795 ptrdiff_t nlines = min (PTRDIFF_MAX, iarg);
5796 int ht = window_internal_height (w);
5797 void *itdata = bidi_shelve_cache ();
5798
5799 nlines = clip_to_bounds (this_scroll_margin, nlines,
5800 ht - this_scroll_margin - 1);
5801
5802 SET_TEXT_POS (pt, PT, PT_BYTE);
5803 start_display (&it, w, pt);
5804
5805 /* Move to the beginning of screen line containing PT. */
5806 move_it_by_lines (&it, 0);
5807
5808 /* Move back to find the point which is ARG screen lines above PT. */
5809 if (nlines > 0)
5810 {
5811 it.current_y = 0;
5812 it.vpos = 0;
5813 move_it_by_lines (&it, -nlines);
5814 }
5815
5816 charpos = IT_CHARPOS (it);
5817 bytepos = IT_BYTEPOS (it);
5818
5819 bidi_unshelve_cache (itdata, false);
5820 }
5821 }
5822 else
5823 {
5824 struct position pos;
5825 int ht = window_internal_height (w);
5766 5826
5767 if (center_p) 5827 if (center_p)
5768 iarg = ht / 2; 5828 iarg = ht / 2;
5769 else if (iarg < 0) 5829 else if (iarg < 0)
5770 iarg += ht; 5830 iarg += ht;
5771 5831
5772 /* Don't let it get into the margin at either top or bottom. */ 5832 /* Don't let it get into the margin at either top or bottom. */
5773 iarg = clip_to_bounds (this_scroll_margin, iarg, 5833 iarg = clip_to_bounds (this_scroll_margin, iarg,
5774 ht - this_scroll_margin - 1); 5834 ht - this_scroll_margin - 1);
5775 5835
5776 pos = *vmotion (PT, PT_BYTE, - iarg, w); 5836 pos = *vmotion (PT, PT_BYTE, - iarg, w);
5777 charpos = pos.bufpos; 5837 charpos = pos.bufpos;
5778 bytepos = pos.bytepos; 5838 bytepos = pos.bytepos;
5779 } 5839 }
5780 5840
5781 /* Set the new window start. */ 5841 /* Set the new window start. */
5782 set_marker_both (w->start, w->contents, charpos, bytepos); 5842 set_marker_both (w->start, w->contents, charpos, bytepos);
5783 w->window_end_valid = false; 5843 w->window_end_valid = false;
5784 5844
5785 w->optional_new_start = true; 5845 w->optional_new_start = true;
5786 5846
5787 w->start_at_line_beg = (bytepos == BEGV_BYTE 5847 w->start_at_line_beg = (bytepos == BEGV_BYTE
5788 || FETCH_BYTE (bytepos - 1) == '\n'); 5848 || FETCH_BYTE (bytepos - 1) == '\n');
5789 5849
5790 wset_redisplay (w); 5850 wset_redisplay (w);
5791 5851
5792 return Qnil; 5852 return Qnil;
5853 }
5793} 5854}
5794 5855
5795DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width, 5856DEFUN ("window-text-width", Fwindow_text_width, Swindow_text_width,
@@ -5836,52 +5897,68 @@ pixels. */)
5836} 5897}
5837 5898
5838DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line, 5899DEFUN ("move-to-window-line", Fmove_to_window_line, Smove_to_window_line,
5839 1, 1, "P", 5900 1, 2, "P\ni",
5840 doc: /* Position point relative to window. 5901 doc: /* Position point relative to window.
5841ARG nil means position point at center of window. 5902ARG nil means position point at center of window.
5842Else, ARG specifies vertical position within the window; 5903Else, ARG specifies vertical position within the window;
5843zero means top of window, negative means relative to bottom of window. */) 5904zero means top of window, negative means relative to bottom of window.
5844 (Lisp_Object arg) 5905
5845{ 5906When GROUP is non-nil, and `move-to-window-line-group-function' is set to a
5846 struct window *w = XWINDOW (selected_window); 5907function, then instead of the above, that function is called with the
5847 int lines, start; 5908single argument ARG, and its result returned.
5848 Lisp_Object window; 5909
5910If GROUP is non-nil, and WINDOW is part of a group of windows collectively
5911displaying a buffer (such as with Follow Mode), position point relative to
5912the group of windows as a whole rather than the individual WINDOW. This
5913condition holds when `move-to-window-line-group-function' is set to a
5914function, in which case `move-to-window-line' calls the function with the
5915argument ARG, then returns its result, instead of doing its normal
5916processing. */)
5917 (Lisp_Object arg, Lisp_Object group)
5918{
5919 if (!NILP (group)
5920 && FUNCTIONP (Vmove_to_window_line_group_function))
5921 return call1 (Vmove_to_window_line_group_function, arg);
5922 {
5923 struct window *w = XWINDOW (selected_window);
5924 int lines, start;
5925 Lisp_Object window;
5849#if false 5926#if false
5850 int this_scroll_margin; 5927 int this_scroll_margin;
5851#endif 5928#endif
5852 5929
5853 if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer)) 5930 if (!(BUFFERP (w->contents) && XBUFFER (w->contents) == current_buffer))
5854 /* This test is needed to make sure PT/PT_BYTE make sense in w->contents 5931 /* This test is needed to make sure PT/PT_BYTE make sense in w->contents
5855 when passed below to set_marker_both. */ 5932 when passed below to set_marker_both. */
5856 error ("move-to-window-line called from unrelated buffer"); 5933 error ("move-to-window-line called from unrelated buffer");
5857 5934
5858 window = selected_window; 5935 window = selected_window;
5859 start = marker_position (w->start); 5936 start = marker_position (w->start);
5860 if (start < BEGV || start > ZV) 5937 if (start < BEGV || start > ZV)
5861 { 5938 {
5862 int height = window_internal_height (w); 5939 int height = window_internal_height (w);
5863 Fvertical_motion (make_number (- (height / 2)), window, Qnil); 5940 Fvertical_motion (make_number (- (height / 2)), window, Qnil);
5864 set_marker_both (w->start, w->contents, PT, PT_BYTE); 5941 set_marker_both (w->start, w->contents, PT, PT_BYTE);
5865 w->start_at_line_beg = !NILP (Fbolp ()); 5942 w->start_at_line_beg = !NILP (Fbolp ());
5866 w->force_start = true; 5943 w->force_start = true;
5867 } 5944 }
5868 else 5945 else
5869 Fgoto_char (w->start); 5946 Fgoto_char (w->start);
5870 5947
5871 lines = displayed_window_lines (w); 5948 lines = displayed_window_lines (w);
5872 5949
5873#if false 5950#if false
5874 this_scroll_margin = max (0, min (scroll_margin, lines / 4)); 5951 this_scroll_margin = max (0, min (scroll_margin, lines / 4));
5875#endif 5952#endif
5876 5953
5877 if (NILP (arg)) 5954 if (NILP (arg))
5878 XSETFASTINT (arg, lines / 2); 5955 XSETFASTINT (arg, lines / 2);
5879 else 5956 else
5880 { 5957 {
5881 EMACS_INT iarg = XINT (Fprefix_numeric_value (arg)); 5958 EMACS_INT iarg = XINT (Fprefix_numeric_value (arg));
5882 5959
5883 if (iarg < 0) 5960 if (iarg < 0)
5884 iarg = iarg + lines; 5961 iarg = iarg + lines;
5885 5962
5886#if false /* This code would prevent move-to-window-line from moving point 5963#if false /* This code would prevent move-to-window-line from moving point
5887 to a place inside the scroll margins (which would cause the 5964 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. */)
5889 it is probably better not to install it. However, it is here 5966 it is probably better not to install it. However, it is here
5890 inside #if false so as not to lose it. -- rms. */ 5967 inside #if false so as not to lose it. -- rms. */
5891 5968
5892 /* Don't let it get into the margin at either top or bottom. */ 5969 /* Don't let it get into the margin at either top or bottom. */
5893 iarg = max (iarg, this_scroll_margin); 5970 iarg = max (iarg, this_scroll_margin);
5894 iarg = min (iarg, lines - this_scroll_margin - 1); 5971 iarg = min (iarg, lines - this_scroll_margin - 1);
5895#endif 5972#endif
5896 5973
5897 arg = make_number (iarg); 5974 arg = make_number (iarg);
5898 } 5975 }
5899 5976
5900 /* Skip past a partially visible first line. */ 5977 /* Skip past a partially visible first line. */
5901 if (w->vscroll) 5978 if (w->vscroll)
5902 XSETINT (arg, XINT (arg) + 1); 5979 XSETINT (arg, XINT (arg) + 1);
5903 5980
5904 return Fvertical_motion (arg, window, Qnil); 5981 return Fvertical_motion (arg, window, Qnil);
5982 }
5905} 5983}
5906 5984
5907 5985
@@ -7175,6 +7253,12 @@ syms_of_window (void)
7175 DEFSYM (Qclone_of, "clone-of"); 7253 DEFSYM (Qclone_of, "clone-of");
7176 DEFSYM (Qfloor, "floor"); 7254 DEFSYM (Qfloor, "floor");
7177 DEFSYM (Qceiling, "ceiling"); 7255 DEFSYM (Qceiling, "ceiling");
7256 DEFSYM (Qwindow_start_group_function, "window-start-group-function");
7257 DEFSYM (Qwindow_end_group_function, "window-end-group-function");
7258 DEFSYM (Qset_window_start_group_function, "set-window-start-group-function");
7259 DEFSYM (Qrecenter_group_function, "recenter-group-function");
7260 DEFSYM (Qpos_visible_in_window_p_group_function, "pos-visible-in-window-p-group-function");
7261 DEFSYM (Qmove_to_window_line_group_function, "move-to-window-line-group-function");
7178 7262
7179 staticpro (&Vwindow_list); 7263 staticpro (&Vwindow_list);
7180 7264
@@ -7346,6 +7430,72 @@ Note that this optimization can cause the portion of the buffer
7346displayed after a scrolling operation to be somewhat inaccurate. */); 7430displayed after a scrolling operation to be somewhat inaccurate. */);
7347 Vfast_but_imprecise_scrolling = false; 7431 Vfast_but_imprecise_scrolling = false;
7348 7432
7433 DEFVAR_LISP ("window-start-group-function", Vwindow_start_group_function,
7434 doc: /* Function to call for `window-start' when its GROUP parameter is non-nil.
7435When this variable contains a function, and `window-start' is called with a
7436non-nil GROUP parameter, the function is called instead of `window-start''s
7437normal action. `window-start' passes the function its argument WINDOW, which
7438might be nil. The function may recursively call `window-start'. */);
7439 Vwindow_start_group_function = Qnil;
7440 Fmake_variable_buffer_local (Qwindow_start_group_function);
7441 Fput (Qwindow_start_group_function, Qpermanent_local, Qt);
7442
7443 DEFVAR_LISP ("window-end-group-function", Vwindow_end_group_function,
7444 doc: /* Function to call for `window-end' if its GROUP parameter is non-nil.
7445When this variable contains a function, and `window-end' is called with a
7446non-nil GROUP parameter, the function is called instead of `window-end''s
7447normal action. `window-end' passes the function its two parameters WINDOW,
7448and UPDATE. The function may call `window-end' recursively. */);
7449 Vwindow_end_group_function = Qnil;
7450 Fmake_variable_buffer_local (Qwindow_end_group_function);
7451 Fput (Qwindow_end_group_function, Qpermanent_local, Qt);
7452
7453 DEFVAR_LISP ("set-window-start-group-function",
7454 Vset_window_start_group_function,
7455 doc: /* The function to call for `set-window-start' when its GROUP parameter is non-nil.
7456When this variable contains a function, and `set-window-start' is called
7457with a non-nil GROUP parameter, the function is called instead of
7458`set-window-start''s normal action. `set-window-start' passes the function
7459its three parameters WINDOW, POS, and NOFORCE. The function may call
7460`set-window-start' recursively. */);
7461 Vset_window_start_group_function = Qnil;
7462 Fmake_variable_buffer_local (Qset_window_start_group_function);
7463 Fput (Qset_window_start_group_function, Qpermanent_local, Qt);
7464
7465 DEFVAR_LISP ("recenter-group-function", Vrecenter_group_function,
7466 doc: /* Function to call for `recenter' when its GROUP parameter is non-nil.
7467When this variable contains a function, and `recenter' is called with a
7468non-nil GROUP parameter, the function is called instead of `recenter''s
7469normal action. `recenter' passes the function its parameter ARG. The
7470function may call `recenter' recursively. */);
7471 Vrecenter_group_function = Qnil;
7472 Fmake_variable_buffer_local (Qrecenter_group_function);
7473 Fput (Qrecenter_group_function, Qpermanent_local, Qt);
7474
7475 DEFVAR_LISP ("pos-visible-in-window-p-group-function",
7476 Vpos_visible_in_window_p_group_function,
7477 doc: /* Function for `pos-visible-in-window-p' when its GROUP parameter is non-nil.
7478When this variable contains a function, and `pos-visible-in-window-p' is
7479called with a non-nil GROUP parameter, the function is called instead of
7480`pos-visible-in-window-p''s normal action. `pos-visible-in-window-p'
7481passes the function its three parameters POS, WINDOW, and PARTIALLY. The
7482function may call `pos-visible-in-window-p' recursively. */);
7483 Vpos_visible_in_window_p_group_function = Qnil;
7484 Fmake_variable_buffer_local (Qpos_visible_in_window_p_group_function);
7485 Fput (Qpos_visible_in_window_p_group_function, Qpermanent_local, Qt);
7486
7487 DEFVAR_LISP ("move-to-window-line-group-function",
7488 Vmove_to_window_line_group_function,
7489 doc: /* Function for `move-to-window-line' when its GROUP parameter is non-nil.
7490When this variable contains a function, and `move-to-window-line' is
7491called with a non-nil GROUP parameter, the function is called instead of
7492`move-to-window-line''s normal action. `move-to-window-line' passes the
7493function its parameter ARG. The function may call `move-to-window-line'
7494recursively. */);
7495 Vmove_to_window_line_group_function = Qnil;
7496 Fmake_variable_buffer_local (Qmove_to_window_line_group_function);
7497 Fput (Qmove_to_window_line_group_function, Qpermanent_local, Qt);
7498
7349 defsubr (&Sselected_window); 7499 defsubr (&Sselected_window);
7350 defsubr (&Sminibuffer_window); 7500 defsubr (&Sminibuffer_window);
7351 defsubr (&Swindow_minibuffer_p); 7501 defsubr (&Swindow_minibuffer_p);