[PATCH] tab-bar.el: add defcustoms for functions to call after opening and before closing

classic Classic list List threaded Threaded
2 messages Options
Reply | Threaded
Open this post in threaded view
|

[PATCH] tab-bar.el: add defcustoms for functions to call after opening and before closing

Robert Cochran
Hi all,

A rough version of the patch we were talking about in the other thread
on persistent naming. To recap:

New defcustoms, tab-bar-post-open-functions and
tab-bar-pre-close-functions. Call all of the function in
post-open-functions at the end of tab-bar-new-tab-to, passing in the new
tab as an argument. Similar for pre-close-functions, but slightly more
complicated to lend some expressive power. Each of the functions take 2
arguments, the tab itself, and a flag indicating whether or not it's the
last tab. The big thing is that the return values are kept track of, and
if *any* of the functions return a non-nil value
(ie `(cl-some (mapcar (lambda (x) (funcall x tab last-tab))) pre-close-functions)`
is non-nil), it short-circuits and doesn't run the rest of
tab-bar-close-tab. This would be useful to provide to users so
that they can do other clean-up tasks when the tab is closed, as well as
providing a way to protect a tab from being closed. Ditto for being able
to do some tab set-up when you open a new tab.

This is going to take some additional work. I couldn't think of a clean
way to gracefully provide a way to easily make permanent changes to the
tab for the purposes of tab-bar-post-open-functions - the functions
within have to grovel through the tabs frame parameter and write back a value
themselves. I personally don't find this an optimal user experience, but
feedback on that point is welcome.

This will also need to be documented in the manual, but I can do that
after we hash out some details - I just didn't want to make changes to
the manual until we're satisfied with the semantics of this whole thing.

Patch follows. Thoughts on it appreciated.

Thanks,
--
~Robert Cochran

GPG Fingerprint - BD0C 5F8B 381C 64F0 F3CE  E7B9 EC9A 872C 41B2 77C2


From 696c7598d716a6c0bfb873b224e9c63c756dd45e Mon Sep 17 00:00:00 2001
From: Robert Cochran <[hidden email]>
Date: Fri, 8 Nov 2019 11:29:43 -0800
Subject: [PATCH] Add variables for functions to call after tab open and before
 close

* lisp/tab-bar.el (tab-bar-tab-post-open-functions,
tab-bar-tab-pre-close-functions): New defcustoms
(tab-bar-new-tab-to, tab-bar-close-tab): Use new defcustoms
---
 lisp/tab-bar.el | 127 +++++++++++++++++++++++++++++++-----------------
 1 file changed, 82 insertions(+), 45 deletions(-)

diff --git a/lisp/tab-bar.el b/lisp/tab-bar.el
index e915930c27..eac785abaf 100644
--- a/lisp/tab-bar.el
+++ b/lisp/tab-bar.el
@@ -659,6 +659,15 @@ tab-bar-new-tab-to
   :group 'tab-bar
   :version "27.1")
 
