unofficial mirror of bug-gnu-emacs@gnu.org 
 help / color / mirror / code / Atom feed
* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
@ 2020-01-18 20:57 ` Yuan Fu
  2020-01-31 10:13   ` Eli Zaretskii
  2020-04-14  1:17   ` bug#39181: Fwd: Delivery Status Notification (Failure) Yuan Fu
  0 siblings, 2 replies; 48+ messages in thread
From: Yuan Fu @ 2020-01-18 20:57 UTC (permalink / raw)
  To: 39181

[-- Attachment #1: Type: text/plain, Size: 330 bytes --]

Right now if a user wants to use gdb with many windows, the only layout option is the default 6-window layout. This patch  allows a user to save her own layout, and use this layout in gdb-mi sessions. I also included my layout (`default-rearrange`) Maybe we can even ship with some default layouts that a user can choose from?


[-- Attachment #2: default-rearrange.png --]
[-- Type: image/png, Size: 34597 bytes --]

[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




[-- Attachment #4: store-window-new.patch --]
[-- Type: application/octet-stream, Size: 16010 bytes --]

From 6db5201e418bc15ff51a170124a84182093c64db Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 17 Oct 2019 17:35:48 -0400
Subject: [PATCH 1/3] Add with-selected-window-undedicated

* lisp/window.el (with-selected-window-undedicated): new
---
 lisp/window.el | 13 +++++++++++++
 1 file changed, 13 insertions(+)

diff --git a/lisp/window.el b/lisp/window.el
index 433486385d..98b8b21f8a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,19 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       ,@body
+       (when ,window-dedicated-sym
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates
+         ;; non-nil and t, so we cannot simply set to t
+         (set-window-dedicated-p nil ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.24.1


From bb5e41e230a8452b41a569386131648eb2599501 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Mon, 14 Oct 2019 21:11:43 -0400
Subject: [PATCH 2/3] Add window configuration save/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Now you can save a gdb window configuration to a file with
‘gdb-store-window-configuration’ and restore it from a file
with ‘gdb-restore-window-configuration’. Set a default window
configuration by setting gdb-default-window-configuration-file.
Note that for the default window configuration to take effect,
gdb-many-windows needs to be t.

* lisp/progmodes/gdb-mi.el (require): add pcase, wrap inside
‘eval-when-compile’

(gdb-get-source-buffer): new, extracted out of gdb-restore-window
(gdb-restore-window): extract out gdb-get-source-buffer

(gdb-store-window-directory, gdb-buffer-p, gdb-function-buffer-p,
gdb--buffer-type, gdb--inhibit-window-dedicated,
gdb-store-window-configuration, gdb-restore-window-configuration): new

(gdb-default-window-configuration-file): new
(gdb-setup-windows): Add a condition branch that loads default window
config when available

(gdb-many-windows, gdb-get-source-file): add comments
---
 lisp/progmodes/gdb-mi.el | 210 ++++++++++++++++++++++++++++++++-------
 1 file changed, 176 insertions(+), 34 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index b08d487af3..cf0c8f29a9 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -91,7 +91,8 @@
 (require 'gud)
 (require 'json)
 (require 'bindat)
-(require 'cl-lib)
+(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -589,6 +590,20 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-store-window-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-configuration-file nil
+  "If non-nil, gdb loads this window configuration file on startup.
+This should be an absolute file path."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -4574,41 +4589,164 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil.
+
+The source file would be the most relevant file or the main file."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-configuration-file
+      (gdb-restore-window-configuration
+       gdb-default-window-configuration-file)
+    ;; default layout
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is gdb-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a gdb function buffer.
+
+E.g., locals buffer, registers buffer, but don't include the main
+command buffer (the one in where you type gdb commands) or source
+buffers."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the buffer type of BUFFER or nil.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+This symbol can be passed to `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a gdb function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-store-window-configuration (file)
+  "Save current window configuration to FILE.
+
+You can later restore this configuration from that file by
+`gdb-restore-window-configuration'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-store-window-directory default-directory))))
+  ;; we replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and store the window configuration
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters))
+          window-config)
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) if a user arranged the window configuration
+                      ;; herself and saves it, windows are probably not
+                      ;; dedicated 2) we use the same dedication flag as
+                      ;; in `gdb-display-buffer'
+                      (set-window-dedicated-p nil t)
+                      ;; we save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create'
+                      ;; one example: `gdb-registers-buffer'
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; command buffer
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; save the window configuration to FILE
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-restore-window-configuration (file)
+  "Restore window configuration from FILE.
+
+FILE should be a window configuration file saved by
+`gdb-store-window-configuration'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-store-window-directory default-directory))))
+  ;; basically we restore window configuration and go through each
+  ;; window and restore the function buffers
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; don't leak buffer
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               (goto-char (point-min))
+                               ;; we need to go to point-min even we
+                               ;; are reading the whole buffer
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; do the same thing as in
+                                   ;; `gdb-setup-windows'
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4627,6 +4765,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; this function is used when the user messed up window
+  ;; configuration and want to "reset to default". the function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4678,6 +4819,7 @@ gdb-reset
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; this function is called only once on startup
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
-- 
2.24.1


From c7ac67ec759d16e1f5d212b6fadf93d4ec853ace Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 16 Jan 2020 18:52:17 -0500
Subject: [PATCH 3/3] Restore window configuration when gdb quits

Make gdb preserve the window configuration that the user had before
starting gdb.

* lisp/progmodes/gdb-mi.el (gdb--window-configuration-before): new
(gdb): save configuration before start
(gdb-reset): restore window configuration
---
 lisp/progmodes/gdb-mi.el | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index cf0c8f29a9..582ccf9562 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -243,6 +243,9 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -762,6 +765,10 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; save window configuration before starting gdb so we can restore
+  ;; it after gdb quits
+  (setq gdb--window-configuration-before (window-state-get))
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4814,7 +4821,9 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; recover window configuration
+  (window-state-put gdb--window-configuration-before))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
-- 
2.24.1


[-- Attachment #5: default-rearrange --]
[-- Type: application/octet-stream, Size: 3008 bytes --]

(((min-height . 12) (min-width . 30) (min-height-ignore . 9) (min-width-ignore . 12) (min-height-safe . 3) (min-width-safe . 6) (min-pixel-height . 204) (min-pixel-width . 240) (min-pixel-height-ignore . 153) (min-pixel-width-ignore . 96) (min-pixel-height-safe . 51) (min-pixel-width-safe . 48)) hc (pixel-width . 1920) (pixel-height . 992) (total-width . 240) (total-height . 58) (normal-height . 1.0) (normal-width . 1.0) (combination-limit) (leaf (pixel-width . 783) (pixel-height . 992) (total-width . 98) (total-height . 58) (normal-height . 1.0) (normal-width . 0.4059618986693193) (parameters (gdb-buffer-type . source)) (buffer " *gdb-placeholder*" (selected . t) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated) (point . 1) (start . 1))) (vc (pixel-width . 847) (pixel-height . 992) (total-width . 106) (total-height . 58) (normal-height . 1.0) (normal-width . 0.4427600627286984) (combination-limit) (leaf (pixel-width . 847) (pixel-height . 483) (total-width . 106) (total-height . 28) (normal-height . 0.4829123497015181) (normal-width . 1.0) (parameters (gdb-buffer-type . command)) (buffer " *gdb-placeholder*" (selected) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated) (point . 1) (start . 1))) (leaf (pixel-width . 847) (pixel-height . 278) (total-width . 106) (total-height . 16) (normal-height . 0.28280773143438453) (normal-width . 1.0) (parameters (gdb-buffer-type . gdb-inferior-io)) (buffer " *gdb-placeholder*" (selected) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated . t) (point . 1) (start . 1))) (leaf (last . t) (pixel-width . 847) (pixel-height . 231) (total-width . 106) (total-height . 14) (normal-height . 0.23427991886409735) (normal-width . 1.0) (parameters (gdb-buffer-type . gdb-breakpoints-buffer)) (buffer " *gdb-placeholder*" (selected) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated . t) (point . 1) (start . 1)))) (vc (last . t) (pixel-width . 290) (pixel-height . 992) (total-width . 36) (total-height . 58) (normal-height . 1.0) (normal-width . 0.15127803860198227) (combination-limit) (leaf (pixel-width . 290) (pixel-height . 499) (total-width . 36) (total-height . 29) (normal-height . 0.5) (normal-width . 1.0) (parameters (gdb-buffer-type . gdb-locals-buffer)) (buffer " *gdb-placeholder*" (selected) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated . t) (point . 1) (start . 1))) (leaf (last . t) (pixel-width . 290) (pixel-height . 493) (total-width . 36) (total-height . 29) (normal-height . 0.5) (normal-width . 1.0) (parameters (gdb-buffer-type . gdb-stack-buffer)) (buffer " *gdb-placeholder*" (selected) (hscroll . 0) (fringes 8 8 nil nil) (margins nil) (scroll-bars nil 0 t nil 0 t nil) (vscroll . 0) (dedicated . t) (point . 1) (start . 1)))))

[-- Attachment #6: Type: text/plain, Size: 9188 bytes --]



In GNU Emacs 27.0.50 (build 3, x86_64-apple-darwin19.0.0, NS appkit-1894.10 Version 10.15.1 (Build 19B88))
of 2019-11-30 built on missSilver
Repository revision: e2828795d73637577c7726965974a047fe2d7119
Repository branch: master
Windowing system distributor 'Apple', version 10.3.1894
System Description:  Mac OS X 10.15.2

Recent messages:
Checking 71 files in /Users/yuan/attic/emacs/lisp/erc...
Checking 34 files in /Users/yuan/attic/emacs/lisp/emulation...
Checking 180 files in /Users/yuan/attic/emacs/lisp/emacs-lisp...
Checking 24 files in /Users/yuan/attic/emacs/lisp/cedet...
Checking 59 files in /Users/yuan/attic/emacs/lisp/calendar...
Checking 87 files in /Users/yuan/attic/emacs/lisp/calc...
Checking 113 files in /Users/yuan/attic/emacs/lisp/obsolete...
Checking for load-path shadows...done
Auto-saving...
Buffer *unsent mail to bug-gnu-emacs@gnu.org*<2> modified; kill anyway? (y or n) y

Configured using:
'configure --with-modules --with-pdumper=yes
--oldincludedir=/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk/usr/include/libxml2/'

Configured features:
NOTIFY KQUEUE ACL GNUTLS LIBXML2 ZLIB TOOLKIT_SCROLL_BARS NS MODULES
THREADS PDUMPER LCMS2

Important settings:
  value of $LC_CTYPE: UTF-8
  value of $LANG: en_CN.UTF-8
  locale-coding-system: utf-8-unix

Major mode: Emacs-Lisp

Minor modes in effect:
  magit-todos-mode: t
  bug-reference-prog-mode: t
  desktop-save-mode: t
  ghelp-global-minor-mode: t
  minibuffer-electric-default-mode: t
  flymake-mode: t
  global-magit-file-mode: t
  magit-file-mode: t
  global-git-commit-mode: t
  async-bytecomp-package-mode: t
  shell-dirtrack-mode: t
  flyspell-mode: t
  outshine-mode: t
  ws-butler-global-mode: t
  ws-butler-mode: t
  minions-mode: t
  eyebrowse-mode: t
  savehist-mode: t
  global-hl-todo-mode: t
  hl-todo-mode: t
  global-highlight-parentheses-mode: t
  highlight-parentheses-mode: t
  rainbow-delimiters-mode: t
  global-undo-tree-mode: t
  undo-tree-mode: t
  electric-pair-mode: t
  winner-mode: t
  aggressive-indent-mode: t
  ivy-prescient-mode: t
  prescient-persist-mode: t
  recentf-mode: t
  which-key-mode: t
  general-override-mode: t
  outline-minor-mode: t
  ivy-mode: t
  company-mode: t
  override-global-mode: t
  tooltip-mode: t
  global-eldoc-mode: t
  eldoc-mode: t
  electric-quote-mode: t
  electric-indent-mode: t
  mouse-wheel-mode: t
  menu-bar-mode: t
  file-name-shadow-mode: t
  global-font-lock-mode: t
  font-lock-mode: t
  auto-composition-mode: t
  auto-encryption-mode: t
  auto-compression-mode: t
  transient-mark-mode: t
  hs-minor-mode: t

Load-path shadows:
/Users/yuan/.emacs.d/ranch/winman/windman hides /Users/yuan/.emacs.d/ranch/windman/windman
/Users/yuan/.emacs.d/ranch/nerd-font/test/test-helper hides /Users/yuan/.emacs.d/ranch/doom-themes/test/test-helper
/Users/yuan/.emacs.d/ranch/julia-mode/julia-mode hides /Users/yuan/.emacs.d/package/julia-mode-20190813.1326/julia-mode
/Users/yuan/.emacs.d/ranch/julia-mode/julia-latexsubs hides /Users/yuan/.emacs.d/package/julia-mode-20190813.1326/julia-latexsubs
/Users/yuan/.emacs.d/ranch/matlab-emacs/mlint hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/mlint
/Users/yuan/.emacs.d/ranch/matlab-emacs/company-matlab-shell hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/company-matlab-shell
/Users/yuan/.emacs.d/ranch/matlab-emacs/linemark hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/linemark
/Users/yuan/.emacs.d/ranch/matlab-emacs/semanticdb-matlab hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/semanticdb-matlab
/Users/yuan/.emacs.d/ranch/matlab-emacs/semantic-matlab hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/semantic-matlab
/Users/yuan/.emacs.d/ranch/matlab-emacs/srecode-matlab hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/srecode-matlab
/Users/yuan/.emacs.d/ranch/matlab-emacs/matlab hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/matlab
/Users/yuan/.emacs.d/ranch/matlab-emacs/cedet-matlab hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/cedet-matlab
/Users/yuan/.emacs.d/ranch/matlab-emacs/tlc hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/tlc
/Users/yuan/.emacs.d/ranch/matlab-emacs/matlab-publish hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/matlab-publish
/Users/yuan/.emacs.d/ranch/matlab-emacs/matlab-mode-pkg hides /Users/yuan/.emacs.d/package/matlab-mode-20180928.1526/matlab-mode-pkg
/Users/yuan/.emacs.d/package/faceup-20170925.1946/faceup hides /Users/yuan/attic/emacs/lisp/emacs-lisp/faceup

Features:
(magit-todos pcre2el rxt re-builder grep checkdoc lisp-mnt bug-reference
vc-mtn vc-hg ffap tramp tramp-loaddefs trampver tramp-integration
files-x tramp-compat parse-time iso8601 ls-lisp shadow sort mail-extr
emacsbug sendmail vc-git vc-bzr vc-src vc-sccs vc-svn vc-cvs vc-rcs vc
vc-dispatcher magit-bookmark bookmark company-oddmuse company-keywords
company-etags etags fileloop company-gtags company-dabbrev-code
company-dabbrev company-files company-capf company-cmake company-xcode
company-clang company-semantic company-eclim company-template
company-bbdb hideshow desktop frameset trivial-copy ghelp-eglot
ghelp-helpful ghelp-builtin ghelp cus-edit cus-start cus-load
luna-publish utility pause luna-general-config minibuf-eldef eglot array
jsonrpc ert pp ewoc debug flymake-proc flymake warnings url-util
magit-submodule magit-obsolete magit-blame magit-stash magit-reflog
magit-bisect magit-push magit-pull magit-fetch magit-clone magit-remote
magit-commit magit-sequence magit-notes magit-worktree magit-tag
magit-merge magit-branch magit-reset magit-files magit-refs magit-status
magit magit-repos magit-apply magit-wip magit-log which-func magit-diff
smerge-mode diff-mode magit-core magit-autorevert autorevert filenotify
magit-margin magit-transient magit-process magit-mode transient
git-commit magit-git magit-section magit-utils crm log-edit message rmc
puny rfc822 mml mml-sec epa derived epg epg-config gnus-util rmail
rmail-loaddefs text-property-search mm-decode mm-bodies mm-encode
mail-parse rfc2231 rfc2047 rfc2045 mm-util ietf-drums mail-prsvr
mailabbrev mail-utils gmm-utils mailheader pcvs-util add-log with-editor
async-bytecomp async shell server flyspell ispell outshine
outshine-org-cmds outorg isolate inline expand-region
text-mode-expansions the-org-mode-expansions er-basic-expansions
thingatpt expand-region-core expand-region-custom ws-butler minions
eyebrowse savehist buffer-move windmove hl-todo highlight-parentheses
rainbow-delimiters doom-cyberpunk-theme undo-tree diff
doom-one-light-theme elec-pair winner doom-themes doom-themes-base
windman aggressive-indent find-char ivy-prescient prescient recentf-ext
recentf tree-widget wid-edit which-key general helpful imenu trace
edebug backtrace info-look f dash-functional help-fns radix-tree
elisp-refs s loop dash org-element avl-tree generator org advice
org-macro org-footnote org-pcomplete pcomplete org-list org-faces
org-entities time-date noutline outline org-version ob-emacs-lisp ob
ob-tangle org-src ob-ref ob-lob ob-table ob-keys ob-exp ob-comint
ob-core ob-eval org-compat org-macs org-loaddefs format-spec find-func
cal-menu calendar cal-loaddefs counsel xdg xref project dired
dired-loaddefs compile comint ansi-color swiper cl-extra help-mode ivy
delsel ring colir color ivy-overlay company edmacro kmacro pcase
use-package use-package-ensure use-package-delight use-package-diminish
use-package-bind-key bind-key easy-mmode use-package-core finder-inf
tex-site info cowboy package easymenu browse-url url-handlers url-parse
auth-source cl-seq eieio eieio-core cl-macs eieio-loaddefs
password-cache json subr-x map url-vars cl-loaddefs cl-lib lunary
lunary-ui luna-f rx seq byte-opt gv bytecomp byte-compile cconv tooltip
eldoc electric uniquify ediff-hook vc-hooks lisp-float-type mwheel
term/ns-win ns-win ucs-normalize mule-util term/common-win tool-bar dnd
fontset image regexp-opt fringe tabulated-list replace newcomment
text-mode elisp-mode lisp-mode prog-mode register page tab-bar menu-bar
rfn-eshadow isearch timer select scroll-bar mouse jit-lock font-lock
syntax facemenu font-core term/tty-colors frame minibuffer cl-generic
cham georgian utf-8-lang misc-lang vietnamese tibetan thai tai-viet lao
korean japanese eucjp-ms cp51932 hebrew greek romanian slovak czech
european ethiopic indian cyrillic chinese composite charscript charprop
case-table epa-hook jka-cmpr-hook help simple abbrev obarray
cl-preloaded nadvice loaddefs button faces cus-face macroexp files
text-properties overlay sha1 md5 base64 format env code-pages mule
custom widget hashtable-print-readable backquote threads kqueue cocoa ns
lcms2 multi-tty make-network-process emacs)

Memory information:
((conses 16 198008 22858)
(symbols 48 9376 47)
(strings 32 38332 2464)
(string-bytes 1 1117094)
(vectors 16 24687)
(vector-slots 8 290292 27848)
(floats 8 516 434)
(intervals 56 14668 1334)
(buffers 1000 28))

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-01-18 20:57 ` bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout Yuan Fu
@ 2020-01-31 10:13   ` Eli Zaretskii
  2020-02-02 14:22     ` Yuan Fu
  2020-04-14  1:17   ` bug#39181: Fwd: Delivery Status Notification (Failure) Yuan Fu
  1 sibling, 1 reply; 48+ messages in thread
From: Eli Zaretskii @ 2020-01-31 10:13 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

> From: Yuan Fu <casouri@gmail.com>
> Date: Sat, 18 Jan 2020 15:57:32 -0500
> 
> Right now if a user wants to use gdb with many windows, the only layout option is the default 6-window layout.

This is not entirely accurate.  The user could start "M-x gdb"
normally, then selectively display some of the other windows via the
Gud->GDB-Windows or Gud->GDB-Frames menus.

> This patch  allows a user to save her own layout, and use this layout in gdb-mi sessions.

I'd prefer to extend the existing capability by adding a feature, both
in the menu bar and as a command, to save and restore the window
configuration set via the above-mentioned existing facilities.  WDYT?

Please also fix all the minor nits I mentioned in review of your
previous patches: quoting, 2 blanks between sentences, complete
sentences in comments and log messages, etc.  Last, but not least,
please don't break a single patch into several separate ones, unless
each one of the separate patches can make sense if applied on its own.

Thanks.





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-01-31 10:13   ` Eli Zaretskii
@ 2020-02-02 14:22     ` Yuan Fu
  2020-02-07 22:28       ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-02-02 14:22 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 39181



> On Jan 31, 2020, at 5:13 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> From: Yuan Fu <casouri@gmail.com>
>> Date: Sat, 18 Jan 2020 15:57:32 -0500
>> 
>> Right now if a user wants to use gdb with many windows, the only layout option is the default 6-window layout.
> 
> This is not entirely accurate.  The user could start "M-x gdb"
> normally, then selectively display some of the other windows via the
> Gud->GDB-Windows or Gud->GDB-Frames menus.
> 
>> This patch  allows a user to save her own layout, and use this layout in gdb-mi sessions.
> 
> I'd prefer to extend the existing capability by adding a feature, both
> in the menu bar and as a command, to save and restore the window
> configuration set via the above-mentioned existing facilities.  WDYT?

Thanks makes sense; will do. I’ll put them in GUD windows menu.


> Please also fix all the minor nits I mentioned in review of your
> previous patches: quoting, 2 blanks between sentences, complete
> sentences in comments and log messages, etc.  Last, but not least,
> please don't break a single patch into several separate ones, unless
> each one of the separate patches can make sense if applied on its own.

Will do.

Yuan






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-02 14:22     ` Yuan Fu
@ 2020-02-07 22:28       ` Yuan Fu
  2020-02-10  4:56         ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-02-07 22:28 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 547 bytes --]

Here is the new patch. New-store-window.patch contains the one allowing user store/restore window config. I added the two menu items. And I changed the name to gdb-save-window-layout and gdb-load-window-layout because I think they are shorter and easier to understand; WDYT?

Restore-after-quit.patch contain the patch which restores the  original window configuration after gdb quits. Maybe I should send it as a separate patch?

BTW, It used to be three commits, I merged the first one on window.el into new-restore-window.patch.

Yuan


[-- Attachment #2: new-store-window.patch --]
[-- Type: application/octet-stream, Size: 2038 bytes --]

From 95cb85d242f8270387e873f65a383b2e4cb5336f Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 16 Jan 2020 18:52:17 -0500
Subject: [PATCH] Restore window configuration when gdb quits

Make gdb preserve the window configuration that the user had before
starting gdb.

* lisp/progmodes/gdb-mi.el (gdb--window-configuration-before): New
variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
---
 lisp/progmodes/gdb-mi.el | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index 8c7e81d727..4b18c5cd80 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -243,6 +243,9 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -762,6 +765,10 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; save window configuration before starting gdb so we can restore
+  ;; it after gdb quits
+  (setq gdb--window-configuration-before (window-state-get))
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4821,7 +4828,9 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; recover window configuration
+  (window-state-put gdb--window-configuration-before))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
-- 
2.25.0


[-- Attachment #3: restore-after-quit.patch --]
[-- Type: application/octet-stream, Size: 2038 bytes --]

From fcee6b9498359b089d4f99ee23cd037e615a4568 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 16 Jan 2020 18:52:17 -0500
Subject: [PATCH] Restore window configuration when gdb quits

Make gdb preserve the window configuration that the user had before
starting gdb.

* lisp/progmodes/gdb-mi.el (gdb--window-configuration-before): New
variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
---
 lisp/progmodes/gdb-mi.el | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index 3c2eee5b73..8b5c4f2ec1 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -243,6 +243,9 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -762,6 +765,10 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; save window configuration before starting gdb so we can restore
+  ;; it after gdb quits
+  (setq gdb--window-configuration-before (window-state-get))
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4819,7 +4826,9 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; recover window configuration
+  (window-state-put gdb--window-configuration-before))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-07 22:28       ` Yuan Fu
@ 2020-02-10  4:56         ` Yuan Fu
  2020-02-15  8:08           ` Eli Zaretskii
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-02-10  4:56 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 76 bytes --]

