Mercurial > emacs
changeset 78602:b86e73d7c635
(reset_var_on_error): New fun.
(signal_before_change, signal_after_change):
Use it to reset (after|before)-change-functions to nil in case of error.
Bind inhibit-modification-hooks to t.
Don't bind (after|before)-change-functions to nil while they run.
author | Stefan Monnier <monnier@iro.umontreal.ca> |
---|---|
date | Tue, 21 Aug 2007 18:22:03 +0000 |
parents | 047d1fc93812 |
children | eb7052b9d8b1 |
files | etc/NEWS lispref/text.texi src/ChangeLog src/insdel.c |
diffstat | 4 files changed, 57 insertions(+), 90 deletions(-) [+] |
line wrap: on
line diff
--- a/etc/NEWS Tue Aug 21 16:25:41 2007 +0000 +++ b/etc/NEWS Tue Aug 21 18:22:03 2007 +0000 @@ -79,6 +79,11 @@ * Lisp Changes in Emacs 22.2. ++++ +** inhibit-modification-hooks is bound to t while running modification hooks. +As a happy consequence, after-change-functions and before-change-functions +are not bound to nil any more while running an (after|before)-change-function. + ** New function `window-full-width-p' returns t if a window is as wide as its frame.
--- a/lispref/text.texi Tue Aug 21 16:25:41 2007 +0000 +++ b/lispref/text.texi Tue Aug 21 18:22:03 2007 +0000 @@ -4364,35 +4364,6 @@ functions. @end defmac -The two variables above are temporarily bound to @code{nil} during the -time that any of these functions is running. This means that if one of -these functions changes the buffer, that change won't run these -functions. If you do want a hook function to make changes that run -these functions, make it bind these variables back to their usual -values. - -One inconvenient result of this protective feature is that you cannot -have a function in @code{after-change-functions} or -@code{before-change-functions} which changes the value of that variable. -But that's not a real limitation. If you want those functions to change -the list of functions to run, simply add one fixed function to the hook, -and code that function to look in another variable for other functions -to call. Here is an example: - -@example -(setq my-own-after-change-functions nil) -(defun indirect-after-change-function (beg end len) - (let ((list my-own-after-change-functions)) - (while list - (funcall (car list) beg end len) - (setq list (cdr list))))) - -@group -(add-hooks 'after-change-functions - 'indirect-after-change-function) -@end group -@end example - @defvar first-change-hook This variable is a normal hook that is run whenever a buffer is changed that was previously in the unmodified state. @@ -4404,6 +4375,13 @@ described above in this section, as well as the hooks attached to certain special text properties (@pxref{Special Properties}) and overlay properties (@pxref{Overlay Properties}). + +Also, this variable is bound to non-@code{nil} while running those +same hook variables, so that by default modifying the buffer from +a modification hook does not cause other modification hooks to be run. +If you do want modification hooks to be run in a particular piece of +code that is itself run from a modification hook, then rebind locally +@code{inhibit-modification-hooks} to @code{nil}. @end defvar @ignore
--- a/src/ChangeLog Tue Aug 21 16:25:41 2007 +0000 +++ b/src/ChangeLog Tue Aug 21 18:22:03 2007 +0000 @@ -1,3 +1,11 @@ +2007-08-21 Stefan Monnier <monnier@iro.umontreal.ca> + + * insdel.c (reset_var_on_error): New fun. + (signal_before_change, signal_after_change): + Use it to reset (after|before)-change-functions to nil in case of error. + Bind inhibit-modification-hooks to t. + Don't bind (after|before)-change-functions to nil while they run. + 2007-08-19 Andreas Schwab <schwab@suse.de> * alloc.c (pure): Round PURESIZE up.
--- a/src/insdel.c Tue Aug 21 16:25:41 2007 +0000 +++ b/src/insdel.c Tue Aug 21 18:22:03 2007 +0000 @@ -2137,6 +2137,21 @@ #define FETCH_END \ (! NILP (end_marker) ? Fmarker_position (end_marker) : end) +/* Set a variable to nil if an error occurred. + Don't change the variable if there was no error. + VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG). + VARIABLE is the variable to maybe set to nil. + NO-ERROR-FLAG is nil if there was an error, + anything else meaning no error (so this function does nothing). */ +Lisp_Object +reset_var_on_error (val) + Lisp_Object val; +{ + if (NILP (XCDR (val))) + Fset (XCAR (val), Qnil); + return Qnil; +} + /* Signal a change to the buffer immediately before it happens. START_INT and END_INT are the bounds of the text to be changed. @@ -2152,6 +2167,7 @@ Lisp_Object start_marker, end_marker; Lisp_Object preserve_marker; struct gcpro gcpro1, gcpro2, gcpro3; + int count = SPECPDL_INDEX (); if (inhibit_modification_hooks) return; @@ -2163,6 +2179,8 @@ end_marker = Qnil; GCPRO3 (preserve_marker, start_marker, end_marker); + specbind (Qinhibit_modification_hooks, Qt); + /* If buffer is unmodified, run a special hook for that case. */ if (SAVE_MODIFF >= MODIFF && !NILP (Vfirst_change_hook) @@ -2177,46 +2195,22 @@ if (!NILP (Vbefore_change_functions)) { Lisp_Object args[3]; - Lisp_Object before_change_functions; - Lisp_Object after_change_functions; - struct gcpro gcpro1, gcpro2; - struct buffer *old = current_buffer; - struct buffer *new; + Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil); PRESERVE_VALUE; PRESERVE_START_END; - /* "Bind" before-change-functions and after-change-functions - to nil--but in a way that errors don't know about. - That way, if there's an error in them, they will stay nil. */ - before_change_functions = Vbefore_change_functions; - after_change_functions = Vafter_change_functions; - Vbefore_change_functions = Qnil; - Vafter_change_functions = Qnil; - GCPRO2 (before_change_functions, after_change_functions); + /* Mark before-change-functions to be reset to nil in case of error. */ + record_unwind_protect (reset_var_on_error, rvoe_arg); /* Actually run the hook functions. */ args[0] = Qbefore_change_functions; args[1] = FETCH_START; args[2] = FETCH_END; - run_hook_list_with_args (before_change_functions, 3, args); + Frun_hook_with_args (3, args); - /* "Unbind" the variables we "bound" to nil. Beware a - buffer-local hook which changes the buffer when run (e.g. W3). */ - if (old != current_buffer) - { - new = current_buffer; - set_buffer_internal (old); - Vbefore_change_functions = before_change_functions; - Vafter_change_functions = after_change_functions; - set_buffer_internal (new); - } - else - { - Vbefore_change_functions = before_change_functions; - Vafter_change_functions = after_change_functions; - } - UNGCPRO; + /* There was no error: unarm the reset_on_error. */ + XSETCDR (rvoe_arg, Qt); } if (current_buffer->overlays_before || current_buffer->overlays_after) @@ -2232,6 +2226,8 @@ free_marker (end_marker); RESTORE_VALUE; UNGCPRO; + + unbind_to (count, Qnil); } /* Signal a change immediately after it happens. @@ -2245,6 +2241,7 @@ signal_after_change (charpos, lendel, lenins) int charpos, lendel, lenins; { + int count = SPECPDL_INDEX (); if (inhibit_modification_hooks) return; @@ -2275,48 +2272,25 @@ if (!NILP (combine_after_change_list)) Fcombine_after_change_execute (); + specbind (Qinhibit_modification_hooks, Qt); + if (!NILP (Vafter_change_functions)) { Lisp_Object args[4]; - Lisp_Object before_change_functions; - Lisp_Object after_change_functions; - struct buffer *old = current_buffer; - struct buffer *new; - struct gcpro gcpro1, gcpro2; + Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil); - /* "Bind" before-change-functions and after-change-functions - to nil--but in a way that errors don't know about. - That way, if there's an error in them, they will stay nil. */ - before_change_functions = Vbefore_change_functions; - after_change_functions = Vafter_change_functions; - Vbefore_change_functions = Qnil; - Vafter_change_functions = Qnil; - GCPRO2 (before_change_functions, after_change_functions); + /* Mark after-change-functions to be reset to nil in case of error. */ + record_unwind_protect (reset_var_on_error, rvoe_arg); /* Actually run the hook functions. */ args[0] = Qafter_change_functions; XSETFASTINT (args[1], charpos); XSETFASTINT (args[2], charpos + lenins); XSETFASTINT (args[3], lendel); - run_hook_list_with_args (after_change_functions, - 4, args); + Frun_hook_with_args (4, args); - /* "Unbind" the variables we "bound" to nil. Beware a - buffer-local hook which changes the buffer when run (e.g. W3). */ - if (old != current_buffer) - { - new = current_buffer; - set_buffer_internal (old); - Vbefore_change_functions = before_change_functions; - Vafter_change_functions = after_change_functions; - set_buffer_internal (new); - } - else - { - Vbefore_change_functions = before_change_functions; - Vafter_change_functions = after_change_functions; - } - UNGCPRO; + /* There was no error: unarm the reset_on_error. */ + XSETCDR (rvoe_arg, Qt); } if (current_buffer->overlays_before || current_buffer->overlays_after) @@ -2332,6 +2306,8 @@ if (lendel == 0) report_interval_modification (make_number (charpos), make_number (charpos + lenins)); + + unbind_to (count, Qnil); } Lisp_Object