aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGemini Lasswell2018-08-03 10:28:28 -0700
committerGemini Lasswell2018-08-03 10:28:28 -0700
commitda0054c30729e58259c1e7251cb03c8ef13ff943 (patch)
treef3fd4b5256aa6c6786d0ac4f80fb1d87dcc2e401
parente65ec81fc3e556719fae8d8b4b42f571c7e9f4fc (diff)
parent95b2ab3dccdc756614b4c8f45a7b206d61753705 (diff)
downloademacs-da0054c30729e58259c1e7251cb03c8ef13ff943.tar.gz
emacs-da0054c30729e58259c1e7251cb03c8ef13ff943.zip
Merge branch 'scratch/backtrace-mode'
-rw-r--r--doc/lispref/debugging.texi115
-rw-r--r--doc/lispref/edebug.texi12
-rw-r--r--doc/misc/ert.texi8
-rw-r--r--etc/NEWS40
-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
-rw-r--r--test/lisp/emacs-lisp/backtrace-tests.el436
-rw-r--r--test/lisp/emacs-lisp/cl-print-tests.el178
-rw-r--r--test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el2
-rw-r--r--test/lisp/emacs-lisp/edebug-tests.el18
-rw-r--r--test/lisp/emacs-lisp/ert-tests.el2
16 files changed, 2326 insertions, 460 deletions
diff --git a/doc/lispref/debugging.texi b/doc/lispref/debugging.texi
index 1b1f87465db..9b3ba6cf7ee 100644
--- a/doc/lispref/debugging.texi
+++ b/doc/lispref/debugging.texi
@@ -81,7 +81,8 @@ debugger recursively. @xref{Recursive Editing}.
81* Function Debugging:: Entering it when a certain function is called. 81* Function Debugging:: Entering it when a certain function is called.
82* Variable Debugging:: Entering it when a variable is modified. 82* Variable Debugging:: Entering it when a variable is modified.
83* Explicit Debug:: Entering it at a certain point in the program. 83* Explicit Debug:: Entering it at a certain point in the program.
84* Using Debugger:: What the debugger does; what you see while in it. 84* Using Debugger:: What the debugger does.
85* Backtraces:: What you see while in the debugger.
85* Debugger Commands:: Commands used while in the debugger. 86* Debugger Commands:: Commands used while in the debugger.
86* Invoking the Debugger:: How to call the function @code{debug}. 87* Invoking the Debugger:: How to call the function @code{debug}.
87* Internals of Debugger:: Subroutines of the debugger, and global variables. 88* Internals of Debugger:: Subroutines of the debugger, and global variables.
@@ -392,32 +393,82 @@ this is not what you want, you can either set
392@code{eval-expression-debug-on-error} to @code{nil}, or set 393@code{eval-expression-debug-on-error} to @code{nil}, or set
393@code{debug-on-error} to @code{nil} in @code{debugger-mode-hook}. 394@code{debug-on-error} to @code{nil} in @code{debugger-mode-hook}.
394 395
396 The debugger itself must be run byte-compiled, since it makes
397assumptions about the state of the Lisp interpreter. These
398assumptions are false if the debugger is running interpreted.
399
400@node Backtraces
401@subsection Backtraces
402@cindex backtrace buffer
403
404Debugger mode is derived from Backtrace mode, which is also used to
405show backtraces by Edebug and ERT. (@pxref{Edebug}, and @ref{Top,the
406ERT manual,, ert, ERT: Emacs Lisp Regression Testing}.)
407
408@cindex stack frame
409The backtrace buffer shows you the functions that are executing and
410their argument values. When a backtrace buffer is created, it shows
411each stack frame on one, possibly very long, line. (A stack frame is
412the place where the Lisp interpreter records information about a
413particular invocation of a function.) The most recently called
414function will be at the top.
415
395@cindex current stack frame 416@cindex current stack frame
396 The backtrace buffer shows you the functions that are executing and 417In a backtrace you can specify a stack frame by moving point to a line
397their argument values. It also allows you to specify a stack frame by 418describing that frame. The frame whose line point is on is considered
398moving point to the line describing that frame. (A stack frame is the 419the @dfn{current frame}.
399place where the Lisp interpreter records information about a particular 420
400invocation of a function.) The frame whose line point is on is 421If a function name is underlined, that means Emacs knows where its
401considered the @dfn{current frame}. Some of the debugger commands 422source code is located. You can click with the mouse on that name, or
402operate on the current frame. If a line starts with a star, that means 423move to it and type @key{RET}, to visit the source code. You can also
403that exiting that frame will call the debugger again. This is useful 424type @key{RET} while point is on any name of a function or variable
404for examining the return value of a function. 425which is not underlined, to see help information for that symbol in a
405 426help buffer, if any exists. The @code{xref-find-definitions} command,
406 If a function name is underlined, that means the debugger knows 427bound to @key{M-.}, can also be used on any identifier in a backtrace
407where its source code is located. You can click with the mouse on 428(@pxref{Looking Up Identifiers,,,emacs, The GNU Emacs Manual}).
408that name, or move to it and type @key{RET}, to visit the source code. 429
430In backtraces, the tails of long lists and the ends of long strings,
431vectors or structures, as well as objects which are deeply nested,
432will be printed as underlined ``...''. You can click with the mouse
433on a ``...'', or type @key{RET} while point is on it, to show the part
434of the object that was hidden. To control how much abbreviation is
435done, customize @code{backtrace-line-length}.
436
437Here is a list of commands for navigating and viewing backtraces:
409 438
410 The debugger itself must be run byte-compiled, since it makes 439@table @kbd
411assumptions about how many stack frames are used for the debugger 440@item v
412itself. These assumptions are false if the debugger is running 441Toggle the display of local variables of the current stack frame.
413interpreted. 442
443@item p
444Move to the beginning of the frame, or to the beginning
445of the previous frame.
446
447@item n
448Move to the beginning of the next frame.
449
450@item +
451Add line breaks and indentation to the top-level Lisp form at point to
452make it more readable.
453
454@item -
455Collapse the top-level Lisp form at point back to a single line.
456
457@item #
458Toggle @code{print-circle} for the frame at point.
459
460@item .
461Expand all the forms abbreviated with ``...'' in the frame at point.
462
463@end table
414 464
415@node Debugger Commands 465@node Debugger Commands
416@subsection Debugger Commands 466@subsection Debugger Commands
417@cindex debugger command list 467@cindex debugger command list
418 468
419 The debugger buffer (in Debugger mode) provides special commands in 469 The debugger buffer (in Debugger mode) provides special commands in
420addition to the usual Emacs commands. The most important use of 470addition to the usual Emacs commands and to the Backtrace mode commands
471described in the previous section. The most important use of
421debugger commands is for stepping through code, so that you can see 472debugger commands is for stepping through code, so that you can see
422how control flows. The debugger can step through the control 473how control flows. The debugger can step through the control
423structures of an interpreted function, but cannot do so in a 474structures of an interpreted function, but cannot do so in a
@@ -427,6 +478,11 @@ the same function. (To do this, visit the source for the function and
427type @kbd{C-M-x} on its definition.) You cannot use the Lisp debugger 478type @kbd{C-M-x} on its definition.) You cannot use the Lisp debugger
428to step through a primitive function. 479to step through a primitive function.
429 480
481Some of the debugger commands operate on the current frame. If a
482frame starts with a star, that means that exiting that frame will call the
483debugger again. This is useful for examining the return value of a
484function.
485
430@c FIXME: Add @findex for the following commands? --xfq 486@c FIXME: Add @findex for the following commands? --xfq
431 Here is a list of Debugger mode commands: 487 Here is a list of Debugger mode commands:
432 488
@@ -502,8 +558,6 @@ Display a list of functions that will invoke the debugger when called.
502This is a list of functions that are set to break on entry by means of 558This is a list of functions that are set to break on entry by means of
503@code{debug-on-entry}. 559@code{debug-on-entry}.
504 560
505@item v
506Toggle the display of local variables of the current stack frame.
507@end table 561@end table
508 562
509@node Invoking the Debugger 563@node Invoking the Debugger
@@ -624,20 +678,19 @@ of @code{debug} (@pxref{Invoking the Debugger}).
624@cindex run time stack 678@cindex run time stack
625@cindex call stack 679@cindex call stack
626This function prints a trace of Lisp function calls currently active. 680This function prints a trace of Lisp function calls currently active.
627This is the function used by @code{debug} to fill up the 681The trace is identical to the one that @code{debug} would show in the
628@file{*Backtrace*} buffer. It is written in C, since it must have access 682@file{*Backtrace*} buffer. The return value is always nil.
629to the stack to determine which function calls are active. The return
630value is always @code{nil}.
631 683
632In the following example, a Lisp expression calls @code{backtrace} 684In the following example, a Lisp expression calls @code{backtrace}
633explicitly. This prints the backtrace to the stream 685explicitly. This prints the backtrace to the stream
634@code{standard-output}, which, in this case, is the buffer 686@code{standard-output}, which, in this case, is the buffer
635@samp{backtrace-output}. 687@samp{backtrace-output}.
636 688
637Each line of the backtrace represents one function call. The line shows 689Each line of the backtrace represents one function call. The line
638the values of the function's arguments if they are all known; if they 690shows the function followed by a list of the values of the function's
639are still being computed, the line says so. The arguments of special 691arguments if they are all known; if they are still being computed, the
640forms are elided. 692line consists of a list containing the function and its unevaluated
693arguments. Long lists or deeply nested structures may be elided.
641 694
642@smallexample 695@smallexample
643@group 696@group
@@ -654,7 +707,7 @@ forms are elided.
654@group 707@group
655----------- Buffer: backtrace-output ------------ 708----------- Buffer: backtrace-output ------------
656 backtrace() 709 backtrace()
657 (list ...computing arguments...) 710 (list 'testing (backtrace))
658@end group 711@end group
659 (progn ...) 712 (progn ...)
660 eval((progn (1+ var) (list 'testing (backtrace)))) 713 eval((progn (1+ var) (list 'testing (backtrace))))
@@ -685,7 +738,7 @@ example would look as follows:
685@group 738@group
686----------- Buffer: backtrace-output ------------ 739----------- Buffer: backtrace-output ------------
687 (backtrace) 740 (backtrace)
688 (list ...computing arguments...) 741 (list 'testing (backtrace))
689@end group 742@end group
690 (progn ...) 743 (progn ...)
691 (eval (progn (1+ var) (list 'testing (backtrace)))) 744 (eval (progn (1+ var) (list 'testing (backtrace))))
diff --git a/doc/lispref/edebug.texi b/doc/lispref/edebug.texi
index b9cc1d5afc2..54200b99903 100644
--- a/doc/lispref/edebug.texi
+++ b/doc/lispref/edebug.texi
@@ -442,8 +442,16 @@ Redisplay the most recently known expression result in the echo area
442Display a backtrace, excluding Edebug's own functions for clarity 442Display a backtrace, excluding Edebug's own functions for clarity
443(@code{edebug-backtrace}). 443(@code{edebug-backtrace}).
444 444
445You cannot use debugger commands in the backtrace buffer in Edebug as 445@xref{Backtraces}, for a description of backtraces
446you would in the standard debugger. 446and the commands which work on them.
447
448If you would like to see Edebug's functions in the backtrace,
449use @kbd{M-x edebug-backtrace-show-instrumentation}. To hide them
450again use @kbd{M-x edebug-backtrace-hide-instrumentation}.
451
452If a backtrace frame starts with @samp{>} that means that Edebug knows
453where the source code for the frame is located. Use @kbd{s} to jump
454to the source code for the current frame.
447 455
448The backtrace buffer is killed automatically when you continue 456The backtrace buffer is killed automatically when you continue
449execution. 457execution.
diff --git a/doc/misc/ert.texi b/doc/misc/ert.texi
index 82e0e27ed1c..6a34f5c5722 100644
--- a/doc/misc/ert.texi
+++ b/doc/misc/ert.texi
@@ -273,9 +273,11 @@ moving point to it and typing @kbd{@key{RET}} jumps to its definition.
273@cindex backtrace of a failed test 273@cindex backtrace of a failed test
274Pressing @kbd{r} re-runs the test near point on its own. Pressing 274Pressing @kbd{r} re-runs the test near point on its own. Pressing
275@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the 275@kbd{d} re-runs it with the debugger enabled. @kbd{.} jumps to the
276definition of the test near point (@kbd{@key{RET}} has the same effect if 276definition of the test near point (@kbd{@key{RET}} has the same effect
277point is on the name of the test). On a failed test, @kbd{b} shows 277if point is on the name of the test). On a failed test, @kbd{b} shows
278the backtrace of the failure. 278the backtrace of the failure. @xref{Debugging,, Backtraces, elisp,
279GNU Emacs Lisp Reference Manual}, for more information about
280backtraces.
279 281
280@kindex l@r{, in ert results buffer} 282@kindex l@r{, in ert results buffer}
281@kbd{l} shows the list of @code{should} forms executed in the test. 283@kbd{l} shows the list of @code{should} forms executed in the test.
diff --git a/etc/NEWS b/etc/NEWS
index 6ccf6fc0890..9c78bd5a05d 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -466,6 +466,14 @@ the shift key.
466*** Isearch now remembers the regexp-based search mode for words/symbols 466*** Isearch now remembers the regexp-based search mode for words/symbols
467and case-sensitivity together with search strings in the search ring. 467and case-sensitivity together with search strings in the search ring.
468 468
469** Debugger
470
471+++
472*** The Lisp Debugger is now based on 'backtrace-mode'.
473Backtrace mode adds fontification and commands for changing the
474appearance of backtrace frames. See the node "Backtraces" in the Elisp
475manual for documentation of the new mode and its commands.
476
469** Edebug 477** Edebug
470 478
471+++ 479+++
@@ -475,14 +483,27 @@ using the new variables 'edebug-behavior-alist',
475'edebug-new-definition-function'. Edebug's behavior can be changed 483'edebug-new-definition-function'. Edebug's behavior can be changed
476globally or for individual definitions. 484globally or for individual definitions.
477 485
486+++
487*** Edebug's backtrace buffer now uses 'backtrace-mode'.
488Backtrace mode adds fontification, links and commands for changing the
489appearance of backtrace frames. See the node "Backtraces" in the Elisp
490manual for documentation of the new mode and its commands.
491
492The binding of 'd' in Edebug's keymap is now 'edebug-pop-to-backtrace'
493which replaces 'edebug-backtrace'. Consequently Edebug's backtrace
494windows now behave like those of the Lisp Debugger and of ERT, in that
495when they appear they will be the selected window.
496
497The new 'backtrace-goto-source' command, bound to 's', works in
498Edebug's backtraces on backtrace frames whose source code has
499been instrumented by Edebug.
500
478** Enhanced xterm support 501** Enhanced xterm support
479 502
480*** New variable 'xterm-set-window-title' controls whether Emacs sets 503*** New variable 'xterm-set-window-title' controls whether Emacs sets
481the XTerm window title. This feature is experimental and is disabled 504the XTerm window title. This feature is experimental and is disabled
482by default. 505by default.
483 506
484** Gamegrid
485
486** grep 507** grep
487 508
488+++ 509+++
@@ -499,6 +520,14 @@ The abbreviation can be disabled by the new option
499*** New variable 'ert-quiet' allows to make ERT output in batch mode 520*** New variable 'ert-quiet' allows to make ERT output in batch mode
500less verbose by removing non-essential information. 521less verbose by removing non-essential information.
501 522
523+++
524*** ERT's backtrace buffer now uses 'backtrace-mode'.
525Backtrace mode adds fontification and commands for changing the
526appearance of backtrace frames. See the node "Backtraces" in the Elisp
527manual for documentation of the new mode and its commands.
528
529** Gamegrid
530
502--- 531---
503*** Gamegrid now determines its default glyph size based on display 532*** Gamegrid now determines its default glyph size based on display
504dimensions, instead of always using 16 pixels. As a result, Tetris, 533dimensions, instead of always using 16 pixels. As a result, Tetris,
@@ -669,6 +698,13 @@ transport strategies as well as a separate API to use them. A
669transport implementation for process-based communication, such as is 698transport implementation for process-based communication, such as is
670used by the Language Server Protocol (LSP), is readily available. 699used by the Language Server Protocol (LSP), is readily available.
671 700
701+++
702** Backtrace mode improves viewing of Elisp backtraces.
703Backtrace mode adds pretty printing, fontification and ellipsis
704expansion to backtrace buffers produced by the Lisp debugger, Edebug
705and ERT. See the node "Backtraces" in the Elisp manual for
706documentation of the new mode and its commands.
707
672 708
673* Incompatible Lisp Changes in Emacs 27.1 709* Incompatible Lisp Changes in Emacs 27.1
674 710
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
diff --git a/test/lisp/emacs-lisp/backtrace-tests.el b/test/lisp/emacs-lisp/backtrace-tests.el
new file mode 100644
index 00000000000..edd45c770c5
--- /dev/null
+++ b/test/lisp/emacs-lisp/backtrace-tests.el
@@ -0,0 +1,436 @@
1;;; backtrace-tests.el --- Tests for backtraces -*- lexical-binding: t; -*-
2
3;; Copyright (C) 2018 Free Software Foundation, Inc.
4
5;; Author: Gemini Lasswell
6
7;; This file is part of GNU Emacs.
8
9;; GNU Emacs is free software: you can redistribute it and/or modify
10;; it under the terms of the GNU General Public License as published by
11;; the Free Software Foundation, either version 3 of the License, or
12;; (at your option) any later version.
13
14;; GNU Emacs is distributed in the hope that it will be useful,
15;; but WITHOUT ANY WARRANTY; without even the implied warranty of
16;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17;; GNU General Public License for more details.
18
19;; You should have received a copy of the GNU General Public License
20;; along with GNU Emacs. If not, see <https://www.gnu.org/licenses/>.
21
22;;; Code:
23
24(require 'backtrace)
25(require 'ert)
26(require 'ert-x)
27(require 'seq)
28
29;; Delay evaluation of the backtrace-creating functions until
30;; load so that the backtraces are the same whether this file
31;; is compiled or not.
32
33(eval-and-compile
34 (defconst backtrace-tests--uncompiled-functions
35 '(progn
36 (defun backtrace-tests--make-backtrace (arg)
37 (backtrace-tests--setup-buffer))
38
39 (defun backtrace-tests--setup-buffer ()
40 "Set up the current buffer in backtrace mode."
41 (backtrace-mode)
42 (setq backtrace-frames (backtrace-get-frames))
43 (let ((this-index))
44 ;; Discard all past `backtrace-tests-make-backtrace'.
45 (dotimes (index (length backtrace-frames))
46 (when (eq (backtrace-frame-fun (nth index backtrace-frames))
47 'backtrace-tests--make-backtrace)
48 (setq this-index index)))
49 (setq backtrace-frames (seq-subseq backtrace-frames 0 (1+ this-index))))
50 (backtrace-print))))
51
52 (eval backtrace-tests--uncompiled-functions))
53
54(defun backtrace-tests--backtrace-lines ()
55 (if debugger-stack-frame-as-list
56 '(" (backtrace-get-frames)\n"
57 " (setq backtrace-frames (backtrace-get-frames))\n"
58 " (backtrace-tests--setup-buffer)\n"
59 " (backtrace-tests--make-backtrace %s)\n")
60 '(" backtrace-get-frames()\n"
61 " (setq backtrace-frames (backtrace-get-frames))\n"
62 " backtrace-tests--setup-buffer()\n"
63 " backtrace-tests--make-backtrace(%s)\n")))
64
65(defconst backtrace-tests--line-count (length (backtrace-tests--backtrace-lines)))
66
67(defun backtrace-tests--backtrace-lines-with-locals ()
68 (let ((lines (backtrace-tests--backtrace-lines))
69 (locals '(" [no locals]\n"
70 " [no locals]\n"
71 " [no locals]\n"
72 " arg = %s\n")))
73 (apply #'append (cl-mapcar #'list lines locals))))
74
75(defun backtrace-tests--result (value)
76 (format (apply #'concat (backtrace-tests--backtrace-lines))
77 (cl-prin1-to-string value)))
78
79(defun backtrace-tests--result-with-locals (value)
80 (let ((str (cl-prin1-to-string value)))
81 (format (apply #'concat (backtrace-tests--backtrace-lines-with-locals))
82 str str)))
83
84;; TODO check that debugger-batch-max-lines still works
85
86(defconst backtrace-tests--header "Test header\n")
87(defun backtrace-tests--insert-header ()
88 (insert backtrace-tests--header))
89
90;;; Tests
91
92(ert-deftest backtrace-tests--variables ()
93 "Backtrace buffers can show and hide local variables."
94 (ert-with-test-buffer (:name "variables")
95 (let ((results (concat backtrace-tests--header
96 (backtrace-tests--result 'value)))
97 (last-frame (format (nth (1- backtrace-tests--line-count)
98 (backtrace-tests--backtrace-lines)) 'value))
99 (last-frame-with-locals
100 (format (apply #'concat (nthcdr (* 2 (1- backtrace-tests--line-count))
101 (backtrace-tests--backtrace-lines-with-locals)))
102 'value 'value)))
103 (backtrace-tests--make-backtrace 'value)
104 (setq backtrace-insert-header-function #'backtrace-tests--insert-header)
105 (backtrace-print)
106 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
107 results))
108 ;; Go to the last frame.
109 (goto-char (point-max))
110 (forward-line -1)
111 ;; Turn on locals for that frame.
112 (backtrace-toggle-locals)
113 (should (string= (backtrace-tests--get-substring (point) (point-max))
114 last-frame-with-locals))
115 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
116 (concat results
117 (format (car (last (backtrace-tests--backtrace-lines-with-locals)))
118 'value))))
119 ;; Turn off locals for that frame.
120 (backtrace-toggle-locals)
121 (should (string= (backtrace-tests--get-substring (point) (point-max))
122 last-frame))
123 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
124 results))
125 ;; Turn all locals on.
126 (backtrace-toggle-locals '(4))
127 (should (string= (backtrace-tests--get-substring (point) (point-max))
128 last-frame-with-locals))
129 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
130 (concat backtrace-tests--header
131 (backtrace-tests--result-with-locals 'value))))
132 ;; Turn all locals off.
133 (backtrace-toggle-locals '(4))
134 (should (string= (backtrace-tests--get-substring
135 (point) (+ (point) (length last-frame)))
136 last-frame))
137 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
138 results)))))
139
140(ert-deftest backtrace-tests--backward-frame ()
141 "`backtrace-backward-frame' moves backward to the start of a frame."
142 (ert-with-test-buffer (:name "backward")
143 (let ((results (concat backtrace-tests--header
144 (backtrace-tests--result nil))))
145 (backtrace-tests--make-backtrace nil)
146 (setq backtrace-insert-header-function #'backtrace-tests--insert-header)
147 (backtrace-print)
148 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
149 results))
150
151 ;; Try to move backward from header.
152 (goto-char (+ (point-min) (/ (length backtrace-tests--header) 2)))
153 (let ((pos (point)))
154 (should-error (backtrace-backward-frame))
155 (should (= pos (point))))
156
157 ;; Try to move backward from start of first line.
158 (forward-line)
159 (let ((pos (point)))
160 (should-error (backtrace-backward-frame))
161 (should (= pos (point))))
162
163 ;; Move backward from middle of line.
164 (let ((start (point)))
165 (forward-char (/ (length (nth 0 (backtrace-tests--backtrace-lines))) 2))
166 (backtrace-backward-frame)
167 (should (= start (point))))
168
169 ;; Move backward from end of buffer.
170 (goto-char (point-max))
171 (backtrace-backward-frame)
172 (let* ((last (format (car (last (backtrace-tests--backtrace-lines))) nil))
173 (len (length last)))
174 (should (string= (buffer-substring-no-properties (point) (+ (point) len))
175 last)))
176
177 ;; Move backward from start of line.
178 (backtrace-backward-frame)
179 (let* ((line (car (last (backtrace-tests--backtrace-lines) 2)))
180 (len (length line)))
181 (should (string= (buffer-substring-no-properties (point) (+ (point) len))
182 line))))))
183
184(ert-deftest backtrace-tests--forward-frame ()
185 "`backtrace-forward-frame' moves forward to the start of a frame."
186 (ert-with-test-buffer (:name "forward")
187 (let* ((arg '(1 2 3))
188 (results (concat backtrace-tests--header
189 (backtrace-tests--result arg)))
190 (first-line (nth 0 (backtrace-tests--backtrace-lines))))
191 (backtrace-tests--make-backtrace arg)
192 (setq backtrace-insert-header-function #'backtrace-tests--insert-header)
193 (backtrace-print)
194 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
195 results))
196 ;; Move forward from header.
197 (goto-char (+ (point-min) (/ (length backtrace-tests--header) 2)))
198 (backtrace-forward-frame)
199 (should (string= (backtrace-tests--get-substring
200 (point) (+ (point) (length first-line)))
201 first-line))
202
203 (let ((start (point))
204 (offset (/ (length first-line) 2))
205 (second-line (nth 1 (backtrace-tests--backtrace-lines))))
206 ;; Move forward from start of first frame.
207 (backtrace-forward-frame)
208 (should (string= (backtrace-tests--get-substring
209 (point) (+ (point) (length second-line)))
210 second-line))
211 ;; Move forward from middle of first frame.
212 (goto-char (+ start offset))
213 (backtrace-forward-frame)
214 (should (string= (backtrace-tests--get-substring
215 (point) (+ (point) (length second-line)))
216 second-line)))
217 ;; Try to move forward from middle of last frame.
218 (goto-char (- (point-max)
219 (/ 2 (length (car (last (backtrace-tests--backtrace-lines)))))))
220 (should-error (backtrace-forward-frame))
221 ;; Try to move forward from end of buffer.
222 (goto-char (point-max))
223 (should-error (backtrace-forward-frame)))))
224
225(ert-deftest backtrace-tests--single-and-multi-line ()
226 "Forms in backtrace frames can be on a single line or on multiple lines."
227 (ert-with-test-buffer (:name "single-multi-line")
228 (let* ((arg '(lambda (x) ; Quote this so it isn't made into a closure.
229 (let ((number (1+ x)))
230 (+ x number))))
231 (header-string "Test header: ")
232 (header (format "%s%s\n" header-string arg))
233 (insert-header-function (lambda ()
234 (insert header-string)
235 (insert (backtrace-print-to-string arg))
236 (insert "\n")))
237 (results (concat header (backtrace-tests--result arg)))
238 (last-line (format (nth (1- backtrace-tests--line-count)
239 (backtrace-tests--backtrace-lines))
240 arg))
241 (last-line-locals (format (nth (1- (* 2 backtrace-tests--line-count))
242 (backtrace-tests--backtrace-lines-with-locals))
243 arg)))
244
245 (backtrace-tests--make-backtrace arg)
246 (setq backtrace-insert-header-function insert-header-function)
247 (backtrace-print)
248 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
249 results))
250 ;; Check pp and collapse for the form in the header.
251 (goto-char (point-min))
252 (backtrace-tests--verify-single-and-multi-line header)
253 ;; Check pp and collapse for the last frame.
254 (goto-char (point-max))
255 (backtrace-backward-frame)
256 (backtrace-tests--verify-single-and-multi-line last-line)
257 ;; Check pp and collapse for local variables in the last line.
258 (goto-char (point-max))
259 (backtrace-backward-frame)
260 (backtrace-toggle-locals)
261 (forward-line)
262 (backtrace-tests--verify-single-and-multi-line last-line-locals))))
263
264(defun backtrace-tests--verify-single-and-multi-line (line)
265 "Verify that `backtrace-single-line' and `backtrace-multi-line' work at point.
266Point should be at the beginning of a line, and LINE should be a
267string containing the text of the line at point. Assume that the
268line contains the strings \"lambda\" and \"number\"."
269 (let ((pos (point)))
270 (backtrace-multi-line)
271 ;; Verify point is still at the start of the line.
272 (should (= pos (point))))
273
274 ;; Verify the form now spans multiple lines.
275 (let ((pos (point)))
276 (search-forward "number")
277 (should-not (= pos (point-at-bol))))
278 ;; Collapse the form.
279 (backtrace-single-line)
280 ;; Verify that the form is now back on one line,
281 ;; and that point is at the same place.
282 (should (string= (backtrace-tests--get-substring
283 (- (point) 6) (point)) "number"))
284 (should-not (= (point) (point-at-bol)))
285 (should (string= (backtrace-tests--get-substring
286 (point-at-bol) (1+ (point-at-eol)))
287 line)))
288
289(ert-deftest backtrace-tests--print-circle ()
290 "Backtrace buffers can toggle `print-circle' syntax."
291 (ert-with-test-buffer (:name "print-circle")
292 (let* ((print-circle nil)
293 (arg (let ((val (make-list 5 'a))) (nconc val val) val))
294 (results (backtrace-tests--make-regexp
295 (backtrace-tests--result arg)))
296 (results-circle (regexp-quote (let ((print-circle t))
297 (backtrace-tests--result arg))))
298 (last-frame (backtrace-tests--make-regexp
299 (format (nth (1- backtrace-tests--line-count)
300 (backtrace-tests--backtrace-lines))
301 arg)))
302 (last-frame-circle (regexp-quote
303 (let ((print-circle t))
304 (format (nth (1- backtrace-tests--line-count)
305 (backtrace-tests--backtrace-lines))
306 arg)))))
307 (backtrace-tests--make-backtrace arg)
308 (backtrace-print)
309 (should (string-match-p results
310 (backtrace-tests--get-substring (point-min) (point-max))))
311 ;; Go to the last frame.
312 (goto-char (point-max))
313 (forward-line -1)
314 ;; Turn on print-circle for that frame.
315 (backtrace-toggle-print-circle)
316 (should (string-match-p last-frame-circle
317 (backtrace-tests--get-substring (point) (point-max))))
318 ;; Turn off print-circle for the frame.
319 (backtrace-toggle-print-circle)
320 (should (string-match-p last-frame
321 (backtrace-tests--get-substring (point) (point-max))))
322 (should (string-match-p results
323 (backtrace-tests--get-substring (point-min) (point-max))))
324 ;; Turn print-circle on for the buffer.
325 (backtrace-toggle-print-circle '(4))
326 (should (string-match-p last-frame-circle
327 (backtrace-tests--get-substring (point) (point-max))))
328 (should (string-match-p results-circle
329 (backtrace-tests--get-substring (point-min) (point-max))))
330 ;; Turn print-circle off.
331 (backtrace-toggle-print-circle '(4))
332 (should (string-match-p last-frame
333 (backtrace-tests--get-substring
334 (point) (+ (point) (length last-frame)))))
335 (should (string-match-p results
336 (backtrace-tests--get-substring (point-min) (point-max)))))))
337
338(defun backtrace-tests--make-regexp (str)
339 "Make regexp from STR for `backtrace-tests--print-circle'.
340Used for results of printing circular objects without
341`print-circle' on. Look for #n in string STR where n is any
342digit and replace with #[0-9]."
343 (let ((regexp (regexp-quote str)))
344 (with-temp-buffer
345 (insert regexp)
346 (goto-char (point-min))
347 (while (re-search-forward "#[0-9]" nil t)
348 (replace-match "#[0-9]")))
349 (buffer-string)))
350
351(ert-deftest backtrace-tests--expand-ellipsis ()
352 "Backtrace buffers ellipsify large forms as buttons which expand the ellipses."
353 ;; make a backtrace with an ellipsis
354 ;; expand the ellipsis
355 (ert-with-test-buffer (:name "variables")
356 (let* ((print-level nil)
357 (print-length nil)
358 (backtrace-line-length 300)
359 (arg (make-list 40 (make-string 10 ?a)))
360 (results (backtrace-tests--result arg)))
361 (backtrace-tests--make-backtrace arg)
362 (backtrace-print)
363
364 ;; There should be an ellipsis. Find and expand it.
365 (goto-char (point-min))
366 (search-forward "...")
367 (backward-char)
368 (push-button)
369
370 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
371 results)))))
372
373(ert-deftest backtrace-tests--expand-ellipses ()
374 "Backtrace buffers ellipsify large forms and can expand the ellipses."
375 (ert-with-test-buffer (:name "variables")
376 (let* ((print-level nil)
377 (print-length nil)
378 (backtrace-line-length 300)
379 (arg (let ((outer (make-list 40 (make-string 10 ?a)))
380 (nested (make-list 40 (make-string 10 ?b))))
381 (setf (nth 39 nested) (make-list 40 (make-string 10 ?c)))
382 (setf (nth 39 outer) nested)
383 outer))
384 (results (backtrace-tests--result-with-locals arg)))
385
386 ;; Make a backtrace with local variables visible.
387 (backtrace-tests--make-backtrace arg)
388 (backtrace-print)
389 (backtrace-toggle-locals '(4))
390
391 ;; There should be two ellipses.
392 (goto-char (point-min))
393 (should (search-forward "..."))
394 (should (search-forward "..."))
395 (should-error (search-forward "..."))
396
397 ;; Expanding the last frame without argument should expand both
398 ;; ellipses, but the expansions will contain one ellipsis each.
399 (let ((buffer-len (- (point-max) (point-min))))
400 (goto-char (point-max))
401 (backtrace-backward-frame)
402 (backtrace-expand-ellipses)
403 (should (> (- (point-max) (point-min)) buffer-len))
404 (goto-char (point-min))
405 (should (search-forward "..."))
406 (should (search-forward "..."))
407 (should-error (search-forward "...")))
408
409 ;; Expanding with argument should remove all ellipses.
410 (goto-char (point-max))
411 (backtrace-backward-frame)
412 (backtrace-expand-ellipses '(4))
413 (goto-char (point-min))
414
415 (should-error (search-forward "..."))
416 (should (string= (backtrace-tests--get-substring (point-min) (point-max))
417 results)))))
418
419
420(ert-deftest backtrace-tests--to-string ()
421 "Backtraces can be produced as strings."
422 (let ((frames (ert-with-test-buffer (:name nil)
423 (backtrace-tests--make-backtrace "string")
424 backtrace-frames)))
425 (should (string= (backtrace-to-string frames)
426 (backtrace-tests--result "string")))))
427
428(defun backtrace-tests--get-substring (beg end)
429 "Return the visible text between BEG and END.
430Strip the string properties because it makes failed test results
431easier to read."
432 (substring-no-properties (filter-buffer-substring beg end)))
433
434(provide 'backtrace-tests)
435
436;;; backtrace-tests.el ends here
diff --git a/test/lisp/emacs-lisp/cl-print-tests.el b/test/lisp/emacs-lisp/cl-print-tests.el
index 404d323d0c1..a469b5526c0 100644
--- a/test/lisp/emacs-lisp/cl-print-tests.el
+++ b/test/lisp/emacs-lisp/cl-print-tests.el
@@ -56,19 +56,30 @@
56 (let ((long-list (make-list 5 'a)) 56 (let ((long-list (make-list 5 'a))
57 (long-vec (make-vector 5 'b)) 57 (long-vec (make-vector 5 'b))
58 (long-struct (cl-print-tests-con)) 58 (long-struct (cl-print-tests-con))
59 (long-string (make-string 5 ?a))
59 (print-length 4)) 60 (print-length 4))
60 (should (equal "(a a a a ...)" (cl-prin1-to-string long-list))) 61 (should (equal "(a a a a ...)" (cl-prin1-to-string long-list)))
61 (should (equal "[b b b b ...]" (cl-prin1-to-string long-vec))) 62 (should (equal "[b b b b ...]" (cl-prin1-to-string long-vec)))
62 (should (equal "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)" 63 (should (equal "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)"
63 (cl-prin1-to-string long-struct))))) 64 (cl-prin1-to-string long-struct)))
65 (should (equal "\"aaaa...\"" (cl-prin1-to-string long-string)))))
64 66
65(ert-deftest cl-print-tests-4 () 67(ert-deftest cl-print-tests-4 ()
66 "CL printing observes `print-level'." 68 "CL printing observes `print-level'."
67 (let ((deep-list '(a (b (c (d (e)))))) 69 (let* ((deep-list '(a (b (c (d (e))))))
68 (deep-struct (cl-print-tests-con)) 70 (buried-vector '(a (b (c (d [e])))))
69 (print-level 4)) 71 (deep-struct (cl-print-tests-con))
72 (buried-struct `(a (b (c (d ,deep-struct)))))
73 (buried-string '(a (b (c (d #("hello" 0 5 (cl-print-test t)))))))
74 (buried-simple-string '(a (b (c (d "hello")))))
75 (print-level 4))
70 (setf (cl-print-tests-struct-a deep-struct) deep-list) 76 (setf (cl-print-tests-struct-a deep-struct) deep-list)
71 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string deep-list))) 77 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string deep-list)))
78 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string buried-vector)))
79 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string buried-struct)))
80 (should (equal "(a (b (c (d ...))))" (cl-prin1-to-string buried-string)))
81 (should (equal "(a (b (c (d \"hello\"))))"
82 (cl-prin1-to-string buried-simple-string)))
72 (should (equal "#s(cl-print-tests-struct :a (a (b (c ...))) :b nil :c nil :d nil :e nil)" 83 (should (equal "#s(cl-print-tests-struct :a (a (b (c ...))) :b nil :c nil :d nil :e nil)"
73 (cl-prin1-to-string deep-struct))))) 84 (cl-prin1-to-string deep-struct)))))
74 85
@@ -82,6 +93,129 @@
82 (should (equal "((quote a) (function b) (\\` ((\\, c) (\\,@ d))))" 93 (should (equal "((quote a) (function b) (\\` ((\\, c) (\\,@ d))))"
83 (cl-prin1-to-string quoted-stuff)))))) 94 (cl-prin1-to-string quoted-stuff))))))
84 95
96(ert-deftest cl-print-tests-strings ()
97 "CL printing prints strings and propertized strings."
98 (let* ((str1 "abcdefghij")
99 (str2 #("abcdefghij" 3 6 (bold t) 7 9 (italic t)))
100 (str3 #("abcdefghij" 0 10 (test t)))
101 (obj '(a b))
102 ;; Since the byte compiler reuses string literals,
103 ;; and the put-text-property call is destructive, use
104 ;; copy-sequence to make a new string.
105 (str4 (copy-sequence "abcdefghij")))
106 (put-text-property 0 5 'test obj str4)
107 (put-text-property 7 10 'test obj str4)
108
109 (should (equal "\"abcdefghij\"" (cl-prin1-to-string str1)))
110 (should (equal "#(\"abcdefghij\" 3 6 (bold t) 7 9 (italic t))"
111 (cl-prin1-to-string str2)))
112 (should (equal "#(\"abcdefghij\" 0 10 (test t))"
113 (cl-prin1-to-string str3)))
114 (let ((print-circle nil))
115 (should
116 (equal
117 "#(\"abcdefghij\" 0 5 (test (a b)) 7 10 (test (a b)))"
118 (cl-prin1-to-string str4))))
119 (let ((print-circle t))
120 (should
121 (equal
122 "#(\"abcdefghij\" 0 5 (test #1=(a b)) 7 10 (test #1#))"
123 (cl-prin1-to-string str4))))))
124
125(ert-deftest cl-print-tests-ellipsis-cons ()
126 "Ellipsis expansion works in conses."
127 (let ((print-length 4)
128 (print-level 3))
129 (cl-print-tests-check-ellipsis-expansion
130 '(0 1 2 3 4 5) "(0 1 2 3 ...)" "4 5")
131 (cl-print-tests-check-ellipsis-expansion
132 '(0 1 2 3 4 5 6 7 8 9) "(0 1 2 3 ...)" "4 5 6 7 ...")
133 (cl-print-tests-check-ellipsis-expansion
134 '(a (b (c (d (e))))) "(a (b (c ...)))" "(d (e))")
135 (cl-print-tests-check-ellipsis-expansion
136 (let ((x (make-list 6 'b)))
137 (setf (nthcdr 6 x) 'c)
138 x)
139 "(b b b b ...)" "b b . c")))
140
141(ert-deftest cl-print-tests-ellipsis-vector ()
142 "Ellipsis expansion works in vectors."
143 (let ((print-length 4)
144 (print-level 3))
145 (cl-print-tests-check-ellipsis-expansion
146 [0 1 2 3 4 5] "[0 1 2 3 ...]" "4 5")
147 (cl-print-tests-check-ellipsis-expansion
148 [0 1 2 3 4 5 6 7 8 9] "[0 1 2 3 ...]" "4 5 6 7 ...")
149 (cl-print-tests-check-ellipsis-expansion
150 [a [b [c [d [e]]]]] "[a [b [c ...]]]" "[d [e]]")))
151
152(ert-deftest cl-print-tests-ellipsis-string ()
153 "Ellipsis expansion works in strings."
154 (let ((print-length 4)
155 (print-level 3))
156 (cl-print-tests-check-ellipsis-expansion
157 "abcdefg" "\"abcd...\"" "efg")
158 (cl-print-tests-check-ellipsis-expansion
159 "abcdefghijk" "\"abcd...\"" "efgh...")
160 (cl-print-tests-check-ellipsis-expansion
161 '(1 (2 (3 #("abcde" 0 5 (test t)))))
162 "(1 (2 (3 ...)))" "#(\"abcd...\" 0 5 (test t))")
163 (cl-print-tests-check-ellipsis-expansion
164 #("abcd" 0 1 (bold t) 1 2 (invisible t) 3 4 (italic t))
165 "#(\"abcd\" 0 1 (bold t) ...)" "1 2 (invisible t) ...")))
166
167(ert-deftest cl-print-tests-ellipsis-struct ()
168 "Ellipsis expansion works in structures."
169 (let ((print-length 4)
170 (print-level 3)
171 (struct (cl-print-tests-con)))
172 (cl-print-tests-check-ellipsis-expansion
173 struct "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)" ":e nil")
174 (let ((print-length 2))
175 (cl-print-tests-check-ellipsis-expansion
176 struct "#s(cl-print-tests-struct :a nil :b nil ...)" ":c nil :d nil ..."))
177 (cl-print-tests-check-ellipsis-expansion
178 `(a (b (c ,struct)))
179 "(a (b (c ...)))"
180 "#s(cl-print-tests-struct :a nil :b nil :c nil :d nil ...)")))
181
182(ert-deftest cl-print-tests-ellipsis-circular ()
183 "Ellipsis expansion works with circular objects."
184 (let ((wide-obj (list 0 1 2 3 4))
185 (deep-obj `(0 (1 (2 (3 (4))))))
186 (print-length 4)
187 (print-level 3))
188 (setf (nth 4 wide-obj) wide-obj)
189 (setf (car (cadadr (cadadr deep-obj))) deep-obj)
190 (let ((print-circle nil))
191 (cl-print-tests-check-ellipsis-expansion-rx
192 wide-obj (regexp-quote "(0 1 2 3 ...)") "\\`#[0-9]\\'")
193 (cl-print-tests-check-ellipsis-expansion-rx
194 deep-obj (regexp-quote "(0 (1 (2 ...)))") "\\`(3 (#[0-9]))\\'"))
195 (let ((print-circle t))
196 (cl-print-tests-check-ellipsis-expansion
197 wide-obj "#1=(0 1 2 3 ...)" "#1#")
198 (cl-print-tests-check-ellipsis-expansion
199 deep-obj "#1=(0 (1 (2 ...)))" "(3 (#1#))"))))
200
201(defun cl-print-tests-check-ellipsis-expansion (obj expected expanded)
202 (let* ((result (cl-prin1-to-string obj))
203 (pos (next-single-property-change 0 'cl-print-ellipsis result))
204 value)
205 (should pos)
206 (setq value (get-text-property pos 'cl-print-ellipsis result))
207 (should (equal expected result))
208 (should (equal expanded (with-output-to-string (cl-print-expand-ellipsis
209 value nil))))))
210
211(defun cl-print-tests-check-ellipsis-expansion-rx (obj expected expanded)
212 (let* ((result (cl-prin1-to-string obj))
213 (pos (next-single-property-change 0 'cl-print-ellipsis result))
214 (value (get-text-property pos 'cl-print-ellipsis result)))
215 (should (string-match expected result))
216 (should (string-match expanded (with-output-to-string
217 (cl-print-expand-ellipsis value nil))))))
218
85(ert-deftest cl-print-circle () 219(ert-deftest cl-print-circle ()
86 (let ((x '(#1=(a . #1#) #1#))) 220 (let ((x '(#1=(a . #1#) #1#)))
87 (let ((print-circle nil)) 221 (let ((print-circle nil))
@@ -99,5 +233,41 @@
99 (let ((print-circle t)) 233 (let ((print-circle t))
100 (should (equal "(0 . #1=(0 . #1#))" (cl-prin1-to-string x)))))) 234 (should (equal "(0 . #1=(0 . #1#))" (cl-prin1-to-string x))))))
101 235
236(ert-deftest cl-print-tests-print-to-string-with-limit ()
237 (let* ((thing10 (make-list 10 'a))
238 (thing100 (make-list 100 'a))
239 (thing10x10 (make-list 10 thing10))
240 (nested-thing (let ((val 'a))
241 (dotimes (_i 20)
242 (setq val (list val)))
243 val))
244 ;; Make a consistent environment for this test.
245 (print-circle nil)
246 (print-level nil)
247 (print-length nil))
248
249 ;; Print something that fits in the space given.
250 (should (string= (cl-prin1-to-string thing10)
251 (cl-print-to-string-with-limit #'cl-prin1 thing10 100)))
252
253 ;; Print something which needs to be abbreviated and which can be.
254 (should (< (length (cl-print-to-string-with-limit #'cl-prin1 thing100 100))
255 100
256 (length (cl-prin1-to-string thing100))))
257
258 ;; Print something resistant to easy abbreviation.
259 (should (string= (cl-prin1-to-string thing10x10)
260 (cl-print-to-string-with-limit #'cl-prin1 thing10x10 100)))
261
262 ;; Print something which should be abbreviated even if the limit is large.
263 (should (< (length (cl-print-to-string-with-limit #'cl-prin1 nested-thing 1000))
264 (length (cl-prin1-to-string nested-thing))))
265
266 ;; Print with no limits.
267 (dolist (thing (list thing10 thing100 thing10x10 nested-thing))
268 (let ((rep (cl-prin1-to-string thing)))
269 (should (string= rep (cl-print-to-string-with-limit #'cl-prin1 thing 0)))
270 (should (string= rep (cl-print-to-string-with-limit #'cl-prin1 thing nil)))))))
271
102 272
103;;; cl-print-tests.el ends here. 273;;; cl-print-tests.el ends here.
diff --git a/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el
index f3fc78d4e12..97dead057a9 100644
--- a/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el
+++ b/test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el
@@ -41,7 +41,7 @@
41(defun edebug-test-code-range (num) 41(defun edebug-test-code-range (num)
42 !start!(let ((index 0) 42 !start!(let ((index 0)
43 (result nil)) 43 (result nil))
44 (while (< index num)!test! 44 (while !lt!(< index num)!test!
45 (push index result)!loop! 45 (push index result)!loop!
46 (cl-incf index))!end-loop! 46 (cl-incf index))!end-loop!
47 (nreverse result))) 47 (nreverse result)))
diff --git a/test/lisp/emacs-lisp/edebug-tests.el b/test/lisp/emacs-lisp/edebug-tests.el
index 7d780edf285..7880aaf95bc 100644
--- a/test/lisp/emacs-lisp/edebug-tests.el
+++ b/test/lisp/emacs-lisp/edebug-tests.el
@@ -432,9 +432,11 @@ test and possibly others should be updated."
432 (verify-keybinding "P" 'edebug-view-outside) ;; same as v 432 (verify-keybinding "P" 'edebug-view-outside) ;; same as v
433 (verify-keybinding "W" 'edebug-toggle-save-windows) 433 (verify-keybinding "W" 'edebug-toggle-save-windows)
434 (verify-keybinding "?" 'edebug-help) 434 (verify-keybinding "?" 'edebug-help)
435 (verify-keybinding "d" 'edebug-backtrace) 435 (verify-keybinding "d" 'edebug-pop-to-backtrace)
436 (verify-keybinding "-" 'negative-argument) 436 (verify-keybinding "-" 'negative-argument)
437 (verify-keybinding "=" 'edebug-temp-display-freq-count))) 437 (verify-keybinding "=" 'edebug-temp-display-freq-count)
438 (should (eq (lookup-key backtrace-mode-map "n") 'backtrace-forward-frame))
439 (should (eq (lookup-key backtrace-mode-map "s") 'backtrace-goto-source))))
438 440
439(ert-deftest edebug-tests-stop-point-at-start-of-first-instrumented-function () 441(ert-deftest edebug-tests-stop-point-at-start-of-first-instrumented-function ()
440 "Edebug stops at the beginning of an instrumented function." 442 "Edebug stops at the beginning of an instrumented function."
@@ -924,5 +926,17 @@ test and possibly others should be updated."
924 "g" 926 "g"
925 (should (equal edebug-tests-@-result "The result of applying + to (1 x) is 11"))))) 927 (should (equal edebug-tests-@-result "The result of applying + to (1 x) is 11")))))
926 928
929(ert-deftest edebug-tests-backtrace-goto-source ()
930 "Edebug can jump to instrumented source from its *Edebug-Backtrace* buffer."
931 (edebug-tests-with-normal-env
932 (edebug-tests-setup-@ "range" '(2) t)
933 (edebug-tests-run-kbd-macro
934 "@ SPC SPC"
935 (edebug-tests-should-be-at "range" "lt")
936 "dns" ; Pop to backtrace, next frame, goto source.
937 (edebug-tests-should-be-at "range" "start")
938 "g"
939 (should (equal edebug-tests-@-result '(0 1))))))
940
927(provide 'edebug-tests) 941(provide 'edebug-tests)
928;;; edebug-tests.el ends here 942;;; edebug-tests.el ends here
diff --git a/test/lisp/emacs-lisp/ert-tests.el b/test/lisp/emacs-lisp/ert-tests.el
index cb957bd9fd6..1fe5b79ef36 100644
--- a/test/lisp/emacs-lisp/ert-tests.el
+++ b/test/lisp/emacs-lisp/ert-tests.el
@@ -376,7 +376,7 @@ This macro is used to test if macroexpansion in `should' works."
376 (test (make-ert-test :body test-body)) 376 (test (make-ert-test :body test-body))
377 (result (ert-run-test test))) 377 (result (ert-run-test test)))
378 (should (ert-test-failed-p result)) 378 (should (ert-test-failed-p result))
379 (should (eq (nth 1 (car (ert-test-failed-backtrace result))) 379 (should (eq (backtrace-frame-fun (car (ert-test-failed-backtrace result)))
380 'signal)))) 380 'signal))))
381 381
382(ert-deftest ert-test-messages () 382(ert-deftest ert-test-messages ()