214 lines
7.7 KiB
EmacsLisp
214 lines
7.7 KiB
EmacsLisp
;;; +tab-bar.el -*- lexical-binding: t; -*-
|
||
|
||
;;; Commentary:
|
||
|
||
;; Emacs 28 comes with an easy-to-use `tab-bar-format' option, but I still use
|
||
;; Emacs 27 on my Windows machine. Thus, the code in this file.
|
||
|
||
;;; Code:
|
||
|
||
(require 'tab-bar)
|
||
|
||
(defface +tab-bar-extra
|
||
'((t :inherit (tab-bar font-lock-comment-face)))
|
||
"Tab bar face for extra information, like the menu-bar and time."
|
||
:group 'basic-faces)
|
||
|
||
|
||
;; Common
|
||
|
||
(defun +tab-bar-misc-info ()
|
||
"Display `mode-line-misc-info', formatted for the tab-bar."
|
||
`((global menu-item ,(string-trim-right
|
||
(format-mode-line mode-line-misc-info))
|
||
|
||
ignore :help (discord-date-string))))
|
||
|
||
(defvar +tab-bar-show-original nil
|
||
"Original value of `tab-bar-show'.")
|
||
|
||
(defun +tab-bar-basename ()
|
||
"Generate the tab name from the basename of the buffer of the
|
||
selected window."
|
||
(let* ((tab-file-name (buffer-file-name (window-buffer
|
||
(minibuffer-selected-window)))))
|
||
(concat " "
|
||
(if tab-file-name
|
||
(file-name-nondirectory tab-file-name)
|
||
(+tab-bar-tab-name-truncated-left)))))
|
||
|
||
;;; FIXME this doesn't work...
|
||
;; (defvar +tab-bar-tab-min-width 8
|
||
;; "Minimum width of a tab on the tab bar.")
|
||
|
||
;; (defvar +tab-bar-tab-max-width 24
|
||
;; "Maximum width of a tab on the tab bar.")
|
||
|
||
;; (defun +tab-bar-fluid-calculate-width ()
|
||
;; "Calculate the width of each tab in the tab-bar."
|
||
;; (let* ((tab-bar-list (cdr (tab-bar-make-keymap-1)))
|
||
;; (tab-bar-avail-width (frame-width))
|
||
;; (tab-bar-tab-count (length (tab-bar-tabs)))
|
||
;; (tab-bar-close-button-char-width 1)
|
||
;; (tab-bar-add-tab-button-char-width 1)
|
||
;; (tab-bar-total-width
|
||
;; (length (mapconcat
|
||
;; (lambda (el)
|
||
;; (when-let ((str (car-safe (cdr-safe (cdr-safe el)))))
|
||
;; (substring-no-properties (eval str))))
|
||
;; tab-bar-list)))
|
||
;; (tab-bar-total-tab-width
|
||
;; (+ (* tab-bar-tab-count tab-bar-close-button-char-width)
|
||
;; tab-bar-add-tab-button-char-width
|
||
;; (length (mapconcat
|
||
;; (lambda (el)
|
||
;; (substring-no-properties (alist-get 'name el)))
|
||
;; (tab-bar-tabs)))))
|
||
;; (tab-bar-total-nontab-width (- tab-bar-total-width
|
||
;; tab-bar-total-tab-width)))
|
||
;; (min +tab-bar-tab-max-width
|
||
;; (max +tab-bar-tab-min-width
|
||
;; (/ (- tab-bar-avail-width
|
||
;; tab-bar-total-tab-width
|
||
;; tab-bar-total-nontab-width)
|
||
;; tab-bar-tab-count)))))
|
||
|
||
;; (defun +tab-bar-fluid-width ()
|
||
;; "Generate the tab name to fluidly fit in the given space."
|
||
;; (let* ((tab-file-name (buffer-file-name (window-buffer
|
||
;; (minibuffer-selected-window)))))
|
||
;; (format (format " %%s%%%ds" (+tab-bar-fluid-calculate-width))
|
||
;; (if tab-file-name
|
||
;; (file-name-nondirectory tab-file-name)
|
||
;; (+tab-bar-tab-name-truncated-left))
|
||
;; " ")))
|
||
|
||
(defun +tab-bar-tab-name-truncated-left ()
|
||
"Generate the tab name from the buffer of the selected window.
|
||
This is just like `tab-bar-tab-name-truncated', but truncates the
|
||
name to the left."
|
||
(let* ((tab-name (buffer-name (window-buffer (minibuffer-selected-window))))
|
||
(ellipsis (cond
|
||
(tab-bar-tab-name-ellipsis)
|
||
((char-displayable-p ?…) "…")
|
||
("...")))
|
||
(l-ell (length ellipsis))
|
||
(l-name (length tab-name)))
|
||
(if (< (length tab-name) tab-bar-tab-name-truncated-max)
|
||
tab-name
|
||
(propertize (concat
|
||
(when (> (+ l-name l-ell) tab-bar-tab-name-truncated-max)
|
||
ellipsis)
|
||
(truncate-string-to-width tab-name l-name
|
||
(max 0 (- l-name tab-bar-tab-name-truncated-max l-ell))))
|
||
'help-echo tab-name))))
|
||
|
||
|
||
;;; Menu bar
|
||
;; stole from https://github.com/emacs-mirror/emacs/blob/master/lisp/tab-bar.el
|
||
|
||
(defun +tab-bar-menu-bar (event)
|
||
"Pop up the same menu as displayed by the menu bar.
|
||
Used by `tab-bar-format-menu-bar'."
|
||
(interactive "e")
|
||
(let ((menu (make-sparse-keymap (propertize "Menu Bar" 'hide t))))
|
||
(run-hooks 'activate-menubar-hook 'menu-bar-update-hook)
|
||
(map-keymap (lambda (key binding)
|
||
(when (consp binding)
|
||
(define-key-after menu (vector key)
|
||
(copy-sequence binding))))
|
||
(menu-bar-keymap))
|
||
(popup-menu menu event)))
|
||
|
||
(defun +tab-bar-format-menu-bar ()
|
||
"Produce the Menu button for the tab bar that shows the menu bar."
|
||
`((menu-bar menu-item (propertize " Ɛ " 'face '+tab-bar-extra)
|
||
+tab-bar-menu-bar :help "Menu Bar")))
|
||
|
||
|
||
;;; Tab bar format tabs
|
||
|
||
(require 'el-patch)
|
||
(el-patch-feature tab-bar)
|
||
(with-eval-after-load 'tab-bar
|
||
(el-patch-defun tab-bar--format-tab (tab i)
|
||
"Format TAB using its index I and return the result as a keymap."
|
||
(append
|
||
(el-patch-remove
|
||
`((,(intern (format "sep-%i" i)) menu-item ,(tab-bar-separator) ignore)))
|
||
(cond
|
||
((eq (car tab) 'current-tab)
|
||
`((current-tab
|
||
menu-item
|
||
,(funcall tab-bar-tab-name-format-function tab i)
|
||
ignore
|
||
:help "Current tab")))
|
||
(t
|
||
`((,(intern (format "tab-%i" i))
|
||
menu-item
|
||
,(funcall tab-bar-tab-name-format-function tab i)
|
||
,(alist-get 'binding tab)
|
||
:help "Click to visit tab"))))
|
||
(when (alist-get 'close-binding tab)
|
||
`((,(if (eq (car tab) 'current-tab) 'C-current-tab (intern (format "C-tab-%i" i)))
|
||
menu-item ""
|
||
,(alist-get 'close-binding tab)))))))
|
||
|
||
|
||
;; Emacs 27
|
||
|
||
(defun +tab-bar-misc-info-27 (output &rest _)
|
||
"Display `mode-line-misc-info' in the `tab-bar' on Emacs 27.
|
||
This is :filter-return advice for `tab-bar-make-keymap-1'."
|
||
(let* ((reserve (length (format-mode-line mode-line-misc-info)))
|
||
(str (propertize " "
|
||
'display `(space :align-to (- right (- 0 right-margin)
|
||
,reserve)))))
|
||
(prog1 (append output
|
||
`((align-right menu-item ,str nil))
|
||
(+tab-bar-misc-info)))))
|
||
|
||
|
||
;; Emacs 28
|
||
|
||
(defvar +tab-bar-format-original nil
|
||
"Original value of `tab-bar-format'.")
|
||
|
||
(defun +tab-bar-misc-info-28 ()
|
||
"Display `mode-line-misc-info', right-aligned, on Emacs 28."
|
||
(append (unless (memq 'tab-bar-format-align-right tab-bar-format)
|
||
'(tab-bar-format-align-right))
|
||
'(+tab-bar-misc-info)))
|
||
|
||
|
||
|
||
(define-minor-mode +tab-bar-misc-info-mode
|
||
"Show the `mode-line-misc-info' in the `tab-bar'."
|
||
:lighter ""
|
||
:global t
|
||
(if +tab-bar-misc-info-mode
|
||
(progn ; Enable
|
||
(setq +tab-bar-show-original tab-bar-show)
|
||
(cond
|
||
((boundp 'tab-bar-format) ; Emacs 28
|
||
(setq +tab-bar-format-original tab-bar-format)
|
||
(unless (memq '+tab-bar-misc-info tab-bar-format)
|
||
(setq tab-bar-format
|
||
(append tab-bar-format (+tab-bar-misc-info-28)))))
|
||
((fboundp 'tab-bar-make-keymap-1) ; Emacs 27
|
||
(advice-add 'tab-bar-make-keymap-1 :filter-return
|
||
'+tab-bar-misc-info-27)))
|
||
(setq tab-bar-show t))
|
||
(progn ; Disable
|
||
(setq tab-bar-show +tab-bar-show-original)
|
||
(cond
|
||
((boundp 'tab-bar-format) ; Emacs 28
|
||
(setq tab-bar-format +tab-bar-format-original))
|
||
((fboundp 'tab-bar-make-keymap-1) ; Emacs 27
|
||
(advice-remove 'tab-bar-make-keymap-1 '+tab-bar-misc-info-27))))))
|
||
|
||
|
||
|
||
(provide '+tab-bar)
|
||
;;; +tab-bar.el ends here
|