Skip to content

Commit fa9aa06

Browse files
committed
pull 29.1 elisp
1 parent 17cd073 commit fa9aa06

File tree

7 files changed

+349
-633
lines changed

7 files changed

+349
-633
lines changed

lisp/emacs-lisp/backtrace.el

Lines changed: 69 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;;; backtrace.el --- generic major mode for Elisp backtraces -*- lexical-binding: t -*-
22

3-
;; Copyright (C) 2018-2024 Free Software Foundation, Inc.
3+
;; Copyright (C) 2018-2023 Free Software Foundation, Inc.
44

55
;; Author: Gemini Lasswell
66
;; Keywords: lisp, tools, maint
@@ -135,7 +135,8 @@ frames before its nearest activation frame are discarded."
135135
;; Font Locking support
136136

137137
(defconst backtrace--font-lock-keywords
138-
'()
138+
'((backtrace--match-ellipsis-in-string
139+
(1 'button prepend)))
139140
"Expressions to fontify in Backtrace mode.
140141
Fontify these in addition to the expressions Emacs Lisp mode
141142
fontifies.")
@@ -153,6 +154,16 @@ fontifies.")
153154
backtrace--font-lock-keywords)
154155
"Gaudy level highlighting for Backtrace mode.")
155156

157+
(defun backtrace--match-ellipsis-in-string (bound)
158+
;; Fontify ellipses within strings as buttons.
159+
;; This is necessary because ellipses are text property buttons
160+
;; instead of overlay buttons, which is done because there could
161+
;; be a large number of them.
162+
(when (re-search-forward "\\(\\.\\.\\.\\)\"" bound t)
163+
(and (get-text-property (- (point) 2) 'cl-print-ellipsis)
164+
(get-text-property (- (point) 3) 'cl-print-ellipsis)
165+
(get-text-property (- (point) 4) 'cl-print-ellipsis))))
166+
156167
;;; Xref support
157168

158169
(defun backtrace--xref-backend () 'elisp)
@@ -413,12 +424,12 @@ the buffer."
413424
(overlay-put o 'evaporate t))))
414425

415426
(defun backtrace--change-button-skip (beg end value)
416-
"Change the `skip' property on all buttons between BEG and END.
417-
Set it to VALUE unless the button is a `cl-print-ellipsis' button."
427+
"Change the skip property on all buttons between BEG and END.
428+
Set it to VALUE unless the button is a `backtrace-ellipsis' button."
418429
(let ((inhibit-read-only t))
419430
(setq beg (next-button beg))
420431
(while (and beg (< beg end))
421-
(unless (eq (button-type beg) 'cl-print-ellipsis)
432+
(unless (eq (button-type beg) 'backtrace-ellipsis)
422433
(button-put beg 'skip value))
423434
(setq beg (next-button beg)))))
424435

@@ -486,15 +497,34 @@ Reprint the frame with the new view plist."
486497
`(backtrace-index ,index backtrace-view ,view))
487498
(goto-char min)))
488499

489-
(defun backtrace--expand-ellipsis (orig-fun begin end val _length &rest args)
490-
"Wrapper to expand an ellipsis.
491-
For use on `cl-print-expand-ellipsis-function'."
492-
(let* ((props (backtrace-get-text-properties begin))
500+
(defun backtrace-expand-ellipsis (button)
501+
"Expand display of the elided form at BUTTON."
502+
(interactive)
503+
(goto-char (button-start button))
504+
(unless (get-text-property (point) 'cl-print-ellipsis)
505+
(if (and (> (point) (point-min))
506+
(get-text-property (1- (point)) 'cl-print-ellipsis))
507+
(backward-char)
508+
(user-error "No ellipsis to expand here")))
509+
(let* ((end (next-single-property-change (point) 'cl-print-ellipsis))
510+
(begin (previous-single-property-change end 'cl-print-ellipsis))
511+
(value (get-text-property begin 'cl-print-ellipsis))
512+
(props (backtrace-get-text-properties begin))
493513
(inhibit-read-only t))
494514
(backtrace--with-output-variables (backtrace-get-view)
495-
(let ((end (apply orig-fun begin end val backtrace-line-length args)))
496-
(add-text-properties begin end props)
497-
end))))
515+
(delete-region begin end)
516+
(insert (cl-print-to-string-with-limit #'cl-print-expand-ellipsis value
517+
backtrace-line-length))
518+
(setq end (point))
519+
(goto-char begin)
520+
(while (< (point) end)
521+
(let ((next (next-single-property-change (point) 'cl-print-ellipsis
522+
nil end)))
523+
(when (get-text-property (point) 'cl-print-ellipsis)
524+
(make-text-button (point) next :type 'backtrace-ellipsis))
525+
(goto-char next)))
526+
(goto-char begin)
527+
(add-text-properties begin end props))))
498528

499529
(defun backtrace-expand-ellipses (&optional no-limit)
500530
"Expand display of all \"...\"s in the backtrace frame at point.
@@ -667,6 +697,13 @@ line and recenter window line accordingly."
667697
(recenter window-line)))
668698
(goto-char (point-min)))))
669699

700+
;; Define button type used for ...'s.
701+
;; Set skip property so you don't have to TAB through 100 of them to
702+
;; get to the next function name.
703+
(define-button-type 'backtrace-ellipsis
704+
'skip t 'action #'backtrace-expand-ellipsis
705+
'help-echo "mouse-2, RET: expand this ellipsis")
706+
670707
(defun backtrace-print-to-string (obj &optional limit)
671708
"Return a printed representation of OBJ formatted for backtraces.
672709
Attempt to get the length of the returned string under LIMIT
@@ -678,10 +715,21 @@ characters with appropriate settings of `print-level' and
678715
(defun backtrace--print-to-string (sexp &optional limit)
679716
;; This is for use by callers who wrap the call with
680717
;; backtrace--with-output-variables.
681-
(propertize (cl-print-to-string-with-limit #'backtrace--print sexp
682-
(or limit backtrace-line-length))
683-
;; Add a unique backtrace-form property.
684-
'backtrace-form (gensym)))
718+
(setq limit (or limit backtrace-line-length))
719+
(with-temp-buffer
720+
(insert (cl-print-to-string-with-limit #'backtrace--print sexp limit))
721+
;; Add a unique backtrace-form property.
722+
(put-text-property (point-min) (point) 'backtrace-form (gensym))
723+
;; Make buttons from all the "..."s. Since there might be many of
724+
;; them, use text property buttons.
725+
(goto-char (point-min))
726+
(while (< (point) (point-max))
727+
(let ((end (next-single-property-change (point) 'cl-print-ellipsis
728+
nil (point-max))))
729+
(when (get-text-property (point) 'cl-print-ellipsis)
730+
(make-text-button (point) end :type 'backtrace-ellipsis))
731+
(goto-char end)))
732+
(buffer-string)))
685733

686734
(defun backtrace-print-frame (frame view)
687735
"Insert a backtrace FRAME at point formatted according to VIEW.
@@ -720,10 +768,9 @@ Format it according to VIEW."
720768
(def (find-function-advised-original fun))
721769
(fun-file (or (symbol-file fun 'defun)
722770
(and (subrp def)
723-
(not (special-form-p def))
771+
(not (eq 'unevalled (cdr (subr-arity def))))
724772
(find-lisp-object-file-name fun def))))
725-
(fun-beg (point))
726-
(fun-end nil))
773+
(fun-pt (point)))
727774
(cond
728775
((and evald (not debugger-stack-frame-as-list))
729776
(if (atom fun)
@@ -733,7 +780,6 @@ Format it according to VIEW."
733780
fun
734781
(when (and args (backtrace--line-length-or-nil))
735782
(/ backtrace-line-length 2)))))
736-
(setq fun-end (point))
737783
(if args
738784
(insert (backtrace--print-to-string
739785
args
@@ -749,16 +795,10 @@ Format it according to VIEW."
749795
(t
750796
(let ((fun-and-args (cons fun args)))
751797
(insert (backtrace--print-to-string fun-and-args)))
752-
;; Skip the open-paren.
753-
(cl-incf fun-beg)))
798+
(cl-incf fun-pt)))
754799
(when fun-file
755-
(make-text-button fun-beg
756-
(or fun-end
757-
(+ fun-beg
758-
;; FIXME: `backtrace--print-to-string' will
759-
;; not necessarily print FUN in the same way
760-
;; as it did when it was in FUN-AND-ARGS!
761-
(length (backtrace--print-to-string fun))))
800+
(make-text-button fun-pt (+ fun-pt
801+
(length (backtrace--print-to-string fun)))
762802
:type 'help-function-def
763803
'help-args (list fun fun-file)))
764804
;; After any frame that uses eval-buffer, insert a comment that
@@ -879,8 +919,6 @@ followed by `backtrace-print-frame', once for each stack frame."
879919
(setq-local filter-buffer-substring-function #'backtrace--filter-visible)
880920
(setq-local indent-line-function 'lisp-indent-line)
881921
(setq-local indent-region-function 'lisp-indent-region)
882-
(add-function :around (local 'cl-print-expand-ellipsis-function)
883-
#'backtrace--expand-ellipsis)
884922
(add-hook 'xref-backend-functions #'backtrace--xref-backend nil t))
885923

886924
(put 'backtrace-mode 'mode-class 'special)

lisp/emacs-lisp/debug.el

Lines changed: 35 additions & 61 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
;;; debug.el --- debuggers and related commands for Emacs -*- lexical-binding: t -*-
22

3-
;; Copyright (C) 1985-1986, 1994, 2001-2024 Free Software Foundation,
3+
;; Copyright (C) 1985-1986, 1994, 2001-2023 Free Software Foundation,
44
;; Inc.
55

66
;; Maintainer: emacs-devel@gnu.org
@@ -153,24 +153,11 @@ where CAUSE can be:
153153
(insert (debugger--buffer-state-content state)))
154154
(goto-char (debugger--buffer-state-pos state)))
155155

156-
(defvar debugger--last-error nil)
157-
158-
(defun debugger--duplicate-p (args)
159-
(pcase args
160-
(`(error ,err . ,_) (and (consp err) (eq err debugger--last-error)))))
161-
162156
;;;###autoload
163157
(setq debugger 'debug)
164158
;;;###autoload
165159
(defun debug (&rest args)
166160
"Enter debugger. \\<debugger-mode-map>`\\[debugger-continue]' returns from the debugger.
167-
168-
In interactive sessions, this switches to a backtrace buffer and shows
169-
the Lisp backtrace of function calls there. In batch mode (more accurately,
170-
when `noninteractive' is non-nil), it shows the Lisp backtrace on the
171-
standard error stream (unless `backtrace-on-error-noninteractive' is nil),
172-
and then kills Emacs, causing it to exit with a negative exit code.
173-
174161
Arguments are mainly for use when this is called from the internals
175162
of the evaluator.
176163
@@ -181,14 +168,9 @@ first will be printed into the backtrace buffer.
181168
If `inhibit-redisplay' is non-nil when this function is called,
182169
the debugger will not be entered."
183170
(interactive)
184-
(if (or inhibit-redisplay
185-
(debugger--duplicate-p args))
186-
;; Don't really try to enter debugger within an eval from redisplay
187-
;; or if we already popper into the debugger for this error,
188-
;; which can happen when we have several nested `handler-bind's that
189-
;; want to invoke the debugger.
171+
(if inhibit-redisplay
172+
;; Don't really try to enter debugger within an eval from redisplay.
190173
debugger-value
191-
(setq debugger--last-error nil)
192174
(let ((non-interactive-frame
193175
(or noninteractive ;FIXME: Presumably redundant.
194176
;; If we're in the initial-frame (where `message' just
@@ -211,7 +193,7 @@ the debugger will not be entered."
211193
(let (debugger-value
212194
(debugger-previous-state
213195
(if (get-buffer "*Backtrace*")
214-
(with-current-buffer "*Backtrace*"
196+
(with-current-buffer (get-buffer "*Backtrace*")
215197
(debugger--save-buffer-state))))
216198
(debugger-args args)
217199
(debugger-buffer (get-buffer-create "*Backtrace*"))
@@ -248,11 +230,12 @@ the debugger will not be entered."
248230
(unwind-protect
249231
(save-excursion
250232
(when (eq (car debugger-args) 'debug)
251-
(let ((base (debugger--backtrace-base)))
252-
(backtrace-debug 1 t base) ;FIXME!
253-
;; Place an extra debug-on-exit for macro's.
254-
(when (eq 'lambda (car-safe (cadr (backtrace-frame 1 base))))
255-
(backtrace-debug 2 t base))))
233+
;; Skip the frames for backtrace-debug, byte-code,
234+
;; debug--implement-debug-on-entry and the advice's `apply'.
235+
(backtrace-debug 4 t)
236+
;; Place an extra debug-on-exit for macro's.
237+
(when (eq 'lambda (car-safe (cadr (backtrace-frame 4))))
238+
(backtrace-debug 5 t)))
256239
(with-current-buffer debugger-buffer
257240
(unless (derived-mode-p 'debugger-mode)
258241
(debugger-mode))
@@ -329,12 +312,6 @@ the debugger will not be entered."
329312
(backtrace-mode))))
330313
(with-timeout-unsuspend debugger-with-timeout-suspend)
331314
(set-match-data debugger-outer-match-data)))
332-
(when (eq 'error (car-safe debugger-args))
333-
;; Remember the error we just debugged, to avoid re-entering
334-
;; the debugger if some higher-up `handler-bind' invokes us
335-
;; again, oblivious that the error was already debugged from
336-
;; a more deeply nested `handler-bind'.
337-
(setq debugger--last-error (nth 1 debugger-args)))
338315
(setq debug-on-next-call debugger-step-after-exit)
339316
debugger-value))))
340317

@@ -359,10 +336,11 @@ Make functions into cross-reference buttons if DO-XREFS is non-nil."
359336
(defun debugger-setup-buffer (args)
360337
"Initialize the `*Backtrace*' buffer for entry to the debugger.
361338
That buffer should be current already and in `debugger-mode'."
362-
(setq backtrace-frames
363-
;; The `base' frame is the one that gets index 0 and it is the entry to
364-
;; the debugger, so drop it with `cdr'.
365-
(cdr (backtrace-get-frames (debugger--backtrace-base))))
339+
(setq backtrace-frames (nthcdr
340+
;; Remove debug--implement-debug-on-entry and the
341+
;; advice's `apply' frame.
342+
(if (eq (car args) 'debug) 3 1)
343+
(backtrace-get-frames 'debug)))
366344
(when (eq (car-safe args) 'exit)
367345
(setq debugger-value (nth 1 args))
368346
(setf (cl-getf (backtrace-frame-flags (car backtrace-frames))
@@ -492,29 +470,26 @@ removes itself from that hook."
492470
(setq debugger-jumping-flag nil)
493471
(remove-hook 'post-command-hook 'debugger-reenable))
494472

495-
(defun debugger-frame-number ()
473+
(defun debugger-frame-number (&optional skip-base)
496474
"Return number of frames in backtrace before the one point points at."
497-
(let ((index (backtrace-get-index)))
475+
(let ((index (backtrace-get-index))
476+
(count 0))
498477
(unless index
499478
(error "This line is not a function call"))
500-
;; We have 3 representations of the backtrace: the real in C in `specpdl',
501-
;; the one stored in `backtrace-frames' and the textual version in
502-
;; the buffer. Check here that the one from `backtrace-frames' is in sync
503-
;; with the one from `specpdl'.
504-
(cl-assert (equal (backtrace-frame-fun (nth index backtrace-frames))
505-
(nth 1 (backtrace-frame (1+ index)
506-
(debugger--backtrace-base)))))
507-
;; The `base' frame is the one that gets index 0 and it is the entry to
508-
;; the debugger, so the first non-debugger frame is 1.
509-
;; This `+1' skips the same frame as the `cdr' in
510-
;; `debugger-setup-buffer'.
511-
(1+ index)))
479+
(unless skip-base
480+
(while (not (eq (cadr (backtrace-frame count)) 'debug))
481+
(setq count (1+ count)))
482+
;; Skip debug--implement-debug-on-entry frame.
483+
(when (eq 'debug--implement-debug-on-entry
484+
(cadr (backtrace-frame (1+ count))))
485+
(setq count (+ 2 count))))
486+
(+ count index)))
512487

513488
(defun debugger-frame ()
514489
"Request entry to debugger when this frame exits.
515490
Applies to the frame whose line point is on in the backtrace."
516491
(interactive)
517-
(backtrace-debug (debugger-frame-number) t (debugger--backtrace-base))
492+
(backtrace-debug (debugger-frame-number) t)
518493
(setf
519494
(cl-getf (backtrace-frame-flags (nth (backtrace-get-index) backtrace-frames))
520495
:debug-on-exit)
@@ -525,7 +500,7 @@ Applies to the frame whose line point is on in the backtrace."
525500
"Do not enter debugger when this frame exits.
526501
Applies to the frame whose line point is on in the backtrace."
527502
(interactive)
528-
(backtrace-debug (debugger-frame-number) nil (debugger--backtrace-base))
503+
(backtrace-debug (debugger-frame-number) nil)
529504
(setf
530505
(cl-getf (backtrace-frame-flags (nth (backtrace-get-index) backtrace-frames))
531506
:debug-on-exit)
@@ -544,16 +519,18 @@ Applies to the frame whose line point is on in the backtrace."
544519
(defun debugger--backtrace-base ()
545520
"Return the function name that marks the top of the backtrace.
546521
See `backtrace-frame'."
547-
(or (cadr (memq :backtrace-base debugger-args))
548-
#'debug))
522+
(cond ((eq 'debug--implement-debug-on-entry
523+
(cadr (backtrace-frame 1 'debug)))
524+
'debug--implement-debug-on-entry)
525+
(t 'debug)))
549526

550527
(defun debugger-eval-expression (exp &optional nframe)
551528
"Eval an expression, in an environment like that outside the debugger.
552529
The environment used is the one when entering the activation frame at point."
553530
(interactive
554531
(list (read--expression "Eval in stack frame: ")))
555532
(let ((nframe (or nframe
556-
(condition-case nil (debugger-frame-number)
533+
(condition-case nil (1+ (debugger-frame-number 'skip-base))
557534
(error 0)))) ;; If on first line.
558535
(base (debugger--backtrace-base)))
559536
(debugger-env-macro
@@ -668,7 +645,7 @@ Complete list of commands:
668645
(princ (debugger-eval-expression exp))
669646
(terpri))
670647

671-
(with-current-buffer debugger-record-buffer
648+
(with-current-buffer (get-buffer debugger-record-buffer)
672649
(message "%s"
673650
(buffer-substring (line-beginning-position 0)
674651
(line-end-position 0)))))
@@ -686,10 +663,7 @@ functions to break on entry."
686663
(if (or inhibit-debug-on-entry debugger-jumping-flag)
687664
nil
688665
(let ((inhibit-debug-on-entry t))
689-
(funcall debugger 'debug :backtrace-base
690-
;; An offset of 1 because we need to skip the advice
691-
;; OClosure that called us.
692-
'(1 . debug--implement-debug-on-entry)))))
666+
(funcall debugger 'debug))))
693667

694668
;;;###autoload
695669
(defun debug-on-entry (function)

0 commit comments

Comments
 (0)