comparison lisp/follow.el @ 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 503c8d0db2bc
children 3d5ce6a8b477
comparison
equal deleted inserted replaced
100768:20014579730d 100769:3e7e498772cb
878 (list (+ end 1) nil))) 878 (list (+ end 1) nil)))
879 ;; Emacs: We have to calculate the end by ourselves. 879 ;; Emacs: We have to calculate the end by ourselves.
880 ;; This code works on both XEmacs and Emacs, but now 880 ;; This code works on both XEmacs and Emacs, but now
881 ;; that XEmacs has got custom-written code, this could 881 ;; that XEmacs has got custom-written code, this could
882 ;; be optimized for Emacs. 882 ;; be optimized for Emacs.
883 (let ((orig-win (and win (selected-window))) 883 (let (height buffer-end-p)
884 height 884 (with-selected-window (or win (selected-window))
885 buffer-end-p) 885 (save-excursion
886 (if win (select-window win)) 886 (goto-char (window-start))
887 (prog1 887 (setq height
888 (save-excursion 888 (- (window-height)
889 (goto-char (window-start)) 889 (if header-line-format 2 1)))
890 (setq height 890 (setq buffer-end-p
891 (- (window-height) 891 (if (bolp)
892 (if header-line-format 2 1))) 892 (not (= height (vertical-motion height)))
893 (setq buffer-end-p 893 (save-restriction
894 (if (bolp) 894 ;; Fix a mis-feature in `vertical-motion':
895 (not (= height (vertical-motion height))) 895 ;; The start of the window is assumed to
896 (save-restriction 896 ;; coinside with the start of a line.
897 ;; Fix a mis-feature in `vertical-motion': 897 (narrow-to-region (point) (point-max))
898 ;; The start of the window is assumed to 898 (not (= height (vertical-motion height))))))
899 ;; coinside with the start of a line. 899 (list (point) buffer-end-p))))))
900 (narrow-to-region (point) (point-max))
901 (not (= height (vertical-motion height))))))
902 (list (point) buffer-end-p))
903 (if orig-win
904 (select-window orig-win))))))
905 900
906 901
907 ;; Can't use `save-window-excursion' since it triggers a redraw. 902 ;; Can't use `save-window-excursion' since it triggers a redraw.
908 (defun follow-calc-win-start (windows pos win) 903 (defun follow-calc-win-start (windows pos win)
909 "Calculate where WIN will start if the first in WINDOWS start at POS. 904 "Calculate where WIN will start if the first in WINDOWS start at POS.
953 948
954 (defun follow-windows-start-end (windows) 949 (defun follow-windows-start-end (windows)
955 "Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS." 950 "Builds a list of (WIN START END BUFFER-END-P) for every window in WINDOWS."
956 (if (follow-cache-valid-p windows) 951 (if (follow-cache-valid-p windows)
957 follow-windows-start-end-cache 952 follow-windows-start-end-cache
958 (let ((win-start-end '()) 953 (let ((orig-win (selected-window))
959 (orig-win (selected-window))) 954 win-start-end)
960 (while windows 955 (dolist (w windows)
961 (select-window (car windows)) 956 (select-window w)
962 (setq win-start-end 957 (push (cons w (cons (window-start) (follow-calc-win-end)))
963 (cons (cons (car windows) 958 win-start-end))
964 (cons (window-start)
965 (follow-calc-win-end)))
966 win-start-end))
967 (setq windows (cdr windows)))
968 (select-window orig-win) 959 (select-window orig-win)
969 (setq follow-windows-start-end-cache (nreverse win-start-end)) 960 (setq follow-windows-start-end-cache (nreverse win-start-end)))))
970 follow-windows-start-end-cache)))
971 961
972 962
973 (defsubst follow-pos-visible (pos win win-start-end) 963 (defsubst follow-pos-visible (pos win win-start-end)
974 "Non-nil when POS is visible in WIN." 964 "Non-nil when POS is visible in WIN."
975 (let ((wstart-wend-bend (cdr (assq win win-start-end)))) 965 (let ((wstart-wend-bend (cdr (assq win win-start-end))))
976 (and (>= pos (car wstart-wend-bend)) 966 (and (>= pos (car wstart-wend-bend))
977 (or (< pos (car (cdr wstart-wend-bend))) 967 (or (< pos (cadr wstart-wend-bend))
978 (nth 2 wstart-wend-bend))))) 968 (nth 2 wstart-wend-bend)))))
979 969
980 970
981 ;; By `aligned' we mean that for all adjecent windows, the end of the 971 ;; By `aligned' we mean that for all adjacent windows, the end of the
982 ;; first is equal with the start of the successor. The first window 972 ;; first is equal with the start of the successor. The first window
983 ;; should start at a full screen line. 973 ;; should start at a full screen line.
984 974
985 (defsubst follow-windows-aligned-p (win-start-end) 975 (defsubst follow-windows-aligned-p (win-start-end)
986 "Non-nil if the follower windows are aligned." 976 "Non-nil if the follower windows are aligned."
987 (let ((res t)) 977 (let ((res t))
988 (save-excursion 978 (save-excursion
989 (goto-char (window-start (car (car win-start-end)))) 979 (goto-char (window-start (caar win-start-end)))
990 (if (bolp) 980 (unless (bolp)
991 nil 981 (vertical-motion 0 (caar win-start-end))
992 (vertical-motion 0 (car (car win-start-end))) 982 (setq res (eq (point) (window-start (caar win-start-end))))))
993 (setq res (eq (point) (window-start (car (car win-start-end)))))))
994 (while (and res (cdr win-start-end)) 983 (while (and res (cdr win-start-end))
995 ;; At least two followers left 984 ;; At least two followers left
996 (setq res (eq (car (cdr (cdr (car win-start-end)))) 985 (setq res (eq (car (cdr (cdr (car win-start-end))))
997 (car (cdr (car (cdr win-start-end)))))) 986 (car (cdr (car (cdr win-start-end))))))
998 (setq win-start-end (cdr win-start-end))) 987 (setq win-start-end (cdr win-start-end)))
1051 ;; command. 1040 ;; command.
1052 (when (follow-pos-visible dest (caar win-start-end) win-start-end) 1041 (when (follow-pos-visible dest (caar win-start-end) win-start-end)
1053 (setq win (caar win-start-end)) 1042 (setq win (caar win-start-end))
1054 (select-window win)) 1043 (select-window win))
1055 (setq win-start-end (cdr win-start-end))) 1044 (setq win-start-end (cdr win-start-end)))
1045 ;; The last line of the window may be partially visible; if so,
1046 ;; and if point is visible in the next window, select the next
1047 ;; window instead.
1048 (and (/= dest (point-max))
1049 win-start-end
1050 (follow-pos-visible dest (caar win-start-end) win-start-end)
1051 (setq win (caar win-start-end))
1052 (select-window win))
1056 win)) 1053 win))
1057 1054
1058 1055
1059 ;; Lets select a window showing the end. Make sure we only select it if it 1056 ;; Lets select a window showing the end. Make sure we only select it if it
1060 ;; it wasn't just moved here. (i.e. M-> shall not unconditionally place 1057 ;; it wasn't just moved here. (i.e. M-> shall not unconditionally place
1088 ;; be redisplayed with the first window fixed. This is useful for 1085 ;; be redisplayed with the first window fixed. This is useful for
1089 ;; example when the user has pressed return at the bottom of a window 1086 ;; example when the user has pressed return at the bottom of a window
1090 ;; as the point is not visible in any window. 1087 ;; as the point is not visible in any window.
1091 1088
1092 (defun follow-select-if-visible-from-first (dest windows) 1089 (defun follow-select-if-visible-from-first (dest windows)
1093 "Select and return a window with DEST, if WINDOWS are redrawn from top." 1090 "Try to select one of WINDOWS without repositioning the topmost window.
1094 (let ((win nil) 1091 If one of the windows in WINDOWS contains DEST, select it, call
1095 end-pos-end-p) 1092 `follow-redisplay', move point to DEST, and return that window.
1093 Otherwise, return nil."
1094 (let (win end-pos-end-p)
1096 (save-excursion 1095 (save-excursion
1097 (goto-char (window-start (car windows))) 1096 (goto-char (window-start (car windows)))
1098 ;; Make sure the line start in the beginning of a real screen 1097 ;; Make sure the line start in the beginning of a real screen
1099 ;; line. 1098 ;; line.
1100 (vertical-motion 0 (car windows)) 1099 (vertical-motion 0 (car windows))
1101 (if (< dest (point)) 1100 (when (>= dest (point))
1102 ;; Above the start, not visible.
1103 nil
1104 ;; At or below the start. Check the windows. 1101 ;; At or below the start. Check the windows.
1105 (save-window-excursion 1102 (save-window-excursion
1106 (while (and (not win) windows) 1103 (let ((windows windows))
1107 (set-window-start (car windows) (point) 'noforce) 1104 (while (and (not win) windows)
1108 (setq end-pos-end-p (follow-calc-win-end (car windows))) 1105 (set-window-start (car windows) (point) 'noforce)
1109 (goto-char (car end-pos-end-p)) 1106 (setq end-pos-end-p (follow-calc-win-end (car windows)))
1110 ;; Visible, if dest above end, or if eob is visible inside 1107 (goto-char (car end-pos-end-p))
1111 ;; the window. 1108 ;; Visible, if dest above end, or if eob is visible inside
1112 (if (or (car (cdr end-pos-end-p)) 1109 ;; the window.
1113 (< dest (point))) 1110 (if (or (car (cdr end-pos-end-p))
1111 (< dest (point)))
1114 (setq win (car windows)) 1112 (setq win (car windows))
1115 (setq windows (cdr windows))))))) 1113 (setq windows (cdr windows))))))))
1116 (if win 1114 (when win
1117 (select-window win)) 1115 (select-window win)
1116 (follow-redisplay windows (car windows))
1117 (goto-char dest))
1118 win)) 1118 win))
1119 1119
1120 1120
1121 ;;}}} 1121 ;;}}}
1122 ;;{{{ Redisplay 1122 ;;{{{ Redisplay
1124 ;; Redraw all the windows on the screen, starting with the top window. 1124 ;; Redraw all the windows on the screen, starting with the top window.
1125 ;; The window used as as marker is WIN, or the selcted window if WIN 1125 ;; The window used as as marker is WIN, or the selcted window if WIN
1126 ;; is nil. Start every window directly after the end of the previous 1126 ;; is nil. Start every window directly after the end of the previous
1127 ;; window, to make sure long lines are displayed correctly. 1127 ;; window, to make sure long lines are displayed correctly.
1128 1128
1129 (defun follow-redisplay (&optional windows win) 1129 (defun follow-redisplay (&optional windows win preserve-win)
1130 "Reposition the WINDOWS around WIN. 1130 "Reposition the WINDOWS around WIN.
1131 Should the point be too close to the roof we redisplay everything 1131 Should the point be too close to the roof we redisplay everything
1132 from the top. WINDOWS should contain a list of windows to 1132 from the top. WINDOWS should contain a list of windows to
1133 redisplay, it is assumed that WIN is a member of the list. 1133 redisplay, it is assumed that WIN is a member of the list.
1134 Should WINDOWS be nil, the windows displaying the 1134 Should WINDOWS be nil, the windows displaying the
1135 same buffer as WIN, in the current frame, are used. 1135 same buffer as WIN, in the current frame, are used.
1136 Should WIN be nil, the selected window is used." 1136 Should WIN be nil, the selected window is used.
1137 If PRESERVE-WIN is non-nil, keep WIN itself unchanged while
1138 repositioning the other windows."
1137 (or win (setq win (selected-window))) 1139 (or win (setq win (selected-window)))
1138 (or windows (setq windows (follow-all-followers win))) 1140 (or windows (setq windows (follow-all-followers win)))
1139 ;; Calculate the start of the first window. 1141 ;; Calculate the start of the first window.
1140 (let* ((old-win-start (window-start win)) 1142 (let* ((old-win-start (window-start win))
1141 (try-first-start (follow-estimate-first-window-start 1143 (try-first-start (follow-estimate-first-window-start
1152 (t 1154 (t
1153 (follow-debug-message "below") 1155 (follow-debug-message "below")
1154 (follow-calculate-first-window-start-from-below 1156 (follow-calculate-first-window-start-from-below
1155 windows try-first-start win old-win-start))))) 1157 windows try-first-start win old-win-start)))))
1156 (dolist (w windows) 1158 (dolist (w windows)
1157 (set-window-start w start) 1159 (unless (and preserve-win (eq w win))
1160 (set-window-start w start))
1158 (setq start (car (follow-calc-win-end w)))))) 1161 (setq start (car (follow-calc-win-end w))))))
1159 1162
1160 1163
1161 (defun follow-estimate-first-window-start (windows win start) 1164 (defun follow-estimate-first-window-start (windows win start)
1162 "Estimate the position of the first window. 1165 "Estimate the position of the first window.
1308 (get this-command 'follow-mode-use-cache)) 1311 (get this-command 'follow-mode-use-cache))
1309 (follow-invalidate-cache)) 1312 (follow-invalidate-cache))
1310 (when (and follow-mode 1313 (when (and follow-mode
1311 (not (window-minibuffer-p win))) 1314 (not (window-minibuffer-p win)))
1312 ;; The buffer shown in the selected window is in follow 1315 ;; The buffer shown in the selected window is in follow
1313 ;; mode. Find the current state of the display and cache 1316 ;; mode. Find the current state of the display.
1314 ;; the result for speed (i.e. `aligned' and `visible'.)
1315 (let* ((windows (follow-all-followers win)) 1317 (let* ((windows (follow-all-followers win))
1316 (dest (point)) 1318 (dest (point))
1317 (win-start-end (progn 1319 (win-start-end (progn
1318 (follow-update-window-start (car windows)) 1320 (follow-update-window-start (car windows))
1319 (follow-windows-start-end windows))) 1321 (follow-windows-start-end windows)))
1320 (aligned (follow-windows-aligned-p win-start-end)) 1322 (aligned (follow-windows-aligned-p win-start-end))
1321 (visible (follow-pos-visible dest win win-start-end))) 1323 (visible (follow-pos-visible dest win win-start-end))
1324 selected-window-up-to-date)
1322 (unless (and aligned visible) 1325 (unless (and aligned visible)
1323 (follow-invalidate-cache)) 1326 (follow-invalidate-cache))
1324 (follow-avoid-tail-recenter) 1327 (follow-avoid-tail-recenter)
1325 ;; Select a window to display point. 1328 ;; Select a window to display point.
1326 (unless follow-internal-force-redisplay 1329 (unless follow-internal-force-redisplay
1390 ;; If we can position the cursor without moving the first 1393 ;; If we can position the cursor without moving the first
1391 ;; window, do it. This is the case that catches `RET' 1394 ;; window, do it. This is the case that catches `RET'
1392 ;; at the bottom of a window. 1395 ;; at the bottom of a window.
1393 ((follow-select-if-visible-from-first dest windows) 1396 ((follow-select-if-visible-from-first dest windows)
1394 (follow-debug-message "Below first") 1397 (follow-debug-message "Below first")
1395 (setq visible t aligned t) 1398 (setq visible t aligned t))
1396 (follow-redisplay windows (car windows))
1397 (goto-char dest))
1398 ;; None of the above. For simplicity, we stick to the 1399 ;; None of the above. For simplicity, we stick to the
1399 ;; selected window. 1400 ;; selected window.
1400 (t 1401 (t
1401 (follow-debug-message "None") 1402 (follow-debug-message "None")
1402 (setq visible nil aligned nil)))) 1403 (setq visible nil aligned nil))))
1403 ;; If a new window has been selected, make sure that the 1404 ;; If a new window has been selected, make sure that the
1404 ;; old is not scrolled when the point is outside the 1405 ;; old is not scrolled when the point is outside the
1405 ;; window. 1406 ;; window.
1406 (or (eq win (selected-window)) 1407 (unless (eq win (selected-window))
1407 (let ((p (window-point win))) 1408 (let ((p (window-point win)))
1408 (set-window-start win (window-start win) nil) 1409 (set-window-start win (window-start win) nil)
1409 (set-window-point win p)))) 1410 (set-window-point win p))))
1410 ;; Make sure the point is visible in the selected window.
1411 ;; (This could lead to a scroll.)
1412 (unless (or visible 1411 (unless (or visible
1413 (follow-pos-visible dest win win-start-end)) 1412 ;; Use the UPDATE argument of window-end
1413 ;; instead of calling follow-pos-visible
1414 ;; (which may be inaccurate for partially
1415 ;; visible lines).
1416 (and (>= dest (window-start))
1417 (< dest (window-end nil t))))
1418 ;; If point is not visible in the selected window,
1419 ;; perform a redisplay; this causes scrolling.
1414 (sit-for 0) 1420 (sit-for 0)
1421 (setq selected-window-up-to-date t)
1415 (follow-avoid-tail-recenter) 1422 (follow-avoid-tail-recenter)
1416 (setq win-start-end (follow-windows-start-end windows)) 1423 (setq win-start-end (follow-windows-start-end windows))
1417 (follow-invalidate-cache) 1424 (follow-invalidate-cache)
1418 (setq aligned nil)) 1425 (setq aligned nil))
1419 ;; Redraw the windows whenever needed. 1426 ;; Now redraw the windows around the selected window.
1420 (unless (and (not follow-internal-force-redisplay) 1427 (unless (and (not follow-internal-force-redisplay)
1421 (or aligned 1428 (or aligned
1422 (follow-windows-aligned-p win-start-end)) 1429 (follow-windows-aligned-p win-start-end))
1423 (follow-point-visible-all-windows-p 1430 (follow-point-visible-all-windows-p
1424 win-start-end)) 1431 win-start-end))
1425 (setq follow-internal-force-redisplay nil) 1432 (setq follow-internal-force-redisplay nil)
1426 (follow-redisplay windows (selected-window)) 1433 (follow-redisplay windows (selected-window)
1434 selected-window-up-to-date)
1427 (setq win-start-end (follow-windows-start-end windows)) 1435 (setq win-start-end (follow-windows-start-end windows))
1428 (follow-invalidate-cache) 1436 (follow-invalidate-cache)
1429 ;; When the point ends up in another window. This 1437 ;; When the point ends up in another window. This
1430 ;; happens when dest is in the beginning of the file and 1438 ;; happens when dest is in the beginning of the file and
1431 ;; the selected window is not the first. It can also, 1439 ;; the selected window is not the first. It can also,
1831 ;; point is visible at a window below, 1839 ;; point is visible at a window below,
1832 ;; redisplay and select it. 1840 ;; redisplay and select it.
1833 ((follow-select-if-visible-from-first 1841 ((follow-select-if-visible-from-first
1834 new-window-point windows) 1842 new-window-point windows)
1835 (follow-debug-message "filter: Seen from first") 1843 (follow-debug-message "filter: Seen from first")
1836 (follow-redisplay windows (car windows))
1837 (goto-char new-window-point)
1838 (setq win-start-end 1844 (setq win-start-end
1839 (follow-windows-start-end windows))) 1845 (follow-windows-start-end windows)))
1840 ;; None of the above. We stick to the current window. 1846 ;; None of the above. We stick to the current window.
1841 (t 1847 (t
1842 (follow-debug-message "filter: nothing"))) 1848 (follow-debug-message "filter: nothing")))