ログ日記

作業ログと日記とメモ

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に書いた。
やっと設定が出来てきたところなので使い勝手はまた今度。