I updated my patch according to your suggestion in the memory patch.

Yuan


[-- Attachment #2: restore-after-quit.patch --]
[-- Type: application/octet-stream, Size: 2038 bytes --]

From fcee6b9498359b089d4f99ee23cd037e615a4568 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 16 Jan 2020 18:52:17 -0500
Subject: [PATCH] Restore previous window layout when gdb quits

Make gdb preserve the window configuration that the user had before
starting gdb.
* lisp/progmodes/gdb-mi.el (gdb--window-configuration-before): New
variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
---
 lisp/progmodes/gdb-mi.el | 11 ++++++++++-
 1 file changed, 10 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index 3c2eee5b73..8b5c4f2ec1 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -243,6 +243,9 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -762,6 +765,10 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; save window configuration before starting gdb so we can restore
+  ;; it after gdb quits
+  (setq gdb--window-configuration-before (window-state-get))
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4819,7 +4826,9 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; recover window configuration
+  (window-state-put gdb--window-configuration-before))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
-- 
2.25.0


[-- Attachment #3: new-store-window.patch --]
[-- Type: application/octet-stream, Size: 14641 bytes --]

From 014b52066abff85eac6b90933eec7d3aa0259fda Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Thu, 17 Oct 2019 17:35:48 -0400
Subject: [PATCH] Allow user to save/load window layout in gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'. Set a default window configuration by
setting 'gdb-default-window-layout-file'. Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-selected-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 217 +++++++++++++++++++++++++++++++++------
 lisp/window.el           |  13 +++
 2 files changed, 196 insertions(+), 34 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index b08d487af3..8c7e81d727 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -91,7 +91,8 @@
 (require 'gud)
 (require 'json)
 (require 'bindat)
-(require 'cl-lib)
+(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -589,6 +590,20 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, gdb loads this window layout file on startup.
+If not absolute, GDB will look under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -4459,6 +4474,8 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load window layout" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save window layout" . gdb-save-window-layout))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4574,41 +4591,169 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil.
+
+The source file would be the most relevant file or the main file."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is gdb-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a gdb function buffer.
+
+E.g., locals buffer, registers buffer, but don't include the main
+command buffer (the one in where you type gdb commands) or source
+buffers."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the buffer type of BUFFER or nil.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+This symbol can be passed to `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a gdb function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters))
+          window-config)
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4627,6 +4772,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4678,6 +4826,7 @@ gdb-reset
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index 433486385d..0f4f242212 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,19 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       ,@body
+       (when ,window-dedicated-sym
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates
+         ;; non-nil and t, so we cannot simply set to t.
+         (set-window-dedicated-p nil ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.0


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-10  4:56         ` Yuan Fu
@ 2020-02-15  8:08           ` Eli Zaretskii
  2020-02-15  9:55             ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Eli Zaretskii @ 2020-02-15  8:08 UTC (permalink / raw)
  To: Yuan Fu, martin rudalics; +Cc: 39181

> From: Yuan Fu <casouri@gmail.com>
> Date: Sun, 9 Feb 2020 23:56:47 -0500
> Cc: 39181@debbugs.gnu.org
> 
> I updated my patch according to your suggestion in the memory patch.

Thanks.

Martin, any comments?





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-15  8:08           ` Eli Zaretskii
@ 2020-02-15  9:55             ` martin rudalics
  2020-02-15 10:19               ` Eli Zaretskii
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-02-15  9:55 UTC (permalink / raw)
  To: Eli Zaretskii, Yuan Fu; +Cc: 39181

 > Martin, any comments?

IMHO "restoring the previous window layout when gdb quits" should be
opt-in (or at least opt-out).  Maybe it also should restore the layout
only when 'gdb-many-windows' is non-nil, so we'd have the three option
values nil, t, and if-gdb-many-windows (in a menu entry).

martin





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-15  9:55             ` martin rudalics
@ 2020-02-15 10:19               ` Eli Zaretskii
  2020-02-15 20:37                 ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Eli Zaretskii @ 2020-02-15 10:19 UTC (permalink / raw)
  To: martin rudalics; +Cc: casouri, 39181

> Cc: 39181@debbugs.gnu.org
> From: martin rudalics <rudalics@gmx.at>
> Date: Sat, 15 Feb 2020 10:55:34 +0100
> 
>  > Martin, any comments?
> 
> IMHO "restoring the previous window layout when gdb quits" should be
> opt-in (or at least opt-out).  Maybe it also should restore the layout
> only when 'gdb-many-windows' is non-nil, so we'd have the three option
> values nil, t, and if-gdb-many-windows (in a menu entry).

I had the same thoughts myself, and I agree on both counts.





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-15 10:19               ` Eli Zaretskii
@ 2020-02-15 20:37                 ` Yuan Fu
  2020-02-16 10:00                   ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-02-15 20:37 UTC (permalink / raw)
  To: Eli Zaretskii; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 687 bytes --]



> On Feb 15, 2020, at 5:19 AM, Eli Zaretskii <eliz@gnu.org> wrote:
> 
>> Cc: 39181@debbugs.gnu.org
>> From: martin rudalics <rudalics@gmx.at>
>> Date: Sat, 15 Feb 2020 10:55:34 +0100
>> 
>>> Martin, any comments?
>> 
>> IMHO "restoring the previous window layout when gdb quits" should be
>> opt-in (or at least opt-out).  Maybe it also should restore the layout
>> only when 'gdb-many-windows' is non-nil, so we'd have the three option
>> values nil, t, and if-gdb-many-windows (in a menu entry).
> 
> I had the same thoughts myself, and I agree on both counts.

I agree. I made it into a custom variable with choices, is that what you mean by menu entry?

Yuan


[-- Attachment #2.1: Type: text/html, Size: 2607 bytes --]

[-- Attachment #2.2: restore-after-quit-new.patch --]
[-- Type: application/octet-stream, Size: 3652 bytes --]

From 03ab125435ca145ca81707e09524c64d63c80183 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Sat, 15 Feb 2020 15:15:20 -0500
Subject: [PATCH] Restore previous window layout when gdb quits

Make gdb preserve the window configuration that the user had before
starting gdb.
* lisp/progmodes/gdb-mi.el (gdb--window-configuration-before): New
variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
---
 lisp/progmodes/gdb-mi.el | 43 +++++++++++++++++++++++++++++++++++++++-
 1 file changed, 42 insertions(+), 1 deletion(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..313fc58dce 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -253,6 +253,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -761,6 +780,16 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (setq gdb--window-configuration-before (window-state-get)))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4705,7 +4734,19 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
-- 
2.25.0


[-- Attachment #2.3: Type: text/html, Size: 226 bytes --]

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-15 20:37                 ` Yuan Fu
@ 2020-02-16 10:00                   ` martin rudalics
  2020-03-03 23:41                     ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-02-16 10:00 UTC (permalink / raw)
  To: Yuan Fu, Eli Zaretskii; +Cc: 39181

 > I agree. I made it into a custom variable with choices,

That's the first part of what I meant.

 > is that what you mean by menu entry?

The menu entry could be written on top of the 'defcustom' and appear as
a toggle somewhere near the "Restore Window Layout" menu entry.  Also we
should explain the differences between that entry and your new option
since otherwise users might get confused.

martin





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-02-16 10:00                   ` martin rudalics
@ 2020-03-03 23:41                     ` Yuan Fu
  2020-03-04 13:28                       ` Fu Yuan
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-03 23:41 UTC (permalink / raw)
  To: martin rudalics; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 288 bytes --]

Sorry for the delay, I missed the mail and was waiting for your reply. I added a toggle button in the menu with help echos. How does it look? Since there are more than two options, I make the button to toggle between the two basic ones. Also I combined two patches into one.

Yuan



[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 18341 bytes --]

From 9ce7c5a14de3dfb6baf91a4287d4462157dd31e1 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add window streo/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'.  Set a default window configuration by
setting 'gdb-default-window-layout-file'.  Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
Make gdb preserve the window configuration that the user had before
starting gdb.  In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu. Add "Restore window layout" button to menu.
(gdb-toggle-restore-window-layout): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-selected-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 271 ++++++++++++++++++++++++++++++++++-----
 lisp/window.el           |  13 ++
 2 files changed, 249 insertions(+), 35 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..bee33b69e4 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -91,7 +91,8 @@
 (require 'gud)
 (require 'json)
 (require 'bindat)
-(require 'cl-lib)
+(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +254,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +623,20 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, gdb loads this window layout file on startup.
+If not absolute, GDB will look under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +795,16 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (setq gdb--window-configuration-before (window-state-get)))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4535,13 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load window layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save window layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
+  (define-key menu [restore-layout-when-finish]
+    '(menu-item "Restore window layout"
+                gdb-toggle-restore-window-layout
+                . (:button (:toggle . gdb-restore-window-layout-after-quit)
+                           :help "If on, GDB restores the window layout you had before starting GDB after it quits")))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4606,41 +4657,175 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-layout ()
+  "Toggle whether to restore window layout when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-layout-after-quit
+        (not gdb-restore-window-layout-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil.
+
+The source file would be the most relevant file or the main file."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is gdb-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a gdb function buffer.
+
+E.g., locals buffer, registers buffer, but don't include the main
+command buffer (the one in where you type gdb commands) or source
+buffers."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the buffer type of BUFFER or nil.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+This symbol can be passed to `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a gdb function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters))
+          window-config)
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4659,6 +4844,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4893,24 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..229400966a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,19 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       ,@body
+       (when ,window-dedicated-sym
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates
+         ;; non-nil and t, so we cannot simply set to t.
+         (set-window-dedicated-p nil ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-03 23:41                     ` Yuan Fu
@ 2020-03-04 13:28                       ` Fu Yuan
  2020-03-05  6:12                         ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Fu Yuan @ 2020-03-04 13:28 UTC (permalink / raw)
  To: martin rudalics; +Cc: 39181

Actually, it is more intuitive to _alwaya_ save the original window layout on startup and restore when quit  depending on the toggle variable. Let me fix that tonight. Sorry for the fuzz.

Yuan

> 在 2020年3月3日,下午6:41,Yuan Fu <casouri@gmail.com> 写道:
> 
> Sorry for the delay, I missed the mail and was waiting for your reply. I added a toggle button in the menu with help echos. How does it look? Since there are more than two options, I make the button to toggle between the two basic ones. Also I combined two patches into one.
> 
> Yuan
> 
> 
> <new-window.patch>





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-04 13:28                       ` Fu Yuan
@ 2020-03-05  6:12                         ` Yuan Fu
  2020-03-05  9:14                           ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-05  6:12 UTC (permalink / raw)
  To: martin rudalics; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 26 bytes --]

Here is the patch.

Yuan


[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 18137 bytes --]

From c5226b26f117806572da3cc0acbe709c37880181 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add window streo/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'.  Set a default window configuration by
setting 'gdb-default-window-layout-file'.  Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
Make gdb preserve the window configuration that the user had before
starting gdb.  In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu. Add "Restore window layout" button to menu.
(gdb-toggle-restore-window-layout): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-selected-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 267 ++++++++++++++++++++++++++++++++++-----
 lisp/window.el           |  13 ++
 2 files changed, 245 insertions(+), 35 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..c8d4c0ceab 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -91,7 +91,8 @@
 (require 'gud)
 (require 'json)
 (require 'bindat)
-(require 'cl-lib)
+(eval-when-compile (require 'cl-lib))
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +254,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +623,20 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, gdb loads this window layout file on startup.
+If not absolute, GDB will look under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +795,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-layout-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4531,13 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load window layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save window layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
+  (define-key menu [restore-layout-when-finish]
+    '(menu-item "Restore window layout"
+                gdb-toggle-restore-window-layout
+                . (:button (:toggle . gdb-restore-window-layout-after-quit)
+                           :help "If on, GDB restores the window layout you had before starting GDB after it quits")))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4606,41 +4653,175 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-layout ()
+  "Toggle whether to restore window layout when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-layout-after-quit
+        (not gdb-restore-window-layout-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil.
+
+The source file would be the most relevant file or the main file."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is gdb-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a gdb function buffer.
+
+E.g., locals buffer, registers buffer, but don't include the main
+command buffer (the one in where you type gdb commands) or source
+buffers."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the buffer type of BUFFER or nil.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+This symbol can be passed to `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a gdb function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters))
+          window-config)
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4659,6 +4840,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4889,24 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..229400966a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,19 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       ,@body
+       (when ,window-dedicated-sym
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates
+         ;; non-nil and t, so we cannot simply set to t.
+         (set-window-dedicated-p nil ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-05  6:12                         ` Yuan Fu
@ 2020-03-05  9:14                           ` martin rudalics
  2020-03-07 18:09                             ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-05  9:14 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

 > Here is the patch.

Thanks. It gets me here the following warnings when building on master
(sorry, I'm a complete ignorant of the 'cl-' snafu):

   ELC      progmodes/gdb-mi.elc

In toplevel form:
../../lisp/progmodes/gdb-mi.el:4738:1: Warning: Unused lexical variable
     ‘window-config’

In end of data:
../../lisp/progmodes/gdb-mi.el:5069:1: Warning: the following functions might
     not be defined at runtime: cl-delete-if, cl-find-if, cl-mapcar

A few remarks:

+  "If non-nil, gdb loads this window layout file on startup.
+If not absolute, GDB will look under `gdb-window-layout-directory'."

We should settle in descriptions on whether we write gdb or GDB.  Also,
"if not absolute" is too terse IMHO.

+  (define-key menu [load-layout] '("Load window layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save window layout" "Save current GDB window layout to a file" . gdb-save-window-layout))

I think we could omit the "window" in these which should allow us to,
instead of

+    '(menu-item "Restore window layout"

say

+    '(menu-item "Restore layout after quit"

so it becomes more clear that "load" and "save" act _immediately_ on the
current state while "restore" is a more general setting that becomes
effective only when the user quits.  (Note also that in general we
cannot guarantee that menu tooltips are always shown on each and every
system where Emacs runs.)

Also, I would mention all four possible values of
'gdb-restore-window-layout-after-quit' (currently "toggle" indicates
that there are only two of them) like, for example, with the side values
for tool-bar mode.

+  "Return a buffer displaying source file or nil.
+
+The source file would be the most relevant file or the main file."

This is IMHO too terse in every respect.  Neither "source file" nor
"main file" are canonical terms in the context of GDB so we should
explain here how they are set up (what is nil in this context?).

+E.g., locals buffer, registers buffer, but don't include the main

I would say "Function buffers are locals buffers, ...".

+(defun gdb--buffer-type (buffer)
+  "Return the buffer type of BUFFER or nil.

Maybe

"Return the type of BUFFER if it is a GDB function buffer."

would be better.

Thanks for your work on this, martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-05  9:14                           ` martin rudalics
@ 2020-03-07 18:09                             ` Yuan Fu
  2020-03-07 19:07                               ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-07 18:09 UTC (permalink / raw)
  To: martin rudalics; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 2872 bytes --]



> On Mar 5, 2020, at 4:14 AM, martin rudalics <rudalics@gmx.at> wrote:
> 
> > Here is the patch.
> 
> Thanks. It gets me here the following warnings when building on master
> (sorry, I'm a complete ignorant of the 'cl-' snafu):
> 
>  ELC      progmodes/gdb-mi.elc
> 
> In toplevel form:
> ../../lisp/progmodes/gdb-mi.el:4738:1: Warning: Unused lexical variable
>    ‘window-config’
> 
> In end of data:
> ../../lisp/progmodes/gdb-mi.el:5069:1: Warning: the following functions might
>    not be defined at runtime: cl-delete-if, cl-find-if, cl-mapcar

Fixed.

> 
> A few remarks:
> 
> +  "If non-nil, gdb loads this window layout file on startup.
> +If not absolute, GDB will look under `gdb-window-layout-directory'."
> 
> We should settle in descriptions on whether we write gdb or GDB.  Also,
> "if not absolute" is too terse IMHO.
> 
> +  (define-key menu [load-layout] '("Load window layout" "Load GDB window layout from a file" . gdb-load-window-layout))
> +  (define-key menu [save-layout] '("Save window layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
> 
> I think we could omit the "window" in these which should allow us to,
> instead of
> 
> +    '(menu-item "Restore window layout"
> 
> say
> 
> +    '(menu-item "Restore layout after quit"
> 
> so it becomes more clear that "load" and "save" act _immediately_ on the
> current state while "restore" is a more general setting that becomes
> effective only when the user quits.  (Note also that in general we
> cannot guarantee that menu tooltips are always shown on each and every
> system where Emacs runs.)

> 
> Also, I would mention all four possible values of
> 'gdb-restore-window-layout-after-quit' (currently "toggle" indicates
> that there are only two of them) like, for example, with the side values
> for tool-bar mode.

I removed “window” in their names. I mentioned the other possible values in the help echo, is that fine? Not sure where else I can put it.

> 
> +  "Return a buffer displaying source file or nil.
> +
> +The source file would be the most relevant file or the main file."
> 
> This is IMHO too terse in every respect.  Neither "source file" nor
> "main file" are canonical terms in the context of GDB so we should
> explain here how they are set up (what is nil in this context?).
> 
> +E.g., locals buffer, registers buffer, but don't include the main
> 
> I would say "Function buffers are locals buffers, ...".
> 
> +(defun gdb--buffer-type (buffer)
> +  "Return the buffer type of BUFFER or nil.
> 
> Maybe
> 
> "Return the type of BUFFER if it is a GDB function buffer."
> 
> would be better.

I added some explanations. I hope they are clear now.

> Thanks for your work on this, Martin

Thanks for reviewing :-)

Here is the new patch.

Yuan


[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 18574 bytes --]

From 8c17b76667317ef85a10df5244a8ad3a44b172b9 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add window streo/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'.  Set a default window configuration by
setting 'gdb-default-window-layout-file'.  Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
Make gdb preserve the window configuration that the user had before
starting gdb.  In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu. Add "Restore window layout" button to menu.
(gdb-toggle-restore-window-layout): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-selected-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 272 ++++++++++++++++++++++++++++++++++-----
 lisp/window.el           |  13 ++
 2 files changed, 251 insertions(+), 34 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..206d81ec6a 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -92,6 +92,8 @@
 (require 'json)
 (require 'bindat)
 (require 'cl-lib)
+(require 'cl-seq)
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +255,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +624,22 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, GDB loads this window layout file on startup.
+This should be the path to the window layout file.  If the path
+is not an absolute path, GDB treats it as a relative path and
+looks under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +798,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-layout-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4534,14 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
+  (define-key menu [restore-layout-when-finish]
+    '(menu-item "Restore layout after quit"
+                gdb-toggle-restore-window-layout
+                . (:button (:toggle . gdb-restore-window-layout-after-quit)
+                           :help "Toggle between always restore the window layout after GDB quits and never restore.
+You can also change this setting in Customize to conditionally restore.")))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4606,41 +4657,178 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-layout ()
+  "Toggle whether to restore window layout when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-layout-after-quit
+        (not gdb-restore-window-layout-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil if we can't find one.
+
+The source file is the file that contains the code at where GDB
+stops.  There could be multiple source files during a debugging
+session, we get the most recently showed one.  If program hasn't
+start running yet, the source file is the \"main file\" at where
+the GDB session starts (see `gdb-main-file')."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is GDB-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a GDB function buffer.
+
+Function buffers are locals buffer, registers buffer, etc, but
+not including main command buffer (the one in where you type GDB
+commands) or source buffers (that displays program source code)."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the type of BUFFER if it is a function buffer.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+These symbols are used by `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a GDB function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4659,6 +4847,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4896,24 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..229400966a 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,19 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       ,@body
+       (when ,window-dedicated-sym
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates
+         ;; non-nil and t, so we cannot simply set to t.
+         (set-window-dedicated-p nil ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 4 bytes --]






^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-07 18:09                             ` Yuan Fu
@ 2020-03-07 19:07                               ` Štěpán Němec
  2020-03-07 19:17                                 ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-07 19:07 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

On Sat, 7 Mar 2020 13:09:53 -0500
Yuan Fu wrote:

[...]

> diff --git a/lisp/window.el b/lisp/window.el
> index bd825c09e1..229400966a 100644
> --- a/lisp/window.el
> +++ b/lisp/window.el
> @@ -278,6 +278,19 @@ with-displayed-buffer-window
>  	     (funcall ,vquit-function ,window ,value)
>  	   ,value)))))
>  
> +(defmacro with-selected-window-undedicated (&rest body)
> +  "Run BODY in the selected window temporarily undedicated."
> +  (let ((window-dedicated-sym (gensym)))
> +    `(let ((,window-dedicated-sym (window-dedicated-p)))
> +       (when ,window-dedicated-sym
> +         (set-window-dedicated-p nil nil))
> +       ,@body
> +       (when ,window-dedicated-sym
> +         ;; `window-dedicated-p' returns the value set by
> +         ;; `set-window-dedicated-p', which differentiates
> +         ;; non-nil and t, so we cannot simply set to t.
> +         (set-window-dedicated-p nil ,window-dedicated-sym)))))
> +
>  ;; The following two functions are like `window-next-sibling' and
>  ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
>  ;; they don't substitute the selected window for nil), and they return

I'm sorry, I only skimmed through your patch, but shouldn't this use
'unwind-protect'? Otherwise the "temporarily" won't hold in case of
abnormal exit from BODY, unless I'm missing something.

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-07 19:07                               ` Štěpán Němec
@ 2020-03-07 19:17                                 ` Yuan Fu
  2020-03-09  9:01                                   ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-07 19:17 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 1592 bytes --]



> On Mar 7, 2020, at 2:07 PM, Štěpán Němec <stepnem@gmail.com> wrote:
> 
> On Sat, 7 Mar 2020 13:09:53 -0500
> Yuan Fu wrote:
> 
> [...]
> 
>> diff --git a/lisp/window.el b/lisp/window.el
>> index bd825c09e1..229400966a 100644
>> --- a/lisp/window.el
>> +++ b/lisp/window.el
>> @@ -278,6 +278,19 @@ with-displayed-buffer-window
>> 	     (funcall ,vquit-function ,window ,value)
>> 	   ,value)))))
>> 
>> +(defmacro with-selected-window-undedicated (&rest body)
>> +  "Run BODY in the selected window temporarily undedicated."
>> +  (let ((window-dedicated-sym (gensym)))
>> +    `(let ((,window-dedicated-sym (window-dedicated-p)))
>> +       (when ,window-dedicated-sym
>> +         (set-window-dedicated-p nil nil))
>> +       ,@body
>> +       (when ,window-dedicated-sym
>> +         ;; `window-dedicated-p' returns the value set by
>> +         ;; `set-window-dedicated-p', which differentiates
>> +         ;; non-nil and t, so we cannot simply set to t.
>> +         (set-window-dedicated-p nil ,window-dedicated-sym)))))
>> +
>> ;; The following two functions are like `window-next-sibling' and
>> ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
>> ;; they don't substitute the selected window for nil), and they return
> 
> I'm sorry, I only skimmed through your patch, but shouldn't this use
> 'unwind-protect'? Otherwise the "temporarily" won't hold in case of
> abnormal exit from BODY, unless I'm missing something.
> 
> — 
> Štěpán

Thanks for spotting that. I added the unwind-protext form.

Yuan


[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 18622 bytes --]

From 1c628f05b11de93102fc7972f6c5ecd396e6cebf Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add window streo/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'.  Set a default window configuration by
setting 'gdb-default-window-layout-file'.  Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
Make gdb preserve the window configuration that the user had before
starting gdb.  In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu. Add "Restore window layout" button to menu.
(gdb-toggle-restore-window-layout): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-selected-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 272 ++++++++++++++++++++++++++++++++++-----
 lisp/window.el           |  14 ++
 2 files changed, 252 insertions(+), 34 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..206d81ec6a 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -92,6 +92,8 @@
 (require 'json)
 (require 'bindat)
 (require 'cl-lib)
+(require 'cl-seq)
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +255,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +624,22 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, GDB loads this window layout file on startup.
+This should be the path to the window layout file.  If the path
+is not an absolute path, GDB treats it as a relative path and
+looks under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +798,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-layout-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4534,14 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
+  (define-key menu [restore-layout-when-finish]
+    '(menu-item "Restore layout after quit"
+                gdb-toggle-restore-window-layout
+                . (:button (:toggle . gdb-restore-window-layout-after-quit)
+                           :help "Toggle between always restore the window layout after GDB quits and never restore.
+You can also change this setting in Customize to conditionally restore.")))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4606,41 +4657,178 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-layout ()
+  "Toggle whether to restore window layout when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-layout-after-quit
+        (not gdb-restore-window-layout-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil if we can't find one.
+
+The source file is the file that contains the code at where GDB
+stops.  There could be multiple source files during a debugging
+session, we get the most recently showed one.  If program hasn't
+start running yet, the source file is the \"main file\" at where
+the GDB session starts (see `gdb-main-file')."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is GDB-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a GDB function buffer.
+
+Function buffers are locals buffer, registers buffer, etc, but
+not including main command buffer (the one in where you type GDB
+commands) or source buffers (that displays program source code)."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the type of BUFFER if it is a function buffer.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+These symbols are used by `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a GDB function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-selected-window-undedicated
+               (set-window-buffer nil placeholder)
+               (set-window-prev-buffers (selected-window) nil)
+               (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-selected-window-undedicated
+                      (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4659,6 +4847,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4896,24 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..9ddde7d1c1 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,20 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-selected-window-undedicated (&rest body)
+  "Run BODY in the selected window temporarily undedicated."
+  (let ((window-dedicated-sym (gensym)))
+    `(let ((,window-dedicated-sym (window-dedicated-p)))
+       (when ,window-dedicated-sym
+         (set-window-dedicated-p nil nil))
+       (unwind-protect
+           (progn ,@body)
+         (when ,window-dedicated-sym
+           ;; `window-dedicated-p' returns the value set by
+           ;; `set-window-dedicated-p', which differentiates
+           ;; non-nil and t, so we cannot simply set to t.
+           (set-window-dedicated-p nil ,window-dedicated-sym))))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 4 bytes --]






^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-07 19:17                                 ` Yuan Fu
@ 2020-03-09  9:01                                   ` martin rudalics
  2020-03-09 17:59                                     ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-09  9:01 UTC (permalink / raw)
  To: Yuan Fu, Štěpán Němec; +Cc: 39181

 >> I'm sorry, I only skimmed through your patch, but shouldn't this use
 >> 'unwind-protect'? Otherwise the "temporarily" won't hold in case of
 >> abnormal exit from BODY, unless I'm missing something.
 >>
 >> —
 >> Štěpán
 >
 > Thanks for spotting that. I added the unwind-protext form.

If we want to be more strict about this macro then how about the
following forms:

(with-selected-window-undedicated
  (set-window-dedicated-p nil t))

will leave the selected window dedicated which does not really violate
the contract of the macro but is unexpected at least.

The following is more serious: Suppose a user has a >= 2 windows layout
and does

(set-window-dedicated-p nil t)
(with-selected-window-undedicated
  (other-window 1))

which will have the macro make some other window dedicated and the
initially selected window undedicated.  A similar thing happens with

(set-window-dedicated-p nil t)
(with-selected-window-undedicated
  (delete-window))

The macro should be named 'with-window-undedicated', take a WINDOW (nil
for the selected one) as first argument and BODY as second.  It should
restore the dedicated status of WINDOW to what it was before running
BODY and leave the dedicated status of all other windows alone.  IMHO.

martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09  9:01                                   ` martin rudalics
@ 2020-03-09 17:59                                     ` Yuan Fu
  2020-03-09 19:18                                       ` Štěpán Němec
  2020-03-10  8:48                                       ` martin rudalics
  0 siblings, 2 replies; 48+ messages in thread
From: Yuan Fu @ 2020-03-09 17:59 UTC (permalink / raw)
  To: martin rudalics; +Cc: Štěpán Němec, 39181

[-- Attachment #1: Type: text/plain, Size: 1554 bytes --]



> On Mar 9, 2020, at 5:01 AM, martin rudalics <rudalics@gmx.at> wrote:
> 
> >> I'm sorry, I only skimmed through your patch, but shouldn't this use
> >> 'unwind-protect'? Otherwise the "temporarily" won't hold in case of
> >> abnormal exit from BODY, unless I'm missing something.
> >>
> >> —
> >> Štěpán
> >
> > Thanks for spotting that. I added the unwind-protext form.
> 
> If we want to be more strict about this macro then how about the
> following forms:
> 
> (with-selected-window-undedicated
> (set-window-dedicated-p nil t))
> 
> will leave the selected window dedicated which does not really violate
> the contract of the macro but is unexpected at least.
> 
> The following is more serious: Suppose a user has a >= 2 windows layout
> and does
> 
> (set-window-dedicated-p nil t)
> (with-selected-window-undedicated
> (other-window 1))
> 
> which will have the macro make some other window dedicated and the
> initially selected window undedicated.  A similar thing happens with
> 
> (set-window-dedicated-p nil t)
> (with-selected-window-undedicated
> (delete-window))
> 
> The macro should be named 'with-window-undedicated', take a WINDOW (nil
> for the selected one) as first argument and BODY as second.  It should
> restore the dedicated status of WINDOW to what it was before running
> BODY and leave the dedicated status of all other windows alone.  IMHO.
> 
> Martin
> 

I updated the patch accordingly. Could you have a look at the docsting? I had a hard time writing it.

Yuan


[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 19354 bytes --]

From c27f39a3e321a7a1111f71dd95573104f025c89c Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add window streo/restore feature for gdb-mi
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

Add a feature that allows a user to save a gdb window layout to a file
with 'gdb-save-window-layout' and load it back it with
'gdb-load-window-layout'.  Set a default window configuration by
setting 'gdb-default-window-layout-file'.  Note that for the default
window configuration to take effect, 'gdb-many-windows' needs to be t.
Make gdb preserve the window configuration that the user had before
starting gdb.  In window.el, add 'with-selected-window-undedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase', wrap 'pcase' and 'cl-lib'
inside ‘eval-when-compile’.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-layout-after-quit): New custom variable.
(gdb): Save configuration on startup.
(gdb-reset): Restore window configuration after quit.
(gdb-window-layout-directory, gdb-default-window-layout-file): New
variables.
(gud-menu-map): Add "Load window layout" and "Save window layout" to
menu. Add "Restore window layout" button to menu.
(gdb-toggle-restore-window-layout): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
layout when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-layout, gdb-load-window-layout): New functions.
(gdb-many-windows, gdb-get-source-file): Add comments.
* lisp/window.el (with-window-undedicated): New function.
---
 lisp/progmodes/gdb-mi.el | 272 ++++++++++++++++++++++++++++++++++-----
 lisp/window.el           |  29 +++++
 2 files changed, 267 insertions(+), 34 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..dd0ec304a3 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -92,6 +92,8 @@
 (require 'json)
 (require 'bindat)
 (require 'cl-lib)
+(require 'cl-seq)
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +255,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-layout-after-quit nil
+  "Specify whether to restore the window layout the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    'if-show-main -- Restore only if `gdb-show-main' is non-nil
+    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +624,22 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-layout-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-layout-file nil
+  "If non-nil, GDB loads this window layout file on startup.
+This should be the path to the window layout file.  If the path
+is not an absolute path, GDB treats it as a relative path and
+looks under `gdb-window-layout-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +798,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-layout-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4534,14 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [load-layout] '("Load layout" "Load GDB window layout from a file" . gdb-load-window-layout))
+  (define-key menu [save-layout] '("Save layout" "Save current GDB window layout to a file" . gdb-save-window-layout))
+  (define-key menu [restore-layout-when-finish]
+    '(menu-item "Restore layout after quit"
+                gdb-toggle-restore-window-layout
+                . (:button (:toggle . gdb-restore-window-layout-after-quit)
+                           :help "Toggle between always restore the window layout after GDB quits and never restore.
+You can also change this setting in Customize to conditionally restore.")))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4606,41 +4657,178 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-layout ()
+  "Toggle whether to restore window layout when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-layout-after-quit
+        (not gdb-restore-window-layout-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil if we can't find one.
+
+The source file is the file that contains the code at where GDB
+stops.  There could be multiple source files during a debugging
+session, we get the most recently showed one.  If program hasn't
+start running yet, the source file is the \"main file\" at where
+the GDB session starts (see `gdb-main-file')."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-layout-file
+      (gdb-load-window-layout
+       (if (file-name-absolute-p gdb-default-window-layout-file)
+           gdb-default-window-layout-file
+         (expand-file-name gdb-default-window-layout-file
+                           gdb-window-layout-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer
+       win2
+       (or (gdb-get-source-buffer)
+           (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is GDB-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a GDB function buffer.
+
+Function buffers are locals buffer, registers buffer, etc, but
+not including main command buffer (the one in where you type GDB
+commands) or source buffers (that displays program source code)."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the type of BUFFER if it is a function buffer.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+These symbols are used by `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a GDB function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-layout (file)
+  "Save current window layout (window configuration) to FILE.
+
+You can later restore this layout from that file by
+`gdb-load-window-layout'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-window-undedicated nil
+                (set-window-buffer nil placeholder)
+                (set-window-prev-buffers (selected-window) nil)
+                (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-layout (file)
+  "Restore window layout (window configuration) from FILE.
+
+FILE should be a window layout file saved by
+`gdb-save-window-layout'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-layout-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min even we
+                               ;; are reading the whole buffer.
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (if gdb-source-window
+                                 (window-buffer gdb-source-window)
+                               (or (gdb-get-source-buffer)
+                                   ;; Do the same thing as in
+                                   ;; `gdb-setup-windows' if no source
+                                   ;; buffer is found.
+                                   (list-buffers-noselect))))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-window-undedicated nil
+                       (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4659,6 +4847,9 @@ gdb-many-windows
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
 This arrangement depends on the value of option `gdb-many-windows'."
+  ;; This function is used when the user messed up window
+  ;; configuration and want to "reset to default".  The function that
+  ;; sets up window configuration on start up is `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4896,24 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-layout-after-quit t)
+            (and (eq gdb-restore-window-layout-after-quit 'if-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-layout-after-quit 'if-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.  Maybe the user changed the configuration
+      ;; after starting GDB, who knows.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..709e30a462 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,35 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-window-undedicated (window &rest body)
+  "Select WINDOW, set it to be undedicated and execute BODY.
+
+WINDOW is only set to be undedicated temporarily while executing
+BODY.  That is, the original value of WINDOW's dedication is
+restored after executing BODY.  If WINDOW is nil, use the
+selected window.  The value returned is the value of the last
+form in BODY.
+
+This macro uses `with-selected-window' to select WINDOW (which see)."
+  (declare (indent 1) (debug t))
+  (let ((window-dedicated-sym (gensym))
+        (window-sym (gensym)))
+    `(with-selected-window (or ,window (selected-window))
+       (let ((,window-dedicated-sym (window-dedicated-p))
+             (,window-sym (selected-window)))
+         (when ,window-dedicated-sym
+           (set-window-dedicated-p nil nil))
+         (unwind-protect
+             (progn ,@body)
+           ;; Even if the user does weird things like switching to
+           ;; another window in BODY, we are covered because we've
+           ;; saved the intended window in WINDOW-SYM.
+           (when ,window-dedicated-sym
+             ;; `window-dedicated-p' returns the value set by
+             ;; `set-window-dedicated-p', which differentiates
+             ;; non-nil and t, so we cannot simply set to t.
+             (set-window-dedicated-p ,window-sym ,window-dedicated-sym)))))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09 17:59                                     ` Yuan Fu
@ 2020-03-09 19:18                                       ` Štěpán Němec
  2020-03-09 20:17                                         ` Yuan Fu
  2020-03-10  8:48                                       ` martin rudalics
  1 sibling, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-09 19:18 UTC (permalink / raw)
  To: Yuan Fu, martin rudalics; +Cc: 39181


Again, having little experience with gdb(-mi) I have mostly checked the
doc strings; I hope Martin will provide better feedback.

Other than the nit picks below, I have one question: is there any
difference between "window layout" and "window configuration" in this
context? You seem to be using both interchangeably, both in
documentation and the function/variable names. There seems to be some
prior usage of "layout" in gdb-mi, but the general Emacs term AFAIK is
"configuration". Wouldn't it make sense to unify the usage somewhat, at
least in the new code? Just an observation (I found it confusing.)

On Mon, 09 Mar 2020 13:59:31 -0400
Yuan Fu wrote:

> From c27f39a3e321a7a1111f71dd95573104f025c89c Mon Sep 17 00:00:00 2001
> From: Yuan Fu <casouri@gmail.com>
> Date: Tue, 3 Mar 2020 18:30:03 -0500
> Subject: [PATCH] Add window streo/restore feature for gdb-mi
> MIME-Version: 1.0
> Content-Type: text/plain; charset=UTF-8
> Content-Transfer-Encoding: 8bit
>
> Add a feature that allows a user to save a gdb window layout to a file
> with 'gdb-save-window-layout' and load it back it with
                                                 ^^
Typo?

[...]

> +(defcustom gdb-restore-window-layout-after-quit nil
> +  "Specify whether to restore the window layout the user had before gdb starts.
> +
> +Possible values are:
> +    t -- Always restore.
> +    nil -- Don't restore.
> +    'if-show-main -- Restore only if `gdb-show-main' is non-nil
> +    'if-many-windows -- Restore only if variable `gdb-many-windows' is non-nil."
       ^^^^^^^^^^^^^^^^
The documented symbols don't match the actual ones below. Also, if you
want to quote them in the doc string, the convention (which you do
follow elsewhere) is `like-this', 'this is confusing.

> +  :type '(choice
> +          (const :tag "Always restore" t)
> +          (const :tag "Don't restore" nil)
> +          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
> +          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
                                                          ^^^^^^^^^^^^^^^^^^^
[...]

> +(defun gdb-toggle-restore-window-layout ()
> +  "Toggle whether to restore window layout when GDB quit."
                                                       ^^^^
                                                       quits
> +  (interactive)
> +  (setq gdb-restore-window-layout-after-quit
> +        (not gdb-restore-window-layout-after-quit)))
> +
> +(defun gdb-get-source-buffer ()
> +  "Return a buffer displaying source file or nil if we can't find one.
> +
> +The source file is the file that contains the code at where GDB
                                                      ^^^^^^^^
Just "where", or perhaps "source location where"?

> +stops.  There could be multiple source files during a debugging
> +session, we get the most recently showed one.  If program hasn't
> +start running yet, the source file is the \"main file\" at where
                                                           ^^^^^^^^
Same as above.

> +the GDB session starts (see `gdb-main-file')."

[...]

> +(defun gdb-function-buffer-p (buffer)
> +  "Return t if BUFFER is a GDB function buffer.
> +
> +Function buffers are locals buffer, registers buffer, etc, but
> +not including main command buffer (the one in where you type GDB
                                              ^^^^^^^^
Again, just "where".

> +commands) or source buffers (that displays program source code)."
                                            ^
"display"

[...]

> +(defun gdb-load-window-layout (file)
> +  "Restore window layout (window configuration) from FILE.
> +
> +FILE should be a window layout file saved by
> +`gdb-save-window-layout'."
> +  (interactive (list (read-file-name
> +                      "Restore window configuration from file: "
> +                      (or gdb-window-layout-directory default-directory))))
> +  ;; Basically, we restore window configuration and go through each
> +  ;; window and restore the function buffers.
> +  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
> +    (unwind-protect ; Don't leak buffer.
> +        (let ((window-config (with-temp-buffer
> +                               (insert-file-contents file)
> +                               ;; We need to go to point-min even we
> +                               ;; are reading the whole buffer.

I can't understand this comment. Maybe "even" should have been "so that"?

> +                               (goto-char (point-min))
> +                               (read (current-buffer))))

[...]

> @@ -4659,6 +4847,9 @@ gdb-many-windows
>  (defun gdb-restore-windows ()
>    "Restore the basic arrangement of windows used by gdb.
>  This arrangement depends on the value of option `gdb-many-windows'."
> +  ;; This function is used when the user messed up window
> +  ;; configuration and want to "reset to default".  The function that
                          ^^^^
"wants"

[...]

> +(defmacro with-window-undedicated (window &rest body)
> +  "Select WINDOW, set it to be undedicated and execute BODY.
> +
> +WINDOW is only set to be undedicated temporarily while executing
> +BODY.  That is, the original value of WINDOW's dedication is
> +restored after executing BODY.  If WINDOW is nil, use the
> +selected window.  The value returned is the value of the last
> +form in BODY.

The "temporarily" thing is actually expected/understood with with-
macros, so I think it could be simplified to something like the
following (BTW, while there are occurences or "non-dedicated" in Emacs
sources, there are no occurences of "undedicated". Another thing to
maybe consider for the sake of consistency/least surprise.):

"Evaluate BODY with WINDOW selected and temporarily made non-dedicated.

If WINDOW is nil, use the selected window.  Return the value of the last
form in BODY."

Thank you,

  Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09 19:18                                       ` Štěpán Němec
@ 2020-03-09 20:17                                         ` Yuan Fu
  2020-03-09 20:54                                           ` Štěpán Němec
  2020-03-10  8:49                                           ` martin rudalics
  0 siblings, 2 replies; 48+ messages in thread
From: Yuan Fu @ 2020-03-09 20:17 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 905 bytes --]


> On Mar 9, 2020, at 3:18 PM, Štěpán Němec <stepnem@gmail.com> wrote:
> 
> 
> Other than the nit picks below, I have one question: is there any
> difference between "window layout" and "window configuration" in this
> context? You seem to be using both interchangeably, both in
> documentation and the function/variable names. There seems to be some
> prior usage of "layout" in gdb-mi, but the general Emacs term AFAIK is
> "configuration". Wouldn't it make sense to unify the usage somewhat, at
> least in the new code? Just an observation (I found it confusing.)
> 

No, not really, that’s my fault. Before I go in and change every word, which one do you think I should use: configuration or layout? Configuration is the conventional Emacs term but I felt that layout is easier to understand for a user. If you are find with both I guess I will change all to configuration.

Yuan 

[-- Attachment #2: Type: text/html, Size: 7267 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09 20:17                                         ` Yuan Fu
@ 2020-03-09 20:54                                           ` Štěpán Němec
  2020-03-10  8:49                                           ` martin rudalics
  1 sibling, 0 replies; 48+ messages in thread
From: Štěpán Němec @ 2020-03-09 20:54 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

On Mon, 09 Mar 2020 16:17:59 -0400
Yuan Fu wrote:

>> On Mar 9, 2020, at 3:18 PM, Štěpán Němec <stepnem@gmail.com> wrote:
>> 
>> 
>> Other than the nit picks below, I have one question: is there any
>> difference between "window layout" and "window configuration" in this
>> context? You seem to be using both interchangeably, both in
>> documentation and the function/variable names. There seems to be some
>> prior usage of "layout" in gdb-mi, but the general Emacs term AFAIK is
>> "configuration". Wouldn't it make sense to unify the usage somewhat, at
>> least in the new code? Just an observation (I found it confusing.)
>> 
>
> No, not really, that’s my fault. Before I go in and change every word,
> which one do you think I should use: configuration or layout?
> Configuration is the conventional Emacs term but I felt that layout is
> easier to understand for a user. If you are find with both I guess I
> will change all to configuration.

If they mean the same thing, then I'd say "configuration" is definitely
the way to go.

It is true that some Emacs terms might be unfamiliar to new users, but
thanks to the excellent documentation capabilities it's very easy to
learn the respective definitions (e.g., in this case 'C-h r i
configuration' will display the relevant manual entries). It is
important to stay consistent.

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09 17:59                                     ` Yuan Fu
  2020-03-09 19:18                                       ` Štěpán Němec
@ 2020-03-10  8:48                                       ` martin rudalics
  1 sibling, 0 replies; 48+ messages in thread
From: martin rudalics @ 2020-03-10  8:48 UTC (permalink / raw)
  To: Yuan Fu; +Cc: Štěpán Němec, 39181

 > I updated the patch accordingly. Could you have a look at the docsting? I had a hard time writing it.

Please don't select WINDOW in 'with-window-undedicated'.  In general,
try to avoid selecting a window unless it is really needed.  This is
particularly important when WINDOW can be on another frame where
selecting WINDOW entails switching to that frame with all its overhead.

I would use something like the untested below.  This could be then
useful for 'ffap-other-frame' or 'ffap-dired-other-frame' as well.

martin


(defmacro with-window-undedicated (window &rest body)
   "Execute BODY with WINDOW temporarily undedicated.
WINDOW must be a live window and defaults to the selected one."
   (declare (indent 1) (debug t))
   (let ((window-dedicated-sym (gensym))
         (window-sym (gensym)))
     `(let* ((,window-sym (window-normalize-window window t))
	    (,window-dedicated-sym (window-dedicated-p ,window-sym)))
        (set-window-dedicated-p ,window-sym nil)
        (unwind-protect
            (progn ,@body)
	 (set-window-dedicated-p ,window-sym ,window-dedicated-sym)))))





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-09 20:17                                         ` Yuan Fu
  2020-03-09 20:54                                           ` Štěpán Němec
@ 2020-03-10  8:49                                           ` martin rudalics
  2020-03-10 18:05                                             ` Fu Yuan
  1 sibling, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-10  8:49 UTC (permalink / raw)
  To: Yuan Fu, Štěpán Němec; +Cc: 39181

 > No, not really, that’s my fault. Before I go in and change every word,
 > which one do you think I should use: configuration or layout?
 > Configuration is the conventional Emacs term but I felt that layout is
 > easier to understand for a user. If you are find with both I guess I
 > will change all to configuration.

While I agree with Štěpán that we should use a unified nomenclature
wherever possible, most GDB users will probably prefer "layout" here.

martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-10  8:49                                           ` martin rudalics
@ 2020-03-10 18:05                                             ` Fu Yuan
  2020-03-11  8:52                                               ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Fu Yuan @ 2020-03-10 18:05 UTC (permalink / raw)
  To: martin rudalics; +Cc: Štěpán Němec, 39181

Hmmm, so what is the best approach here? Use layout in button names (in the menu bar) and configuration everywhere else? And maybe mention the equivalence between layout and configuration in help echo?

Yuan

> 在 2020年3月10日,上午4:49,martin rudalics <rudalics@gmx.at> 写道:
> 
> > No, not really, that’s my fault. Before I go in and change every word,
> > which one do you think I should use: configuration or layout?
> > Configuration is the conventional Emacs term but I felt that layout is
> > easier to understand for a user. If you are find with both I guess I
> > will change all to configuration.
> 
> While I agree with Štěpán that we should use a unified nomenclature
> wherever possible, most GDB users will probably prefer "layout" here.
> 
> martin
> 





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-10 18:05                                             ` Fu Yuan
@ 2020-03-11  8:52                                               ` martin rudalics
  2020-03-11  9:26                                                 ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-11  8:52 UTC (permalink / raw)
  To: Fu Yuan; +Cc: Štěpán Němec, 39181

 > Hmmm, so what is the best approach here? Use layout in button names
 > (in the menu bar) and configuration everywhere else?

I think so.  Štěpán?

 > And maybe mention
 > the equivalence between layout and configuration in help echo?

I'd mention it in section 27.6.5.1 GDB User Interface Layout of the
Emacs manual.

martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-11  8:52                                               ` martin rudalics
@ 2020-03-11  9:26                                                 ` Štěpán Němec
  2020-03-12  8:22                                                   ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-11  9:26 UTC (permalink / raw)
  To: martin rudalics, Fu Yuan; +Cc: 39181

On Wed, 11 Mar 2020 09:52:37 +0100
martin rudalics wrote:

>  > Hmmm, so what is the best approach here? Use layout in button names
>  > (in the menu bar) and configuration everywhere else?
>
> I think so.  Štěpán?

That certainly sounds better, thank you, although I'm still wondering if
the "layout" gdb-mi speaks of really corresponds exactly to the usual
window configuration (the object/data structure)? And if it does, it
still seems to me that it would be better to just use the Emacs term,
for the reasons I explained in my previous message, but as an only very
occasional GDB user I don't feel I should be pushing for such a change.

>  > And maybe mention
>  > the equivalence between layout and configuration in help echo?
>
> I'd mention it in section 27.6.5.1 GDB User Interface Layout of the
> Emacs manual.

FWIW, I found that section confusing for other reasons as well, e.g. it
seems to even mix "frame layout" and "window layout" in apparently the
same sense? I think fixing _that_ at least should be uncontroversial. :-)

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-11  9:26                                                 ` Štěpán Němec
@ 2020-03-12  8:22                                                   ` martin rudalics
  2020-03-12  8:49                                                     ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-12  8:22 UTC (permalink / raw)
  To: Štěpán Němec, Fu Yuan; +Cc: 39181

 > That certainly sounds better, thank you, although I'm still wondering if
 > the "layout" gdb-mi speaks of really corresponds exactly to the usual
 > window configuration (the object/data structure)? And if it does, it
 > still seems to me that it would be better to just use the Emacs term,
 > for the reasons I explained in my previous message, but as an only very
 > occasional GDB user I don't feel I should be pushing for such a change.

I would use the term "layout" for what is visible on screen and
"configuration" for the underlying data structure.

 >> I'd mention it in section 27.6.5.1 GDB User Interface Layout of the
 >> Emacs manual.
 >
 > FWIW, I found that section confusing for other reasons as well, e.g. it
 > seems to even mix "frame layout" and "window layout" in apparently the
 > same sense? I think fixing _that_ at least should be uncontroversial. :-)

Let alone the fact that "frame" in the context of GDB and Emacs has two
quite different meanings ...

martin





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-12  8:22                                                   ` martin rudalics
@ 2020-03-12  8:49                                                     ` Štěpán Němec
  2020-03-12 19:21                                                       ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-12  8:49 UTC (permalink / raw)
  To: martin rudalics, Fu Yuan; +Cc: 39181

On Thu, 12 Mar 2020 09:22:23 +0100
martin rudalics wrote:

>  > That certainly sounds better, thank you, although I'm still wondering if
>  > the "layout" gdb-mi speaks of really corresponds exactly to the usual
>  > window configuration (the object/data structure)? And if it does, it
>  > still seems to me that it would be better to just use the Emacs term,
>  > for the reasons I explained in my previous message, but as an only very
>  > occasional GDB user I don't feel I should be pushing for such a change.
>
> I would use the term "layout" for what is visible on screen and
> "configuration" for the underlying data structure.

Yes, I think I understood that, and would consider it an improvement.
All I was trying to reiterate is that _if_ the layout means exactly the
same as "window configuration", then I would prefer to call it just that
even in the UI, to avoid cognitive overhead (AKA head scratching) for
Emacs users used to that term, while not causing significant
inconvenience for those not familiar with the Emacs term (it's
consistently used and the documentation readily accessible). But of
course I defer to your decision.

>  >> I'd mention it in section 27.6.5.1 GDB User Interface Layout of the
>  >> Emacs manual.
>  >
>  > FWIW, I found that section confusing for other reasons as well, e.g. it
>  > seems to even mix "frame layout" and "window layout" in apparently the
>  > same sense? I think fixing _that_ at least should be uncontroversial. :-)
>
> Let alone the fact that "frame" in the context of GDB and Emacs has two
> quite different meanings ...

Yeah, I had double-checked the GDB docs and there is no evidence of
"frame" being used in any non-stack-related sense. The confusion
probably stems from the Info manual writer trying to use the Emacs
terms while not being quite familiar with them themselves.

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-12  8:49                                                     ` Štěpán Němec
@ 2020-03-12 19:21                                                       ` Yuan Fu
  2020-03-13 20:09                                                         ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-12 19:21 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

Then I’ll use “layout” in the menu bar buttons and “window configuration” in other places. A plus for “layout” in UI is that it’s shorter. 

Yuan




^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-12 19:21                                                       ` Yuan Fu
@ 2020-03-13 20:09                                                         ` Yuan Fu
       [not found]                                                           ` <87lfo4netg.fsf@gmail.com>
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-13 20:09 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 1369 bytes --]

Here’s the patch that addresses Štěpán and Martin’s review:

Apart from fixing all the mentioned issues, I also moved “Restore Window Layout” button from “GDB-MI” menu to “GDB-WINDOWS” menu, since it fits there better; I also mentioned that this button works for the new user-supplied default layout, too, if a user sets `gdb-default-window-configuration-file’. 

As for the layout vs configuration, I used “layout” in UI buttons, and used “window configuration (i.e. window layout)” in help echos. Hopefully the user will understand that we use “window configuration” internally but it’s (almost) the same as a window layout in gdb. This also plays well with existing UI that uses “layout”. The window layout that we save to a file in gdb is a window configuration structure with some small differences: simply loading that window configuration with `window-state-put' doesn’t give back your gdb layout. (because all the buffers (local, breakpoint, etc) are different from session to session.) `gdb-load-window-configuration` does some trick to recreate each buffers. Apart from that the layout and window configuration is the same thing.

Martin suggests to mention the layout vs window configuration in the manual, should I also document this new feature in the same section? (Let me learn texinfo first ;-)

Yuan


[-- Attachment #2.1: Type: text/html, Size: 1807 bytes --]

[-- Attachment #2.2: new-window.patch --]
[-- Type: application/octet-stream, Size: 20057 bytes --]

From 59590d8a8b21908cefc10787029a6db1ae5fdfef Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add store/restore window configuration feature for gdb-mi

Add a feature that allows a user to save a gdb window
configuration (window layout) to a file with
'gdb-save-window-configuration' and load it back with
'gdb-load-window-configuration'.  Set a default window configuration
by setting 'gdb-default-window-configuration-file'.  Note that for the
default window configuration to take effect, 'gdb-many-windows' needs
to be t.  Add an option to make gdb preserve the window configuration
that the user had before starting gdb.  In window.el, add
'with-selected-window-non-dedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase' and 'cl-seq'.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-configuration-after-quit): New custom variable.
(gdb-window-configuration-directory,
gdb-default-window-configuration-file): New variables.
(gdb): Save configuration on startup.
(gud-menu-map): Add "Load Layout" and "Save Layout" to menu. Add
"Restore Layout After Quit" button to menu.  Rename "Restore Window
Layout" to "Restore Default Layout", add some help echo, and move it
from "GDB-MI" menu to "GDB-WINDOWs" menu.
(gdb-toggle-restore-window-configuration): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
configuration when available.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-configuration, gdb-load-window-configuration): New
functions.
(gdb-restore-windows): Edit docstring to mention
'gdb-default-window-configuration-file' and add comments.
(gdb-reset): Restore window configuration after quit.
(gdb-get-source-file): Add comments.
* lisp/window.el (with-window-non-dedicated): New macro.
---
 lisp/progmodes/gdb-mi.el | 279 +++++++++++++++++++++++++++++++++------
 lisp/window.el           |  18 +++
 2 files changed, 259 insertions(+), 38 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..e1a2993b4e 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -92,6 +92,8 @@
 (require 'json)
 (require 'bindat)
 (require 'cl-lib)
+(require 'cl-seq)
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +255,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-configuration-after-quit nil
+  "Whether to restore the window configuration the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    `if-gdb-show-main' -- Restore only if variable `gdb-show-main' is non-nil
+    `if-gdb-many-windows' -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +624,26 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-configuration-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-configuration-file nil
+  "If non-nil, GDB loads this window configuration file on startup.
+
+This should be the path to the window configuration file.  If the path
+is not an absolute path, GDB treats it as a relative path and
+looks under `gdb-window-configuration-directory'.
+
+Note that this variable only takes effect when variable
+`gdb-many-windows' is t."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +802,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-configuration-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4538,15 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [gdb-restore-windows]
+    '(menu-item "Restore Default Layout" gdb-restore-windows
+      :help "Restore standard layout for debug session. I.e., the layout we get when session starts."))
+  (define-key menu [load-layout] '("Load Layout" "Load GDB window configuration (i.e. window layout) from a file" . gdb-load-window-configuration))
+  (define-key menu [save-layout] '("Save Layout" "Save current GDB window configuration (i.e. window layout) to a file" . gdb-save-window-configuration))
+  (define-key menu [restore-layout-after-quit]
+    '(menu-item "Restore Layout After Quit" gdb-toggle-restore-window-configuration
+       :button (:toggle . gdb-restore-window-configuration-after-quit)
+       :help "Toggle between always restore the window configuration (i.e. window layout) after GDB quits and never restore.\n You can also change this setting in Customize to conditionally restore."))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4529,9 +4585,6 @@ gdb-preempt-existing-or-display-buffer
     '(menu-item "Display Other Windows" gdb-many-windows
       :help "Toggle display of locals, stack and breakpoint information"
       :button (:toggle . gdb-many-windows)))
-  (define-key menu [gdb-restore-windows]
-    '(menu-item "Restore Window Layout" gdb-restore-windows
-      :help "Restore standard layout for debug session."))
   (define-key menu [sep1]
     '(menu-item "--"))
   (define-key menu [all-threads]
@@ -4606,41 +4659,174 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-configuration ()
+  "Toggle whether to restore window configuration when GDB quit."
+  (interactive)
+  (setq gdb-restore-window-configuration-after-quit
+        (not gdb-restore-window-configuration-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil if we can't find one.
+
+The source file is the file that contains the source location
+where GDB stops.  There could be multiple source files during a
+debugging session, we get the most recently showed one.  If
+program hasn't start running yet, the source file is the \"main
+file\" where the GDB session starts (see `gdb-main-file')."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
   "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  (if gdb-default-window-configuration-file
+      (gdb-load-window-configuration
+       (if (file-name-absolute-p gdb-default-window-configuration-file)
+           gdb-default-window-configuration-file
+         (expand-file-name gdb-default-window-configuration-file
+                           gdb-window-configuration-directory)))
+    ;; Create default layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer win2 (or (gdb-get-source-buffer)
+                                  (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is GDB-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a GDB function buffer.
+
+Function buffers are locals buffer, registers buffer, etc, but
+not including main command buffer (the one where you type GDB
+commands) or source buffers (that display program source code)."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the type of BUFFER if it is a function buffer.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+These symbols are used by `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a GDB function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-configuration (file)
+  "Save current window configuration (i.e. window layout) to FILE.
+
+You can later restore this configuration from that file by
+`gdb-load-window-configuration'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-configuration-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-window-non-dedicated nil
+                (set-window-buffer nil placeholder)
+                (set-window-prev-buffers (selected-window) nil)
+                (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-configuration (file)
+  "Restore window configuration (i.e. window layout) from FILE.
+
+FILE should be a window configuration file saved by
+`gdb-save-window-configuration'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-configuration-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min because
+                               ;; `read' reads from point
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (or (gdb-get-source-buffer)
+                                 ;; Do the same thing as in
+                                 ;; `gdb-setup-windows' if no source
+                                 ;; buffer is found.
+                                 (list-buffers-noselect)))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-window-non-dedicated nil
+                       (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4658,7 +4844,12 @@ gdb-many-windows
 
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
-This arrangement depends on the value of option `gdb-many-windows'."
+This arrangement depends on the value of option
+`gdb-many-windows' and `gdb-default-window-configuration-file'."
+  ;; This function is used when the user messed up window
+  ;; configuration and wants to "reset to default".  The function that
+  ;; sets up window configuration on start up is
+  ;; `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4896,23 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-configuration-after-quit t)
+            (and (eq gdb-restore-window-configuration-after-quit 'if-gdb-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-configuration-after-quit 'if-gdb-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..d774d906cd 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,24 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-window-non-dedicated (window &rest body)
+  "Evaluate BODY with WINDOW selected and temporarily made non-dedicated.
+
+If WINDOW is nil, use the selected window.  Return the value of the last form in BODY."
+  (declare (indent 1) (debug t))
+  (let ((window-dedicated-sym (gensym))
+        (window-sym (gensym)))
+    `(let* ((,window-sym (window-normalize-window ,window t))
+            (,window-dedicated-sym (window-dedicated-p ,window-sym)))
+       (set-window-dedicated-p ,window-sym nil)
+       (unwind-protect
+           (progn ,@body)
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates non-nil and
+         ;; t, so we cannot simply use t here. That's why we use
+         ;; `window-dedicated-sym'.
+         (set-window-dedicated-p ,window-sym ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #2.3: Type: text/html, Size: 151 bytes --]

^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
       [not found]                                                           ` <87lfo4netg.fsf@gmail.com>
@ 2020-03-13 21:13                                                             ` Štěpán Němec
  2020-03-13 21:40                                                               ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-13 21:13 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

[resending to Cc: Martin and the bug list, sorry]

On Fri, 13 Mar 2020 16:09:06 -0400
Yuan Fu wrote:

> Apart from fixing all the mentioned issues,

Almost... :-)

> The window layout that we save to a file in gdb is a window
> configuration structure with some small differences: simply loading
> that window configuration with `window-state-put' doesn’t give back
> your gdb layout. (because all the buffers (local, breakpoint, etc) are
> different from session to session.) `gdb-load-window-configuration`
> does some trick to recreate each buffers. Apart from that the layout
> and window configuration is the same thing.

Thank you for the clarification! It would be nice to have that in the
documentation as well.

> @@ -253,6 +255,25 @@ gdb-output-sink
>                disposition of output generated by commands that
>                gdb mode sends to gdb on its own behalf.")
> 
> +(defvar gdb--window-configuration-before nil
> +  "Stores the window configuration before starting gdb.")
> +
> +(defcustom gdb-restore-window-configuration-after-quit nil
> +  "Whether to restore the window configuration the user had before gdb starts.

Probably better stick to the standard "If non-nil, restore...".

> @@ -4491,6 +4538,15 @@ gdb-preempt-existing-or-display-buffer
>    (define-key gud-menu-map [displays]
>      `(menu-item "GDB-Windows" ,menu
>                 :visible (eq gud-minor-mode 'gdbmi)))
> +  (define-key menu [gdb-restore-windows]
> +    '(menu-item "Restore Default Layout" gdb-restore-windows
> +      :help "Restore standard layout for debug session. I.e., the layout we get when session starts."))

How about just "Restore standard layout for debug session (the layout we
get when session starts)." Or, even more concisely, "Restore the initial
GDB window layout"?

> +  (define-key menu [load-layout] '("Load Layout" "Load GDB window configuration (i.e. window layout) from a file" . gdb-load-window-configuration))

You can omit the "i.e.". Actually, even better perhaps just "window
configuration (layout)"? Similarly below.

> @@ -4606,41 +4659,174 @@ gdb-set-window-buffer
>    (set-window-buffer window (get-buffer name))
>    (set-window-dedicated-p window t))
> 
> +(defun gdb-toggle-restore-window-configuration ()
> +  "Toggle whether to restore window configuration when GDB quit."
                                                               ^^^^
quits

> +  (interactive)
> +  (setq gdb-restore-window-configuration-after-quit
> +        (not gdb-restore-window-configuration-after-quit)))
> +
> +(defun gdb-get-source-buffer ()
> +  "Return a buffer displaying source file or nil if we can't find one.
> +
> +The source file is the file that contains the source location
> +where GDB stops.  There could be multiple source files during a
> +debugging session, we get the most recently showed one.  If
> +program hasn't start running yet, the source file is the \"main
                   ^^^^^
started

> +file\" where the GDB session starts (see `gdb-main-file')."
> +  (if gud-last-last-frame
> +      (gud-find-file (car gud-last-last-frame))
> +    (when gdb-main-file
> +      (gud-find-file gdb-main-file))))
> +
>  (defun gdb-setup-windows ()
>    "Layout the window pattern for option `gdb-many-windows'."
       ^^^^^^
"Lay out" (verb)

> @@ -4658,7 +4844,12 @@ gdb-many-windows
> 
>  (defun gdb-restore-windows ()
>    "Restore the basic arrangement of windows used by gdb.
> -This arrangement depends on the value of option `gdb-many-windows'."
> +This arrangement depends on the value of option
> +`gdb-many-windows' and `gdb-default-window-configuration-file'."

How about "This arrangement depends on the values of `gdb-many-windows'
and `gdb-default-window-configuration-file'."

> diff --git a/lisp/window.el b/lisp/window.el
> index bd825c09e1..d774d906cd 100644
> --- a/lisp/window.el
> +++ b/lisp/window.el
> @@ -278,6 +278,24 @@ with-displayed-buffer-window
>              (funcall ,vquit-function ,window ,value)
>            ,value)))))
> 
> +(defmacro with-window-non-dedicated (window &rest body)
> +  "Evaluate BODY with WINDOW selected and temporarily made non-dedicated.
                                 ^^^^^^^^^^^^
This is now no longer true.

Thank you,

  Štěpán

P.S.: This time your patch somehow ended up hidden as
application/ocet-stream after the HTML part, instead of inline in plain
text.





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-13 21:13                                                             ` Štěpán Němec
@ 2020-03-13 21:40                                                               ` Yuan Fu
  2020-03-13 22:12                                                                 ` Štěpán Němec
  2020-03-15 15:55                                                                 ` martin rudalics
  0 siblings, 2 replies; 48+ messages in thread
From: Yuan Fu @ 2020-03-13 21:40 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 5045 bytes --]

> 
>> The window layout that we save to a file in gdb is a window
>> configuration structure with some small differences: simply loading
>> that window configuration with `window-state-put' doesn’t give back
>> your gdb layout. (because all the buffers (local, breakpoint, etc) are
>> different from session to session.) `gdb-load-window-configuration`
>> does some trick to recreate each buffers. Apart from that the layout
>> and window configuration is the same thing.
> 
> Thank you for the clarification! It would be nice to have that in the
> documentation as well.

I added some comments in the menu-map code. Hope that’s helps.


>> @@ -253,6 +255,25 @@ gdb-output-sink
>>               disposition of output generated by commands that
>>               gdb mode sends to gdb on its own behalf.")
>> 
>> +(defvar gdb--window-configuration-before nil
>> +  "Stores the window configuration before starting gdb.")
>> +
>> +(defcustom gdb-restore-window-configuration-after-quit nil
>> +  "Whether to restore the window configuration the user had before gdb starts.
> 
> Probably better stick to the standard "If non-nil, restore...".
> 
>> @@ -4491,6 +4538,15 @@ gdb-preempt-existing-or-display-buffer
>>   (define-key gud-menu-map [displays]
>>     `(menu-item "GDB-Windows" ,menu
>>                :visible (eq gud-minor-mode 'gdbmi)))
>> +  (define-key menu [gdb-restore-windows]
>> +    '(menu-item "Restore Default Layout" gdb-restore-windows
>> +      :help "Restore standard layout for debug session. I.e., the layout we get when session starts."))
> 
> How about just "Restore standard layout for debug session (the layout we
> get when session starts)." Or, even more concisely, "Restore the initial
> GDB window layout"?
> 
>> +  (define-key menu [load-layout] '("Load Layout" "Load GDB window configuration (i.e. window layout) from a file" . gdb-load-window-configuration))
> 
> You can omit the "i.e.". Actually, even better perhaps just "window
> configuration (layout)"? Similarly below.
> 
>> @@ -4606,41 +4659,174 @@ gdb-set-window-buffer
>>   (set-window-buffer window (get-buffer name))
>>   (set-window-dedicated-p window t))
>> 
>> +(defun gdb-toggle-restore-window-configuration ()
>> +  "Toggle whether to restore window configuration when GDB quit."
>                                                               ^^^^
> quits
> 
>> +  (interactive)
>> +  (setq gdb-restore-window-configuration-after-quit
>> +        (not gdb-restore-window-configuration-after-quit)))
>> +
>> +(defun gdb-get-source-buffer ()
>> +  "Return a buffer displaying source file or nil if we can't find one.
>> +
>> +The source file is the file that contains the source location
>> +where GDB stops.  There could be multiple source files during a
>> +debugging session, we get the most recently showed one.  If
>> +program hasn't start running yet, the source file is the \"main
>                   ^^^^^
> started
> 
>> +file\" where the GDB session starts (see `gdb-main-file')."
>> +  (if gud-last-last-frame
>> +      (gud-find-file (car gud-last-last-frame))
>> +    (when gdb-main-file
>> +      (gud-find-file gdb-main-file))))
>> +
>> (defun gdb-setup-windows ()
>>   "Layout the window pattern for option `gdb-many-windows'."
>       ^^^^^^
> "Lay out" (verb)

All fixed.

> 
>> @@ -4658,7 +4844,12 @@ gdb-many-windows
>> 
>> (defun gdb-restore-windows ()
>>   "Restore the basic arrangement of windows used by gdb.
>> -This arrangement depends on the value of option `gdb-many-windows'."
>> +This arrangement depends on the value of option
>> +`gdb-many-windows' and `gdb-default-window-configuration-file'."
> 
> How about "This arrangement depends on the values of `gdb-many-windows'
> and `gdb-default-window-configuration-file'."
> 

I changed “option” to “variable”, I have to keep “variable” otherwise the byte compiler complains about there existing function and variable “gdb-many-windows”.

>> diff --git a/lisp/window.el b/lisp/window.el
>> index bd825c09e1..d774d906cd 100644
>> --- a/lisp/window.el
>> +++ b/lisp/window.el
>> @@ -278,6 +278,24 @@ with-displayed-buffer-window
>>             (funcall ,vquit-function ,window ,value)
>>           ,value)))))
>> 
>> +(defmacro with-window-non-dedicated (window &rest body)
>> +  "Evaluate BODY with WINDOW selected and temporarily made non-dedicated.
>                                 ^^^^^^^^^^^^
> This is now no longer true.
> 

Fixed.

> Thank you,
> 
>  Štěpán

Thanks for catching all the issues.

> 
> P.S.: This time your patch somehow ended up hidden as
> application/ocet-stream after the HTML part, instead of inline in plain
> text.

I’m not sure what causes that. I’ve been using Apple Mail and I just copy the patch file into my reply. It actually surprises me that they showed up in plain text before. This time I made the mail plain text, hopefully that makes the patch also in plain text.

Yuan



[-- Attachment #2: new-window.patch --]
[-- Type: application/octet-stream, Size: 20707 bytes --]

From baaae030b7ec76d91ba2798278ed06f92d4d8eec Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Tue, 3 Mar 2020 18:30:03 -0500
Subject: [PATCH] Add store/restore window configuration feature for gdb-mi

Add a feature that allows a user to save a gdb window
configuration (window layout) to a file with
'gdb-save-window-configuration' and load it back with
'gdb-load-window-configuration'.  Set a default window configuration
by setting 'gdb-default-window-configuration-file'.  Note that for the
default window configuration to take effect, 'gdb-many-windows' needs
to be t.  Add an option to make gdb preserve the window configuration
that the user had before starting gdb.  In window.el, add
'with-selected-window-non-dedicated'.
* lisp/progmodes/gdb-mi.el (require): add 'pcase' and 'cl-seq'.
(gdb--window-configuration-before): New variable.
(gdb-restore-window-configuration-after-quit): New custom variable.
(gdb-window-configuration-directory,
gdb-default-window-configuration-file): New variables.
(gdb): Save configuration on startup.
(gud-menu-map): Add "Load Layout" and "Save Layout" to menu. Add
"Restore Layout After Quit" button to menu.  Rename "Restore Window
Layout" to "Restore Default Layout", add some help echo, and move it
from "GDB-MI" menu to "GDB-WINDOWs" menu.
(gdb-toggle-restore-window-configuration): New function.
(gdb-get-source-buffer): New function, extracted out of
'gdb-restore-window'.
(gdb-setup-windows): Add a condition branch that loads default window
configuration when available.  Fix docstring.
(gdb-buffer-p, gdb-function-buffer-p, gdb--buffer-type,
gdb-save-window-configuration, gdb-load-window-configuration): New
functions.
(gdb-restore-windows): Edit docstring to mention
'gdb-default-window-configuration-file' and add comments.
(gdb-reset): Restore window configuration after quit.
(gdb-get-source-file): Add comments.
* lisp/window.el (with-window-non-dedicated): New macro.
---
 lisp/progmodes/gdb-mi.el | 292 +++++++++++++++++++++++++++++++++------
 lisp/window.el           |  18 +++
 2 files changed, 271 insertions(+), 39 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index da5a2a503a..686c2e5206 100644
--- a/lisp/progmodes/gdb-mi.el
+++ b/lisp/progmodes/gdb-mi.el
@@ -92,6 +92,8 @@
 (require 'json)
 (require 'bindat)
 (require 'cl-lib)
+(require 'cl-seq)
+(eval-when-compile (require 'pcase))
 
 (declare-function speedbar-change-initial-expansion-list
                   "speedbar" (new-default))
@@ -253,6 +255,25 @@ gdb-output-sink
 	       disposition of output generated by commands that
 	       gdb mode sends to gdb on its own behalf.")
 
+(defvar gdb--window-configuration-before nil
+  "Stores the window configuration before starting gdb.")
+
+(defcustom gdb-restore-window-configuration-after-quit nil
+  "If non-nil, restore the window configuration the user had before gdb starts.
+
+Possible values are:
+    t -- Always restore.
+    nil -- Don't restore.
+    `if-gdb-show-main' -- Restore only if variable `gdb-show-main' is non-nil
+    `if-gdb-many-windows' -- Restore only if variable `gdb-many-windows' is non-nil."
+  :type '(choice
+          (const :tag "Always restore" t)
+          (const :tag "Don't restore" nil)
+          (const :tag "Depends on `gdb-show-main'" 'if-gdb-show-main)
+          (const :tag "Depends on `gdb-many-windows'" 'if-gdb-many-windows))
+  :group 'gdb
+  :version "28.1")
+
 (defcustom gdb-discard-unordered-replies t
   "Non-nil means discard any out-of-order GDB replies.
 This protects against lost GDB replies, assuming that GDB always
@@ -603,6 +624,26 @@ gdb-show-main
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-configuration-directory user-emacs-directory
+  "The default directory where window configuration files are stored.
+If nil, use `default-directory'."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
+(defcustom gdb-default-window-configuration-file nil
+  "If non-nil, GDB loads this window configuration (layout) file on startup.
+
+This should be the path to the window configuration file.  If the path
+is not an absolute path, GDB treats it as a relative path and
+looks under `gdb-window-configuration-directory'.
+
+Note that this variable only takes effect when variable
+`gdb-many-windows' is t."
+  :type 'string
+  :group 'gdb
+  :version "28.1")
+
 (defvar gdbmi-debug-mode nil
   "When non-nil, print the messages sent/received from GDB/MI in *Messages*.")
 
@@ -761,6 +802,12 @@ gdb
     (gdb-restore-windows)
     (error
      "Multiple debugging requires restarting in text command mode"))
+
+  ;; Save window configuration before starting gdb so we can restore
+  ;; it after gdb quits. Save it regardless of the value of
+  ;; `gdb-restore-window-configuration-after-quit'.
+  (setq gdb--window-configuration-before (window-state-get))
+
   ;;
   (gud-common-init command-line nil 'gud-gdbmi-marker-filter)
 
@@ -4491,6 +4538,26 @@ gdb-preempt-existing-or-display-buffer
   (define-key gud-menu-map [displays]
     `(menu-item "GDB-Windows" ,menu
 		:visible (eq gud-minor-mode 'gdbmi)))
+  (define-key menu [gdb-restore-windows]
+    '(menu-item "Restore Initial Layout" gdb-restore-windows
+      :help "Restore the initial GDB window layout."))
+  ;; Window layout vs window configuration: We use "window layout" in
+  ;; GDB UI.  Internally we refer to "window configuration" because
+  ;; that's the data structure used to store window layouts.  Though
+  ;; bare in mind that there is a small difference between what we
+  ;; store and what normal window configuration functions
+  ;; output. Because GDB buffers (source, local, breakpoint, etc) are
+  ;; different between each debugging sessions, simply save/load
+  ;; window configurations doesn't
+  ;; work. `gdb-save-window-configuration' and
+  ;; `gdb-load-window-configuration' do some tricks to store and
+  ;; recreate each buffer in the layout.
+  (define-key menu [load-layout] '("Load Layout" "Load GDB window configuration (layout) from a file" . gdb-load-window-configuration))
+  (define-key menu [save-layout] '("Save Layout" "Save current GDB window configuration (layout) to a file" . gdb-save-window-configuration))
+  (define-key menu [restore-layout-after-quit]
+    '(menu-item "Restore Layout After Quit" gdb-toggle-restore-window-configuration
+       :button (:toggle . gdb-restore-window-configuration-after-quit)
+       :help "Toggle between always restore the window configuration (layout) after GDB quits and never restore.\n You can also change this setting in Customize to conditionally restore."))
   (define-key menu [gdb] '("Gdb" . gdb-display-gdb-buffer))
   (define-key menu [threads] '("Threads" . gdb-display-threads-buffer))
   (define-key menu [memory] '("Memory" . gdb-display-memory-buffer))
@@ -4529,9 +4596,6 @@ gdb-preempt-existing-or-display-buffer
     '(menu-item "Display Other Windows" gdb-many-windows
       :help "Toggle display of locals, stack and breakpoint information"
       :button (:toggle . gdb-many-windows)))
-  (define-key menu [gdb-restore-windows]
-    '(menu-item "Restore Window Layout" gdb-restore-windows
-      :help "Restore standard layout for debug session."))
   (define-key menu [sep1]
     '(menu-item "--"))
   (define-key menu [all-threads]
@@ -4606,41 +4670,174 @@ gdb-set-window-buffer
   (set-window-buffer window (get-buffer name))
   (set-window-dedicated-p window t))
 
+(defun gdb-toggle-restore-window-configuration ()
+  "Toggle whether to restore window configuration when GDB quits."
+  (interactive)
+  (setq gdb-restore-window-configuration-after-quit
+        (not gdb-restore-window-configuration-after-quit)))
+
+(defun gdb-get-source-buffer ()
+  "Return a buffer displaying source file or nil if we can't find one.
+
+The source file is the file that contains the source location
+where GDB stops.  There could be multiple source files during a
+debugging session, we get the most recently showed one.  If
+program hasn't started running yet, the source file is the \"main
+file\" where the GDB session starts (see `gdb-main-file')."
+  (if gud-last-last-frame
+      (gud-find-file (car gud-last-last-frame))
+    (when gdb-main-file
+      (gud-find-file gdb-main-file))))
+
 (defun gdb-setup-windows ()
-  "Layout the window pattern for option `gdb-many-windows'."
-  (gdb-get-buffer-create 'gdb-locals-buffer)
-  (gdb-get-buffer-create 'gdb-stack-buffer)
-  (gdb-get-buffer-create 'gdb-breakpoints-buffer)
-  (set-window-dedicated-p (selected-window) nil)
-  (switch-to-buffer gud-comint-buffer)
-  (delete-other-windows)
-  (let ((win0 (selected-window))
-        (win1 (split-window nil ( / ( * (window-height) 3) 4)))
-        (win2 (split-window nil ( / (window-height) 3)))
-        (win3 (split-window-right)))
-    (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
-    (select-window win2)
-    (set-window-buffer
-     win2
-     (if gud-last-last-frame
-         (gud-find-file (car gud-last-last-frame))
-       (if gdb-main-file
-           (gud-find-file gdb-main-file)
-         ;; Put buffer list in window if we
-         ;; can't find a source file.
-         (list-buffers-noselect))))
-    (setq gdb-source-window (selected-window))
-    (let ((win4 (split-window-right)))
-      (gdb-set-window-buffer
-       (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
-    (select-window win1)
-    (gdb-set-window-buffer (gdb-stack-buffer-name))
-    (let ((win5 (split-window-right)))
-      (gdb-set-window-buffer (if gdb-show-threads-by-default
-                                 (gdb-threads-buffer-name)
-                               (gdb-breakpoints-buffer-name))
-                             nil win5))
-    (select-window win0)))
+  "Lay out the window pattern for option `gdb-many-windows'."
+  (if gdb-default-window-configuration-file
+      (gdb-load-window-configuration
+       (if (file-name-absolute-p gdb-default-window-configuration-file)
+           gdb-default-window-configuration-file
+         (expand-file-name gdb-default-window-configuration-file
+                           gdb-window-configuration-directory)))
+    ;; Create efault layout as before.
+    (gdb-get-buffer-create 'gdb-locals-buffer)
+    (gdb-get-buffer-create 'gdb-stack-buffer)
+    (gdb-get-buffer-create 'gdb-breakpoints-buffer)
+    (set-window-dedicated-p (selected-window) nil)
+    (switch-to-buffer gud-comint-buffer)
+    (delete-other-windows)
+    (let ((win0 (selected-window))
+          (win1 (split-window nil ( / ( * (window-height) 3) 4)))
+          (win2 (split-window nil ( / (window-height) 3)))
+          (win3 (split-window-right)))
+      (gdb-set-window-buffer (gdb-locals-buffer-name) nil win3)
+      (select-window win2)
+      (set-window-buffer win2 (or (gdb-get-source-buffer)
+                                  (list-buffers-noselect)))
+      (setq gdb-source-window (selected-window))
+      (let ((win4 (split-window-right)))
+        (gdb-set-window-buffer
+         (gdb-get-buffer-create 'gdb-inferior-io) nil win4))
+      (select-window win1)
+      (gdb-set-window-buffer (gdb-stack-buffer-name))
+      (let ((win5 (split-window-right)))
+        (gdb-set-window-buffer (if gdb-show-threads-by-default
+                                   (gdb-threads-buffer-name)
+                                 (gdb-breakpoints-buffer-name))
+                               nil win5))
+      (select-window win0))))
+
+(defun gdb-buffer-p (buffer)
+  "Return t if BUFFER is GDB-related."
+  (with-current-buffer buffer
+    (eq gud-minor-mode 'gdbmi)))
+
+(defun gdb-function-buffer-p (buffer)
+  "Return t if BUFFER is a GDB function buffer.
+
+Function buffers are locals buffer, registers buffer, etc, but
+not including main command buffer (the one where you type GDB
+commands) or source buffers (that display program source code)."
+  (with-current-buffer buffer
+    (derived-mode-p 'gdb-parent-mode 'gdb-inferior-io-mode)))
+
+(defun gdb--buffer-type (buffer)
+  "Return the type of BUFFER if it is a function buffer.
+
+Buffer type is like `gdb-registers-type', `gdb-stack-buffer'.
+These symbols are used by `gdb-get-buffer-create'.
+
+Return nil if BUFFER isn't a GDB function buffer."
+  (with-current-buffer buffer
+    (cl-loop for rule in gdb-buffer-rules
+             for mode-name = (gdb-rules-buffer-mode rule)
+             for type = (car rule)
+             if (eq mode-name major-mode)
+             return type
+             finally return nil)))
+
+(defun gdb-save-window-configuration (file)
+  "Save current window configuration (layout) to FILE.
+
+You can later restore this configuration from that file by
+`gdb-load-window-configuration'."
+  (interactive (list (read-file-name
+                      "Save window configuration to file: "
+                      (or gdb-window-configuration-directory default-directory))))
+  ;; We replace the buffer in each window with a placeholder, store
+  ;; the buffer type (register, breakpoint, etc) in window parameters,
+  ;; and write the window configuration to the file.
+  (save-window-excursion
+    (let ((placeholder (get-buffer-create " *gdb-placeholder*"))
+          (window-persistent-parameters
+           (cons '(gdb-buffer-type . writable) window-persistent-parameters)))
+      (unwind-protect
+          (dolist (win (window-list nil 'no-minibuffer))
+            (select-window win)
+            (when (gdb-buffer-p (current-buffer))
+              (set-window-parameter
+               nil 'gdb-buffer-type
+               (cond ((gdb-function-buffer-p (current-buffer))
+                      ;; 1) If a user arranged the window
+                      ;; configuration herself and saves it, windows
+                      ;; are probably not dedicated.  2) We use the
+                      ;; same dedication flag as in
+                      ;; `gdb-display-buffer'.
+                      (set-window-dedicated-p nil t)
+                      ;; We save this gdb-buffer-type symbol so
+                      ;; we can later pass it to `gdb-get-buffer-create';
+                      ;; one example: `gdb-registers-buffer'.
+                      (or (gdb--buffer-type (current-buffer))
+                          (error "Unrecognized gdb buffer mode: %s" major-mode)))
+                     ;; Command buffer.
+                     ((derived-mode-p 'gud-mode) 'command)
+                     ((equal (selected-window) gdb-source-window) 'source)))
+              (with-window-non-dedicated nil
+                (set-window-buffer nil placeholder)
+                (set-window-prev-buffers (selected-window) nil)
+                (set-window-next-buffers (selected-window) nil))))
+        ;; Save the window configuration to FILE.
+        (let ((window-config (window-state-get nil t)))
+          (with-temp-buffer
+            (prin1 window-config (current-buffer))
+            (write-file file t)))
+        (kill-buffer placeholder)))))
+
+(defun gdb-load-window-configuration (file)
+  "Restore window configuration (layout) from FILE.
+
+FILE should be a window configuration file saved by
+`gdb-save-window-configuration'."
+  (interactive (list (read-file-name
+                      "Restore window configuration from file: "
+                      (or gdb-window-configuration-directory default-directory))))
+  ;; Basically, we restore window configuration and go through each
+  ;; window and restore the function buffers.
+  (let* ((placeholder (get-buffer-create " *gdb-placeholder*")))
+    (unwind-protect ; Don't leak buffer.
+        (let ((window-config (with-temp-buffer
+                               (insert-file-contents file)
+                               ;; We need to go to point-min because
+                               ;; `read' reads from point
+                               (goto-char (point-min))
+                               (read (current-buffer))))
+              (source-buffer (or (gdb-get-source-buffer)
+                                 ;; Do the same thing as in
+                                 ;; `gdb-setup-windows' if no source
+                                 ;; buffer is found.
+                                 (list-buffers-noselect)))
+              buffer-type)
+          (window-state-put window-config (frame-root-window))
+          (dolist (window (window-list nil 'no-minibuffer))
+            (with-selected-window window
+              (setq buffer-type (window-parameter nil 'gdb-buffer-type))
+              (pcase buffer-type
+                ('source (when source-buffer
+                           (set-window-buffer nil source-buffer)
+                           (setq gdb-source-window (selected-window))))
+                ('command (switch-to-buffer gud-comint-buffer))
+                (_ (let ((buffer (gdb-get-buffer-create buffer-type)))
+                     (with-window-non-dedicated nil
+                       (set-window-buffer nil buffer))))))))
+      (kill-buffer placeholder))))
 
 (define-minor-mode gdb-many-windows
   "If nil just pop up the GUD buffer unless `gdb-show-main' is t.
@@ -4658,7 +4855,12 @@ gdb-many-windows
 
 (defun gdb-restore-windows ()
   "Restore the basic arrangement of windows used by gdb.
-This arrangement depends on the value of option `gdb-many-windows'."
+This arrangement depends on the values of variable
+`gdb-many-windows' and `gdb-default-window-configuration-file'."
+  ;; This function is used when the user messed up window
+  ;; configuration and wants to "reset to default".  The function that
+  ;; sets up window configuration on start up is
+  ;; `gdb-get-source-file'.
   (interactive)
   (switch-to-buffer gud-comint-buffer) ;Select the right window and frame.
   (delete-other-windows)
@@ -4705,11 +4907,23 @@ gdb-reset
   (if (boundp 'speedbar-frame) (speedbar-timer-fn))
   (setq gud-running nil)
   (setq gdb-active-process nil)
-  (remove-hook 'after-save-hook 'gdb-create-define-alist t))
+  (remove-hook 'after-save-hook 'gdb-create-define-alist t)
+  ;; Recover window configuration.
+  (when (or (eq gdb-restore-window-configuration-after-quit t)
+            (and (eq gdb-restore-window-configuration-after-quit 'if-gdb-show-main)
+                 gdb-show-main)
+            (and (eq gdb-restore-window-configuration-after-quit 'if-gdb-many-windows)
+                 gdb-many-windows))
+    (when gdb--window-configuration-before
+      (window-state-put gdb--window-configuration-before)
+      ;; This way we don't accidentally restore an outdated window
+      ;; configuration.
+      (setq gdb--window-configuration-before nil))))
 
 (defun gdb-get-source-file ()
   "Find the source file where the program starts and display it with related
 buffers, if required."
+  ;; This function is called only once on startup.
   (goto-char (point-min))
   (if (re-search-forward gdb-source-file-regexp nil t)
       (setq gdb-main-file (read (match-string 1))))
diff --git a/lisp/window.el b/lisp/window.el
index bd825c09e1..db552f4b4d 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,24 @@ with-displayed-buffer-window
 	     (funcall ,vquit-function ,window ,value)
 	   ,value)))))
 
+(defmacro with-window-non-dedicated (window &rest body)
+  "Evaluate BODY with WINDOW temporarily made non-dedicated.
+
+If WINDOW is nil, use the selected window.  Return the value of the last form in BODY."
+  (declare (indent 1) (debug t))
+  (let ((window-dedicated-sym (gensym))
+        (window-sym (gensym)))
+    `(let* ((,window-sym (window-normalize-window ,window t))
+            (,window-dedicated-sym (window-dedicated-p ,window-sym)))
+       (set-window-dedicated-p ,window-sym nil)
+       (unwind-protect
+           (progn ,@body)
+         ;; `window-dedicated-p' returns the value set by
+         ;; `set-window-dedicated-p', which differentiates non-nil and
+         ;; t, so we cannot simply use t here. That's why we use
+         ;; `window-dedicated-sym'.
+         (set-window-dedicated-p ,window-sym ,window-dedicated-sym)))))
+
 ;; The following two functions are like `window-next-sibling' and
 ;; `window-prev-sibling' but the WINDOW argument is _not_ optional (so
 ;; they don't substitute the selected window for nil), and they return
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 3 bytes --]





^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-13 21:40                                                               ` Yuan Fu
@ 2020-03-13 22:12                                                                 ` Štěpán Němec
  2020-03-15 15:55                                                                 ` martin rudalics
  1 sibling, 0 replies; 48+ messages in thread
From: Štěpán Němec @ 2020-03-13 22:12 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

On Fri, 13 Mar 2020 17:40:30 -0400
Yuan Fu wrote:

> I added some comments in the menu-map code. Hope that’s helps.

Thanks.

> All fixed.

Confirmed, thank you.

>> P.S.: This time your patch somehow ended up hidden as
>> application/ocet-stream after the HTML part, instead of inline in plain
>> text.

> I’m not sure what causes that. I’ve been using Apple Mail and I just
> copy the patch file into my reply. It actually surprises me that they
> showed up in plain text before. This time I made the mail plain text,
> hopefully that makes the patch also in plain text.

Yeah, actually the difference wasn't in the patch MIME type per se, but
in the fact that it was included in a multipart/mixed section together
with the HTML part (all that in one big multipart/alternative), so it
wasn't visible in the text/plain part. This one is same as your previous
e-mails again (text/plain), thanks.

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-13 21:40                                                               ` Yuan Fu
  2020-03-13 22:12                                                                 ` Štěpán Němec
@ 2020-03-15 15:55                                                                 ` martin rudalics
  2020-03-16  0:13                                                                   ` Yuan Fu
  1 sibling, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-15 15:55 UTC (permalink / raw)
  To: Yuan Fu, Štěpán Němec; +Cc: 39181

I pushed your patch to master, tweaking the change log, some doc-strings
and breaking a couple of lines so they fit into our line length limits.
Please have a look at whether anything has been broken by these tweaks.

We certainly need a few NEWS entries for this.  Kindly supply them.

Thanks again for your work and to Štěpán for the comments, martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-15 15:55                                                                 ` martin rudalics
@ 2020-03-16  0:13                                                                   ` Yuan Fu
  2020-03-16  9:24                                                                     ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-16  0:13 UTC (permalink / raw)
  To: martin rudalics; +Cc: Štěpán Němec, 39181


> On Mar 15, 2020, at 11:55 AM, martin rudalics <rudalics@gmx.at> wrote:
> 
> I pushed your patch to master, tweaking the change log, some doc-strings
> and breaking a couple of lines so they fit into our line length limits.
> Please have a look at whether anything has been broken by these tweaks.
> 
> We certainly need a few NEWS entries for this.  Kindly supply them.

Thanks. I’ll prepare a patch for etc/news. Should I also update the manual?

Yuan







^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-16  0:13                                                                   ` Yuan Fu
@ 2020-03-16  9:24                                                                     ` martin rudalics
  2020-03-20 20:03                                                                       ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-16  9:24 UTC (permalink / raw)
  To: Yuan Fu; +Cc: Štěpán Němec, 39181

 > Thanks. I’ll prepare a patch for etc/news. Should I also update the manual?

Please do.

Thanks, martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-16  9:24                                                                     ` martin rudalics
@ 2020-03-20 20:03                                                                       ` Yuan Fu
  2020-03-20 20:58                                                                         ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-20 20:03 UTC (permalink / raw)
  To: martin rudalics; +Cc: Štěpán Němec, 39181

[-- Attachment #1: Type: text/plain, Size: 231 bytes --]


> On Mar 16, 2020, at 5:24 AM, martin rudalics <rudalics@gmx.at> wrote:
> 
> > Thanks. I’ll prepare a patch for etc/news. Should I also update the manual?
> 
> Please do.
> 
> Thanks, martin
> 

Here it is.

Yuan


[-- Attachment #2: news.patch --]
[-- Type: application/octet-stream, Size: 4414 bytes --]

From 8becfc67a17700dec92b427bf10ef11e81615c12 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Sun, 15 Mar 2020 21:02:14 -0400
Subject: [PATCH] Add documentation for previous commit by Yuan Fu

* etc/NEWS: Add news entry.
* doc/emacs/building.texi (GDB User Interface Layout): Add
documentation for 'gdb-save-window-configuration',
'gdb-load-window-configuration',
'gdb-default-window-configuration-file',
'gdb-window-configuration-directory',
'gdb-restore-window-configuration-after-quit'.  Change 'many-windows
layout' to 'default layout'
---
 doc/emacs/building.texi | 29 ++++++++++++++++++++++++++---
 etc/NEWS                | 16 ++++++++++++++++
 2 files changed, 42 insertions(+), 3 deletions(-)

diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index 38963f225c..130abc41e8 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -568,7 +568,7 @@ Starting GUD
 
 @item M-x gud-gdb
 @findex gud-gdb
-Run GDB, using a GUD interaction buffer for input and output to the
+Run GDB, using a GUD action buffer for input and output to the
 GDB subprocess (@pxref{Debugger Operation}).  If such a buffer already
 exists, switch to it; otherwise, create the buffer and switch to it.
 
@@ -975,9 +975,26 @@ GDB User Interface Layout
 @end group
 @end smallexample
 
+@findex gdb-save-window-configuration
+@findex gdb-load-window-configuration
+@vindex gdb-default-window-configuration-file
+@vindex gdb-window-configuration-directory
+  You can customize the window layout bese on the one above, and save
+the custom layout to a file by @code{gdb-save-window-configuration}.
+Then you can later load this layout back by
+@code{gdb-load-window-configuration}.  (Simply put, window
+configuration is the Emacs jargon for window layout.)  You can set
+your custom layout file as the default one used by
+@code{gdb-many-windows} by customizing
+@code{gdb-default-window-configuration-file}.  If it is not an
+absolute path, GDB looks under
+@code{gdb-window-configuration-directory} (default to
+@file{~/.emacs.d}) for the file.
+
 @findex gdb-restore-windows
 @findex gdb-many-windows
-  If you ever change the window layout, you can restore the many-windows
+@vindex gdb-restore-window-configuration-after-quit
+  If you ever change the window layout, you can restore the default
 layout by typing @kbd{M-x gdb-restore-windows}.  To toggle
 between the many windows layout and a simple layout with just the GUD
 interaction buffer and a source file, type @kbd{M-x gdb-many-windows}.
@@ -988,7 +1005,13 @@ GDB User Interface Layout
 of windows on your original frame will not be affected.  A separate
 frame for GDB sessions can come in especially handy if you work on a
 text-mode terminal, where the screen estate for windows could be at a
-premium.
+premium.  If you choose to start GDB in the same frame, consider
+setting @code{gdb-restore-window-configuration-after-quit} to
+non-@code{nil}.  Then GDB restores your original window layout after
+it quits.  Set to @code{t} to always restore; set to
+@code{if-gdb-many-windows} to restore only when
+@code{gdb-many-windows} is @code{t}; set to @code{if-gdb-show-main} to
+restore only when @code{gdb-show-main} is @code{t}.
 
   You may also specify additional GDB-related buffers to display,
 either in the same frame or a different one.  Select the buffers you
diff --git a/etc/NEWS b/etc/NEWS
index 87e634f2c1..692ecf7f0a 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -174,6 +174,22 @@ key             binding
 / v             package-menu-filter-by-version
 / /             package-menu-filter-clear
 
+** gdb-mi
+
+*** gdb-mi can now store and restore window configurations.
+Use 'gdb-save-window-configuration' to save window configuration to a
+file, and 'gdb-load-window-configuration' to load from a file.  They
+can also be accessed through menu bar under Gud -- GDB-Windows.
+'gdb-default-window-configuration-file', when non-nil, is loaded when
+GDB starts up.
+
+*** gdb-mi can now restore window configuration after finished.
+Set 'gdb-restore-window-configuration-after-quit' to non-nil and GDB
+will remember the window configuration before GDB started, and restore
+it after it quits.  Toggle button is also provided under Gud --
+GDB-WINDOWS.
+
+
 \f
 * New Modes and Packages in Emacs 28.1
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-20 20:03                                                                       ` Yuan Fu
@ 2020-03-20 20:58                                                                         ` Štěpán Němec
  2020-03-21 18:00                                                                           ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-20 20:58 UTC (permalink / raw)
  To: Yuan Fu, martin rudalics; +Cc: 39181

On Fri, 20 Mar 2020 16:03:45 -0400
Yuan Fu wrote:

> Here it is.

Thank you! A few comments:

> diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
> index 38963f225c..130abc41e8 100644
> --- a/doc/emacs/building.texi
> +++ b/doc/emacs/building.texi
> @@ -568,7 +568,7 @@ Starting GUD
>  
>  @item M-x gud-gdb
>  @findex gud-gdb
> -Run GDB, using a GUD interaction buffer for input and output to the
> +Run GDB, using a GUD action buffer for input and output to the
                        ^^^^^^
Is there really any reason for this change? If the buffer is "for
input and output", isn't "interaction" more fitting than "action"?

[...]

> +  You can customize the window layout bese on the one above, and save
                                         ^^^^
"based"?

> +the custom layout to a file by @code{gdb-save-window-configuration}.
                               ^^
I think "using" would be better here.

> +Then you can later load this layout back by
                                            ^^
same here

> +@code{gdb-load-window-configuration}.  (Simply put, window

[...]

> +@code{gdb-default-window-configuration-file}.  If it is not an
> +absolute path, GDB looks under
> +@code{gdb-window-configuration-directory} (default to
> +@file{~/.emacs.d}) for the file.

"(which defaults to user-emacs-directory)" (ideally with a proper
reference to that variable).

[...]

> +premium.  If you choose to start GDB in the same frame, consider
> +setting @code{gdb-restore-window-configuration-after-quit} to
> +non-@code{nil}.  Then GDB restores your original window layout after

"to a non-nil value.  Your original window layout will then be restored
after GDB quits."

> +it quits.  Set to @code{t} to always restore; set to
              ^^^^^^
"Set it to" or "Use", similarly for the following ones.

> +@code{if-gdb-many-windows} to restore only when
> +@code{gdb-many-windows} is @code{t}; set to @code{if-gdb-show-main} to
> +restore only when @code{gdb-show-main} is @code{t}.

IIUC, these two `t's for gdb-show-main and gdb-many-windows should
really be "non-nil"s, i.e., there is no special treatment of the symbol
`t' as opposed to generalized boolean.

> diff --git a/etc/NEWS b/etc/NEWS
> index 87e634f2c1..692ecf7f0a 100644
> --- a/etc/NEWS
> +++ b/etc/NEWS
> @@ -174,6 +174,22 @@ key             binding
>  / v             package-menu-filter-by-version
>  / /             package-menu-filter-clear
>  
> +** gdb-mi
> +
> +*** gdb-mi can now store and restore window configurations.
> +Use 'gdb-save-window-configuration' to save window configuration to a
> +file, and 'gdb-load-window-configuration' to load from a file.  They
                                                                   ^^^^
"These commands"?

> +can also be accessed through menu bar under Gud -- GDB-Windows.

[...]

> +*** gdb-mi can now restore window configuration after finished.
                                                   ^^^^^^^^^^^^^^
"when finished" or "after quit"

> +Set 'gdb-restore-window-configuration-after-quit' to non-nil and GDB
                                                                    ^^^
Better say "Emacs": at that point GDB is gone. :-)

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-20 20:58                                                                         ` Štěpán Němec
@ 2020-03-21 18:00                                                                           ` Yuan Fu
  2020-03-21 18:39                                                                             ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-21 18:00 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 972 bytes --]

Thanks for reviewing! Almost all fixed with some comments:

>> +@code{gdb-default-window-configuration-file}.  If it is not an
>> +absolute path, GDB looks under
>> +@code{gdb-window-configuration-directory} (default to
>> +@file{~/.emacs.d}) for the file.
> 
> "(which defaults to user-emacs-directory)" (ideally with a proper
> reference to that variable).

The whole Emacs user manual only referenced user-emacs-directory once when talking about ~/.emacs.d. OTOH ~/.emacs.d is used across the manual. I think ~/.emacs.d is better.

>> +premium.  If you choose to start GDB in the same frame, consider
>> +setting @code{gdb-restore-window-configuration-after-quit} to
>> +non-@code{nil}.  Then GDB restores your original window layout after
> 
> "to a non-nil value.  Your original window layout will then be restored
> after GDB quits."

I prefer active voice than passive voice but don’t object the latter. So I changed it accordingly.

Yuan


[-- Attachment #2: news.patch --]
[-- Type: application/octet-stream, Size: 4089 bytes --]

From 5f6f35712c069b031e3ac7e28c413b206adffd0c Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Sun, 15 Mar 2020 21:02:14 -0400
Subject: [PATCH] Add documentation for previous commit by Yuan Fu

* etc/NEWS: Add news entry.
* doc/emacs/building.texi (GDB User Interface Layout): Add
documentation for 'gdb-save-window-configuration',
'gdb-load-window-configuration',
'gdb-default-window-configuration-file',
'gdb-window-configuration-directory',
'gdb-restore-window-configuration-after-quit'.  Change 'many-windows
layout' to 'default layout'
---
 doc/emacs/building.texi | 27 +++++++++++++++++++++++++--
 etc/NEWS                | 16 ++++++++++++++++
 2 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index 38963f225c..a9d2a21d76 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -975,9 +975,26 @@ GDB User Interface Layout
 @end group
 @end smallexample
 
+@findex gdb-save-window-configuration
+@findex gdb-load-window-configuration
+@vindex gdb-default-window-configuration-file
+@vindex gdb-window-configuration-directory
+  You can customize the window layout based on the one above, and save
+the custom layout to a file using
+@code{gdb-save-window-configuration}.  Then you can later load this
+layout back using @code{gdb-load-window-configuration}.  (Simply put,
+window configuration is the Emacs jargon for window layout.)  You can
+set your custom layout file as the default one used by
+@code{gdb-many-windows} by customizing
+@code{gdb-default-window-configuration-file}.  If it is not an
+absolute path, GDB looks under
+@code{gdb-window-configuration-directory} (which defaults to
+@file{~/.emacs.d}) for the file.
+
 @findex gdb-restore-windows
 @findex gdb-many-windows
-  If you ever change the window layout, you can restore the many-windows
+@vindex gdb-restore-window-configuration-after-quit
+  If you ever change the window layout, you can restore the default
 layout by typing @kbd{M-x gdb-restore-windows}.  To toggle
 between the many windows layout and a simple layout with just the GUD
 interaction buffer and a source file, type @kbd{M-x gdb-many-windows}.
@@ -988,7 +1005,13 @@ GDB User Interface Layout
 of windows on your original frame will not be affected.  A separate
 frame for GDB sessions can come in especially handy if you work on a
 text-mode terminal, where the screen estate for windows could be at a
-premium.
+premium.  If you choose to start GDB in the same frame, consider
+setting @code{gdb-restore-window-configuration-after-quit} to a
+non-@code{nil} value.  Your original layout will then be restored
+after GDB quits.  Use @code{t} to always restore; use
+@code{if-gdb-many-windows} to restore only when
+@code{gdb-many-windows} is non-@code{nil}; use @code{if-gdb-show-main}
+to restore only when @code{gdb-show-main} is non-@code{nil}.
 
   You may also specify additional GDB-related buffers to display,
 either in the same frame or a different one.  Select the buffers you
diff --git a/etc/NEWS b/etc/NEWS
index 87e634f2c1..f61bff6a4f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -174,6 +174,22 @@ key             binding
 / v             package-menu-filter-by-version
 / /             package-menu-filter-clear
 
+** gdb-mi
+
+*** gdb-mi can now store and restore window configurations.
+Use 'gdb-save-window-configuration' to save window configuration to a
+file, and 'gdb-load-window-configuration' to load from a file.  These
+commands can also be accessed through menu bar under Gud --
+GDB-Windows.  'gdb-default-window-configuration-file', when non-nil,
+is loaded when GDB starts up.
+
+*** gdb-mi can now restore window configuration after quit.
+Set 'gdb-restore-window-configuration-after-quit' to non-nil and Emacs
+will remember the window configuration before GDB started, and restore
+it after GDB quits.  Toggle button is also provided under Gud --
+GDB-Windows.
+
+
 \f
 * New Modes and Packages in Emacs 28.1
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-21 18:00                                                                           ` Yuan Fu
@ 2020-03-21 18:39                                                                             ` Štěpán Němec
  2020-03-21 21:03                                                                               ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-21 18:39 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

On Sat, 21 Mar 2020 14:00:56 -0400
Yuan Fu wrote:

>>> +@code{gdb-default-window-configuration-file}.  If it is not an
>>> +absolute path, GDB looks under
>>> +@code{gdb-window-configuration-directory} (default to
>>> +@file{~/.emacs.d}) for the file.
>> 
>> "(which defaults to user-emacs-directory)" (ideally with a proper
>> reference to that variable).
>
> The whole Emacs user manual only referenced user-emacs-directory once when talking about ~/.emacs.d. OTOH ~/.emacs.d is used across the manual. I think ~/.emacs.d is better.

Saying "defaults to ~/.emacs.d" is strictly speaking incorrect, because
it really defaults to user-emacs-directory, which might or might not be
"~/.emacs.d". It is true that the same could probably be argued about
some other occurences in the manuals (most of which probably go back
even before the introduction of user-emacs-directory in Emacs 23, let
alone the recent XDG-compliance efforts), though, so if you insist, at
least it will be incorrect somewhat _consistently_ (and can be fixed
later together with the other places) :-D.

>>> +premium.  If you choose to start GDB in the same frame, consider
>>> +setting @code{gdb-restore-window-configuration-after-quit} to
>>> +non-@code{nil}.  Then GDB restores your original window layout after
>> 
>> "to a non-nil value.  Your original window layout will then be restored
>> after GDB quits."
>
> I prefer active voice than passive voice but don’t object the latter. So I changed it accordingly.

The real issue here was more the semantics than the grammar, similarly
to the NEWS correction: it's really Emacs, not GDB, that restores the
windows after GDB quits.

Thanks!

-- 
Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-21 18:39                                                                             ` Štěpán Němec
@ 2020-03-21 21:03                                                                               ` Yuan Fu
  2020-03-21 21:49                                                                                 ` Štěpán Němec
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-21 21:03 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 1917 bytes --]



> On Mar 21, 2020, at 2:39 PM, Štěpán Němec <stepnem@gmail.com> wrote:
> 
> On Sat, 21 Mar 2020 14:00:56 -0400
> Yuan Fu wrote:
> 
>>>> +@code{gdb-default-window-configuration-file}.  If it is not an
>>>> +absolute path, GDB looks under
>>>> +@code{gdb-window-configuration-directory} (default to
>>>> +@file{~/.emacs.d}) for the file.
>>> 
>>> "(which defaults to user-emacs-directory)" (ideally with a proper
>>> reference to that variable).
>> 
>> The whole Emacs user manual only referenced user-emacs-directory once when talking about ~/.emacs.d. OTOH ~/.emacs.d is used across the manual. I think ~/.emacs.d is better.
> 
> Saying "defaults to ~/.emacs.d" is strictly speaking incorrect, because
> it really defaults to user-emacs-directory, which might or might not be
> "~/.emacs.d". It is true that the same could probably be argued about
> some other occurences in the manuals (most of which probably go back
> even before the introduction of user-emacs-directory in Emacs 23, let
> alone the recent XDG-compliance efforts), though, so if you insist, at
> least it will be incorrect somewhat _consistently_ (and can be fixed
> later together with the other places) :-D.
> 
>>>> +premium.  If you choose to start GDB in the same frame, consider
>>>> +setting @code{gdb-restore-window-configuration-after-quit} to
>>>> +non-@code{nil}.  Then GDB restores your original window layout after
>>> 
>>> "to a non-nil value.  Your original window layout will then be restored
>>> after GDB quits."
>> 
>> I prefer active voice than passive voice but don’t object the latter. So I changed it accordingly.
> 
> The real issue here was more the semantics than the grammar, similarly
> to the NEWS correction: it's really Emacs, not GDB, that restores the
> windows after GDB quits.
> 
> 

I changed to user-emacs-directory. I hope I get the xref right :-)

Yuan



[-- Attachment #2: news.patch --]
[-- Type: application/octet-stream, Size: 4215 bytes --]

From c75dd5f5ad5bddc323e57844dcdd45239734aa8a Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Sun, 15 Mar 2020 21:02:14 -0400
Subject: [PATCH] Add documentation for previous commit by Yuan Fu

* etc/NEWS: Add news entry.
* doc/emacs/building.texi (GDB User Interface Layout): Add
documentation for 'gdb-save-window-configuration',
'gdb-load-window-configuration',
'gdb-default-window-configuration-file',
'gdb-window-configuration-directory',
'gdb-restore-window-configuration-after-quit'.  Change 'many-windows
layout' to 'default layout'
---
 doc/emacs/building.texi | 30 ++++++++++++++++++++++++++++--
 etc/NEWS                | 16 ++++++++++++++++
 2 files changed, 44 insertions(+), 2 deletions(-)

diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index 38963f225c..11bc239012 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -975,9 +975,29 @@ GDB User Interface Layout
 @end group
 @end smallexample
 
+@findex gdb-save-window-configuration
+@findex gdb-load-window-configuration
+@vindex gdb-default-window-configuration-file
+@vindex gdb-window-configuration-directory
+  You can customize the window layout based on the one above, and save
+the custom layout to a file using
+@code{gdb-save-window-configuration}.  Then you can later load this
+layout back using @code{gdb-load-window-configuration}.  (Simply put,
+window configuration is the Emacs jargon for window layout.)  You can
+set your custom layout file as the default one used by
+@code{gdb-many-windows} by customizing
+@code{gdb-default-window-configuration-file}.  If it is not an
+absolute path, GDB looks under
+@code{gdb-window-configuration-directory} for the file.
+@code{gdb-window-configuration-directory} defaults to
+@code{user-emacs-directory} (@pxref{How Emacs Finds Your Init File,,,
+custom.texi, Customization}).
+
+
 @findex gdb-restore-windows
 @findex gdb-many-windows
-  If you ever change the window layout, you can restore the many-windows
+@vindex gdb-restore-window-configuration-after-quit
+  If you ever change the window layout, you can restore the default
 layout by typing @kbd{M-x gdb-restore-windows}.  To toggle
 between the many windows layout and a simple layout with just the GUD
 interaction buffer and a source file, type @kbd{M-x gdb-many-windows}.
@@ -988,7 +1008,13 @@ GDB User Interface Layout
 of windows on your original frame will not be affected.  A separate
 frame for GDB sessions can come in especially handy if you work on a
 text-mode terminal, where the screen estate for windows could be at a
-premium.
+premium.  If you choose to start GDB in the same frame, consider
+setting @code{gdb-restore-window-configuration-after-quit} to a
+non-@code{nil} value.  Your original layout will then be restored
+after GDB quits.  Use @code{t} to always restore; use
+@code{if-gdb-many-windows} to restore only when
+@code{gdb-many-windows} is non-@code{nil}; use @code{if-gdb-show-main}
+to restore only when @code{gdb-show-main} is non-@code{nil}.
 
   You may also specify additional GDB-related buffers to display,
 either in the same frame or a different one.  Select the buffers you
diff --git a/etc/NEWS b/etc/NEWS
index 87e634f2c1..f61bff6a4f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -174,6 +174,22 @@ key             binding
 / v             package-menu-filter-by-version
 / /             package-menu-filter-clear
 
+** gdb-mi
+
+*** gdb-mi can now store and restore window configurations.
+Use 'gdb-save-window-configuration' to save window configuration to a
+file, and 'gdb-load-window-configuration' to load from a file.  These
+commands can also be accessed through menu bar under Gud --
+GDB-Windows.  'gdb-default-window-configuration-file', when non-nil,
+is loaded when GDB starts up.
+
+*** gdb-mi can now restore window configuration after quit.
+Set 'gdb-restore-window-configuration-after-quit' to non-nil and Emacs
+will remember the window configuration before GDB started, and restore
+it after GDB quits.  Toggle button is also provided under Gud --
+GDB-Windows.
+
+
 \f
 * New Modes and Packages in Emacs 28.1
 
-- 
2.25.1


[-- Attachment #3: Type: text/plain, Size: 2 bytes --]




^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-21 21:03                                                                               ` Yuan Fu
@ 2020-03-21 21:49                                                                                 ` Štěpán Němec
  2020-03-24 16:14                                                                                   ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: Štěpán Němec @ 2020-03-21 21:49 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181

On Sat, 21 Mar 2020 17:03:11 -0400
Yuan Fu wrote:

> I changed to user-emacs-directory. I hope I get the xref right :-)

Well, you could have just tested it, like I did, and... turns out you
didn't. :-D

> +@code{gdb-window-configuration-directory} defaults to
> +@code{user-emacs-directory} (@pxref{How Emacs Finds Your Init File,,,
> +custom.texi, Customization}).

It's actually the same manual, albeit compiled from multiple files, so,
just @pxref{Find Init} appears to work.

Thanks,

  Štěpán





^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-21 21:49                                                                                 ` Štěpán Němec
@ 2020-03-24 16:14                                                                                   ` Yuan Fu
  2020-03-27  9:01                                                                                     ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-24 16:14 UTC (permalink / raw)
  To: Štěpán Němec; +Cc: 39181

[-- Attachment #1: Type: text/plain, Size: 717 bytes --]


> On Mar 21, 2020, at 5:49 PM, Štěpán Němec <stepnem@gmail.com> wrote:
> 
> On Sat, 21 Mar 2020 17:03:11 -0400
> Yuan Fu wrote:
> 
>> I changed to user-emacs-directory. I hope I get the xref right :-)
> 
> Well, you could have just tested it, like I did, and... turns out you
> didn't. :-D
> 
>> +@code{gdb-window-configuration-directory} defaults to
>> +@code{user-emacs-directory} (@pxref{How Emacs Finds Your Init File,,,
>> +custom.texi, Customization}).
> 
> It's actually the same manual, albeit compiled from multiple files, so,
> just @pxref{Find Init} appears to work.

Didn’t thought of that, sorry. I’ll remember to test it next time. Here’s the new patch.
Thanks.

Yuan


[-- Attachment #2: news.patch --]
[-- Type: application/octet-stream, Size: 4161 bytes --]

From 0ad6b9579383e36334084f59ffcf389611f9ca19 Mon Sep 17 00:00:00 2001
From: Yuan Fu <casouri@gmail.com>
Date: Sun, 15 Mar 2020 21:02:14 -0400
Subject: [PATCH] Add documentation for previous commit by Yuan Fu

* etc/NEWS: Add news entry.
* doc/emacs/building.texi (GDB User Interface Layout): Add
documentation for 'gdb-save-window-configuration',
'gdb-load-window-configuration',
'gdb-default-window-configuration-file',
'gdb-window-configuration-directory',
'gdb-restore-window-configuration-after-quit'.  Change 'many-windows
layout' to 'default layout'
---
 doc/emacs/building.texi | 29 +++++++++++++++++++++++++++--
 etc/NEWS                | 16 ++++++++++++++++
 2 files changed, 43 insertions(+), 2 deletions(-)

diff --git a/doc/emacs/building.texi b/doc/emacs/building.texi
index 38963f225c..fc69cdcc8f 100644
--- a/doc/emacs/building.texi
+++ b/doc/emacs/building.texi
@@ -975,9 +975,28 @@ GDB User Interface Layout
 @end group
 @end smallexample
 
+@findex gdb-save-window-configuration
+@findex gdb-load-window-configuration
+@vindex gdb-default-window-configuration-file
+@vindex gdb-window-configuration-directory
+  You can customize the window layout based on the one above, and save
+the custom layout to a file using
+@code{gdb-save-window-configuration}.  Then you can later load this
+layout back using @code{gdb-load-window-configuration}.  (Simply put,
+window configuration is the Emacs jargon for window layout.)  You can
+set your custom layout file as the default one used by
+@code{gdb-many-windows} by customizing
+@code{gdb-default-window-configuration-file}.  If it is not an
+absolute path, GDB looks under
+@code{gdb-window-configuration-directory} for the file.
+@code{gdb-window-configuration-directory} defaults to
+@code{user-emacs-directory} (@pxref{Find Init}).
+
+
 @findex gdb-restore-windows
 @findex gdb-many-windows
-  If you ever change the window layout, you can restore the many-windows
+@vindex gdb-restore-window-configuration-after-quit
+  If you ever change the window layout, you can restore the default
 layout by typing @kbd{M-x gdb-restore-windows}.  To toggle
 between the many windows layout and a simple layout with just the GUD
 interaction buffer and a source file, type @kbd{M-x gdb-many-windows}.
@@ -988,7 +1007,13 @@ GDB User Interface Layout
 of windows on your original frame will not be affected.  A separate
 frame for GDB sessions can come in especially handy if you work on a
 text-mode terminal, where the screen estate for windows could be at a
-premium.
+premium.  If you choose to start GDB in the same frame, consider
+setting @code{gdb-restore-window-configuration-after-quit} to a
+non-@code{nil} value.  Your original layout will then be restored
+after GDB quits.  Use @code{t} to always restore; use
+@code{if-gdb-many-windows} to restore only when
+@code{gdb-many-windows} is non-@code{nil}; use @code{if-gdb-show-main}
+to restore only when @code{gdb-show-main} is non-@code{nil}.
 
   You may also specify additional GDB-related buffers to display,
 either in the same frame or a different one.  Select the buffers you
diff --git a/etc/NEWS b/etc/NEWS
index 87e634f2c1..f61bff6a4f 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -174,6 +174,22 @@ key             binding
 / v             package-menu-filter-by-version
 / /             package-menu-filter-clear
 
+** gdb-mi
+
+*** gdb-mi can now store and restore window configurations.
+Use 'gdb-save-window-configuration' to save window configuration to a
+file, and 'gdb-load-window-configuration' to load from a file.  These
+commands can also be accessed through menu bar under Gud --
+GDB-Windows.  'gdb-default-window-configuration-file', when non-nil,
+is loaded when GDB starts up.
+
+*** gdb-mi can now restore window configuration after quit.
+Set 'gdb-restore-window-configuration-after-quit' to non-nil and Emacs
+will remember the window configuration before GDB started, and restore
+it after GDB quits.  Toggle button is also provided under Gud --
+GDB-Windows.
+
+
 \f
 * New Modes and Packages in Emacs 28.1
 
-- 
2.25.1


^ permalink raw reply related	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-24 16:14                                                                                   ` Yuan Fu
@ 2020-03-27  9:01                                                                                     ` martin rudalics
  2020-03-27 16:28                                                                                       ` Yuan Fu
  0 siblings, 1 reply; 48+ messages in thread
From: martin rudalics @ 2020-03-27  9:01 UTC (permalink / raw)
  To: Yuan Fu, Štěpán Němec; +Cc: 39181

 > Here’s the new patch.

Installed now with a few tweaks.  Please have a look.

Thanks, martin






^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-27  9:01                                                                                     ` martin rudalics
@ 2020-03-27 16:28                                                                                       ` Yuan Fu
  2020-04-14  8:05                                                                                         ` martin rudalics
  0 siblings, 1 reply; 48+ messages in thread
From: Yuan Fu @ 2020-03-27 16:28 UTC (permalink / raw)
  To: martin rudalics; +Cc: Štěpán Němec, 39181



> On Mar 27, 2020, at 5:01 AM, martin rudalics <rudalics@gmx.at> wrote:
> 
> > Here’s the new patch.
> 
> Installed now with a few tweaks.  Please have a look.
> 
> Thanks, martin
> 


Thanks.

Yuan




^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: Fwd: Delivery Status Notification (Failure)
  2020-01-18 20:57 ` bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout Yuan Fu
  2020-01-31 10:13   ` Eli Zaretskii
@ 2020-04-14  1:17   ` Yuan Fu
  1 sibling, 0 replies; 48+ messages in thread
From: Yuan Fu @ 2020-04-14  1:17 UTC (permalink / raw)
  To: 39181-done

[-- Attachment #1: Type: text/plain, Size: 3290 bytes --]



> Begin forwarded message:
> 
> From: Mail Delivery Subsystem <mailer-daemon@googlemail.com>
> Subject: Delivery Status Notification (Failure)
> Date: April 13, 2020 at 9:17:28 PM GMT-4
> To: casouri@gmail.com
> 
> 
> Address not found
> Your message wasn't delivered to  <>39181-done@debuggs.gnu.org <mailto:39181-done@debuggs.gnu.org> because the domain debuggs.gnu.org <http://debuggs.gnu.org/>couldn't be found. Check for typos or unnecessary spaces and try again.
> The response was:
> DNS Error: 11672847 DNS type 'mx' lookup of debuggs.gnu.org <http://debuggs.gnu.org/> responded with code NXDOMAIN Domain name not found: debuggs.gnu.org <http://debuggs.gnu.org/>Reporting-MTA: dns; googlemail.com <http://googlemail.com/>
> Received-From-MTA: dns; casouri@gmail.com <mailto:casouri@gmail.com>
> Arrival-Date: Mon, 13 Apr 2020 18:17:26 -0700 (PDT)
> X-Original-Message-ID: <27D34924-67A4-4C59-8DD4-160465E9CC4D@gmail.com <mailto:27D34924-67A4-4C59-8DD4-160465E9CC4D@gmail.com>>
> 
> Final-Recipient: rfc822; 39181-done@debuggs.gnu.org <mailto:39181-done@debuggs.gnu.org>
> Action: failed
> Status: 4.0.0
> Diagnostic-Code: smtp; DNS Error: 11672847 DNS type 'mx' lookup of debuggs.gnu.org <http://debuggs.gnu.org/> responded with code NXDOMAIN
> Domain name not found: debuggs.gnu.org <http://debuggs.gnu.org/>
> Last-Attempt-Date: Mon, 13 Apr 2020 18:17:28 -0700 (PDT)
> 
> From: Yuan Fu <casouri@gmail.com <mailto:casouri@gmail.com>>
> Subject: Fwd: Delivery Status Notification (Failure)
> Date: April 13, 2020 at 9:17:25 PM GMT-4
> To: 39181-done@debuggs.gnu.org <mailto:39181-done@debuggs.gnu.org>
> 
> 
> 
> --Apple-Mail=_04B0AE11-8C4E-4FBB-9A28-93028C1DEC54
> Content-Transfer-Encoding: quoted-printable
> Content-Type: text/html;
> 	charset=utf-8
> 
> <html><head><meta http-equiv=3D"Content-Type" content=3D"text/html; =
> charset=3Dutf-8"></head><body style=3D"word-wrap: break-word; =
> -webkit-nbsp-mode: space; line-break: after-white-space;" class=3D""><br =
> class=3D""><div><br class=3D""><blockquote type=3D"cite" class=3D""><div =
> class=3D"">Begin forwarded message:</div><br =
> class=3D"Apple-interchange-newline"><div style=3D"margin-top: 0px; =
> margin-right: 0px; margin-bottom: 0px; margin-left: 0px;" class=3D""><span=
> style=3D"font-family: -webkit-system-font, Helvetica Neue, Helvetica, =
> sans-serif; color:rgba(0, 0, 0, 1.0);" class=3D""><b class=3D"">From: =
> </b></span><span style=3D"font-family: -webkit-system-font, Helvetica =
> Neue, Helvetica, sans-serif;" class=3D"">Mail Delivery Subsystem &lt;<a =
> href=3D"mailto:mailer-daemon@googlemail.com <mailto:mailer-daemon@googlemail.com>" =
> class=3D"">mailer-daemon@googlemail.com <mailto:mailer-daemon@googlemail.com></a>&gt;<br =
> class=3D""></span></div><div style=3D"margin-top: 0px; margin-right: =
> 0px; margin-bottom: 0px; margin-left: 0px;" class=3D""><span =
> style=3D"font-family: -webkit-system-font, Helvetica Neue, Helvetica, =
> sans-serif; color:rgba(0, 0, 0, 1.0);" class=3D""><b class=3D"">Subject: =
> </b></span><span style=3D"font-family: -webkit-system-font, Helvetica =
> Neue, Helvetica, sans-serif;" class=3D""><b class=3D"">Delivery Status =
> Notification (Failure)
> ----- Message truncated -----


[-- Attachment #2.1: Type: text/html, Size: 50191 bytes --]

[-- Attachment #2.2: icon.png --]
[-- Type: image/png, Size: 1450 bytes --]

^ permalink raw reply	[flat|nested] 48+ messages in thread

* bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout
  2020-03-27 16:28                                                                                       ` Yuan Fu
@ 2020-04-14  8:05                                                                                         ` martin rudalics
  0 siblings, 0 replies; 48+ messages in thread
From: martin rudalics @ 2020-04-14  8:05 UTC (permalink / raw)
  To: Yuan Fu; +Cc: 39181-done, Štěpán Němec

tags 39181 fixed
close 39181 28.1
quit

Hopefully marked as done now.

martin





^ permalink raw reply	[flat|nested] 48+ messages in thread

end of thread, other threads:[~2020-04-14  8:05 UTC | newest]

Thread overview: 48+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
     [not found] <5e950f28.1c69fb81.61726.5731.GMR@mx.google.com>
2020-01-18 20:57 ` bug#39181: 27.0.50; [PATCH] Allow users to store & restore gdb-mi layout Yuan Fu
2020-01-31 10:13   ` Eli Zaretskii
2020-02-02 14:22     ` Yuan Fu
2020-02-07 22:28       ` Yuan Fu
2020-02-10  4:56         ` Yuan Fu
2020-02-15  8:08           ` Eli Zaretskii
2020-02-15  9:55             ` martin rudalics
2020-02-15 10:19               ` Eli Zaretskii
2020-02-15 20:37                 ` Yuan Fu
2020-02-16 10:00                   ` martin rudalics
2020-03-03 23:41                     ` Yuan Fu
2020-03-04 13:28                       ` Fu Yuan
2020-03-05  6:12                         ` Yuan Fu
2020-03-05  9:14                           ` martin rudalics
2020-03-07 18:09                             ` Yuan Fu
2020-03-07 19:07                               ` Štěpán Němec
2020-03-07 19:17                                 ` Yuan Fu
2020-03-09  9:01                                   ` martin rudalics
2020-03-09 17:59                                     ` Yuan Fu
2020-03-09 19:18                                       ` Štěpán Němec
2020-03-09 20:17                                         ` Yuan Fu
2020-03-09 20:54                                           ` Štěpán Němec
2020-03-10  8:49                                           ` martin rudalics
2020-03-10 18:05                                             ` Fu Yuan
2020-03-11  8:52                                               ` martin rudalics
2020-03-11  9:26                                                 ` Štěpán Němec
2020-03-12  8:22                                                   ` martin rudalics
2020-03-12  8:49                                                     ` Štěpán Němec
2020-03-12 19:21                                                       ` Yuan Fu
2020-03-13 20:09                                                         ` Yuan Fu
     [not found]                                                           ` <87lfo4netg.fsf@gmail.com>
2020-03-13 21:13                                                             ` Štěpán Němec
2020-03-13 21:40                                                               ` Yuan Fu
2020-03-13 22:12                                                                 ` Štěpán Němec
2020-03-15 15:55                                                                 ` martin rudalics
2020-03-16  0:13                                                                   ` Yuan Fu
2020-03-16  9:24                                                                     ` martin rudalics
2020-03-20 20:03                                                                       ` Yuan Fu
2020-03-20 20:58                                                                         ` Štěpán Němec
2020-03-21 18:00                                                                           ` Yuan Fu
2020-03-21 18:39                                                                             ` Štěpán Němec
2020-03-21 21:03                                                                               ` Yuan Fu
2020-03-21 21:49                                                                                 ` Štěpán Němec
2020-03-24 16:14                                                                                   ` Yuan Fu
2020-03-27  9:01                                                                                     ` martin rudalics
2020-03-27 16:28                                                                                       ` Yuan Fu
2020-04-14  8:05                                                                                         ` martin rudalics
2020-03-10  8:48                                       ` martin rudalics
2020-04-14  1:17   ` bug#39181: Fwd: Delivery Status Notification (Failure) Yuan Fu

Code repositories for project(s) associated with this public inbox

	https://git.savannah.gnu.org/cgit/emacs.git

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).