diff options
-rw-r--r-- | doc/lispref/debugging.texi | 31 | ||||
-rw-r--r-- | doc/lispref/elisp.texi | 2 | ||||
-rw-r--r-- | doc/lispref/variables.texi | 61 | ||||
-rw-r--r-- | etc/NEWS | 5 | ||||
-rw-r--r-- | src/data.c | 9 |
5 files changed, 108 insertions, 0 deletions
diff --git a/doc/lispref/debugging.texi b/doc/lispref/debugging.texi index 6c0908acccb..c80b0f95b37 100644 --- a/doc/lispref/debugging.texi +++ b/doc/lispref/debugging.texi @@ -69,6 +69,7 @@ debugger recursively. @xref{Recursive Editing}. * Error Debugging:: Entering the debugger when an error happens. * Infinite Loops:: Stopping and debugging a program that doesn't exit. * Function Debugging:: Entering it when a certain function is called. +* Variable Debugging:: Entering it when a variable is modified. * Explicit Debug:: Entering it at a certain point in the program. * Using Debugger:: What the debugger does; what you see while in it. * Debugger Commands:: Commands used while in the debugger. @@ -290,6 +291,36 @@ Calling @code{cancel-debug-on-entry} does nothing to a function which is not currently set up to break on entry. @end deffn +@node Variable Debugging +@subsection Entering the debugger when a variable is modified +@cindex variable write debugging +@cindex debugging changes to variables + +Sometimes a problem with a function is due to a wrong setting of a +variable. Setting up the debugger to trigger whenever the variable is +changed is a quick way to find the origin of the setting. + +@deffn Command debug-on-variable-change variable +This function arranges for the debugger to be called whenever +@var{variable} is modified. + +It is implemented using the watchpoint mechanism, so it inherits the +same characteristics and limitations: all aliases of @var{variable} +will be watched together, only dynamic variables can be watched, and +changes to the objects referenced by variables are not detected. For +details, see @ref{Watching Variables}. +@end deffn + +@deffn Command cancel-debug-on-variable-change &optional variable +This function undoes the effect of @code{debug-on-variable-change} on +@var{variable}. When called interactively, it prompts for +@var{variable} in the minibuffer. If @var{variable} is omitted or +@code{nil}, it cancels break-on-change for all variables. Calling +@code{cancel-debug-on-variable-change} does nothing to a variable +which is not currently set up to break on change. +@end deffn + + @node Explicit Debug @subsection Explicit Entry to the Debugger @cindex debugger, explicit entry diff --git a/doc/lispref/elisp.texi b/doc/lispref/elisp.texi index 708bd9c3094..6983ab77c63 100644 --- a/doc/lispref/elisp.texi +++ b/doc/lispref/elisp.texi @@ -498,6 +498,7 @@ Variables * Accessing Variables:: Examining values of variables whose names are known only at run time. * Setting Variables:: Storing new values in variables. +* Watching Variables:: Running a function when a variable is changed. * Variable Scoping:: How Lisp chooses among local and global values. * Buffer-Local Variables:: Variable values in effect only in one buffer. * File Local Variables:: Handling local variable lists in files. @@ -642,6 +643,7 @@ The Lisp Debugger * Error Debugging:: Entering the debugger when an error happens. * Infinite Loops:: Stopping and debugging a program that doesn't exit. * Function Debugging:: Entering it when a certain function is called. +* Variable Debugging:: Entering it when a variable is modified. * Explicit Debug:: Entering it at a certain point in the program. * Using Debugger:: What the debugger does; what you see while in it. * Debugger Commands:: Commands used while in the debugger. diff --git a/doc/lispref/variables.texi b/doc/lispref/variables.texi index 4f2274f81a0..d777e4da509 100644 --- a/doc/lispref/variables.texi +++ b/doc/lispref/variables.texi @@ -34,6 +34,7 @@ representing the variable. * Accessing Variables:: Examining values of variables whose names are known only at run time. * Setting Variables:: Storing new values in variables. +* Watching Variables:: Running a function when a variable is changed. * Variable Scoping:: How Lisp chooses among local and global values. * Buffer-Local Variables:: Variable values in effect only in one buffer. * File Local Variables:: Handling local variable lists in files. @@ -766,6 +767,66 @@ error is signaled. @end example @end defun +@node Watching Variables +@section Running a function when a variable is changed. +@cindex variable watchpoints +@cindex watchpoints for Lisp variables + +It is sometimes useful to take some action when a variable changes its +value. The watchpoint facility provides the means to do so. Some +possible uses for this feature include keeping display in sync with +variable settings, and invoking the debugger to track down unexpected +changes to variables (@pxref{Variable Debugging}). + +The following functions may be used to manipulate and query the watch +functions for a variable. + +@defun add-variable-watcher symbol watch-function +This function arranges for @var{watch-function} to be called whenever +@var{symbol} is modified. Modifications through aliases +(@pxref{Variable Aliases}) will have the same effect. + +@var{watch-function} will be called with 4 arguments: (@var{symbol} +@var{newval} @var{operation} @var{where}). + +@var{symbol} is the variable being changed. +@var{newval} is the value it will be changed to. +@var{operation} is a symbol representing the kind of change, one of: +`set', `let', `unlet', `makunbound', and `defvaralias'. +@var{where} is a buffer if the buffer-local value of the variable is +being changed, nil otherwise. +@end defun + +@defun remove-variable-watch symbol watch-function +This function removes @var{watch-function} from @var{symbol}'s list of +watchers. +@end defun + +@defun get-variable-watchers symbol +This function returns the list of @var{symbol}'s active watcher +functions. +@end defun + +@subsection Limitations + +There are a couple of ways in which a variable could be modifed (or at +least appear to be modified) without triggering a watchpoint. + +Since watchpoints are attached to symbols, modification to the +objects contained within variables (e.g., by a list modification +function @pxref{Modifying Lists}) is not caught by this mechanism. + +Additionally, C code can modify the value of variables directly, +bypassing the watchpoint mechanism. + +A minor limitation of this feature, again because it targets symbols, +is that only variables of dynamic scope may be watched. This poses +little difficulty, since modifications to lexical variables can be +discovered easily by inspecting the code within the scope of the +variable (unlike dynamic variables, which can be modified by any code +at all, @pxref{Variable Scoping}). + + @node Variable Scoping @section Scoping Rules for Variable Bindings @cindex scoping rule @@ -716,6 +716,11 @@ consistency with the new functions. For compatibility, 'sxhash' remains as an alias to 'sxhash-equal'. +++ +** New function `add-variable-watcher' can be used to call a function +when a symbol's value is changed. This is used to implement the new +debugger command `debug-on-variable-change'. + ++++ ** Time conversion functions that accept a time zone rule argument now allow it to be OFFSET or a list (OFFSET ABBR), where the integer OFFSET is a count of seconds east of Universal Time, and the string diff --git a/src/data.c b/src/data.c index 81846b5dcbc..eee2a52a37a 100644 --- a/src/data.c +++ b/src/data.c @@ -1428,6 +1428,15 @@ harmonize_variable_watchers (Lisp_Object alias, Lisp_Object base_variable) DEFUN ("add-variable-watcher", Fadd_variable_watcher, Sadd_variable_watcher, 2, 2, 0, doc: /* Cause WATCH-FUNCTION to be called when SYMBOL is set. + +It will be called with 4 arguments: (SYMBOL NEWVAL OPERATION WHERE). +SYMBOL is the variable being changed. +NEWVAL is the value it will be changed to. +OPERATION is a symbol representing the kind of change, one of: `set', +`let', `unlet', `makunbound', and `defvaralias'. +WHERE is a buffer if the buffer-local value of the variable being +changed, nil otherwise. + All writes to aliases of SYMBOL will call WATCH-FUNCTION too. */) (Lisp_Object symbol, Lisp_Object watch_function) { |