Mercurial > emacs
changeset 100769:3e7e498772cb
(follow-calc-win-end): Use with-selected-window.
(follow-windows-start-end, follow-pos-visible)
(follow-windows-aligned-p): Code cleanup.
(follow-select-if-visible): Try to avoid partially-visible lines.
(follow-select-if-visible-from-first): Call follow-redisplay and
move point to destination.
(follow-redisplay): New arg, to keep selected window unchanged.
(follow-post-command-hook): In final check for destination
visibility, use window-start and window-end instead of the less
accurate follow-pos-visible. If the selected window is redrawn,
tell follow-redisplay to preserve it.
author | Chong Yidong <cyd@stupidchicken.com> |
---|---|
date | Tue, 30 Dec 2008 13:26:23 +0000 |
parents | 20014579730d |
children | 2cf5314ee101 |
files | lisp/follow.el |
diffstat | 1 files changed, 84 insertions(+), 78 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/follow.el Tue Dec 30 13:25:52 2008 +0000 +++ b/lisp/follow.el Tue Dec 30 13:26:23 2008 +0000 @@ -880,28 +880,23 @@ ;; This code works on both XEmacs and Emacs, but now ;; that XEmacs has got custom-written code, this could ;; be optimized for Emacs. - (let ((orig-win (and win (selected-window))) - height - buffer-end-p) - (if win (select-window win)) - (prog1 - (save-excursion - (goto-char (window-start)) - (setq height - (- (window-height) - (if header-line-format 2 1))) - (setq buffer-end-p - (if (bolp) - (not (= height (vertical-motion height))) - (save-restriction - ;; Fix a mis-feature in `vertical-motion': - ;; The start of the window is assumed to - ;; coinside with the start of a line. - (narrow-to-region (point) (point-max)) - (not (= height (vertical-motion height)))))) - (list (point) buffer-end-p)) - (if orig-win - (select-window orig-win)))))) + (let (height buffer-end-p) + (with-selected-window (or win (selected-window)) + (save-excursion + (goto-char (window-start)) + (setq height + (- (window-height) + (if header-line-format 2 1))) + (setq buffer-end-p + (if (bolp) + (not (= height (vertical-motion height))) + (save-restriction + ;; Fix a mis-feature in `vertical-motion': + ;; The start of the window is assumed to + ;; coinside with the start of a line. + (narrow-to-region (point) (point-max)) + (not (= height (vertical-motion height)))))) + (list (point) buffer-end-p)))))) ;; Can't use `save-window-excursion' since it triggers a redraw. @@ -955,30 +950,25 @@ "Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS." (if (follow-cache-valid-p windows) follow-windows-start-end-cache - (let ((win-start-end '()) - (orig-win (selected-window))) - (while windows - (select-window (car windows)) - (setq win-start-end - (cons (cons (car windows) - (cons (window-start) - (follow-calc-win-end))) - win-start-end)) - (setq windows (cdr windows))) + (let ((orig-win (selected-window)) + win-start-end) + (dolist (w windows) + (select-window w) + (push (cons w (cons (window-start) (follow-calc-win-end))) + win-start-end)) (select-window orig-win) - (setq follow-windows-start-end-cache (nreverse win-start-end)) - follow-windows-start-end-cache))) + (setq follow-windows-start-end-cache (nreverse win-start-end))))) (defsubst follow-pos-visible (pos win win-start-end) "Non-nil when POS is visible in WIN." (let ((wstart-wend-bend (cdr (assq win win-start-end)))) (and (>= pos (car wstart-wend-bend)) - (or (< pos (car (cdr wstart-wend-bend))) + (or (< pos (cadr wstart-wend-bend)) (nth 2 wstart-wend-bend))))) -;; By `aligned' we mean that for all adjecent windows, the end of the +;; By `aligned' we mean that for all adjacent windows, the end of the ;; first is equal with the start of the successor. The first window ;; should start at a full screen line. @@ -986,11 +976,10 @@ "Non-nil if the follower windows are aligned." (let ((res t)) (save-excursion - (goto-char (window-start (car (car win-start-end)))) - (if (bolp) - nil - (vertical-motion 0 (car (car win-start-end))) - (setq res (eq (point) (window-start (car (car win-start-end))))))) + (goto-char (window-start (caar win-start-end))) + (unless (bolp) + (vertical-motion 0 (caar win-start-end)) + (setq res (eq (point) (window-start (caar win-start-end)))))) (while (and res (cdr win-start-end)) ;; At least two followers left (setq res (eq (car (cdr (cdr (car win-start-end)))) @@ -1053,6 +1042,14 @@ (setq win (caar win-start-end)) (select-window win)) (setq win-start-end (cdr win-start-end))) + ;; The last line of the window may be partially visible; if so, + ;; and if point is visible in the next window, select the next + ;; window instead. + (and (/= dest (point-max)) + win-start-end + (follow-pos-visible dest (caar win-start-end) win-start-end) + (setq win (caar win-start-end)) + (select-window win)) win)) @@ -1090,31 +1087,34 @@ ;; as the point is not visible in any window. (defun follow-select-if-visible-from-first (dest windows) - "Select and return a window with DEST, if WINDOWS are redrawn from top." - (let ((win nil) - end-pos-end-p) + "Try to select one of WINDOWS without repositioning the topmost window. +If one of the windows in WINDOWS contains DEST, select it, call +`follow-redisplay', move point to DEST, and return that window. +Otherwise, return nil." + (let (win end-pos-end-p) (save-excursion (goto-char (window-start (car windows))) ;; Make sure the line start in the beginning of a real screen ;; line. (vertical-motion 0 (car windows)) - (if (< dest (point)) - ;; Above the start, not visible. - nil + (when (>= dest (point)) ;; At or below the start. Check the windows. (save-window-excursion - (while (and (not win) windows) - (set-window-start (car windows) (point) 'noforce) - (setq end-pos-end-p (follow-calc-win-end (car windows))) - (goto-char (car end-pos-end-p)) - ;; Visible, if dest above end, or if eob is visible inside - ;; the window. - (if (or (car (cdr end-pos-end-p)) - (< dest (point))) + (let ((windows windows)) + (while (and (not win) windows) + (set-window-start (car windows) (point) 'noforce) + (setq end-pos-end-p (follow-calc-win-end (car windows))) + (goto-char (car end-pos-end-p)) + ;; Visible, if dest above end, or if eob is visible inside + ;; the window. + (if (or (car (cdr end-pos-end-p)) + (< dest (point))) (setq win (car windows)) - (setq windows (cdr windows))))))) - (if win - (select-window win)) + (setq windows (cdr windows)))))))) + (when win + (select-window win) + (follow-redisplay windows (car windows)) + (goto-char dest)) win)) @@ -1126,14 +1126,16 @@ ;; is nil. Start every window directly after the end of the previous ;; window, to make sure long lines are displayed correctly. -(defun follow-redisplay (&optional windows win) +(defun follow-redisplay (&optional windows win preserve-win) "Reposition the WINDOWS around WIN. Should the point be too close to the roof we redisplay everything from the top. WINDOWS should contain a list of windows to redisplay, it is assumed that WIN is a member of the list. Should WINDOWS be nil, the windows displaying the same buffer as WIN, in the current frame, are used. -Should WIN be nil, the selected window is used." +Should WIN be nil, the selected window is used. +If PRESERVE-WIN is non-nil, keep WIN itself unchanged while +repositioning the other windows." (or win (setq win (selected-window))) (or windows (setq windows (follow-all-followers win))) ;; Calculate the start of the first window. @@ -1154,7 +1156,8 @@ (follow-calculate-first-window-start-from-below windows try-first-start win old-win-start))))) (dolist (w windows) - (set-window-start w start) + (unless (and preserve-win (eq w win)) + (set-window-start w start)) (setq start (car (follow-calc-win-end w)))))) @@ -1310,15 +1313,15 @@ (when (and follow-mode (not (window-minibuffer-p win))) ;; The buffer shown in the selected window is in follow - ;; mode. Find the current state of the display and cache - ;; the result for speed (i.e. `aligned' and `visible'.) + ;; mode. Find the current state of the display. (let* ((windows (follow-all-followers win)) (dest (point)) (win-start-end (progn (follow-update-window-start (car windows)) (follow-windows-start-end windows))) (aligned (follow-windows-aligned-p win-start-end)) - (visible (follow-pos-visible dest win win-start-end))) + (visible (follow-pos-visible dest win win-start-end)) + selected-window-up-to-date) (unless (and aligned visible) (follow-invalidate-cache)) (follow-avoid-tail-recenter) @@ -1392,9 +1395,7 @@ ;; at the bottom of a window. ((follow-select-if-visible-from-first dest windows) (follow-debug-message "Below first") - (setq visible t aligned t) - (follow-redisplay windows (car windows)) - (goto-char dest)) + (setq visible t aligned t)) ;; None of the above. For simplicity, we stick to the ;; selected window. (t @@ -1403,27 +1404,34 @@ ;; If a new window has been selected, make sure that the ;; old is not scrolled when the point is outside the ;; window. - (or (eq win (selected-window)) - (let ((p (window-point win))) - (set-window-start win (window-start win) nil) - (set-window-point win p)))) - ;; Make sure the point is visible in the selected window. - ;; (This could lead to a scroll.) + (unless (eq win (selected-window)) + (let ((p (window-point win))) + (set-window-start win (window-start win) nil) + (set-window-point win p)))) (unless (or visible - (follow-pos-visible dest win win-start-end)) + ;; Use the UPDATE argument of window-end + ;; instead of calling follow-pos-visible + ;; (which may be inaccurate for partially + ;; visible lines). + (and (>= dest (window-start)) + (< dest (window-end nil t)))) + ;; If point is not visible in the selected window, + ;; perform a redisplay; this causes scrolling. (sit-for 0) + (setq selected-window-up-to-date t) (follow-avoid-tail-recenter) (setq win-start-end (follow-windows-start-end windows)) (follow-invalidate-cache) (setq aligned nil)) - ;; Redraw the windows whenever needed. + ;; Now redraw the windows around the selected window. (unless (and (not follow-internal-force-redisplay) (or aligned (follow-windows-aligned-p win-start-end)) (follow-point-visible-all-windows-p win-start-end)) (setq follow-internal-force-redisplay nil) - (follow-redisplay windows (selected-window)) + (follow-redisplay windows (selected-window) + selected-window-up-to-date) (setq win-start-end (follow-windows-start-end windows)) (follow-invalidate-cache) ;; When the point ends up in another window. This @@ -1833,8 +1841,6 @@ ((follow-select-if-visible-from-first new-window-point windows) (follow-debug-message "filter: Seen from first") - (follow-redisplay windows (car windows)) - (goto-char new-window-point) (setq win-start-end (follow-windows-start-end windows))) ;; None of the above. We stick to the current window.