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.

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.

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'.

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 done mode 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))))
    ;; If we found modes to use, invoke them now, outside the save-excursion.
    (if modes
	(catch 'nop
	  (dolist (mode (nreverse modes))
	    (if (not (functionp mode))
		(message "Ignoring unknown mode `%s'" mode)
	      (setq done t)
	      (or (set-auto-mode-0 mode keep-mode-if-same)
		  ;; continuing would call minor modes again, toggling them off
		  (throw 'nop nil))))))
    ;; Check for auto-mode-alist entry in dir-locals.
    (unless done
      (with-demoted-errors "Directory-local variables error: %s"
	;; Note this is a no-op if enable-local-variables is nil.
        (let* ((mode-alist (cdr (hack-dir-local--get-variables
                                 (lambda (key) (eq key 'auto-mode-alist))))))
          (setq done (set-auto-mode--apply-alist mode-alist
                                                 keep-mode-if-same t)))))
    (and (not done)
	 (setq mode (hack-local-variables t (not try-locals)))
	 (not (memq mode modes))	; already tried and failed
	 (if (not (functionp mode))
	     (message "Ignoring unknown mode `%s'" mode)
	   (setq done t)
	   (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.
    (and (not done)
	 (setq mode (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.
	 (setq done (assoc-default
		     (file-name-nondirectory mode)
		     (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 done keep-mode-if-same))
    ;; Next try matching the buffer beginning against magic-mode-alist.
    (unless done
      (if (setq done (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 done keep-mode-if-same)))
    ;; Next compare the filename against the entries in auto-mode-alist.
    (unless done
      (setq done (set-auto-mode--apply-alist auto-mode-alist
                                             keep-mode-if-same nil)))
    ;; Next try matching the buffer beginning against magic-fallback-mode-alist.
    (unless done
      (if (setq done (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 done keep-mode-if-same)))
    (unless done
      (set-buffer-major-mode (current-buffer)))))