From 82ab795ed6c45ce5f45e7a37f23c9129fde37639 Mon Sep 17 00:00:00 2001 From: USAMI Kenta Date: Thu, 16 Jul 2026 23:20:39 +0900 Subject: [PATCH] Honor major-mode-remap-alist in php-mode-maybe Emacs's built-in php-ts-mode registers (add-to-list 'treesit-major-mode-remap-alist '(php-mode . php-ts-mode)) with the comment "To be able to toggle between an external package and core ts-mode", and `treesit-enabled-modes' copies that entry into `major-mode-remap-alist' when the user opts in. So a user who installs this package and enables php-ts-mode is asking for php-ts-mode. They did not get it for .php files. `set-auto-mode' applies the remapping to whatever `auto-mode-alist' names, so entries pointing straight at `php-mode' honored it, but ".php"/".phtml" go through `php-mode-maybe', which is not itself a remap target and called the derived mode directly. The same buffer therefore opened in different modes depending on the extension: a.stub auto-mode-alist=php-mode -> php-ts-mode (remapped) a.php auto-mode-alist=php-mode-maybe -> php-mode (not remapped) Resolve the derived mode through `major-mode-remap' so `php-mode-maybe' behaves as if `auto-mode-alist' had named the mode directly. The function is Emacs 30 or later, hence the `fboundp' guard; on older Emacs the mode is used unchanged, as before. Note that no adjustment is needed for `php-base-mode': php-ts-mode declares `php-mode' as an extra parent via `derived-mode-add-parents', and `php-mode' derives from `php-base-mode', so once this package is loaded `derived-mode-all-parents' of php-ts-mode already resolves to (php-ts-mode php-mode php-base-mode prog-mode). --- lisp/php.el | 11 +++++++++-- tests/php-mode-test.el | 35 +++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+), 2 deletions(-) diff --git a/lisp/php.el b/lisp/php.el index 671ff1c7..2e072376 100644 --- a/lisp/php.el +++ b/lisp/php.el @@ -646,10 +646,17 @@ indentation." ;;;###autoload (defun php-mode-maybe () - "Select PHP mode or other major mode." + "Select PHP mode or other major mode. + +The selected mode is resolved through `major-mode-remap-alist' the way +`set-auto-mode' would have done had `auto-mode-alist' named it directly, +so that a remapping the user asked for still applies. Emacs's built-in +`php-ts-mode' registers (php-mode . php-ts-mode) for exactly this +purpose, and `treesit-enabled-modes' installs it when the user opts in." (interactive) (run-hooks php-mode-maybe-hook) - (funcall (php-derivation-major-mode))) + (let ((mode (php-derivation-major-mode))) + (funcall (if (fboundp 'major-mode-remap) (major-mode-remap mode) mode)))) ;;;###autoload (defun php-current-class () diff --git a/tests/php-mode-test.el b/tests/php-mode-test.el index 8985c2d6..da662856 100644 --- a/tests/php-mode-test.el +++ b/tests/php-mode-test.el @@ -699,6 +699,41 @@ than depending on `poly-php', because that package pulls in a released (set-buffer-modified-p nil) (setq buffer-file-name nil))))) +(define-derived-mode php-mode-test--stub-ts-mode prog-mode "PHP/stub-ts" + "Stand-in for `php-ts-mode' in `php-mode-test-mode-remap'. +Using a stub keeps the test independent of whether the PHP tree-sitter +grammar is installed.") + +(ert-deftest php-mode-test-mode-remap () + "`php-mode-maybe' must honor `major-mode-remap-alist'. + +Emacs's built-in `php-ts-mode' registers (php-mode . php-ts-mode) in +`treesit-major-mode-remap-alist' so that users can toggle between this +package and the core tree-sitter mode; `treesit-enabled-modes' copies +that entry into `major-mode-remap-alist' when they opt in. + +`set-auto-mode' applies the remapping to whatever `auto-mode-alist' +names, so file patterns pointing straight at `php-mode' honored it while +those going through `php-mode-maybe' did not: the same buffer became +`php-mode' for \".php\" but the remapped mode for \".stub\"." + (skip-unless (fboundp 'major-mode-remap)) + (dolist (probe '((((php-mode . php-mode-test--stub-ts-mode)) + . php-mode-test--stub-ts-mode) + ;; Without a remapping the derived mode is used as-is. + (nil . php-mode))) + (let ((major-mode-remap-alist (car probe))) + (with-temp-buffer + ;; `php-derivation-major-mode' consults `buffer-file-name'. + (setq buffer-file-name (expand-file-name "remap.php" temporary-file-directory)) + (unwind-protect + (progn + (insert "