;; -*- lexical-binding: t -*- (defmacro tab-ex/save-tab-excursion (&rest body) (declare (indent defun)) (let ((old-tab-sym (gensym))) `(let ((,old-tab-sym (tab-bar--current-tab-index))) (unwind-protect (progn ,@body) (tab-bar-select-tab (1+ ,old-tab-sym)))))) (defmacro tab-ex/with-current-tab (new-tab &rest body) (declare (indent 1)) `(tab-ex/save-tab-excursion (tab-bar-select-tab (1+ (tab-bar--tab-index ,new-tab))) ;; No need to wrap body in a progn here; we're already in a progn in the ;; parent macro. ,@body)) (defun tab-ex/tab-buffers (tab) ;; Windows aren't usable after exiting `tab-ex/with-current-tab's ;; scope, so you have to extract the buffers out of them *then*, ;; otherwise they go stale (likely as a result of them no longer ;; being 'visible') and you can't pull the buffers out of them ;; anymore (delete-dups (if (eq (car tab) 'current-tab) (mapcar #'window-buffer (window-list-1 (frame-first-window) 'nomini)) (tab-ex/with-current-tab tab (mapcar #'window-buffer (window-list-1 (frame-first-window) 'nomini)))))) (defun tab-ex/gud-tab-explicit-name (tab) "Rename tab and enable gud-many-buffers when tab is made with the gud core buffer. When using GUD in many-buffers mode, rename the tab to something static so that it's easy to tell at a glance what the tab contains despite the various buffer names." (let ((tab-buffers (tab-ex/tab-buffers tab))) (dolist (buf tab-buffers) (with-current-buffer buf (when (and (eq major-mode 'gud-mode)) ;; It's probably better to have a way to just have gud make ;; a new tab and always have many windows on, but this is ;; just an example. (gdb-many-windows) (save-match-data (string-match "\\*gud-\\(.*\\)\\*" (buffer-name)) (setf (alist-get 'name tab) (format "GUD: %s" (match-string 1 (buffer-name))) (alist-get 'explicit-name tab) t))))))) (defun tab-ex/tab-has-unsaved-files (tab _lastp) ;; Don't let the user close a tab if they have unsaved buffers, to ;; make sure they don't lose track of unsaved buffers, for ;; example. In this case, it doesn't matter if this tab is the last ;; one or not. (cl-loop for buffer in (tab-ex/tab-buffers tab) always (and (with-current-buffer buffer buffer-file-name) (not (buffer-modified-p buffer))))) (defun tab-ex/kill-non-file-buffers-in-tab (tab lastp) ;; Don't kill the buffers if the tab is going to still be visible in ;; the end of it. (unless lastp (tab-ex/with-current-tab tab ;; Just kill all the visible non-file buffers, for the sake of example. (mapc #'kill-buffer (cl-loop for buffer in (tab-ex/tab-buffers tab) unless (with-current-buffer buffer buffer-file-name) collect buffer))))) (add-hook 'tab-bar-tab-post-open-functions #'tab-ex/gud-tab-explicit-name) (add-hook 'tab-bar-tab-prevent-close-functions #'tab-ex/tab-has-unsaved-files) (add-hook 'tab-bar-tab-pre-close-functions #'tab-ex/kill-non-file-buffers-in-tab)