/* Buffer manipulation primitives for GNU Emacs.
- Copyright (C) 1985, 1986, 1987, 1988, 1989, 1993, 1994, 1995, 1997, 1998
+ Copyright (C) 1985,86,87,88,89,93,94,95,97,98, 1999
Free Software Foundation, Inc.
This file is part of GNU Emacs.
Lisp_Object Qbefore_change_functions;
Lisp_Object Qafter_change_functions;
+/* If nonzero, all modification hooks are suppressed. */
+int inhibit_modification_hooks;
+
Lisp_Object Qfundamental_mode, Qmode_class, Qpermanent_local;
Lisp_Object Qprotected_field;
BUF_OVERLAY_MODIFF (b) = 1;
BUF_SAVE_MODIFF (b) = 1;
BUF_INTERVALS (b) = 0;
+ BUF_UNCHANGED_MODIFIED (b) = 1;
+ BUF_OVERLAY_UNCHANGED_MODIFIED (b) = 1;
+ BUF_END_UNCHANGED (b) = 0;
+ BUF_BEG_UNCHANGED (b) = 0;
*(BUF_GPT_ADDR (b)) = *(BUF_Z_ADDR (b)) = 0; /* Put an anchor '\0'. */
b->newline_cache = 0;
b->width_run_cache = 0;
b->width_table = Qnil;
+ b->minibuffer_prompt_length = Qnil;
+ b->prevent_redisplay_optimizations_p = 1;
/* Put this on the chain of all buffers including killed ones. */
b->next = all_buffers;
b->newline_cache = 0;
b->width_run_cache = 0;
b->width_table = Qnil;
+ b->minibuffer_prompt_length = Qnil;
/* Put this on the chain of all buffers including killed ones. */
b->next = all_buffers;
b->mark = Fmake_marker ();
b->name = name;
+ /* The multibyte status belongs to the base buffer. */
+ b->enable_multibyte_characters = b->base_buffer->enable_multibyte_characters;
+
/* Make sure the base buffer has markers for its narrowing. */
if (NILP (b->base_buffer->pt_marker))
{
XSETFASTINT (b->save_length, 0);
b->last_window_start = 1;
/* It is more conservative to start out "changed" than "unchanged". */
- b->clip_changed = 1;
+ b->clip_changed = 0;
+ b->prevent_redisplay_optimizations_p = 1;
b->backed_up = Qnil;
b->auto_save_modified = 0;
b->auto_save_failure_time = -1;
If buffer becoming unmodified, unlock the file. */
fn = current_buffer->file_truename;
- if (!NILP (fn))
+ /* Test buffer-file-name so that binding it to nil is effective. */
+ if (!NILP (fn) && ! NILP (current_buffer->filename))
{
already = SAVE_MODIFF < MODIFF;
if (!already && !NILP (flag))
return current_buffer->name;
}
-DEFUN ("other-buffer", Fother_buffer, Sother_buffer, 0, 2, 0,
+DEFUN ("other-buffer", Fother_buffer, Sother_buffer, 0, 3, 0,
"Return most recently selected buffer other than BUFFER.\n\
Buffers not visible in windows are preferred to visible buffers,\n\
unless optional second argument VISIBLE-OK is non-nil.\n\
+If the optional third argument FRAME is non-nil, use that frame's\n\
+buffer list instead of the selected frame's buffer list.\n\
If no other buffer exists, the buffer `*scratch*' is returned.\n\
If BUFFER is omitted or nil, some interesting buffer is returned.")
- (buffer, visible_ok)
- register Lisp_Object buffer, visible_ok;
+ (buffer, visible_ok, frame)
+ register Lisp_Object buffer, visible_ok, frame;
{
Lisp_Object Fset_buffer_major_mode ();
register Lisp_Object tail, buf, notsogood, tem, pred, add_ons;
notsogood = Qnil;
+ if (NILP (frame))
+ frame = Fselected_frame ();
+
tail = Vbuffer_alist;
- pred = frame_buffer_predicate ();
+ pred = frame_buffer_predicate (frame);
/* Consider buffers that have been seen in the selected frame
before other buffers. */
- tem = frame_buffer_list ();
+ tem = frame_buffer_list (frame);
add_ons = Qnil;
while (CONSP (tem))
{
and give up if so. */
if (b == current_buffer)
{
- tem = Fother_buffer (buf, Qnil);
+ tem = Fother_buffer (buf, Qnil, Qnil);
Fset_buffer (tem);
if (b == current_buffer)
return Qnil;
b->width_run_cache = 0;
}
b->width_table = Qnil;
+ b->minibuffer_prompt_length = Qnil;
UNBLOCK_INPUT;
b->undo_list = Qnil;
Lisp_Object buf;
{
register Lisp_Object link, prev;
+ Lisp_Object frame;
+ frame = Fselected_frame ();
prev = Qnil;
for (link = Vbuffer_alist; CONSP (link); link = XCONS (link)->cdr)
/* Now move this buffer to the front of frame_buffer_list also. */
prev = Qnil;
- for (link = frame_buffer_list (); CONSP (link); link = XCONS (link)->cdr)
+ for (link = frame_buffer_list (frame); CONSP (link);
+ link = XCONS (link)->cdr)
{
if (EQ (XCONS (link)->car, buf))
break;
if (CONSP (link))
{
if (NILP (prev))
- set_frame_buffer_list (XCONS (frame_buffer_list ())->cdr);
+ set_frame_buffer_list (frame,
+ XCONS (frame_buffer_list (frame))->cdr);
else
XCONS (prev)->cdr = XCONS (XCONS (prev)->cdr)->cdr;
- XCONS (link)->cdr = frame_buffer_list ();
- set_frame_buffer_list (link);
+ XCONS (link)->cdr = frame_buffer_list (frame);
+ set_frame_buffer_list (frame, link);
}
else
- set_frame_buffer_list (Fcons (buf, frame_buffer_list ()));
+ set_frame_buffer_list (frame, Fcons (buf, frame_buffer_list (frame)));
}
DEFUN ("set-buffer-major-mode", Fset_buffer_major_mode, Sset_buffer_major_mode, 1, 1, 0,
return unbind_to (count, Qnil);
}
-DEFUN ("switch-to-buffer", Fswitch_to_buffer, Sswitch_to_buffer, 1, 2, "BSwitch to buffer: ",
- "Select buffer BUFFER in the current window.\n\
-BUFFER may be a buffer or a buffer name.\n\
-Optional second arg NORECORD non-nil means\n\
-do not put this buffer at the front of the list of recently selected ones.\n\
-\n\
-WARNING: This is NOT the way to work on another buffer temporarily\n\
-within a Lisp program! Use `set-buffer' instead. That avoids messing with\n\
-the window-buffer correspondences.")
- (buffer, norecord)
- Lisp_Object buffer, norecord;
+/* If switching buffers in WINDOW would be an error, return
+ a C string saying what the error would be. */
+
+char *
+no_switch_window (window)
+ Lisp_Object window;
{
- register Lisp_Object buf;
Lisp_Object tem;
-
- if (EQ (minibuf_window, selected_window))
- error ("Cannot switch buffers in minibuffer window");
- tem = Fwindow_dedicated_p (selected_window);
+ if (EQ (minibuf_window, window))
+ return "Cannot switch buffers in minibuffer window";
+ tem = Fwindow_dedicated_p (window);
if (!NILP (tem))
- error ("Cannot switch buffers in a dedicated window");
+ return "Cannot switch buffers in a dedicated window";
+ return NULL;
+}
+
+/* Switch to buffer BUFFER in the selected window.
+ If NORECORD is non-nil, don't call record_buffer. */
+
+Lisp_Object
+switch_to_buffer_1 (buffer, norecord)
+ Lisp_Object buffer, norecord;
+{
+ register Lisp_Object buf;
if (NILP (buffer))
- buf = Fother_buffer (Fcurrent_buffer (), Qnil);
+ buf = Fother_buffer (Fcurrent_buffer (), Qnil, Qnil);
else
{
buf = Fget_buffer (buffer);
return buf;
}
+DEFUN ("switch-to-buffer", Fswitch_to_buffer, Sswitch_to_buffer, 1, 2, "BSwitch to buffer: ",
+ "Select buffer BUFFER in the current window.\n\
+BUFFER may be a buffer or a buffer name.\n\
+Optional second arg NORECORD non-nil means\n\
+do not put this buffer at the front of the list of recently selected ones.\n\
+\n\
+WARNING: This is NOT the way to work on another buffer temporarily\n\
+within a Lisp program! Use `set-buffer' instead. That avoids messing with\n\
+the window-buffer correspondences.")
+ (buffer, norecord)
+ Lisp_Object buffer, norecord;
+{
+ char *err;
+
+ err = no_switch_window (selected_window);
+ if (err) error (err);
+
+ return switch_to_buffer_1 (buffer, norecord);
+}
+
DEFUN ("pop-to-buffer", Fpop_to_buffer, Spop_to_buffer, 1, 3, 0,
"Select buffer BUFFER in some window, preferably a different one.\n\
If BUFFER is nil, then some other buffer is chosen.\n\
{
register Lisp_Object buf;
if (NILP (buffer))
- buf = Fother_buffer (Fcurrent_buffer (), Qnil);
+ buf = Fother_buffer (Fcurrent_buffer (), Qnil, Qnil);
else
{
buf = Fget_buffer (buffer);
set_buffer_internal (b)
register struct buffer *b;
{
- register struct buffer *old_buf;
- register Lisp_Object tail, valcontents;
- Lisp_Object tem;
-
- if (current_buffer == b)
- return;
-
- windows_or_buffers_changed = 1;
- set_buffer_internal_1 (b);
+ if (current_buffer != b)
+ {
+ /* Set windows_or_buffers_changed only if buffer is displayed
+ somewhere. This enables redisplay optimizations if a
+ background task like deferred fontification changes buffers,
+ but none that are currently displayed. */
+ if (!windows_or_buffers_changed
+ && selected_frame)
+ {
+ Lisp_Object buffer;
+ XSETBUFFER (buffer, b);
+ if (!NILP (Fget_buffer_window (buffer, Qvisible)))
+ ++windows_or_buffers_changed;
+ }
+
+ set_buffer_internal_1 (b);
+ }
}
/* Set the current buffer to B, and do not set windows_or_buffers_changed.
XSETBUFFER (buffer, current_buffer);
/* If we're burying the current buffer, unshow it. */
- Fswitch_to_buffer (Fother_buffer (buffer, Qnil), Qnil);
+ Fswitch_to_buffer (Fother_buffer (buffer, Qnil, Qnil), Qnil);
}
else
{
Lisp_Object flag;
{
Lisp_Object tail, markers;
+ struct buffer *other;
+
+ if (current_buffer->base_buffer)
+ error ("Cannot do `set-buffer-multibyte' on an indirect buffer");
/* Do nothing if nothing actually changes. */
if (NILP (flag) == NILP (current_buffer->enable_multibyte_characters))
set_intervals_multibyte (1);
}
+ /* Copy this buffer's new multibyte status
+ into all of its indirect buffers. */
+ for (other = all_buffers; other; other = other->next)
+ if (other->base_buffer == current_buffer && !NILP (other->name))
+ other->enable_multibyte_characters
+ = current_buffer->enable_multibyte_characters;
+
return flag;
}
\f
Store in *NEXT_PTR the next position after POS where an overlay starts,
or ZV if there are no more overlays.
Store in *PREV_PTR the previous position before POS where an overlay ends,
- or BEGV if there are no previous overlays.
+ or where an overlay starts which ends at or after POS;
+ or BEGV if there are no such overlays.
NEXT_PTR and/or PREV_PTR may be 0, meaning don't store that info.
*VEC_PTR and *LEN_PTR should contain a valid vector and size
prev = endpos;
break;
}
+ startpos = OVERLAY_POSITION (start);
+ /* This one ends at or after POS
+ so its start counts for PREV_PTR if it's before POS. */
+ if (prev < startpos && startpos < pos)
+ prev = startpos;
if (endpos == pos)
continue;
- startpos = OVERLAY_POSITION (start);
if (startpos <= pos)
{
if (idx == len)
Either make it bigger, or don't store any more in it. */
if (extend)
{
- *len_ptr = len *= 2;
+ /* Make it work with an initial len == 0. */
+ len *= 2;
+ if (len == 0)
+ len = 4;
+ *len_ptr = len;
vec = (Lisp_Object *) xrealloc (vec, len * sizeof (Lisp_Object));
*vec_ptr = vec;
}
if (extend)
{
*len_ptr = len *= 2;
+ if (len == 0)
+ len = *len_ptr = 4;
vec = (Lisp_Object *) xrealloc (vec, len * sizeof (Lisp_Object));
*vec_ptr = vec;
}
if (!inhibit_storing)
vec[idx] = overlay;
idx++;
+
+ if (startpos < pos && startpos > prev)
+ prev = startpos;
}
else if (endpos < pos && endpos > prev)
prev = endpos;
+ else if (endpos == pos && startpos > prev)
+ prev = startpos;
}
if (next_ptr)
we must do other windows. */
if (buf != XBUFFER (XWINDOW (selected_window)->buffer))
windows_or_buffers_changed = 1;
- /* If it's not current, we can't use beg_unchanged, end_unchanged for it. */
- else if (buf != current_buffer)
- windows_or_buffers_changed = 1;
/* If multiple windows show this buffer, we must do other windows. */
else if (buffer_shared > 1)
windows_or_buffers_changed = 1;
else
- {
- if (unchanged_modified == MODIFF
- && overlay_unchanged_modified == OVERLAY_MODIFF)
- {
- beg_unchanged = start - BEG;
- end_unchanged = Z - end;
- }
- else
- {
- if (Z - end < end_unchanged)
- end_unchanged = Z - end;
- if (start - BEG < beg_unchanged)
- beg_unchanged = start - BEG;
- }
- }
+ BUF_COMPUTE_UNCHANGED (buf, start, end);
++BUF_OVERLAY_MODIFF (buf);
}
/* Put all the overlays we want in a vector in overlay_vec.
Store the length in len.
- prevpos gets the position of an overlay end. */
+ prevpos gets the position of the previous change. */
noverlays = overlays_at (XINT (pos), 1, &overlay_vec, &len,
(int *) 0, &prevpos);
- /* If any of these overlays starts after prevpos,
- maybe use its starting point instead. */
- for (i = 0; i < noverlays; i++)
- {
- Lisp_Object ostart;
- int ostartpos;
-
- ostart = OVERLAY_START (overlay_vec[i]);
- ostartpos = OVERLAY_POSITION (ostart);
- if (ostartpos > prevpos && ostartpos < XINT (pos))
- prevpos = ostartpos;
- }
-
- /* If any overlay ends at pos, consider its starting point too. */
- for (tail = current_buffer->overlays_before;
- GC_CONSP (tail);
- tail = XCONS (tail)->cdr)
- {
- Lisp_Object overlay, ostart;
- int ostartpos;
-
- overlay = XCONS (tail)->car;
-
- ostart = OVERLAY_START (overlay);
- ostartpos = OVERLAY_POSITION (ostart);
- if (ostartpos > prevpos && ostartpos < XINT (pos))
- prevpos = ostartpos;
- }
-
xfree (overlay_vec);
return make_number (prevpos);
}
/* real setup is done in loaddefs.el */
buffer_defaults.mode_line_format = build_string ("%-");
+ buffer_defaults.top_line_format = Qnil;
buffer_defaults.abbrev_mode = Qnil;
buffer_defaults.overwrite_mode = Qnil;
buffer_defaults.case_fold_search = Qt;
buffer_defaults.cache_long_line_scans = Qnil;
buffer_defaults.file_truename = Qnil;
XSETFASTINT (buffer_defaults.display_count, 0);
+ buffer_defaults.indicate_empty_lines = Qnil;
+ buffer_defaults.scroll_up_aggressively = Qnil;
+ buffer_defaults.scroll_down_aggressively = Qnil;
buffer_defaults.display_time = Qnil;
/* Assign the local-flags to the slots that have default values.
XSETFASTINT (buffer_local_flags.buffer_file_coding_system, 0x80000);
/* Make this one a permanent local. */
buffer_permanent_local_flags |= 0x80000;
+ XSETFASTINT (buffer_local_flags.left_margin_width, 0x100000);
+ XSETFASTINT (buffer_local_flags.right_margin_width, 0x200000);
+ XSETFASTINT (buffer_local_flags.indicate_empty_lines, 0x400000);
+ XSETFASTINT (buffer_local_flags.scroll_up_aggressively, 0x800000);
+ XSETFASTINT (buffer_local_flags.scroll_down_aggressively, 0x1000000);
+ XSETFASTINT (buffer_local_flags.top_line_format, 0x2000000);
Vbuffer_alist = Qnil;
current_buffer = 0;
Vbuffer_alist = Qnil;
Fset_buffer (Fget_buffer_create (build_string ("*scratch*")));
+
+ inhibit_modification_hooks = 0;
}
void
"Default value of `mode-line-format' for buffers that don't override it.\n\
This is the same as (default-value 'mode-line-format).");
+ DEFVAR_LISP_NOPRO ("default-top-line-format",
+ &buffer_defaults.top_line_format,
+ "Default value of `top-line-format' for buffers that don't override it.\n\
+This is the same as (default-value 'top-line-format).");
+
DEFVAR_LISP_NOPRO ("default-abbrev-mode",
&buffer_defaults.abbrev_mode,
"Default value of `abbrev-mode' for buffers that do not override it.\n\
DEFVAR_LISP_NOPRO ("default-enable-multibyte-characters",
&buffer_defaults.enable_multibyte_characters,
- "Default value of `enable-multibyte-characters' for buffers not overriding it.\n\
- This is the same as (default-value 'enable-multibyte-characters).");
+ "*Default value of `enable-multibyte-characters' for buffers not overriding it.\n\
+This is the same as (default-value 'enable-multibyte-characters).");
DEFVAR_LISP_NOPRO ("default-buffer-file-coding-system",
&buffer_defaults.buffer_file_coding_system,
"Default value of `buffer-file-coding-system' for buffers not overriding it.\n\
- This is the same as (default-value 'buffer-file-coding-system).");
+This is the same as (default-value 'buffer-file-coding-system).");
DEFVAR_LISP_NOPRO ("default-truncate-lines",
&buffer_defaults.truncate_lines,
The file type is nil for text, t for binary.");
#endif
+ DEFVAR_LISP_NOPRO ("default-left-margin-width",
+ &buffer_defaults.left_margin_width,
+ "Default value of `left-margin-width' for buffers that don't override it.\n\
+This is the same as (default-value 'left-margin-width).");
+
+ DEFVAR_LISP_NOPRO ("default-right-margin-width",
+ &buffer_defaults.right_margin_width,
+ "Default value of `right_margin_width' for buffers that don't override it.\n\
+This is the same as (default-value 'right-margin-width).");
+
+ DEFVAR_LISP_NOPRO ("default-indicate-empty-lines",
+ &buffer_defaults.indicate_empty_lines,
+ "Default value of `indicate-empty-lines' for buffers that don't override it.\n\
+This is the same as (default-value 'indicate-empty-lines).");
+
+ DEFVAR_LISP_NOPRO ("default-scroll-up-aggressively",
+ &buffer_defaults.scroll_up_aggressively,
+ "Default value of `scroll-up-aggressively' for buffers that\n\
+don't override it. This is the same as (default-value\n\
+'scroll-up-aggressively).");
+
+ DEFVAR_LISP_NOPRO ("default-scroll-down-aggressively",
+ &buffer_defaults.scroll_down_aggressively,
+ "Default value of `scroll-down-aggressively' for buffers that\n\
+don't override it. This is the same as (default-value\n\
+'scroll-down-aggressively).");
+
+ DEFVAR_PER_BUFFER ("top-line-format", ¤t_buffer->top_line_format,
+ Qnil,
+ "Analogous to `mode-line-format', but for a mode line displayed\n\
+at the top of windows.");
+
DEFVAR_PER_BUFFER ("mode-line-format", ¤t_buffer->mode_line_format,
Qnil, 0);
Qnil,
"Template for displaying mode line for current buffer.\n\
Each buffer has its own value of this variable.\n\
-Value may be a string, a symbol or a list or cons cell.\n\
+Value may be nil, a string, a symbol or a list or cons cell.\n\
+A value of nil means don't display a mode line.\n\
For a symbol, its value is used (but it is ignored if t or nil).\n\
A string appearing directly as the value of a symbol is processed verbatim\n\
in that the %-constructs below are not recognized.\n\
DEFVAR_PER_BUFFER ("case-fold-search", ¤t_buffer->case_fold_search,
Qnil,
- "*Non-nil if searches should ignore case.\n\
+ "*Non-nil if searches and matches should ignore case.\n\
Automatically becomes buffer-local when set in any fashion.");
DEFVAR_PER_BUFFER ("fill-column", ¤t_buffer->fill_column,
DEFVAR_PER_BUFFER ("enable-multibyte-characters",
¤t_buffer->enable_multibyte_characters,
make_number (-1),
- "*Non-nil means the buffer contents are regarded as multi-byte form\n\
-of characters, not a binary code. This affects the display, file I/O,\n\
-and behaviors of various editing commands.");
+ "Non-nil means the buffer contents are regarded as multi-byte characters.\n\
+Otherwise they are regarded as unibyte. This affects the display,\n\
+file I/O and the behavior of various editing commands.\n\
+\n\
+This variable is buffer-local but you cannot set it directly;\n\
+use the function `set-buffer-multibyte' to change a buffer's representation.\n\
+Changing its default value with `setq-default' is supported.\n\
+See also variable `default-enable-multibyte-characters' and Info node\n\
+`(elisp)Text Representations'.");
DEFVAR_PER_BUFFER ("buffer-file-coding-system",
¤t_buffer->buffer_file_coding_system, Qnil,
"Coding system to be used for encoding the buffer contents on saving.\n\
-If it is nil, the buffer is saved without any code conversion unless\n\
-some coding system is specified in `file-coding-system-alist'\n\
+This variable applies to saving the buffer, and also to `write-region'\n\
+and other functions that use `write-region'.\n\
+It does not apply to sending output to subprocesses, however.\n\
+\n\
+If this is nil, the buffer is saved without any code conversion\n\
+unless some coding system is specified in `file-coding-system-alist'\n\
for the buffer file.\n\
\n\
+The variable `coding-system-for-write', if non-nil, overrides this variable.\n\
+\n\
This variable is never applied to a way of decoding\n\
a file while reading it.");
DEFVAR_PER_BUFFER ("buffer-display-table", ¤t_buffer->display_table,
Qnil, 0);
+ DEFVAR_PER_BUFFER ("left-margin-width", ¤t_buffer->left_margin_width,
+ Qnil,
+ "*Width of left marginal area for display of a buffer.\n\
+Automatically becomes buffer-local when set in any fashion.\n\
+A value of nil means no marginal area.");
+
+ DEFVAR_PER_BUFFER ("right-margin-width", ¤t_buffer->right_margin_width,
+ Qnil,
+ "*Width of right marginal area for display of a buffer.\n\
+Automatically becomes buffer-local when set in any fashion.\n\
+A value of nil means no marginal area.");
+
+ DEFVAR_PER_BUFFER ("indicate-empty-lines",
+ ¤t_buffer->indicate_empty_lines, Qnil,
+ "*Non-nil means visually indicate lines not displaying text.\n\
+Automatically becomes buffer-local when set in any fashion.\n");
+
+ DEFVAR_PER_BUFFER ("scroll-up-aggressively",
+ ¤t_buffer->scroll_up_aggressively, Qnil,
+ "*If a number, scroll display up aggressively.\n\
+If scrolling a window because point is above the window start, choose\n\
+a new window start so that point ends up that fraction of the window's\n\
+height from the bottom of the window.\n\
+Automatically becomes buffer-local when set in any fashion.");
+
+ DEFVAR_PER_BUFFER ("scroll-down-aggressively",
+ ¤t_buffer->scroll_down_aggressively, Qnil,
+ "*If a number, scroll display down aggressively.\n\
+If scrolling a window because point is below the window end, choose\n\
+a new window start so that point ends up that fraction of the window's\n\
+height from the top of the window.\n\
+Automatically becomes buffer-local when set in any fashion.");
+
/*DEFVAR_LISP ("debug-check-symbol", &Vcheck_symbol,
"Don't ask.");
*/
That's because these variables are temporarily set to nil.\n\
As a result, a hook function cannot straightforwardly alter the value of\n\
these variables. See the Emacs Lisp manual for a way of\n\
-accomplishing an equivalent result by using other variables.");
+accomplishing an equivalent result by using other variables.\n\
+\n\
+If an unhandled error happens in running these functions,\n\
+the variable's value remains nil. That prevents the error\n\
+from happening repeatedly and making Emacs nonfunctional.");
Vbefore_change_functions = Qnil;
DEFVAR_LISP ("after-change-functions", &Vafter_change_functions,
That's because these variables are temporarily set to nil.\n\
As a result, a hook function cannot straightforwardly alter the value of\n\
these variables. See the Emacs Lisp manual for a way of\n\
-accomplishing an equivalent result by using other variables.");
-
+accomplishing an equivalent result by using other variables.\n\
+\n\
+If an unhandled error happens in running these functions,\n\
+the variable's value remains nil. That prevents the error\n\
+from happening repeatedly and making Emacs nonfunctional.");
Vafter_change_functions = Qnil;
DEFVAR_LISP ("first-change-hook", &Vfirst_change_hook,