diff options
author | Alan Mackenzie <acm@muc.de> | 2015-11-11 12:02:48 +0000 |
---|---|---|
committer | Alan Mackenzie <acm@muc.de> | 2015-12-04 18:15:27 +0000 |
commit | 64c57303658f69b019c4599f8c960a5623855410 (patch) | |
tree | cf49a687ba1784eb52451ab02a5d99013c88caab /lisp/follow.el | |
parent | 30f3432e9519f61882faa303e7851e761d2d18ea (diff) | |
download | emacs-64c57303658f69b019c4599f8c960a5623855410.tar.gz |
First commit to scratch/follow. Make Isearch work with Follow Mode, etc.
doc/lispref/window.texi (Basic Windows): Add paragraph defining "Group of
Windows" and new @defun selected-window-group.
(Window Start and End): Describe new &optional parameter GROUP and
...-group-function for window-start, window-end, set-window-start, and
pos-visible-in-window-p.
(Textual Scrolling) Describe the same for recenter.
doc/lispref/positions.texi (Screen Lines): Describe the same for
move-to-window-line.
src/window.c (Fwindow_start, Fwindow_end, Fset_window_start)
(Fpos_visible_in_window_p, Frecenter, Fmove_to_window_line): To each, add ar
new optional parameter "group". At the beginning of each, check whether the
corresponding ...-group-function is set to a function, and if so execute this
function in place of the normal processing.
(syms_of_window): Define symbols for the six new variables below.
(window-start-group-function, window-end-group-function)
(set-window-start-group-function, recenter-group-function)
(pos-visible-in-window-p-group-function, move-to-window-line-group-function):
New permanent local buffer local variables.
src/keyboard.c (Fposn_at_point): Add extra parameter in call to
Fpos_visible_in_window_p.
lisp/window.el (selected-window-group-function): New permanent local buffer
local variable.
(selected-window-group): New function.
lisp/follow.el (follow-mode): Set the ...-group-function variables at mode
enable, kill them at mode disable. Add/remove follow-after-change to/from
after-change-functions.
(follow-start-end-invalid): New variable.
(follow-redisplay): Manipulate follow-start-end-invalid.
(follow-after-change, follow-window-start, follow-window-end)
(follow-set-window-start, follow-pos-visible-in-window-p)
(follow-move-to-window-line, follow-sit-for): New functions.
lisp/isearch.el (isearch-call-message): New macro.
(isearch-update, with-isearch-suspended, isearch-del-char)
(isearch-search-and-update, isearch-ring-adjust): Invoke above new macro.
(with-isearch-suspended): Rearrange code such that isearch-call-message is
invoked before point is moved.
(isearch-message): Add comment about where point must be at function call.
(isearch-search): Remove call to isearch-message.
(isearch-lazy-highlight-window-group): New variable.
(isearch-lazy-highlight-new-loop): Unconditionally start idle timer. Move
the battery of tests to ...
(isearch-lazy-highlight-maybe-new-loop): New function, started by idle timer.
Note: (sit-for 0) is still called.
(isearch-lazy-highlight-update): Check membership of
isearch-lazy-highlight-window-group. Don't set the `window' overlay
property.
(isearch-update, isearch-done, isearch-string-out-of-window)
(isearch-back-into-window, isearch-lazy-highlight-maybe-new-loop)
(isearch-lazy-highlight-search, isearch-lazy-highlight-update)
(isearch-lazy-highlight-update): Call the six amended primitives (see
src/window.c above) with the new `group' argument set to t, to cooperate
with Follow Mode.
Diffstat (limited to 'lisp/follow.el')
-rw-r--r-- | lisp/follow.el | 193 |
1 files changed, 191 insertions, 2 deletions
diff --git a/lisp/follow.el b/lisp/follow.el index 37de923e6a5..2cbf0f2b93d 100644 --- a/lisp/follow.el +++ b/lisp/follow.el @@ -421,7 +421,19 @@ Keys specific to Follow mode: (progn (add-hook 'compilation-filter-hook 'follow-align-compilation-windows t t) (add-hook 'post-command-hook 'follow-post-command-hook t) - (add-hook 'window-size-change-functions 'follow-window-size-change 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 + '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)) + ;; Remove globally-installed hook functions only if there is no ;; other Follow mode buffer. (let ((buffers (buffer-list)) @@ -432,6 +444,17 @@ Keys specific to Follow mode: (unless following (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 '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) + + (remove-hook 'after-change-functions 'follow-after-change t) (remove-hook 'compilation-filter-hook 'follow-align-compilation-windows t))) (defun follow-find-file-hook () @@ -1015,6 +1038,10 @@ Otherwise, return nil." ;; is nil. Start every window directly after the end of the previous ;; window, to make sure long lines are displayed correctly. +(defvar follow-start-end-invalid t + "When non-nil, indicates `follow-windows-start-end-cache' is invalid.") +(make-variable-buffer-local 'follow-start-end-invalid) + (defun follow-redisplay (&optional windows win preserve-win) "Reposition the WINDOWS around WIN. Should point be too close to the roof we redisplay everything @@ -1047,7 +1074,8 @@ repositioning the other windows." (dolist (w windows) (unless (and preserve-win (eq w win)) (set-window-start w start)) - (setq start (car (follow-calc-win-end w)))))) + (setq start (car (follow-calc-win-end w)))) + (setq follow-start-end-invalid nil))) (defun follow-estimate-first-window-start (windows win start) "Estimate the position of the first window. @@ -1443,6 +1471,167 @@ non-first windows in Follow mode." (add-hook 'window-scroll-functions 'follow-avoid-tail-recenter t) +;;; Low level window start and end. + +;; 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. + +(defun follow-after-change (_beg _end _old-len) + "After change function: set `follow-start-end-invalid'." + (setq follow-start-end-invalid t)) + +(defun follow-window-start (&optional window) + "Return position at which display currently starts in the +Follow Mode group of windows which includes WINDOW. + +WINDOW must be a live window and defaults to the selected one. +This is updated by redisplay or by calling +`follow-set-window-start'." + (let ((windows (follow-all-followers window))) + (window-start (car windows)))) + +(defun follow-window-end (&optional window update) + "Return position at which display currently ends in the Follow + Mode group of windows which includes WINDOW. + + WINDOW must be a live window and defaults to the selected one. + This is updated by redisplay, when it runs to completion. + Simply changing the buffer text or setting `window-start' does + not update this value. + + Return nil if there is no recorded value. (This can happen if + the last redisplay of WINDOW was preempted, and did not + finish.) If UPDATE is non-nil, compute the up-to-date position + if it isn't already recorded." + (let* ((windows (follow-all-followers window)) + (last (car (last windows)))) + (when (and update follow-start-end-invalid) + (follow-redisplay windows (car windows))) + (window-end last update))) + +(defun follow-set-window-start (window pos &optional noforce) + "Make display in the Follow Mode group of windows which includes +WINDOW start at position POS in WINDOW's buffer. + +WINDOW must be a live window and defaults to the selected one. Return +POS. Optional third arg NOFORCE non-nil inhibits next redisplay from +overriding motion of point in order to display at this exact start." + (let ((windows (follow-all-followers window))) + (setq follow-start-end-invalid t) + (set-window-start (car windows) pos noforce))) + +(defun follow-pos-visible-in-window-p (&optional pos window partially) + "Return non-nil if position POS is currently on the frame in one of + the windows in the Follow Mode group which includes WINDOW. + +WINDOW must be a live window and defaults to the selected one. + +Return nil if that position is scrolled vertically out of view. If a +character is only partially visible, nil is returned, unless the +optional argument PARTIALLY is non-nil. If POS is only out of view +because of horizontal scrolling, return non-nil. If POS is t, it +specifies the position of the last visible glyph in WINDOW. POS +defaults to point in WINDOW; WINDOW defaults to the selected window. + +If POS is visible, return t if PARTIALLY is nil; if PARTIALLY is non-nil, +the return value is a list of 2 or 6 elements (X Y [RTOP RBOT ROWH VPOS]), +where X and Y are the pixel coordinates relative to the top left corner +of the actual window containing it. The remaining elements are +omitted if the character after POS is fully visible; otherwise, RTOP +and RBOT are the number of pixels off-window at the top and bottom of +the screen line (\"row\") containing POS, ROWH is the visible height +of that row, and VPOS is the row number \(zero-based)." + (let* ((windows (follow-all-followers window)) + (last (car (last windows)))) + (when follow-start-end-invalid + (follow-redisplay windows (car windows))) + (let* ((cache (follow-windows-start-end windows)) + (last-elt (car (last cache))) + our-pos pertinent-elt) + (setq pertinent-elt + (if (eq pos t) + last-elt + (setq our-pos (or pos (point))) + (catch 'element + (while cache + (when (< our-pos (nth 2 (car cache))) + (throw 'element (car cache))) + (setq cache (cdr cache))) + last-elt))) + (pos-visible-in-window-p our-pos (car pertinent-elt) partially)))) + +(defun follow-move-to-window-line (arg) + "Position point relative to the Follow mode group containing the selected window. +ARG nil means position point at center of the window group. +Else, ARG specifies vertical position within the window group; +zero means top of the first window in the group, negative means + relative to bottom of the last window in the group." + (let* ((windows (follow-all-followers)) + (start-end (follow-windows-start-end windows)) + (rev-start-end (reverse start-end)) + (lines 0) + middle-window elt count) + (select-window + (cond + ((null arg) + (setq rev-start-end (nthcdr (/ (length windows) 2) rev-start-end)) + (prog1 (car (car rev-start-end)) + (while (setq rev-start-end (cdr rev-start-end)) + (setq elt (car rev-start-end) + count (count-screen-lines (cadr elt) (nth 2 elt) + nil (car elt)) + lines (+ lines count))))) + ((>= arg 0) + (while (and (cdr start-end) + (progn + (setq elt (car start-end) + count (count-screen-lines (cadr elt) (nth 2 elt) + nil (car elt))) + (>= arg count))) + (setq arg (- arg count) + lines (+ lines count) + start-end (cdr start-end))) + (car (car start-end))) + (t ; (< arg 0) + (while (and (cadr rev-start-end) + (progn + (setq elt (car rev-start-end) + count (count-lines (cadr elt) (nth 2 elt))) + (<= arg (- count)))) + (setq arg (+ arg count) + rev-start-end (cdr rev-start-end))) + (prog1 (car (car rev-start-end)) + (while (setq rev-start-end (cdr rev-start-end)) + (setq elt (car rev-start-end) + count (count-screen-lines (cadr elt) (nth 2 elt) + nil (car elt)) + 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 |