emacs-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

feature/native-comp e57d5a7 5/5: Merge remote-tracking branch 'savannah/


From: Andrea Corallo
Subject: feature/native-comp e57d5a7 5/5: Merge remote-tracking branch 'savannah/master' into HEAD
Date: Tue, 17 Mar 2020 04:28:54 -0400 (EDT)

branch: feature/native-comp
commit e57d5a71ba765bbd225974b3d61ecd9d80f73220
Merge: 159f61b 9dccaf8
Author: Andrea Corallo <address@hidden>
Commit: Andrea Corallo <address@hidden>

    Merge remote-tracking branch 'savannah/master' into HEAD
---
 lisp/progmodes/gdb-mi.el | 293 ++++++++++++++++++++++++++++++++++++++++-------
 lisp/window.el           |  18 +++
 src/alloc.c              |  22 ++--
 3 files changed, 285 insertions(+), 48 deletions(-)

diff --git a/lisp/progmodes/gdb-mi.el b/lisp/progmodes/gdb-mi.el
index c262232..ea3b1b8 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,27 @@ Possible values are these symbols:
               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 window configuration as of before GDB started.
+
+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 +626,25 @@ Also display the main routine in the disassembly buffer if 
present."
   :group 'gdb
   :version "22.1")
 
+(defcustom gdb-window-configuration-directory user-emacs-directory
+  "Directory where GDB 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, load this window configuration (layout) on startup.
+This should be the full name of the window configuration file.
+If this 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 +803,12 @@ detailed description of this mode.
     (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)
 
@@ -4494,6 +4542,26 @@ SPLIT-HORIZONTAL and show BUF in the new window."
   (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))
@@ -4532,9 +4600,6 @@ SPLIT-HORIZONTAL and show BUF in the new window."
     '(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]
@@ -4609,41 +4674,172 @@ window is dedicated."
   (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 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 is not 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.
@@ -4661,7 +4857,12 @@ of the debugged program.  Non-nil means display the 
layout shown for
 
 (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)
@@ -4708,11 +4909,25 @@ Kills the gdb buffers, and resets variables and the 
source buffers."
   (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 fc1e7d4..b54f163 100644
--- a/lisp/window.el
+++ b/lisp/window.el
@@ -278,6 +278,24 @@ displays the buffer specified by BUFFER-OR-NAME before 
running BODY."
             (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
diff --git a/src/alloc.c b/src/alloc.c
index ac17307..db0646c 100644
--- a/src/alloc.c
+++ b/src/alloc.c
@@ -4604,21 +4604,23 @@ live_vector_p (struct mem_node *m, void *p)
   return !NILP (live_vector_holding (m, p));
 }
 
-/* If P is a pointer into a live buffer, return the buffer.
-   Otherwise, return nil.  M is a pointer to the mem_block for P.  */
+/* If P is a pointer into a valid buffer object, return the buffer.
+   Otherwise, return nil.  M is a pointer to the mem_block for P.
+   If IGNORE_KILLED is non-zero, treat killed buffers as invalid.  */
 
 static Lisp_Object
-live_buffer_holding (struct mem_node *m, void *p)
+live_buffer_holding (struct mem_node *m, void *p, bool ignore_killed)
 {
-  /* P must point into the block, and the buffer
-     must not have been killed.  */
+  /* P must point into the block, and the buffer must not
+     have been killed unless ALL-BUFFERS is true.  */
   if (m->type == MEM_TYPE_BUFFER)
     {
       struct buffer *b = m->start;
       char *cb = m->start;
       char *cp = p;
       ptrdiff_t offset = cp - cb;
-      if (0 <= offset && offset < sizeof *b && !NILP (b->name_))
+      if (0 <= offset && offset < sizeof *b
+         && !(ignore_killed && NILP (b->name_)))
        {
          Lisp_Object obj;
          XSETBUFFER (obj, b);
@@ -4628,10 +4630,12 @@ live_buffer_holding (struct mem_node *m, void *p)
   return Qnil;
 }
 
+/* If P is a pointer into a live (valid and not killed) buffer object,
+   return non-zero.  */
 static bool
 live_buffer_p (struct mem_node *m, void *p)
 {
-  return !NILP (live_buffer_holding (m, p));
+  return !NILP (live_buffer_holding (m, p, true));
 }
 
 /* Mark OBJ if we can prove it's a Lisp_Object.  */
@@ -4689,7 +4693,7 @@ mark_maybe_object (Lisp_Object obj)
 
        case Lisp_Vectorlike:
          mark_p = (EQ (obj, live_vector_holding (m, po))
-                   || EQ (obj, live_buffer_holding (m, po)));
+                   || EQ (obj, live_buffer_holding (m, po, false)));
          break;
 
        default:
@@ -4759,7 +4763,7 @@ mark_maybe_pointer (void *p)
          break;
 
        case MEM_TYPE_BUFFER:
-         obj = live_buffer_holding (m, p);
+         obj = live_buffer_holding (m, p, false);
          break;
 
        case MEM_TYPE_CONS:



reply via email to

[Prev in Thread] Current Thread [Next in Thread]