Mercurial > emacs
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"))) |