14

Q: how can I get new buffers to honor the mapping in auto-mode-alist?

When finding a file, Emacs compares the file extension to the auto-mode-alist to determine which major mode to use for the file's buffer. Is there a way to use the information in auto-mode-alist to determine the mode for a buffer that does not (yet) have a file associated with it?

That is: if I open a new buffer whose name has something that looks like a file extension, can I get it to open automatically in the expected mode? For example, if I were to open a new buffer a-new-buffer.el that is not yet associated with a file, I want it to open in emacs-lisp-mode rather than in the default mode.

Dan
  • 32,584
  • 6
  • 98
  • 168
  • 2
    Just curious: What's the use case? IOW, why/when/in-what-context do you want to do this? Typically, if you want a buffer to be associated with a file and so pick up its mode from `auto-mode-alist`, you make the buffer ["*visit*"](https://www.gnu.org/software/emacs/manual/html_node/elisp/Visiting-Files.html) the file (and that takes care of everything). – Drew Oct 23 '14 at 16:01
  • 5
    Examples of typical use cases for me are: a) a temporary `org` buffer to test out new functions I'm writing for use in `org-mode`; b) a temporary `R` buffer to do some quick, throw-away statistical manipulations; c) a temporary text buffer to compose an email. In each case, I do *not* want to create a file to visit, I just want a throw-away buffer that nonetheless opens in the appropriate mode. – Dan Oct 23 '14 at 16:11
  • 3
    File visiting commands such as `C-x C-f` do ***not*** "*create a file to visit*". That is a fundamental misunderstanding. They simply do exactly what you are looking for. It is *only* if and when you try to ***save the buffer*** that a file gets created. If you do not try to save the buffer, no file is created. What you want, from what I understand so far, is to "visit a file" (which really means open a buffer in the proper mode). – Drew Oct 23 '14 at 16:16
  • 8
    Like Dan, I create temporary buffers all the time that I don't want associated with an actual file path. Visiting a bogus file path would work but there is at least a small amount of friction in choosing a directory (or accepting the current one). There could be other side-effects depending on the rest of your configuration: Auto-save behavior? Ibuffer groups or projectile projects determined by path? Ido confirmation prompts? Since buffers have to have a name anyway, using the name to set the mode automatically for a temporary buffer makes sense to me. – glucas Oct 23 '14 at 17:20
  • @Drew, yes, that makes sense and would be the simplest answer to this question -- could you post it as an answer, please? It deserves an upgrade. – Dan Oct 23 '14 at 18:48
  • @glucas: I totally agree. I also use it all the time for temp Org buffers, and it's a bit annoying that I have to switch Org-mode on manually. – mbork Oct 23 '14 at 20:01

6 Answers6

10

If have been using Juri Linkov's solution for years.

I create temporary buffers with something like C-x b test.org C-j. The major-mode is determined by file extension via auto-mode-alist.

;; http://thread.gmane.org/gmane.emacs.devel/115520/focus=115794
(setq-default major-mode
              (lambda () (if buffer-file-name
                          (fundamental-mode)
                            (let ((buffer-file-name (buffer-name)))
                          (set-auto-mode)))))

To test the effect you can try (prog1 (and (switch-to-buffer "my-new.org") major-mode) (kill-buffer "my-new.org")) => org-mode. In a clean emacs -q the test would return fundamental-mode.

rasmus
  • 2,682
  • 13
  • 20
  • 1
    That's very clever! It's also tidier than the `advice` option I'd been using. Not sure I'm following what the `(prog1...)` part is for, but the `(setq-default major-mode ...)` part is quite nice. – Dan Oct 24 '14 at 15:46
  • 1
    Feel free to check out [Juri's Emacs site](http://www.jurta.org/en/emacs). The `prog1`-test just shows that you get the expected `major-mode` when switching to a buffer. Eval the `setq-default` part and run the test. It will return `org-mode` Without the `setq-default` it would return `fundamental-mode`. – rasmus Oct 24 '14 at 15:50
  • This is way more elegant than my buffer-list-update-hook. Thanks! – glucas Nov 11 '14 at 20:50
  • this should be the accepted answer, since it actually answers the question rather than telling us why we are wrong for asking it :) – Shlomi Sep 08 '16 at 22:19
9

File visiting commands such as C-x C-f do not create a file to visit. They do what you are looking for.

It is only if and when you try to save the buffer that a file gets created.

If you do not try to save the buffer, no file is created. What you want, from what I understand so far, is to "visit a file" (which really means open a buffer in the proper mode).

Drew
  • 75,699
  • 9
  • 109
  • 225
  • 5
    What if you have a hyperactive finger that constantly saves the current file every 3 seconds regardless of what you want? – Malabarba Oct 23 '14 at 22:23
  • 2
    @Malabarba: Actually, I do have that finger! ;-) Maybe most of us old farts do - a habit picked up back when things crashed all the time. But anyway, I do just what I suggested: edit in a throw-away visited-file buffer. If my overactive muscle memory gets in the way and tries to save, so be it: either I hit `n` to not save or (often) I just save the buffer. And later I toss the created file...or not.) – Drew Oct 23 '14 at 23:35
  • 1
    @Malabarba It's exactly to prevent the clutter of temp files, that I started using major-mode specific scratch buffers. If the scratch deserves a file, you can always save that as one later. – Kaushal Modi Oct 24 '14 at 04:52
  • 2
    @kaushalmodi: Nothing against your mode-specific buffers, but a "*clutter of temp files*" is as easily cleaned up as a *clutter of buffers*. ;-) And the hyperactive finger in a mode-specific scratch buffer presents the same problem, presumably - same as it does in `*scratch*`: you are prompted to save when that finger automatically hits `C-x C-s`. – Drew Oct 24 '14 at 04:56
