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
next 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).