Monday, October 29, 2018

Back in 2007 I posted that I was going to try buffer tabs for Emacs. At the time I wasn't sure if I'd like it. People told me that there's no point in displaying them visually like in other editors, that Emacs had better ways of switching buffers. I ended up abandoning Tab Bar mode. But some time later, I started using it again, and I found the new version worked really well for my needs.

This post is mostly about the visual customization I use. I make the current tab blue, the other tabs gray, and the background dark gray. I use powerline to make the tabs look more like tabs:

( require ' tabbar ) ( customize-set-variable 'tabbar-background-color "gray20" ) ( customize-set-variable 'tabbar-separator ' ( 0.5 )) ( customize-set-variable 'tabbar-use-images nil ) ( tabbar-mode 1 ) ( define-key global-map [( alt j )] 'tabbar-backward ) ( define-key global-map [( alt k )] 'tabbar-forward ) ( set-face-attribute 'tabbar-default nil :background "gray20" :foreground "gray60" :distant-foreground "gray50" :family "Helvetica Neue" :box nil ) ( set-face-attribute 'tabbar-unselected nil :background "gray80" :foreground "black" :box nil ) ( set-face-attribute 'tabbar-modified nil :foreground "red4" :box nil :inherit 'tabbar-unselected ) ( set-face-attribute 'tabbar-selected nil :background "#4090c0" :foreground "white" :box nil ) ( set-face-attribute 'tabbar-selected-modified nil :inherit 'tabbar-selected :foreground "GoldenRod2" :box nil ) ( set-face-attribute 'tabbar-button nil :box nil )

To make the tabs look nicer I use powerline. The way powerline is written, I need to make sure I set the tabbar colors before I run powerline-wave-left . The way tabbar.el is written, it resets the label function tabbar-bar-label-function when you run (tabbar-mode 1) , so I need to set it after I start tabbar mode:

( require ' powerline ) ( defvar my/tabbar-height 20 ) ( defvar my/tabbar-left ( powerline-wave-right 'tabbar-default nil my/tabbar-height )) ( defvar my/tabbar-right ( powerline-wave-left nil 'tabbar-default my/tabbar-height )) ( defun my/tabbar-tab-label-function ( tab ) ( powerline-render ( list my/tabbar-left ( format " %s " ( car tab )) my/tabbar-right ))) ( setq tabbar-tab-label-function #'my/tabbar-tab-label-function )

In addition to the visual changes, I also want to limit the buffers that show up. Instead of displaying all buffers, I display only the ones from the current project. I have my own project code (from before projectile and project.el existed) but this is a generic version of what I use, based on code from EmacsWiki:

( defun my/tabbar-buffer-groups () ( cond (( member ( buffer-name ) ' ( "*Completions*" "*scratch*" "*Messages*" "*Ediff Registry*" )) ( list "#hide" )) ( t ( list ( or ( cdr ( project-current )) ( expand-file-name default-directory )))))) ( setq tabbar-buffer-groups-function #'my/tabbar-buffer-groups )

I also decided to keep the tabs sorted, using code from EmacsWiki:

( defun tabbar-add-tab ( tabset object &optional _append_ignored ) "Add to TABSET a tab with value OBJECT if there isn't one there yet. If the tab is added, it is added at the beginning of the tab list, unless the optional argument APPEND is non-nil, in which case it is added at the end." ( let (( tabs ( tabbar-tabs tabset ))) ( if ( tabbar-get-tab object tabset ) tabs ( let (( tab ( tabbar-make-tab object tabset ))) ( tabbar-set-template tabset nil ) ( set tabset ( sort ( cons tab tabs ) ( lambda ( a b ) ( string< ( buffer-name ( car a )) ( buffer-name ( car b ))))))))))

I have other customizations too but they're rather specific to my needs. The main one is tab color customization, which you can see in my post about spaceline.

I tried testing this code on a minimal setup. Let me know if you have trouble with it!

[2019-03-09] Also check out awesome-tab and centaur-tabs as alternatives to tabbar.

Labels: emacs, howto