unofficial mirror of emacs-devel@gnu.org 
 help / color / mirror / code / Atom feed
From: Robert Cochran <robert-emacs@cochranmail.com>
To: emacs-devel@gnu.org
Subject: [PATCH] tab-bar.el: add defcustoms for functions to call after opening and before closing
Date: Fri, 08 Nov 2019 11:57:05 -0800	[thread overview]
Message-ID: <87h83egmzy.fsf@cochranmail.com> (raw)

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

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


[-- Warning: decoded text below may be mangled, UTF-8 assumed --]
[-- Attachment #2: New variables for functions to call after opening/before closing a tab --]
[-- Type: text/x-patch, Size: 7563 bytes --]

From 696c7598d716a6c0bfb873b224e9c63c756dd45e Mon Sep 17 00:00:00 2001
From: Robert Cochran <robert-git@cochranmail.com>
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	other threads:[~2019-11-08 19:57 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2019-11-08 19:57 Robert Cochran [this message]
2019-11-10 20:42 ` [PATCH] tab-bar.el: add defcustoms for functions to call after opening and before closing Juri Linkov
2019-12-02 22:46   ` Robert Cochran
2019-12-03 23:19     ` Juri Linkov
2019-12-04 20:29       ` Robert Cochran
2019-12-04 22:45         ` Juri Linkov
2019-12-05 23:05           ` Robert Cochran
2019-12-07 22:25             ` Juri Linkov
2019-12-11 18:32               ` Robert Cochran

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://www.gnu.org/software/emacs/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=87h83egmzy.fsf@cochranmail.com \
    --to=robert-emacs@cochranmail.com \
    --cc=emacs-devel@gnu.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
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).