Mercurial > emacs
changeset 16580:f4429e6fe33c
(a) split lazy-lock-defer-time into lazy-lock-defer-time and lazy-lock-defer-on-the-fly, (b) add lazy-lock-defer-on-scrolling, (c) use these to choose one of lazy-lock-defer-line-after-change, lazy-lock-defer-rest-after-change, lazy-lock-fontify-line-after-change, lazy-lock-fontify-rest-after-change to add to after-change-functions, (d) use with-current-buffer rather than save-excursion, (e) avoid integer overflow in lazy-lock-percent-fontified.
author | Simon Marshall <simon@gnu.org> |
---|---|
date | Sat, 16 Nov 1996 13:33:51 +0000 |
parents | aadb4abdeaaa |
children | 1d74abbf5ac3 |
files | lisp/lazy-lock.el |
diffstat | 1 files changed, 389 insertions(+), 167 deletions(-) [+] |
line wrap: on
line diff
--- a/lisp/lazy-lock.el Sat Nov 16 13:31:02 1996 +0000 +++ b/lisp/lazy-lock.el Sat Nov 16 13:33:51 1996 +0000 @@ -4,7 +4,7 @@ ;; Author: Simon Marshall <simon@gnu.ai.mit.edu> ;; Keywords: faces files -;; Version: 2.06 +;; Version: 2.07 ;;; This file is part of GNU Emacs. @@ -25,11 +25,10 @@ ;;; Commentary: -;; Purpose: -;; -;; To make visiting buffers in `font-lock-mode' faster by making fontification -;; be demand-driven, deferred and stealthy. -;; Fontification only occurs when, and where, necessary. +;; Lazy Lock mode is a Font Lock support mode. +;; It makes visiting buffers in Font Lock mode faster by making fontification +;; be demand-driven, deferred and stealthy, so that fontification only occurs +;; when, and where, necessary. ;; ;; See caveats and feedback below. ;; See also the fast-lock package. (But don't use them at the same time!) @@ -106,10 +105,11 @@ ;; implemented by placing a function on `window-scroll-functions'. However, ;; not all scrolling occurs when `window-start' has changed. A change in ;; window size, e.g., via C-x 1, or a significant deletion, e.g., of a number -;; of lines, causes `window-end' to change without changing `window-start'. -;; Arguably, these events are not scrolling events, but fontification must -;; occur for lazy-lock.el to work. Hooks `window-size-change-functions' and -;; `redisplay-end-trigger-functions' were added for these circumstances. +;; of lines, causes text previously invisible (i.e., after `window-end') to +;; become visible without changing `window-start'. Arguably, these events are +;; not scrolling events, but fontification must occur for lazy-lock.el to work. +;; Hooks `window-size-change-functions' and `redisplay-end-trigger-functions' +;; were added for these circumstances. ;; ;; Ben Wing thinks these hooks are "horribly horribly kludgy", and implemented ;; a `pre-idle-hook', a `mother-of-all-post-command-hooks', for XEmacs 19.14. @@ -147,16 +147,14 @@ ;; or after given amounts of idle time. Thus, the feature deals with the above ;; problems (a), (b) and (c). Version 2 deferral and stealth are implemented ;; by functions on Idle Timers. (A function on XEmacs' `pre-idle-hook' is -;; similar to an Emacs Idle Timer function with a fixed zero second timeout. -;; Hey, maybe I could stop using `window-scroll-functions' for demand-driven -;; fontification and use a zero second Emacs Idle Timer instead? Only joking!) +;; similar to an Emacs Idle Timer function with a fixed zero second timeout.) ;; Caveats: ;; -;; Lazy Lock mode does not work efficiently with Outline mode. This is because -;; when in Outline mode, although text may be hidden (not visible in the -;; window), the text is visible to Emacs Lisp code (not surprisingly) and Lazy -;; Lock fontifies it mercilessly. Maybe it will be fixed one day. +;; Lazy Lock mode does not work efficiently with Outline mode. +;; This is because when in Outline mode, although text may be not visible to +;; you in the window, the text is visible to Emacs Lisp code (not surprisingly) +;; and Lazy Lock fontifies it mercilessly. Maybe it will be fixed one day. ;; ;; Because buffer text is not necessarily fontified, other packages that expect ;; buffer text to be fontified in Font Lock mode either might not work as @@ -174,13 +172,6 @@ ;; ;; Currently XEmacs does not have the features to support this version of ;; lazy-lock.el. Maybe it will one day. - -;; Feedback: -;; -;; Feedback is welcome. -;; To submit a bug report (or make comments) please use the mechanism provided: -;; -;; M-x lazy-lock-submit-bug-report RET ;; History: ;; @@ -226,11 +217,22 @@ ;; - Added `do-while' macro ;; - Renamed `lazy-lock-let-buffer-state' macro to `save-buffer-state' ;; - Returned `lazy-lock-fontify-after-install' hack (Darren Hall hint) -;; - Added `lazy-lock-defer-driven' functionality (Scott Byer hint) +;; - Added `lazy-lock-defer-on-scrolling' functionality (Scott Byer hint) ;; - Made `lazy-lock-mode' wrap `font-lock-support-mode' ;; 2.05--2.06: ;; - Made `lazy-lock-fontify-after-defer' swap correctly (Scott Byer report) +;; 2.06--2.07: +;; - Added `lazy-lock-stealth-load' functionality (Rob Hooft hint) +;; - Made `lazy-lock-unstall' call `lazy-lock-fontify-region' if needed +;; - Made `lazy-lock-mode' call `lazy-lock-unstall' only if needed +;; - Made `lazy-lock-defer-after-scroll' do `set-window-redisplay-end-trigger' +;; - Added `lazy-lock-defer-contextually' functionality +;; - Added `lazy-lock-defer-on-the-fly' from `lazy-lock-defer-time' +;; - Renamed `lazy-lock-defer-driven' to `lazy-lock-defer-on-scrolling' +;; - Removed `lazy-lock-submit-bug-report' and bade farewell +;;; Code: + (require 'font-lock) ;; Make sure lazy-lock.el is supported. @@ -275,7 +277,38 @@ The order of execution is thus BODY, TEST, BODY, TEST and so on until TEST returns nil." (` (while (progn (,@ body) (, test))))) - (put 'do-while 'lisp-indent-function (get 'while 'lisp-indent-function))) + (put 'do-while 'lisp-indent-function (get 'while 'lisp-indent-function)) + ;; + ;; We use this for clarity and speed. Borrowed from a future Emacs. + (or (fboundp 'with-current-buffer) + (defmacro with-current-buffer (buffer &rest body) + "Execute the forms in BODY with BUFFER as the current buffer. +The value returned is the value of the last form in BODY." + (` (save-excursion (set-buffer (, buffer)) (,@ body))))) + (put 'with-current-buffer 'lisp-indent-function 1)) + +;(defun lazy-lock-submit-bug-report () +; "Submit via mail a bug report on lazy-lock.el." +; (interactive) +; (let ((reporter-prompt-for-summary-p t)) +; (reporter-submit-bug-report "simon@gnu.ai.mit.edu" "lazy-lock 2.07" +; '(lazy-lock-minimum-size lazy-lock-defer-on-the-fly +; lazy-lock-defer-on-scrolling lazy-lock-defer-contextually +; lazy-lock-defer-time lazy-lock-stealth-time +; lazy-lock-stealth-load lazy-lock-stealth-nice lazy-lock-stealth-lines +; lazy-lock-stealth-verbose) +; nil nil +; (concat "Hi Si., +; +;I want to report a bug. I've read the `Bugs' section of `Info' on Emacs, so I +;know how to make a clear and unambiguous report. To reproduce the bug: +; +;Start a fresh editor via `" invocation-name " -no-init-file -no-site-file'. +;In the `*scratch*' buffer, evaluate:")))) + +(defvar lazy-lock-mode nil) +(defvar lazy-lock-buffers nil) ; for deferral +(defvar lazy-lock-timers (cons nil nil)) ; for deferral and stealth ;; User Variables: @@ -291,8 +324,21 @@ The value of this variable is used when Lazy Lock mode is turned on.") -(defvar lazy-lock-defer-driven nil - "*If non-nil, means fontification should be defer-driven. +(defvar lazy-lock-defer-on-the-fly t + "*If non-nil, means fontification after a change should be deferred. +If nil, means on-the-fly fontification is performed. This means when changes +occur in the buffer, those areas are immediately fontified. +If a list, it should be a list of `major-mode' symbol names for which deferred +fontification should occur. The sense of the list is negated if it begins with +`not'. For example: + (c-mode c++-mode) +means that on-the-fly fontification is deferred for buffers in C and C++ modes +only, and deferral does not occur otherwise. + +The value of this variable is used when Lazy Lock mode is turned on.") + +(defvar lazy-lock-defer-on-scrolling nil + "*If non-nil, means fontification after a scroll should be deferred. If nil, means demand-driven fontification is performed. This means when scrolling into unfontified areas of the buffer, those areas are immediately fontified. Thus scrolling never presents unfontified areas. However, since @@ -307,22 +353,32 @@ is first fontified, after which subsequent scrolling may present future buffer insertions momentarily unfontified. However, since fontification does not occur during scrolling after the buffer is first fontified, scrolling will -become faster. +become faster. (But, since contextual changes continually occur, such a value +makes little sense if `lazy-lock-defer-contextually' is non-nil.) + +The value of this variable is used when Lazy Lock mode is turned on.") + +(defvar lazy-lock-defer-contextually 'syntax-driven + "*If non-nil, means deferred fontification should be syntactically true. +If nil, means deferred fontification occurs only on those lines modified. This +means where modification on a line causes syntactic change on subsequent lines, +those subsequent lines are not refontified to reflect their new context. +If t, means deferred fontification occurs on those lines modified and all +subsequent lines. This means those subsequent lines are refontified to reflect +their new syntactic context, either immediately or when scrolling into them. +If any other value, e.g., `syntax-driven', means deferred syntactically true +fontification occurs only if syntactic fontification is performed using the +buffer mode's syntax table, i.e., only if `font-lock-keywords-only' is nil. The value of this variable is used when Lazy Lock mode is turned on.") (defvar lazy-lock-defer-time - (if (featurep 'lisp-float-type) (/ (float 1) (float 4)) 1) + (if (featurep 'lisp-float-type) (/ (float 1) (float 3)) 1) "*Time in seconds to delay before beginning deferred fontification. Deferred fontification occurs if there is no input within this time. -If nil, means fontification is never deferred. However, fontification occurs -on-the-fly or during scrolling, which may be slow. -If a list, it should be of the form (MAJOR-MODES . TIME), where MAJOR-MODES is -a list of `major-mode' symbols for which deferred fontification should occur. -The sense of the list is negated if it begins with `not'. For example: - ((c-mode c++-mode) . 0.25) -means that the deferral time is 0.25s for buffers in C or C++ modes, and -deferral does not occur otherwise. +If nil, means fontification is never deferred, regardless of the values of the +variables `lazy-lock-defer-on-the-fly', `lazy-lock-defer-on-scrolling' and +`lazy-lock-defer-contextually'. The value of this variable is used when Lazy Lock mode is turned on.") @@ -339,19 +395,31 @@ To speed up input response during stealth fontification, at the cost of stealth taking longer to fontify, you could reduce the value of this variable.") +(defvar lazy-lock-stealth-load + (when (condition-case nil (load-average) (error)) 200) + "*Load in percentage above which stealth fontification is suspended. +Stealth fontification pauses when the system short-term load average (as +returned by the function `load-average' if supported) goes above this level, +thus reducing the demand that stealth fontification makes on the system. +If nil, means stealth fontification is never suspended. +To reduce machine load during stealth fontification, at the cost of stealth +taking longer to fontify, you could reduce the value of this variable. +See also `lazy-lock-stealth-nice'.") + (defvar lazy-lock-stealth-nice (if (featurep 'lisp-float-type) (/ (float 1) (float 8)) 1) "*Time in seconds to pause between chunks of stealth fontification. -Each iteration of stealth fontification is separated by this amount of time. +Each iteration of stealth fontification is separated by this amount of time, +thus reducing the demand that stealth fontification makes on the system. +If nil, means stealth fontification is never paused. To reduce machine load during stealth fontification, at the cost of stealth -taking longer to fontify, you could increase the value of this variable.") +taking longer to fontify, you could increase the value of this variable. +See also `lazy-lock-stealth-load'.") -(defvar lazy-lock-stealth-verbose (not (null font-lock-verbose)) +(defvar lazy-lock-stealth-verbose + (when (featurep 'lisp-float-type) + (and font-lock-verbose (not lazy-lock-defer-contextually))) "*If non-nil, means stealth fontification should show status messages.") - -(defvar lazy-lock-mode nil) -(defvar lazy-lock-buffers nil) ; for deferral -(defvar lazy-lock-timers (cons nil nil)) ; for deferral and stealth ;; User Functions: @@ -365,65 +433,63 @@ When Lazy Lock mode is enabled, fontification can be lazy in a number of ways: - - Demand-driven buffer fontification if `lazy-lock-minimum-size' is non-nil. - This means initial fontification does not occur if the buffer is greater - than `lazy-lock-minimum-size' characters in length. Instead, fontification - occurs when necessary, such as when scrolling through the buffer would - otherwise reveal unfontified areas. This is useful if buffer fontification - is too slow for large buffers. +- Demand-driven buffer fontification if `lazy-lock-minimum-size' is non-nil. + This means initial fontification does not occur if the buffer is greater than + `lazy-lock-minimum-size' characters in length. Instead, fontification occurs + when necessary, such as when scrolling through the buffer would otherwise + reveal unfontified areas. This is useful if buffer fontification is too slow + for large buffers. - - Defer-driven buffer fontification if `lazy-lock-defer-driven' is non-nil. - This means all fontification is deferred, such as fontification that occurs - when scrolling through the buffer would otherwise reveal unfontified areas. - Instead, these areas are seen momentarily unfontified. This is useful if - demand-driven fontification is too slow to keep up with scrolling. +- Deferred scroll fontification if `lazy-lock-defer-on-scrolling' is non-nil. + This means demand-driven fontification does not occur as you scroll. + Instead, fontification is deferred until after `lazy-lock-defer-time' seconds + of Emacs idle time, while Emacs remains idle. This is useful if + fontification is too slow to keep up with scrolling. + +- Deferred on-the-fly fontification if `lazy-lock-defer-on-the-fly' is non-nil. + This means on-the-fly fontification does not occur as you type. Instead, + fontification is deferred until after `lazy-lock-defer-time' seconds of Emacs + idle time, while Emacs remains idle. This is useful if fontification is too + slow to keep up with your typing. - - Deferred on-the-fly fontification if `lazy-lock-defer-time' is non-nil. - This means on-the-fly fontification does not occur as you type. Instead, - fontification is deferred until after `lazy-lock-defer-time' seconds of - Emacs idle time, while Emacs remains idle. This is useful if on-the-fly - fontification is too slow to keep up with your typing. +- Deferred context fontification if `lazy-lock-defer-contextually' is non-nil. + This means fontification updates the buffer corresponding to true syntactic + context, after `lazy-lock-defer-time' seconds of Emacs idle time, while Emacs + remains idle. Otherwise, fontification occurs on modified lines only, and + subsequent lines can remain fontified corresponding to previous syntactic + contexts. This is useful where strings or comments span lines. - - Stealthy buffer fontification if `lazy-lock-stealth-time' is non-nil. - This means remaining unfontified areas of buffers are fontified if Emacs has - been idle for `lazy-lock-stealth-time' seconds, while Emacs remains idle. - This is useful if any buffer has demand- or defer-driven fontification. +- Stealthy buffer fontification if `lazy-lock-stealth-time' is non-nil. + This means remaining unfontified areas of buffers are fontified if Emacs has + been idle for `lazy-lock-stealth-time' seconds, while Emacs remains idle. + This is useful if any buffer has any deferred fontification. -See also variables `lazy-lock-stealth-lines', `lazy-lock-stealth-nice' and -`lazy-lock-stealth-verbose' for stealth fontification. +Basic Font Lock mode on-the-fly fontification behaviour fontifies modified +lines only. Thus, if `lazy-lock-defer-contextually' is non-nil, Lazy Lock mode +on-the-fly fontification may fontify differently, albeit correctly. In any +event, to refontify some lines you can use \\[font-lock-fontify-block]. -Use \\[lazy-lock-submit-bug-report] to send bug reports or feedback." +Stealth fontification only occurs while the system remains unloaded. +If the system load rises above `lazy-lock-stealth-load' percent, stealth +fontification is suspended. Stealth fontification intensity is controlled via +the variable `lazy-lock-stealth-nice' and `lazy-lock-stealth-lines', and +verbosity is controlled via the variable `lazy-lock-stealth-verbose'." (interactive "P") - (set (make-local-variable 'lazy-lock-mode) - (and (not (memq 'lazy-lock-mode font-lock-inhibit-thing-lock)) - (if arg (> (prefix-numeric-value arg) 0) (not lazy-lock-mode)))) - (cond ((and lazy-lock-mode (not font-lock-mode)) - ;; Turned on `lazy-lock-mode' rather than `font-lock-mode'. - (let ((font-lock-support-mode 'lazy-lock-mode)) - (font-lock-mode t))) - (lazy-lock-mode - ;; Turn ourselves on. - (lazy-lock-install)) - (t - ;; Turn ourselves off. - (lazy-lock-unstall)))) - -(defun lazy-lock-submit-bug-report () - "Submit via mail a bug report on lazy-lock.el." - (interactive) - (let ((reporter-prompt-for-summary-p t)) - (reporter-submit-bug-report "simon@gnu.ai.mit.edu" "lazy-lock 2.06" - '(lazy-lock-minimum-size lazy-lock-defer-driven lazy-lock-defer-time - lazy-lock-stealth-time lazy-lock-stealth-nice lazy-lock-stealth-lines - lazy-lock-stealth-verbose) - nil nil - (concat "Hi Si., - -I want to report a bug. I've read the `Bugs' section of `Info' on Emacs, so I -know how to make a clear and unambiguous report. To reproduce the bug: - -Start a fresh Emacs via `" invocation-name " -no-init-file -no-site-file'. -In the `*scratch*' buffer, evaluate:")))) + (let* ((was-on lazy-lock-mode) + (now-on (unless (memq 'lazy-lock-mode font-lock-inhibit-thing-lock) + (if arg (> (prefix-numeric-value arg) 0) (not was-on))))) + (cond ((and now-on (not font-lock-mode)) + ;; Turned on `lazy-lock-mode' rather than `font-lock-mode'. + (let ((font-lock-support-mode 'lazy-lock-mode)) + (font-lock-mode t))) + (now-on + ;; Turn ourselves on. + (set (make-local-variable 'lazy-lock-mode) t) + (lazy-lock-install)) + (was-on + ;; Turn ourselves off. + (set (make-local-variable 'lazy-lock-mode) nil) + (lazy-lock-unstall))))) ;;;###autoload (defun turn-on-lazy-lock () @@ -431,7 +497,12 @@ (lazy-lock-mode t)) (defun lazy-lock-install () - (let ((min-size (font-lock-value-in-major-mode lazy-lock-minimum-size))) + (let ((min-size (font-lock-value-in-major-mode lazy-lock-minimum-size)) + (defer-change (and lazy-lock-defer-time lazy-lock-defer-on-the-fly)) + (defer-scroll (and lazy-lock-defer-time lazy-lock-defer-on-scrolling)) + (defer-context (and lazy-lock-defer-time lazy-lock-defer-contextually + (or (eq lazy-lock-defer-contextually t) + (null font-lock-keywords-only))))) ;; ;; Tell Font Lock whether Lazy Lock will do fontification. (make-local-variable 'font-lock-fontified) @@ -449,39 +520,51 @@ ;; ;; Add the fontification hooks. (lazy-lock-install-hooks - (or (numberp lazy-lock-defer-time) - (if (eq (car (car lazy-lock-defer-time)) 'not) - (not (memq major-mode (cdr (car lazy-lock-defer-time)))) - (memq major-mode (car lazy-lock-defer-time)))) font-lock-fontified - (eq lazy-lock-defer-driven t)) + (cond ((eq (car-safe defer-change) 'not) + (not (memq major-mode (cdr defer-change)))) + ((listp defer-change) + (memq major-mode defer-change)) + (t + defer-change)) + (eq defer-scroll t) + defer-context) ;; ;; Add the fontification timers. (lazy-lock-install-timers - (or (cdr-safe lazy-lock-defer-time) lazy-lock-defer-time) + (if (or defer-change defer-scroll defer-context) lazy-lock-defer-time) lazy-lock-stealth-time))) -(defun lazy-lock-install-hooks (deferring fontifying defer-driven) +(defun lazy-lock-install-hooks (fontifying + defer-change defer-scroll defer-context) ;; - ;; Add hook if lazy-lock.el is deferring or is fontifying on scrolling. - (when (or deferring fontifying) + ;; Add hook if lazy-lock.el is fontifying on scrolling or is deferring. + (when (or fontifying defer-change defer-scroll defer-context) (make-local-hook 'window-scroll-functions) - (add-hook 'window-scroll-functions (if (and deferring defer-driven) + (add-hook 'window-scroll-functions (if defer-scroll 'lazy-lock-defer-after-scroll 'lazy-lock-fontify-after-scroll) nil t)) ;; - ;; Add hook if lazy-lock.el is not deferring and is fontifying. - (when (and (not deferring) fontifying) + ;; Add hook if lazy-lock.el is fontifying and is not deferring changes. + (when (and fontifying (not defer-change) (not defer-context)) (make-local-hook 'before-change-functions) (add-hook 'before-change-functions 'lazy-lock-arrange-before-change nil t)) ;; - ;; Add hook if lazy-lock.el is deferring. - (when deferring - (remove-hook 'after-change-functions 'font-lock-after-change-function t) - (add-hook 'after-change-functions 'lazy-lock-defer-after-change nil t)) + ;; Replace Font Lock mode hook. + (remove-hook 'after-change-functions 'font-lock-after-change-function t) + (add-hook 'after-change-functions + (cond ((and defer-change defer-context) + 'lazy-lock-defer-rest-after-change) + (defer-change + 'lazy-lock-defer-line-after-change) + (defer-context + 'lazy-lock-fontify-rest-after-change) + (t + 'lazy-lock-fontify-line-after-change)) + nil t) ;; - ;; Add package-specific hooks. + ;; Add package-specific hook. (make-local-hook 'outline-view-change-hook) (add-hook 'outline-view-change-hook 'lazy-lock-fontify-after-outline nil t)) @@ -506,6 +589,22 @@ (defun lazy-lock-unstall () ;; + ;; If Font Lock mode is still enabled, make sure that the buffer is + ;; fontified, and reinstall its hook. We must do this first. + (when font-lock-mode + (when (lazy-lock-unfontified-p) + (let ((verbose (if (numberp font-lock-verbose) + (> (buffer-size) font-lock-verbose) + font-lock-verbose))) + (if verbose (message "Fontifying %s..." (buffer-name))) + ;; Make sure we fontify etc. in the whole buffer. + (save-restriction + (widen) + (lazy-lock-fontify-region (point-min) (point-max))) + (if verbose (message "Fontifying %s...%s" (buffer-name) + (if (lazy-lock-unfontified-p) "quit" "done"))))) + (add-hook 'after-change-functions 'font-lock-after-change-function nil t)) + ;; ;; Remove the text properties. (lazy-lock-after-unfontify-buffer) ;; @@ -513,19 +612,33 @@ (remove-hook 'window-scroll-functions 'lazy-lock-fontify-after-scroll t) (remove-hook 'window-scroll-functions 'lazy-lock-defer-after-scroll t) (remove-hook 'before-change-functions 'lazy-lock-arrange-before-change t) - (remove-hook 'after-change-functions 'lazy-lock-defer-after-change t) - (remove-hook 'outline-view-change-hook 'lazy-lock-fontify-after-outline t) - ;; - ;; If Font Lock mode is still enabled, reinstall its hook. - (when font-lock-mode - (add-hook 'after-change-functions 'font-lock-after-change-function nil t))) + (remove-hook 'after-change-functions 'lazy-lock-fontify-line-after-change t) + (remove-hook 'after-change-functions 'lazy-lock-fontify-rest-after-change t) + (remove-hook 'after-change-functions 'lazy-lock-defer-line-after-change t) + (remove-hook 'after-change-functions 'lazy-lock-defer-rest-after-change t) + (remove-hook 'outline-view-change-hook 'lazy-lock-fontify-after-outline t)) ;; Hook functions. +;; Lazy Lock mode intervenes when (1) a previously invisible buffer region +;; becomes visible, i.e., for demand- or defer-driven on-the-scroll +;; fontification, (2) a buffer modification occurs, i.e., for defer-driven +;; on-the-fly fontification, (3) Emacs becomes idle, i.e., for fontification of +;; deferred fontification and stealth fontification, and (4) other special +;; occasions. + +;; 1. There are three ways whereby this can happen. +;; +;; (a) Scrolling the window, either explicitly (e.g., `scroll-up') or +;; implicitly (e.g., `search-forward'). Here, `window-start' changes. +;; Fontification occurs by adding `lazy-lock-fontify-after-scroll' (for +;; demand-driven fontification) or `lazy-lock-defer-after-scroll' (for +;; defer-driven fontification) to the hook `window-scroll-functions'. + (defun lazy-lock-fontify-after-scroll (window window-start) ;; Called from `window-scroll-functions'. - ;; Fontify WINDOW from WINDOW-START. We cannot use `window-end' so we work - ;; out what it would be via `vertical-motion'. + ;; Fontify WINDOW from WINDOW-START following the scroll. We cannot use + ;; `window-end' so we work out what it would be via `vertical-motion'. (save-excursion (goto-char window-start) (vertical-motion (window-height window) window) @@ -534,21 +647,25 @@ ;; result in an unnecessary trigger after this if we did not cancel it now. (set-window-redisplay-end-trigger window nil)) -(defun lazy-lock-fontify-after-trigger (window trigger-point) - ;; Called from `redisplay-end-trigger-functions'. - ;; Fontify WINDOW from TRIGGER-POINT. We cannot use `window-end' so we work - ;; out what it would be via `vertical-motion'. - ;; We could probably just use `lazy-lock-fontify-after-scroll' without loss: - ;; (lazy-lock-fontify-after-scroll window (window-start window)) - (save-excursion - (goto-char (window-start window)) - (vertical-motion (window-height window) window) - (lazy-lock-fontify-region trigger-point (point)))) +(defun lazy-lock-defer-after-scroll (window window-start) + ;; Called from `window-scroll-functions'. + ;; Defer fontification following the scroll. Save the current buffer so that + ;; we subsequently fontify in all windows showing the buffer. + (unless (memq (current-buffer) lazy-lock-buffers) + (push (current-buffer) lazy-lock-buffers)) + ;; A prior deletion that did not cause scrolling, followed by a scroll, would + ;; result in an unnecessary trigger after this if we did not cancel it now. + (set-window-redisplay-end-trigger window nil)) + +;; (b) Resizing the window, either explicitly (e.g., `enlarge-window') or +;; implicitly (e.g., `delete-other-windows'). Here, `window-end' changes. +;; Fontification occurs by adding `lazy-lock-fontify-after-resize' to the +;; hook `window-size-change-functions'. (defun lazy-lock-fontify-after-resize (frame) ;; Called from `window-size-change-functions'. - ;; Fontify windows in FRAME. We cannot use `window-start' or `window-end' so - ;; we fontify conservatively. + ;; Fontify windows in FRAME following the resize. We cannot use + ;; `window-start' or `window-end' so we fontify conservatively. (save-excursion (save-selected-window (select-frame frame) @@ -559,6 +676,15 @@ (set-window-redisplay-end-trigger window nil))) 'nomini frame)))) +;; (c) Deletion in the buffer. Here, a `window-end' marker can become visible. +;; Fontification occurs by adding `lazy-lock-arrange-before-change' to +;; `before-change-functions' and `lazy-lock-fontify-after-trigger' to the +;; hook `redisplay-end-trigger-functions'. Before every deletion, the +;; marker `window-redisplay-end-trigger' position is set to the soon-to-be +;; changed `window-end' position. If the marker becomes visible, +;; `lazy-lock-fontify-after-trigger' gets called. Ouch. Note that we only +;; have to deal with this eventuality if there is no on-the-fly deferral. + (defun lazy-lock-arrange-before-change (beg end) ;; Called from `before-change-functions'. ;; Arrange that if text becomes visible it will be fontified (if a deletion @@ -572,22 +698,63 @@ (set-marker (window-redisplay-end-trigger window) (window-end window)) (setq windows (cdr windows)))))) -(defun lazy-lock-defer-after-scroll (window window-start) - ;; Called from `window-scroll-functions'. - ;; Defer fontification following the scroll. Save the current buffer so that - ;; we subsequently fontify in all windows showing the buffer. - (unless (memq (current-buffer) lazy-lock-buffers) - (push (current-buffer) lazy-lock-buffers))) +(defun lazy-lock-fontify-after-trigger (window trigger-point) + ;; Called from `redisplay-end-trigger-functions'. + ;; Fontify WINDOW from TRIGGER-POINT. We cannot use `window-end' so we work + ;; out what it would be via `vertical-motion'. + ;; We could probably just use `lazy-lock-fontify-after-scroll' without loss: + ;; (lazy-lock-fontify-after-scroll window (window-start window)) + (save-excursion + (goto-char (window-start window)) + (vertical-motion (window-height window) window) + (lazy-lock-fontify-region trigger-point (point)))) -(defun lazy-lock-defer-after-change (beg end old-len) +;; 2. Modified text must be marked as unfontified so it can be identified and +;; fontified later when Emacs is idle. Deferral occurs by adding one of +;; `lazy-lock-fontify-*-after-change' (for on-the-fly fontification) or +;; `lazy-lock-defer-*-after-change' (for deferred fontification) to the +;; hook `after-change-functions'. + +(defalias 'lazy-lock-fontify-line-after-change ;; Called from `after-change-functions'. - ;; Defer fontification of the current line. Save the current buffer so that - ;; we subsequently fontify in all windows showing the buffer. + ;; Fontify the current change. + 'font-lock-after-change-function) + +(defun lazy-lock-fontify-rest-after-change (beg end old-len) + ;; Called from `after-change-functions'. + ;; Fontify the current change and defer fontification of the rest of the + ;; buffer. Save the current buffer so that we subsequently fontify in all + ;; windows showing the buffer. + (lazy-lock-fontify-line-after-change beg end old-len) (save-buffer-state nil (unless (memq (current-buffer) lazy-lock-buffers) (push (current-buffer) lazy-lock-buffers)) - (remove-text-properties - (max (1- beg) (point-min)) (min (1+ end) (point-max)) '(lazy-lock nil)))) + (remove-text-properties end (point-max) '(lazy-lock nil)))) + +(defun lazy-lock-defer-line-after-change (beg end old-len) + ;; Called from `after-change-functions'. + ;; Defer fontification of the current change. Save the current buffer so + ;; that we subsequently fontify in all windows showing the buffer. + (save-buffer-state nil + (unless (memq (current-buffer) lazy-lock-buffers) + (push (current-buffer) lazy-lock-buffers)) + (remove-text-properties (max (1- beg) (point-min)) + (min (1+ end) (point-max)) + '(lazy-lock nil)))) + +(defun lazy-lock-defer-rest-after-change (beg end old-len) + ;; Called from `after-change-functions'. + ;; Defer fontification of the rest of the buffer. Save the current buffer so + ;; that we subsequently fontify in all windows showing the buffer. + (save-buffer-state nil + (unless (memq (current-buffer) lazy-lock-buffers) + (push (current-buffer) lazy-lock-buffers)) + (remove-text-properties (max (1- beg) (point-min)) + (point-max) + '(lazy-lock nil)))) + +;; 3. Deferred fontification and stealth fontification are done from these two +;; functions. They are set up as Idle Timers. (defun lazy-lock-fontify-after-defer () ;; Called from `timer-idle-list'. @@ -599,7 +766,7 @@ (setq windows (cdr windows))) (setq lazy-lock-buffers (cdr lazy-lock-buffers)))) ;; Add hook if fontification should now be defer-driven in this buffer. - (when (and lazy-lock-mode lazy-lock-defer-driven + (when (and lazy-lock-mode lazy-lock-defer-on-scrolling (memq 'lazy-lock-fontify-after-scroll window-scroll-functions) (not (or (input-pending-p) (lazy-lock-unfontified-p)))) (remove-hook 'window-scroll-functions 'lazy-lock-fontify-after-scroll t) @@ -617,19 +784,30 @@ (if (not (and lazy-lock-mode (lazy-lock-unfontified-p))) (setq continue (not (input-pending-p))) ;; Fontify regions in this buffer while there is no input. - (do-while (and (lazy-lock-unfontified-p) - (setq continue (sit-for lazy-lock-stealth-nice))) - (when lazy-lock-stealth-verbose - (if message - (message "Fontifying stealthily... %2d%% of %s" - (lazy-lock-percent-fontified) (buffer-name)) - (message "Fontifying stealthily...") - (setq message t))) - (lazy-lock-fontify-chunk))) + (do-while (and (lazy-lock-unfontified-p) continue) + (if (and lazy-lock-stealth-load + (> (car (load-average)) lazy-lock-stealth-load)) + ;; Wait a while before continuing with the loop. + (progn + (when message + (message "Fontifying stealthily...suspended") + (setq message nil)) + (setq continue (sit-for (or lazy-lock-stealth-time 30)))) + ;; Fontify a chunk. + (when lazy-lock-stealth-verbose + (if message + (message "Fontifying stealthily... %2d%% of %s" + (lazy-lock-percent-fontified) (buffer-name)) + (message "Fontifying stealthily...") + (setq message t))) + (lazy-lock-fontify-chunk) + (setq continue (sit-for (or lazy-lock-stealth-nice 0)))))) (setq buffers (cdr buffers)))) (when message (message "Fontifying stealthily...%s" (if continue "done" "quit")))))) +;; 4. Special circumstances. + (defun lazy-lock-fontify-after-outline () ;; Called from `outline-view-change-hook'. ;; Fontify windows showing the current buffer, as its visibility has changed. @@ -716,16 +894,14 @@ (defun lazy-lock-fontify-window (window) ;; Fontify in WINDOW between `window-start' and `window-end'. ;; We can only do this when we can use `window-start' and `window-end'. - (save-excursion - (set-buffer (window-buffer window)) + (with-current-buffer (window-buffer window) (lazy-lock-fontify-region (window-start window) (window-end window)))) (defun lazy-lock-fontify-conservatively (window) ;; Fontify in WINDOW conservatively around point. ;; Where we cannot use `window-start' and `window-end' we do `window-height' ;; lines around point. That way we guarantee to have done enough. - (save-excursion - (set-buffer (window-buffer window)) + (with-current-buffer (window-buffer window) (lazy-lock-fontify-region (save-excursion (vertical-motion (- (window-height window)) window) (point)) @@ -742,13 +918,15 @@ ;; Return the percentage (of characters) of the buffer that are fontified. (save-restriction (widen) - (let ((beg (point-min)) (end (point-max)) (size 0) next) + (let ((beg (point-min)) (size 0) next) ;; Find where the next fontified region begins. - (while (setq beg (text-property-any beg end 'lazy-lock t)) - (setq next (or (text-property-any beg end 'lazy-lock nil) end) - size (+ size (- next beg)) - beg next)) - (/ (* size 100) (buffer-size))))) + (while (setq beg (text-property-any beg (point-max) 'lazy-lock t)) + (setq next (or (text-property-any beg (point-max) 'lazy-lock nil) + (point-max))) + (incf size (- next beg)) + (setq beg next)) + ;; Float because using integer multiplication will frequently overflow. + (truncate (* (/ (float size) (point-max)) 100))))) ;; Version dependent workarounds and fixes. @@ -784,6 +962,50 @@ (while lazy-lock-install (mapcar 'lazy-lock-fontify-conservatively (get-buffer-window-list (pop lazy-lock-install) 'nomini t))))) + +(when (consp lazy-lock-defer-time) + ;; + ;; In 2.06.04 and below, `lazy-lock-defer-time' could specify modes and time. + (with-output-to-temp-buffer "*Help*" + (princ "The value of the variable `lazy-lock-defer-time' was\n ") + (princ lazy-lock-defer-time) + (princ "\n") + (princ "This variable cannot now be a list of modes and time, ") + (princ "so instead use the forms:\n") + (princ " (setq lazy-lock-defer-time ") + (princ (cdr lazy-lock-defer-time)) + (princ ")\n") + (princ " (setq lazy-lock-defer-on-the-fly '") + (princ (car lazy-lock-defer-time)) + (princ ")\n") + (princ "in your ~/.emacs. ") + (princ "The above forms have been evaluated for this editor session,\n") + (princ "but you should change your ~/.emacs now.")) + (setq lazy-lock-defer-on-the-fly (car lazy-lock-defer-time) + lazy-lock-defer-time (cdr lazy-lock-defer-time))) + +(when (boundp 'lazy-lock-defer-driven) + ;; + ;; In 2.06.04 and below, `lazy-lock-defer-driven' was the variable name. + (with-output-to-temp-buffer "*Help*" + (princ "The value of the variable `lazy-lock-defer-driven' is set to ") + (if (memq lazy-lock-defer-driven '(nil t)) + (princ lazy-lock-defer-driven) + (princ "`") + (princ lazy-lock-defer-driven) + (princ "'")) + (princ ".\n") + (princ "This variable is now called `lazy-lock-defer-on-scrolling',\n") + (princ "so instead use the form:\n") + (princ " (setq lazy-lock-defer-on-scrolling ") + (unless (memq lazy-lock-defer-driven '(nil t)) + (princ "'")) + (princ lazy-lock-defer-driven) + (princ ")\n") + (princ "in your ~/.emacs. ") + (princ "The above form has been evaluated for this editor session,\n") + (princ "but you should change your ~/.emacs now.")) + (setq lazy-lock-defer-on-scrolling lazy-lock-defer-driven)) ;; Possibly absent.