Tabs to show overlapping windows are becoming more common these days, especially in terminals, browsers, and chat programs. The idea is that a single window can contain several … buffers. Emacs already has this, and has had this for a long time. It's just that by default Emacs doesn't have visible tabs to show the buffers. XEmacs and SXEmacs can show tabs with “buffer tabs”; for GNU Emacs 21 you need to install TabBar mode (thanks to Jemima for finding this), which gives you tabs like this:

screenshot of tabbar-mode

Well, it doesn't look like that by default. The standard settings give each tab a 3d button appearance. I wanted something simpler, so I changed the settings:

;; Note: for tabbar 2.0 use 
;; tabbar-default not tabbar-default-face,
;; tabbar-selected not tabbar-selected-face,
;; tabbar-button not tabbar-button-face,
;; tabbar-separator not tabbar-separator-face
(set-face-attribute
 'tabbar-default-face nil
 :background "gray60")
(set-face-attribute
 'tabbar-unselected-face nil
 :background "gray85"
 :foreground "gray30"
 :box nil)
(set-face-attribute
 'tabbar-selected-face nil
 :background "#f2f2f6"
 :foreground "black"
 :box nil)
(set-face-attribute
 'tabbar-button-face nil
 :box '(:line-width 1 :color "gray72" :style released-button))
(set-face-attribute
 'tabbar-separator-face nil
 :height 0.7)

(tabbar-mode 1)
(define-key global-map [(alt j)] 'tabbar-backward)
(define-key global-map [(alt k)] 'tabbar-forward)

This makes the currently selected tab match my default background (#f2f2f6), removes the 3d borders, and adds a bit of space between the tabs. I also define Alt+J and Alt+K to switch tabs; I use the same keys in other tabbed apps, because they're easier to type than moving my hands to the arrow keys.

TabBar-mode looks neat, but I'm not sure how useful it will be. In Emacs I have lots of buffers—more than will fit as tabs. The main thing I like so far are the keys for cycling between related buffers, but as the number of buffers grows it becomes faster to switch directly to the buffer I want.

Edit: [2010-11-20] I like tabbar-mode but I also find myself using other buffer switching quite a bit. I'm using tabbar within a project, and ido-switch-buffer for moving between projects. I've changed the tabbar groups to show only buffers in the same directory:

(defun my-tabbar-buffer-groups (buffer)
  "Put files in the same directory into the same tab bar"
    (with-current-buffer (get-buffer buffer)
      (list (expand-file-name default-directory))))
(setq tabbar-buffer-groups-function 
      'my-tabbar-buffer-groups)

Update: [2017-09-24] I wanted to make the tabs look more like tabs in other apps, so I used powerline's "wave" separators with tabbar:

  (defvar my/tabbar-left "/" "Separator on left side of tab")
  (defvar my/tabbar-right "\\" "Separator on right side of tab")
  (defun my/tabbar-tab-label-function (tab)
    (powerline-render (list my/tabbar-left
                            (format " %s  " (car tab))
                            my/tabbar-right)))
  (require 'powerline)
  (setq my/tabbar-left (powerline-wave-right 'tabbar-default nil 24))
  (setq my/tabbar-right (powerline-wave-left nil 'tabbar-default 24))
  (tabbar-mode 1)
  (setq tabbar-tab-label-function #'my/tabbar-tab-label-function)))

Note that tabbar resets tabbar-tab-label-function after you run (tabbar-mode 1) so you need to make you set it after you've already activated tabbar-mode.

Labels:

27 comments:

Anonymous wrote at Tuesday, May 1, 2007 at 7:18:00 AM PDT

I think there are a couple of extra right-parens in there.

But this is really cool--I'm psyched you wrote about it.

Amit wrote at Tuesday, May 1, 2007 at 8:37:00 AM PDT

Yes, you're right -- I've fixed the parentheses. Thanks!

Anonymous wrote at Tuesday, May 1, 2007 at 2:23:00 PM PDT

Also be sure to check out the excellent IDO package, which as an example makes it so much easier to switch buffers (when you have many open).

Amit wrote at Tuesday, May 1, 2007 at 3:51:00 PM PDT

In addition to ido, I've also tried out iswitchb and isearchb. Isearchb is the most interesting in that it's like isearch, but for switching buffers. It switches buffers as you type, and you don't have to press RET to get out. I just haven't used it enough to get used to it yet.

All three of these are included with GNU Emacs 22.

Anonymous wrote at Wednesday, July 25, 2007 at 6:41:00 PM PDT

Just out of curiosity, how many buffers do you usually use? Ever since I've started using desktop, I don't think I've ever had below a hundred buffers open on my laptop - maybe fifty or so at work. It's a ridiculously large number for most editors, but with the buffer switch modes you went over, it's absolutely manageable. Is it typical, though?

Amit wrote at Wednesday, July 25, 2007 at 11:54:00 PM PDT

Although I use desktop, I tend to keep few buffers open at once (maybe 10–20), and use recent file history to reopen them. I'm not using tabbar right now; I'm experimenting with icicles. However I'm still not happy and continue to experiment.

Anonymous wrote at Wednesday, October 3, 2007 at 10:20:00 AM PDT

tabbar-mode has the concept of groups so you don't see all buffers at once.

What font are you using at the screenshot (in the buffer, not the tabbar)?

Amit wrote at Friday, October 5, 2007 at 8:46:00 PM PDT

I did try groups but eventually gave up on tabbar. I found it changed the behavior of killing buffers. Normally, if you're in A, then visit B, then kill B, you go back to A, but with tabbar, it would choose some other buffer. That was annoying enough that I stopped using tabbar.

The buffer font in the screenshot is Monaco (on the Mac). These days I use DejaVu Sans Mono.

Unknown wrote at Tuesday, April 8, 2008 at 6:21:00 AM PDT

Hi amit,

Iam using xemacs-21.4.14
I have options->display->buffers tab visible set
Even then my xemacs is not showing buffers, any comment.

u can reply to pradeep.buddy@gmail.com

ritesh wrote at Saturday, April 26, 2008 at 11:20:00 PM PDT

Is there a way to get tabs through the GUI, like you have in gvim, V.7 and up? It would be so much more convenient to have multiple tabs instead of multiple frames. The lisp-based solution mentioned here doesn't appeal to me too much :(
For sentimental reasons (!!), I build my apps (Emacs, Gvim, etc) with Motif. I'd looked at the motif code for gvim, but X-window programming is something i'm totally unfamiliar with. And, in Emacs's case, there's elisp on top of everything else.

Amit wrote at Sunday, April 27, 2008 at 9:20:00 AM PDT

Interactively, you can use M-x tabbar-mode. The Lisp I pasted was because I didn't like the defaults and wanted to customize it.

gvim's tabs look nicer because they're gtk tabs, whereas Emacs is more cross platform and has tabs on many different systems. Emacs is pretty old fashioned in this regard.

Saurabh wrote at Thursday, August 28, 2008 at 3:26:00 PM PDT

when i tried to load the library it gave this error.
No such face tabbar-seperator face
any idea what the problem is.

Saurabh wrote at Thursday, August 28, 2008 at 3:27:00 PM PDT

After I loaded the tabbar-library this is what i get.

No such face tabbar-seperator face

Amit wrote at Thursday, August 28, 2008 at 6:45:00 PM PDT

Saurabh, what version of tabbar-mode do you have? I posted this blog entry over a year ago so it's possible something has changed in a newer version of tabbar-mode. I have version 1.2, written in 2003 by David Ponce.

Tom wrote at Thursday, September 25, 2008 at 5:47:00 PM PDT

I had problems too, using tabbar 2.0. I think a fair amount has changed, shame the defaults suck :(

Anonymous wrote at Tuesday, October 7, 2008 at 7:13:00 AM PDT

I like your cosmetic changes. I think it's an improvement.

Anonymous wrote at Friday, November 21, 2008 at 7:30:00 AM PST

;; maybe you will find this usefull
;; it moves the current tab left/right rearranging the
;; tabs (as in konsole for example)
;; beware, this is my first elisp(lisp) hack
;; it probably looks terrible

(defun tabbar-move-tab (&optional right)
"Move current tab to the left or to the right
if RIGHT is set."
(let* ((ctabset nil)
(ctabs nil)
(ctab nil)
(hd nil)
(tl nil))
(and
(setq ctabset (tabbar-current-tabset 't))
(setq ctabs (tabbar-tabs ctabset))
(setq ctab (tabbar-selected-tab ctabset))
(setq tl ctabs)
(setq hd '())) ;; nil
(while (and (cdr tl) (not (eq ctab (car tl))) (not (eq ctab (cadr tl))))
(setq hd (append hd (list (car tl)))
tl (cdr tl)))
(set ctabset
(cond
((and (not right) (null hd) (eq ctab (car tl)))
(append (cdr tl) (list (car tl))))
((not right)
(append hd (list (cadr tl)) (list (car tl)) (cddr tl)))
((and right (not (cddr tl)))
(append (list (cadr tl)) hd (list (car tl))))
((and right (eq ctab (car tl)))
(append hd (list (cadr tl)) (list (car tl)) (cddr tl)))
(right
(append hd (list (car tl)) (list (caddr tl)) (list (cadr tl)) (cdddr tl)))
))
(put ctabset 'template nil)
(tabbar-display-update)))

(defun tabbar-move-tab-left ()
"Move tab left."
(interactive)
(tabbar-move-tab))

(defun tabbar-move-tab-right ()
"Move tab right."
(interactive)
(tabbar-move-tab))

宋时歌 wrote at Sunday, November 23, 2008 at 3:21:00 PM PST

Looks really cool, much better than the default. Thanks.

Shige

geekd wrote at Wednesday, July 22, 2009 at 11:33:00 AM PDT

If you are using tabbar 2.0, and Amit's code doesn't work for you, just remove the -face off of each of Amit's statements. For example:

'tabbar-default-face nil
becomes
'tabbar-default nil

and then it will work.

good luck!
-geekd

Anonymous wrote at Sunday, July 26, 2009 at 8:04:00 PM PDT

Thanks whocares. Works great with one minor change.
tabbar-move-tab-right should be:
(defun tabbar-move-tab-right ()
"Move tab right."
(interactive)
(tabbar-move-tab t))

Zach Hannes wrote at Tuesday, January 12, 2010 at 10:38:00 AM PST

Hi all, I don't know much lisp and I have two questions. 1) When I open a PHP file the tabbar disappears. How can I stop that? 2) How does one turn off tab grouping? With the exception of loading and requiring tabbar, the tabbar section of my .emacs looks exactly like Amit's above.

Zach Hannes wrote at Wednesday, January 13, 2010 at 1:34:00 PM PST

Fixed this by adding

(tabbar-local-mode 1)
(header-line-mode 0)

to my php-mode hook in my .emacs file.

My eye caught a relevant bit on this page http://www.emacswiki.org/emacs/TabBarMode which says tabbar-mode gets turned off if a frame loads a mode that uses the same space that tabbar-mode uses. One such mode might be the header-line-format, which pops up a small transparent overlay, duplicating the first line of code, in modes like php-mode.

3demax wrote at Tuesday, October 4, 2011 at 3:54:00 PM PDT

Hello, Amit!

Could you please exlain how do you make tabs look like in this picture?
http://www.emacswiki.org/pics/static/NyanModeWithCustomBackground.png

Trying to do somethin similar only gives me that
http://imgur.com/rUhWP.png

So the question is how did you add some padding around the buffer name? Because :box is not playing good

I'm using the following code in my emacs init file
;; Tabbar settings
(set-face-attribute
'tabbar-default nil
;; :background "gray60")
:background "#202020"
:box '(:line-width 1 :color "black" :style nil))
(set-face-attribute
'tabbar-unselected nil
;; :background "gray85"
:background "gray30"
:foreground "white"
:box '(:line-width 5 :color "gray30" :style nil))
(set-face-attribute
'tabbar-selected nil
;; :background "#f2f2f6"
:background "gray75"
:foreground "black"
:box '(:line-width 5 :color "gray75" :style nil))
(set-face-attribute
'tabbar-highlight nil
;; :background "#f2f2f6"
:background "white"
:foreground "black"
:underline nil
:box '(:line-width 5 :color "white" :style nil))
(set-face-attribute
'tabbar-button nil
;; :box '(:line-width 1 :color "gray72" :style released-button))
:box '(:line-width 0 :color "gray72" :style nil))
(set-face-attribute
'tabbar-separator nil
:height 1.0)

Amit Patel wrote at Tuesday, October 4, 2011 at 6:30:00 PM PDT

Hi 3demax, I can't produce the screenshot right now but you might try M-x customize-variable tabbar-separator ; it controls spacing between the tabs.

3demax wrote at Wednesday, October 5, 2011 at 8:06:00 AM PDT

Thanks, it helps. Not quite in a stright way (I decided to simply add spaces at the end of the tabs), but it helps avoid some glitches that appeared.

Now it looks like http://imgur.com/b0SNN
Code here https://gist.github.com/1264635

And also thanks for inspiration. I've never thought emacs could be tweaked that way:)

Amit wrote at Wednesday, October 5, 2011 at 11:20:00 AM PDT

3demax: Ah! That makes sense. This is of course making me want to experiment with tabbar some more :-)

Amit wrote at Wednesday, October 5, 2011 at 12:22:00 PM PDT

For tabbar 2.0, you can add spaces to the tab text with:

(setq tabbar-tab-label-function (lambda (tab) (format " %s " (car tab))))