6

Figured out an advice-based way using ideas that came from @Drew's comments and @glucas's answer which I'll record here in case they're useful for anyone.

The short version: use after advice to query whether the buffer has an associated file name, set one temporarily if it doesn't, and then let the rest of the set-auto-mode machinery take care of the details. After a bit of testing (not extensive), it seems to be working just fine.

For ido-switch-buffer and vanilla switch-to-buffer, the two bits of advice would be:

(defadvice ido-switch-buffer (after set-mode activate)
  (unless buffer-file-name
    (let ((buffer-file-name (buffer-name)))
      (set-auto-mode t))))

(defadvice switch-to-buffer (after set-mode activate)
  (unless buffer-file-name
    (let ((buffer-file-name (buffer-name)))
      (set-auto-mode t))))

I find this option helpful on top of the find-file point that @Drew raised because my fingers can get ahead of my brain: muscle memory will often get me into switch-to-buffer territory before it fully occurs to me that find-file would do what I need. Now, both options are available.

UPDATE: small but potentially-irritating bug in the above code: it will re-run the mode hook on the buffer each time you switch to it. The following gives it an actual filename off of the /tmp directory and avoids that problem:

(defadvice ido-switch-buffer (after set-mode activate)
  (unless buffer-file-name
    (setq buffer-file-name (concat "/tmp/" (buffer-name)))
    (set-auto-mode t)))
Dan
  • 32,584
  • 6
  • 98
  • 168
  • Looks good, and cleans up the cruft my answer had accumulated over a couple iterations. :-) I'll just repeat here that if you don't want to set an actual file path for some reason you can use a buffer-local variable to avoid setting the mode multiple times. – glucas Oct 23 '14 at 20:17
5

The set-auto-mode function sets the mode based on the file associated with a buffer. Here's a function that temporarily sets buffer-file-name from the buffer name in order to set the mode:

(defun my/set-buffer-mode (buffer &optional again)
  "Set the mode for BUFFER from the name.  
When called repeatedly for the same buffer only set the mode the first
time, unless the optional argument AGAIN is specified.
Does nothing if the buffer is associated with a file."

  (with-current-buffer buffer
    (when again (kill-local-variable 'my/buffer-mode-set))
    (unless (or buffer-file-name 
            (local-variable-p 'my/buffer-mode-set))
      (let ((buffer-file-name (buffer-name)))
        (set-auto-mode t)
        (setq-local my/buffer-mode-set t)))))

You can run this when a buffer is renamed using advice:

(defadvice rename-buffer (after my/rename-update-mode activate)
  (my/set-buffer-mode (current-buffer) 'again))

I'm not sure about the best place to hook this in to affect new buffers. Here I'm using the buffer-list-update-hook, but that gets called in more cases than we need.

(add-hook 'buffer-list-update-hook
      '(lambda ()
         (my/set-buffer-mode (car (buffer-list)))))
glucas
  • 20,175
  • 1
  • 51
  • 83
  • The above answer has been revised to deal with some issues. In particular, I've gone back to using the `buffer-list-update-hook` because the `change-major-mode-hook` was not being called with the expected current buffer. For the record it probably makes more sense to advise your switch-buffer function as @Dan does in his answer. – glucas Oct 23 '14 at 21:13
3

You can use *scratch* buffers for the purpose of creating temporary buffers that have the same major mode as the file that you might be working on.

Here an an emacs SE answer that might solve your problem:

How can I quickly toggle between a file and a *scratch* buffer having the same major mode?

The referenced question and answer were both posted by me. The answer in the function does the following:

  • If you are working in X major mode in a file, calling this function creates a new scratch buffer called *scratch-X-mode* if one doesn't already exist, and switches to this newly created buffer.
  • If *scratch-X-mode* already exists, it simply switches to that buffer.
  • Calling this function again while in that scratch buffer will bring you back to the file buffer you were originally working on.
Kaushal Modi
  • 25,203
  • 3
  • 74
  • 179
0

There are some good answers here but I wanted to add one more thing. Emacs has a ton of features and there are often several ways to do something. You can add tags at the top of any file which tell emacs what type its, regardless of extension. For example adding this:

# -*-Python-*-

At the top of a file will let emacs know it's a Python file even with out the .py extension. Note that the # at the beginning of the line is a Python style comment. For different file types you can use different comments. For example to specify a Lisp file you use ; to start a comment line and set the mode in emacs like this:

; -*- mode: Lisp;-*-

See Specifying File Variables and Choosing File Modes.

programking
  • 7,064
  • 9
  • 41
  • 62
jcoffland
  • 101
  • 1
  • Yes, but the question here is specifically about creating a new temporary buffer (e.g. with `switch-buffer`) and having the mode automatically set to something other than `fundamental-mode`. – glucas Oct 23 '14 at 21:16