aboutsummaryrefslogtreecommitdiffstats
path: root/lisp
diff options
context:
space:
mode:
authorGemini Lasswell2018-08-03 10:28:28 -0700
committerGemini Lasswell2018-08-03 10:28:28 -0700
commitda0054c30729e58259c1e7251cb03c8ef13ff943 (patch)
treef3fd4b5256aa6c6786d0ac4f80fb1d87dcc2e401 /lisp
parente65ec81fc3e556719fae8d8b4b42f571c7e9f4fc (diff)
parent95b2ab3dccdc756614b4c8f45a7b206d61753705 (diff)
downloademacs-da0054c30729e58259c1e7251cb03c8ef13ff943.tar.gz
emacs-da0054c30729e58259c1e7251cb03c8ef13ff943.zip
Merge branch 'scratch/backtrace-mode'
Diffstat (limited to 'lisp')
-rw-r--r--lisp/emacs-lisp/backtrace.el916
-rw-r--r--lisp/emacs-lisp/cl-print.el297
-rw-r--r--lisp/emacs-lisp/debug.el463
-rw-r--r--lisp/emacs-lisp/edebug.el229
-rw-r--r--lisp/emacs-lisp/ert.el41
-rw-r--r--lisp/emacs-lisp/lisp-mode.el10
-rw-r--r--lisp/subr.el19
7 files changed, 1561 insertions, 414 deletions
diff --git a/lisp/emacs-lisp/backtrace.el b/lisp/emacs-lisp/backtrace.el
new file mode 100644
index 00000000000..f13b43b465c
--- /dev/null
+++ b/lisp/emacs-lisp/backtrace.el
@@ -0,0 +1,916 @@
1;;; backtrace.el --- generic major mode for Elisp backtraces -*- lexical-binding: t -*-
2
3;; Copyright (C) 2018 Free Software Foundation, Inc.
4
5;; Author: Gemini Lasswell
6;; Keywords: lisp, tools, maint
7;; Version: 1.0
8
9;; This file is part of GNU Emacs.
10
11;; GNU Emacs is free software: you can redistribute it and/or modify
12;; it under the terms of the GNU General Public License as published by
13;; the Free Software Foundation, either version 3 of the License, or
14;; (at your option) any later version.
15
16;; GNU Emacs is distributed in the hope that it will be useful,
17;; but WITHOUT ANY WARRANTY; without even the implied warranty of
18;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19;; GNU General Public License for more details.
20
21;; You should have received a copy of the GNU General Public License
22;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
23
24;;; Commentary:
25
26;; This file defines Backtrace mode, a generic major mode for displaying
27;; Elisp stack backtraces, which can be used as is or inherited from
28;; by another mode.
29
30;; For usage information, see the documentation of `backtrace-mode'.
31
32;;; Code:
33
34(eval-when-compile (require 'cl-lib))
35(eval-when-compile (require 'pcase))
36(eval-when-compile (require 'subr-x)) ; if-let
37(require 'help-mode) ; Define `help-function-def' button type.
38(require 'lisp-mode)
39
40;;; Options
41
42(defgroup backtrace nil
43 "Viewing of Elisp backtraces."
44 :group 'lisp)
45
46(defcustom backtrace-fontify t
47 "If non-nil, fontify Backtrace buffers.
48Set to nil to disable fontification, which may be necessary in
49order to debug the code that does fontification."
50 :type 'boolean
51 :group 'backtrace
52 :version "27.1")
53
54(defcustom backtrace-line-length 5000
55 "Target length for lines in Backtrace buffers.
56Backtrace mode will attempt to abbreviate printing of backtrace
57frames to make them shorter than this, but success is not
58guaranteed. If set to nil or zero, Backtrace mode will not
59abbreviate the forms it prints."
60 :type 'integer
61 :group 'backtrace
62 :version "27.1")
63
64;;; Backtrace frame data structure
65
66(cl-defstruct
67 (backtrace-frame
68 (:constructor backtrace-make-frame))
69 evald ; Non-nil if argument evaluation is complete.
70 fun ; The function called/to call in this frame.
71 args ; Either evaluated or unevaluated arguments to the function.
72 flags ; A plist, possible properties are :debug-on-exit and :source-available.
73 locals ; An alist containing variable names and values.
74 buffer ; If non-nil, the buffer in use by eval-buffer or eval-region.
75 pos ; The position in the buffer.
76 )
77
78(cl-defun backtrace-get-frames
79 (&optional base &key (constructor #'backtrace-make-frame))
80 "Collect all frames of current backtrace into a list.
81The list will contain objects made by CONSTRUCTOR, which
82defaults to `backtrace-make-frame' and which, if provided, should
83be the constructor of a structure which includes
84`backtrace-frame'. If non-nil, BASE should be a function, and
85frames before its nearest activation frame are discarded."
86 (let ((frames nil)
87 (eval-buffers eval-buffer-list))
88 (mapbacktrace (lambda (evald fun args flags)
89 (push (funcall constructor
90 :evald evald :fun fun
91 :args args :flags flags)
92 frames))
93 (or base 'backtrace-get-frames))
94 (setq frames (nreverse frames))
95 ;; Add local variables to each frame, and the buffer position
96 ;; to frames containing eval-buffer or eval-region.
97 (dotimes (idx (length frames))
98 (let ((frame (nth idx frames)))
99 ;; `backtrace--locals' gives an error when idx is 0. But the
100 ;; locals for frame 0 are not needed, because when we get here
101 ;; from debug-on-entry, the locals aren't bound yet, and when
102 ;; coming from Edebug or ERT there is an Edebug or ERT
103 ;; function at frame 0.
104 (when (> idx 0)
105 (setf (backtrace-frame-locals frame)
106 (backtrace--locals idx (or base 'backtrace-get-frames))))
107 (when (and eval-buffers (memq (backtrace-frame-fun frame)
108 '(eval-buffer eval-region)))
109 ;; This will get the wrong result if there are two nested
110 ;; eval-region calls for the same buffer. That's not a very
111 ;; useful case.
112 (with-current-buffer (pop eval-buffers)
113 (setf (backtrace-frame-buffer frame) (current-buffer))
114 (setf (backtrace-frame-pos frame) (point))))))
115 frames))
116
117;; Button definition for jumping to a buffer position.
118
119(define-button-type 'backtrace-buffer-pos
120 'action #'backtrace--pop-to-buffer-pos
121 'help-echo "mouse-2, RET: Show reading position")
122
123(defun backtrace--pop-to-buffer-pos (button)
124 "Pop to the buffer and position for the BUTTON at point."
125 (let* ((buffer (button-get button 'backtrace-buffer))
126 (pos (button-get button 'backtrace-pos)))
127 (if (buffer-live-p buffer)
128 (progn
129 (pop-to-buffer buffer)
130 (goto-char (max (point-min) (min (point-max) pos))))
131 (message "Buffer has been killed"))))
132
133;; Font Locking support
134
135(defconst backtrace--font-lock-keywords
136 '((backtrace--match-ellipsis-in-string
137 (1 'button prepend)))
138 "Expressions to fontify in Backtrace mode.
139Fontify these in addition to the expressions Emacs Lisp mode
140fontifies.")
141
142(defconst backtrace-font-lock-keywords
143 (append lisp-el-font-lock-keywords-for-backtraces
144 backtrace--font-lock-keywords)
145 "Default expressions to highlight in Backtrace mode.")
146(defconst backtrace-font-lock-keywords-1
147 (append lisp-el-font-lock-keywords-for-backtraces-1
148 backtrace--font-lock-keywords)
149 "Subdued level highlighting for Backtrace mode.")
150(defconst backtrace-font-lock-keywords-2
151 (append lisp-el-font-lock-keywords-for-backtraces-2
152 backtrace--font-lock-keywords)
153 "Gaudy level highlighting for Backtrace mode.")
154
155(defun backtrace--match-ellipsis-in-string (bound)
156 ;; Fontify ellipses within strings as buttons.
157 ;; This is necessary because ellipses are text property buttons
158 ;; instead of overlay buttons, which is done because there could
159 ;; be a large number of them.
160 (when (re-search-forward "\\(\\.\\.\\.\\)\"" bound t)
161 (and (get-text-property (- (point) 2) 'cl-print-ellipsis)
162 (get-text-property (- (point) 3) 'cl-print-ellipsis)
163 (get-text-property (- (point) 4) 'cl-print-ellipsis))))
164
165;;; Xref support
166
167(defun backtrace--xref-backend () 'elisp)
168
169;;; Backtrace mode variables
170
171(defvar-local backtrace-frames nil
172 "Stack frames displayed in the current Backtrace buffer.
173This should be a list of `backtrace-frame' objects.")
174
175(defvar-local backtrace-view nil
176 "A plist describing how to render backtrace frames.
177Possible entries are :show-flags, :show-locals and :print-circle.")
178
179(defvar-local backtrace-insert-header-function nil
180 "Function for inserting a header for the current Backtrace buffer.
181If nil, no header will be created. Note that Backtrace buffers
182are fontified as in Emacs Lisp Mode, the header text included.")
183
184(defvar backtrace-revert-hook nil
185 "Hook run before reverting a Backtrace buffer.
186This is commonly used to recompute `backtrace-frames'.")
187
188(defvar-local backtrace-print-function #'cl-prin1
189 "Function used to print values in the current Backtrace buffer.")
190
191(defvar-local backtrace-goto-source-functions nil
192 "Abnormal hook used to jump to the source code for the current frame.
193Each hook function is called with no argument, and should return
194non-nil if it is able to switch to the buffer containing the
195source code. Execution of the hook will stop if one of the
196functions returns non-nil. When adding a function to this hook,
197you should also set the :source-available flag for the backtrace
198frames where the source code location is known.")
199
200(defvar backtrace-mode-map
201 (let ((map (copy-keymap special-mode-map)))
202 (set-keymap-parent map button-buffer-map)
203 (define-key map "n" 'backtrace-forward-frame)
204 (define-key map "p" 'backtrace-backward-frame)
205 (define-key map "v" 'backtrace-toggle-locals)
206 (define-key map "#" 'backtrace-toggle-print-circle)
207 (define-key map "s" 'backtrace-goto-source)
208 (define-key map "\C-m" 'backtrace-help-follow-symbol)
209 (define-key map "+" 'backtrace-multi-line)
210 (define-key map "-" 'backtrace-single-line)
211 (define-key map "." 'backtrace-expand-ellipses)
212 (define-key map [follow-link] 'mouse-face)
213 (define-key map [mouse-2] 'mouse-select-window)
214 (easy-menu-define nil map ""
215 '("Backtrace"
216 ["Next Frame" backtrace-forward-frame
217 :help "Move cursor forwards to the start of a backtrace frame"]
218 ["Previous Frame" backtrace-backward-frame
219 :help "Move cursor backwards to the start of a backtrace frame"]
220 "--"
221 ["Show Variables" backtrace-toggle-locals
222 :style toggle
223 :active (backtrace-get-index)
224 :selected (plist-get (backtrace-get-view) :show-locals)
225 :help "Show or hide the local variables for the frame at point"]
226 ["Expand \"...\"s" backtrace-expand-ellipses
227 :help "Expand all the abbreviated forms in the current frame"]
228 ["Show on Multiple Lines" backtrace-multi-line
229 :help "Use line breaks and indentation to make a form more readable"]
230 ["Show on Single Line" backtrace-single-line]
231 "--"
232 ["Go to Source" backtrace-goto-source
233 :active (and (backtrace-get-index)
234 (plist-get (backtrace-frame-flags
235 (nth (backtrace-get-index) backtrace-frames))
236 :source-available))
237 :help "Show the source code for the current frame"]
238 ["Help for Symbol" backtrace-help-follow-symbol
239 :help "Show help for symbol at point"]
240 ["Describe Backtrace Mode" describe-mode
241 :help "Display documentation for backtrace-mode"]))
242 map)
243 "Local keymap for `backtrace-mode' buffers.")
244
245(defconst backtrace--flags-width 2
246 "Width in characters of the flags for a backtrace frame.")
247
248;;; Navigation and Text Properties
249
250;; This mode uses the following text properties:
251;; backtrace-index: The index into the buffer-local variable
252;; `backtrace-frames' for the frame at point, or nil if outside of a
253;; frame (in the buffer header).
254;; backtrace-view: A plist describing how the frame is printed. See
255;; the docstring for the buffer-local variable `backtrace-view.
256;; backtrace-section: The part of a frame which point is in. Either
257;; `func' or `locals'. At the moment just used to show and hide the
258;; local variables. Derived modes which do additional printing
259;; could define their own frame sections.
260;; backtrace-form: A value applied to each printed representation of a
261;; top-level s-expression, which needs to be different for sexps
262;; printed adjacent to each other, so the limits can be quickly
263;; found for pretty-printing.
264
265(defsubst backtrace-get-index (&optional pos)
266 "Return the index of the backtrace frame at POS.
267The value is an index into `backtrace-frames', or nil.
268POS, if omitted or nil, defaults to point."
269 (get-text-property (or pos (point)) 'backtrace-index))
270
271(defsubst backtrace-get-section (&optional pos)
272 "Return the section of a backtrace frame at POS.
273POS, if omitted or nil, defaults to point."
274 (get-text-property (or pos (point)) 'backtrace-section))
275
276(defsubst backtrace-get-view (&optional pos)
277 "Return the view plist of the backtrace frame at POS.
278POS, if omitted or nil, defaults to point."
279 (get-text-property (or pos (point)) 'backtrace-view))
280
281(defsubst backtrace-get-form (&optional pos)
282 "Return the backtrace form data for the form printed at POS.
283POS, if omitted or nil, defaults to point."
284 (get-text-property (or pos (point)) 'backtrace-form))
285
286(defun backtrace-get-frame-start (&optional pos)
287 "Return the beginning position of the frame at POS in the buffer.
288POS, if omitted or nil, defaults to point."
289 (let ((posn (or pos (point))))
290 (if (or (= (point-min) posn)
291 (not (eq (backtrace-get-index posn)
292 (backtrace-get-index (1- posn)))))
293 posn
294 (previous-single-property-change posn 'backtrace-index nil (point-min)))))
295
296(defun backtrace-get-frame-end (&optional pos)
297 "Return the position of the end of the frame at POS in the buffer.
298POS, if omitted or nil, defaults to point."
299 (next-single-property-change (or pos (point))
300 'backtrace-index nil (point-max)))
301
302(defun backtrace-forward-frame ()
303 "Move forward to the beginning of the next frame."
304 (interactive)
305 (let ((max (backtrace-get-frame-end)))
306 (when (= max (point-max))
307 (user-error "No next stack frame"))
308 (goto-char max)))
309
310(defun backtrace-backward-frame ()
311 "Move backward to the start of a stack frame."
312 (interactive)
313 (let ((current-index (backtrace-get-index))
314 (min (backtrace-get-frame-start)))
315 (if (or (and (/= (point) (point-max)) (null current-index))
316 (= min (point-min))
317 (and (= min (point))
318 (null (backtrace-get-index (1- min)))))
319 (user-error "No previous stack frame"))
320 (if (= min (point))
321 (goto-char (backtrace-get-frame-start (1- min)))
322 (goto-char min))))
323
324;; Other Backtrace mode commands
325
326(defun backtrace-revert (&rest _ignored)
327 "The `revert-buffer-function' for `backtrace-mode'.
328It runs `backtrace-revert-hook', then calls `backtrace-print'."
329 (interactive)
330 (unless (derived-mode-p 'backtrace-mode)
331 (error "The current buffer is not in Backtrace mode"))
332 (run-hooks 'backtrace-revert-hook)
333 (backtrace-print t))
334
335(defmacro backtrace--with-output-variables (view &rest body)
336 "Bind output variables according to VIEW and execute BODY."
337 (declare (indent 1))
338 `(let ((print-escape-control-characters t)
339 (print-escape-newlines t)
340 (print-circle (plist-get ,view :print-circle))
341 (standard-output (current-buffer)))
342 ,@body))
343
344(defun backtrace-toggle-locals (&optional all)
345 "Toggle the display of local variables for the backtrace frame at point.
346With prefix argument ALL, toggle the value of :show-locals in
347`backtrace-view', which affects all of the backtrace frames in
348the buffer."
349 (interactive "P")
350 (if all
351 (let ((pos (make-marker))
352 (visible (not (plist-get backtrace-view :show-locals))))
353 (setq backtrace-view (plist-put backtrace-view :show-locals visible))
354 (set-marker-insertion-type pos t)
355 (set-marker pos (point))
356 (goto-char (point-min))
357 ;; Skip the header.
358 (unless (backtrace-get-index)
359 (goto-char (backtrace-get-frame-end)))
360 (while (< (point) (point-max))
361 (backtrace--set-frame-locals-visible visible)
362 (goto-char (backtrace-get-frame-end)))
363 (goto-char pos)
364 (when (invisible-p pos)
365 (goto-char (backtrace-get-frame-start))))
366 (let ((index (backtrace-get-index)))
367 (unless index
368 (user-error "Not in a stack frame"))
369 (backtrace--set-frame-locals-visible
370 (not (plist-get (backtrace-get-view) :show-locals))))))
371
372(defun backtrace--set-frame-locals-visible (visible)
373 "Set the visibility of the local vars for the frame at point to VISIBLE."
374 (let ((pos (point))
375 (index (backtrace-get-index))
376 (start (backtrace-get-frame-start))
377 (end (backtrace-get-frame-end))
378 (view (copy-sequence (backtrace-get-view)))
379 (inhibit-read-only t))
380 (setq view (plist-put view :show-locals visible))
381 (goto-char (backtrace-get-frame-start))
382 (while (not (or (= (point) end)
383 (eq (backtrace-get-section) 'locals)))
384 (goto-char (next-single-property-change (point)
385 'backtrace-section nil end)))
386 (cond
387 ((and (= (point) end) visible)
388 ;; The locals section doesn't exist so create it.
389 (let ((standard-output (current-buffer)))
390 (backtrace--with-output-variables view
391 (backtrace--print-locals
392 (nth index backtrace-frames) view))
393 (add-text-properties end (point) `(backtrace-index ,index))
394 (goto-char pos)))
395 ((/= (point) end)
396 ;; The locals section does exist, so add or remove the overlay.
397 (backtrace--set-locals-visible-overlay (point) end visible)
398 (goto-char (if (invisible-p pos) start pos))))
399 (add-text-properties start (backtrace-get-frame-end)
400 `(backtrace-view ,view))))
401
402(defun backtrace--set-locals-visible-overlay (beg end visible)
403 (backtrace--change-button-skip beg end (not visible))
404 (if visible
405 (remove-overlays beg end 'invisible t)
406 (let ((o (make-overlay beg end)))
407 (overlay-put o 'invisible t)
408 (overlay-put o 'evaporate t))))
409
410(defun backtrace--change-button-skip (beg end value)
411 "Change the skip property on all buttons between BEG and END.
412Set it to VALUE unless the button is a `backtrace-ellipsis' button."
413 (let ((inhibit-read-only t))
414 (setq beg (next-button beg))
415 (while (and beg (< beg end))
416 (unless (eq (button-type beg) 'backtrace-ellipsis)
417 (button-put beg 'skip value))
418 (setq beg (next-button beg)))))
419
420(defun backtrace-toggle-print-circle (&optional all)
421 "Toggle `print-circle' for the backtrace frame at point.
422With prefix argument ALL, toggle the value of :print-circle in
423`backtrace-view', which affects all of the backtrace frames in
424the buffer."
425 (interactive "P")
426 (backtrace--toggle-feature :print-circle all))
427
428(defun backtrace--toggle-feature (feature all)
429 "Toggle FEATURE for the current backtrace frame or for the buffer.
430FEATURE should be one of the options in `backtrace-view'. If ALL
431is non-nil, toggle FEATURE for all frames in the buffer. After
432toggling the feature, reprint the affected frame(s). Afterwards
433position point at the start of the frame it was in before."
434 (if all
435 (let ((index (backtrace-get-index))
436 (pos (point))
437 (at-end (= (point) (point-max)))
438 (value (not (plist-get backtrace-view feature))))
439 (setq backtrace-view (plist-put backtrace-view feature value))
440 (goto-char (point-min))
441 ;; Skip the header.
442 (unless (backtrace-get-index)
443 (goto-char (backtrace-get-frame-end)))
444 (while (< (point) (point-max))
445 (backtrace--set-feature feature value)
446 (goto-char (backtrace-get-frame-end)))
447 (if (not index)
448 (goto-char (if at-end (point-max) pos))
449 (goto-char (point-min))
450 (while (and (not (eql index (backtrace-get-index)))
451 (< (point) (point-max)))
452 (goto-char (backtrace-get-frame-end)))))
453 (let ((index (backtrace-get-index)))
454 (unless index
455 (user-error "Not in a stack frame"))
456 (backtrace--set-feature feature
457 (not (plist-get (backtrace-get-view) feature))))))
458
459(defun backtrace--set-feature (feature value)
460 "Set FEATURE in the view plist of the frame at point to VALUE.
461Reprint the frame with the new view plist."
462 (let ((inhibit-read-only t)
463 (view (copy-sequence (backtrace-get-view)))
464 (index (backtrace-get-index))
465 (min (backtrace-get-frame-start))
466 (max (backtrace-get-frame-end)))
467 (setq view (plist-put view feature value))
468 (delete-region min max)
469 (goto-char min)
470 (backtrace-print-frame (nth index backtrace-frames) view)
471 (add-text-properties min (point)
472 `(backtrace-index ,index backtrace-view ,view))
473 (goto-char min)))
474
475(defun backtrace-expand-ellipsis (button)
476 "Expand display of the elided form at BUTTON."
477 (interactive)
478 (goto-char (button-start button))
479 (unless (get-text-property (point) 'cl-print-ellipsis)
480 (if (and (> (point) (point-min))
481 (get-text-property (1- (point)) 'cl-print-ellipsis))
482 (backward-char)
483 (user-error "No ellipsis to expand here")))
484 (let* ((end (next-single-property-change (point) 'cl-print-ellipsis))
485 (begin (previous-single-property-change end 'cl-print-ellipsis))
486 (value (get-text-property begin 'cl-print-ellipsis))
487 (props (backtrace-get-text-properties begin))
488 (inhibit-read-only t))
489 (backtrace--with-output-variables (backtrace-get-view)
490 (delete-region begin end)
491 (insert (cl-print-to-string-with-limit #'cl-print-expand-ellipsis value
492 backtrace-line-length))
493 (setq end (point))
494 (goto-char begin)
495 (while (< (point) end)
496 (let ((next (next-single-property-change (point) 'cl-print-ellipsis
497 nil end)))
498 (when (get-text-property (point) 'cl-print-ellipsis)
499 (make-text-button (point) next :type 'backtrace-ellipsis))
500 (goto-char next)))
501 (goto-char begin)
502 (add-text-properties begin end props))))
503
504(defun backtrace-expand-ellipses (&optional no-limit)
505 "Expand display of all \"...\"s in the backtrace frame at point.
506\\<backtrace-mode-map>
507Each ellipsis will be limited to `backtrace-line-length'
508characters in its expansion. With optional prefix argument
509NO-LIMIT, do not limit the number of characters. Note that with
510or without the argument, using this command can result in very
511long lines and very poor display performance. If this happens
512and is a problem, use `\\[revert-buffer]' to return to the
513initial state of the Backtrace buffer."
514 (interactive "P")
515 (save-excursion
516 (let ((start (backtrace-get-frame-start))
517 (end (backtrace-get-frame-end))
518 (backtrace-line-length (unless no-limit backtrace-line-length)))
519 (goto-char end)
520 (while (> (point) start)
521 (let ((next (previous-single-property-change (point) 'cl-print-ellipsis
522 nil start)))
523 (when (get-text-property (point) 'cl-print-ellipsis)
524 (push-button (point)))
525 (goto-char next))))))
526
527(defun backtrace-multi-line ()
528 "Show the top level s-expression at point on multiple lines with indentation."
529 (interactive)
530 (backtrace--reformat-sexp #'backtrace--multi-line))
531
532(defun backtrace--multi-line ()
533 "Pretty print the current buffer, then remove the trailing newline."
534 (set-syntax-table emacs-lisp-mode-syntax-table)
535 (pp-buffer)
536 (goto-char (1- (point-max)))
537 (delete-char 1))
538
539(defun backtrace-single-line ()
540 "Show the top level s-expression at point on one line."
541 (interactive)
542 (backtrace--reformat-sexp #'backtrace--single-line))
543
544(defun backtrace--single-line ()
545 "Replace line breaks and following indentation with spaces.
546Works on the current buffer."
547 (goto-char (point-min))
548 (while (re-search-forward "\n[[:blank:]]*" nil t)
549 (replace-match " ")))
550
551(defun backtrace--reformat-sexp (format-function)
552 "Reformat the top level sexp at point.
553Locate the top level sexp at or following point on the same line,
554and reformat it with FORMAT-FUNCTION, preserving the location of
555point within the sexp. If no sexp is found before the end of
556the line or buffer, signal an error.
557
558FORMAT-FUNCTION will be called without arguments, with the
559current buffer set to a temporary buffer containing only the
560content of the sexp."
561 (let* ((orig-pos (point))
562 (pos (point))
563 (tag (backtrace-get-form pos))
564 (end (next-single-property-change pos 'backtrace-form))
565 (begin (previous-single-property-change end 'backtrace-form
566 nil (point-min))))
567 (unless tag
568 (when (or (= end (point-max)) (> end (point-at-eol)))
569 (user-error "No form here to reformat"))
570 (goto-char end)
571 (setq pos end
572 end (next-single-property-change pos 'backtrace-form)
573 begin (previous-single-property-change end 'backtrace-form
574 nil (point-min))))
575 (let* ((offset (when (>= orig-pos begin) (- orig-pos begin)))
576 (offset-marker (when offset (make-marker)))
577 (content (buffer-substring begin end))
578 (props (backtrace-get-text-properties begin))
579 (inhibit-read-only t))
580 (delete-region begin end)
581 (insert (with-temp-buffer
582 (insert content)
583 (when offset
584 (set-marker-insertion-type offset-marker t)
585 (set-marker offset-marker (+ (point-min) offset)))
586 (funcall format-function)
587 (when offset
588 (setq offset (- (marker-position offset-marker) (point-min))))
589 (buffer-string)))
590 (when offset
591 (set-marker offset-marker (+ begin offset)))
592 (save-excursion
593 (goto-char begin)
594 (indent-sexp))
595 (add-text-properties begin (point) props)
596 (if offset
597 (goto-char (marker-position offset-marker))
598 (goto-char orig-pos)))))
599
600(defun backtrace-get-text-properties (pos)
601 "Return a plist of backtrace-mode's text properties at POS."
602 (apply #'append
603 (mapcar (lambda (prop)
604 (list prop (get-text-property pos prop)))
605 '(backtrace-section backtrace-index backtrace-view
606 backtrace-form))))
607
608(defun backtrace-goto-source ()
609 "If its location is known, jump to the source code for the frame at point."
610 (interactive)
611 (let* ((index (or (backtrace-get-index) (user-error "Not in a stack frame")))
612 (frame (nth index backtrace-frames))
613 (source-available (plist-get (backtrace-frame-flags frame)
614 :source-available)))
615 (unless (and source-available
616 (catch 'done
617 (dolist (func backtrace-goto-source-functions)
618 (when (funcall func)
619 (throw 'done t)))))
620 (user-error "Source code location not known"))))
621
622(defun backtrace-help-follow-symbol (&optional pos)
623 "Follow cross-reference at POS, defaulting to point.
624For the cross-reference format, see `help-make-xrefs'."
625 (interactive "d")
626 (unless pos
627 (setq pos (point)))
628 (unless (push-button pos)
629 ;; Check if the symbol under point is a function or variable.
630 (let ((sym
631 (intern
632 (save-excursion
633 (goto-char pos) (skip-syntax-backward "w_")
634 (buffer-substring (point)
635 (progn (skip-syntax-forward "w_")
636 (point)))))))
637 (when (or (boundp sym) (fboundp sym) (facep sym))
638 (describe-symbol sym)))))
639
640;; Print backtrace frames
641
642(defun backtrace-print (&optional remember-pos)
643 "Populate the current Backtrace mode buffer.
644This erases the buffer and inserts printed representations of the
645frames. Optional argument REMEMBER-POS, if non-nil, means to
646move point to the entry with the same ID element as the current
647line and recenter window line accordingly."
648 (let ((inhibit-read-only t)
649 entry-index saved-pt window-line)
650 (and remember-pos
651 (setq entry-index (backtrace-get-index))
652 (when (eq (window-buffer) (current-buffer))
653 (setq window-line
654 (count-screen-lines (window-start) (point)))))
655 (erase-buffer)
656 (when backtrace-insert-header-function
657 (funcall backtrace-insert-header-function))
658 (dotimes (idx (length backtrace-frames))
659 (let ((beg (point))
660 (elt (nth idx backtrace-frames)))
661 (and entry-index
662 (equal entry-index idx)
663 (setq entry-index nil
664 saved-pt (point)))
665 (backtrace-print-frame elt backtrace-view)
666 (add-text-properties
667 beg (point)
668 `(backtrace-index ,idx backtrace-view ,backtrace-view))))
669 (set-buffer-modified-p nil)
670 ;; If REMEMBER-POS was specified, move to the "old" location.
671 (if saved-pt
672 (progn (goto-char saved-pt)
673 (when window-line
674 (recenter window-line)))
675 (goto-char (point-min)))))
676
677;; Define button type used for ...'s.
678;; Set skip property so you don't have to TAB through 100 of them to
679;; get to the next function name.
680(define-button-type 'backtrace-ellipsis
681 'skip t 'action #'backtrace-expand-ellipsis
682 'help-echo "mouse-2, RET: expand this ellipsis")
683
684(defun backtrace-print-to-string (obj &optional limit)
685 "Return a printed representation of OBJ formatted for backtraces.
686Attempt to get the length of the returned string under LIMIT
687charcters with appropriate settings of `print-level' and
688`print-length.' LIMIT defaults to `backtrace-line-length'."
689 (backtrace--with-output-variables backtrace-view
690 (backtrace--print-to-string obj limit)))
691
692(defun backtrace--print-to-string (sexp &optional limit)
693 ;; This is for use by callers who wrap the call with
694 ;; backtrace--with-output-variables.
695 (setq limit (or limit backtrace-line-length))
696 (with-temp-buffer
697 (insert (cl-print-to-string-with-limit #'backtrace--print sexp limit))
698 ;; Add a unique backtrace-form property.
699 (put-text-property (point-min) (point) 'backtrace-form (gensym))
700 ;; Make buttons from all the "..."s. Since there might be many of
701 ;; them, use text property buttons.
702 (goto-char (point-min))
703 (while (< (point) (point-max))
704 (let ((end (next-single-property-change (point) 'cl-print-ellipsis
705 nil (point-max))))
706 (when (get-text-property (point) 'cl-print-ellipsis)
707 (make-text-button (point) end :type 'backtrace-ellipsis))
708 (goto-char end)))
709 (buffer-string)))
710
711(defun backtrace-print-frame (frame view)
712 "Insert a backtrace FRAME at point formatted according to VIEW.
713Tag the sections of the frame with the `backtrace-section' text
714property for use by navigation."
715 (backtrace--with-output-variables view
716 (backtrace--print-flags frame view)
717 (backtrace--print-func-and-args frame view)
718 (backtrace--print-locals frame view)))
719
720(defun backtrace--print-flags (frame view)
721 "Print the flags of a backtrace FRAME if enabled in VIEW."
722 (let ((beg (point))
723 (flag (plist-get (backtrace-frame-flags frame) :debug-on-exit))
724 (source (plist-get (backtrace-frame-flags frame) :source-available)))
725 (when (plist-get view :show-flags)
726 (when source (insert ">"))
727 (when flag (insert "*")))
728 (insert (make-string (- backtrace--flags-width (- (point) beg)) ?\s))
729 (put-text-property beg (point) 'backtrace-section 'func)))
730
731(defun backtrace--print-func-and-args (frame _view)
732 "Print the function, arguments and buffer position of a backtrace FRAME.
733Format it according to VIEW."
734 (let* ((beg (point))
735 (evald (backtrace-frame-evald frame))
736 (fun (backtrace-frame-fun frame))
737 (args (backtrace-frame-args frame))
738 (def (and (symbolp fun) (fboundp fun) (symbol-function fun)))
739 (fun-file (or (symbol-file fun 'defun)
740 (and (subrp def)
741 (not (eq 'unevalled (cdr (subr-arity def))))
742 (find-lisp-object-file-name fun def))))
743 (fun-pt (point)))
744 (cond
745 ((and evald (not debugger-stack-frame-as-list))
746 (if (atom fun)
747 (funcall backtrace-print-function fun)
748 (insert
749 (backtrace--print-to-string fun (when args (/ backtrace-line-length 2)))))
750 (if args
751 (insert (backtrace--print-to-string
752 args (max (truncate (/ backtrace-line-length 5))
753 (- backtrace-line-length (- (point) beg)))))
754 ;; The backtrace-form property is so that backtrace-multi-line
755 ;; will find it. backtrace-multi-line doesn't do anything
756 ;; useful with it, just being consistent.
757 (let ((start (point)))
758 (insert "()")
759 (put-text-property start (point) 'backtrace-form t))))
760 (t
761 (let ((fun-and-args (cons fun args)))
762 (insert (backtrace--print-to-string fun-and-args)))
763 (cl-incf fun-pt)))
764 (when fun-file
765 (make-text-button fun-pt (+ fun-pt (length (symbol-name fun)))
766 :type 'help-function-def
767 'help-args (list fun fun-file)))
768 ;; After any frame that uses eval-buffer, insert a comment that
769 ;; states the buffer position it's reading at.
770 (when (backtrace-frame-pos frame)
771 (insert " ; Reading at ")
772 (let ((pos (point)))
773 (insert (format "buffer position %d" (backtrace-frame-pos frame)))
774 (make-button pos (point) :type 'backtrace-buffer-pos
775 'backtrace-buffer (backtrace-frame-buffer frame)
776 'backtrace-pos (backtrace-frame-pos frame))))
777 (insert "\n")
778 (put-text-property beg (point) 'backtrace-section 'func)))
779
780(defun backtrace--print-locals (frame view)
781 "Print a backtrace FRAME's local variables according to VIEW.
782Print them only if :show-locals is non-nil in the VIEW plist."
783 (when (plist-get view :show-locals)
784 (let* ((beg (point))
785 (locals (backtrace-frame-locals frame)))
786 (if (null locals)
787 (insert " [no locals]\n")
788 (pcase-dolist (`(,symbol . ,value) locals)
789 (insert " ")
790 (backtrace--print symbol)
791 (insert " = ")
792 (insert (backtrace--print-to-string value))
793 (insert "\n")))
794 (put-text-property beg (point) 'backtrace-section 'locals))))
795
796(defun backtrace--print (obj &optional stream)
797 "Attempt to print OBJ to STREAM using `backtrace-print-function'.
798Fall back to `prin1' if there is an error."
799 (condition-case err
800 (funcall backtrace-print-function obj stream)
801 (error
802 (message "Error in backtrace printer: %S" err)
803 (prin1 obj stream))))
804
805(defun backtrace-update-flags ()
806 "Update the display of the flags in the backtrace frame at point."
807 (let ((view (backtrace-get-view))
808 (begin (backtrace-get-frame-start)))
809 (when (plist-get view :show-flags)
810 (save-excursion
811 (goto-char begin)
812 (let ((props (backtrace-get-text-properties begin))
813 (inhibit-read-only t)
814 (standard-output (current-buffer)))
815 (delete-char backtrace--flags-width)
816 (backtrace--print-flags (nth (backtrace-get-index) backtrace-frames)
817 view)
818 (add-text-properties begin (point) props))))))
819
820(defun backtrace--filter-visible (beg end &optional _delete)
821 "Return the visible text between BEG and END."
822 (let ((result ""))
823 (while (< beg end)
824 (let ((next (next-single-char-property-change beg 'invisible)))
825 (unless (get-char-property beg 'invisible)
826 (setq result (concat result (buffer-substring beg (min end next)))))
827 (setq beg next)))
828 result))
829
830;;; The mode definition
831
832(define-derived-mode backtrace-mode special-mode "Backtrace"
833 "Generic major mode for examining an Elisp stack backtrace.
834This mode can be used directly, or other major modes can be
835derived from it, using `define-derived-mode'.
836
837In this major mode, the buffer contains some optional lines of
838header text followed by backtrace frames, each consisting of one
839or more whole lines.
840
841Letters in this mode do not insert themselves; instead they are
842commands.
843\\<backtrace-mode-map>
844\\{backtrace-mode-map}
845
846A mode which inherits from Backtrace mode, or a command which
847creates a backtrace-mode buffer, should usually do the following:
848
849 - Set `backtrace-revert-hook', if the buffer contents need
850 to be specially recomputed prior to `revert-buffer'.
851 - Maybe set `backtrace-insert-header-function' to a function to create
852 header text for the buffer.
853 - Set `backtrace-frames' (see below).
854 - Maybe modify `backtrace-view' (see below).
855 - Maybe set `backtrace-print-function'.
856
857A command which creates or switches to a Backtrace mode buffer,
858such as `ert-results-pop-to-backtrace-for-test-at-point', should
859initialize `backtrace-frames' to a list of `backtrace-frame'
860objects (`backtrace-get-frames' is provided for that purpose, if
861desired), and may optionally modify `backtrace-view', which is a
862plist describing the appearance of the backtrace. Finally, it
863should call `backtrace-print'.
864
865`backtrace-print' calls `backtrace-insert-header-function'
866followed by `backtrace-print-frame', once for each stack frame."
867 :syntax-table emacs-lisp-mode-syntax-table
868 (when backtrace-fontify
869 (setq font-lock-defaults
870 '((backtrace-font-lock-keywords
871 backtrace-font-lock-keywords-1
872 backtrace-font-lock-keywords-2)
873 nil nil nil nil
874 (font-lock-syntactic-face-function
875 . lisp-font-lock-syntactic-face-function))))
876 (setq truncate-lines t)
877 (buffer-disable-undo)
878 ;; In debug.el, from 1998 to 2009 this was set to nil, reason stated
879 ;; was because of bytecode. Since 2009 it's been set to t, but the
880 ;; default is t so I think this isn't necessary.
881 ;; (set-buffer-multibyte t)
882 (setq-local revert-buffer-function #'backtrace-revert)
883 (setq-local filter-buffer-substring-function #'backtrace--filter-visible)
884 (setq-local indent-line-function 'lisp-indent-line)
885 (setq-local indent-region-function 'lisp-indent-region)
886 (add-hook 'xref-backend-functions #'backtrace--xref-backend nil t))
887
888(put 'backtrace-mode 'mode-class 'special)
889
890;;; Backtrace printing
891
892;;;###autoload
893(defun backtrace ()
894 "Print a trace of Lisp function calls currently active.
895Output stream used is value of `standard-output'."
896 (princ (backtrace-to-string (backtrace-get-frames 'backtrace)))
897 nil)
898
899(defun backtrace-to-string(&optional frames)
900 "Format FRAMES, a list of `backtrace-frame' objects, for output.
901Return the result as a string. If FRAMES is nil, use all
902function calls currently active."
903 (unless frames (setq frames (backtrace-get-frames 'backtrace-to-string)))
904 (let ((backtrace-fontify nil))
905 (with-temp-buffer
906 (backtrace-mode)
907 (setq backtrace-view '(:show-flags t)
908 backtrace-frames frames
909 backtrace-print-function #'cl-prin1)
910 (backtrace-print)
911 (substring-no-properties (filter-buffer-substring (point-min)
912 (point-max))))))
913
914(provide 'backtrace)
915
916;;; backtrace.el ends here
diff --git a/lisp/emacs-lisp/cl-print.el b/lisp/emacs-lisp/cl-print.el
index bf5b1e878d5..c63f5ac005c 100644
--- a/lisp/emacs-lisp/cl-print.el
+++ b/lisp/emacs-lisp/cl-print.el
@@ -55,10 +55,19 @@ call other entry points instead, such as `cl-prin1'."
55 ;; we should only use it for objects which don't have nesting. 55 ;; we should only use it for objects which don't have nesting.
56 (prin1 object stream)) 56 (prin1 object stream))
57 57
58(cl-defgeneric cl-print-object-contents (_object _start _stream)
59 "Dispatcher to print the contents of OBJECT on STREAM.
60Print the contents starting with the item at START, without
61delimiters."
62 ;; Every cl-print-object method which can print an ellipsis should
63 ;; have a matching cl-print-object-contents method to expand an
64 ;; ellipsis.
65 (error "Missing cl-print-object-contents method"))
66
58(cl-defmethod cl-print-object ((object cons) stream) 67(cl-defmethod cl-print-object ((object cons) stream)
59 (if (and cl-print--depth (natnump print-level) 68 (if (and cl-print--depth (natnump print-level)
60 (> cl-print--depth print-level)) 69 (> cl-print--depth print-level))
61 (princ "..." stream) 70 (cl-print-insert-ellipsis object 0 stream)
62 (let ((car (pop object)) 71 (let ((car (pop object))
63 (count 1)) 72 (count 1))
64 (if (and print-quoted 73 (if (and print-quoted
@@ -84,23 +93,60 @@ call other entry points instead, such as `cl-prin1'."
84 (princ " " stream) 93 (princ " " stream)
85 (if (or (not (natnump print-length)) (> print-length count)) 94 (if (or (not (natnump print-length)) (> print-length count))
86 (cl-print-object (pop object) stream) 95 (cl-print-object (pop object) stream)
87 (princ "..." stream) 96 (cl-print-insert-ellipsis object print-length stream)
88 (setq object nil)) 97 (setq object nil))
89 (cl-incf count)) 98 (cl-incf count))
90 (when object 99 (when object
91 (princ " . " stream) (cl-print-object object stream)) 100 (princ " . " stream) (cl-print-object object stream))
92 (princ ")" stream))))) 101 (princ ")" stream)))))
93 102
103(cl-defmethod cl-print-object-contents ((object cons) _start stream)
104 (let ((count 0))
105 (while (and (consp object)
106 (not (cond
107 (cl-print--number-table
108 (numberp (gethash object cl-print--number-table)))
109 ((memq object cl-print--currently-printing))
110 (t (push object cl-print--currently-printing)
111 nil))))
112 (unless (zerop count)
113 (princ " " stream))
114 (if (or (not (natnump print-length)) (> print-length count))
115 (cl-print-object (pop object) stream)
116 (cl-print-insert-ellipsis object print-length stream)
117 (setq object nil))
118 (cl-incf count))
119 (when object
120 (princ " . " stream) (cl-print-object object stream))))
121
94(cl-defmethod cl-print-object ((object vector) stream) 122(cl-defmethod cl-print-object ((object vector) stream)
95 (princ "[" stream) 123 (if (and cl-print--depth (natnump print-level)
96 (let ((count (length object))) 124 (> cl-print--depth print-level))
97 (dotimes (i (if (natnump print-length) 125 (cl-print-insert-ellipsis object 0 stream)
98 (min print-length count) count)) 126 (princ "[" stream)
99 (unless (zerop i) (princ " " stream)) 127 (let* ((len (length object))
100 (cl-print-object (aref object i) stream)) 128 (limit (if (natnump print-length)
101 (when (and (natnump print-length) (< print-length count)) 129 (min print-length len) len)))
102 (princ " ..." stream))) 130 (dotimes (i limit)
103 (princ "]" stream)) 131 (unless (zerop i) (princ " " stream))
132 (cl-print-object (aref object i) stream))
133 (when (< limit len)
134 (princ " " stream)
135 (cl-print-insert-ellipsis object limit stream)))
136 (princ "]" stream)))
137
138(cl-defmethod cl-print-object-contents ((object vector) start stream)
139 (let* ((len (length object))
140 (limit (if (natnump print-length)
141 (min (+ start print-length) len) len))
142 (i start))
143 (while (< i limit)
144 (unless (= i start) (princ " " stream))
145 (cl-print-object (aref object i) stream)
146 (cl-incf i))
147 (when (< limit len)
148 (princ " " stream)
149 (cl-print-insert-ellipsis object limit stream))))
104 150
105(cl-defmethod cl-print-object ((object hash-table) stream) 151(cl-defmethod cl-print-object ((object hash-table) stream)
106 (princ "#<hash-table " stream) 152 (princ "#<hash-table " stream)
@@ -199,21 +245,135 @@ into a button whose action shows the function's disassembly.")
199 (princ ")" stream))) 245 (princ ")" stream)))
200 246
201(cl-defmethod cl-print-object ((object cl-structure-object) stream) 247(cl-defmethod cl-print-object ((object cl-structure-object) stream)
202 (princ "#s(" stream) 248 (if (and cl-print--depth (natnump print-level)
249 (> cl-print--depth print-level))
250 (cl-print-insert-ellipsis object 0 stream)
251 (princ "#s(" stream)
252 (let* ((class (cl-find-class (type-of object)))
253 (slots (cl--struct-class-slots class))
254 (len (length slots))
255 (limit (if (natnump print-length)
256 (min print-length len) len)))
257 (princ (cl--struct-class-name class) stream)
258 (dotimes (i limit)
259 (let ((slot (aref slots i)))
260 (princ " :" stream)
261 (princ (cl--slot-descriptor-name slot) stream)
262 (princ " " stream)
263 (cl-print-object (aref object (1+ i)) stream)))
264 (when (< limit len)
265 (princ " " stream)
266 (cl-print-insert-ellipsis object limit stream)))
267 (princ ")" stream)))
268
269(cl-defmethod cl-print-object-contents ((object cl-structure-object) start stream)
203 (let* ((class (cl-find-class (type-of object))) 270 (let* ((class (cl-find-class (type-of object)))
204 (slots (cl--struct-class-slots class)) 271 (slots (cl--struct-class-slots class))
205 (count (length slots))) 272 (len (length slots))
206 (princ (cl--struct-class-name class) stream) 273 (limit (if (natnump print-length)
207 (dotimes (i (if (natnump print-length) 274 (min (+ start print-length) len) len))
208 (min print-length count) count)) 275 (i start))
276 (while (< i limit)
209 (let ((slot (aref slots i))) 277 (let ((slot (aref slots i)))
210 (princ " :" stream) 278 (unless (= i start) (princ " " stream))
279 (princ ":" stream)
211 (princ (cl--slot-descriptor-name slot) stream) 280 (princ (cl--slot-descriptor-name slot) stream)
212 (princ " " stream) 281 (princ " " stream)
213 (cl-print-object (aref object (1+ i)) stream))) 282 (cl-print-object (aref object (1+ i)) stream))
214 (when (and (natnump print-length) (< print-length count)) 283 (cl-incf i))
215 (princ " ..." stream))) 284 (when (< limit len)
216 (princ ")" stream)) 285 (princ " " stream)
286 (cl-print-insert-ellipsis object limit stream))))
287
288(cl-defmethod cl-print-object ((object string) stream)
289 (unless stream (setq stream standard-output))
290 (let* ((has-properties (or (text-properties-at 0 object)
291 (next-property-change 0 object)))
292 (len (length object))
293 (limit (if (natnump print-length) (min print-length len) len)))
294 (if (and has-properties
295 cl-print--depth
296 (natnump print-level)
297 (> cl-print--depth print-level))
298 (cl-print-insert-ellipsis object 0 stream)
299 ;; Print all or part of the string
300 (when has-properties
301 (princ "#(" stream))
302 (if (= limit len)
303 (prin1 (if has-properties (substring-no-properties object) object)
304 stream)
305 (let ((part (concat (substring-no-properties object 0 limit) "...")))
306 (prin1 part stream)
307 (when (bufferp stream)
308 (with-current-buffer stream
309 (cl-print-propertize-ellipsis object limit
310 (- (point) 4)
311 (- (point) 1) stream)))))
312 ;; Print the property list.
313 (when has-properties
314 (let* ((interval-limit (and (natnump print-length)
315 (max 1 (/ print-length 3))))
316 (interval-count 0)
317 (start-pos (if (text-properties-at 0 object)
318 0 (next-property-change 0 object)))
319 (end-pos (next-property-change start-pos object len)))
320 (while (and (or (null interval-limit)
321 (< interval-count interval-limit))
322 (< start-pos len))
323 (let ((props (text-properties-at start-pos object)))
324 (when props
325 (princ " " stream) (princ start-pos stream)
326 (princ " " stream) (princ end-pos stream)
327 (princ " " stream) (cl-print-object props stream)
328 (cl-incf interval-count))
329 (setq start-pos end-pos
330 end-pos (next-property-change start-pos object len))))
331 (when (< start-pos len)
332 (princ " " stream)
333 (cl-print-insert-ellipsis object (list start-pos) stream)))
334 (princ ")" stream)))))
335
336(cl-defmethod cl-print-object-contents ((object string) start stream)
337 ;; If START is an integer, it is an index into the string, and the
338 ;; ellipsis that needs to be expanded is part of the string. If
339 ;; START is a cons, its car is an index into the string, and the
340 ;; ellipsis that needs to be expanded is in the property list.
341 (let* ((len (length object)))
342 (if (atom start)
343 ;; Print part of the string.
344 (let* ((limit (if (natnump print-length)
345 (min (+ start print-length) len) len))
346 (substr (substring-no-properties object start limit))
347 (printed (prin1-to-string substr))
348 (trimmed (substring printed 1 (1- (length printed)))))
349 (princ trimmed)
350 (when (< limit len)
351 (cl-print-insert-ellipsis object limit stream)))
352
353 ;; Print part of the property list.
354 (let* ((first t)
355 (interval-limit (and (natnump print-length)
356 (max 1 (/ print-length 3))))
357 (interval-count 0)
358 (start-pos (car start))
359 (end-pos (next-property-change start-pos object len)))
360 (while (and (or (null interval-limit)
361 (< interval-count interval-limit))
362 (< start-pos len))
363 (let ((props (text-properties-at start-pos object)))
364 (when props
365 (if first
366 (setq first nil)
367 (princ " " stream))
368 (princ start-pos stream)
369 (princ " " stream) (princ end-pos stream)
370 (princ " " stream) (cl-print-object props stream)
371 (cl-incf interval-count))
372 (setq start-pos end-pos
373 end-pos (next-property-change start-pos object len))))
374 (when (< start-pos len)
375 (princ " " stream)
376 (cl-print-insert-ellipsis object (list start-pos) stream))))))
217 377
218;;; Circularity and sharing. 378;;; Circularity and sharing.
219 379
@@ -275,8 +435,17 @@ into a button whose action shows the function's disassembly.")
275 (push cdr stack) 435 (push cdr stack)
276 (push car stack)) 436 (push car stack))
277 ((pred stringp) 437 ((pred stringp)
278 ;; We presumably won't print its text-properties. 438 (let* ((len (length object))
279 nil) 439 (start (if (text-properties-at 0 object)
440 0 (next-property-change 0 object)))
441 (end (and start
442 (next-property-change start object len))))
443 (while (and start (< start len))
444 (let ((props (text-properties-at start object)))
445 (when props
446 (push props stack))
447 (setq start end
448 end (next-property-change start object len))))))
280 ((or (pred arrayp) (pred byte-code-function-p)) 449 ((or (pred arrayp) (pred byte-code-function-p))
281 ;; FIXME: Inefficient for char-tables! 450 ;; FIXME: Inefficient for char-tables!
282 (dotimes (i (length object)) 451 (dotimes (i (length object))
@@ -291,6 +460,48 @@ into a button whose action shows the function's disassembly.")
291 (cl-print--find-sharing object print-number-table))) 460 (cl-print--find-sharing object print-number-table)))
292 print-number-table)) 461 print-number-table))
293 462
463(defun cl-print-insert-ellipsis (object start stream)
464 "Print \"...\" to STREAM with the `cl-print-ellipsis' text property.
465Save state in the text property in order to print the elided part
466of OBJECT later. START should be 0 if the whole OBJECT is being
467elided, otherwise it should be an index or other pointer into the
468internals of OBJECT which can be passed to
469`cl-print-object-contents' at a future time."
470 (unless stream (setq stream standard-output))
471 (let ((ellipsis-start (and (bufferp stream)
472 (with-current-buffer stream (point)))))
473 (princ "..." stream)
474 (when ellipsis-start
475 (with-current-buffer stream
476 (cl-print-propertize-ellipsis object start ellipsis-start (point)
477 stream)))))
478
479(defun cl-print-propertize-ellipsis (object start beg end stream)
480 "Add the `cl-print-ellipsis' property between BEG and END.
481STREAM should be a buffer. OBJECT and START are as described in
482`cl-print-insert-ellipsis'."
483 (let ((value (list object start cl-print--number-table
484 cl-print--currently-printing)))
485 (with-current-buffer stream
486 (put-text-property beg end 'cl-print-ellipsis value stream))))
487
488;;;###autoload
489(defun cl-print-expand-ellipsis (value stream)
490 "Print the expansion of an ellipsis to STREAM.
491VALUE should be the value of the `cl-print-ellipsis' text property
492which was attached to the ellipsis by `cl-prin1'."
493 (let ((cl-print--depth 1)
494 (object (nth 0 value))
495 (start (nth 1 value))
496 (cl-print--number-table (nth 2 value))
497 (print-number-table (nth 2 value))
498 (cl-print--currently-printing (nth 3 value)))
499 (when (eq object (car cl-print--currently-printing))
500 (pop cl-print--currently-printing))
501 (if (equal start 0)
502 (cl-print-object object stream)
503 (cl-print-object-contents object start stream))))
504
294;;;###autoload 505;;;###autoload
295(defun cl-prin1 (object &optional stream) 506(defun cl-prin1 (object &optional stream)
296 "Print OBJECT on STREAM according to its type. 507 "Print OBJECT on STREAM according to its type.
@@ -313,5 +524,45 @@ node `(elisp)Output Variables'."
313 (cl-prin1 object (current-buffer)) 524 (cl-prin1 object (current-buffer))
314 (buffer-string))) 525 (buffer-string)))
315 526
527;;;###autoload
528(defun cl-print-to-string-with-limit (print-function value limit)
529 "Return a string containing a printed representation of VALUE.
530Attempt to get the length of the returned string under LIMIT
531characters with appropriate settings of `print-level' and
532`print-length.' Use PRINT-FUNCTION to print, which should take
533the arguments VALUE and STREAM and which should respect
534`print-length' and `print-level'. LIMIT may be nil or zero in
535which case PRINT-FUNCTION will be called with `print-level' and
536`print-length' bound to nil.
537
538Use this function with `cl-prin1' to print an object,
539abbreviating it with ellipses to fit within a size limit. Use
540this function with `cl-prin1-expand-ellipsis' to expand an
541ellipsis, abbreviating the expansion to stay within a size
542limit."
543 (setq limit (and (natnump limit)
544 (not (zerop limit))
545 limit))
546 ;; Since this is used by the debugger when stack space may be
547 ;; limited, if you increase print-level here, add more depth in
548 ;; call_debugger (bug#31919).
549 (let* ((print-length (when limit (min limit 50)))
550 (print-level (when limit (min 8 (truncate (log limit)))))
551 (delta (when limit
552 (max 1 (truncate (/ print-length print-level))))))
553 (with-temp-buffer
554 (catch 'done
555 (while t
556 (erase-buffer)
557 (funcall print-function value (current-buffer))
558 ;; Stop when either print-level is too low or the value is
559 ;; successfully printed in the space allowed.
560 (when (or (not limit)
561 (< (- (point-max) (point-min)) limit)
562 (= print-level 2))
563 (throw 'done (buffer-string)))
564 (cl-decf print-level)
565 (cl-decf print-length delta))))))
566
316(provide 'cl-print) 567(provide 'cl-print)
317;;; cl-print.el ends here 568;;; cl-print.el ends here
diff --git a/lisp/emacs-lisp/debug.el b/lisp/emacs-lisp/debug.el
index 0efaa637129..7fc2b41c70c 100644
--- a/lisp/emacs-lisp/debug.el
+++ b/lisp/emacs-lisp/debug.el
@@ -28,6 +28,7 @@
28;;; Code: 28;;; Code:
29 29
30(require 'cl-lib) 30(require 'cl-lib)
31(require 'backtrace)
31(require 'button) 32(require 'button)
32 33
33(defgroup debugger nil 34(defgroup debugger nil
@@ -133,6 +134,25 @@ where CAUSE can be:
133- exit: called because of exit of a flagged function. 134- exit: called because of exit of a flagged function.
134- error: called because of `debug-on-error'.") 135- error: called because of `debug-on-error'.")
135 136
137(cl-defstruct (debugger--buffer-state
138 (:constructor debugger--save-buffer-state
139 (&aux (mode major-mode)
140 (header backtrace-insert-header-function)
141 (frames backtrace-frames)
142 (content (buffer-string))
143 (pos (point)))))
144 mode header frames content pos)
145
146(defun debugger--restore-buffer-state (state)
147 (unless (derived-mode-p (debugger--buffer-state-mode state))
148 (funcall (debugger--buffer-state-mode state)))
149 (setq backtrace-insert-header-function (debugger--buffer-state-header state)
150 backtrace-frames (debugger--buffer-state-frames state))
151 (let ((inhibit-read-only t))
152 (erase-buffer)
153 (insert (debugger--buffer-state-content state)))
154 (goto-char (debugger--buffer-state-pos state)))
155
136;;;###autoload 156;;;###autoload
137(setq debugger 'debug) 157(setq debugger 'debug)
138;;;###autoload 158;;;###autoload
@@ -174,7 +194,7 @@ first will be printed into the backtrace buffer."
174 (debugger-previous-state 194 (debugger-previous-state
175 (if (get-buffer "*Backtrace*") 195 (if (get-buffer "*Backtrace*")
176 (with-current-buffer (get-buffer "*Backtrace*") 196 (with-current-buffer (get-buffer "*Backtrace*")
177 (list major-mode (buffer-string))))) 197 (debugger--save-buffer-state))))
178 (debugger-args args) 198 (debugger-args args)
179 (debugger-buffer (get-buffer-create "*Backtrace*")) 199 (debugger-buffer (get-buffer-create "*Backtrace*"))
180 (debugger-old-buffer (current-buffer)) 200 (debugger-old-buffer (current-buffer))
@@ -236,7 +256,8 @@ first will be printed into the backtrace buffer."
236 (window-total-height debugger-window))) 256 (window-total-height debugger-window)))
237 (error nil))) 257 (error nil)))
238 (setq debugger-previous-window debugger-window)) 258 (setq debugger-previous-window debugger-window))
239 (debugger-mode) 259 (unless (derived-mode-p 'debugger-mode)
260 (debugger-mode))
240 (debugger-setup-buffer debugger-args) 261 (debugger-setup-buffer debugger-args)
241 (when noninteractive 262 (when noninteractive
242 ;; If the backtrace is long, save the beginning 263 ;; If the backtrace is long, save the beginning
@@ -280,15 +301,14 @@ first will be printed into the backtrace buffer."
280 (setq debugger-previous-window nil)) 301 (setq debugger-previous-window nil))
281 ;; Restore previous state of debugger-buffer in case we were 302 ;; Restore previous state of debugger-buffer in case we were
282 ;; in a recursive invocation of the debugger, otherwise just 303 ;; in a recursive invocation of the debugger, otherwise just
283 ;; erase the buffer and put it into fundamental mode. 304 ;; erase the buffer.
284 (when (buffer-live-p debugger-buffer) 305 (when (buffer-live-p debugger-buffer)
285 (with-current-buffer debugger-buffer 306 (with-current-buffer debugger-buffer
286 (let ((inhibit-read-only t)) 307 (if debugger-previous-state
287 (erase-buffer) 308 (debugger--restore-buffer-state debugger-previous-state)
288 (if (null debugger-previous-state) 309 (setq backtrace-insert-header-function nil)
289 (fundamental-mode) 310 (setq backtrace-frames nil)
290 (insert (nth 1 debugger-previous-state)) 311 (backtrace-print))))
291 (funcall (nth 0 debugger-previous-state))))))
292 (with-timeout-unsuspend debugger-with-timeout-suspend) 312 (with-timeout-unsuspend debugger-with-timeout-suspend)
293 (set-match-data debugger-outer-match-data))) 313 (set-match-data debugger-outer-match-data)))
294 (setq debug-on-next-call debugger-step-after-exit) 314 (setq debug-on-next-call debugger-step-after-exit)
@@ -301,112 +321,80 @@ first will be printed into the backtrace buffer."
301 (message "Error in debug printer: %S" err) 321 (message "Error in debug printer: %S" err)
302 (prin1 obj stream)))) 322 (prin1 obj stream))))
303 323
304(defun debugger-insert-backtrace (frames do-xrefs)
305 "Format and insert the backtrace FRAMES at point.
306Make functions into cross-reference buttons if DO-XREFS is non-nil."
307 (let ((standard-output (current-buffer))
308 (eval-buffers eval-buffer-list))
309 (require 'help-mode) ; Define `help-function-def' button type.
310 (pcase-dolist (`(,evald ,fun ,args ,flags) frames)
311 (insert (if (plist-get flags :debug-on-exit)
312 "* " " "))
313 (let ((fun-file (and do-xrefs (symbol-file fun 'defun)))
314 (fun-pt (point)))
315 (cond
316 ((and evald (not debugger-stack-frame-as-list))
317 (debugger--print fun)
318 (if args (debugger--print args) (princ "()")))
319 (t
320 (debugger--print (cons fun args))
321 (cl-incf fun-pt)))
322 (when fun-file
323 (make-text-button fun-pt (+ fun-pt (length (symbol-name fun)))
324 :type 'help-function-def
325 'help-args (list fun fun-file))))
326 ;; After any frame that uses eval-buffer, insert a line that
327 ;; states the buffer position it's reading at.
328 (when (and eval-buffers (memq fun '(eval-buffer eval-region)))
329 (insert (format " ; Reading at buffer position %d"
330 ;; This will get the wrong result if there are
331 ;; two nested eval-region calls for the same
332 ;; buffer. That's not a very useful case.
333 (with-current-buffer (pop eval-buffers)
334 (point)))))
335 (insert "\n"))))
336
337(defun debugger-setup-buffer (args) 324(defun debugger-setup-buffer (args)
338 "Initialize the `*Backtrace*' buffer for entry to the debugger. 325 "Initialize the `*Backtrace*' buffer for entry to the debugger.
339That buffer should be current already." 326That buffer should be current already and in debugger-mode."
340 (setq buffer-read-only nil) 327 (setq backtrace-frames (nthcdr
341 (erase-buffer) 328 ;; Remove debug--implement-debug-on-entry and the
342 (set-buffer-multibyte t) ;Why was it nil ? -stef 329 ;; advice's `apply' frame.
343 (setq buffer-undo-list t) 330 (if (eq (car args) 'debug) 3 1)
331 (backtrace-get-frames 'debug)))
332 (when (eq (car-safe args) 'exit)
333 (setq debugger-value (nth 1 args))
334 (setf (cl-getf (backtrace-frame-flags (car backtrace-frames))
335 :debug-on-exit)
336 nil))
337
338 (setq backtrace-view (plist-put backtrace-view :show-flags t)
339 backtrace-insert-header-function (lambda ()
340 (debugger--insert-header args))
341 backtrace-print-function debugger-print-function)
342 (backtrace-print)
343 ;; Place point on "stack frame 0" (bug#15101).
344 (goto-char (point-min))
345 (search-forward ":" (line-end-position) t)
346 (when (and (< (point) (line-end-position))
347 (= (char-after) ?\s))
348 (forward-char)))
349
350(defun debugger--insert-header (args)
351 "Insert the header for the debugger's Backtrace buffer.
352Include the reason for debugger entry from ARGS."
344 (insert "Debugger entered") 353 (insert "Debugger entered")
345 (let ((frames (nthcdr 354 (pcase (car args)
346 ;; Remove debug--implement-debug-on-entry and the 355 ;; lambda is for debug-on-call when a function call is next.
347 ;; advice's `apply' frame. 356 ;; debug is for debug-on-entry function called.
348 (if (eq (car args) 'debug) 3 1) 357 ((or `lambda `debug)
349 (backtrace-frames 'debug))) 358 (insert "--entering a function:\n"))
350 (print-escape-newlines t) 359 ;; Exiting a function.
351 (print-escape-control-characters t) 360 (`exit
352 ;; If you increase print-level, add more depth in call_debugger. 361 (insert "--returning value: ")
353 (print-level 8) 362 (insert (backtrace-print-to-string debugger-value))
354 (print-length 50) 363 (insert ?\n))
355 (pos (point))) 364 ;; Watchpoint triggered.
356 (pcase (car args) 365 ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args)))
357 ;; lambda is for debug-on-call when a function call is next. 366 (insert
358 ;; debug is for debug-on-entry function called. 367 "--"
359 ((or `lambda `debug) 368 (pcase details
360 (insert "--entering a function:\n") 369 (`(makunbound nil) (format "making %s void" symbol))
361 (setq pos (1- (point)))) 370 (`(makunbound ,buffer) (format "killing local value of %s in buffer %s"
362 ;; Exiting a function. 371 symbol buffer))
363 (`exit 372 (`(defvaralias ,_) (format "aliasing %s to %s" symbol newval))
364 (insert "--returning value: ") 373 (`(let ,_) (format "let-binding %s to %s" symbol
365 (setq pos (point)) 374 (backtrace-print-to-string newval)))
366 (setq debugger-value (nth 1 args)) 375 (`(unlet ,_) (format "ending let-binding of %s" symbol))
367 (debugger--print debugger-value (current-buffer)) 376 (`(set nil) (format "setting %s to %s" symbol
368 (setf (cl-getf (nth 3 (car frames)) :debug-on-exit) nil) 377 (backtrace-print-to-string newval)))
369 (insert ?\n)) 378 (`(set ,buffer) (format "setting %s in buffer %s to %s"
370 ;; Watchpoint triggered. 379 symbol buffer
371 ((and `watchpoint (let `(,symbol ,newval . ,details) (cdr args))) 380 (backtrace-print-to-string newval)))
372 (insert 381 (_ (error "unrecognized watchpoint triggered %S" (cdr args))))
373 "--" 382 ": ")
374 (pcase details 383 (insert ?\n))
375 (`(makunbound nil) (format "making %s void" symbol)) 384 ;; Debugger entered for an error.
376 (`(makunbound ,buffer) (format "killing local value of %s in buffer %s" 385 (`error
377 symbol buffer)) 386 (insert "--Lisp error: ")
378 (`(defvaralias ,_) (format "aliasing %s to %s" symbol newval)) 387 (insert (backtrace-print-to-string (nth 1 args)))
379 (`(let ,_) (format "let-binding %s to %S" symbol newval)) 388 (insert ?\n))
380 (`(unlet ,_) (format "ending let-binding of %s" symbol)) 389 ;; debug-on-call, when the next thing is an eval.
381 (`(set nil) (format "setting %s to %S" symbol newval)) 390 (`t
382 (`(set ,buffer) (format "setting %s in buffer %s to %S" 391 (insert "--beginning evaluation of function call form:\n"))
383 symbol buffer newval)) 392 ;; User calls debug directly.
384 (_ (error "unrecognized watchpoint triggered %S" (cdr args)))) 393 (_
385 ": ") 394 (insert ": ")
386 (setq pos (point)) 395 (insert (backtrace-print-to-string (if (eq (car args) 'nil)
387 (insert ?\n)) 396 (cdr args) args)))
388 ;; Debugger entered for an error. 397 (insert ?\n))))
389 (`error
390 (insert "--Lisp error: ")
391 (setq pos (point))
392 (debugger--print (nth 1 args) (current-buffer))
393 (insert ?\n))
394 ;; debug-on-call, when the next thing is an eval.
395 (`t
396 (insert "--beginning evaluation of function call form:\n")
397 (setq pos (1- (point))))
398 ;; User calls debug directly.
399 (_
400 (insert ": ")
401 (setq pos (point))
402 (debugger--print
403 (if (eq (car args) 'nil)
404 (cdr args) args)
405 (current-buffer))
406 (insert ?\n)))
407 (debugger-insert-backtrace frames t)
408 ;; Place point on "stack frame 0" (bug#15101).
409 (goto-char pos)))
410 398
411 399
412(defun debugger-step-through () 400(defun debugger-step-through ()
@@ -426,12 +414,12 @@ Enter another debugger on next entry to eval, apply or funcall."
426 (unless debugger-may-continue 414 (unless debugger-may-continue
427 (error "Cannot continue")) 415 (error "Cannot continue"))
428 (message "Continuing.") 416 (message "Continuing.")
429 (save-excursion 417
430 ;; Check to see if we've flagged some frame for debug-on-exit, in which 418 ;; Check to see if we've flagged some frame for debug-on-exit, in which
431 ;; case we'll probably come back to the debugger soon. 419 ;; case we'll probably come back to the debugger soon.
432 (goto-char (point-min)) 420 (dolist (frame backtrace-frames)
433 (if (re-search-forward "^\\* " nil t) 421 (when (plist-get (backtrace-frame-flags frame) :debug-on-exit)
434 (setq debugger-will-be-back t))) 422 (setq debugger-will-be-back t)))
435 (exit-recursive-edit)) 423 (exit-recursive-edit))
436 424
437(defun debugger-return-value (val) 425(defun debugger-return-value (val)
@@ -446,12 +434,11 @@ will be used, such as in a debug on exit from a frame."
446 (setq debugger-value val) 434 (setq debugger-value val)
447 (princ "Returning " t) 435 (princ "Returning " t)
448 (debugger--print debugger-value) 436 (debugger--print debugger-value)
449 (save-excursion
450 ;; Check to see if we've flagged some frame for debug-on-exit, in which 437 ;; Check to see if we've flagged some frame for debug-on-exit, in which
451 ;; case we'll probably come back to the debugger soon. 438 ;; case we'll probably come back to the debugger soon.
452 (goto-char (point-min)) 439 (dolist (frame backtrace-frames)
453 (if (re-search-forward "^\\* " nil t) 440 (when (plist-get (backtrace-frame-flags frame) :debug-on-exit)
454 (setq debugger-will-be-back t))) 441 (setq debugger-will-be-back t)))
455 (exit-recursive-edit)) 442 (exit-recursive-edit))
456 443
457(defun debugger-jump () 444(defun debugger-jump ()
@@ -473,63 +460,40 @@ removes itself from that hook."
473 460
474(defun debugger-frame-number (&optional skip-base) 461(defun debugger-frame-number (&optional skip-base)
475 "Return number of frames in backtrace before the one point points at." 462 "Return number of frames in backtrace before the one point points at."
476 (save-excursion 463 (let ((index (backtrace-get-index))
477 (beginning-of-line) 464 (count 0))
478 (if (looking-at " *;;;\\|[a-z]") 465 (unless index
479 (error "This line is not a function call")) 466 (error "This line is not a function call"))
480 (let ((opoint (point)) 467 (unless skip-base
481 (count 0))
482 (unless skip-base
483 (while (not (eq (cadr (backtrace-frame count)) 'debug)) 468 (while (not (eq (cadr (backtrace-frame count)) 'debug))
484 (setq count (1+ count))) 469 (setq count (1+ count)))
485 ;; Skip debug--implement-debug-on-entry frame. 470 ;; Skip debug--implement-debug-on-entry frame.
486 (when (eq 'debug--implement-debug-on-entry 471 (when (eq 'debug--implement-debug-on-entry
487 (cadr (backtrace-frame (1+ count)))) 472 (cadr (backtrace-frame (1+ count))))
488 (setq count (+ 2 count)))) 473 (setq count (+ 2 count))))
489 (goto-char (point-min)) 474 (+ count index)))
490 (when (looking-at "Debugger entered--\\(Lisp error\\|returning value\\):")
491 (goto-char (match-end 0))
492 (forward-sexp 1))
493 (forward-line 1)
494 (while (progn
495 (forward-char 2)
496 (cond ((debugger--locals-visible-p)
497 (goto-char (next-single-char-property-change
498 (point) 'locals-visible)))
499 ((= (following-char) ?\()
500 (forward-sexp 1))
501 (t
502 (forward-sexp 2)))
503 (forward-line 1)
504 (<= (point) opoint))
505 (if (looking-at " *;;;")
506 (forward-line 1))
507 (setq count (1+ count)))
508 count)))
509 475
510(defun debugger-frame () 476(defun debugger-frame ()
511 "Request entry to debugger when this frame exits. 477 "Request entry to debugger when this frame exits.
512Applies to the frame whose line point is on in the backtrace." 478Applies to the frame whose line point is on in the backtrace."
513 (interactive) 479 (interactive)
514 (backtrace-debug (debugger-frame-number) t) 480 (backtrace-debug (debugger-frame-number) t)
515 (beginning-of-line) 481 (setf
516 (if (= (following-char) ? ) 482 (cl-getf (backtrace-frame-flags (nth (backtrace-get-index) backtrace-frames))
517 (let ((inhibit-read-only t)) 483 :debug-on-exit)
518 (delete-char 1) 484 t)
519 (insert ?*))) 485 (backtrace-update-flags))
520 (beginning-of-line))
521 486
522(defun debugger-frame-clear () 487(defun debugger-frame-clear ()
523 "Do not enter debugger when this frame exits. 488 "Do not enter debugger when this frame exits.
524Applies to the frame whose line point is on in the backtrace." 489Applies to the frame whose line point is on in the backtrace."
525 (interactive) 490 (interactive)
526 (backtrace-debug (debugger-frame-number) nil) 491 (backtrace-debug (debugger-frame-number) nil)
527 (beginning-of-line) 492 (setf
528 (if (= (following-char) ?*) 493 (cl-getf (backtrace-frame-flags (nth (backtrace-get-index) backtrace-frames))
529 (let ((inhibit-read-only t)) 494 :debug-on-exit)
530 (delete-char 1) 495 nil)
531 (insert ? ))) 496 (backtrace-update-flags))
532 (beginning-of-line))
533 497
534(defmacro debugger-env-macro (&rest body) 498(defmacro debugger-env-macro (&rest body)
535 "Run BODY in original environment." 499 "Run BODY in original environment."
@@ -564,69 +528,10 @@ The environment used is the one when entering the activation frame at point."
564 (let ((str (eval-expression-print-format val))) 528 (let ((str (eval-expression-print-format val)))
565 (if str (princ str t)))))))) 529 (if str (princ str t))))))))
566 530
567(defun debugger--locals-visible-p ()
568 "Are the local variables of the current stack frame visible?"
569 (save-excursion
570 (move-to-column 2)
571 (get-text-property (point) 'locals-visible)))
572
573(defun debugger--insert-locals (locals)
574 "Insert the local variables LOCALS at point."
575 (cond ((null locals)
576 (insert "\n [no locals]"))
577 (t
578 (let ((print-escape-newlines t))
579 (dolist (s+v locals)
580 (let ((symbol (car s+v))
581 (value (cdr s+v)))
582 (insert "\n ")
583 (prin1 symbol (current-buffer))
584 (insert " = ")
585 (debugger--print value (current-buffer))))))))
586
587(defun debugger--show-locals ()
588 "For the frame at point, insert locals and add text properties."
589 (let* ((nframe (1+ (debugger-frame-number 'skip-base)))
590 (base (debugger--backtrace-base))
591 (locals (backtrace--locals nframe base))
592 (inhibit-read-only t))
593 (save-excursion
594 (let ((start (progn
595 (move-to-column 2)
596 (point))))
597 (end-of-line)
598 (debugger--insert-locals locals)
599 (add-text-properties start (point) '(locals-visible t))))))
600
601(defun debugger--hide-locals ()
602 "Delete local variables and remove the text property."
603 (let* ((col (current-column))
604 (end (progn
605 (move-to-column 2)
606 (next-single-char-property-change (point) 'locals-visible)))
607 (start (previous-single-char-property-change end 'locals-visible))
608 (inhibit-read-only t))
609 (remove-text-properties start end '(locals-visible))
610 (goto-char start)
611 (end-of-line)
612 (delete-region (point) end)
613 (move-to-column col)))
614
615(defun debugger-toggle-locals ()
616 "Show or hide local variables of the current stack frame."
617 (interactive)
618 (cond ((debugger--locals-visible-p)
619 (debugger--hide-locals))
620 (t
621 (debugger--show-locals))))
622
623 531
624(defvar debugger-mode-map 532(defvar debugger-mode-map
625 (let ((map (make-keymap)) 533 (let ((map (make-keymap)))
626 (menu-map (make-sparse-keymap))) 534 (set-keymap-parent map backtrace-mode-map)
627 (set-keymap-parent map button-buffer-map)
628 (suppress-keymap map)
629 (define-key map "-" 'negative-argument)
630 (define-key map "b" 'debugger-frame) 535 (define-key map "b" 'debugger-frame)
631 (define-key map "c" 'debugger-continue) 536 (define-key map "c" 'debugger-continue)
632 (define-key map "j" 'debugger-jump) 537 (define-key map "j" 'debugger-jump)
@@ -634,63 +539,47 @@ The environment used is the one when entering the activation frame at point."
634 (define-key map "u" 'debugger-frame-clear) 539 (define-key map "u" 'debugger-frame-clear)
635 (define-key map "d" 'debugger-step-through) 540 (define-key map "d" 'debugger-step-through)
636 (define-key map "l" 'debugger-list-functions) 541 (define-key map "l" 'debugger-list-functions)
637 (define-key map "h" 'describe-mode) 542 (define-key map "q" 'debugger-quit)
638 (define-key map "q" 'top-level)
639 (define-key map "e" 'debugger-eval-expression) 543 (define-key map "e" 'debugger-eval-expression)
640 (define-key map "v" 'debugger-toggle-locals) ; "v" is for "variables".
641 (define-key map " " 'next-line)
642 (define-key map "R" 'debugger-record-expression) 544 (define-key map "R" 'debugger-record-expression)
643 (define-key map "\C-m" 'debug-help-follow)
644 (define-key map [mouse-2] 'push-button) 545 (define-key map [mouse-2] 'push-button)
645 (define-key map [menu-bar debugger] (cons "Debugger" menu-map)) 546 (easy-menu-define nil map ""
646 (define-key menu-map [deb-top] 547 '("Debugger"
647 '(menu-item "Quit" top-level 548 ["Step through" debugger-step-through
648 :help "Quit debugging and return to top level")) 549 :help "Proceed, stepping through subexpressions of this expression"]
649 (define-key menu-map [deb-s0] '("--")) 550 ["Continue" debugger-continue
650 (define-key menu-map [deb-descr] 551 :help "Continue, evaluating this expression without stopping"]
651 '(menu-item "Describe Debugger Mode" describe-mode 552 ["Jump" debugger-jump
652 :help "Display documentation for debugger-mode")) 553 :help "Continue to exit from this frame, with all debug-on-entry suspended"]
653 (define-key menu-map [deb-hfol] 554 ["Eval Expression..." debugger-eval-expression
654 '(menu-item "Help Follow" debug-help-follow 555 :help "Eval an expression, in an environment like that outside the debugger"]
655 :help "Follow cross-reference")) 556 ["Display and Record Expression" debugger-record-expression
656 (define-key menu-map [deb-nxt] 557 :help "Display a variable's value and record it in `*Backtrace-record*' buffer"]
657 '(menu-item "Next Line" next-line 558 ["Return value..." debugger-return-value
658 :help "Move cursor down")) 559 :help "Continue, specifying value to return."]
659 (define-key menu-map [deb-s1] '("--")) 560 "--"
660 (define-key menu-map [deb-lfunc] 561 ["Debug frame" debugger-frame
661 '(menu-item "List debug on entry functions" debugger-list-functions 562 :help "Request entry to debugger when this frame exits"]
662 :help "Display a list of all the functions now set to debug on entry")) 563 ["Cancel debug frame" debugger-frame-clear
663 (define-key menu-map [deb-fclear] 564 :help "Do not enter debugger when this frame exits"]
664 '(menu-item "Cancel debug frame" debugger-frame-clear 565 ["List debug on entry functions" debugger-list-functions
665 :help "Do not enter debugger when this frame exits")) 566 :help "Display a list of all the functions now set to debug on entry"]
666 (define-key menu-map [deb-frame] 567 "--"
667 '(menu-item "Debug frame" debugger-frame 568 ["Next Line" next-line
668 :help "Request entry to debugger when this frame exits")) 569 :help "Move cursor down"]
669 (define-key menu-map [deb-s2] '("--")) 570 ["Help for Symbol" backtrace-help-follow-symbol
670 (define-key menu-map [deb-ret] 571 :help "Show help for symbol at point"]
671 '(menu-item "Return value..." debugger-return-value 572 ["Describe Debugger Mode" describe-mode
672 :help "Continue, specifying value to return.")) 573 :help "Display documentation for debugger-mode"]
673 (define-key menu-map [deb-rec] 574 "--"
674 '(menu-item "Display and Record Expression" debugger-record-expression 575 ["Quit" debugger-quit
675 :help "Display a variable's value and record it in `*Backtrace-record*' buffer")) 576 :help "Quit debugging and return to top level"]))
676 (define-key menu-map [deb-eval]
677 '(menu-item "Eval Expression..." debugger-eval-expression
678 :help "Eval an expression, in an environment like that outside the debugger"))
679 (define-key menu-map [deb-jump]
680 '(menu-item "Jump" debugger-jump
681 :help "Continue to exit from this frame, with all debug-on-entry suspended"))
682 (define-key menu-map [deb-cont]
683 '(menu-item "Continue" debugger-continue
684 :help "Continue, evaluating this expression without stopping"))
685 (define-key menu-map [deb-step]
686 '(menu-item "Step through" debugger-step-through
687 :help "Proceed, stepping through subexpressions of this expression"))
688 map)) 577 map))
689 578
690(put 'debugger-mode 'mode-class 'special) 579(put 'debugger-mode 'mode-class 'special)
691 580
692(define-derived-mode debugger-mode fundamental-mode "Debugger" 581(define-derived-mode debugger-mode backtrace-mode "Debugger"
693 "Mode for backtrace buffers, selected in debugger. 582 "Mode for debugging Emacs Lisp using a backtrace.
694\\<debugger-mode-map> 583\\<debugger-mode-map>
695A line starts with `*' if exiting that frame will call the debugger. 584A line starts with `*' if exiting that frame will call the debugger.
696Type \\[debugger-frame] or \\[debugger-frame-clear] to set or remove the `*'. 585Type \\[debugger-frame] or \\[debugger-frame-clear] to set or remove the `*'.
@@ -704,8 +593,6 @@ which functions will enter the debugger when called.
704 593
705Complete list of commands: 594Complete list of commands:
706\\{debugger-mode-map}" 595\\{debugger-mode-map}"
707 (setq truncate-lines t)
708 (set-syntax-table emacs-lisp-mode-syntax-table)
709 (add-hook 'kill-buffer-hook 596 (add-hook 'kill-buffer-hook
710 (lambda () (if (> (recursion-depth) 0) (top-level))) 597 (lambda () (if (> (recursion-depth) 0) (top-level)))
711 nil t) 598 nil t)
@@ -732,27 +619,6 @@ Complete list of commands:
732 (buffer-substring (line-beginning-position 0) 619 (buffer-substring (line-beginning-position 0)
733 (line-end-position 0))))) 620 (line-end-position 0)))))
734 621
735(defun debug-help-follow (&optional pos)
736 "Follow cross-reference at POS, defaulting to point.
737
738For the cross-reference format, see `help-make-xrefs'."
739 (interactive "d")
740 ;; Ideally we'd just do (call-interactively 'help-follow) except that this
741 ;; assumes we're already in a *Help* buffer and reuses it, so it ends up
742 ;; incorrectly "reusing" the *Backtrace* buffer to show the help info.
743 (unless pos
744 (setq pos (point)))
745 (unless (push-button pos)
746 ;; check if the symbol under point is a function or variable
747 (let ((sym
748 (intern
749 (save-excursion
750 (goto-char pos) (skip-syntax-backward "w_")
751 (buffer-substring (point)
752 (progn (skip-syntax-forward "w_")
753 (point)))))))
754 (when (or (boundp sym) (fboundp sym) (facep sym))
755 (describe-symbol sym)))))
756 622
757;; When you change this, you may also need to change the number of 623;; When you change this, you may also need to change the number of
758;; frames that the debugger skips. 624;; frames that the debugger skips.
@@ -853,6 +719,13 @@ To specify a nil argument interactively, exit with an empty minibuffer."
853 ;;(princ "be set to debug on entry, even if it is in the list.") 719 ;;(princ "be set to debug on entry, even if it is in the list.")
854 ))))) 720 )))))
855 721
722(defun debugger-quit ()
723 "Quit debugging and return to the top level."
724 (interactive)
725 (if (= (recursion-depth) 0)
726 (quit-window)
727 (top-level)))
728
856(defun debug--implement-debug-watch (symbol newval op where) 729(defun debug--implement-debug-watch (symbol newval op where)
857 "Conditionally call the debugger. 730 "Conditionally call the debugger.
858This function is called when SYMBOL's value is modified." 731This function is called when SYMBOL's value is modified."
diff --git a/lisp/emacs-lisp/edebug.el b/lisp/emacs-lisp/edebug.el
index f0c0db182ed..fa418c68281 100644
--- a/lisp/emacs-lisp/edebug.el
+++ b/lisp/emacs-lisp/edebug.el
@@ -52,6 +52,7 @@
52 52
53;;; Code: 53;;; Code:
54 54
55(require 'backtrace)
55(require 'macroexp) 56(require 'macroexp)
56(require 'cl-lib) 57(require 'cl-lib)
57(eval-when-compile (require 'pcase)) 58(eval-when-compile (require 'pcase))
@@ -206,8 +207,7 @@ Use this with caution since it is not debugged."
206 "Non-nil if Edebug should unwrap results of expressions. 207 "Non-nil if Edebug should unwrap results of expressions.
207That is, Edebug will try to remove its own instrumentation from the result. 208That is, Edebug will try to remove its own instrumentation from the result.
208This is useful when debugging macros where the results of expressions 209This is useful when debugging macros where the results of expressions
209are instrumented expressions. But don't do this when results might be 210are instrumented expressions."
210circular or an infinite loop will result."
211 :type 'boolean 211 :type 'boolean
212 :group 'edebug) 212 :group 'edebug)
213 213
@@ -1265,25 +1265,59 @@ purpose by adding an entry to this alist, and setting
1265(defun edebug-unwrap (sexp) 1265(defun edebug-unwrap (sexp)
1266 "Return the unwrapped SEXP or return it as is if it is not wrapped. 1266 "Return the unwrapped SEXP or return it as is if it is not wrapped.
1267The SEXP might be the result of wrapping a body, which is a list of 1267The SEXP might be the result of wrapping a body, which is a list of
1268expressions; a `progn' form will be returned enclosing these forms." 1268expressions; a `progn' form will be returned enclosing these forms.
1269 (if (consp sexp) 1269Does not unwrap inside vectors, records, structures, or hash tables."
1270 (cond 1270 (pcase sexp
1271 ((eq 'edebug-after (car sexp)) 1271 (`(edebug-after ,_before-form ,_after-index ,form)
1272 (nth 3 sexp)) 1272 form)
1273 ((eq 'edebug-enter (car sexp)) 1273 (`(lambda ,args (edebug-enter ',_sym ,_arglist
1274 (macroexp-progn (nthcdr 2 (nth 1 (nth 3 sexp))))) 1274 (function (lambda nil . ,body))))
1275 (t sexp);; otherwise it is not wrapped, so just return it. 1275 `(lambda ,args ,@body))
1276 ) 1276 (`(closure ,env ,args (edebug-enter ',_sym ,_arglist
1277 sexp)) 1277 (function (lambda nil . ,body))))
1278 `(closure ,env ,args ,@body))
1279 (`(edebug-enter ',_sym ,_args (function (lambda nil . ,body)))
1280 (macroexp-progn body))
1281 (_ sexp)))
1278 1282
1279(defun edebug-unwrap* (sexp) 1283(defun edebug-unwrap* (sexp)
1280 "Return the SEXP recursively unwrapped." 1284 "Return the SEXP recursively unwrapped."
1285 (let ((ht (make-hash-table :test 'eq)))
1286 (edebug--unwrap1 sexp ht)))
1287
1288(defun edebug--unwrap1 (sexp hash-table)
1289 "Unwrap SEXP using HASH-TABLE of things already unwrapped.
1290HASH-TABLE contains the results of unwrapping cons cells within
1291SEXP, which are reused to avoid infinite loops when SEXP is or
1292contains a circular object."
1281 (let ((new-sexp (edebug-unwrap sexp))) 1293 (let ((new-sexp (edebug-unwrap sexp)))
1282 (while (not (eq sexp new-sexp)) 1294 (while (not (eq sexp new-sexp))
1283 (setq sexp new-sexp 1295 (setq sexp new-sexp
1284 new-sexp (edebug-unwrap sexp))) 1296 new-sexp (edebug-unwrap sexp)))
1285 (if (consp new-sexp) 1297 (if (consp new-sexp)
1286 (mapcar #'edebug-unwrap* new-sexp) 1298 (let ((result (gethash new-sexp hash-table nil)))
1299 (unless result
1300 (let ((remainder new-sexp)
1301 current)
1302 (setq result (cons nil nil)
1303 current result)
1304 (while
1305 (progn
1306 (puthash remainder current hash-table)
1307 (setf (car current)
1308 (edebug--unwrap1 (car remainder) hash-table))
1309 (setq remainder (cdr remainder))
1310 (cond
1311 ((atom remainder)
1312 (setf (cdr current)
1313 (edebug--unwrap1 remainder hash-table))
1314 nil)
1315 ((gethash remainder hash-table nil)
1316 (setf (cdr current) (gethash remainder hash-table nil))
1317 nil)
1318 (t (setq current
1319 (setf (cdr current) (cons nil nil)))))))))
1320 result)
1287 new-sexp))) 1321 new-sexp)))
1288 1322
1289 1323
@@ -3658,7 +3692,7 @@ be installed in `emacs-lisp-mode-map'.")
3658 3692
3659 ;; misc 3693 ;; misc
3660 (define-key map "?" 'edebug-help) 3694 (define-key map "?" 'edebug-help)
3661 (define-key map "d" 'edebug-backtrace) 3695 (define-key map "d" 'edebug-pop-to-backtrace)
3662 3696
3663 (define-key map "-" 'negative-argument) 3697 (define-key map "-" 'negative-argument)
3664 3698
@@ -3916,8 +3950,10 @@ Global commands prefixed by `global-edebug-prefix':
3916;; (setq debugger 'debug) ; use the standard debugger 3950;; (setq debugger 'debug) ; use the standard debugger
3917 3951
3918;; Note that debug and its utilities must be byte-compiled to work, 3952;; Note that debug and its utilities must be byte-compiled to work,
3919;; since they depend on the backtrace looking a certain way. But 3953;; since they depend on the backtrace looking a certain way. Edebug
3920;; edebug is not dependent on this, yet. 3954;; will work if not byte-compiled, but it will not be able correctly
3955;; remove its instrumentation from backtraces unless it is
3956;; byte-compiled.
3921 3957
3922(defun edebug (&optional arg-mode &rest args) 3958(defun edebug (&optional arg-mode &rest args)
3923 "Replacement for `debug'. 3959 "Replacement for `debug'.
@@ -3947,49 +3983,136 @@ Otherwise call `debug' normally."
3947 (apply #'debug arg-mode args) 3983 (apply #'debug arg-mode args)
3948 )) 3984 ))
3949 3985
3950 3986;;; Backtrace buffer
3951(defun edebug-backtrace () 3987
3952 "Display a non-working backtrace. Better than nothing..." 3988(defvar-local edebug-backtrace-frames nil
3989 "Stack frames of the current Edebug Backtrace buffer without instrumentation.
3990This should be a list of `edebug---frame' objects.")
3991(defvar-local edebug-instrumented-backtrace-frames nil
3992 "Stack frames of the current Edebug Backtrace buffer with instrumentation.
3993This should be a list of `edebug---frame' objects.")
3994
3995;; Data structure for backtrace frames with information
3996;; from Edebug instrumentation found in the backtrace.
3997(cl-defstruct
3998 (edebug--frame
3999 (:constructor edebug--make-frame)
4000 (:include backtrace-frame))
4001 def-name before-index after-index)
4002
4003(defun edebug-pop-to-backtrace ()
4004 "Display the current backtrace in a `backtrace-mode' window."
3953 (interactive) 4005 (interactive)
3954 (if (or (not edebug-backtrace-buffer) 4006 (if (or (not edebug-backtrace-buffer)
3955 (null (buffer-name edebug-backtrace-buffer))) 4007 (null (buffer-name edebug-backtrace-buffer)))
3956 (setq edebug-backtrace-buffer 4008 (setq edebug-backtrace-buffer
3957 (generate-new-buffer "*Backtrace*")) 4009 (generate-new-buffer "*Edebug Backtrace*"))
3958 ;; Else, could just display edebug-backtrace-buffer. 4010 ;; Else, could just display edebug-backtrace-buffer.
3959 ) 4011 )
3960 (with-output-to-temp-buffer (buffer-name edebug-backtrace-buffer) 4012 (pop-to-buffer edebug-backtrace-buffer)
3961 (setq edebug-backtrace-buffer standard-output) 4013 (unless (derived-mode-p 'backtrace-mode)
3962 (let ((print-escape-newlines t) 4014 (backtrace-mode)
3963 (print-length 50) ; FIXME cf edebug-safe-prin1-to-string 4015 (add-hook 'backtrace-goto-source-functions 'edebug--backtrace-goto-source))
3964 last-ok-point) 4016 (setq edebug-instrumented-backtrace-frames
3965 (backtrace) 4017 (backtrace-get-frames 'edebug-debugger
3966 4018 :constructor #'edebug--make-frame)
3967 ;; Clean up the backtrace. 4019 edebug-backtrace-frames (edebug--strip-instrumentation
3968 ;; Not quite right for current edebug scheme. 4020 edebug-instrumented-backtrace-frames)
3969 (set-buffer edebug-backtrace-buffer) 4021 backtrace-frames edebug-backtrace-frames)
3970 (setq truncate-lines t) 4022 (backtrace-print)
3971 (goto-char (point-min)) 4023 (goto-char (point-min)))
3972 (setq last-ok-point (point)) 4024
3973 (if t (progn 4025(defun edebug--strip-instrumentation (frames)
3974 4026 "Return a new list of backtrace frames with instrumentation removed.
3975 ;; Delete interspersed edebug internals. 4027Remove frames for Edebug's functions and the lambdas in
3976 (while (re-search-forward "^ (?edebug" nil t) 4028`edebug-enter' wrappers. Fill in the def-name, before-index
3977 (beginning-of-line) 4029and after-index fields in both FRAMES and the returned list
3978 (cond 4030of deinstrumented frames, for those frames where the source
3979 ((looking-at "^ (edebug-after") 4031code location is known."
3980 ;; Previous lines may contain code, so just delete this line. 4032 (let (skip-next-lambda def-name before-index after-index results
3981 (setq last-ok-point (point)) 4033 (index (length frames)))
3982 (forward-line 1) 4034 (dolist (frame (reverse frames))
3983 (delete-region last-ok-point (point))) 4035 (let ((new-frame (copy-edebug--frame frame))
3984 4036 (fun (edebug--frame-fun frame))
3985 ((looking-at (if debugger-stack-frame-as-list 4037 (args (edebug--frame-args frame)))
3986 "^ (edebug" 4038 (cl-decf index)
3987 "^ edebug")) 4039 (pcase fun
3988 (forward-line 1) 4040 ('edebug-enter
3989 (delete-region last-ok-point (point)) 4041 (setq skip-next-lambda t
3990 ))) 4042 def-name (nth 0 args)))
3991 ))))) 4043 ('edebug-after
4044 (setq before-index (if (consp (nth 0 args))
4045 (nth 1 (nth 0 args))
4046 (nth 0 args))
4047 after-index (nth 1 args)))
4048 ((pred edebug--symbol-not-prefixed-p)
4049 (edebug--unwrap-frame new-frame)
4050 (edebug--add-source-info new-frame def-name before-index after-index)
4051 (edebug--add-source-info frame def-name before-index after-index)
4052 (push new-frame results)
4053 (setq before-index nil
4054 after-index nil))
4055 (`(,(or 'lambda 'closure) . ,_)
4056 (unless skip-next-lambda
4057 (edebug--unwrap-frame new-frame)
4058 (edebug--add-source-info frame def-name before-index after-index)
4059 (edebug--add-source-info new-frame def-name before-index after-index)
4060 (push new-frame results))
4061 (setq before-index nil
4062 after-index nil
4063 skip-next-lambda nil)))))
4064 results))
4065
4066(defun edebug--symbol-not-prefixed-p (sym)
4067 "Return non-nil if SYM is a symbol not prefixed by \"edebug-\"."
4068 (and (symbolp sym)
4069 (not (string-prefix-p "edebug-" (symbol-name sym)))))
4070
4071(defun edebug--unwrap-frame (frame)
4072 "Remove Edebug's instrumentation from FRAME.
4073Strip it from the function and any unevaluated arguments."
4074 (setf (edebug--frame-fun frame) (edebug-unwrap* (edebug--frame-fun frame)))
4075 (unless (edebug--frame-evald frame)
4076 (let (results)
4077 (dolist (arg (edebug--frame-args frame))
4078 (push (edebug-unwrap* arg) results))
4079 (setf (edebug--frame-args frame) (nreverse results)))))
4080
4081(defun edebug--add-source-info (frame def-name before-index after-index)
4082 "Update FRAME with the additional info needed by an edebug--frame.
4083Save DEF-NAME, BEFORE-INDEX and AFTER-INDEX in FRAME."
4084 (when (and before-index def-name)
4085 (setf (edebug--frame-flags frame)
4086 (plist-put (copy-sequence (edebug--frame-flags frame))
4087 :source-available t)))
4088 (setf (edebug--frame-def-name frame) (and before-index def-name))
4089 (setf (edebug--frame-before-index frame) before-index)
4090 (setf (edebug--frame-after-index frame) after-index))
4091
4092(defun edebug--backtrace-goto-source ()
4093 (let* ((index (backtrace-get-index))
4094 (frame (nth index backtrace-frames)))
4095 (when (edebug--frame-def-name frame)
4096 (let* ((data (get (edebug--frame-def-name frame) 'edebug))
4097 (marker (nth 0 data))
4098 (offsets (nth 2 data)))
4099 (pop-to-buffer (marker-buffer marker))
4100 (goto-char (+ (marker-position marker)
4101 (aref offsets (edebug--frame-before-index frame))))))))
4102
4103(defun edebug-backtrace-show-instrumentation ()
4104 "Show Edebug's instrumentation in an Edebug Backtrace buffer."
4105 (interactive)
4106 (unless (eq backtrace-frames edebug-instrumented-backtrace-frames)
4107 (setq backtrace-frames edebug-instrumented-backtrace-frames)
4108 (revert-buffer)))
3992 4109
4110(defun edebug-backtrace-hide-instrumentation ()
4111 "Hide Edebug's instrumentation in an Edebug Backtrace buffer."
4112 (interactive)
4113 (unless (eq backtrace-frames edebug-backtrace-frames)
4114 (setq backtrace-frames edebug-backtrace-frames)
4115 (revert-buffer)))
3993 4116
3994;;; Trace display 4117;;; Trace display
3995 4118
@@ -4163,7 +4286,7 @@ It is removed when you hit any char."
4163 ["Bounce to Current Point" edebug-bounce-point t] 4286 ["Bounce to Current Point" edebug-bounce-point t]
4164 ["View Outside Windows" edebug-view-outside t] 4287 ["View Outside Windows" edebug-view-outside t]
4165 ["Previous Result" edebug-previous-result t] 4288 ["Previous Result" edebug-previous-result t]
4166 ["Show Backtrace" edebug-backtrace t] 4289 ["Show Backtrace" edebug-pop-to-backtrace t]
4167 ["Display Freq Count" edebug-display-freq-count t]) 4290 ["Display Freq Count" edebug-display-freq-count t])
4168 4291
4169 ("Eval" 4292 ("Eval"
diff --git a/lisp/emacs-lisp/ert.el b/lisp/emacs-lisp/ert.el
index cad21044f15..eb9695d0c12 100644
--- a/lisp/emacs-lisp/ert.el
+++ b/lisp/emacs-lisp/ert.el
@@ -60,6 +60,7 @@
60(require 'cl-lib) 60(require 'cl-lib)
61(require 'button) 61(require 'button)
62(require 'debug) 62(require 'debug)
63(require 'backtrace)
63(require 'easymenu) 64(require 'easymenu)
64(require 'ewoc) 65(require 'ewoc)
65(require 'find-func) 66(require 'find-func)
@@ -677,13 +678,6 @@ and is displayed in front of the value of MESSAGE-FORM."
677(cl-defstruct (ert-test-aborted-with-non-local-exit 678(cl-defstruct (ert-test-aborted-with-non-local-exit
678 (:include ert-test-result))) 679 (:include ert-test-result)))
679 680
680(defun ert--print-backtrace (backtrace do-xrefs)
681 "Format the backtrace BACKTRACE to the current buffer."
682 (let ((print-escape-newlines t)
683 (print-level 8)
684 (print-length 50))
685 (debugger-insert-backtrace backtrace do-xrefs)))
686
687;; A container for the state of the execution of a single test and 681;; A container for the state of the execution of a single test and
688;; environment data needed during its execution. 682;; environment data needed during its execution.
689(cl-defstruct ert--test-execution-info 683(cl-defstruct ert--test-execution-info
@@ -732,7 +726,7 @@ run. ARGS are the arguments to `debugger'."
732 ;; use. 726 ;; use.
733 ;; 727 ;;
734 ;; Grab the frames above the debugger. 728 ;; Grab the frames above the debugger.
735 (backtrace (cdr (backtrace-frames debugger))) 729 (backtrace (cdr (backtrace-get-frames debugger)))
736 (infos (reverse ert--infos))) 730 (infos (reverse ert--infos)))
737 (setf (ert--test-execution-info-result info) 731 (setf (ert--test-execution-info-result info)
738 (cl-ecase type 732 (cl-ecase type
@@ -1406,9 +1400,8 @@ Returns the stats object."
1406 (ert-test-result-with-condition 1400 (ert-test-result-with-condition
1407 (message "Test %S backtrace:" (ert-test-name test)) 1401 (message "Test %S backtrace:" (ert-test-name test))
1408 (with-temp-buffer 1402 (with-temp-buffer
1409 (ert--print-backtrace 1403 (insert (backtrace-to-string
1410 (ert-test-result-with-condition-backtrace result) 1404 (ert-test-result-with-condition-backtrace result)))
1411 nil)
1412 (if (not ert-batch-backtrace-right-margin) 1405 (if (not ert-batch-backtrace-right-margin)
1413 (message "%s" 1406 (message "%s"
1414 (buffer-substring-no-properties (point-min) 1407 (buffer-substring-no-properties (point-min)
@@ -2450,20 +2443,20 @@ To be used in the ERT results buffer."
2450 (cl-etypecase result 2443 (cl-etypecase result
2451 (ert-test-passed (error "Test passed, no backtrace available")) 2444 (ert-test-passed (error "Test passed, no backtrace available"))
2452 (ert-test-result-with-condition 2445 (ert-test-result-with-condition
2453 (let ((backtrace (ert-test-result-with-condition-backtrace result)) 2446 (let ((buffer (get-buffer-create "*ERT Backtrace*")))
2454 (buffer (get-buffer-create "*ERT Backtrace*")))
2455 (pop-to-buffer buffer) 2447 (pop-to-buffer buffer)
2456 (let ((inhibit-read-only t)) 2448 (unless (derived-mode-p 'backtrace-mode)
2457 (buffer-disable-undo) 2449 (backtrace-mode))
2458 (erase-buffer) 2450 (setq backtrace-insert-header-function
2459 (ert-simple-view-mode) 2451 (lambda () (ert--insert-backtrace-header (ert-test-name test)))
2460 (set-buffer-multibyte t) ; mimic debugger-setup-buffer 2452 backtrace-frames (ert-test-result-with-condition-backtrace result))
2461 (setq truncate-lines t) 2453 (backtrace-print)
2462 (ert--print-backtrace backtrace t) 2454 (goto-char (point-min)))))))
2463 (goto-char (point-min)) 2455
2464 (insert (substitute-command-keys "Backtrace for test `")) 2456(defun ert--insert-backtrace-header (name)
2465 (ert-insert-test-name-button (ert-test-name test)) 2457 (insert (substitute-command-keys "Backtrace for test `"))
2466 (insert (substitute-command-keys "':\n")))))))) 2458 (ert-insert-test-name-button name)
2459 (insert (substitute-command-keys "':\n")))
2467 2460
2468(defun ert-results-pop-to-messages-for-test-at-point () 2461(defun ert-results-pop-to-messages-for-test-at-point ()
2469 "Display the part of the *Messages* buffer generated during the test at point. 2462 "Display the part of the *Messages* buffer generated during the test at point.
diff --git a/lisp/emacs-lisp/lisp-mode.el b/lisp/emacs-lisp/lisp-mode.el
index 6313c63ecfe..afb7cbd1dd7 100644
--- a/lisp/emacs-lisp/lisp-mode.el
+++ b/lisp/emacs-lisp/lisp-mode.el
@@ -517,6 +517,16 @@ This will generate compile-time constants from BINDINGS."
517(defvar lisp-cl-font-lock-keywords lisp-cl-font-lock-keywords-1 517(defvar lisp-cl-font-lock-keywords lisp-cl-font-lock-keywords-1
518 "Default expressions to highlight in Lisp modes.") 518 "Default expressions to highlight in Lisp modes.")
519 519
520;; Support backtrace mode.
521(defconst lisp-el-font-lock-keywords-for-backtraces lisp-el-font-lock-keywords
522 "Default highlighting from Emacs Lisp mod used in Backtrace mode.")
523(defconst lisp-el-font-lock-keywords-for-backtraces-1 lisp-el-font-lock-keywords-1
524 "Subdued highlighting from Emacs Lisp mode used in Backtrace mode.")
525(defconst lisp-el-font-lock-keywords-for-backtraces-2
526 (remove (assoc 'lisp--match-hidden-arg lisp-el-font-lock-keywords-2)
527 lisp-el-font-lock-keywords-2)
528 "Gaudy highlighting from Emacs Lisp mode used in Backtrace mode.")
529
520(defun lisp-string-in-doc-position-p (listbeg startpos) 530(defun lisp-string-in-doc-position-p (listbeg startpos)
521 "Return true if a doc string may occur at STARTPOS inside a list. 531 "Return true if a doc string may occur at STARTPOS inside a list.
522LISTBEG is the position of the start of the innermost list 532LISTBEG is the position of the start of the innermost list
diff --git a/lisp/subr.el b/lisp/subr.el
index f8c19efc379..fbb3e49a35c 100644
--- a/lisp/subr.el
+++ b/lisp/subr.el
@@ -4687,25 +4687,6 @@ The properties used on SYMBOL are `composefunc', `sendfunc',
4687 (put symbol 'hookvar (or hookvar 'mail-send-hook))) 4687 (put symbol 'hookvar (or hookvar 'mail-send-hook)))
4688 4688
4689 4689
4690(defun backtrace--print-frame (evald func args flags)
4691 "Print a trace of a single stack frame to `standard-output'.
4692EVALD, FUNC, ARGS, FLAGS are as in `mapbacktrace'."
4693 (princ (if (plist-get flags :debug-on-exit) "* " " "))
4694 (cond
4695 ((and evald (not debugger-stack-frame-as-list))
4696 (cl-prin1 func)
4697 (if args (cl-prin1 args) (princ "()")))
4698 (t
4699 (cl-prin1 (cons func args))))
4700 (princ "\n"))
4701
4702(defun backtrace ()
4703 "Print a trace of Lisp function calls currently active.
4704Output stream used is value of `standard-output'."
4705 (let ((print-level (or print-level 8))
4706 (print-escape-control-characters t))
4707 (mapbacktrace #'backtrace--print-frame 'backtrace)))
4708
4709(defun backtrace-frames (&optional base) 4690(defun backtrace-frames (&optional base)
4710 "Collect all frames of current backtrace into a list. 4691 "Collect all frames of current backtrace into a list.
4711If non-nil, BASE should be a function, and frames before its 4692If non-nil, BASE should be a function, and frames before its