Fix handling of debugger window. (Bug#8789)

* window.el (display-buffer-in-previous-window): New buffer
display action function.

* emacs-lisp/debug.el (debugger-bury-or-kill): New option.
(debugger-previous-window): New variable.
(debug): Rewrite using display-buffer-in-previous-window,
quit-restore-window and debugger-bury-or-kill.  (Bug#8789)
This commit is contained in:
Martin Rudalics 2012-09-08 15:28:11 +02:00
parent aa7d57c507
commit fa2bcf435d
4 changed files with 174 additions and 80 deletions

View file

@ -645,6 +645,8 @@ The interpretation of the DECLS is determined by `defun-declarations-alist'.
** New error type and new function `user-error'. Doesn't trigger the debugger.
** New option `debugger-bury-or-kill'.
+++
** New utility function `buffer-narrowed-p'.
@ -653,20 +655,25 @@ The interpretation of the DECLS is determined by `defun-declarations-alist'.
*** The functions get-lru-window, get-mru-window and get-largest-window
now accept a third argument to avoid choosing the selected window.
*** New macro with-temp-buffer-window.
*** New macro `with-temp-buffer-window'.
*** New option temp-buffer-resize-frames.
*** New option `temp-buffer-resize-frames'.
*** New function fit-frame-to-buffer and new option
fit-frame-to-buffer-bottom-margin.
*** New function `fit-frame-to-buffer' and new option
`fit-frame-to-buffer-bottom-margin'.
*** New display action function display-buffer-below-selected.
*** New display action functions `display-buffer-below-selected' and
`display-buffer-in-previous-window'.
*** New display action alist `inhibit-switch-frame', if non-nil, tells
display action functions to avoid changing which frame is selected.
*** New display action alist entry `inhibit-switch-frame', if non-nil,
tells display action functions to avoid changing which frame is
selected.
*** New display action alist `pop-up-frame-parameters', if non-nil,
specifies frame parameters to give any newly-created frame.
*** New display action alist entry `pop-up-frame-parameters', if
non-nil, specifies frame parameters to give any newly-created frame.
*** New display action alist entry `previous-window', if non-nil,
specifies window to reuse in `display-buffer-in-previous-window'.
*** The following variables are obsolete, as they can be replaced by
appropriate entries in the `display-buffer-alist' function introduced

View file

@ -1,3 +1,13 @@
2012-09-08 Martin Rudalics <rudalics@gmx.at>
* window.el (display-buffer-in-previous-window): New buffer
display action function.
* emacs-lisp/debug.el (debugger-bury-or-kill): New option.
(debugger-previous-window): New variable.
(debug): Rewrite using display-buffer-in-previous-window,
quit-restore-window and debugger-bury-or-kill. (Bug#8789)
2012-09-07 Stefan Monnier <monnier@iro.umontreal.ca>
* emacs-lisp/byte-run.el (defun): Tweak message. Simplify code.

View file

@ -48,6 +48,39 @@ the middle is discarded, and just the beginning and end are displayed."
:group 'debugger
:version "21.1")
(defcustom debugger-bury-or-kill 'bury
"How to proceed with the debugger buffer when exiting `debug'.
The value used here affects the behavior of operations on any
window previously showing the debugger buffer.
`nil' means that if its window is not deleted when exiting the
debugger, invoking `switch-to-prev-buffer' will usually show
the debugger buffer again.
`append' means that if the window is not deleted, the debugger
buffer moves to the end of the window's previous buffers so
it's less likely that a future invocation of
`switch-to-prev-buffer' will switch to it. Also, it moves the
buffer to the end of the frame's buffer list.
`bury' means that if the window is not deleted, its buffer is
removed from the window's list of previous buffers. Also, it
moves the buffer to the end of the frame's buffer list. This
value provides the most reliable remedy to not have
`switch-to-prev-buffer' switch to the debugger buffer again
without killing the buffer.
`kill' means to kill the debugger buffer.
The value used here is passed to `quit-restore-window'."
:type '(choice
(const :tag "Keep alive" nil)
(const :tag "Append" 'append)
(const :tag "Bury" 'bury)
(const :tag "Kill" 'kill))
:group 'debugger
:version "24.2")
(defvar debug-function-list nil
"List of functions currently set for debug on entry.")
@ -60,6 +93,9 @@ the middle is discarded, and just the beginning and end are displayed."
(defvar debugger-old-buffer nil
"This is the buffer that was current when the debugger was entered.")
(defvar debugger-previous-window nil
"This is the window last showing the debugger buffer.")
(defvar debugger-previous-backtrace nil
"The contents of the previous backtrace (including text properties).
This is to optimize `debugger-make-xrefs'.")
@ -133,7 +169,7 @@ first will be printed into the backtrace buffer."
(with-current-buffer (get-buffer "*Backtrace*")
(list major-mode (buffer-string)))))
(debugger-buffer (get-buffer-create "*Backtrace*"))
(debugger-old-buffer (current-buffer))
(debugger-window nil)
(debugger-step-after-exit nil)
(debugger-will-be-back nil)
;; Don't keep reading from an executing kbd macro!
@ -184,78 +220,63 @@ first will be printed into the backtrace buffer."
(cursor-in-echo-area nil))
(unwind-protect
(save-excursion
(save-window-excursion
(with-no-warnings
(setq unread-command-char -1))
(when (eq (car debugger-args) 'debug)
;; Skip the frames for backtrace-debug, byte-code,
;; and implement-debug-on-entry.
(backtrace-debug 4 t)
;; Place an extra debug-on-exit for macro's.
(when (eq 'lambda (car-safe (cadr (backtrace-frame 4))))
(backtrace-debug 5 t)))
(pop-to-buffer debugger-buffer)
(debugger-mode)
(debugger-setup-buffer debugger-args)
(when noninteractive
;; If the backtrace is long, save the beginning
;; and the end, but discard the middle.
(when (> (count-lines (point-min) (point-max))
debugger-batch-max-lines)
(goto-char (point-min))
(forward-line (/ 2 debugger-batch-max-lines))
(let ((middlestart (point)))
(goto-char (point-max))
(forward-line (- (/ 2 debugger-batch-max-lines)
debugger-batch-max-lines))
(delete-region middlestart (point)))
(insert "...\n"))
(with-no-warnings
(setq unread-command-char -1))
(when (eq (car debugger-args) 'debug)
;; Skip the frames for backtrace-debug, byte-code,
;; and implement-debug-on-entry.
(backtrace-debug 4 t)
;; Place an extra debug-on-exit for macro's.
(when (eq 'lambda (car-safe (cadr (backtrace-frame 4))))
(backtrace-debug 5 t)))
(pop-to-buffer
debugger-buffer
`((display-buffer-reuse-window
display-buffer-in-previous-window)
. (,(when debugger-previous-window
`(previous-window . ,debugger-previous-window)))))
(setq debugger-window (selected-window))
(setq debugger-previous-window debugger-window)
(debugger-mode)
(debugger-setup-buffer debugger-args)
(when noninteractive
;; If the backtrace is long, save the beginning
;; and the end, but discard the middle.
(when (> (count-lines (point-min) (point-max))
debugger-batch-max-lines)
(goto-char (point-min))
(message "%s" (buffer-string))
(kill-emacs -1))
(forward-line (/ 2 debugger-batch-max-lines))
(let ((middlestart (point)))
(goto-char (point-max))
(forward-line (- (/ 2 debugger-batch-max-lines)
debugger-batch-max-lines))
(delete-region middlestart (point)))
(insert "...\n"))
(goto-char (point-min))
(message "%s" (buffer-string))
(kill-emacs -1))
(message "")
(let ((standard-output nil)
(buffer-read-only t))
(message "")
(let ((standard-output nil)
(buffer-read-only t))
(message "")
;; Make sure we unbind buffer-read-only in the right buffer.
(save-excursion
(recursive-edit)))))
;; Kill or at least neuter the backtrace buffer, so that users
;; don't try to execute debugger commands in an invalid context.
(if (get-buffer-window debugger-buffer 0)
;; Still visible despite the save-window-excursion? Maybe it
;; it's in a pop-up frame. It would be annoying to delete and
;; recreate it every time the debugger stops, so instead we'll
;; erase it (and maybe hide it) but keep it alive.
(with-current-buffer debugger-buffer
(with-selected-window (get-buffer-window debugger-buffer 0)
(when (and (window-dedicated-p (selected-window))
(not debugger-will-be-back))
;; If the window is not dedicated, burying the buffer
;; will mean that the frame created for it is left
;; around showing some random buffer, and next time we
;; pop to the debugger buffer we'll create yet
;; another frame.
;; If debugger-will-be-back is non-nil, the frame
;; would need to be de-iconified anyway immediately
;; after when we re-enter the debugger, so iconifying it
;; here would cause flashing.
;; Drew Adams is not happy with this: he wants to frame
;; to be left at the top-level, still working on how
;; best to do that.
(bury-buffer))))
(unless debugger-previous-state
(kill-buffer debugger-buffer)))
;; Restore the previous state of the debugger-buffer, in case we were
;; in a recursive invocation of the debugger.
(when (buffer-live-p debugger-buffer)
(with-current-buffer debugger-buffer
(let ((inhibit-read-only t))
(erase-buffer)
(if (null debugger-previous-state)
(fundamental-mode)
(insert (nth 1 debugger-previous-state))
(funcall (nth 0 debugger-previous-state))))))
;; Make sure we unbind buffer-read-only in the right buffer.
(save-excursion
(recursive-edit))))
(when (and (window-live-p debugger-window)
(eq (window-buffer debugger-window) debugger-buffer))
;; Unshow debugger-buffer.
(quit-restore-window debugger-window debugger-bury-or-kill))
;; Restore previous state of debugger-buffer in case we were
;; in a recursive invocation of the debugger, otherwise just
;; erase the buffer and put it into fundamental mode.
(when (buffer-live-p debugger-buffer)
(with-current-buffer debugger-buffer
(let ((inhibit-read-only t))
(erase-buffer)
(if (null debugger-previous-state)
(fundamental-mode)
(insert (nth 1 debugger-previous-state))
(funcall (nth 0 debugger-previous-state))))))
(with-timeout-unsuspend debugger-with-timeout-suspend)
(set-match-data debugger-outer-match-data)))
;; Put into effect the modified values of these variables

View file

@ -5521,6 +5521,62 @@ the selected one."
(window--display-buffer
buffer window 'reuse display-buffer-mark-dedicated)))))
(defun display-buffer-in-previous-window (buffer alist)
"Display BUFFER in a window previously showing it.
If ALIST has a non-nil `inhibit-same-window' entry, the selected
window is not eligible for reuse.
If ALIST contains a `reusable-frames' entry, its value determines
which frames to search for a reusable window:
nil -- the selected frame (actually the last non-minibuffer frame)
A frame -- just that frame
`visible' -- all visible frames
0 -- all frames on the current terminal
t -- all frames.
If ALIST contains no `reusable-frames' entry, search just the
selected frame if `display-buffer-reuse-frames' and
`pop-up-frames' are both nil; search all frames on the current
terminal if either of those variables is non-nil.
If ALIST has a `previous-window' entry, the window specified by
that entry will override any other window found by the methods
above, even if that window never showed BUFFER before."
(let* ((alist-entry (assq 'reusable-frames alist))
(inhibit-same-window
(cdr (assq 'inhibit-same-window alist)))
(frames (cond
(alist-entry (cdr alist-entry))
((if (eq pop-up-frames 'graphic-only)
(display-graphic-p)
pop-up-frames)
0)
(display-buffer-reuse-frames 0)
(t (last-nonminibuffer-frame))))
entry best-window second-best-window window)
;; Scan windows whether they have shown the buffer recently.
(catch 'best
(dolist (window (window-list-1 (frame-first-window) 'nomini frames))
(when (and (assq buffer (window-prev-buffers window))
(not (window-dedicated-p window)))
(if (eq window (selected-window))
(unless inhibit-same-window
(setq second-best-window window))
(setq best-window window)
(throw 'best t)))))
;; When ALIST has a `previous-window' entry, that entry may override
;; anything we found so far.
(when (and (setq window (cdr (assq 'previous-window alist)))
(window-live-p window)
(not (window-dedicated-p window)))
(if (eq window (selected-window))
(unless inhibit-same-window
(setq second-best-window window))
(setq best-window window)))
;; Return best or second best window found.
(when (setq window (or best-window second-best-window))
(window--display-buffer buffer window 'reuse))))
(defun display-buffer-use-some-window (buffer alist)
"Display BUFFER in an existing window.
Search for a usable window, set that window to the buffer, and