35

I have this by default in my auto-mode-alist:

("\\.js\\'" . javascript-mode)

(even with emacs -Q). I'd like to substitute js2-mode for javascript-mode. Of course, I could use assq-delete-all and then add-to-list again, but I'm wondering whether there isn't a better way.

Edit: I explicitly do not want to use Customize, I prefer crafting my init.el myself.

36

While @Dan's answer is a perfectly fine solution, it is unnecessary. One of the reasons Emacs uses an alist here is that with an alist you can simply add a new element to the front of the list and it will shadow matches further down the list.

(add-to-list 'auto-mode-alist '("\\.js\\'" . js2-mode))
  • 1
    Care to say why the downvote? – Drew Nov 11 '14 at 22:02
  • 1
    I upvoted your answer - thanks, it's good to know. I guess the downvoter thought this solution to be inelegant (I have to say that I agree, though do not consider it a reason for downvoting - after all, it both solves my problem and is a valuable piece of information!) – mbork Nov 11 '14 at 22:15
  • 7
    Admittedly, it does not answer the question of how to replace. Its point is that you do not need to replace (unless you have some other need than what you described). – Drew Nov 11 '14 at 22:19
  • 2
    the question wasn't really about replacing in the technical sense but rather changing in the higher-level sense. – Erik Allik Nov 12 '14 at 0:43
  • 2
    This will always work no matter if the cons is in pure space or will be removed in some future version. – politza Nov 30 '16 at 20:52
33

Use setf to change the value in place:

(setf (cdr (rassoc 'javascript-mode auto-mode-alist)) 'js2-mode)

If you want to replace a value in the list, then setf is the generalized machinery you need to do so. For the more idiomatic way to deal with the auto-mode-alist, see @Drew's answer (and his explanation of shadowing).

  • Wow. I feel stupid now. Thanks! (And the idea of (almost) every place to be setfable should really be shown to Java guys.) – mbork Nov 11 '14 at 21:17
  • 6
    @mbork You might enjoy this classic explanation to the Perl guys. lists.warhead.org.uk/pipermail/iwe/2005-July/000130.html – purple_arrows Nov 11 '14 at 22:43
  • @mbork: really no reason to feel silly -- setf is used all the time in Common Lisp, but you run across it a lot less frequently in elisp. – Dan Nov 13 '14 at 2:11
  • @Dan: true. Now I'm wondering why Elisp uses setf so seldom, compared to CL... – mbork Nov 13 '14 at 20:23
19

The fastest way to actually change the cons cell is probably setcdr

setcdr is a built-in function in `C source code'.

(setcdr CELL NEWCDR)

Set the cdr of CELL to be NEWCDR.  Returns NEWCDR.

It's worth noting that setf isn't available in older Emacsen, but setcdr is.


*** Welcome to IELM ***  Type (describe-mode) for help.
ELISP> (setq tmp '((one . 1) (two . 2) (three . 4)))
((one . 1)
 (two . 2)
 (three . 4))

ELISP> (setcdr (assq 'three tmp) 3)
3 (#o3, #x3, ?\C-c)
ELISP> tmp
((one . 1)
 (two . 2)
 (three . 3))
  • Do you happen to know which version of Emacs added setf? – dshepherd Nov 6 '18 at 17:47
  • 1
    @dsheperd not off-hand, no. Why do you need to know? I would say any emacs that should be targeted for new development will have setf, but there might not be handling for it for the kind of data you want to set. They're called generalized variables. – Sean Allred Nov 6 '18 at 20:10
  • I wanted to know if it was ok to use setf in some new code, but as you said it turns out there was no generalised variable for what I wanted until too recent a version anyway. – dshepherd Nov 7 '18 at 8:40
5

The OP asks for a solution which handles alists that have string keys. To handle that, see this question. If by chance you only need to handle alists with symbol keys, then as of Emacs 25 you can use:

(setf (alist-get <key> <alist>) <value>)

to replace a cdr. If you have access to Emacs 26, this technique does work with string keys, as follows:

(setf (alist-get "\\.js\\'" auto-mode-alist nil nil #'equal) 'js2-mode)

Note that there are also other ways in Emacs 26 to handle string keys; see this question as mentioned above.

  • (setf (alist-get "\\.js\\'" auto-mode-alist nil nil #'equal) 'js2-mode) should work (requires Emacs 26 though). – npostavs Feb 3 '18 at 13:45
  • @RadonRosborough: there is an edit feature. Consider to fix you answer. – antonio Feb 27 '18 at 21:41
  • You are using alist-get with the string "\\.js\\'", but alist-get is based on assq, so it won't work with a string as you claim in your answer. – antonio Mar 2 '18 at 5:22
  • @antonio Oh yes, you are entirely correct. I had not realized that the question as posted actually does require a solution which handles string keys. I'll make the edit, thanks! – Radon Rosborough Mar 2 '18 at 19:34
2

If you know you won't use javascript-mode ever again let auto-mode-alist untouched and add to your init.el

  (defalias 'javascript-mode 'js2-mode "Some handy explanation goes here.")
  • 1
    Actually, there is no javascript-mode, really: javascript-mode is only an alias for js-mode (by default) and it was done this way specifically so that users could do like you suggest if they prefer js2-mode (without losing the ability to use js-mode if they want to). – Stefan May 17 '17 at 13:14
  • I derived my answer from habit of aliasing for cperl-mode and nxml-mode. So what would do the trick here? (defalias 'js-mode 'js2-mode)? – Matthias May 17 '17 at 13:22
  • 1
    You misunderstood me. I'm saying that your answer is exactly right and doesn't prevent you from using "javascript-mode" since what you call by that name is really js-mode (contrary to what happens for perl-mode, for instance). – Stefan May 17 '17 at 13:25
  • Got it... (casual javascript-mode user here) – Matthias May 17 '17 at 13:29

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.