Search code examples
emacselispdot-emacs

Emacs auto-minor-mode based on extension


I found this question somewhat on the topic, but is there a way [in emacs] to set a minor mode (or a list thereof) based on extension? For example, it's pretty easy to find out that major modes can be manipulated like so

(add-to-list 'auto-mode-alist '("\\.notes\\'" . text-mode))

and what I'd ideally like to be able to do is

(add-to-list 'auto-minor-mode-alist '("\\.notes\\'" . auto-fill-mode))

The accept answer of the linked question mentions hooks, specifically temp-buffer-setup-hook. To use this, you have to add a function to the hook like so

(add-hook 'temp-buffer-setup-hook #'my-func-to-set-minor-mode)

My question is two-fold:

  1. Is there an easier way to do this, similar to major modes?
  2. If not, how would one write the function for the hook?
    1. It needs to check the file path against a regular expression.
    2. If it matches, activate the desired mode (e.g. auto-fill-mode).

Feeble and buggy attempt at a solution:

;; Enables the given minor mode for the current buffer it it matches regex
;; my-pair is a cons cell (regular-expression . minor-mode)
(defun enable-minor-mode (my-pair)
  (if buffer-file-name ; If we are visiting a file,
      ;; and the filename matches our regular expression,
      (if (string-match (car my-pair) buffer-file-name) 
      (funcall (cdr my-pair))))) ; enable the minor mode

; used as
(add-hook 'temp-buffer-setup-hook
          (lambda ()
            (enable-minor-mode '("\\.notes\\'" . auto-fill-mode))))

Solution

  • This code seems to give what you want:

    (defvar auto-minor-mode-alist ()
      "Alist of filename patterns vs correpsonding minor mode functions, see `auto-mode-alist'
    All elements of this alist are checked, meaning you can enable multiple minor modes for the same regexp.")
    
    (defun enable-minor-mode-based-on-extension ()
      "Check file name against `auto-minor-mode-alist' to enable minor modes
    the checking happens for all pairs in auto-minor-mode-alist"
      (when buffer-file-name
        (let ((name (file-name-sans-versions buffer-file-name))
              (remote-id (file-remote-p buffer-file-name))
              (case-fold-search auto-mode-case-fold)
              (alist auto-minor-mode-alist))
          ;; Remove remote file name identification.
          (when (and (stringp remote-id)
                     (string-match-p (regexp-quote remote-id) name))
            (setq name (substring name (match-end 0))))
          (while (and alist (caar alist) (cdar alist))
            (if (string-match-p (caar alist) name)
                (funcall (cdar alist) 1))
            (setq alist (cdr alist))))))
    
    (add-hook 'find-file-hook #'enable-minor-mode-based-on-extension)
    

    Note: the comparison is done with string-match-p which follows the case-fold-search settings during comparison.