From: Helmut Eller <eller.helmut@gmail.com>
To: Stefan Monnier <monnier@iro.umontreal.ca>
Cc: emacs-devel@gnu.org
Subject: Re: debugger-toggle-locals
Date: Mon, 02 Dec 2013 12:04:16 +0100 [thread overview]
Message-ID: <m2a9gjsedr.fsf@gmail.com> (raw)
In-Reply-To: <jwviov842xk.fsf-monnier+emacs@gnu.org> (Stefan Monnier's message of "Sun, 01 Dec 2013 17:47:31 -0500")
[-- Attachment #1: Type: text/plain, Size: 1785 bytes --]
On Sun, Dec 01 2013, Stefan Monnier wrote:
>> x: 9
>> y: 114
>> z: (9 . 114)
>
> Nitpick: I'd rather have "x=" instead of "x:" (for me, ":" means "has type").
Well, it's a debugger for Lisp not for ML.
>> +(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)))
>
> Please use it in debugger-eval-expression as well.
OK.
>> +(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) 'debugger-locals-visible-p)))
>
> The text property shouldn't have a name that ends in `-p'; these names
> are for predicates (i.e. functions returning a boolean), not for
> variables or object fields, or symbol/text properties.
OK.
>> + (dolist (s+v locals)
>> + (let ((symbol (car s+v))
>> + (value (cdr s+v))
>
> You can use (pcase-dolist (`(,symbol . ,value) locals) ...).
So many ` , . cause cancer of the semicolon.
>> + (cond ((string= (symbol-name symbol)
>> "internal-interpreter-environment")
>> + (cond ((or (null value)
>> + (and (null (cdr value))
>> + (symbol (car value))))
> ^^^^^^
> should be `symbolp', right?
>
> `value' can also be of the form (a b t).
> I.e. (null (cdr value)) is false, but there are no values.
>
> But I tend to think that the handling of
> internal-interpreter-environment should be kept in the C code: this
> symbol should by and large not be exported to Elisp.
Done. Moved it to C.
Helmut
[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: locals.patch --]
[-- Type: text/x-diff, Size: 6511 bytes --]
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index aa5b25b..ce2616c 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))))
+
\f
(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 "t" 'debugger-toggle-locals)
(define-key map " " 'next-line)
(define-key map "R" 'debugger-record-expression)
(define-key map "\C-m" 'debug-help-follow)
diff --git a/src/eval.c b/src/eval.c
index d3fcec5..b538a40 100644
--- a/src/eval.c
+++ b/src/eval.c
@@ -3576,6 +3576,66 @@ 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'.
+The result is an alist. */)
+ (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!");
+
+ /* move values to specpdl */
+ 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;
+}
+
\f
void
mark_specpdl (void)
@@ -3824,6 +3884,7 @@ alist of active lexical bindings. */);
defsubr (&Sbacktrace);
defsubr (&Sbacktrace_frame);
defsubr (&Sbacktrace_eval);
+ defsubr (&Sbacktrace_locals);
defsubr (&Sspecial_variable_p);
defsubr (&Sfunctionp);
}
next prev parent reply other threads:[~2013-12-02 11:04 UTC|newest]
Thread overview: 5+ messages / expand[flat|nested] mbox.gz Atom feed top
2013-11-23 8:57 debugger-toggle-locals Helmut Eller
2013-11-24 14:13 ` debugger-toggle-locals Stefan Monnier
2013-12-01 22:47 ` debugger-toggle-locals Stefan Monnier
2013-12-02 11:04 ` Helmut Eller [this message]
2013-12-02 14:46 ` debugger-toggle-locals Stefan Monnier
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
List information: https://www.gnu.org/software/emacs/
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=m2a9gjsedr.fsf@gmail.com \
--to=eller.helmut@gmail.com \
--cc=emacs-devel@gnu.org \
--cc=monnier@iro.umontreal.ca \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
Code repositories for project(s) associated with this public inbox
https://git.savannah.gnu.org/cgit/emacs.git
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).