branch: externals/bufferlo
commit 6435cf220905ecc9120980b958a79b2433fd90df
Author: Florian Rommel <m...@florommel.de>
Commit: Florian Rommel <m...@florommel.de>

    Automatic bookmark saving on frame deletion and closing tabs
    
    Replaces bufferlo-delete-frame-kill-buffers-save-bookmark-prompt
    and bufferlo-close-tab-kill-buffers-save-bookmark-prompt
    by bufferlo-bookmark-frame-save-on-delete
    and bufferlo-bookmark-tab-save-on-close.
---
 bufferlo.el | 244 ++++++++++++++++++++++++++++++++++++++++--------------------
 1 file changed, 163 insertions(+), 81 deletions(-)

diff --git a/bufferlo.el b/bufferlo.el
index 15153be828..d9bc8ec81c 100644
--- a/bufferlo.el
+++ b/bufferlo.el
@@ -167,22 +167,64 @@ when it was last saved."
                 (const :tag "Make a new frame and restore its geometry" 
restore-geometry)
                 (const :tag "Reuse the current frame" nil)))
 
-(defcustom bufferlo-delete-frame-kill-buffers-save-bookmark-prompt nil
-  "If non-nil, offer to save bookmark before killing the frame and buffers."
-  :type 'boolean)
-
 (defcustom bufferlo-delete-frame-kill-buffers-prompt nil
   "If non-nil, confirm before deleting the frame and killing buffers."
   :type 'boolean)
 
-(defcustom bufferlo-close-tab-kill-buffers-save-bookmark-prompt nil
-  "If non-nil, offer to save bookmark before closing the tab and killing 
buffers."
-  :type 'boolean)
-
 (defcustom bufferlo-close-tab-kill-buffers-prompt nil
   "If non-nil, confirm before closing the tab and killing buffers."
   :type 'boolean)
 
+(defcustom bufferlo-bookmark-frame-save-on-delete nil
+  "Control automatically saving the frame bookmark on frame deletion.
+
+nil does not save the frame bookmark when deleting the frame.
+
+t always saves the frame bookmark when deleting the frame.
+Prompts for a new bookmark name if the frame is not associated with a bookmark.
+
+\\='when-bookmarked saves the bookmark only if the frame is already associated
+with a current bookmark.
+
+\\='on-kill-buffers behaves like t but only for the function
+`bufferlo-delete-frame-kill-buffers'.
+
+\\='on-kill-buffers-when-bookmarked behaves like \\='when-bookmarked but only
+for `bufferlo-delete-frame-kill-buffers'."
+  :type '(radio (const :tag "Do not save" nil)
+                (const :tag "Always save" t)
+                (const :tag "Only if the frame is already associated with a 
bookmark"
+                       when-bookmarked)
+                (const :tag "Only if killing buffers"
+                       on-kill-buffers)
+                (const :tag "Only if killing buffers and associated with a 
bookmark"
+                       on-kill-buffers-when-bookmarked)))
+
+(defcustom bufferlo-bookmark-tab-save-on-close nil
+  "Control automatically saving the tab bookmark on tab deletion.
+
+nil does not save the tab bookmark when closing the tab.
+
+t always saves the tab bookmark when closing the tab.
+Prompts for a new bookmark name if the tab is not associated with a bookmark.
+
+\\='when-bookmarked saves the bookmark only if the tab is already associated 
with
+a current bookmark.
+
+\\='on-kill-buffers behaves like t but only for the function
+`bufferlo-tab-close-kill-buffers'.
+
+\\='on-kill-buffers-when-bookmarked behaves like \\='when-bookmarked but only
+for `bufferlo-tab-close-kill-buffers'."
+  :type '(radio (const :tag "Do not save" nil)
+                (const :tag "Always save" t)
+                (const :tag "Only if the tab is already associated with a 
bookmark"
+                       when-bookmarked)
+                (const :tag "Only if killing buffers"
+                       on-kill-buffers)
+                (const :tag "Only if killing buffers and associated with a 
bookmark"
+                       on-kill-buffers-when-bookmarked)))
+
 (defcustom bufferlo-bookmark-frame-load-policy 'prompt
   "Control loading a frame bookmark into a already-bookmarked frame.
 
@@ -734,6 +776,11 @@ string, FACE is the face for STR."
         (when (and (not bufferlo--command-line-noload)
                    (not (eq bufferlo-bookmarks-load-at-emacs-startup 'noload)))
           (add-hook 'window-setup-hook #'bufferlo--bookmarks-load-startup))
+        ;; Save bookmark on close-tab and delete-frame
+        (add-hook 'tab-bar-tab-pre-close-functions
+                  #'bufferlo-bookmark--tab-save-on-close)
+        (add-hook 'delete-frame-functions
+                  #'bufferlo-bookmark--frame-save-on-delete)
         ;; bookmark advice
         (advice-add 'bookmark-rename :around 
#'bufferlo--bookmark-rename-advice)
         (advice-add 'bookmark-delete :around 
#'bufferlo--bookmark-delete-advice)
@@ -769,6 +816,11 @@ string, FACE is the face for STR."
     (remove-hook 'kill-emacs-hook #'bufferlo--bookmarks-save-at-emacs-exit)
     ;; load bookmarks at startup option
     (remove-hook 'window-setup-hook #'bufferlo-bookmarks-load)
+    ;; Save bookmark on close-tab and delete-frame
+    (remove-hook 'tab-bar-tab-pre-close-functions
+                 #'bufferlo-bookmark--tab-save-on-close)
+    (remove-hook 'delete-frame-functions
+                 #'bufferlo-bookmark--frame-save-on-delete)
     ;; bookmark advice
     (advice-remove 'bookmark-rename #'bufferlo--bookmark-rename-advice)
     (advice-remove 'bookmark-delete #'bufferlo--bookmark-delete-advice)))
@@ -1285,24 +1337,23 @@ Ignores buffers whose names start with a space, unless 
optional
 argument INTERNAL-TOO is non-nil."
   (interactive "P")
   (bufferlo--warn)
-  (let ((kill t))
-    (when bufferlo-kill-buffers-prompt
-      (setq kill (y-or-n-p "Kill bufferlo local buffers? ")))
-    (when kill
-      (let* ((exclude (bufferlo--merge-regexp-list
-                       (append '("a^") bufferlo-kill-buffers-exclude-filters)))
-             (kill-list (if killall
-                            (bufferlo--get-buffers frame tabnum)
-                          (bufferlo--get-exclusive-buffers frame tabnum)))
-             (buffers (seq-filter
-                       (lambda (b)
-                         (not (and
-                               ;; (or internal-too (/= (aref (buffer-name b) 
0) ?\s)) ; NOTE: this can cause null reference errors
-                               (or internal-too (not (string-prefix-p " " 
(buffer-name b))))
-                               (string-match-p exclude (buffer-name b)))))
-                       kill-list)))
-        (dolist (b buffers)
-          (bufferlo--kill-buffer b))))))
+  (when (or (not bufferlo-kill-buffers-prompt)
+            (y-or-n-p "Kill bufferlo local buffers? "))
+    (let* ((exclude (bufferlo--merge-regexp-list
+                     (append '("a^") bufferlo-kill-buffers-exclude-filters)))
+           (kill-list (if killall
+                          (bufferlo--get-buffers frame tabnum)
+                        (bufferlo--get-exclusive-buffers frame tabnum)))
+           (buffers (seq-filter
+                     (lambda (b)
+                       (not (and
+                             ;; (or internal-too (/= (aref (buffer-name b) 0) 
?\s)) ; NOTE: this can cause null reference errors
+                             (or internal-too
+                                 (not (string-prefix-p " " (buffer-name b))))
+                             (string-match-p exclude (buffer-name b)))))
+                     kill-list)))
+      (dolist (b buffers)
+        (bufferlo--kill-buffer b)))))
 
 (defun bufferlo-kill-orphan-buffers (&optional internal-too)
   "Kill all buffers that are not in any local list of a frame or tab.
@@ -1311,21 +1362,20 @@ argument INTERNAL-TOO is non-nil.
 Buffers matching `bufferlo-kill-buffers-exclude-filters' are never killed."
   (interactive)
   (bufferlo--warn)
-  (let ((kill t))
-    (when bufferlo-kill-buffers-prompt
-      (setq kill (y-or-n-p "Kill bufferlo orphan buffers? ")))
-    (when kill
-      (let* ((exclude (bufferlo--merge-regexp-list
-                       (append '("a^") bufferlo-kill-buffers-exclude-filters)))
-             (buffers (seq-filter
-                       (lambda (b)
-                         (not (and
-                               ;; (or internal-too (/= (aref (buffer-name b) 
0) ?\s)) ; NOTE: this can cause null reference errors
-                               (or internal-too (not (string-prefix-p " " 
(buffer-name b))))
-                               (string-match-p exclude (buffer-name b)))))
-                       (bufferlo--get-orphan-buffers))))
-        (dolist (b buffers)
-          (bufferlo--kill-buffer b))))))
+  (when (or (not bufferlo-kill-buffers-prompt)
+            (y-or-n-p "Kill bufferlo local buffers? "))
+    (let* ((exclude (bufferlo--merge-regexp-list
+                     (append '("a^") bufferlo-kill-buffers-exclude-filters)))
+           (buffers (seq-filter
+                     (lambda (b)
+                       (not (and
+                             ;; (or internal-too (/= (aref (buffer-name b) 0) 
?\s)) ; NOTE: this can cause null reference errors
+                             (or internal-too
+                                 (not (string-prefix-p " " (buffer-name b))))
+                             (string-match-p exclude (buffer-name b)))))
+                     (bufferlo--get-orphan-buffers))))
+      (dolist (b buffers)
+        (bufferlo--kill-buffer b)))))
 
 (defun bufferlo-delete-frame-kill-buffers (&optional frame internal-too)
   "Delete a frame and kill the local buffers of its tabs.
@@ -1334,31 +1384,35 @@ Ignores buffers whose names start with a space, unless 
optional
 argument INTERNAL-TOO is non-nil."
   (interactive)
   (bufferlo--warn)
-  (let ((kill t)
-        (frame (or frame (selected-frame))))
-    (let ((fbm (frame-parameter frame 'bufferlo-bookmark-frame-name)))
-      (when (and fbm
-                 bufferlo-delete-frame-kill-buffers-save-bookmark-prompt)
-        (when (y-or-n-p
-               (concat "Save frame bookmark \"" fbm "\"? "))
-          (with-selected-frame frame ; needed if called in a batch
-            (bufferlo-bookmark-frame-save-current))))
-      (when bufferlo-delete-frame-kill-buffers-prompt
-        (setq kill (y-or-n-p "Kill frame and its buffers? ")))
-      (when kill
-        ;; If batch, raise frame in case of prompts for buffers that need 
saving.
-        (raise-frame frame)
-        ;; kill-buffer calls replace-buffer-in-windows which will
-        ;; delete windows *and* their frame so we have to test if
-        ;; the frame in question is still live.
-        (bufferlo-kill-buffers nil frame 'all internal-too)
-        (when (frame-live-p frame)
-          ;; TODO: Emacs 30 frame-deletable-p
-          ;; account for top-level, non-child frames
-          (when (= 1 (length (seq-filter
-                              (lambda (x) (null (frame-parameter x 
'parent-frame)))
-                              (frame-list))))
-            (make-frame)) ; leave one for the user
+  (setq frame (or frame (selected-frame)))
+  (when (or (not bufferlo-delete-frame-kill-buffers-prompt)
+            (y-or-n-p "Kill frame and its buffers? "))
+    (let ((fbm (frame-parameter frame 'bufferlo-bookmark-frame-name))
+          (save-as-current (lambda (frame)
+                             ;; We need this if called in a batch
+                             (with-selected-frame frame
+                               (bufferlo-bookmark-frame-save-current)))))
+      (pcase bufferlo-bookmark-frame-save-on-delete
+        ((or 't 'on-kill-buffers)
+         (when (y-or-n-p (concat "Save frame bookmark \"" fbm "\"? "))
+           (funcall save-as-current frame)))
+        ((or 'when-bookmarked 'on-kill-buffers-when-bookmarked)
+         (when fbm (funcall save-as-current frame))))
+      ;; If batch, raise frame in case of prompts for buffers that need saving.
+      (raise-frame frame)
+      (let ((bufferlo-kill-buffers-prompt nil))
+        (bufferlo-kill-buffers nil frame 'all internal-too))
+      ;; kill-buffer calls replace-buffer-in-windows which will
+      ;; delete windows *and* their frame so we have to test if
+      ;; the frame in question is still live.
+      (when (frame-live-p frame)
+        ;; TODO: Emacs 30 frame-deletable-p
+        ;; account for top-level, non-child frames
+        (when (= 1 (length (seq-filter
+                            (lambda (x) (null (frame-parameter x 
'parent-frame)))
+                            (frame-list))))
+          (make-frame)) ; leave one for the user
+        (let ((bufferlo-bookmark-frame-save-on-delete nil))
           (delete-frame frame))))))
 
 (defun bufferlo-tab-close-kill-buffers (&optional killall internal-too)
@@ -1367,19 +1421,20 @@ The optional arguments KILLALL and INTERNAL-TOO are 
passed to
 `bufferlo-kill-buffers'."
   (interactive "P")
   (bufferlo--warn)
-  (let ((kill t)
-        (tbm (alist-get 'bufferlo-bookmark-tab-name 
(tab-bar--current-tab-find))))
-    (when (and tbm
-               bufferlo-close-tab-kill-buffers-save-bookmark-prompt)
-      (when (y-or-n-p
-             (concat "Save tab bookmark \"" tbm "\"? "))
-        (bufferlo-bookmark-tab-save-current)))
-    (when bufferlo-close-tab-kill-buffers-prompt
-      (setq kill (y-or-n-p "Kill tab and its buffers? ")))
-    (when kill
-      (bufferlo-kill-buffers killall nil nil internal-too)
-      (let ((tab-bar-close-last-tab-choice 'delete-frame))
-        (ignore-errors (tab-bar-close-tab)))))) ; catch errors in case this is 
the last tab on the last frame
+  (when (or (not bufferlo-close-tab-kill-buffers-prompt)
+            (y-or-n-p "Kill tab and its buffers? "))
+    (let ((tbm (alist-get 'bufferlo-bookmark-tab-name 
(tab-bar--current-tab-find))))
+      (pcase bufferlo-bookmark-tab-save-on-close
+        ((or 't 'on-kill-buffers)
+         (when (y-or-n-p (concat "Save tab bookmark \"" tbm "\"? "))
+           (bufferlo-bookmark-tab-save-current)))
+        ((or 'when-bookmarked 'on-kill-buffers-when-bookmarked)
+         (when tbm (bufferlo-bookmark-tab-save tbm))))
+      (let ((bufferlo-kill-buffers-prompt nil))
+        (bufferlo-kill-buffers killall nil nil internal-too))
+      (let ((bufferlo-bookmark-tab-save-on-close nil))
+        ;; Catch errors in case this is the last tab on the last frame
+        (ignore-errors (tab-bar-close-tab))))))
 
 (defun bufferlo-isolate-project (&optional file-buffers-only)
   "Isolate a project in the frame or tab.
@@ -2940,6 +2995,33 @@ Duplicate bookmarks are handled according to
               (push abm-name abm-names-to-save))))
         (bufferlo--bookmarks-save abm-names-to-save abms)))))
 
+(defun bufferlo-bookmark--frame-save-on-delete (frame)
+  "`frame-delete' advice for saving the current frame bookmark on deletion.
+FRAME is the frame being deleted."
+  (let ((fbm (frame-parameter frame 'bufferlo-bookmark-frame-name)))
+    (pcase bufferlo-bookmark-frame-save-on-delete
+      ('t
+       (when (y-or-n-p (if fbm
+                           (concat "Save frame bookmark \"" fbm "\"? ")
+                         "Save new frame bookmark? "))
+         (bufferlo-bookmark-frame-save-current)))
+      ('when-bookmarked
+       (when fbm (bufferlo--bookmark-frame-save fbm))))))
+
+(defun bufferlo-bookmark--tab-save-on-close (tab _only)
+  "Function for saving the current tab bookmark on deletion.
+Intended as a hook function for `tab-bar-tab-pre-close-functions'.
+TAB is the tab being closed. _ONLY is for compatibility with the hook."
+  (let ((tbm (alist-get 'bufferlo-bookmark-tab-name tab)))
+    (pcase bufferlo-bookmark-tab-save-on-close
+      ('t
+       (when (y-or-n-p (if tbm
+                           (concat "Save tab bookmark \"" tbm "\"? ")
+                         "Save new tab bookmark? "))
+         (bufferlo-bookmark-tab-save-current)))
+      ('when-bookmarked
+       (when tbm (bufferlo--bookmark-tab-save tbm))))))
+
 (defun bufferlo--bookmarks-save-at-emacs-exit ()
   "Save bufferlo bookmarks at Emacs exit.
 This honors `bufferlo-bookmarks-save-at-emacs-exit' by predicate or
@@ -3102,7 +3184,7 @@ which defaults to all frames, if not specified."
           (raise-frame) ; if called in a batch, raise frame in case of prompts 
for buffers that need saving
           (tab-bar-select-tab abm-tab-number)
           (let ((bufferlo-kill-buffers-prompt nil)
-                (bufferlo-close-tab-kill-buffers-save-bookmark-prompt nil)
+                (bufferlo-bookmark-tab-save-on-close nil)
                 (bufferlo-close-tab-kill-buffers-prompt nil))
             (bufferlo-tab-close-kill-buffers)))
         (when (frame-live-p orig-frame)
@@ -3111,7 +3193,7 @@ which defaults to all frames, if not specified."
       (let ((abm-frame (alist-get 'frame (cadr abm))))
         (with-selected-frame abm-frame
           (let ((bufferlo-kill-buffers-prompt nil)
-                (bufferlo-delete-frame-kill-buffers-save-bookmark-prompt nil)
+                (bufferlo-bookmark-frame-save-on-delete nil)
                 (bufferlo-delete-frame-kill-buffers-prompt nil))
             (bufferlo-delete-frame-kill-buffers)))))
     ;; Frame and/or tab could now be gone.

Reply via email to