diff options
| author | Gemini Lasswell | 2018-08-03 10:28:28 -0700 |
|---|---|---|
| committer | Gemini Lasswell | 2018-08-03 10:28:28 -0700 |
| commit | da0054c30729e58259c1e7251cb03c8ef13ff943 (patch) | |
| tree | f3fd4b5256aa6c6786d0ac4f80fb1d87dcc2e401 | |
| parent | e65ec81fc3e556719fae8d8b4b42f571c7e9f4fc (diff) | |
| parent | 95b2ab3dccdc756614b4c8f45a7b206d61753705 (diff) | |
| download | emacs-da0054c30729e58259c1e7251cb03c8ef13ff943.tar.gz emacs-da0054c30729e58259c1e7251cb03c8ef13ff943.zip | |
Merge branch 'scratch/backtrace-mode'
| -rw-r--r-- | doc/lispref/debugging.texi | 115 | ||||
| -rw-r--r-- | doc/lispref/edebug.texi | 12 | ||||
| -rw-r--r-- | doc/misc/ert.texi | 8 | ||||
| -rw-r--r-- | etc/NEWS | 40 | ||||
| -rw-r--r-- | lisp/emacs-lisp/backtrace.el | 916 | ||||
| -rw-r--r-- | lisp/emacs-lisp/cl-print.el | 297 | ||||
| -rw-r--r-- | lisp/emacs-lisp/debug.el | 463 | ||||
| -rw-r--r-- | lisp/emacs-lisp/edebug.el | 229 | ||||
| -rw-r--r-- | lisp/emacs-lisp/ert.el | 41 | ||||
| -rw-r--r-- | lisp/emacs-lisp/lisp-mode.el | 10 | ||||
| -rw-r--r-- | lisp/subr.el | 19 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/backtrace-tests.el | 436 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/cl-print-tests.el | 178 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/edebug-resources/edebug-test-code.el | 2 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/edebug-tests.el | 18 | ||||
| -rw-r--r-- | test/lisp/emacs-lisp/ert-tests.el | 2 |
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 | ||
| 397 | assumptions about the state of the Lisp interpreter. These | ||
| 398 | assumptions are false if the debugger is running interpreted. | ||
| 399 | |||
| 400 | @node Backtraces | ||
| 401 | @subsection Backtraces | ||
| 402 | @cindex backtrace buffer | ||
| 403 | |||
| 404 | Debugger mode is derived from Backtrace mode, which is also used to | ||
| 405 | show backtraces by Edebug and ERT. (@pxref{Edebug}, and @ref{Top,the | ||
| 406 | ERT manual,, ert, ERT: Emacs Lisp Regression Testing}.) | ||
| 407 | |||
| 408 | @cindex stack frame | ||
| 409 | The backtrace buffer shows you the functions that are executing and | ||
| 410 | their argument values. When a backtrace buffer is created, it shows | ||
| 411 | each stack frame on one, possibly very long, line. (A stack frame is | ||
| 412 | the place where the Lisp interpreter records information about a | ||
| 413 | particular invocation of a function.) The most recently called | ||
| 414 | function 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 | 417 | In a backtrace you can specify a stack frame by moving point to a line |
| 397 | their argument values. It also allows you to specify a stack frame by | 418 | describing that frame. The frame whose line point is on is considered |
| 398 | moving point to the line describing that frame. (A stack frame is the | 419 | the @dfn{current frame}. |
| 399 | place where the Lisp interpreter records information about a particular | 420 | |
| 400 | invocation of a function.) The frame whose line point is on is | 421 | If a function name is underlined, that means Emacs knows where its |
| 401 | considered the @dfn{current frame}. Some of the debugger commands | 422 | source code is located. You can click with the mouse on that name, or |
| 402 | operate on the current frame. If a line starts with a star, that means | 423 | move to it and type @key{RET}, to visit the source code. You can also |
| 403 | that exiting that frame will call the debugger again. This is useful | 424 | type @key{RET} while point is on any name of a function or variable |
| 404 | for examining the return value of a function. | 425 | which is not underlined, to see help information for that symbol in a |
| 405 | 426 | help buffer, if any exists. The @code{xref-find-definitions} command, | |
| 406 | If a function name is underlined, that means the debugger knows | 427 | bound to @key{M-.}, can also be used on any identifier in a backtrace |
| 407 | where its source code is located. You can click with the mouse on | 428 | (@pxref{Looking Up Identifiers,,,emacs, The GNU Emacs Manual}). |
| 408 | that name, or move to it and type @key{RET}, to visit the source code. | 429 | |
| 430 | In backtraces, the tails of long lists and the ends of long strings, | ||
| 431 | vectors or structures, as well as objects which are deeply nested, | ||
| 432 | will be printed as underlined ``...''. You can click with the mouse | ||
| 433 | on a ``...'', or type @key{RET} while point is on it, to show the part | ||
| 434 | of the object that was hidden. To control how much abbreviation is | ||
| 435 | done, customize @code{backtrace-line-length}. | ||
| 436 | |||
| 437 | Here 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 |
| 411 | assumptions about how many stack frames are used for the debugger | 440 | @item v |
| 412 | itself. These assumptions are false if the debugger is running | 441 | Toggle the display of local variables of the current stack frame. |
| 413 | interpreted. | 442 | |
| 443 | @item p | ||
| 444 | Move to the beginning of the frame, or to the beginning | ||
| 445 | of the previous frame. | ||
| 446 | |||
| 447 | @item n | ||
| 448 | Move to the beginning of the next frame. | ||
| 449 | |||
| 450 | @item + | ||
| 451 | Add line breaks and indentation to the top-level Lisp form at point to | ||
| 452 | make it more readable. | ||
| 453 | |||
| 454 | @item - | ||
| 455 | Collapse the top-level Lisp form at point back to a single line. | ||
| 456 | |||
| 457 | @item # | ||
| 458 | Toggle @code{print-circle} for the frame at point. | ||
| 459 | |||
| 460 | @item . | ||
| 461 | Expand 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 |
| 420 | addition to the usual Emacs commands. The most important use of | 470 | addition to the usual Emacs commands and to the Backtrace mode commands |
| 471 | described in the previous section. The most important use of | ||
| 421 | debugger commands is for stepping through code, so that you can see | 472 | debugger commands is for stepping through code, so that you can see |
| 422 | how control flows. The debugger can step through the control | 473 | how control flows. The debugger can step through the control |
| 423 | structures of an interpreted function, but cannot do so in a | 474 | structures 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 | |||
| 427 | type @kbd{C-M-x} on its definition.) You cannot use the Lisp debugger | 478 | type @kbd{C-M-x} on its definition.) You cannot use the Lisp debugger |
| 428 | to step through a primitive function. | 479 | to step through a primitive function. |
| 429 | 480 | ||
| 481 | Some of the debugger commands operate on the current frame. If a | ||
| 482 | frame starts with a star, that means that exiting that frame will call the | ||
| 483 | debugger again. This is useful for examining the return value of a | ||
| 484 | function. | ||
| 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. | |||
| 502 | This is a list of functions that are set to break on entry by means of | 558 | This 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 | ||
| 506 | Toggle 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 |
| 626 | This function prints a trace of Lisp function calls currently active. | 680 | This function prints a trace of Lisp function calls currently active. |
| 627 | This is the function used by @code{debug} to fill up the | 681 | The 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. |
| 629 | to the stack to determine which function calls are active. The return | ||
| 630 | value is always @code{nil}. | ||
| 631 | 683 | ||
| 632 | In the following example, a Lisp expression calls @code{backtrace} | 684 | In the following example, a Lisp expression calls @code{backtrace} |
| 633 | explicitly. This prints the backtrace to the stream | 685 | explicitly. 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 | ||
| 637 | Each line of the backtrace represents one function call. The line shows | 689 | Each line of the backtrace represents one function call. The line |
| 638 | the values of the function's arguments if they are all known; if they | 690 | shows the function followed by a list of the values of the function's |
| 639 | are still being computed, the line says so. The arguments of special | 691 | arguments if they are all known; if they are still being computed, the |
| 640 | forms are elided. | 692 | line consists of a list containing the function and its unevaluated |
| 693 | arguments. 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 | |||
| 442 | Display a backtrace, excluding Edebug's own functions for clarity | 442 | Display a backtrace, excluding Edebug's own functions for clarity |
| 443 | (@code{edebug-backtrace}). | 443 | (@code{edebug-backtrace}). |
| 444 | 444 | ||
| 445 | You cannot use debugger commands in the backtrace buffer in Edebug as | 445 | @xref{Backtraces}, for a description of backtraces |
| 446 | you would in the standard debugger. | 446 | and the commands which work on them. |
| 447 | |||
| 448 | If you would like to see Edebug's functions in the backtrace, | ||
| 449 | use @kbd{M-x edebug-backtrace-show-instrumentation}. To hide them | ||
| 450 | again use @kbd{M-x edebug-backtrace-hide-instrumentation}. | ||
| 451 | |||
| 452 | If a backtrace frame starts with @samp{>} that means that Edebug knows | ||
| 453 | where the source code for the frame is located. Use @kbd{s} to jump | ||
| 454 | to the source code for the current frame. | ||
| 447 | 455 | ||
| 448 | The backtrace buffer is killed automatically when you continue | 456 | The backtrace buffer is killed automatically when you continue |
| 449 | execution. | 457 | execution. |
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 |
| 274 | Pressing @kbd{r} re-runs the test near point on its own. Pressing | 274 | Pressing @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 |
| 276 | definition of the test near point (@kbd{@key{RET}} has the same effect if | 276 | definition of the test near point (@kbd{@key{RET}} has the same effect |
| 277 | point is on the name of the test). On a failed test, @kbd{b} shows | 277 | if point is on the name of the test). On a failed test, @kbd{b} shows |
| 278 | the backtrace of the failure. | 278 | the backtrace of the failure. @xref{Debugging,, Backtraces, elisp, |
| 279 | GNU Emacs Lisp Reference Manual}, for more information about | ||
| 280 | backtraces. | ||
| 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. |
| @@ -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 |
| 467 | and case-sensitivity together with search strings in the search ring. | 467 | and 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'. | ||
| 473 | Backtrace mode adds fontification and commands for changing the | ||
| 474 | appearance of backtrace frames. See the node "Backtraces" in the Elisp | ||
| 475 | manual 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 |
| 476 | globally or for individual definitions. | 484 | globally or for individual definitions. |
| 477 | 485 | ||
| 486 | +++ | ||
| 487 | *** Edebug's backtrace buffer now uses 'backtrace-mode'. | ||
| 488 | Backtrace mode adds fontification, links and commands for changing the | ||
| 489 | appearance of backtrace frames. See the node "Backtraces" in the Elisp | ||
| 490 | manual for documentation of the new mode and its commands. | ||
| 491 | |||
| 492 | The binding of 'd' in Edebug's keymap is now 'edebug-pop-to-backtrace' | ||
| 493 | which replaces 'edebug-backtrace'. Consequently Edebug's backtrace | ||
| 494 | windows now behave like those of the Lisp Debugger and of ERT, in that | ||
| 495 | when they appear they will be the selected window. | ||
| 496 | |||
| 497 | The new 'backtrace-goto-source' command, bound to 's', works in | ||
| 498 | Edebug's backtraces on backtrace frames whose source code has | ||
| 499 | been 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 |
| 481 | the XTerm window title. This feature is experimental and is disabled | 504 | the XTerm window title. This feature is experimental and is disabled |
| 482 | by default. | 505 | by 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 |
| 500 | less verbose by removing non-essential information. | 521 | less verbose by removing non-essential information. |
| 501 | 522 | ||
| 523 | +++ | ||
| 524 | *** ERT's backtrace buffer now uses 'backtrace-mode'. | ||
| 525 | Backtrace mode adds fontification and commands for changing the | ||
| 526 | appearance of backtrace frames. See the node "Backtraces" in the Elisp | ||
| 527 | manual 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 |
| 504 | dimensions, instead of always using 16 pixels. As a result, Tetris, | 533 | dimensions, 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 | |||
| 669 | transport implementation for process-based communication, such as is | 698 | transport implementation for process-based communication, such as is |
| 670 | used by the Language Server Protocol (LSP), is readily available. | 699 | used by the Language Server Protocol (LSP), is readily available. |
| 671 | 700 | ||
| 701 | +++ | ||
| 702 | ** Backtrace mode improves viewing of Elisp backtraces. | ||
| 703 | Backtrace mode adds pretty printing, fontification and ellipsis | ||
| 704 | expansion to backtrace buffers produced by the Lisp debugger, Edebug | ||
| 705 | and ERT. See the node "Backtraces" in the Elisp manual for | ||
| 706 | documentation 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. | ||
| 48 | Set to nil to disable fontification, which may be necessary in | ||
| 49 | order 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. | ||
| 56 | Backtrace mode will attempt to abbreviate printing of backtrace | ||
| 57 | frames to make them shorter than this, but success is not | ||
| 58 | guaranteed. If set to nil or zero, Backtrace mode will not | ||
| 59 | abbreviate 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. | ||
| 81 | The list will contain objects made by CONSTRUCTOR, which | ||
| 82 | defaults to `backtrace-make-frame' and which, if provided, should | ||
| 83 | be the constructor of a structure which includes | ||
| 84 | `backtrace-frame'. If non-nil, BASE should be a function, and | ||
| 85 | frames 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. | ||
| 139 | Fontify these in addition to the expressions Emacs Lisp mode | ||
| 140 | fontifies.") | ||
| 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. | ||
| 173 | This should be a list of `backtrace-frame' objects.") | ||
| 174 | |||
| 175 | (defvar-local backtrace-view nil | ||
| 176 | "A plist describing how to render backtrace frames. | ||
| 177 | Possible 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. | ||
| 181 | If nil, no header will be created. Note that Backtrace buffers | ||
| 182 | are 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. | ||
| 186 | This 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. | ||
| 193 | Each hook function is called with no argument, and should return | ||
| 194 | non-nil if it is able to switch to the buffer containing the | ||
| 195 | source code. Execution of the hook will stop if one of the | ||
| 196 | functions returns non-nil. When adding a function to this hook, | ||
| 197 | you should also set the :source-available flag for the backtrace | ||
| 198 | frames 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. | ||
| 267 | The value is an index into `backtrace-frames', or nil. | ||
| 268 | POS, 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. | ||
| 273 | POS, 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. | ||
| 278 | POS, 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. | ||
| 283 | POS, 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. | ||
| 288 | POS, 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. | ||
| 298 | POS, 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'. | ||
| 328 | It 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. | ||
| 346 | With prefix argument ALL, toggle the value of :show-locals in | ||
| 347 | `backtrace-view', which affects all of the backtrace frames in | ||
| 348 | the 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. | ||
| 412 | Set 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. | ||
| 422 | With prefix argument ALL, toggle the value of :print-circle in | ||
| 423 | `backtrace-view', which affects all of the backtrace frames in | ||
| 424 | the 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. | ||
| 430 | FEATURE should be one of the options in `backtrace-view'. If ALL | ||
| 431 | is non-nil, toggle FEATURE for all frames in the buffer. After | ||
| 432 | toggling the feature, reprint the affected frame(s). Afterwards | ||
| 433 | position 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. | ||
| 461 | Reprint 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> | ||
| 507 | Each ellipsis will be limited to `backtrace-line-length' | ||
| 508 | characters in its expansion. With optional prefix argument | ||
| 509 | NO-LIMIT, do not limit the number of characters. Note that with | ||
| 510 | or without the argument, using this command can result in very | ||
| 511 | long lines and very poor display performance. If this happens | ||
| 512 | and is a problem, use `\\[revert-buffer]' to return to the | ||
| 513 | initial 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. | ||
| 546 | Works 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. | ||
| 553 | Locate the top level sexp at or following point on the same line, | ||
| 554 | and reformat it with FORMAT-FUNCTION, preserving the location of | ||
| 555 | point within the sexp. If no sexp is found before the end of | ||
| 556 | the line or buffer, signal an error. | ||
| 557 | |||
| 558 | FORMAT-FUNCTION will be called without arguments, with the | ||
| 559 | current buffer set to a temporary buffer containing only the | ||
| 560 | content 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. | ||
| 624 | For 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. | ||
| 644 | This erases the buffer and inserts printed representations of the | ||
| 645 | frames. Optional argument REMEMBER-POS, if non-nil, means to | ||
| 646 | move point to the entry with the same ID element as the current | ||
| 647 | line 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. | ||
| 686 | Attempt to get the length of the returned string under LIMIT | ||
| 687 | charcters 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. | ||
| 713 | Tag the sections of the frame with the `backtrace-section' text | ||
| 714 | property 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. | ||
| 733 | Format 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. | ||
| 782 | Print 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'. | ||
| 798 | Fall 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. | ||
| 834 | This mode can be used directly, or other major modes can be | ||
| 835 | derived from it, using `define-derived-mode'. | ||
| 836 | |||
| 837 | In this major mode, the buffer contains some optional lines of | ||
| 838 | header text followed by backtrace frames, each consisting of one | ||
| 839 | or more whole lines. | ||
| 840 | |||
| 841 | Letters in this mode do not insert themselves; instead they are | ||
| 842 | commands. | ||
| 843 | \\<backtrace-mode-map> | ||
| 844 | \\{backtrace-mode-map} | ||
| 845 | |||
| 846 | A mode which inherits from Backtrace mode, or a command which | ||
| 847 | creates 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 | |||
| 857 | A command which creates or switches to a Backtrace mode buffer, | ||
| 858 | such as `ert-results-pop-to-backtrace-for-test-at-point', should | ||
| 859 | initialize `backtrace-frames' to a list of `backtrace-frame' | ||
| 860 | objects (`backtrace-get-frames' is provided for that purpose, if | ||
| 861 | desired), and may optionally modify `backtrace-view', which is a | ||
| 862 | plist describing the appearance of the backtrace. Finally, it | ||
| 863 | should call `backtrace-print'. | ||
| 864 | |||
| 865 | `backtrace-print' calls `backtrace-insert-header-function' | ||
| 866 | followed 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. | ||
| 895 | Output 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. | ||
| 901 | Return the result as a string. If FRAMES is nil, use all | ||
| 902 | function 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. | ||
| 60 | Print the contents starting with the item at START, without | ||
| 61 | delimiters." | ||
| 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. | ||
| 465 | Save state in the text property in order to print the elided part | ||
| 466 | of OBJECT later. START should be 0 if the whole OBJECT is being | ||
| 467 | elided, otherwise it should be an index or other pointer into the | ||
| 468 | internals 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. | ||
| 481 | STREAM 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. | ||
| 491 | VALUE should be the value of the `cl-print-ellipsis' text property | ||
| 492 | which 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. | ||
| 530 | Attempt to get the length of the returned string under LIMIT | ||
| 531 | characters with appropriate settings of `print-level' and | ||
| 532 | `print-length.' Use PRINT-FUNCTION to print, which should take | ||
| 533 | the arguments VALUE and STREAM and which should respect | ||
| 534 | `print-length' and `print-level'. LIMIT may be nil or zero in | ||
| 535 | which case PRINT-FUNCTION will be called with `print-level' and | ||
| 536 | `print-length' bound to nil. | ||
| 537 | |||
| 538 | Use this function with `cl-prin1' to print an object, | ||
| 539 | abbreviating it with ellipses to fit within a size limit. Use | ||
| 540 | this function with `cl-prin1-expand-ellipsis' to expand an | ||
| 541 | ellipsis, abbreviating the expansion to stay within a size | ||
| 542 | limit." | ||
| 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. | ||
| 306 | Make 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. |
| 339 | That buffer should be current already." | 326 | That 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. | ||
| 352 | Include 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. |
| 512 | Applies to the frame whose line point is on in the backtrace." | 478 | Applies 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. |
| 524 | Applies to the frame whose line point is on in the backtrace." | 489 | Applies 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> |
| 695 | A line starts with `*' if exiting that frame will call the debugger. | 584 | A line starts with `*' if exiting that frame will call the debugger. |
| 696 | Type \\[debugger-frame] or \\[debugger-frame-clear] to set or remove the `*'. | 585 | Type \\[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 | ||
| 705 | Complete list of commands: | 594 | Complete 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 | |||
| 738 | For 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. |
| 858 | This function is called when SYMBOL's value is modified." | 731 | This 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. |
| 207 | That is, Edebug will try to remove its own instrumentation from the result. | 208 | That is, Edebug will try to remove its own instrumentation from the result. |
| 208 | This is useful when debugging macros where the results of expressions | 209 | This is useful when debugging macros where the results of expressions |
| 209 | are instrumented expressions. But don't do this when results might be | 210 | are instrumented expressions." |
| 210 | circular 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. |
| 1267 | The SEXP might be the result of wrapping a body, which is a list of | 1267 | The SEXP might be the result of wrapping a body, which is a list of |
| 1268 | expressions; a `progn' form will be returned enclosing these forms." | 1268 | expressions; a `progn' form will be returned enclosing these forms. |
| 1269 | (if (consp sexp) | 1269 | Does 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. | ||
| 1290 | HASH-TABLE contains the results of unwrapping cons cells within | ||
| 1291 | SEXP, which are reused to avoid infinite loops when SEXP is or | ||
| 1292 | contains 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. | ||
| 3990 | This 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. | ||
| 3993 | This 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. | 4027 | Remove 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) | 4029 | and after-index fields in both FRAMES and the returned list |
| 3978 | (cond | 4030 | of deinstrumented frames, for those frames where the source |
| 3979 | ((looking-at "^ (edebug-after") | 4031 | code 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. | ||
| 4073 | Strip 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. | ||
| 4083 | Save 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. |
| 522 | LISTBEG is the position of the start of the innermost list | 532 | LISTBEG 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'. | ||
| 4692 | EVALD, 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. | ||
| 4704 | Output 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. |
| 4711 | If non-nil, BASE should be a function, and frames before its | 4692 | If 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. | ||
| 266 | Point should be at the beginning of a line, and LINE should be a | ||
| 267 | string containing the text of the line at point. Assume that the | ||
| 268 | line 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'. | ||
| 340 | Used for results of printing circular objects without | ||
| 341 | `print-circle' on. Look for #n in string STR where n is any | ||
| 342 | digit 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. | ||
| 430 | Strip the string properties because it makes failed test results | ||
| 431 | easier 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 () |