File: lazy-lock.el.html

Purpose:

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!)

Installation:

Put in your ~/.emacs:

(setq font-lock-support-mode 'lazy-lock-mode)

Start up a new Emacs and use font-lock as usual (except that you can use the so-called "gaudier" fontification regexps on big files without frustration).

In a buffer (which has font-lock-mode(var)/font-lock-mode(fun) enabled) which is at least lazy-lock-minimum-size characters long, buffer fontification will not occur and only the visible portion of the buffer will be fontified. Motion around the buffer will fontify those visible portions not previously fontified. If stealth fontification is enabled, buffer fontification will occur in invisible parts of the buffer after lazy-lock-stealth-time seconds of idle time. If on-the-fly fontification is deferred, on-the-fly fontification will occur after lazy-lock-defer-time seconds of idle time.

User-visible differences with version 1:

- Version 2 can defer on-the-fly fontification. Therefore you need not, and
should not, use defer-lock.el with this version of lazy-lock.el.

A number of variables have changed meaning:

- A value of nil for the variable lazy-lock-minimum-size means never turn
on demand-driven fontification. In version 1 this meant always turn on demand-driven fontification. If you really want demand-driven fontification regardless of buffer size, set this variable to 0.

- The variable lazy-lock-stealth-lines cannot have a nil value. In
version 1 this meant use window-height as the maximum number of lines to fontify as a stealth chunk. This makes no sense; stealth fontification is of a buffer, not a window.

Implementation differences with version 1:

- Version 1 of lazy-lock.el is a bit of a hack. Version 1 demand-driven
fontification, the core feature of lazy-lock.el, is implemented by placing a function on post-command-hook. This function fontifies where necessary, i.e., where a window scroll has occurred. However, there are a number of problems with using post-command-hook:

(a) As the name suggests, post-command-hook is run after every command,
    i.e., frequently and regardless of whether scrolling has occurred.
(b) Scrolling can occur during a command, when post-command-hook is not
    run, i.e., it is not necessarily run after scrolling has occurred.
(c) When post-command-hook is run, there is nothing to suggest where
    scrolling might have occurred, i.e., which windows have scrolled.

Thus lazy-lock.el's function is called almost as often as possible, usually when it need not be called, yet it is not always called when it is needed. Also, lazy-lock.el's function must check each window to see if a scroll has occurred there. Worse still, lazy-lock.el's function must fontify a region twice as large as necessary to make sure the window is completely fontified. Basically, post-command-hook is completely inappropriate for lazy-lock.el.

Ideally, we want to attach lazy-lock.el's function to a hook that is run only when scrolling occurs, e.g., window-start has changed, and tells us as much information as we need, i.e., the window and its new buffer region. Richard Stallman implemented a window-scroll-functions for Emacs 19.30. Functions on it are run when window-start has changed, and are supplied with the window and the window's new window-start position. (It would be better if it also supplied the window's new window-end position, but that is calculated as part of the redisplay process, and the functions on
window-scroll-functions are run before redisplay has finished.) Thus, the
hook deals with the above problems (a), (b) and (c).

If only life was that easy. Version 2 demand-driven fontification is mostly 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 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. He then hacked up a version 1 lazy-lock.el to use pre-idle-hook rather than post-command-hook. Whereas functions on post-command-hook are called almost as often as possible, functions on pre-idle-hook really are called as often as possible, even when the mouse moves and, on some systems, while XEmacs is idle. Thus, the hook deals with the above problem (b), but unfortunately it makes (a) worse and does not address (c) at all.

I freely admit that redisplay-end-trigger-functions and, to a much lesser extent, window-size-change-functions are not pretty. However, I feel that a window-scroll-functions feature is cleaner than a pre-idle-hook, and the result is faster and smaller, less intrusive and more targeted, code. Since pre-idle-hook is pretty much like post-command-hook, there is no point in making this version of lazy-lock.el work with it. Anyway, that's Lit 30 of my humble opinion.

- Version 1 stealth fontification is also implemented by placing a function
on post-command-hook. This function waits for a given amount of time, and, if Emacs remains idle, fontifies where necessary. Again, there are a number of problems with using post-command-hook:

(a) Functions on post-command-hook are run sequentially, so this function
    can interfere with other functions on the hook, and vice versa.
(b) This function waits for a given amount of time, so it can interfere with
    various features that are dealt with by Emacs after a command, e.g.,
    region highlighting, asynchronous updating and keystroke echoing.
(c) Fontification may be required during a command, when post-command-hook
    is not run. (Version 2 deferred fontification only.)

Again, post-command-hook is completely inappropriate for lazy-lock.el. Richard Stallman and Morten Welinder implemented internal Timers and Idle Timers for Emacs 19.31. Functions can be run independently at given times 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.)

