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.