タブをカラフルにする(tabbar.el)

tabbar.el (ver 1.3) の改造,まさかの三日目です.上手くいくとこんな感じになります.

以下はバッファ名が*hoge*,hoge.el,hoge.texとなるとき,あるいはdired-modeのときに色を変える例です.

Faces

faceは条件ごとにunselected,selectedの2つが必要になります,面倒なのでマクロを組んで定義します.

(defmacro my-tabbar-define-unselected-face (name &rest attributes)
  "Utility function to create the face `my-tabbar-unselected-NAME-face'."
  (declare (indent 1))
  `(defface ,(intern (format "my-tabbar-unselected-%s-face" name))
     '((t (:inherit tabbar-unselected-face ,@attributes)))
     nil :group 'tabbar))

(defmacro my-tabbar-define-selected-face (name &rest attributes)
  "Utility function to create the face `my-tabbar-selected-NAME-face'."
  (declare (indent 1))
  `(defface ,(intern (format "my-tabbar-selected-%s-face" name))
     '((t (:inherit tabbar-selected-face ,@attributes)))
     nil :group 'tabbar))

(my-tabbar-define-unselected-face buffer
  :background "navy"
  :foreground "gray")

(my-tabbar-define-selected-face buffer
  :background "navy"
  :foreground "gold")

(my-tabbar-define-unselected-face elisp
  :background "firebrick"
  :foreground "gray")

(my-tabbar-define-selected-face elisp
  :background "firebrick"
  :foreground "gold")

(my-tabbar-define-unselected-face tex
  :background "darkgreen"
  :foreground "gray")

(my-tabbar-define-selected-face tex
  :background "darkgreen"
  :foreground "gold")

(my-tabbar-define-unselected-face dired
  :background "purple"
  :foreground "gray")

(my-tabbar-define-selected-face dired
  :background "purple"
  :foreground "gold")

Conditions

タブを受け取り,判別式を通してfaceを返します.一番下が本体の判別式です.

;; tab => conditions => face

(require 'cl)

(defun buffer-local-value-safe (variable &optional buffer-or-name)
  "Return the value of VARIABLE in a current buffer (or in the buffer
BUFFER-OR-NAME). Or else, return nil."
  (when (and (symbolp variable)
             (boundp variable))
    (let ((buf (cond ((null buffer-or-name)
                      (current-buffer))
                     ((stringp buffer-or-name)
                      (get-buffer buffer-or-name))
                     ((member buffer-or-name (buffer-list))
                      buffer-or-name)
                     (t
                      nil))))
      (when buf
        (buffer-local-value variable buf)))))

(defun get-major-mode (&optional buffer-or-name)
  "Return a major-mode of current buffer (or the buffer BUFFER-OR-NAME)."
  (buffer-local-value-safe 'major-mode buffer-or-name))

(defun dired-mode-p (&optional buffer-or-name)
  "Return non-nil if a current buffer (or the buffer BUFFER-OR-NAME) is
opened as `dired-mode'. Otherwise, return nil."
  (if (eq (get-major-mode buffer-or-name) 'dired-mode) t nil))

(defvar my-tabbar-facesets
  '((buffer my-tabbar-selected-buffer-face my-tabbar-unselected-buffer-face)
    (elisp  my-tabbar-selected-elisp-face  my-tabbar-unselected-elisp-face)
    (tex    my-tabbar-selected-tex-face    my-tabbar-unselected-tex-face)
    (dired  my-tabbar-selected-dired-face  my-tabbar-unselected-dired-face)
    (etc    tabbar-selected-face           tabbar-unselected-face))
  "AList of symbols for `tabbar-mode'. Each list must be the form
\(KEY SELECTED-FACE UNSELECTED-FACE).")

(defun my-tabbar-select-face-fn (tab tabset)
  "Select face by buffer name or major-mode, then return selected one."
  (let ((name (car tab))
        suffix faceset)
    (labels ((whichface (key)
                        (setq faceset (cdr (assq key my-tabbar-facesets)))
                        (if (tabbar-selected-p tab tabset)
                            (car faceset)
                          (cadr faceset)))
             (dired-etc ()
                        (if (dired-mode-p name)
                            (whichface 'dired)
                          (whichface 'etc))))
      (cond
       ((string-match "^\\*.*\\*$" name)
        (whichface 'buffer))
       ((string-match "\\.\\([^.]+\\)$" name)
        (setq suffix (match-string-no-properties 1 name))
          (cond ((string= suffix "el")
                 (whichface 'elisp))
                ((string= suffix "tex")
                 (whichface 'tex))
                (t
                 (dired-etc))))
       (t
        (dired-etc))))))

;;; 2010/11/29 Bug Fix
;;; 上のコードだとメンテが面倒なのでmy-tabbar-facesetsとmy-tabbar-select-face-fnを次のように変えてください.
;;; my-tabbar-facesetsを編集するだけで設定可能になります.
;;;
;;; ちなみに設定の仕方はこんな感じ:
;;;
;;; (push '(foo-mode nil my-tabbar-selected-foo-face my-tabbar-unselected-foo-face) my-tabbar-facesets)
;;;

(defvar my-tabbar-facesets
  '((nil             "^\\*.*\\*$" my-tabbar-selected-buffer-face my-tabbar-unselected-buffer-face) ; *foo*
    (emacs-lisp-mode nil          my-tabbar-selected-elisp-face  my-tabbar-unselected-elisp-face)
    (latex-mode      nil          my-tabbar-selected-tex-face    my-tabbar-unselected-tex-face)
    (dired-mode      nil          my-tabbar-selected-dired-face  my-tabbar-unselected-dired-face))
  "AList for `tabbar-mode'. Each list must be the form
\(MAJOR-MODE BUFFER-NAME-RE SELECTED-FACE UNSELECTED-FACE).")

(defun my-tabbar-select-face-fn (tab tabset)
  "Select face by `my-tabbar-facesets', then return selected one."
  (let* ((name (car tab))
         (mode (get-major-mode name))
         (facesets my-tabbar-facesets)
         (defaults '(tabbar-selected-face tabbar-unselected-face))
         (faceset (if (eq mode 'dired-mode)
                      (or (cddr (assq 'dired-mode facesets)) defaults)
                    (loop for (m re f1 f2) in facesets
                          when (or (eq mode m)
                                   (and (stringp re)
                                        (string-match re name)))
                          return (list f1 f2)
                          finally return defaults))))

    (if (tabbar-selected-p tab tabset)
        (car faceset)
      (cadr faceset))))

Overwriting

最後に今作った判別式をheader-line-formatを返す関数に噛ませるわけですが,そういったオプションが用意されていないようなので,tabbar-line-elementという関数を上書きしてしまいます.defaliasの行をコメントアウトするともとに戻ります.

(defun my-tabbar-line-element (tab)
  "Redefined `tabbar-line-element' for using the function
`my-tabbar-select-face-fn'. Original docstring is below.\n
Return an `header-line-format' template element from TAB.
Call `tabbar-tab-label-function' to obtain a label for TAB."
  (let* ((keymap (make-sparse-keymap))
         (qtab   (list 'quote tab))
         (select (tabbar-make-select-tab-command qtab))
         (help   (tabbar-make-help-on-tab-function qtab))
         (label  (if tabbar-tab-label-function
                     (funcall tabbar-tab-label-function tab)
                   tab)))
    ;; Call `tabbar-select-tab-function' on mouse events.
    (define-key keymap [header-line down-mouse-1] 'ignore)
    (define-key keymap [header-line mouse-1] select)
    (define-key keymap [header-line down-mouse-2] 'ignore)
    (define-key keymap [header-line mouse-2] select)
    (define-key keymap [header-line down-mouse-3] 'ignore)
    (define-key keymap [header-line mouse-3] select)
    ;; Return the tab followed by a separator.
    (list (propertize label 'local-map keymap 'help-echo help
                      'face (my-tabbar-select-face-fn ; <= modified
                             tab (tabbar-current-tabset)))
          tabbar-separator-value)))

(defalias 'tabbar-line-element 'my-tabbar-line-element)

以上です.

Memo

ソースはだいたいこんな感じになってる.

(let* ((tabset (tabbar-current-tabset t))    ; => Buffers
       (tab    (tabbar-selected-tab tabset)) ; => ("*scratch*" . Buffers)
       (label  (if tabbar-tab-label-function
                   (funcall tabbar-tab-label-function tab)
                 tab)))
  (insert label))                       ; => " *scratch* "