10

I've started editing a lot of files that have no extensions, but are still in a major mode format.

I'm the only Emacs user on the team, and I don't want to make it a painfully obvious that my editor needs special stuff. I would like to avoid changing the codebase.

  • I can't change the filename
  • I can't use special Emacs comments

I would like Emacs to remember whatever mode I put the file manually in last (with, say M-x conf-mode) and automatically activate that mode when I visit it again.

I use savehist-mode to handle persistence.

Can I do this painlessly?

PythonNut
  • 10,243
  • 2
  • 29
  • 75
  • 1
    If that's the file you can edit, typically you can add a first line with a comment: `# -*- mode: conf -*-`, and that would tip Emacs off to use `conf-mode`. If there are few of them, and you can match them via regular expression, you can add the regexp to `automode-alist`. – wvxvw May 06 '15 at 14:33
  • 2
    @wvxvw one of my limitations is that "_I can't use special Emacs comments_". – PythonNut May 06 '15 at 14:59
  • 2
    Ouch, sorry, I don't understand how I've missed that. Also, it's `auto-mode-alist`, my bad. – wvxvw May 06 '15 at 15:52
  • Clearly the correct answer here is to get the rest of your team to use Emacs. Any other answer is just a workaround. – Malabarba May 12 '15 at 21:26

2 Answers2

13

There are a number of ways to identify the major mode for a file that don't rely on an extension, see Choosing File Modes in the manual.

Depending on the kinds of files you are dealing with, perhaps you could use the magic-mode-alist. Also note that auto-mode-alist is not limited to matching extensions: you can match any part of the file name or path.

If the files you are dealing with are not consistent enough for those mechanisms, one option is to add auto-mode-alist entries that match the entire file name, or match the root path of some project and call a custom function to match names to modes.

If all the files in a given directory are of the same type you could also use a directory-local variable to set the mode. Directory variables can be set in your init file rather than in a .dir-locals file -- see Directory Variables for details.

Update

Here's a quick attempt at managing your own alist of absolute file names and major-modes.

(defvar my-custom-mode-alist '())
(defvar my-custom-mode-alist-file (expand-file-name "custom-file-assoc" user-emacs-directory))

;; command to save the file->mode association of the current buffer
(defun save-file-mode-association ()
  (interactive)
  (when buffer-file-name
    (add-to-list 'my-custom-mode-alist (cons buffer-file-name major-mode))
    (write-custom-mode-alist my-custom-mode-alist-file)))

(defun write-custom-mode-alist (file)
  (with-current-buffer (get-buffer-create " *Custom File Assocations*")
    (goto-char (point-min))
    (delete-region (point-min) (point-max))
    (pp my-custom-mode-alist (current-buffer))
    (condition-case nil
        (write-region (point-min) (point-max) file)
      (file-error (message "Can't write %s" file)))
    (kill-buffer (current-buffer))
    (message "Wrote custom file associations to file %s" file)))

(defun load-custom-mode-alist (file)
  (when (file-exists-p file)
    (with-current-buffer
        (let ((enable-local-variables nil))
          (find-file-noselect file))
      (goto-char (point-min))
      (setq my-custom-mode-alist (read (current-buffer)))
      (setq auto-mode-alist (append auto-mode-alist my-custom-mode-alist))
      (kill-buffer (current-buffer)))))

;; Load any custom file associations and add them to auto-mode-alist
(load-custom-mode-alist my-custom-mode-alist-file)
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
glucas
  • 20,175
  • 1
  • 51
  • 83
  • Unfortunately, the files are pretty loosely formatted, and they have very generic file names. Thanks for pointing out the specifics of `auto-mode-alist`, though. I'll probably keep `magic-mode-alist` in the back of my mind for something in the future. – PythonNut May 06 '15 at 19:05
4

The following, based on Glucas' advice seems to work perfectly.

(defvar file-name-mode-alist '())
;; other stuff here, of course
(setq savehist-additional-variables '(file-name-mode-alist))
(savehist-mode +1)
(setq auto-mode-alist (append auto-mode-alist file-name-mode-alist))

(add-hook 'after-change-major-mode-hook
  (lambda ()
    (when (and
            buffer-file-name
            (not
              (file-name-extension
                buffer-file-name)))
       (setq file-name-mode-alist
        (cons
          (cons buffer-file-name major-mode)
          file-name-mode-alist))
      (setq auto-mode-alist
        (append auto-mode-alist
          (list (cons buffer-file-name major-mode)))))))
PythonNut
  • 10,243
  • 2
  • 29
  • 75