わさっきhb

大学(教育研究)とか ,親馬鹿とか,和歌山とか,とか,とか.

Emacsのマルチタブを,elscreenからtab-bar-modeへ

 月1回くらいの頻度で,~/.emacsなどの手直しをしています.
 ただし~/.emacsは数行の内容で,Emacsのバージョンに応じて読み出すinit.elを切り替えるだけです.init.elが主な見直し対象です.設定ごとに空行を設け,不要な設定は「;」でコメントにしたり取り除いたりしていますが,かなり複雑怪奇です.
 M-x package-list-packagesでパッケージの一覧を見て,更新します.Emacsを終了させて起動して,エラーがないことを確認し,終了させてから,パッケージの古いバージョンのファイルやディレクトリと,auto-saveで作られる不要なファイルを取り除きます.user-emacs-directoryを丸ごと7z化して,他のWindows PCでも参照できるように,ファイルをクラウド領域に置くと,手直し完了です.
 なお,Linuxでの~/.emacsの更新頻度は年1回くらいです.Windows版に話を戻すと,GNU Emacs download - GNU Projectで紹介されているnearby GNU mirrorを使用して,emacs-28.1-installer.exeをインストールし,C:\Program Files\Emacs\emacs-28.1\bin\runemacs.exeをランチャアプリに入れるかショートカットを作ってデスクトップに置いています.もっぱらGUIでの使用です(WSL2などでemacsコマンドは使用しません).
 先月,何だったかEmacsでこういうことができるという紹介ページの中で,elscreenではうまく動かないという説明がありました.
 自分自身,elscreenを愛用してきました.ブラウザのタブを,Emacsでも利用可能というものです.キー操作で,左右や特定の位置のタブに移動できますし,タブの作成や消去も容易です.
 なのですが,M-x package-list-packagesで見ることのできるelscreenのバージョンは20181009.451で,新しいわけではなく,またhttps://github.com/knu/elscreenの各ファイルも最も新しい更新が「4 years ago」です.より新しい,同等の機能に置き換えたほうがいいのかと,思うようになりました.
 「同等の機能」について,tab-bar-modeとtab-line-modeを知り,試してみました.Emacs 27.1で追加されたとのことです.

 elscreenと共存できるのも,興味深かったのですが,この機会に移行するとしましょう.動かしてみると,tab-bar-modeの設定に手を加えるのがよさそうです---と思いながら,1か月が経過してしまいました.
 手を動かして,追加した設定は,次の3つに分かれます.

  1. 主要な操作を,elscreenと同じキーストロークで行える.
  2. デスクトップなどからのファイルのShift+ドラッグにより,新しいタブでファイルを開く.
  3. 閲覧中のタブが分かるように表示する.

 キー操作に関して,次のことができればよいとわかりました.

コマンド名 意味 tab-bar-modeのキーバインド elscreenのキーバインド
tab-new タブの新規作成 C-x t 2 C-z cまたはC-z C-c
tab-next 次のタブへ移動 C-x t o C-z nまたはC-z C-n
tab-previous 前のタブへ移動 C-x t O C-z pまたはC-z C-p
iconify-or-deiconify-frame Emacsを最小化 (なし) C-z C-z*1

 elscreenのプレフィクスキー,C-zは,elscreen.elを読み出さないことで,無効化されます.そして独自に,C-zをプレフィクスキーとし*2,そのあと,上記の表の「elscreenのキーバインド」を設定しました.コードは以下の通りです.

