elscreenのタブをフレームタイトルに入れる


更新

2016-12-15: なぜか画面が一時的に乱れる不具合があるようだったので、 frame-title-format に指定する式を簡単にできるように、タブの名前を格納する変数を適宜フックで更新するように変更しました。

はじめに

elscreenというのはemacsのフレームとウィンドウの間に中間レイヤーを入れて画面の管理をしやすくするパッケージです。ターミナルでいうところのGNU screenやtmuxのようなものです。 elscreen-toggle-display-tab でタブを表示させられるのですが、ずっと出しているとじゃま、しまっておくと肝心なときに出すのが面倒という感じです。Emacs Wikiで、 How to display the list of screens on the frame-title of my Emacs? という記事を発見したので、ちょっと改造して設定してみました。ちなみにもとのハックは get-alistalist-get に変更しないと、emacs 25では動きませんでした。

結果

こんな感じになります。見やすいかというとかなり微妙ですが、じゃまにはならず、見ようと思えば見れるという、ほどよい感じかも。

設定の概要

  • elscreenのタブ名を適当な長さに調節してならべる。
  • アクティブなタブを数字のまわりの括弧で区別する。
  • これをelscreenのフックで適宜更新して変数に格納しておく。
  • これをフレームタイトルに適宜評価されるダイナミックな式として設定する(ファイルのフルパスも貼りつける)。

設定

私のelscreen全体の設定を貼っておきます。*Use frame-title for tabs*から下が実際のハックです。フォーマットは適宜変更するとよいでしょう。

;;;
;;; ELSCREEN-RELATED
;;
;;; elscreen.el
;; https://github.com/knu/elscreen
;; http://www.emacswiki.org/emacs/EmacsLispScreen
;; http://rubikitch.com/2014/09/05/elscreen/
(use-package elscreen
  :init
  ;; Do not set a prefix (conflict with helm)
  (setq elscreen-prefix-key (kbd "C-;"))
  ;;
  :config
  ;; Key configs
  ;; (global-set-key (kbd "A-1") 'elscreen-previous)
  ;; (global-set-key (kbd "A-2") 'elscreen-next)
  ;; ;; Cloning is more useful than fresh creation
  ;; (global-set-key (kbd "A-c") 'elscreen-clone)
  ;; (global-set-key (kbd "A-k") 'elscreen-kill)
  ;; (global-set-key (kbd "A-r") 'elscreen-screen-nickname)
  (global-set-key (kbd "C-; t") 'elscreen-toggle-display-tab)
  (global-set-key (kbd "C-; l") 'helm-elscreen)
  (global-set-key (kbd "C-; h") 'helm-elscreen)
  ;;
  ;; Do not show tabls to save space
  ;; Can use M-x elscreen-toggle-display-tab
  (setq elscreen-display-tab nil)
  ;; No preceding [X] for closing
  (setq elscreen-tab-display-kill-screen nil)
  ;; No [<->] at the beginning
  (setq elscreen-tab-display-control nil)
  ;;
  ;; buffer-dependent naming scheme
  (setq elscreen-buffer-to-nickname-alist
        '(("^dired-mode$" .
           (lambda ()
             (format "Dired(%s)" dired-directory)))
          ("^Info-mode$" .
           (lambda ()
             (format "Info(%s)" (file-name-nondirectory Info-current-file))))
          ("^mew-draft-mode$" .
           (lambda ()
             (format "Mew(%s)" (buffer-name (current-buffer)))))
          ("^mew-" . "Mew")
          ("^irchat-" . "IRChat")
          ("^liece-" . "Liece")
          ("^lookup-" . "Lookup")))
  ;;
  (setq elscreen-mode-to-nickname-alist
        '(("[Ss]hell" . "shell")
          ("compilation" . "compile")
          ("-telnet" . "telnet")
          ("dict" . "OnlineDict")
          ("*WL:Message*" . "Wanderlust")))
  ;;
;;;  Use frame-title for tabs
  ;; How to display the list of screens on the frame-title of my Emacs?
  ;; This is broken. get-alist should be changed to alist-get
  ;; https://www.emacswiki.org/emacs/EmacsLispScreen#toc8
  ;;
  (defvar *elscreen-tab-truncate-length*
    20 "Number of characters to truncate tab names in frame title")
  ;;
  (defun elscreen-tabs-as-string ()
    "Return a string representation of elscreen tab names

Set name truncation length in ELSCREEN-TRUNCATE-LENGTH"
    (let* ((screen-list (sort (elscreen-get-screen-list) '<))
           (screen-to-name-alist (elscreen-get-screen-to-name-alist)))
      ;; mapconcat: mapping and then concate name elements together with separator
      (mapconcat
       (lambda (screen)
         (format (if (string-equal "+" (elscreen-status-label screen))
                     ;; Current screen format
                     "[ %d ] %s"
                   ;; Others
                   "(%d) %s")
                 ;; screen number: replaces %d (integer)
                 screen
                 ;; screen name: replaces %s (string)
                 (elscreen-truncate-screen-name
                  ;; Return the value associated with KEY in ALIST
                  (alist-get screen screen-to-name-alist)
                  *elscreen-tab-truncate-length*)))
       ;; Screen numbers (keys for alist)
       screen-list
       ;; Separator
       " | ")))
  ;;
  (defvar *elscreen-tabs-as-string*
    "" "Variable to hold curent elscreen tab names as a string")
  ;;
  (defun update-elscreen-tabs-as-string ()
    "Update *elscreen-tabs-as-string* variable"
    (interactive)
    (setq *elscreen-tabs-as-string* (elscreen-tabs-as-string)))
  ;;
  ;; Update *elscreen-tabs-as-string* whenever elscreen status updates
  (add-hook 'elscreen-screen-update-hook 'update-elscreen-tabs-as-string)
  ;;
  ;; Set frame title format as combination of current elscreen tabs and buffer/path
  (setq frame-title-format '(:eval (concat *elscreen-tabs-as-string*
                                           "    ||    "
                                           (if buffer-file-name
                                               (abbreviate-file-name buffer-file-name)
                                             "%b"))))
  ;;
  ;; It has to kick in.
  (elscreen-start))

備考

この文章の作成にはorg-modeからQiita形式のMarkdownを出力する ox-qmd を使用しました。