diff options
| author | Stefan Monnier | 2007-08-21 18:22:03 +0000 |
|---|---|---|
| committer | Stefan Monnier | 2007-08-21 18:22:03 +0000 |
| commit | 97c4ef2a204f2be3db8de5c900046abe09d0582a (patch) | |
| tree | d70157d7cbda014f388718d4992374bc0341e913 | |
| parent | d5dac3b9bd5baa34ebb0506d0cd4460397a03d81 (diff) | |
| download | emacs-97c4ef2a204f2be3db8de5c900046abe09d0582a.tar.gz emacs-97c4ef2a204f2be3db8de5c900046abe09d0582a.zip | |
(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.
| -rw-r--r-- | etc/NEWS | 5 | ||||
| -rw-r--r-- | lispref/text.texi | 36 | ||||
| -rw-r--r-- | src/ChangeLog | 8 | ||||
| -rw-r--r-- | src/insdel.c | 100 |
4 files changed, 58 insertions, 91 deletions
| @@ -79,6 +79,11 @@ in to make it use the scrollbars from the system theme. | |||
| 79 | 79 | ||
| 80 | * Lisp Changes in Emacs 22.2. | 80 | * Lisp Changes in Emacs 22.2. |
| 81 | 81 | ||
| 82 | +++ | ||
| 83 | ** inhibit-modification-hooks is bound to t while running modification hooks. | ||
| 84 | As a happy consequence, after-change-functions and before-change-functions | ||
| 85 | are not bound to nil any more while running an (after|before)-change-function. | ||
| 86 | |||
| 82 | ** New function `window-full-width-p' returns t if a window is as wide | 87 | ** New function `window-full-width-p' returns t if a window is as wide |
| 83 | as its frame. | 88 | as its frame. |
| 84 | 89 | ||
diff --git a/lispref/text.texi b/lispref/text.texi index 4448e1cc402..bad68b7f8ae 100644 --- a/lispref/text.texi +++ b/lispref/text.texi | |||
| @@ -4364,35 +4364,6 @@ because it may lead to inefficient behavior for some change hook | |||
| 4364 | functions. | 4364 | functions. |
| 4365 | @end defmac | 4365 | @end defmac |
| 4366 | 4366 | ||
| 4367 | The two variables above are temporarily bound to @code{nil} during the | ||
| 4368 | time that any of these functions is running. This means that if one of | ||
| 4369 | these functions changes the buffer, that change won't run these | ||
| 4370 | functions. If you do want a hook function to make changes that run | ||
| 4371 | these functions, make it bind these variables back to their usual | ||
| 4372 | values. | ||
| 4373 | |||
| 4374 | One inconvenient result of this protective feature is that you cannot | ||
| 4375 | have a function in @code{after-change-functions} or | ||
| 4376 | @code{before-change-functions} which changes the value of that variable. | ||
| 4377 | But that's not a real limitation. If you want those functions to change | ||
| 4378 | the list of functions to run, simply add one fixed function to the hook, | ||
| 4379 | and code that function to look in another variable for other functions | ||
| 4380 | to call. Here is an example: | ||
| 4381 | |||
| 4382 | @example | ||
| 4383 | (setq my-own-after-change-functions nil) | ||
| 4384 | (defun indirect-after-change-function (beg end len) | ||
| 4385 | (let ((list my-own-after-change-functions)) | ||
| 4386 | (while list | ||
| 4387 | (funcall (car list) beg end len) | ||
| 4388 | (setq list (cdr list))))) | ||
| 4389 | |||
| 4390 | @group | ||
| 4391 | (add-hooks 'after-change-functions | ||
| 4392 | 'indirect-after-change-function) | ||
| 4393 | @end group | ||
| 4394 | @end example | ||
| 4395 | |||
| 4396 | @defvar first-change-hook | 4367 | @defvar first-change-hook |
| 4397 | This variable is a normal hook that is run whenever a buffer is changed | 4368 | This variable is a normal hook that is run whenever a buffer is changed |
| 4398 | that was previously in the unmodified state. | 4369 | that was previously in the unmodified state. |
| @@ -4404,6 +4375,13 @@ disabled; none of them run. This affects all the hook variables | |||
| 4404 | described above in this section, as well as the hooks attached to | 4375 | described above in this section, as well as the hooks attached to |
| 4405 | certain special text properties (@pxref{Special Properties}) and overlay | 4376 | certain special text properties (@pxref{Special Properties}) and overlay |
| 4406 | properties (@pxref{Overlay Properties}). | 4377 | properties (@pxref{Overlay Properties}). |
| 4378 | |||
| 4379 | Also, this variable is bound to non-@code{nil} while running those | ||
| 4380 | same hook variables, so that by default modifying the buffer from | ||
| 4381 | a modification hook does not cause other modification hooks to be run. | ||
| 4382 | If you do want modification hooks to be run in a particular piece of | ||
| 4383 | code that is itself run from a modification hook, then rebind locally | ||
| 4384 | @code{inhibit-modification-hooks} to @code{nil}. | ||
| 4407 | @end defvar | 4385 | @end defvar |
| 4408 | 4386 | ||
| 4409 | @ignore | 4387 | @ignore |
diff --git a/src/ChangeLog b/src/ChangeLog index add0663a648..bf015d6959f 100644 --- a/src/ChangeLog +++ b/src/ChangeLog | |||
| @@ -1,3 +1,11 @@ | |||
| 1 | 2007-08-21 Stefan Monnier <monnier@iro.umontreal.ca> | ||
| 2 | |||
| 3 | * insdel.c (reset_var_on_error): New fun. | ||
| 4 | (signal_before_change, signal_after_change): | ||
| 5 | Use it to reset (after|before)-change-functions to nil in case of error. | ||
| 6 | Bind inhibit-modification-hooks to t. | ||
| 7 | Don't bind (after|before)-change-functions to nil while they run. | ||
| 8 | |||
| 1 | 2007-08-19 Andreas Schwab <schwab@suse.de> | 9 | 2007-08-19 Andreas Schwab <schwab@suse.de> |
| 2 | 10 | ||
| 3 | * alloc.c (pure): Round PURESIZE up. | 11 | * alloc.c (pure): Round PURESIZE up. |
diff --git a/src/insdel.c b/src/insdel.c index 14e7478e69a..cd8e2738f9a 100644 --- a/src/insdel.c +++ b/src/insdel.c | |||
| @@ -2137,6 +2137,21 @@ prepare_to_modify_buffer (start, end, preserve_ptr) | |||
| 2137 | #define FETCH_END \ | 2137 | #define FETCH_END \ |
| 2138 | (! NILP (end_marker) ? Fmarker_position (end_marker) : end) | 2138 | (! NILP (end_marker) ? Fmarker_position (end_marker) : end) |
| 2139 | 2139 | ||
| 2140 | /* Set a variable to nil if an error occurred. | ||
| 2141 | Don't change the variable if there was no error. | ||
| 2142 | VAL is a cons-cell (VARIABLE . NO-ERROR-FLAG). | ||
| 2143 | VARIABLE is the variable to maybe set to nil. | ||
| 2144 | NO-ERROR-FLAG is nil if there was an error, | ||
| 2145 | anything else meaning no error (so this function does nothing). */ | ||
| 2146 | Lisp_Object | ||
| 2147 | reset_var_on_error (val) | ||
| 2148 | Lisp_Object val; | ||
| 2149 | { | ||
| 2150 | if (NILP (XCDR (val))) | ||
| 2151 | Fset (XCAR (val), Qnil); | ||
| 2152 | return Qnil; | ||
| 2153 | } | ||
| 2154 | |||
| 2140 | /* Signal a change to the buffer immediately before it happens. | 2155 | /* Signal a change to the buffer immediately before it happens. |
| 2141 | START_INT and END_INT are the bounds of the text to be changed. | 2156 | START_INT and END_INT are the bounds of the text to be changed. |
| 2142 | 2157 | ||
| @@ -2152,6 +2167,7 @@ signal_before_change (start_int, end_int, preserve_ptr) | |||
| 2152 | Lisp_Object start_marker, end_marker; | 2167 | Lisp_Object start_marker, end_marker; |
| 2153 | Lisp_Object preserve_marker; | 2168 | Lisp_Object preserve_marker; |
| 2154 | struct gcpro gcpro1, gcpro2, gcpro3; | 2169 | struct gcpro gcpro1, gcpro2, gcpro3; |
| 2170 | int count = SPECPDL_INDEX (); | ||
| 2155 | 2171 | ||
| 2156 | if (inhibit_modification_hooks) | 2172 | if (inhibit_modification_hooks) |
| 2157 | return; | 2173 | return; |
| @@ -2163,6 +2179,8 @@ signal_before_change (start_int, end_int, preserve_ptr) | |||
| 2163 | end_marker = Qnil; | 2179 | end_marker = Qnil; |
| 2164 | GCPRO3 (preserve_marker, start_marker, end_marker); | 2180 | GCPRO3 (preserve_marker, start_marker, end_marker); |
| 2165 | 2181 | ||
| 2182 | specbind (Qinhibit_modification_hooks, Qt); | ||
| 2183 | |||
| 2166 | /* If buffer is unmodified, run a special hook for that case. */ | 2184 | /* If buffer is unmodified, run a special hook for that case. */ |
| 2167 | if (SAVE_MODIFF >= MODIFF | 2185 | if (SAVE_MODIFF >= MODIFF |
| 2168 | && !NILP (Vfirst_change_hook) | 2186 | && !NILP (Vfirst_change_hook) |
| @@ -2177,46 +2195,22 @@ signal_before_change (start_int, end_int, preserve_ptr) | |||
| 2177 | if (!NILP (Vbefore_change_functions)) | 2195 | if (!NILP (Vbefore_change_functions)) |
| 2178 | { | 2196 | { |
| 2179 | Lisp_Object args[3]; | 2197 | Lisp_Object args[3]; |
| 2180 | Lisp_Object before_change_functions; | 2198 | Lisp_Object rvoe_arg = Fcons (Qbefore_change_functions, Qnil); |
| 2181 | Lisp_Object after_change_functions; | ||
| 2182 | struct gcpro gcpro1, gcpro2; | ||
| 2183 | struct buffer *old = current_buffer; | ||
| 2184 | struct buffer *new; | ||
| 2185 | 2199 | ||
| 2186 | PRESERVE_VALUE; | 2200 | PRESERVE_VALUE; |
| 2187 | PRESERVE_START_END; | 2201 | PRESERVE_START_END; |
| 2188 | 2202 | ||
| 2189 | /* "Bind" before-change-functions and after-change-functions | 2203 | /* Mark before-change-functions to be reset to nil in case of error. */ |
| 2190 | to nil--but in a way that errors don't know about. | 2204 | record_unwind_protect (reset_var_on_error, rvoe_arg); |
| 2191 | That way, if there's an error in them, they will stay nil. */ | ||
| 2192 | before_change_functions = Vbefore_change_functions; | ||
| 2193 | after_change_functions = Vafter_change_functions; | ||
| 2194 | Vbefore_change_functions = Qnil; | ||
| 2195 | Vafter_change_functions = Qnil; | ||
| 2196 | GCPRO2 (before_change_functions, after_change_functions); | ||
| 2197 | 2205 | ||
| 2198 | /* Actually run the hook functions. */ | 2206 | /* Actually run the hook functions. */ |
| 2199 | args[0] = Qbefore_change_functions; | 2207 | args[0] = Qbefore_change_functions; |
| 2200 | args[1] = FETCH_START; | 2208 | args[1] = FETCH_START; |
| 2201 | args[2] = FETCH_END; | 2209 | args[2] = FETCH_END; |
| 2202 | run_hook_list_with_args (before_change_functions, 3, args); | 2210 | Frun_hook_with_args (3, args); |
| 2203 | 2211 | ||
| 2204 | /* "Unbind" the variables we "bound" to nil. Beware a | 2212 | /* There was no error: unarm the reset_on_error. */ |
| 2205 | buffer-local hook which changes the buffer when run (e.g. W3). */ | 2213 | XSETCDR (rvoe_arg, Qt); |
| 2206 | if (old != current_buffer) | ||
| 2207 | { | ||
| 2208 | new = current_buffer; | ||
| 2209 | set_buffer_internal (old); | ||
| 2210 | Vbefore_change_functions = before_change_functions; | ||
| 2211 | Vafter_change_functions = after_change_functions; | ||
| 2212 | set_buffer_internal (new); | ||
| 2213 | } | ||
| 2214 | else | ||
| 2215 | { | ||
| 2216 | Vbefore_change_functions = before_change_functions; | ||
| 2217 | Vafter_change_functions = after_change_functions; | ||
| 2218 | } | ||
| 2219 | UNGCPRO; | ||
| 2220 | } | 2214 | } |
| 2221 | 2215 | ||
| 2222 | if (current_buffer->overlays_before || current_buffer->overlays_after) | 2216 | if (current_buffer->overlays_before || current_buffer->overlays_after) |
| @@ -2232,6 +2226,8 @@ signal_before_change (start_int, end_int, preserve_ptr) | |||
| 2232 | free_marker (end_marker); | 2226 | free_marker (end_marker); |
| 2233 | RESTORE_VALUE; | 2227 | RESTORE_VALUE; |
| 2234 | UNGCPRO; | 2228 | UNGCPRO; |
| 2229 | |||
| 2230 | unbind_to (count, Qnil); | ||
| 2235 | } | 2231 | } |
| 2236 | 2232 | ||
| 2237 | /* Signal a change immediately after it happens. | 2233 | /* Signal a change immediately after it happens. |
| @@ -2245,6 +2241,7 @@ void | |||
| 2245 | signal_after_change (charpos, lendel, lenins) | 2241 | signal_after_change (charpos, lendel, lenins) |
| 2246 | int charpos, lendel, lenins; | 2242 | int charpos, lendel, lenins; |
| 2247 | { | 2243 | { |
| 2244 | int count = SPECPDL_INDEX (); | ||
| 2248 | if (inhibit_modification_hooks) | 2245 | if (inhibit_modification_hooks) |
| 2249 | return; | 2246 | return; |
| 2250 | 2247 | ||
| @@ -2275,48 +2272,25 @@ signal_after_change (charpos, lendel, lenins) | |||
| 2275 | if (!NILP (combine_after_change_list)) | 2272 | if (!NILP (combine_after_change_list)) |
| 2276 | Fcombine_after_change_execute (); | 2273 | Fcombine_after_change_execute (); |
| 2277 | 2274 | ||
| 2275 | specbind (Qinhibit_modification_hooks, Qt); | ||
| 2276 | |||
| 2278 | if (!NILP (Vafter_change_functions)) | 2277 | if (!NILP (Vafter_change_functions)) |
| 2279 | { | 2278 | { |
| 2280 | Lisp_Object args[4]; | 2279 | Lisp_Object args[4]; |
| 2281 | Lisp_Object before_change_functions; | 2280 | Lisp_Object rvoe_arg = Fcons (Qafter_change_functions, Qnil); |
| 2282 | Lisp_Object after_change_functions; | 2281 | |
| 2283 | struct buffer *old = current_buffer; | 2282 | /* Mark after-change-functions to be reset to nil in case of error. */ |
| 2284 | struct buffer *new; | 2283 | record_unwind_protect (reset_var_on_error, rvoe_arg); |
| 2285 | struct gcpro gcpro1, gcpro2; | ||
| 2286 | |||
| 2287 | /* "Bind" before-change-functions and after-change-functions | ||
| 2288 | to nil--but in a way that errors don't know about. | ||
| 2289 | That way, if there's an error in them, they will stay nil. */ | ||
| 2290 | before_change_functions = Vbefore_change_functions; | ||
| 2291 | after_change_functions = Vafter_change_functions; | ||
| 2292 | Vbefore_change_functions = Qnil; | ||
| 2293 | Vafter_change_functions = Qnil; | ||
| 2294 | GCPRO2 (before_change_functions, after_change_functions); | ||
| 2295 | 2284 | ||
| 2296 | /* Actually run the hook functions. */ | 2285 | /* Actually run the hook functions. */ |
| 2297 | args[0] = Qafter_change_functions; | 2286 | args[0] = Qafter_change_functions; |
| 2298 | XSETFASTINT (args[1], charpos); | 2287 | XSETFASTINT (args[1], charpos); |
| 2299 | XSETFASTINT (args[2], charpos + lenins); | 2288 | XSETFASTINT (args[2], charpos + lenins); |
| 2300 | XSETFASTINT (args[3], lendel); | 2289 | XSETFASTINT (args[3], lendel); |
| 2301 | run_hook_list_with_args (after_change_functions, | 2290 | Frun_hook_with_args (4, args); |
| 2302 | 4, args); | ||
| 2303 | 2291 | ||
| 2304 | /* "Unbind" the variables we "bound" to nil. Beware a | 2292 | /* There was no error: unarm the reset_on_error. */ |
| 2305 | buffer-local hook which changes the buffer when run (e.g. W3). */ | 2293 | XSETCDR (rvoe_arg, Qt); |
| 2306 | if (old != current_buffer) | ||
| 2307 | { | ||
| 2308 | new = current_buffer; | ||
| 2309 | set_buffer_internal (old); | ||
| 2310 | Vbefore_change_functions = before_change_functions; | ||
| 2311 | Vafter_change_functions = after_change_functions; | ||
| 2312 | set_buffer_internal (new); | ||
| 2313 | } | ||
| 2314 | else | ||
| 2315 | { | ||
| 2316 | Vbefore_change_functions = before_change_functions; | ||
| 2317 | Vafter_change_functions = after_change_functions; | ||
| 2318 | } | ||
| 2319 | UNGCPRO; | ||
| 2320 | } | 2294 | } |
| 2321 | 2295 | ||
| 2322 | if (current_buffer->overlays_before || current_buffer->overlays_after) | 2296 | if (current_buffer->overlays_before || current_buffer->overlays_after) |
| @@ -2332,6 +2306,8 @@ signal_after_change (charpos, lendel, lenins) | |||
| 2332 | if (lendel == 0) | 2306 | if (lendel == 0) |
| 2333 | report_interval_modification (make_number (charpos), | 2307 | report_interval_modification (make_number (charpos), |
| 2334 | make_number (charpos + lenins)); | 2308 | make_number (charpos + lenins)); |
| 2309 | |||
| 2310 | unbind_to (count, Qnil); | ||
| 2335 | } | 2311 | } |
| 2336 | 2312 | ||
| 2337 | Lisp_Object | 2313 | Lisp_Object |