Back in 2018 I posted about making tabbar.el look pretty. Emacs 27 includes two built-in ways to display tabs:

  1. tab-bar-mode works per frame to show window configurations
  2. tab-line-mode works per window to show buffers

Tab-line mode seems similar to tabbar.el, so I decided to switch from tabbar.el to tab-line.

Screenshot of emacs 27 tab-line-mode
Emacs 27 tab-line mode

I had customized tabbar.el to show only the buffers from the current project, sorted alphabetically. It's even easier with tab-line: tab-line-tabs-function can be set to group mode, which uses tab-line-tabs-buffer-group-function to choose the group name and tab-line-tabs-buffer-group-sort-function to sort the buffers in the group.

(require 's)
(defun my/tab-line-buffer-group (buffer)
  "Use the project.el name for the buffer group"
  (with-current-buffer buffer
    (s-chop-suffix "/" (car (project-roots (project-current))))))

(defun my/buffer-sort (a b) (string< (buffer-name a) (buffer-name b)))
(setq tab-line-tabs-buffer-group-sort-function #'my/buffer-sort)
(setq tab-line-tabs-buffer-group-function #'my/tab-line-buffer-group)
(setq tab-line-tabs-function #'tab-line-tabs-buffer-groups)

Next, I wanted to make the tabs look nicer. As before, I used powerline for this. I disabled the extra buttons by setting tab-line-new-button-show and tab-line-close-button-show to nil, then set tab-line-tab-name-function to a function that adds powerline wave symbols to both sides of the tab name:

(require 'powerline)
(defvar my/tab-height 22)
(defvar my/tab-left (powerline-wave-right 'tab-line nil my/tab-height))
(defvar my/tab-right (powerline-wave-left nil 'tab-line my/tab-height))

(defun my/tab-line-tab-name-buffer (buffer &optional _buffers)
  (powerline-render (list my/tab-left
                          (format " %s  " (buffer-name buffer))
(setq tab-line-tab-name-function #'my/tab-line-tab-name-buffer)
(setq tab-line-new-button-show nil)
(setq tab-line-close-button-show nil)

I used a color scheme that's consistent with my modeline:

(set-face-attribute 'tab-line nil ;; background behind tabs
      :background "gray40"
      :foreground "gray60" :distant-foreground "gray50"
      :family "Fira Sans Condensed" :height 1.0 :box nil)
(set-face-attribute 'tab-line-tab nil ;; active tab in another window
      :inherit 'tab-line
      :foreground "gray70" :background "gray90" :box nil)
(set-face-attribute 'tab-line-tab-current nil ;; active tab in current window
      :background (hsl 300 0.4 0.5) :foreground "white" :box nil)
(set-face-attribute 'tab-line-tab-inactive nil ;; inactive tab
      :background "gray80" :foreground "black" :box nil)
(set-face-attribute 'tab-line-highlight nil ;; mouseover
      :background "white" :foreground 'unspecified)

Finally, I want to use keys to navigate the tabs. I bound H-j to tab-line-switch-to-prev-tab and H-k to tab-line-switch-to-next-tab. I use these same keys for terminals and web browsers.

I'm pretty happy with tab-line mode so far.


1 comment:

Amit wrote at Sunday, July 5, 2020 at 12:37:00 PM PDT

One thing that confuses me is that tab-line-tab-current is supposed to be for the active window but I often find the active window has the tab-line-tab color. For now I'm going to set these two to the same.