From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.org!not-for-mail From: Helmut Eller Newsgroups: gmane.emacs.devel Subject: Re: debugger-toggle-locals Date: Mon, 02 Dec 2013 12:04:16 +0100 Message-ID: References: NNTP-Posting-Host: plane.gmane.org Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" X-Trace: ger.gmane.org 1385982277 18079 80.91.229.3 (2 Dec 2013 11:04:37 GMT) X-Complaints-To: usenet@ger.gmane.org NNTP-Posting-Date: Mon, 2 Dec 2013 11:04:37 +0000 (UTC) Cc: emacs-devel@gnu.org To: Stefan Monnier Original-X-From: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Mon Dec 02 12:04:42 2013 Return-path: Envelope-to: ged-emacs-devel@m.gmane.org Original-Received: from lists.gnu.org ([208.118.235.17]) by plane.gmane.org with esmtp (Exim 4.69) (envelope-from ) id 1VnRIv-0000tJ-T3 for ged-emacs-devel@m.gmane.org; Mon, 02 Dec 2013 12:04:42 +0100 Original-Received: from localhost ([::1]:35253 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VnRIv-00075N-GJ for ged-emacs-devel@m.gmane.org; Mon, 02 Dec 2013 06:04:41 -0500 Original-Received: from eggs.gnu.org ([2001:4830:134:3::10]:40517) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VnRIj-000758-PS for emacs-devel@gnu.org; Mon, 02 Dec 2013 06:04:38 -0500 Original-Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1VnRIb-0001f6-Ac for emacs-devel@gnu.org; Mon, 02 Dec 2013 06:04:29 -0500 Original-Received: from mail-ea0-x22f.google.com ([2a00:1450:4013:c01::22f]:43965) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1VnRIa-0001ex-Vj for emacs-devel@gnu.org; Mon, 02 Dec 2013 06:04:21 -0500 Original-Received: by mail-ea0-f175.google.com with SMTP id z10so8714989ead.6 for ; Mon, 02 Dec 2013 03:04:19 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20120113; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version:content-type; bh=M54Tl/YArHwIZajYtK9cWxqapp74ipW6qJWPgXnHZfo=; b=D91E5/3w8UK+CTgp60qySoVV5b9HVlxtUN+QYuExU9GQXJrhiTcpAUuMcWG4+A8jwH 9yLzHEiKjbOOnJrTIj2O+hnYcfSYpxtdaCNjc48DIaS7iNWd0/Wdj/lpyDOCie48Qq/T WXEfYG2mwCfWoZRQ4hRlm/nwPUWYfsCROpCAD6bp2SSArVoFVhGvdMApBa+59+Vi5VHO Udi2SmdNl/Pzo+UPWaKYvPqTNmt74j77DntJMJNC7ErqdzccC9UNmR86VD4aJwcJIGZQ 48bU1DCAcEmRt28UeL/jrCowWrSJRUf0tEHCjmltlyKzQARKVSBgbqSlj6IC7cMI1fan R/YQ== X-Received: by 10.15.83.8 with SMTP id b8mr61517019eez.6.1385982259595; Mon, 02 Dec 2013 03:04:19 -0800 (PST) Original-Received: from ix (dial-188058.pool.broadband44.net. [212.46.188.58]) by mx.google.com with ESMTPSA id o1sm26185277eea.10.2013.12.02.03.04.17 for (version=TLSv1.2 cipher=RC4-SHA bits=128/128); Mon, 02 Dec 2013 03:04:18 -0800 (PST) Original-Received: from helmut by ix with local (Exim 4.80) (envelope-from ) id 1VnRIW-00086t-Ed; Mon, 02 Dec 2013 12:04:16 +0100 In-Reply-To: (Stefan Monnier's message of "Sun, 01 Dec 2013 17:47:31 -0500") User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/24.3.50 (gnu/linux) X-detected-operating-system: by eggs.gnu.org: Error: Malformed IPv6 address (bad octet value). X-Received-From: 2a00:1450:4013:c01::22f X-BeenThere: emacs-devel@gnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: "Emacs development discussions." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Original-Sender: emacs-devel-bounces+ged-emacs-devel=m.gmane.org@gnu.org Xref: news.gmane.org gmane.emacs.devel:166003 Archived-At: --=-=-= Content-Type: text/plain 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 --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=locals.patch 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)))) + (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; +} + 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); } --=-=-=--