From fd4ee361395060c8afa95393245ac8d51655ae54 Mon Sep 17 00:00:00 2001 From: Yuan Fu Date: Mon, 6 Apr 2020 09:56:15 +0200 Subject: [PATCH] Unify and improve gdb-mi source buffer display logic Unify the behavior of source buffer display for gdb-mi. Before this change, stepping and other gdb command handlers use 'gud-display-line', and 'gdb-goto-breakpoint' uses 'gdb-display-source-buffer'. Now whenever gdb-mi code tries to open a source buffer, 'gdb-display-source-buffer' is used. Also, simplify the logic in 'gdb-display-source-buffer' and add a feature to limit the maximum number of source windows. * doc/emacs/building.texi (GDB User Interface Layout): Explain source file display in GDB. * etc/NEWS (gdb-mi): Add news about source display. * lisp/progmodes/gdb-mi.el (gdb-source-window): Remove variable, change to 'gdb-source-window-list'. (gdb-source-window-list): New variable. (gdb-display-source-buffer-action, gdb-max-source-window-count): New options. (gdb-init-1, gdb-setup-windows, gdb-load-window-configuration, gdb-restore-windows): Use 'gdb-source-window' rather than 'gdb-source-window-list'. (gdb-save-window-configuration): Use 'gdb-source-window' rather than 'gdb-source-window-list'. And consider any buffer that is not a command or function buffer as a source buffer. (gdb-display-source-buffer): Use new logic. (gdb-goto-breakpoint): Remove 'display-buffer' call and don't set 'gdb-source-buffer' anymore. * lisp/progmodes/gud.el (gud-display-line): If used by gdb-mi, use 'gdb-display-source-buffer' rather than 'display-buffer'. Don't set 'gdb-source-buffer' anymore. --- doc/emacs/building.texi | 8 +++++ etc/NEWS | 6 ++++ lisp/progmodes/gdb-mi.el | 75 +++++++++++++++++++++++++++++----------- lisp/progmodes/gud.el | 14 ++++---- 4 files changed, 77 insertions(+), 26 deletions(-) diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi index 8a05680c742..77a0e807c2b 100644 --- a/doc/emacs/building.texi +++ b/doc/emacs/building.texi @@ -1022,6 +1022,14 @@ is the relevant buffer type, such as @samp{breakpoints}. You can do the same with the menu bar, with the @samp{GDB-Windows} and @samp{GDB-Frames} sub-menus of the @samp{GUD} menu. +@vindex gdb-max-source-window-count +@vindex gdb-display-source-buffer-action +By default, GDB uses at most one window to display the source file. +You can make it use more windows by customizing +@code{gdb-max-source-window-count}. You can also customize +@code{gdb-display-source-buffer-action} to control how GDB displays +source files. + When you finish debugging, kill the GUD interaction buffer with @kbd{C-x k}, which will also kill all the buffers associated with the session. However you need not do this if, after editing and diff --git a/etc/NEWS b/etc/NEWS index 81a70e9a974..1af368caa63 100644 --- a/etc/NEWS +++ b/etc/NEWS @@ -233,6 +233,12 @@ will remember the window configuration before GDB started and restore it after GDB quits. A toggle button is also provided under 'Gud -- GDB-Windows'. ++++ +*** gdb-mi now has a better logic for displaying source buffers +Now GDB only uses one source window to display source file by default. +Customize 'gdb-max-source-window-count' to use more than one window. +Control source file display by 'gdb-display-source-buffer-action'. + ** Gravatar --- diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el index 07506834f18..ba586981de6 100644 --- a/lisp/progmodes/gdb-mi.el +++ b/lisp/progmodes/gdb-mi.el @@ -224,7 +224,9 @@ Only used for files that Emacs can't find.") (defvar gdb-source-file-list nil "List of source files for the current executable.") (defvar gdb-first-done-or-error t) -(defvar gdb-source-window nil) +(defvar gdb-source-window-list nil + "List of windows used for displaying source files. +Sorted in most-recently-visited-first order.") (defvar gdb-inferior-status nil) (defvar gdb-continuation nil) (defvar gdb-supports-non-stop nil) @@ -645,6 +647,21 @@ Note that this variable only takes effect when variable :group 'gdb :version "28.1") +(defcustom gdb-display-source-buffer-action '(nil . ((inhibit-same-window . t))) + "`display-buffer' action used when GDB displays a source buffer." + :type 'list + :group 'gdb + :version "28.1") + +(defcustom gdb-max-source-window-count 1 + "Maximum number of source windows to use. +Until there are such number of source windows on screen, GDB +tries to open a new window when visiting a new source file; after +that GDB starts to reuse existing source windows." + :type 'number + :group 'gdb + :version "28.1") + (defvar gdbmi-debug-mode nil "When non-nil, print the messages sent/received from GDB/MI in *Messages*.") @@ -984,7 +1001,7 @@ detailed description of this mode. gdb-first-done-or-error t gdb-buffer-fringe-width (car (window-fringes)) gdb-debug-log nil - gdb-source-window nil + gdb-source-window-list nil gdb-inferior-status nil gdb-continuation nil gdb-buf-publisher '() @@ -2070,17 +2087,36 @@ is running." ;; GDB frame (after up, down etc). If no GDB frame is visible but the last ;; visited breakpoint is, use that window. (defun gdb-display-source-buffer (buffer) - (let* ((last-window (if gud-last-last-frame - (get-buffer-window - (gud-find-file (car gud-last-last-frame))))) - (source-window (or last-window - (if (and gdb-source-window - (window-live-p gdb-source-window)) - gdb-source-window)))) - (when source-window - (setq gdb-source-window source-window) - (set-window-buffer source-window buffer)) - source-window)) + "Find a window to display BUFFER. +Always find a window to display buffer, and return it." + ;; This function doesn't take care of setting up source window(s) at startup, + ;; that's handled by `gdb-setup-windows' (if `gdb-many-windows' is non-nil). + ;; If `buffer' is already shown in a window, use that window. + (or (get-buffer-window buffer) + (progn + ;; First, update the window list. + (setq gdb-source-window-list + (cl-remove-duplicates + (cl-remove-if-not + (lambda (win) + (and (window-live-p win) + (eq (window-frame win) + (selected-frame)))) + gdb-source-window-list))) + ;; Should we create a new window or reuse one? + (if (> gdb-max-source-window-count + (length gdb-source-window-list)) + ;; Create a new window, push it to window list and return it. + (car (push (display-buffer buffer gdb-display-source-buffer-action) + gdb-source-window-list)) + ;; Reuse a window, we use the oldest window and put that to + ;; the front of the window list. + (let ((last-win (car (last gdb-source-window-list))) + (rest (butlast gdb-source-window-list))) + (set-window-buffer last-win buffer) + (setq gdb-source-window-list + (cons last-win rest)) + last-win))))) (defun gdbmi-start-with (str offset match) @@ -4071,9 +4107,7 @@ DOC is an optional documentation string." (let* ((buffer (find-file-noselect (if (file-exists-p file) file (cdr (assoc bptno gdb-location-alist))))) - (window (or (gdb-display-source-buffer buffer) - (display-buffer buffer)))) - (setq gdb-source-window window) + (window (gdb-display-source-buffer buffer))) (with-current-buffer buffer (goto-char (point-min)) (forward-line (1- (string-to-number line))) @@ -4722,7 +4756,7 @@ file\" where the GDB session starts (see `gdb-main-file')." (select-window win2) (set-window-buffer win2 (or (gdb-get-source-buffer) (list-buffers-noselect))) - (setq gdb-source-window (selected-window)) + (setq gdb-source-window-list (list (selected-window))) (let ((win4 (split-window-right))) (gdb-set-window-buffer (gdb-get-buffer-create 'gdb-inferior-io) nil win4)) @@ -4798,7 +4832,8 @@ You can later restore this configuration from that file by (error "Unrecognized gdb buffer mode: %s" major-mode))) ;; Command buffer. ((derived-mode-p 'gud-mode) 'command) - ((equal (selected-window) gdb-source-window) 'source))) + ;; Consider everything else as source buffer. + (t 'source))) (with-window-non-dedicated nil (set-window-buffer nil placeholder) (set-window-prev-buffers (selected-window) nil) @@ -4841,7 +4876,7 @@ FILE should be a window configuration file saved by (pcase buffer-type ('source (when source-buffer (set-window-buffer nil source-buffer) - (setq gdb-source-window (selected-window)))) + (push (selected-window) gdb-source-window-list))) ('command (switch-to-buffer gud-comint-buffer)) (_ (let ((buffer (gdb-get-buffer-create buffer-type))) (with-window-non-dedicated nil @@ -4882,7 +4917,7 @@ This arrangement depends on the values of variable (if gud-last-last-frame (gud-find-file (car gud-last-last-frame)) (gud-find-file gdb-main-file))) - (setq gdb-source-window win))))) + (setq gdb-source-window-list (list win)))))) ;; Called from `gud-sentinel' in gud.el: (defun gdb-reset () diff --git a/lisp/progmodes/gud.el b/lisp/progmodes/gud.el index 567f452b935..eb43e8b7e44 100644 --- a/lisp/progmodes/gud.el +++ b/lisp/progmodes/gud.el @@ -2826,9 +2826,13 @@ Obeying it means displaying in another window the specified file and line." (buffer (with-current-buffer gud-comint-buffer (gud-find-file true-file))) - (window (and buffer - (or (get-buffer-window buffer) - (display-buffer buffer '(nil (inhibit-same-window . t)))))) + (window + (when buffer + (if (eq gud-minor-mode 'gdbmi) + (gdb-display-source-buffer buffer) + ;; Gud still has the old behavior. + (or (get-buffer-window buffer) + (display-buffer buffer '(nil (inhibit-same-window . t))))))) (pos)) (when buffer (with-current-buffer buffer @@ -2858,9 +2862,7 @@ Obeying it means displaying in another window the specified file and line." (widen) (goto-char pos)))) (when window - (set-window-point window gud-overlay-arrow-position) - (if (eq gud-minor-mode 'gdbmi) - (setq gdb-source-window window)))))) + (set-window-point window gud-overlay-arrow-position))))) ;; The gud-call function must do the right thing whether its invoking ;; keystroke is from the GUD buffer itself (via major-mode binding) -- 2.39.5