aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Monnier2024-03-14 22:15:41 -0400
committerStefan Monnier2024-03-14 22:15:41 -0400
commit9422a6737447b186ca017929da79985cef7898a8 (patch)
treed7c71c1153159360a53486036398687b937f6d3e
parentc8c0d0a9550620adb111bf5d9e0155332498a6bf (diff)
downloademacs-9422a6737447b186ca017929da79985cef7898a8.tar.gz
emacs-9422a6737447b186ca017929da79985cef7898a8.zip
(set-auto-mode): Streamline to fix bug#67795
The old code tested if the mode function is `fboundp` but in an inconsistent way and without paying attention to `major-mode-remap`. * lisp/files.el (set-auto-mode-0): Return `:keep` rather than nil if the mode was already set. And emit a warning when the mode function doesn't exist. (set-auto-mode): Remove checks that the mode function exists now that `set-auto-mode-0` does it for us. Adjust to the new return values of that function, and simplify the code using a big `or` instead of a sequence of steps each setting&testing `done`. (hack-local-variables--find-variables): Use `major-mode-remap` when skipping the "mode:" entries that specify modes we don't have. Also, when (eq handle-mode t), don't bother building a list of results only to return a single element in the end.
-rw-r--r--lisp/files.el231
1 files changed, 118 insertions, 113 deletions
diff --git a/lisp/files.el b/lisp/files.el
index 3ca4f047144..766ed573392 100644
--- a/lisp/files.el
+++ b/lisp/files.el
@@ -3425,7 +3425,7 @@ set the major mode only if that would change it. In other words
3425we don't actually set it to the same mode the buffer already has." 3425we don't actually set it to the same mode the buffer already has."
3426 ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*- 3426 ;; Look for -*-MODENAME-*- or -*- ... mode: MODENAME; ... -*-
3427 (let ((try-locals (not (inhibit-local-variables-p))) 3427 (let ((try-locals (not (inhibit-local-variables-p)))
3428 end done mode modes) 3428 end modes)
3429 ;; Once we drop the deprecated feature where mode: is also allowed to 3429 ;; Once we drop the deprecated feature where mode: is also allowed to
3430 ;; specify minor-modes (ie, there can be more than one "mode:"), we can 3430 ;; specify minor-modes (ie, there can be more than one "mode:"), we can
3431 ;; remove this section and just let (hack-local-variables t) handle it. 3431 ;; remove this section and just let (hack-local-variables t) handle it.
@@ -3456,100 +3456,96 @@ we don't actually set it to the same mode the buffer already has."
3456 (push (intern (concat (downcase (buffer-substring (point) end)) 3456 (push (intern (concat (downcase (buffer-substring (point) end))
3457 "-mode")) 3457 "-mode"))
3458 modes)))) 3458 modes))))
3459 ;; If we found modes to use, invoke them now, outside the save-excursion. 3459 (or
3460 (if modes 3460 ;; If we found modes to use, invoke them now, outside the save-excursion.
3461 (catch 'nop 3461 ;; Presume `modes' holds a major mode followed by minor modes.
3462 (dolist (mode (nreverse modes)) 3462 (let ((done ()))
3463 (if (not (functionp mode)) 3463 (dolist (mode (nreverse modes))
3464 (message "Ignoring unknown mode `%s'" mode) 3464 (if (eq done :keep)
3465 (setq done t) 3465 ;; `keep-mode-if-same' is set and the (major) mode
3466 (or (set-auto-mode-0 mode keep-mode-if-same) 3466 ;; was already set. Refrain from calling the following
3467 ;; continuing would call minor modes again, toggling them off 3467 ;; minor modes since they have already been set.
3468 (throw 'nop nil)))))) 3468 ;; It was especially important in the past when calling
3469 ;; Check for auto-mode-alist entry in dir-locals. 3469 ;; minor modes without an arg would toggle them, but it's
3470 (unless done 3470 ;; still preferable to avoid re-enabling them,
3471 (with-demoted-errors "Directory-local variables error: %s" 3471 nil
3472 ;; Note this is a no-op if enable-local-variables is nil. 3472 (let ((res (set-auto-mode-0 mode keep-mode-if-same)))
3473 (let* ((mode-alist (cdr (hack-dir-local--get-variables 3473 (setq done (or res done)))))
3474 (lambda (key) (eq key 'auto-mode-alist)))))) 3474 done)
3475 (setq done (set-auto-mode--apply-alist mode-alist 3475 ;; Check for auto-mode-alist entry in dir-locals.
3476 keep-mode-if-same t))))) 3476 (with-demoted-errors "Directory-local variables error: %s"
3477 (and (not done) 3477 ;; Note this is a no-op if enable-local-variables is nil.
3478 (setq mode (hack-local-variables t (not try-locals))) 3478 (let* ((mode-alist (cdr (hack-dir-local--get-variables
3479 (not (memq mode modes)) ; already tried and failed 3479 (lambda (key) (eq key 'auto-mode-alist))))))
3480 (if (not (functionp mode)) 3480 (set-auto-mode--apply-alist mode-alist keep-mode-if-same t)))
3481 (message "Ignoring unknown mode `%s'" mode) 3481 (let ((mode (hack-local-variables t (not try-locals))))
3482 (setq done t) 3482 (unless (memq mode modes) ; already tried and failed
3483 (set-auto-mode-0 mode keep-mode-if-same))) 3483 (set-auto-mode-0 mode keep-mode-if-same)))
3484 ;; If we didn't, look for an interpreter specified in the first line. 3484 ;; If we didn't, look for an interpreter specified in the first line.
3485 ;; As a special case, allow for things like "#!/bin/env perl", which 3485 ;; As a special case, allow for things like "#!/bin/env perl", which
3486 ;; finds the interpreter anywhere in $PATH. 3486 ;; finds the interpreter anywhere in $PATH.
3487 (and (not done) 3487 (when-let
3488 (setq mode (save-excursion 3488 ((interp (save-excursion
3489 (goto-char (point-min)) 3489 (goto-char (point-min))
3490 (if (looking-at auto-mode-interpreter-regexp) 3490 (if (looking-at auto-mode-interpreter-regexp)
3491 (match-string 2)))) 3491 (match-string 2))))
3492 ;; Map interpreter name to a mode, signaling we're done at the 3492 ;; Map interpreter name to a mode, signaling we're done at the
3493 ;; same time. 3493 ;; same time.
3494 (setq done (assoc-default 3494 (mode (assoc-default
3495 (file-name-nondirectory mode) 3495 (file-name-nondirectory interp)
3496 (mapcar (lambda (e) 3496 (mapcar (lambda (e)
3497 (cons 3497 (cons
3498 (format "\\`%s\\'" (car e)) 3498 (format "\\`%s\\'" (car e))
3499 (cdr e))) 3499 (cdr e)))
3500 interpreter-mode-alist) 3500 interpreter-mode-alist)
3501 #'string-match-p)) 3501 #'string-match-p)))
3502 ;; If we found an interpreter mode to use, invoke it now. 3502 ;; If we found an interpreter mode to use, invoke it now.
3503 (set-auto-mode-0 done keep-mode-if-same)) 3503 (set-auto-mode-0 mode keep-mode-if-same))
3504 ;; Next try matching the buffer beginning against magic-mode-alist. 3504 ;; Next try matching the buffer beginning against magic-mode-alist.
3505 (unless done 3505 (let ((mode (save-excursion
3506 (if (setq done (save-excursion 3506 (goto-char (point-min))
3507 (goto-char (point-min)) 3507 (save-restriction
3508 (save-restriction 3508 (narrow-to-region (point-min)
3509 (narrow-to-region (point-min) 3509 (min (point-max)
3510 (min (point-max) 3510 (+ (point-min) magic-mode-regexp-match-limit)))
3511 (+ (point-min) magic-mode-regexp-match-limit))) 3511 (assoc-default
3512 (assoc-default 3512 nil magic-mode-alist
3513 nil magic-mode-alist 3513 (lambda (re _dummy)
3514 (lambda (re _dummy) 3514 (cond
3515 (cond 3515 ((functionp re)
3516 ((functionp re) 3516 (funcall re))
3517 (funcall re)) 3517 ((stringp re)
3518 ((stringp re) 3518 (let ((case-fold-search nil))
3519 (let ((case-fold-search nil)) 3519 (looking-at re)))
3520 (looking-at re))) 3520 (t
3521 (t 3521 (error
3522 (error 3522 "Problem in magic-mode-alist with element %s"
3523 "Problem in magic-mode-alist with element %s" 3523 re)))))))))
3524 re)))))))) 3524 (set-auto-mode-0 mode keep-mode-if-same))
3525 (set-auto-mode-0 done keep-mode-if-same))) 3525 ;; Next compare the filename against the entries in auto-mode-alist.
3526 ;; Next compare the filename against the entries in auto-mode-alist. 3526 (set-auto-mode--apply-alist auto-mode-alist
3527 (unless done 3527 keep-mode-if-same nil)
3528 (setq done (set-auto-mode--apply-alist auto-mode-alist 3528 ;; Next try matching the buffer beginning against magic-fallback-mode-alist.
3529 keep-mode-if-same nil))) 3529 (let ((mode (save-excursion
3530 ;; Next try matching the buffer beginning against magic-fallback-mode-alist. 3530 (goto-char (point-min))
3531 (unless done 3531 (save-restriction
3532 (if (setq done (save-excursion 3532 (narrow-to-region (point-min)
3533 (goto-char (point-min)) 3533 (min (point-max)
3534 (save-restriction 3534 (+ (point-min) magic-mode-regexp-match-limit)))
3535 (narrow-to-region (point-min) 3535 (assoc-default nil magic-fallback-mode-alist
3536 (min (point-max) 3536 (lambda (re _dummy)
3537 (+ (point-min) magic-mode-regexp-match-limit))) 3537 (cond
3538 (assoc-default nil magic-fallback-mode-alist 3538 ((functionp re)
3539 (lambda (re _dummy) 3539 (funcall re))
3540 (cond 3540 ((stringp re)
3541 ((functionp re) 3541 (let ((case-fold-search nil))
3542 (funcall re)) 3542 (looking-at re)))
3543 ((stringp re) 3543 (t
3544 (let ((case-fold-search nil)) 3544 (error
3545 (looking-at re))) 3545 "Problem with magic-fallback-mode-alist element: %s"
3546 (t 3546 re)))))))))
3547 (error 3547 (set-auto-mode-0 mode keep-mode-if-same))
3548 "Problem with magic-fallback-mode-alist element: %s" 3548 (set-buffer-major-mode (current-buffer)))))
3549 re))))))))
3550 (set-auto-mode-0 done keep-mode-if-same)))
3551 (unless done
3552 (set-buffer-major-mode (current-buffer)))))
3553 3549
3554(defvar-local set-auto-mode--last nil 3550(defvar-local set-auto-mode--last nil
3555 "Remember the mode we have set via `set-auto-mode-0'.") 3551 "Remember the mode we have set via `set-auto-mode-0'.")
@@ -3583,18 +3579,29 @@ and it is meant to be modified by packages rather than users.")
3583 "Apply MODE and return it. 3579 "Apply MODE and return it.
3584If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of 3580If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of
3585any aliases and compared to current major mode. If they are the 3581any aliases and compared to current major mode. If they are the
3586same, do nothing and return nil." 3582same, do nothing and return `:keep'.
3587 (unless (and keep-mode-if-same 3583Return nil if MODE could not be applied."
3588 (or (eq (indirect-function mode) 3584 (when mode
3589 (indirect-function major-mode)) 3585 (if (and keep-mode-if-same
3590 (and set-auto-mode--last 3586 (or (eq (indirect-function mode)
3591 (eq mode (car set-auto-mode--last)) 3587 (indirect-function major-mode))
3592 (eq major-mode (cdr set-auto-mode--last))))) 3588 (and set-auto-mode--last
3593 (when mode 3589 (eq mode (car set-auto-mode--last))
3594 (funcall (major-mode-remap mode)) 3590 (eq major-mode (cdr set-auto-mode--last)))))
3595 (unless (eq mode major-mode) 3591 :keep
3596 (setq set-auto-mode--last (cons mode major-mode))) 3592 (let ((modefun (major-mode-remap mode)))
3597 mode))) 3593 (if (not (functionp modefun))
3594 (progn
3595 (message "Ignoring unknown mode `%s'%s" mode
3596 (if (eq mode modefun) ""
3597 (format " (remapped to `%S')" modefun)))
3598 nil)
3599 (funcall modefun)
3600 (unless (or (eq mode major-mode) ;`set-auto-mode--last' is overkill.
3601 ;; `modefun' is something like a minor mode.
3602 (local-variable-p 'set-auto-mode--last))
3603 (setq set-auto-mode--last (cons mode major-mode)))
3604 mode)))))
3598 3605
3599(defvar file-auto-mode-skip "^\\(#!\\|'\\\\\"\\)" 3606(defvar file-auto-mode-skip "^\\(#!\\|'\\\\\"\\)"
3600 "Regexp of lines to skip when looking for file-local settings. 3607 "Regexp of lines to skip when looking for file-local settings.
@@ -4201,8 +4208,9 @@ major-mode."
4201 (not (string-match 4208 (not (string-match
4202 "-minor\\'" 4209 "-minor\\'"
4203 (setq val2 (downcase (symbol-name val))))) 4210 (setq val2 (downcase (symbol-name val)))))
4204 ;; Allow several mode: elements. 4211 (let ((mode (intern (concat val2 "-mode"))))
4205 (push (intern (concat val2 "-mode")) result)) 4212 (when (fboundp (major-mode-remap mode))
4213 (setq result mode))))
4206 (cond ((eq var 'coding)) 4214 (cond ((eq var 'coding))
4207 ((eq var 'lexical-binding) 4215 ((eq var 'lexical-binding)
4208 (unless hack-local-variables--warned-lexical 4216 (unless hack-local-variables--warned-lexical
@@ -4233,10 +4241,7 @@ major-mode."
4233 val) 4241 val)
4234 result)))))) 4242 result))))))
4235 (forward-line 1))))))) 4243 (forward-line 1)))))))
4236 (if (eq handle-mode t) 4244 result))
4237 ;; Return the final mode: setting that's defined.
4238 (car (seq-filter #'fboundp result))
4239 result)))
4240 4245
4241(defun hack-local-variables-apply () 4246(defun hack-local-variables-apply ()
4242 "Apply the elements of `file-local-variables-alist'. 4247 "Apply the elements of `file-local-variables-alist'.