Function: set-auto-mode

set-auto-mode is a byte-compiled function defined in files.el.gz.

Signature

(set-auto-mode &optional KEEP-MODE-IF-SAME)

Documentation

Select major mode appropriate for current buffer.

To find the right major mode, this function checks for a -*- mode tag checks for a mode: entry in the Local Variables section of the file, checks if there an auto-mode-alist entry in .dir-locals.el, checks if it uses an interpreter listed in interpreter-mode-alist, matches the buffer beginning against magic-mode-alist, compares the file name against the entries in auto-mode-alist, then matches the buffer beginning against magic-fallback-mode-alist. It also obeys major-mode-remap-alist and major-mode-remap-defaults.

If enable-local-variables is nil, or if the file name matches inhibit-local-variables-regexps, this function does not check for any mode: tag anywhere in the file. If local-enable-local-variables is nil, then the only mode: tag that can be relevant is a -*- one.

If the optional argument KEEP-MODE-IF-SAME is non-nil, then we set the major mode only if that would change it. In other words we don't actually set it to the same mode the buffer already has.

View in manual

Probably introduced at or before Emacs version 21.1.

Source Code

;; Defined in /usr/src/emacs/lisp/files.el.gz
(defun set-auto-mode (&optional keep-mode-if-same)
  "Select major mode appropriate for current buffer.

To find the right major mode, this function checks for a -*- mode tag
checks for a `mode:' entry in the Local Variables section of the file,
checks if there an `auto-mode-alist' entry in `.dir-locals.el',
checks if it uses an interpreter listed in `interpreter-mode-alist',
matches the buffer beginning against `magic-mode-alist',
compares the file name against the entries in `auto-mode-alist',
then matches the buffer beginning against `magic-fallback-mode-alist'.
It also obeys `major-mode-remap-alist' and `major-mode-remap-defaults'.

If `enable-local-variables' is nil, or if the file name matches
`inhibit-local-variables-regexps', this function does not check
for any mode: tag anywhere in the file.  If `local-enable-local-variables'
is nil, then the only mode: tag that can be relevant is a -*- one.

If the optional argument KEEP-MODE-IF-SAME is non-nil, then we
set the major mode only if that would change it.  In other words
we don't actually set it to the same mode the buffer already has."
  ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*-
  (let ((try-locals (not (inhibit-local-variables-p)))
	end modes)
    ;; Once we drop the deprecated feature where mode: is also allowed to
    ;; specify minor-modes (ie, there can be more than one "mode:"), we can
    ;; remove this section and just let (hack-local-variables t) handle it.
    ;; Find a -*- mode tag.
    (save-excursion
      (goto-char (point-min))
      (skip-chars-forward " \t\n")
      ;; Note by design local-enable-local-variables does not matter here.
      (and enable-local-variables
	   try-locals
	   (setq end (set-auto-mode-1))
	   (if (save-excursion (search-forward ":" end t))
	       ;; Find all specifications for the `mode:' variable
	       ;; and execute them left to right.
	       (while (let ((case-fold-search t))
			(or (and (looking-at "mode:")
				 (goto-char (match-end 0)))
			    (re-search-forward "[ \t;]mode:" end t)))
		 (skip-chars-forward " \t")
		 (let ((beg (point)))
		   (if (search-forward ";" end t)
		       (forward-char -1)
		     (goto-char end))
		   (skip-chars-backward " \t")
		   (push (intern (concat (downcase (buffer-substring beg (point))) "-mode"))
			 modes)))
	     ;; Simple -*-MODE-*- case.
	     (push (intern (concat (downcase (buffer-substring (point) end))
				   "-mode"))
		   modes))))
    (or
     ;; If we found modes to use, invoke them now, outside the save-excursion.
     ;; Presume `modes' holds a major mode followed by minor modes.
     (let ((done ()))
       (dolist (mode (nreverse modes))
	 (if (eq done :keep)
	     ;; `keep-mode-if-same' is set and the (major) mode
	     ;; was already set.  Refrain from calling the following
	     ;; minor modes since they have already been set.
	     ;; It was especially important in the past when calling
	     ;; minor modes without an arg would toggle them, but it's
             ;; still preferable to avoid re-enabling them,
	     nil
	   (let ((res (set-auto-mode-0 mode keep-mode-if-same)))
	     (setq done (or res done)))))
       done)
     ;; Check for auto-mode-alist entry in dir-locals.
     (with-demoted-errors "Directory-local variables error: %s"
       ;; Note this is a no-op if enable-local-variables is nil.
       ;; We don't use `hack-dir-local-get-variables-functions' here, because
       ;; modes are specific to Emacs.
       (let* ((mode-alist (cdr (hack-dir-local--get-variables
                                (lambda (key) (eq key 'auto-mode-alist))))))
         (set-auto-mode--apply-alist mode-alist keep-mode-if-same t)))
     (let ((mode (hack-local-variables t (not try-locals))))
       (unless (memq mode modes)	; already tried and failed
         (set-auto-mode-0 mode keep-mode-if-same)))
     ;; If we didn't, look for an interpreter specified in the first line.
     ;; As a special case, allow for things like "#!/bin/env perl", which
     ;; finds the interpreter anywhere in $PATH.
     (when-let
	 ((interp (save-excursion
		    (goto-char (point-min))
		    (if (looking-at auto-mode-interpreter-regexp)
			(match-string 2))))
	  ;; Map interpreter name to a mode, signaling we're done at the
	  ;; same time.
	  (mode (assoc-default
		 (file-name-nondirectory interp)
		 (mapcar (lambda (e)
                           (cons
                            (format "\\`%s\\'" (car e))
                            (cdr e)))
			 interpreter-mode-alist)
		 #'string-match-p)))
       ;; If we found an interpreter mode to use, invoke it now.
       (set-auto-mode-0 mode keep-mode-if-same))
     ;; Next try matching the buffer beginning against magic-mode-alist.
     (let ((mode (save-excursion
		   (goto-char (point-min))
		   (save-restriction
		     (narrow-to-region (point-min)
				       (min (point-max)
					    (+ (point-min) magic-mode-regexp-match-limit)))
                     (assoc-default
                      nil magic-mode-alist
                      (lambda (re _dummy)
                        (cond
                         ((functionp re)
                          (funcall re))
                         ((stringp re)
                          (let ((case-fold-search nil))
                            (looking-at re)))
                         (t
                          (error
                           "Problem in magic-mode-alist with element %s"
                           re)))))))))
       (set-auto-mode-0 mode keep-mode-if-same))
     ;; Next compare the filename against the entries in auto-mode-alist.
     (set-auto-mode--apply-alist auto-mode-alist
                                 keep-mode-if-same nil)
     ;; Next try matching the buffer beginning against magic-fallback-mode-alist.
     (let ((mode (save-excursion
		   (goto-char (point-min))
		   (save-restriction
		     (narrow-to-region (point-min)
				       (min (point-max)
					    (+ (point-min) magic-mode-regexp-match-limit)))
		     (assoc-default nil magic-fallback-mode-alist
                                    (lambda (re _dummy)
                                      (cond
                                       ((functionp re)
                                        (funcall re))
                                       ((stringp re)
                                        (let ((case-fold-search nil))
                                          (looking-at re)))
                                       (t
                                        (error
                                         "Problem with magic-fallback-mode-alist element: %s"
                                         re)))))))))
       (set-auto-mode-0 mode keep-mode-if-same))
     (set-buffer-major-mode (current-buffer)))))