- Version 1 has the following problems (relative to version 2):

(a) It is slow when it does its job.
(b) It does not always do its job when it should.
(c) It slows all interaction (when it doesn't need to do its job).
(d) It interferes with other package functions on post-command-hook.
(e) It interferes with Emacs things within the read-eval loop.

Ben's hacked-up lazy-lock.el 1.14 almost solved (b) but made (c) worse.

- Version 2 has the following additional features (relative to version 1):

(a) It can defer fontification (both on-the-fly and on-scrolling).
(b) It can fontify contextually (syntactically true on-the-fly).

Caveats:

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 expected, or might not display buffer text as expected. An example of the latter is occur, which copies lines of buffer text into another buffer.

In Emacs 19.30, Lazy Lock mode does not ensure that an existing buffer is fontified if it is made visible via a minibuffer-less command that replaces an existing window's buffer (e.g., via the Buffers menu). Upgrade!

In Emacs 19.30, Lazy Lock mode does not work well with Transient Mark mode or modes based on Comint mode (e.g., Shell mode), and also interferes with the echoing of keystrokes in the minibuffer. This is because of the way deferral and stealth have to be implemented for Emacs 19.30. Upgrade!

Currently XEmacs does not have the features to support this version of lazy-lock.el. Maybe it will one day.

History:

1.15--2.00:
- Rewrite for Emacs 19.30 and the features rms added to support lazy-lock.el
  so that it could work correctly and efficiently.
- Many thanks to those who reported bugs, fixed bugs, made suggestions or
  otherwise contributed in the version 1 cycle; Jari Aalto, Kevin Broadey,
  Ulrik Dickow, Bill Dubuque, Bob Glickstein, Boris Goldowsky,
  Jonas Jarnestrom, David Karr, Michael Kifer, Erik Naggum, Rick Sladkey,
  Jim Thompson, Ben Wing, Ilya Zakharevich, and Richard Stallman.
2.00--2.01:
- Made lazy-lock-fontify-after-command always sit-for and so redisplay
- Use buffer-name not buffer-live-p (Bill Dubuque hint)
- Made lazy-lock-install do add-to-list not setq of current-buffer
- Made lazy-lock-fontify-after-install loop over buffer list
- Made lazy-lock-arrange-before-change to arrange window-end triggering
- Made lazy-lock-let-buffer-state wrap both befter-change-functions
- Made lazy-lock-fontify-region do condition-case (Hyman Rosen report)
2.01--2.02:
- Use buffer-live-p as buffer-name can barf (Richard Stanton report)
- Made lazy-lock-install set font-lock-fontified (Kevin Davidson report)
- Made lazy-lock-install add hooks only if needed
- Made lazy-lock-unstall add font-lock-after-change-function if needed
2.02--2.03:
- Made lazy-lock-fontify-region do condition-case for quit too
- Made lazy-lock-mode(var)/lazy-lock-mode(fun) respect the value of font-lock-inhibit-thing-lock
- Added lazy-lock-after-unfontify-buffer
- Removed lazy-lock-fontify-after-install hack
- Made lazy-lock-fontify-after-scroll not set-buffer to window-buffer
- Made lazy-lock-fontify-after-trigger not set-buffer to window-buffer
- Made lazy-lock-fontify-after-idle be interruptible (Scott Burson hint)
2.03--2.04:
- Rewrite for Emacs 19.31 idle timers
- Renamed buffer-windows to get-buffer-window-list
- Removed buffer-live-p
- Made lazy-lock-defer-after-change always save current-buffer
- Made lazy-lock-fontify-after-defer just process buffers
- Made lazy-lock-install-hooks add hooks correctly (Kevin Broadey report)
- Made lazy-lock-install cope if lazy-lock-defer-time is a list
2.04--2.05:
- Rewrite for Common Lisp macros
- 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-on-scrolling functionality (Scott Byer hint)
- Made lazy-lock-mode(var)/lazy-lock-mode(fun) 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(var)/lazy-lock-mode(fun) 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
2.07--2.08:
- Made lazy-lock-fontify-conservatively fontify around window-point
- Made save-buffer-state wrap inhibit-point-motion-hooks
- Added Custom support
2.08--2.09:
- Removed byte-* variables from eval-when-compile (Erik Naggum hint)
- Made various wrapping inhibit-point-motion-hooks (Vinicius Latorre hint)
- Made lazy-lock-fontify-after-idle wrap minibuffer-auto-raise
- Made lazy-lock-fontify-after-defer paranoid about deferred buffers
2.09--2.10:
- Use window-end UPDATE arg for Emacs 20.4 and later.
- Made deferral widen before unfontifying (Dan Nicolaescu report)
- Use lazy-lock-fontify-after-visage for hideshow.el (Dan Nicolaescu hint)
- Use other widget where possible (Andreas Schwab fix)
2.10--2.11:
- Used with-temp-message where possible to make messages temporary.

Defined variables (10)

lazy-lock-defer-contextuallyIf non-nil, means deferred fontification should be syntactically true.
lazy-lock-defer-on-scrollingIf non-nil, means fontification after a scroll should be deferred.
lazy-lock-defer-on-the-flyIf non-nil, means fontification after a change should be deferred.
lazy-lock-defer-timeTime in seconds to delay before beginning deferred fontification.
lazy-lock-minimum-sizeMinimum size of a buffer for demand-driven fontification.
lazy-lock-stealth-linesMaximum size of a chunk of stealth fontification.
lazy-lock-stealth-loadLoad in percentage above which stealth fontification is suspended.
lazy-lock-stealth-niceTime in seconds to pause between chunks of stealth fontification.
lazy-lock-stealth-timeTime in seconds to delay before beginning stealth fontification.
lazy-lock-stealth-verboseIf non-nil, means stealth fontification should show status messages.

Defined functions (16)

lazy-lock-arrange-before-change(BEG END)
lazy-lock-defer-after-scroll(WINDOW WINDOW-START)
lazy-lock-defer-line-after-change(BEG END OLD-LEN)
lazy-lock-defer-rest-after-change(BEG END OLD-LEN)
lazy-lock-fontify-after-resize(FRAME)
lazy-lock-fontify-after-scroll(WINDOW WINDOW-START)
lazy-lock-fontify-after-trigger(WINDOW TRIGGER-POINT)
lazy-lock-fontify-conservatively(WINDOW)
lazy-lock-fontify-line-after-change(BEG END &optional OLD-LEN)
lazy-lock-fontify-region(BEG END)
lazy-lock-fontify-rest-after-change(BEG END OLD-LEN)
lazy-lock-fontify-window(WINDOW)
lazy-lock-install-hooks(FONTIFYING DEFER-CHANGE DEFER-SCROLL DEFER-CONTEXT)
lazy-lock-install-timers(DTIME STIME)
lazy-lock-mode(&optional ARG)
turn-on-lazy-lock()

Defined faces (0)