+(defcustom tab-bar-tab-post-open-functions nil
+  "List of functions to call after creating a new tab.
+The current tab is supplied as an argument. Any modifications
+made to the tab need to be stored to the frame's parameter
+alist - see `frame-parameter' and `set-frame-parameter'."
+  :type '(repeat function)
+  :group 'tab-bar
+  :version "27.1")
+
 (defun tab-bar-new-tab-to (&optional to-index)
   "Add a new tab at the absolute position TO-INDEX.
 TO-INDEX counts from 1.  If no TO-INDEX is specified, then add
@@ -697,6 +706,13 @@ tab-bar-new-tab-to
         ;; pushnew handles the head of tabs but not frame-parameter
         (set-frame-parameter nil 'tabs tabs)))
 
+    ;;; FIXME: Ideally, one should be able to change attributes of the
+    ;;; tab without having to write it back via `set-frame-parameter'
+    ;;; or cousins. A possible solution is to provide tab attribute
+    ;;; accessors.
+    (dolist (fun tab-bar-tab-post-open-functions)
+      (funcall fun (tab-bar--tab)))
+
     (when (and (not tab-bar-mode)
                (or (eq tab-bar-show t)
                    (and (natnump tab-bar-show)
@@ -747,6 +763,17 @@ tab-bar-close-last-tab-choice
   :group 'tab-bar
   :version "27.1")
 
+(defcustom tab-bar-tab-pre-close-functions nil
+  "List of functions to call before closing a tab.
+The current tab and a boolean indicating whether or not it is the
+only tab in the frame are supplied as arguments, respectively.
+Each function's returned value is used to determine whether or
+not the tab will be closed. If any of the functions return
+non-nil, then the tab will not be closed."
+  :type '(repeat function)
+  :group 'tab-bar
+  :version "27.1")
+
 (defun tab-bar-close-tab (&optional arg to-index)
   "Close the tab specified by its absolute position ARG.
 If no ARG is specified, then close the current tab and switch
@@ -759,52 +786,62 @@ tab-bar-close-tab
   (interactive "P")
   (let* ((tabs (funcall tab-bar-tabs-function))
          (current-index (tab-bar--current-tab-index tabs))
-         (close-index (if (integerp arg) (1- arg) current-index)))
-    (if (= 1 (length tabs))
-        (pcase tab-bar-close-last-tab-choice
-          ('nil
-           (signal 'user-error '("Attempt to delete the sole tab in a frame")))
-          ('delete-frame
-           (delete-frame))
-          ('tab-bar-mode-disable
-           (tab-bar-mode -1))
-          ((pred functionp)
-           ;; Give the handler function the full extent of the tab's
-           ;; data, not just it's name and explicit-name flag.
-           (funcall tab-bar-close-last-tab-choice (tab-bar--tab))))
-
-      ;; More than one tab still open
-      (when (eq current-index close-index)
-        ;; Select another tab before deleting the current tab
-        (let ((to-index (or (if to-index (1- to-index))
-                            (pcase tab-bar-close-tab-select
-                              ('left (1- current-index))
-                              ('right (if (> (length tabs) (1+ current-index))
-                                          (1+ current-index)
-                                        (1- current-index)))
-                              ('recent (tab-bar--tab-index-recent 1 tabs))))))
-          (setq to-index (max 0 (min (or to-index 0) (1- (length tabs)))))
-          (tab-bar-select-tab (1+ to-index))
-          ;; Re-read tabs after selecting another tab
-          (setq tabs (funcall tab-bar-tabs-function))))
-
-      (let ((close-tab (nth close-index tabs)))
-        (push `((frame . ,(selected-frame))
-                (index . ,close-index)
-                (tab . ,(if (eq (car close-tab) 'current-tab)
-                            (tab-bar--tab)
-                          close-tab)))
-              tab-bar-closed-tabs)
-        (set-frame-parameter nil 'tabs (delq close-tab tabs)))
-
-      (when (and tab-bar-mode
-                 (and (natnump tab-bar-show)
-                      (<= (length tabs) tab-bar-show)))
-        (tab-bar-mode -1))
+         (close-index (if (integerp arg) (1- arg) current-index))
+         prevent-close)
+
+    (dolist (fun tab-bar-tab-pre-close-functions)
+      (let ((function-value (funcall fun
+                                     (nth close-index tabs)
+                                     (= 1 (length tabs)))))
+        ;; Make non-nil return values 'stick'
+        (setf prevent-close (or prevent-close function-value))))
+
+    (unless prevent-close
+      (if (= 1 (length tabs))
+          (pcase tab-bar-close-last-tab-choice
+            ('nil
+             (signal 'user-error '("Attempt to delete the sole tab in a frame")))
+            ('delete-frame
+             (delete-frame))
+            ('tab-bar-mode-disable
+             (tab-bar-mode -1))
+            ((pred functionp)
+             ;; Give the handler function the full extent of the tab's
+             ;; data, not just it's name and explicit-name flag.
+             (funcall tab-bar-close-last-tab-choice (tab-bar--tab))))
+
+        ;; More than one tab still open
+        (when (eq current-index close-index)
+          ;; Select another tab before deleting the current tab
+          (let ((to-index (or (if to-index (1- to-index))
+                              (pcase tab-bar-close-tab-select
+                                ('left (1- current-index))
+                                ('right (if (> (length tabs) (1+ current-index))
+                                            (1+ current-index)
+                                          (1- current-index)))
+                                ('recent (tab-bar--tab-index-recent 1 tabs))))))
+            (setq to-index (max 0 (min (or to-index 0) (1- (length tabs)))))
+            (tab-bar-select-tab (1+ to-index))
+            ;; Re-read tabs after selecting another tab
+            (setq tabs (funcall tab-bar-tabs-function))))
+
+        (let ((close-tab (nth close-index tabs)))
+          (push `((frame . ,(selected-frame))
+                  (index . ,close-index)
+                  (tab . ,(if (eq (car close-tab) 'current-tab)
+                              (tab-bar--tab)
+                            close-tab)))
+                tab-bar-closed-tabs)
+          (set-frame-parameter nil 'tabs (delq close-tab tabs)))
+
+        (when (and tab-bar-mode
+                   (and (natnump tab-bar-show)
+                        (<= (length tabs) tab-bar-show)))
+          (tab-bar-mode -1))
 
-      (force-mode-line-update)
-      (unless tab-bar-mode
-        (message "Deleted tab and switched to %s" tab-bar-close-tab-select)))))
+        (force-mode-line-update)
+        (unless tab-bar-mode
+          (message "Deleted tab and switched to %s" tab-bar-close-tab-select))))))
 
 (defun tab-bar-close-tab-by-name (name)
   "Close the tab by NAME."
--
2.23.0

Reply | Threaded
Open this post in threaded view
|

Re: [PATCH] tab-bar.el: add defcustoms for functions to call after opening and before closing

Juri Linkov-2
> New defcustoms, tab-bar-post-open-functions and
> tab-bar-pre-close-functions. Call all of the function in
> post-open-functions at the end of tab-bar-new-tab-to, passing in the new
> tab as an argument. Similar for pre-close-functions, but slightly more
> complicated to lend some expressive power. Each of the functions take 2
> arguments, the tab itself, and a flag indicating whether or not it's the
> last tab. The big thing is that the return values are kept track of, and
> if *any* of the functions return a non-nil value
> (ie `(cl-some (mapcar (lambda (x) (funcall x tab last-tab))) pre-close-functions)`
> is non-nil), it short-circuits and doesn't run the rest of
> tab-bar-close-tab.

Also similar to run-hook-with-args-until-success.

> This would be useful to provide to users so that they can do other
> clean-up tasks when the tab is closed, as well as providing a way to
> protect a tab from being closed. Ditto for being able to do some tab
> set-up when you open a new tab.

Please bring up some examples of usages for these options because
without examples it's hard to understand how these options are
intended to be used, e.g. how to rename a new tab after creating,
or do a clean-up task on closing, or prevent from closing, etc.

> This is going to take some additional work. I couldn't think of a clean
> way to gracefully provide a way to easily make permanent changes to the
> tab for the purposes of tab-bar-post-open-functions - the functions
> within have to grovel through the tabs frame parameter and write back a value
> themselves. I personally don't find this an optimal user experience, but
> feedback on that point is welcome.

But you provide a new tab as a parameter to tab-bar-post-open-functions.
Could it be used to make changes in a new tab?

> Patch follows.

Thanks.