diff --git a/lisp/files.el b/lisp/files.el index 9c8914bfc50..0707dc09ac8 100644 --- a/lisp/files.el +++ b/lisp/files.el @@ -3010,10 +3010,10 @@ auto-mode-alist ("\\.dbk\\'" . xml-mode) ("\\.dtd\\'" . sgml-mode) ("\\.ds\\(ss\\)?l\\'" . dsssl-mode) - ("\\.js[mx]?\\'" . javascript-mode) + ("\\.js[mx]?\\'" . :js) ;; https://en.wikipedia.org/wiki/.har - ("\\.har\\'" . javascript-mode) - ("\\.json\\'" . js-json-mode) + ("\\.har\\'" . :js) + ("\\.json\\'" . :json) ("\\.[ds]?va?h?\\'" . verilog-mode) ("\\.by\\'" . bovine-grammar-mode) ("\\.wy\\'" . wisent-grammar-mode) @@ -3150,6 +3150,9 @@ auto-mode-alist Visiting a file whose name matches REGEXP specifies FUNCTION as the mode function to use. FUNCTION will be called, unless it is nil. +FUNCTION can also be a keyword denoting a language, to be looked +up in `major-mode-remap-alist'. + If the element has the form (REGEXP FUNCTION NON-NIL), then after calling FUNCTION (if it's not nil), we delete the suffix that matched REGEXP and search the list again for another match. @@ -3206,10 +3209,10 @@ interpreter-mode-alist ("emacs" . emacs-lisp-mode))) "Alist mapping interpreter names to major modes. This is used for files whose first lines match `auto-mode-interpreter-regexp'. -Each element looks like (REGEXP . MODE). +Each element looks like (REGEXP . MODE-OR-LANGUAGE). If REGEXP matches the entire name (minus any directory part) of the interpreter specified in the first line of a script, enable -major mode MODE. +MODE-OR-LANGUAGE. See also `auto-mode-alist'.") @@ -3554,7 +3557,7 @@ major-mode-remap-alist ;; same one we already have, don't actually reset it. We don't want to lose ;; minor modes such as Font Lock. (defun set-auto-mode-0 (mode &optional keep-mode-if-same) - "Apply MODE and return it. + "Apply MODE and return it. MODE can be a function of language. If optional arg KEEP-MODE-IF-SAME is non-nil, MODE is chased of any aliases and compared to current major mode. If they are the same, do nothing and return nil." @@ -3565,11 +3568,43 @@ set-auto-mode-0 (eq mode (car set-auto-mode--last)) (eq major-mode (cdr set-auto-mode--last))))) (when mode - (funcall (alist-get mode major-mode-remap-alist mode)) + ;; XXX: When there's no mapping for `:', we could also + ;; look for a function called `-mode'. + (funcall (alist-get mode major-mode-remap-alist (if (keywordp mode) + #'fundamental-mode + mode))) + (when (keywordp mode) ;Perhaps do that unconditionally. + (run-hooks (intern (format "%s-language-hook" (buffer-language))))) (unless (eq mode major-mode) (setq set-auto-mode--last (cons mode major-mode))) mode))) +(defun buffer-language () + "Return the language of the current buffer. +A language is a lowercase keyword with the name of the language." + ;; Alternatively, we could go through all the matchers in + ;; auto-mode-alist, interpreter-mode-alist, + ;; magic-fallback-mode-alist here, possibly using a cache keyed on + ;; buffer-file-name. But that's probably an overkill: if the user + ;; changes the settings, they can call `M-x revert-buffer' at the end. + (if (keywordp (car set-auto-mode--last)) + (car set-auto-mode--last) + ;; Backward compatibility. + (intern (format ":%s" (replace-regexp-in-string "\\(?:-ts\\)?-mode\\'" "" + (symbol-name major-mode)))))) + +(defun set-buffer-language (language) + "Set the language of the current buffer. +And switch the major mode appropriately." + (interactive + (list (let* ((ct (mapcan + (lambda (pair) (and (keywordp (car pair)) + (list (symbol-name (car pair))))) + major-mode-remap-alist)) + (lang (completing-read "Language: " ct))) + (and lang (intern lang))))) + (set-auto-mode-0 language)) + (defvar file-auto-mode-skip "^\\(#!\\|'\\\\\"\\)" "Regexp of lines to skip when looking for file-local settings. If the first line matches this regular expression, then the -*-...-*- file- diff --git a/lisp/progmodes/js.el b/lisp/progmodes/js.el index 0115feb0e97..218a0f05541 100644 --- a/lisp/progmodes/js.el +++ b/lisp/progmodes/js.el @@ -3895,8 +3895,7 @@ js-ts-mode nil nil))) (treesit-major-mode-setup) - (add-to-list 'auto-mode-alist - '("\\(\\.js[mx]\\|\\.har\\)\\'" . js-ts-mode)))) + (add-to-list 'major-mode-remap-alist '(:js . js-ts-mode)))) (defvar js-ts--s-p-query (when (treesit-available-p) @@ -3977,7 +3976,11 @@ js-jsx--comment-region ;;;###autoload (dolist (name (list "node" "nodejs" "gjs" "rhino")) - (add-to-list 'interpreter-mode-alist (cons (purecopy name) 'js-mode))) + (add-to-list 'interpreter-mode-alist (cons (purecopy name) :js))) + +;;;###autoload +(add-to-list 'major-mode-remap-alist '(:js . js-mode)) +(add-to-list 'major-mode-remap-alist '(:json . js-json-mode)) (provide 'js) diff --git a/lisp/progmodes/json-ts-mode.el b/lisp/progmodes/json-ts-mode.el index 32bc10bbda9..453554a92dd 100644 --- a/lisp/progmodes/json-ts-mode.el +++ b/lisp/progmodes/json-ts-mode.el @@ -165,8 +165,7 @@ json-ts-mode (treesit-major-mode-setup)) (if (treesit-ready-p 'json) - (add-to-list 'auto-mode-alist - '("\\.json\\'" . json-ts-mode))) + (add-to-list 'major-mode-remap-alist '(:json . json-ts-mode))) (provide 'json-ts-mode) diff --git a/lisp/progmodes/python.el b/lisp/progmodes/python.el index 1148da11a06..0d560ee1a5f 100644 --- a/lisp/progmodes/python.el +++ b/lisp/progmodes/python.el @@ -288,9 +288,9 @@ outline-heading-end-regexp (autoload 'help-function-arglist "help-fns") ;;;###autoload -(add-to-list 'auto-mode-alist (cons (purecopy "\\.py[iw]?\\'") 'python-mode)) +(add-to-list 'auto-mode-alist (cons (purecopy "\\.py[iw]?\\'") :python)) ;;;###autoload -(add-to-list 'interpreter-mode-alist (cons (purecopy "python[0-9.]*") 'python-mode)) +(add-to-list 'interpreter-mode-alist (cons (purecopy "python[0-9.]*") :python)) (defgroup python nil "Python Language's flying circus support for Emacs." @@ -6992,8 +6992,7 @@ python-ts-mode (when python-indent-guess-indent-offset (python-indent-guess-indent-offset)) - (add-to-list 'auto-mode-alist '("\\.py[iw]?\\'" . python-ts-mode)) - (add-to-list 'interpreter-mode-alist '("python[0-9.]*" . python-ts-mode)))) + (add-to-list 'major-mode-remap-alist '(:python . python-ts-mode)))) ;;; Completion predicates for M-x ;; Commands that only make sense when editing Python code. diff --git a/lisp/progmodes/ruby-mode.el b/lisp/progmodes/ruby-mode.el index 999fbebfb08..6975cf5be0a 100644 --- a/lisp/progmodes/ruby-mode.el +++ b/lisp/progmodes/ruby-mode.el @@ -2703,11 +2703,13 @@ ruby-mode "\\|Puppet\\|Berks\\|Brew\\|Fast" "\\|Vagrant\\|Guard\\|Pod\\)file" "\\)\\'")) - 'ruby-mode)) + :ruby)) +;;;###autoload +(add-to-list 'major-mode-remap-alist '(:ruby . ruby-mode)) ;;;###autoload (dolist (name (list "ruby" "rbx" "jruby" "j?ruby\\(?:[0-9.]+\\)")) - (add-to-list 'interpreter-mode-alist (cons (purecopy name) 'ruby-mode))) + (add-to-list 'interpreter-mode-alist (cons (purecopy name) :ruby))) (provide 'ruby-mode) diff --git a/lisp/progmodes/ruby-ts-mode.el b/lisp/progmodes/ruby-ts-mode.el index 598eaa461ff..9fb253ba706 100644 --- a/lisp/progmodes/ruby-ts-mode.el +++ b/lisp/progmodes/ruby-ts-mode.el @@ -35,8 +35,8 @@ ;; `treesit-extra-load-path'. ;; This mode doesn't associate itself with .rb files automatically. -;; You can do that either by prepending to the value of -;; `auto-mode-alist', or using `major-mode-remap-alist'. +;; You can do that using `major-mode-remap-alist' (by adding an entry +;; for `:ruby'). ;; Tree Sitter brings a lot of power and versitility which can be ;; broken into these features. @@ -1197,18 +1197,7 @@ ruby-ts-mode (setq-local syntax-propertize-function #'ruby-ts--syntax-propertize)) (if (treesit-ready-p 'ruby) - ;; Copied from ruby-mode.el. - (add-to-list 'auto-mode-alist - (cons (concat "\\(?:\\.\\(?:" - "rbw?\\|ru\\|rake\\|thor" - "\\|jbuilder\\|rabl\\|gemspec\\|podspec" - "\\)" - "\\|/" - "\\(?:Gem\\|Rake\\|Cap\\|Thor" - "\\|Puppet\\|Berks\\|Brew" - "\\|Vagrant\\|Guard\\|Pod\\)file" - "\\)\\'") - 'ruby-ts-mode))) + (add-to-list 'major-mode-remap-alist '(:ruby . ruby-ts-mode))) (provide 'ruby-ts-mode)