diff options
| author | Stefan Monnier | 2024-03-14 22:15:41 -0400 |
|---|---|---|
| committer | Stefan Monnier | 2024-03-14 22:15:41 -0400 |
| commit | 9422a6737447b186ca017929da79985cef7898a8 (patch) | |
| tree | d7c71c1153159360a53486036398687b937f6d3e | |
| parent | c8c0d0a9550620adb111bf5d9e0155332498a6bf (diff) | |
| download | emacs-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.el | 231 |
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 | |||
| 3425 | we don't actually set it to the same mode the buffer already has." | 3425 | we 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. |
| 3584 | If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of | 3580 | If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of |
| 3585 | any aliases and compared to current major mode. If they are the | 3581 | any aliases and compared to current major mode. If they are the |
| 3586 | same, do nothing and return nil." | 3582 | same, do nothing and return `:keep'. |
| 3587 | (unless (and keep-mode-if-same | 3583 | Return 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'. |