summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGemini Lasswell <gazally@runbox.com>2018-06-22 12:53:37 -0700
committerGemini Lasswell <gazally@runbox.com>2018-07-14 08:56:44 -0700
commit2276cc2dad28040781ae21409cafd71549ddb385 (patch)
treebea372b291329bee4137ab4ba6ac182b46fe4acc
parent86f69ed5a3a8787400424e56541af4307dd619f4 (diff)
downloademacs-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.el127
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'.