From: Helmut Eller Date: Mon, 2 Dec 2013 14:45:22 +0000 (-0500) Subject: * lisp/emacs-lisp/debug.el (debugger-toggle-locals): New command. X-Git-Tag: emacs-24.3.90~173^2^2~42^2~45^2~387^2~585 X-Git-Url: http://git.eshelyaron.com/gitweb/?a=commitdiff_plain;h=f345395c71fb70cc6b42edbef06a26bc6b9b31e0;p=emacs.git * lisp/emacs-lisp/debug.el (debugger-toggle-locals): New command. (debugger-mode-map): Bind it. (debugger--backtrace-base): New function. (debugger-eval-expression): Use it. (debugger-frame-number): Skip local vars when present. (debugger--locals-visible-p, debugger--insert-locals) (debugger--show-locals, debugger--hide-locals): New functions. * src/eval.c (Fbacktrace__locals): New function. (syms_of_eval): Defsubr it. --- diff --git a/etc/NEWS b/etc/NEWS index 0bcd13af3eb..1f050c6b975 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -247,6 +247,8 @@ You can pick the name of the function and the variables with `C-x 4 a'. * Changes in Specialized Modes and Packages in Emacs 24.4 +** The backtrace debugger can display local vars with `v'. + ** prolog-use-smie has been removed, along with the non-SMIE indentation code. ** SMIE indentation can be customized via `smie-config'. diff --git a/lisp/ChangeLog b/lisp/ChangeLog index 5bb405e04c3..a105723fb15 100644 --- a/lisp/ChangeLog +++ b/lisp/ChangeLog @@ -1,3 +1,13 @@ +2013-12-02 Helmut Eller + + * emacs-lisp/debug.el (debugger-toggle-locals): New command. + (debugger-mode-map): Bind it. + (debugger--backtrace-base): New function. + (debugger-eval-expression): Use it. + (debugger-frame-number): Skip local vars when present. + (debugger--locals-visible-p, debugger--insert-locals) + (debugger--show-locals, debugger--hide-locals): New functions. + 2013-12-02 Michael Albinus * net/tramp-sh.el (tramp-remote-process-environment): Do not set diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el index aa5b25b05f8..87d1d1eae64 100644 --- a/lisp/emacs-lisp/debug.el +++ b/lisp/emacs-lisp/debug.el @@ -494,9 +494,13 @@ removes itself from that hook." (forward-line 1) (while (progn (forward-char 2) - (if (= (following-char) ?\() - (forward-sexp 1) - (forward-sexp 2)) + (cond ((debugger--locals-visible-p) + (goto-char (next-single-char-property-change + (point) 'locals-visible))) + ((= (following-char) ?\() + (forward-sexp 1)) + (t + (forward-sexp 2))) (forward-line 1) (<= (point) opoint)) (if (looking-at " *;;;") @@ -541,6 +545,14 @@ Applies to the frame whose line point is on in the backtrace." (progn ,@body) (setq debugger-outer-match-data (match-data))))) +(defun debugger--backtrace-base () + "Return the function name that marks the top of the backtrace. +See `backtrace-frame'." + (cond ((eq 'debug--implement-debug-on-entry + (cadr (backtrace-frame 1 'debug))) + 'debug--implement-debug-on-entry) + (t 'debug))) + (defun debugger-eval-expression (exp &optional nframe) "Eval an expression, in an environment like that outside the debugger. The environment used is the one when entering the activation frame at point." @@ -549,15 +561,70 @@ The environment used is the one when entering the activation frame at point." (let ((nframe (or nframe (condition-case nil (1+ (debugger-frame-number 'skip-base)) (error 0)))) ;; If on first line. - (base (if (eq 'debug--implement-debug-on-entry - (cadr (backtrace-frame 1 'debug))) - 'debug--implement-debug-on-entry 'debug))) + (base (debugger--backtrace-base))) (debugger-env-macro (let ((val (backtrace-eval exp nframe base))) (prog1 (prin1 val t) (let ((str (eval-expression-print-format val))) (if str (princ str t)))))))) + +(defun debugger--locals-visible-p () + "Are the local variables of the current stack frame visible?" + (save-excursion + (move-to-column 2) + (get-text-property (point) 'locals-visible))) + +(defun debugger--insert-locals (locals) + "Insert the local variables LOCALS at point." + (cond ((null locals) + (insert "\n [no locals]")) + (t + (let ((print-escape-newlines t)) + (dolist (s+v locals) + (let ((symbol (car s+v)) + (value (cdr s+v))) + (insert "\n ") + (prin1 symbol (current-buffer)) + (insert " = ") + (prin1 value (current-buffer)))))))) + +(defun debugger--show-locals () + "For the frame at point, insert locals and add text properties." + (let* ((nframe (1+ (debugger-frame-number 'skip-base))) + (base (debugger--backtrace-base)) + (locals (backtrace--locals nframe base)) + (inhibit-read-only t)) + (save-excursion + (let ((start (progn + (move-to-column 2) + (point)))) + (end-of-line) + (debugger--insert-locals locals) + (add-text-properties start (point) '(locals-visible t)))))) + +(defun debugger--hide-locals () + "Delete local variables and remove the text property." + (let* ((col (current-column)) + (end (progn + (move-to-column 2) + (next-single-char-property-change (point) 'locals-visible))) + (start (previous-single-char-property-change end 'locals-visible)) + (inhibit-read-only t)) + (remove-text-properties start end '(locals-visible)) + (goto-char start) + (end-of-line) + (delete-region (point) end) + (move-to-column col))) + +(defun debugger-toggle-locals () + "Show or hide local variables of the current stack frame." + (interactive) + (cond ((debugger--locals-visible-p) + (debugger--hide-locals)) + (t + (debugger--show-locals)))) + (defvar debugger-mode-map (let ((map (make-keymap)) @@ -575,6 +642,7 @@ The environment used is the one when entering the activation frame at point." (define-key map "h" 'describe-mode) (define-key map "q" 'top-level) (define-key map "e" 'debugger-eval-expression) + (define-key map "v" 'debugger-toggle-locals) ;"v" is for "v"ariables. (define-key map " " 'next-line) (define-key map "R" 'debugger-record-expression) (define-key map "\C-m" 'debug-help-follow) diff --git a/src/ChangeLog b/src/ChangeLog index e691c91f6b2..db311bdcba5 100644 --- a/src/ChangeLog +++ b/src/ChangeLog @@ -1,3 +1,8 @@ +2013-12-02 Helmut Eller + + * eval.c (Fbacktrace__locals): New function. + (syms_of_eval): Defsubr it. + 2013-12-02 Dmitry Antipov * font.h (FONT_WIDTH, FONT_HEIGHT, FONT_BASE, FONT_DESCENT): diff --git a/src/eval.c b/src/eval.c index d3fcec5aef4..81666830f4e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -3576,6 +3576,73 @@ NFRAMES and BASE specify the activation frame to use, as in `backtrace-frame'. from the debugger. */ return unbind_to (count, eval_sub (exp)); } + +DEFUN ("backtrace--locals", Fbacktrace__locals, Sbacktrace__locals, 1, 2, NULL, + doc: /* Return names and values of local variables of a stack frame. +NFRAMES and BASE specify the activation frame to use, as in `backtrace-frame'. */) + (Lisp_Object nframes, Lisp_Object base) +{ + union specbinding *frame = get_backtrace_frame (nframes, base); + union specbinding *prevframe + = get_backtrace_frame (make_number (XFASTINT (nframes) - 1), base); + ptrdiff_t distance = specpdl_ptr - frame; + Lisp_Object result = Qnil; + eassert (distance >= 0); + + if (!backtrace_p (prevframe)) + error ("Activation frame not found!"); + if (!backtrace_p (frame)) + error ("Activation frame not found!"); + + /* The specpdl entries normally contain the symbol being bound along with its + `old_value', so it can be restored. The new value to which it is bound is + available in one of two places: either in the current value of the + variable (if it hasn't been rebount yet) or in the `old_value' slot of the + next specpdl entry for it. + `backtrace_eval_unrewind' happens to swap the role of `old_value' + and "new value", so we abuse it here, to fetch the new value. + It's ugly (we'd rather not modify global data) and a bit inefficient, + but it does the job for now. */ + backtrace_eval_unrewind (distance); + + /* Grab values. */ + { + union specbinding *tmp = prevframe; + for (; tmp > frame; tmp--) + { + switch (tmp->kind) + { + case SPECPDL_LET: + case SPECPDL_LET_DEFAULT: + case SPECPDL_LET_LOCAL: + { + Lisp_Object sym = specpdl_symbol (tmp); + Lisp_Object val = specpdl_old_value (tmp); + if (EQ (sym, Qinternal_interpreter_environment)) + { + Lisp_Object env = val; + for (; CONSP (env); env = XCDR (env)) + { + Lisp_Object binding = XCAR (env); + if (CONSP (binding)) + result = Fcons (Fcons (XCAR (binding), + XCDR (binding)), + result); + } + } + else + result = Fcons (Fcons (sym, val), result); + } + } + } + } + + /* Restore values from specpdl to original place. */ + backtrace_eval_unrewind (-distance); + + return result; +} + void mark_specpdl (void) @@ -3824,6 +3891,7 @@ alist of active lexical bindings. */); defsubr (&Sbacktrace); defsubr (&Sbacktrace_frame); defsubr (&Sbacktrace_eval); + defsubr (&Sbacktrace__locals); defsubr (&Sspecial_variable_p); defsubr (&Sfunctionp); }