;;; Commentary:
-;;{{{ Documentation
-
;; `Follow mode' is a minor mode for Emacs and XEmacs that
;; combines windows into one tall virtual window.
;;
;; non-selected window unaligned. It will, however, pop right back
;; when it is selected.)
-;;}}}
-
;;; Code:
-;;{{{ Preliminaries
+;; Preliminaries
;; Make the compiler shut up!
;; There are two strategies:
'byte-compile-obsolete)
(put 'frame-first-window 'byte-compile 'nil))))))
-;;}}}
-;;{{{ Variables
+;;; Variables
(defgroup follow nil
"Synchronize windows showing the same buffer."
:group 'follow)
(make-obsolete-variable 'follow-mode-off-hook 'follow-mode-hook "22.2")
-;;{{{ Keymap/Menu
+;;; Keymap/Menu
;; Define keys for the follow-mode minor mode map and replace some
;; functions in the global map. All `follow' mode special functions
"--"
["Follow mode" follow-mode :style toggle :selected follow-mode]))
-;;}}}
-
(defcustom follow-mode-line-text " Follow"
"Text shown in the mode line when Follow mode is active.
Defaults to \" Follow\". Examples of other values
(defvar follow-windows-start-end-cache nil
"Cache used by `follow-window-start-end'.")
-;;}}}
-;;{{{ Debug messages
+;;; Debug messages
;; This inline function must be as small as possible!
;; Maybe we should define a macro that expands to nil if
(if (and (boundp 'follow-debug) follow-debug)
(apply 'message args)))
-;;}}}
-;;{{{ Cache
+;;; Cache
(dolist (cmd follow-cache-command-list)
(put cmd 'follow-mode-use-cache t))
-;;}}}
-
-;;{{{ The mode
+;;; The mode
;;;###autoload
(defun turn-on-follow-mode ()
((not follow-mode) ; Off
(force-mode-line-update))))
-;;}}}
-;;{{{ Find file hook
+;;; Find file hook
;; This will start follow-mode whenever a new file is loaded, if
;; the variable `follow-auto' is non-nil.
"Find-file hook for Follow mode. See the variable `follow-auto'."
(if follow-auto (follow-mode t)))
-;;}}}
-
-;;{{{ User functions
-
-;;;
-;;; User functions usable when in Follow mode.
-;;;
+;;; User functions
-;;{{{ Scroll
+;;; Scroll
;; `scroll-up' and `-down', but for windows in Follow mode.
;;
(vertical-motion (- next-screen-context-lines 1))
(setq follow-internal-force-redisplay t))))))
-;;}}}
-;;{{{ Buffer
+;;; Buffer
;;;###autoload
(defun follow-delete-other-windows-and-split (&optional arg)
(follow-mode 1))
(follow-switch-to-buffer-all))
-;;}}}
-;;{{{ Movement
+;;; Movement
;; Note, these functions are not very useful, at least not unless you
;; rebind the rather cumbersome key sequence `C-c . p'.
(interactive)
(select-window (car (reverse (follow-all-followers)))))
-;;}}}
-;;{{{ Redraw
+;;; Redraw
(defun follow-recenter (&optional arg)
"Recenter the middle window around point.
(sit-for 0)
(follow-redisplay))
-;;}}}
-;;{{{ End of buffer
+;;; End of buffer
(defun follow-end-of-buffer (&optional arg)
"Move point to the end of the buffer, Follow mode style.
(with-no-warnings
(end-of-buffer arg))))
-;;}}}
-
-;;}}}
-
-;;{{{ Display
-
-;;;; The display routines
-
-;;{{{ Information gathering functions
+;;; Display
(defun follow-all-followers (&optional testwin)
"Return all windows displaying the same buffer as the TESTWIN.
(cons pred (cdr windows))))
-;; This function is optimized function for speed!
-
(defun follow-calc-win-end (&optional win)
- "Calculate the presumed window end for WIN.
-
-Actually, the position returned is the start of the next
-window, normally is the end plus one.
-
-If WIN is nil, the selected window is used.
-
-Returns (end-pos end-of-buffer-p)"
- (if (featurep 'xemacs)
- ;; XEmacs can calculate the end of the window by using
- ;; the 'guarantee options. GOOD!
- (let ((end (window-end win t)))
- (if (= end (point-max (window-buffer win)))
- (list end t)
- (list (+ end 1) nil)))
- ;; Emacs: We have to calculate the end by ourselves.
- ;; This code works on both XEmacs and Emacs, but now
- ;; that XEmacs has got custom-written code, this could
- ;; be optimized for Emacs.
- (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
- ;; coincide with the start of a line.
- (narrow-to-region (point) (point-max))
- (not (= height (vertical-motion height))))))
- (list (point) buffer-end-p))))))
-
+ "Calculate the end position for window WIN.
+Return (END-POS END-OF-BUFFER).
+
+Actually, the position returned is the start of the line after
+the last fully-visible line in WIN. If WIN is nil, the selected
+window is used."
+ (let* ((win (or win (selected-window)))
+ (edges (window-inside-pixel-edges win))
+ (ht (- (nth 3 edges) (nth 1 edges)))
+ (last-line-pos (posn-point (posn-at-x-y 0 (1- ht) win))))
+ (if (pos-visible-in-window-p last-line-pos win)
+ (let ((end (window-end win t)))
+ (list end (= end (point-max))))
+ (list last-line-pos nil))))
;; Can't use `save-window-excursion' since it triggers a redraw.
(defun follow-calc-win-start (windows pos win)
(vertical-motion 1 win)
(set-window-start win (point) 'noforce)))))
-;;}}}
-;;{{{ Selection functions
+;;; Selection functions
;; Make a window in WINDOWS selected if it currently
;; is displaying the position DEST.
(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.
+ ;; 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))
(goto-char dest))
win))
-
-;;}}}
-;;{{{ Redisplay
+;;; Redisplay
;; Redraw all the windows on the screen, starting with the top window.
;; The window used as as marker is WIN, or the selected window if WIN
(setq res (point))))))
res)))
-;;}}}
-;;{{{ Avoid tail recenter
+;;; Avoid tail recenter
;; This sets the window internal flag `force_start'. The effect is that
;; windows only displaying the tail aren't recentered.
;; window-start position is equal to (point-max) of the buffer it
;; displays.
;;
-;; This function is also added to `post-command-idle-hook', introduced
-;; in Emacs 19.30. This is needed since the vaccine injected by the
-;; call from `post-command-hook' only works until the next redisplay.
-;; It is possible that the functions in the `post-command-idle-hook'
-;; can cause a redisplay, and hence a new vaccine is needed.
-;;
;; Sometimes, calling this function could actually cause a redisplay,
;; especially if it is placed in the debug filter section. I must
;; investigate this further...
non-first windows in Follow mode."
(if follow-avoid-tail-recenter-p
(let* ((orig-buffer (current-buffer))
- (top (frame-first-window (selected-frame)))
- (win top)
- (who '()) ; list of (buffer . frame)
- start
- pair) ; (buffer . frame)
+ (top (frame-first-window (selected-frame)))
+ (win top)
+ who) ; list of (buffer . frame)
;; If the only window in the frame is a minibuffer
;; window, `next-window' will never find it again...
- (if (window-minibuffer-p top)
- nil
+ (unless (window-minibuffer-p top)
(while ;; look, no body!
- (progn
- (setq start (window-start win))
+ (let ((start (window-start win))
+ (pair (cons (window-buffer win) (window-frame win))))
(set-buffer (window-buffer win))
- (setq pair (cons (window-buffer win) (window-frame win)))
- (if (member pair who)
- (if (and (boundp 'follow-mode) follow-mode
- (eq (point-max) start))
- ;; Write the same window start back, but don't
- ;; set the NOFORCE flag.
- (set-window-start win start))
- (setq who (cons pair who)))
+ (cond ((null (member pair who))
+ (setq who (cons pair who)))
+ ((and follow-mode (eq (point-max) start))
+ ;; Write the same window start back, but don't
+ ;; set the NOFORCE flag.
+ (set-window-start win start)))
(setq win (next-window win 'not t))
(not (eq win top)))) ;; Loop while this is true.
(set-buffer orig-buffer)))))
-;;}}}
-
-;;}}}
-;;{{{ Post Command Hook
+;;; Post Command Hook
;; The magic little box. This function is called after every command.
;; recenter them.
(follow-avoid-tail-recenter)))))
-;;}}}
-;;{{{ The region
+;;; The region
;; Tries to make the highlighted area representing the region look
;; good when spanning several windows.
(set-window-point (car succ) (nth 1 (assq (car succ) win-start-end)))
(setq succ (cdr succ)))))
-;;}}}
-;;{{{ Scroll bar
+;;; Scroll bar
;;;; Scroll-bar support code.
(select-window orig-win)))))
(error nil)))))
-;;}}}
-;;{{{ Process output
+;;; Process output
;; The following sections installs a spy that listens to process
;; output and tries to reposition the windows whose buffers are in
;; Discussion: Should we also advice `process-filter' to make our
;; filter invisible to others?
-;;{{{ Advice for `set-process-filter'
+;;; Advice for `set-process-filter'
;; Do not call this with 'follow-generic-filter as the name of the
;; filter...
(setq alist (cdr alist)))
(setq follow-process-filter-alist new)))
-;;}}}
-;;{{{ Start/stop interception of processes.
+;;; Start/stop interception of processes.
;; Normally, all new processes are intercepted by our `set-process-filter'.
;; This is needed to intercept old processes that were started before we were
follow-process-filter-alist))))
(setq follow-intercept-processes nil))
-;;}}}
-;;{{{ The filter
+;;; The filter
;; The following section is a naive method to make buffers with
;; process output to work with Follow mode. Whenever the start of the
(not (input-pending-p)))
(sit-for 0)))
-;;}}}
-
-;;}}}
-;;{{{ Window size change
+;;; Window size change
;; In Emacs 19.29, the functions in `window-size-change-functions' are
;; called every time a window in a frame changes size. Most notably, it
(set-buffer orig-buffer)
(select-window orig-window)))))
-;;}}}
-
-;;{{{ XEmacs isearch
+;;; XEmacs isearch
;; In XEmacs, isearch often finds matches in other windows than the
;; currently selected. However, when exiting the old window
(current-window-configuration))
(set-buffer buf)))))
-;;}}}
-;;{{{ Tail window handling
+;;; Tail window handling
;; In Emacs (not XEmacs) windows showing nothing are sometimes
;; recentered. When in Follow mode, this is not desirable for
;; By patching `sit-for' we can make sure that to catch all explicit
;; updates initiated by lisp programs. Internal calls, on the other
;; hand, are not handled.
-;;
-;; Please note that the function `follow-avoid-tail-recenter' is also
-;; called from other places, e.g. `post-command-hook' and
-;; `post-command-idle-hook'.
;; If this function is called it is too late for this window, but
;; we might save other windows from being recentered.
This prevents `mouse-drag-region' from messing things up."
(follow-avoid-tail-recenter)))
-;;}}}
-;;{{{ profile support
+;;; Profile support
;; The following (non-evaluated) section can be used to
;; profile this package using `elp'.
follow-post-command-hook
))))
-;;}}}
-
-;;{{{ The end
+;;; The end
(defun follow-unload-function ()
"Unload Follow mode library."
;; continue standard processing
nil)
-;;
-;; We're done!
-;;
-
(provide 'follow)
-;;}}}
-
;; /------------------------------------------------------------------------\
;; | "I [..] am rarely happier then when spending an entire day programming |
;; | my computer to perform automatically a task that it would otherwise |