(defvar my-c-z-prefix (kbd "C-z"))
(setq my-c-z-map (make-keymap))
(define-key global-map my-c-z-prefix my-c-z-map)
(defun my-set-key-c-z-map () (local-set-key my-c-z-prefix my-c-z-map))
(define-key my-c-z-map "c" 'tab-new)
(define-key my-c-z-map (kbd "C-c") 'tab-new)
(define-key my-c-z-map "n" 'tab-next)
(define-key my-c-z-map (kbd "C-n") 'tab-next)
(define-key my-c-z-map "p" 'tab-previous)
(define-key my-c-z-map (kbd "C-p") 'tab-previous)
(define-key my-c-z-map (kbd "C-z") 'iconify-or-deiconify-frame)

 次に,「Shift+ドラッグで,新しいタブでファイルを開く」ためには,elscreen.elをしっかり読まないといけないのかと思ったらハズレで,自分のinit.elに,次のように書いていました.

(when (require 'elscreen nil t)
  (elscreen-start)
  (if window-system
      (progn
        (defun w32-drag-n-drop-elscreen-onefile (event)
          "Edit the file using ElScreen"
          (interactive "e")
          (elscreen-create)
          (find-file event))
        (defun w32-drag-n-drop-elscreen (event)
          "Edit the files using ElScreen"
          (interactive "e")
          (mapcar 'w32-drag-n-drop-elscreen-onefile (car (cdr (cdr event)))))
        (global-set-key [S-drag-n-drop] 'w32-drag-n-drop-elscreen)
        (define-key elscreen-map (kbd "C-z") 'iconify-or-deiconify-frame))))

 [S-drag-n-drop]を,w32-drag-n-drop-elscreenにバインドしています.この関数は,Shift+ドラッグのイベント(event)からファイル名を取り出して,w32-drag-n-drop-elscreen-onefileに渡し,elscreen-create(タブ新設)のあとfind-fileで開いています.
 これと同様のことを,tab-modeで行えばいいのですが…
 find-file-other-tabが「ファイルを新しいタブで開く」で,そのとおりに動作しました.elscreen-createに対応する指示は不要ということです.それから,fileを開く関数の引数がeventというのはおかしいので,filenameに変更しました.
 最終的に出来上がったコードは以下の通りです.

(defun w32-drag-n-drop-tab-mode-onefile (filename)
  "Edit the file using tab-mode"
  (interactive "e")
  (find-file-other-tab filename))
(defun w32-drag-n-drop-tab-mode (event)
  "Edit the files using tab-mode"
  (interactive "e")
  (mapcar 'w32-drag-n-drop-tab-mode-onefile (car (cdr (cdr event)))))
(global-set-key [S-drag-n-drop] 'w32-drag-n-drop-tab-mode)

 最後は,「閲覧中のタブの表示」です.tab-bar-modeを素のままで使用して,戸惑ったのは,閲覧中の(アクティブな)タブの表示が,そうでないタブよりも薄く見えることです.tab-bar-modeを探検するでは,「見た目については、アクティブなタブは tab-bar-tab 、 非アクティブなタブは tab-bar-tab-inactive のfaceを変更すれば変えられる」と説明されています.faceの変更方法は,別途調査しまして,以下のページを参考にしました.

 M-x list-faces-displayを実行し,表示されるFacesバッファで,tab-barから始まる6行分を見ていきました.tab-bar-tabとtab-bar-tab-inactiveの行でEnterを押して設定変更を行い,「Apply and Save」のボタンを押すと,~/.emacsに追記されます.該当コードを取り出してinit.elにコピーしました(~/.emacsへの追記は,Emacsを終了させてから他のテキストエディタで除去しました).以下のコードとなりました.

(custom-set-faces
 '(tab-bar-tab ((t (:inherit tab-bar :background "LightSkyBlue1" :foreground "black" :box (:line-width (1 . 1) :color "navy" :style released-button)))))
 '(tab-bar-tab-inactive ((t (:inherit tab-bar-tab :background "gray93" :foreground "gray50")))))

 Emacsを起動し直すと,タブが表示されません.デフォルトで起動する設定を,書き忘れていました.

(tab-bar-mode +1)

 Emacsスクリーンショットはこんな感じです(実際の利用では,行数はもっと多いです).

*1:elscreen.elではなく自分のinit.elでの設定です.

*2:http://yohshiy.blog.fc2.com/blog-entry-323.htmlの「(defvar my-atkey-prefix」から始まるコードを参考にしました.