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-contextually | If non-nil, means deferred fontification should be syntactically true. |
lazy-lock-defer-on-scrolling | If non-nil, means fontification after a scroll should be deferred. |
lazy-lock-defer-on-the-fly | If non-nil, means fontification after a change should be deferred. |
lazy-lock-defer-time | Time in seconds to delay before beginning deferred fontification. |
lazy-lock-minimum-size | Minimum size of a buffer for demand-driven fontification. |
lazy-lock-stealth-lines | Maximum size of a chunk of stealth fontification. |
lazy-lock-stealth-load | Load in percentage above which stealth fontification is suspended. |
lazy-lock-stealth-nice | Time in seconds to pause between chunks of stealth fontification. |
lazy-lock-stealth-time | Time in seconds to delay before beginning stealth fontification. |
lazy-lock-stealth-verbose | If 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 | () |