diff options
author | Gemini Lasswell <gazally@runbox.com> | 2018-06-22 12:53:37 -0700 |
---|---|---|
committer | Gemini Lasswell <gazally@runbox.com> | 2018-07-14 08:56:44 -0700 |
commit | 2276cc2dad28040781ae21409cafd71549ddb385 (patch) | |
tree | bea372b291329bee4137ab4ba6ac182b46fe4acc | |
parent | 86f69ed5a3a8787400424e56541af4307dd619f4 (diff) | |
download | emacs-2276cc2dad28040781ae21409cafd71549ddb385.tar.gz |
Lazily print backtrace frame local variables
Instead of printing the local variables for all frames when the
backtrace buffer is created, print them when they are first made
visible. Add a prefix argument to backtrace-toggle-locals to toggle
locals display for the entire buffer.
* lisp/emacs-lisp/backtrace.el (backtrace-view): Mention
:show-locals in docstring.
(backtrace-get-frame-end): Handle the possibility that the locals
section might not exist.
(backtrace-toggle-locals): Add prefix argument to toggle visibility of
locals for the whole buffer.
(backtrace--with-output-variables): Move before first use.
(backtrace--set-frame-locals-visible): New function.
(backtrace--set-locals-visible-overlay): New function.
(backtrace--set-locals-visible): Deleted.
(backtrace-toggle-print-circle): Remove TODO comment.
(backtrace--print-locals): Skip printing the locals if they are not
visible.
-rw-r--r-- | lisp/emacs-lisp/backtrace.el | 127 |
1 files changed, 84 insertions, 43 deletions
diff --git a/lisp/emacs-lisp/backtrace.el b/lisp/emacs-lisp/backtrace.el index 5fc436e8ec1..a8759c1bd87 100644 --- a/lisp/emacs-lisp/backtrace.el +++ b/lisp/emacs-lisp/backtrace.el @@ -142,7 +142,8 @@ This should be a list of `backtrace-frame' objects.") (defvar-local backtrace-view nil "A plist describing how to render backtrace frames. -Possible entries are :show-flags, :do-xrefs and :print-circle.") +Possible entries are :show-flags, :show-locals, :do-xrefs and +:print-circle.") ;; TODO not used yet, maybe for Edebug ;; (defvar-local backtrace-printer #'backtrace-print-frame @@ -287,24 +288,74 @@ It runs `backtrace-revert-hook', then calls `backtrace-print'." (run-hooks 'backtrace-revert-hook) (backtrace-print t)) -(defun backtrace-toggle-locals () - "Toggle the display of local variables for the backtrace frame at point. -TODO with argument, toggle all frames." - (interactive) - (let ((index (backtrace-get-index))) - (unless index - (user-error "Not in a stack frame")) - (let ((pos (point))) - (goto-char (backtrace-get-frame-start)) - (while (and (eq index (backtrace-get-index)) - (not (eq (backtrace-get-section) 'locals))) - (goto-char (next-single-property-change (point) 'backtrace-section))) - (let ((end (backtrace-get-section-end))) - (backtrace--set-locals-visible (point) end (invisible-p (point))) - - (goto-char (if (invisible-p pos) end pos)))))) +(defmacro backtrace--with-output-variables (view &rest body) + "Bind output variables according to VIEW and execute BODY." + (declare (indent 1)) + `(let ((print-escape-control-characters t) + (print-escape-newlines t) + (print-circle (plist-get ,view :print-circle)) + (standard-output (current-buffer))) + ,@body)) -(defun backtrace--set-locals-visible (beg end visible) +(defun backtrace-toggle-locals (&optional all) + "Toggle the display of local variables for the backtrace frame at point. +With prefix argument ALL, toggle the value of :show-locals in +`backtrace-view', which affects all of the backtrace frames in +the buffer." + (interactive "P") + (if all + (let ((pos (make-marker)) + (visible (not (plist-get backtrace-view :show-locals)))) + (plist-put backtrace-view :show-locals visible) + (set-marker-insertion-type pos t) + (set-marker pos (point)) + (goto-char (point-min)) + ;; Skip the header. + (unless (backtrace-get-index) + (goto-char (backtrace-get-frame-end))) + (while (< (point) (point-max)) + (backtrace--set-frame-locals-visible visible) + (goto-char (backtrace-get-frame-end))) + (goto-char pos) + (when (invisible-p pos) + (goto-char (backtrace-get-frame-start)))) + (let ((index (backtrace-get-index))) + (unless index + (user-error "Not in a stack frame")) + (backtrace--set-frame-locals-visible + (not (plist-get (backtrace-get-view) :show-locals)))))) + +(defun backtrace--set-frame-locals-visible (visible) + "Set the visibility of the local vars for the frame at point to VISIBLE." + (let ((pos (point)) + (index (backtrace-get-index)) + (start (backtrace-get-frame-start)) + (end (backtrace-get-frame-end)) + (view (copy-sequence (backtrace-get-view))) + (inhibit-read-only t)) + (plist-put view :show-locals visible) + (goto-char (backtrace-get-frame-start)) + (while (not (or (= (point) end) + (eq (backtrace-get-section) 'locals))) + (goto-char (next-single-property-change (point) + 'backtrace-section nil end))) + (cond + ((and (= (point) end) visible) + ;; The locals section doesn't exist so create it. + (let ((standard-output (current-buffer))) + (backtrace--with-output-variables view + (backtrace--print-locals + (nth index backtrace-frames) view)) + (add-text-properties end (point) `(backtrace-index ,index)) + (goto-char pos))) + ((/= (point) end) + ;; The locals section does exist, so add or remove the overlay. + (backtrace--set-locals-visible-overlay (point) end visible) + (goto-char (if (invisible-p pos) start pos)))) + (add-text-properties start (backtrace-get-frame-end) + `(backtrace-view ,view)))) + +(defun backtrace--set-locals-visible-overlay (beg end visible) (backtrace--change-button-skip beg end (not visible)) (if visible (remove-overlays beg end 'invisible t) @@ -334,7 +385,6 @@ FEATURE should be one of the options in `backtrace-view'. After toggling the feature, reprint the frame and position point at the start of the section of the frame it was in before." - ;; TODO preserve (in)visibility of locals (let ((index (backtrace-get-index)) (view (copy-sequence (backtrace-get-view)))) (unless index @@ -357,15 +407,6 @@ before." 'backtrace-section section))) (goto-char pos)))))) -(defmacro backtrace--with-output-variables (view &rest body) - "Bind output variables according to VIEW and execute BODY." - (declare (indent 1)) - `(let ((print-escape-control-characters t) - (print-escape-newlines t) - (print-circle (plist-get ,view :print-circle)) - (standard-output (current-buffer))) - ,@body)) - (defun backtrace-expand-ellipsis (button) "Expand display of the elided form at BUTTON." ;; TODO a command to expand all ... in form at point @@ -664,21 +705,21 @@ Format it according to VIEW." (insert "\n") (put-text-property beg (point) 'backtrace-section 'func))) -(defun backtrace--print-locals (frame _view) - "Print a backtrace FRAME's local variables. -Make them invisible initially." - (let* ((beg (point)) - (locals (backtrace-frame-locals frame))) - (if (null locals) - (insert " [no locals]\n") - (pcase-dolist (`(,symbol . ,value) locals) - (insert " ") - (backtrace--print symbol) - (insert " = ") - (insert (backtrace--print-to-string value)) - (insert "\n"))) - (put-text-property beg (point) 'backtrace-section 'locals) - (backtrace--set-locals-visible beg (point) nil))) +(defun backtrace--print-locals (frame view) + "Print a backtrace FRAME's local variables according to VIEW. +Print them only if :show-locals is non-nil in the VIEW plist." + (when (plist-get view :show-locals) + (let* ((beg (point)) + (locals (backtrace-frame-locals frame))) + (if (null locals) + (insert " [no locals]\n") + (pcase-dolist (`(,symbol . ,value) locals) + (insert " ") + (backtrace--print symbol) + (insert " = ") + (insert (backtrace--print-to-string value)) + (insert "\n"))) + (put-text-property beg (point) 'backtrace-section 'locals)))) (defun backtrace--print (obj) "Attempt to print OBJ using `backtrace-print-function'. |