Emacs の imenu-list をフレームごとに使用できるようにする

2024-06-30 Sun. 10:09:59 JST
Emacs
by kiyozzy

■ Emacs の imenu-list

Emacs の imenu-list はプロセスで1つしか使用できない。本設定はフレーム毎に使用できるようにする。
グローバル変数を frame parameter として扱うことで実現する。
Emacsの設定ファイルなどに記載する。

;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
;; imenu-listをframe毎に使用できるようにする
;; imenu-listはプロセスで1つしか使用できない。本設定はframe毎に使用できるようにする。
;; グローバル変数を frame parameter として扱うことで実現する。
;; 2024-06-30 Sun. 10:07:20 JST : 初版作成

(when (require 'imenu-list nil t)

  ;; frame 作成時にグローバル変数に対応する frame parameter を用意する
  (defun my-imenu-list-hook-after-make-frame-functions (frame)
    (modify-frame-parameters frame (list (cons 'fimenu-list-buffer-name (format "*Ilist-%s*" (frame-parameter frame 'window-id)))))
    (modify-frame-parameters frame (list (cons 'fimenu-list--imenu-entries nil)))
    (modify-frame-parameters frame (list (cons 'fimenu-list--line-entries nil)))
    (modify-frame-parameters frame (list (cons 'fimenu-list--displayed-buffer nil)))
    (modify-frame-parameters frame (list (cons 'fimenu-list--last-location nil)))
    )
  (add-hook 'after-make-frame-functions 'my-imenu-list-hook-after-make-frame-functions)

  ;; "imenu-list"から始まる関数の around advice
  (defun my-imenu-list-around-advice (orig-fun &rest args)
    "My around advice for functions starting with `imenu-list`."
    (if (or (boundp 'my-imenu-list-variable-using) (not (display-graphic-p)))
        ;; 既に frame parameter からローカル変数化しているならば元の関数を呼び出すのみ。
        ;; CUIの時も元の関数を呼び出すのみ(グローバル変数を使う)。
        (apply orig-fun args)
      ;; まだローカル変数化していないならば frame parameter からローカル変数化する
      (let* ((imenu-list-buffer-name (frame-parameter nil 'fimenu-list-buffer-name))
             (imenu-list--imenu-entries (frame-parameter nil 'fimenu-list--imenu-entries))
             (imenu-list--line-entries (frame-parameter nil 'fimenu-list--line-entries))
             (imenu-list--displayed-buffer (frame-parameter nil 'fimenu-list--displayed-buffer))
             (imenu-list--last-location (frame-parameter nil 'fimenu-list--last-location))
             (my-imenu-list-variable-using t) ;; ローカル変数化済フラグ
             (split-height-threshold nil)     ;; windowは常に水平分割
             (split-width-threshold 0)        ;; windowは常に水平分割
             (result (apply orig-fun args)))  ;; 元の関数を呼び出す
        ;; ローカル変数化した内容を frame parameter に保存する
        ;; imenu-list-buffer-name は元々 defconst で変更されないので更新不要
        (modify-frame-parameters nil (list (cons 'fimenu-list--imenu-entries  imenu-list--imenu-entries)))
        (modify-frame-parameters nil (list (cons 'fimenu-list--line-entries  imenu-list--line-entries)))
        (modify-frame-parameters nil (list (cons 'fimenu-list--displayed-buffer  imenu-list--displayed-buffer)))
        (modify-frame-parameters nil (list (cons 'fimenu-list--last-location  imenu-list--last-location)))
        ;; 元の関数の結果を返す
        result)))

  ;; "imenu-list"から始まる関数に advice-add する
  (mapatoms
   (lambda (sym)
     (when (and (symbolp sym)
                (fboundp sym)
                (string-prefix-p "imenu-list" (symbol-name sym)))
       (advice-add sym :around #'my-imenu-list-around-advice))))

  ;; imenu-list バッファの mode-line フォーマット
  (setq imenu-list-mode-line-format
        '("%e" mode-line-front-space mode-line-mule-info mode-line-client
          mode-line-modified mode-line-remote mode-line-frame-identification
          (:propertize "%b" face mode-line-buffer-id) " "
          (:eval (buffer-name
                  ;; frame parameter から表示バッファ名を取得する。
                  ;; CUIの時はグローバル変数を使う。
                  (if (display-graphic-p)
                      (frame-parameter nil 'fimenu-list--displayed-buffer)
                    imenu-list--displayed-buffer)
                  )) " "
          mode-line-end-spaces))
)