;;; syd-tabs.el -*- lexical-binding: t; -*-

;; Many themes neglect tab-bar customisation, leaving it completely unstyled.
;; `vim-tab-bar' will style it to look like Vim's tab-bar, but more importantly,
;; it will colour it to match the current theme.
(use-package vim-tab-bar
  :commands vim-tab-bar-mode)

(with-eval-after-load 'tab-bar
  (setq tab-bar-new-tab-to 'rightmost)

  (defun syd-tab-bar-make-free-name ()
    (cl-loop for i from 1
             with candidate = nil
             do (setq candidate (format "#%d" i))
             while (tab-bar--tab-index-by-name candidate)
             finally return candidate))

  (defun syd-tab-bar-rename-tab (tab new-name)
    ;; HACK: Using `tab-bar-rename-tab' doesn't work, for some reason.  This
    ;; relies on the internal representation of tabs.
    (setf (alist-get 'name tab) new-name
          (alist-get 'explicit-name tab) t))

  (defvar syd-tab-bar-name-function #'syd-tab-bar-make-free-name
    "Nullary function expected to return the name for a new tab.")

  (defvar syd-tab-bar-default-buffer-function #'scratch-buffer)

  (syd-add-hook 'tab-bar-tab-post-open-functions
    (defun syd-tab-bar--name-new-tab-h (tab)
      "Hooks to `tab-bar-tab-post-open-functions' such that new tabs will be
given \"explicit names\" that are static."
      (syd-tab-bar-rename-tab tab (funcall syd-tab-bar-name-function))))

  (defun syd-tab-bar--show-tab-bar-h (_tab)
    "Show the tab-bar if it is not already visible.  See
`syd-tab-bar-hide-tab-bar-h'."
    (unless vim-tab-bar-mode
      (vim-tab-bar-mode 1)))

  (defun syd-tab-bar-new-tab ()
    (interactive)
    (let ((tab-bar-tab-post-open-functions
           (-cons* (lambda (_tab)
                     (switch-to-buffer
                      (funcall syd-tab-bar-default-buffer-function)))
                   #'syd-tab-bar--show-tab-bar-h
                   tab-bar-tab-post-open-functions)))
      (tab-bar-new-tab)))

  (syd-add-hook 'tab-bar-tab-pre-close-functions
    (defun syd-tab-bar-hide-tab-bar-h (_tab _final-tab-p)
      "Hide the tab-bar when there is only a single tab to show.  See
`syd-tab-bar--show-tab-bar-h'."
      (when (<= (length (tab-bar-tabs)) 2) ; The tab hasn't yet been removed.
        (tab-bar-mode -1))))

  (defun syd-tab-bar-new-named-tab (name)
    (interactive (list (read-string "Tab name: ")))
    ;; Relies on `syd-tab-bar--name-new-tab-h'.
    (let ((syd-tab-bar-name-function (lambda () name)))
      (syd-tab-bar-new-tab)))

  (general-def
    :prefix-map 'syd-leader-tab-map
    "[" #'tab-previous
    "]" #'tab-next
    "r" #'tab-rename
    "R" #'tab-rename
    "n" #'syd-tab-bar-new-tab
    "N" #'syd-tab-bar-new-named-tab
    "d" #'tab-close)
  (general-def
    :keymaps 'syd-leader-map
    "TAB" `("Tabs" . ,syd-leader-tab-map)
    "<tab>" `("Tabs" . ,syd-leader-tab-map)))

(provide 'syd-tabs)
