aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorNoam Postavsky2015-11-21 16:03:06 -0500
committerNoam Postavsky2016-12-02 20:25:14 -0500
commitcfd2b9eae17754c0e109961f2880f05012a4891d (patch)
tree6cae519f21b1f9371e13652720dd0c42c34fd08e
parent459a23444e321d25f0b82bede76947576f01ecc3 (diff)
downloademacs-cfd2b9eae17754c0e109961f2880f05012a4891d.tar.gz
emacs-cfd2b9eae17754c0e109961f2880f05012a4891d.zip
Add function to trigger debugger on variable write
* lisp/emacs-lisp/debug.el (debug-on-variable-change): (debug--variable-list): (cancel-debug-on-variable-change): New functions. (debugger-setup-buffer): Add watchpoint clause.
-rw-r--r--lisp/emacs-lisp/debug.el91
1 files changed, 91 insertions, 0 deletions
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index 7d273809fcd..5430b72545a 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -306,6 +306,24 @@ That buffer should be current already."
306 (delete-char 1) 306 (delete-char 1)
307 (insert ? ) 307 (insert ? )
308 (beginning-of-line)) 308 (beginning-of-line))
309 ;; Watchpoint triggered.
310 ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args)))
311 (insert
312 "--"
313 (pcase details
314 (`(makunbound nil) (format "making %s void" symbol))
315 (`(makunbound ,buffer) (format "killing local value of %s in buffer %s"
316 symbol buffer))
317 (`(defvaralias ,_) (format "aliasing %s to %s" symbol newval))
318 (`(let ,_) (format "let-binding %s to %S" symbol newval))
319 (`(unlet ,_) (format "ending let-binding of %s" symbol))
320 (`(set nil) (format "setting %s to %S" symbol newval))
321 (`(set ,buffer) (format "setting %s in buffer %s to %S"
322 symbol buffer newval))
323 (_ (error "unrecognized watchpoint triggered %S" (cdr args))))
324 ": ")
325 (setq pos (point))
326 (insert ?\n))
309 ;; Debugger entered for an error. 327 ;; Debugger entered for an error.
310 (`error 328 (`error
311 (insert "--Lisp error: ") 329 (insert "--Lisp error: ")
@@ -850,6 +868,79 @@ To specify a nil argument interactively, exit with an empty minibuffer."
850 (princ "Note: if you have redefined a function, then it may no longer\n") 868 (princ "Note: if you have redefined a function, then it may no longer\n")
851 (princ "be set to debug on entry, even if it is in the list.")))))) 869 (princ "be set to debug on entry, even if it is in the list."))))))
852 870
871(defun debug--implement-debug-watch (symbol newval op where)
872 "Conditionally call the debugger.
873This function is called when SYMBOL's value is modified."
874 (if (or inhibit-debug-on-entry debugger-jumping-flag)
875 nil
876 (let ((inhibit-debug-on-entry t))
877 (funcall debugger 'watchpoint symbol newval op where))))
878
879;;;###autoload
880(defun debug-on-variable-change (variable)
881 "Trigger a debugger invocation when VARIABLE is changed.
882
883When called interactively, prompt for VARIABLE in the minibuffer.
884
885This works by calling `add-variable-watch' on VARIABLE. If you
886quit from the debugger, this will abort the change (unless the
887change is caused by the termination of a let-binding).
888
889The watchpoint may be circumvented by C code that changes the
890variable directly (i.e., not via `set'). Changing the value of
891the variable (e.g., `setcar' on a list variable) will not trigger
892watchpoint.
893
894Use \\[cancel-debug-on-variable-change] to cancel the effect of
895this command. Uninterning VARIABLE or making it an alias of
896another symbol also cancels it."
897 (interactive
898 (let* ((var-at-point (variable-at-point))
899 (var (and (symbolp var-at-point) var-at-point))
900 (val (completing-read
901 (concat "Debug when setting variable"
902 (if var (format " (default %s): " var) ": "))
903 obarray #'boundp
904 t nil nil (and var (symbol-name var)))))
905 (list (if (equal val "") var (intern val)))))
906 (add-variable-watcher variable #'debug--implement-debug-watch))
907
908;;;###autoload
909(defalias 'debug-watch #'debug-on-variable-change)
910
911
912(defun debug--variable-list ()
913 "List of variables currently set for debug on set."
914 (let ((vars '()))
915 (mapatoms
916 (lambda (s)
917 (when (memq #'debug--implement-debug-watch
918 (get s 'watchers))
919 (push s vars))))
920 vars))
921
922;;;###autoload
923(defun cancel-debug-on-variable-change (&optional variable)
924 "Undo effect of \\[debug-on-variable-change] on VARIABLE.
925If VARIABLE is nil, cancel debug-on-variable-change for all variables.
926When called interactively, prompt for VARIABLE in the minibuffer.
927To specify a nil argument interactively, exit with an empty minibuffer."
928 (interactive
929 (list (let ((name
930 (completing-read
931 "Cancel debug on set for variable (default all variables): "
932 (mapcar #'symbol-name (debug--variable-list)) nil t)))
933 (when name
934 (unless (string= name "")
935 (intern name))))))
936 (if variable
937 (remove-variable-watcher variable #'debug--implement-debug-watch)
938 (message "Canceling debug-watch for all variables")
939 (mapc #'cancel-debug-watch (debug--variable-list))))
940
941;;;###autoload
942(defalias 'cancel-debug-watch #'cancel-debug-on-variable-change)
943
853(provide 'debug) 944(provide 'debug)
854 945
855;;; debug.el ends here 946;;; debug.el ends here