;;; follow.el --- synchronize windows showing the same buffer
-;; Copyright (C) 1995-1997, 1999, 2001-2015 Free Software Foundation,
+;; Copyright (C) 1995-1997, 1999, 2001-2016 Free Software Foundation,
;; Inc.
-;; Author: Anders Lindgren <andersl@andersl.com>
-;; Maintainer: emacs-devel@gnu.org (Anders' email bounces, Sep 2005)
+;; Author: Anders Lindgren
+;; Maintainer: emacs-devel@gnu.org
;; Created: 1995-05-25
;; Keywords: display, window, minor-mode, convenience
makes it possible to walk between windows using normal cursor
movement commands.
-Follow mode comes to its prime when used on a large screen and two
-side-by-side windows are used. The user can, with the help of Follow
-mode, use two full-height windows as though they would have been
-one. Imagine yourself editing a large function, or section of text,
-and being able to use 144 lines instead of the normal 72... (your
+Follow mode comes to its prime when used on a large screen and two or
+more side-by-side windows are used. The user can, with the help of
+Follow mode, use these full-height windows as though they were one.
+Imagine yourself editing a large function, or section of text, and
+being able to use 144 or 216 lines instead of the normal 72... (your
mileage may vary).
To split one large window into two side-by-side windows, the commands
(add-hook 'post-command-hook 'follow-post-command-hook t)
(add-hook 'window-size-change-functions 'follow-window-size-change t)
(add-hook 'after-change-functions 'follow-after-change nil t)
-
- (setq window-start-group-function 'follow-window-start)
- (setq window-end-group-function 'follow-window-end)
- (setq set-window-start-group-function 'follow-set-window-start)
- (setq recenter-group-function 'follow-recenter)
- (setq pos-visible-in-window-p-group-function
+ (add-hook 'isearch-update-post-hook 'follow-post-command-hook nil t)
+ (add-hook 'replace-update-post-hook 'follow-post-command-hook nil t)
+ (add-hook 'ispell-update-post-hook 'follow-post-command-hook nil t)
+
+ (setq window-group-start-function 'follow-window-start)
+ (setq window-group-end-function 'follow-window-end)
+ (setq set-window-group-start-function 'follow-set-window-start)
+ (setq recenter-window-group-function 'follow-recenter)
+ (setq pos-visible-in-window-group-p-function
'follow-pos-visible-in-window-p)
(setq selected-window-group-function 'follow-all-followers)
- (setq move-to-window-line-group-function 'follow-move-to-window-line)
- (setq sit*-for-function 'follow-sit-for))
+ (setq move-to-window-group-line-function 'follow-move-to-window-line))
;; Remove globally-installed hook functions only if there is no
;; other Follow mode buffer.
(remove-hook 'post-command-hook 'follow-post-command-hook)
(remove-hook 'window-size-change-functions 'follow-window-size-change)))
- (kill-local-variable 'sit*-for-function)
- (kill-local-variable 'move-to-window-line-group-function)
+ (kill-local-variable 'move-to-window-group-line-function)
(kill-local-variable 'selected-window-group-function)
- (kill-local-variable 'pos-visible-in-window-p-group-function)
- (kill-local-variable 'recenter-group-function)
- (kill-local-variable 'set-window-start-group-function)
- (kill-local-variable 'window-end-group-function)
- (kill-local-variable 'window-start-group-function)
-
+ (kill-local-variable 'pos-visible-in-window-group-p-function)
+ (kill-local-variable 'recenter-window-group-function)
+ (kill-local-variable 'set-window-group-start-function)
+ (kill-local-variable 'window-group-end-function)
+ (kill-local-variable 'window-group-start-function)
+
+ (remove-hook 'ispell-update-post-hook 'follow-post-command-hook t)
+ (remove-hook 'replace-update-post-hook 'follow-post-command-hook t)
+ (remove-hook 'isearch-update-post-hook 'follow-post-command-hook t)
(remove-hook 'after-change-functions 'follow-after-change t)
(remove-hook 'compilation-filter-hook 'follow-align-compilation-windows t)))
;; position... (This would also be corrected if we would have had a
;; good redisplay abstraction.)
+(defun follow-scroll-up-arg (arg)
+ "Scroll the text in a follow mode window chain up by ARG lines.
+If ARG is nil, scroll the size of the current window.
+
+This is an internal function for `follow-scroll-up' and
+`follow-scroll-up-window'."
+ (let ((opoint (point)) (owin (selected-window)))
+ (while
+ ;; If we are too near EOB, try scrolling the previous window.
+ (condition-case nil (progn (scroll-up arg) nil)
+ (end-of-buffer
+ (condition-case nil (progn (follow-previous-window) t)
+ (error
+ (select-window owin)
+ (goto-char opoint)
+ (signal 'end-of-buffer nil))))))
+ (unless (and scroll-preserve-screen-position
+ (get this-command 'scroll-command))
+ (goto-char opoint))
+ (setq follow-fixed-window t)))
+
+(defun follow-scroll-down-arg (arg)
+ "Scroll the text in a follow mode window chain down by ARG lines.
+If ARG is nil, scroll the size of the current window.
+
+This is an internal function for `follow-scroll-down' and
+`follow-scroll-down-window'."
+ (let ((opoint (point)))
+ (scroll-down arg)
+ (unless (and scroll-preserve-screen-position
+ (get this-command 'scroll-command))
+ (goto-char opoint))
+ (setq follow-fixed-window t)))
+
+;;;###autoload
+(defun follow-scroll-up-window (&optional arg)
+ "Scroll text in a Follow mode window up by that window's size.
+The other windows in the window chain will scroll synchronously.
+
+If called with no ARG, the `next-screen-context-lines' last lines of
+the window will be visible after the scroll.
+
+If called with an argument, scroll ARG lines up.
+Negative ARG means scroll downward.
+
+Works like `scroll-up' when not in Follow mode."
+ (interactive "P")
+ (cond ((not follow-mode)
+ (scroll-up arg))
+ ((eq arg '-)
+ (follow-scroll-down-window))
+ (t (follow-scroll-up-arg arg))))
+(put 'follow-scroll-up-window 'scroll-command t)
+
+;;;###autoload
+(defun follow-scroll-down-window (&optional arg)
+ "Scroll text in a Follow mode window down by that window's size.
+The other windows in the window chain will scroll synchronously.
+
+If called with no ARG, the `next-screen-context-lines' top lines of
+the window in the chain will be visible after the scroll.
+
+If called with an argument, scroll ARG lines down.
+Negative ARG means scroll upward.
+
+Works like `scroll-down' when not in Follow mode."
+ (interactive "P")
+ (cond ((not follow-mode)
+ (scroll-down arg))
+ ((eq arg '-)
+ (follow-scroll-up-window))
+ (t (follow-scroll-down-arg arg))))
+(put 'follow-scroll-down-window 'scroll-command t)
+
;;;###autoload
(defun follow-scroll-up (&optional arg)
"Scroll text in a Follow mode window chain up.
(interactive "P")
(cond ((not follow-mode)
(scroll-up arg))
- ((eq arg '-)
- (follow-scroll-down))
- (t
- (let ((opoint (point)) (owin (selected-window)))
- (while
- ;; If we are too near EOB, try scrolling the previous window.
- (condition-case nil (progn (scroll-up arg) nil)
- (end-of-buffer
- (condition-case nil (progn (follow-previous-window) t)
- (error
- (select-window owin)
- (goto-char opoint)
- (signal 'end-of-buffer nil))))))
- (unless (and scroll-preserve-screen-position
- (get this-command 'scroll-command))
- (goto-char opoint))
- (setq follow-fixed-window t)))))
+ (arg (follow-scroll-up-arg arg))
+ (t
+ (let* ((windows (follow-all-followers))
+ (end (window-end (car (reverse windows)))))
+ (if (eq end (point-max))
+ (signal 'end-of-buffer nil)
+ (select-window (car windows))
+ ;; `window-end' might return nil.
+ (if end
+ (goto-char end))
+ (vertical-motion (- next-screen-context-lines))
+ (set-window-start (car windows) (point)))))))
(put 'follow-scroll-up 'scroll-command t)
;;;###autoload
(interactive "P")
(cond ((not follow-mode)
(scroll-down arg))
- ((eq arg '-)
- (follow-scroll-up))
- (t
- (let ((opoint (point)))
- (scroll-down arg)
- (unless (and scroll-preserve-screen-position
- (get this-command 'scroll-command))
- (goto-char opoint))
- (setq follow-fixed-window t)))))
+ (arg (follow-scroll-down-arg arg))
+ (t
+ (let* ((windows (follow-all-followers))
+ (win (car (reverse windows)))
+ (start (window-start (car windows))))
+ (if (eq start (point-min))
+ (signal 'beginning-of-buffer nil)
+ (select-window win)
+ (goto-char start)
+ (vertical-motion (- (- (window-height win)
+ (if header-line-format 2 1)
+ next-screen-context-lines)))
+ (set-window-start win (point))
+ (goto-char start)
+ (vertical-motion (- next-screen-context-lines 1))
+ (setq follow-internal-force-redisplay t))))))
(put 'follow-scroll-down 'scroll-command t)
(declare-function comint-adjust-point "comint" (window))
(let ((orig-win (selected-window))
win-start-end)
(dolist (w windows)
- (select-window w)
+ (select-window w 'norecord)
(push (cons w (cons (window-start) (follow-calc-win-end)))
win-start-end))
- (select-window orig-win)
+ (select-window orig-win 'norecord)
(setq follow-windows-start-end-cache (nreverse win-start-end)))))
(defsubst follow-pos-visible (pos win win-start-end)
"Redraw all windows in FRAME, when in Follow mode."
;; Below, we call `post-command-hook'. Avoid an infloop.
(unless follow-inside-post-command-hook
- (let ((buffers '())
- (orig-window (selected-window))
- (orig-buffer (current-buffer))
- (orig-frame (selected-frame))
- windows
- buf)
- (select-frame frame)
- (unwind-protect
- (walk-windows
- (lambda (win)
- (setq buf (window-buffer win))
- (unless (memq buf buffers)
- (set-buffer buf)
- (when follow-mode
- (setq windows (follow-all-followers win))
- (if (not (memq orig-window windows))
- (follow-redisplay windows win)
- ;; Make sure we're redrawing around the selected
- ;; window.
- (select-window orig-window)
- (follow-post-command-hook)
- (setq orig-window (selected-window)))
- (setq buffers (cons buf buffers)))))
- 'no-minibuf)
- (select-frame orig-frame)
- (set-buffer orig-buffer)
- (select-window orig-window)))))
+ (save-current-buffer
+ (let ((orig-frame (selected-frame)))
+ (select-frame frame)
+ (let ((picked-window (selected-window)) ; Note: May change below.
+ (seen-buffers '()))
+ (unwind-protect
+ (walk-windows
+ (lambda (win)
+ (let ((buf (window-buffer win)))
+ (unless (memq buf seen-buffers)
+ (set-buffer buf)
+ (when follow-mode
+ (let ((windows (follow-all-followers win)))
+ (if (not (memq picked-window windows))
+ (follow-redisplay windows win)
+ ;; Make sure we're redrawing around the selected
+ ;; window.
+ (select-window picked-window 'norecord)
+ (follow-post-command-hook)
+ (setq picked-window (selected-window))))
+ (push buf seen-buffers)))))
+ 'no-minibuf)
+ (select-window picked-window 'norecord)))
+ (select-frame orig-frame)))))
(add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t)
;; These routines are the Follow Mode versions of the low level
;; functions described on page "Window Start and End" of the elisp
-;; manual, e.g. `window*-start'. The aim is to be able to handle
-;; Follow Mode windows by replacing `window-start' by `window*-start',
-;; etc.
+;; manual, e.g. `window-group-start'. The aim is to be able to handle
+;; Follow Mode windows by replacing `window-start' by
+;; `window-group-start', etc.
(defun follow-after-change (_beg _end _old-len)
"After change function: set `follow-start-end-invalid'."
lines (+ lines count)))))))
(+ lines (move-to-window-line arg))))
-(defun follow-sit-for (seconds &optional nodisp)
- "Redisplay, then wait for SECONDS seconds. Stop when input is available.
-Before redisplaying, synchronise all Follow windows.
-
-SECONDS may be a floating-point value.
-\(On operating systems that do not support waiting for fractions of a
-second, floating-point values are rounded down to the nearest integer.)
-
-Redisplay does not happen if input is available before it starts.
-If optional arg NODISP is t, don't synchronise or redisplay, just
-wait for input.
-
-Value is t if waited the full time with no input arriving, and nil
-otherwise.
-
-The functionality is intended to be the same as `sit-for''s."
- (when (and (not (input-pending-p t))
- (not nodisp))
- (follow-adjust-window (selected-window)))
- (sit-for seconds nodisp))
-
;;; Profile support
;; The following (non-evaluated) section can be used to