emacs-purpose(window-purpose)でバッファを開くウィンドウを目的(モード、ディレクトリ)ごとに固定する
昨日の続き。
Emacsの使い方を変えようとして無理だった - ログ日記
似たような質問を発見した。
stackoverflow.com
この人は .c と .h でウィンドウを固定したかったみたい。
ここでは buffer-stack が挙げられている。
これも少し使ってみたけれど、まあその名の通りstackなので自分の目的とちょっと違う。操作もちょっと大変そう。
次のコメントでは、.hファイルの次のファイルを開くのはIDEからEmacsにbad idea を持ち込むことだとまで言われているな…。まあ自分の場合はファイル名の頭文字とtabで探すのでちょっと異なるのだが。
Buffer management - WikEmacs
wikiを眺めていると、See AlsoにKeep a certain kind of buffers in a window: Purposeという項目があることに気付いた。separate bufferやbuffer list groupingとかで見ていたのでスルーしていた。
emacs-purpose(window-purpose)で目的ごとにバッファリストの設定が出来るっぽい。
確かに、何のためにウィンドウ別の履歴が欲しいのかと言うと、ウィンドウごとにそれぞれ別の目的があるからで、こちらの方がより上位(?)の概念である。単に履歴を分割するよりはhtmlを編集しているときにC-x C-f でphpを開いたり C-x 0 で閉じたとしても、phpファイルを開いたらphp用の別のウィンドウで立ち上がってくれる方が有り難い。
何故誰もオススメしていないのか…。
Usage · bmag/emacs-purpose Wiki · GitHub
ここの最後にdesctop-save-modeにはデフォルトで対応していると書いている。
なかなか良い感じに思えるが、軽く使ってみると以下の二点で不満が出てきた。
・バッファ名ではなくてファイルのpathでルールを作りたい
・切り替え時にデフォルトを表示してほしい
なのでカスタマイズするelispを書いた。
;; overwrite (defadvice purpose--buffer-purpose-name-regexp-1 (around path-regexp (buffer-or-name regexp purpose)) "Add filename with regexp feature." (let* ( (mode (unless (stringp regexp) (car regexp))) (mode-match (if mode (eq (purpose--buffer-major-mode buffer-or-name) mode) t)) (bufname (or (and (bufferp buffer-or-name) (buffer-name buffer-or-name)) buffer-or-name) ) (regexps (if (stringp regexp) regexp (cdr regexp))) (target (if mode (buffer-file-name (get-buffer bufname)) bufname)) ) (when (and mode-match target (string-match-p regexps target)) (setq ad-return-value purpose))) ) (ad-activate 'purpose--buffer-purpose-name-regexp-1) (defadvice purpose-read-buffers-with-purpose (around with-default (purpose)) "Prompt with default." (let* ( (other-buffer (delq (current-buffer) (purpose-buffers-with-purpose purpose))) (default (or (buffer-name (car other-buffer)) "*scratch*")) (ret (completing-read (concat "[PU] Buffer (default " default "): ") (mapcar #'buffer-name other-buffer) nil t nil)) ) (setq ad-return-value (if (string= ret "") default ret)) )) (ad-activate 'purpose-read-buffers-with-purpose)
設定のリストががっつりコアに書かれているので、うまくhookできなかった。なのでコアの関数のカスタマイズ。
引数がregexpじゃなくてコールバックだと良かったんだけれど。
文法を調べつつ書いていてあまり自信なし。
let*で逐次的に書いていてlispっぽくない気がする。
設定はこんなの。
(setq desktop-path '(".")) (desktop-save-mode t) (require 'window-purpose) (purpose-mode) (require 'window-purpose-x) (purpose-x-kill-setup) (add-to-list 'purpose-user-mode-purposes '(php-mode . php)) (add-to-list 'purpose-user-mode-purposes '(web-mode . web)) (add-to-list 'purpose-user-mode-purposes '(js2-mode . js2)) (add-to-list 'purpose-user-mode-purposes '(css-mode . css)) (add-to-list 'purpose-user-mode-purposes '(sql-mode . sql)) (add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Page/") . phppage)) (add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Model/") . phpmodel)) (add-to-list 'purpose-user-regexp-purposes '((php-mode . "/Dto/") . phpdto)) (purpose-compile-user-configuration) ;; purpose-switch-buffer-with-purpose to default (define-purpose-prefix-overload purpose-switch-buffer-overload '(purpose-switch-buffer-with-purpose switch-to-buffer )) (global-set-key [S-right] 'split-window-horizontally) (global-set-key [S-left] 'split-window-horizontally) (global-set-key [S-up] 'split-window-vertically) ;(define-key global-map [S-down] 'delete-other-windows) (global-set-key [right] 'windmove-right) (global-set-key [left] 'windmove-left) (global-set-key [up] 'windmove-up) (global-set-key [down] 'windmove-down)
取り敢えずPHPのモデル用のウィンドウ、画面用のウィンドウ、というように指定できるようにした。
major modeとセットなので、ディレクトリに別の拡張子のファイルがあったらそれは別のルールになるようにしている。
本当は .dir-locals.el に書きたかったんだけれど、こっちも書式がよく分からず取り敢えずinit.elに書いた。
やっと設定が出来てきたところなので使い勝手はまた今度。