22

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))))
Stefan
  • 27,908
  • 4
  • 53
  • 82
Sean Allred
  • 3,558
  • 3
  • 32
  • 71
  • 1
    When you look at `C-h v auto-mode-alist`, the extensions are usually written as `"\\.notes\\'"`. See the final *single* quote. – Olaf Dietsche Dec 19 '12 at 07:01
  • I'll be damned. Missed that when I was doing the type up. Probably exactly explains the trailing backslash thing -_- – Sean Allred Dec 19 '12 at 16:33

2 Answers2

17

The answer by Trey Jackson seems to be a very robust and extensible solution, but I was looking for something simpler. The following code will enable the fictional hmmm-mode when editing .hmmm files:

(add-hook 'find-file-hook
          (lambda ()
            (when (string= (file-name-extension buffer-file-name) "hmmm")
              (hmmm-mode +1))))
Resigned June 2023
  • 4,638
  • 3
  • 38
  • 49
  • Could this be used with a regular expression for the extension? In my case, I'm trying to enable `alchemist-mode` when extensions `ex` and `exs` are found. – sgmonda Feb 24 '20 at 22:53
  • @sgmonda In that case, just do it the standard way. Untested: `(add-to-list 'auto-mode-alist `("\\.ex\\'" . alchemist-mode))`. – Resigned June 2023 Feb 25 '20 at 16:15
14

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.

Stefan
  • 27,908
  • 4
  • 53
  • 82
Trey Jackson
  • 73,529
  • 11
  • 197
  • 229
  • I understand most of what's going on here, although I'm getting an error >Invalid regexp: "Trailing backslash"< with the expression "\\.notes$\\", which I think should be valid. However, I was getting the same results with my attempt, which I'll edit my original question to include shortly. – Sean Allred Dec 19 '12 at 06:26
  • Don't know how, don't know why, but it all works now. Thanks!! – Sean Allred Dec 19 '12 at 07:19
  • In `files.el:set-auto-mode`, `auto-mode-alist` is searched with `(assoc-default name auto-mode-alist 'string-match)`. This could simplify and replace your while loop. – Olaf Dietsche Dec 19 '12 at 16:43
  • 1
    @OlafDietsche Not really, I used the while loop so as to enable multiple minor modes to be turned on for a single file, whereas the `assoc-default` would just give you a single mode. This is done in the spirit of only having one major mode, but enabling many minor modes. – Trey Jackson Dec 19 '12 at 16:48