Skip to content

Commit 78e2c8b

Browse files
authored
Merge pull request #817 from emacs-php/fix/blade-fallback
Fall back on an HTML mode for Blade when web-mode is missing
2 parents deab481 + d89e90d commit 78e2c8b

2 files changed

Lines changed: 81 additions & 4 deletions

File tree

lisp/php.el

Lines changed: 32 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,20 @@ a completion list."
180180
:tag "PHP Blade Template Major Mode"
181181
:type 'function)
182182

183+
(defcustom php-blade-template-major-mode-fallback '(mhtml-mode html-mode)
184+
"Major modes to fall back on for a Blade template.
185+
186+
Used when `php-blade-template-major-mode'`web-mode' by default — is
187+
not installed. The first entry whose function is defined wins; when
188+
none is, `php-default-major-mode' is used.
189+
190+
A Blade template is mostly HTML plus Blade's own directives, so an HTML
191+
mode reads it far better than `php-mode' does. Set this to nil to opt
192+
out and get `php-default-major-mode' instead."
193+
:group 'php
194+
:tag "PHP Blade Template Major Mode Fallback"
195+
:type '(repeat function))
196+
183197
(defcustom php-template-mode-alist
184198
`(("\\.blade" . ,php-blade-template-major-mode)
185199
("\\.phpt\\'" . ,(if (fboundp 'phpt-mode) 'phpt-mode php-default-major-mode))
@@ -623,6 +637,11 @@ indentation."
623637
(setq php--buffer-has-html-tag-cache (cons tick result))
624638
result))))
625639

640+
(defun php--blade-template-fallback-mode ()
641+
"Return the first available mode of `php-blade-template-major-mode-fallback'."
642+
(cl-loop for mode in php-blade-template-major-mode-fallback
643+
thereis (and (fboundp mode) mode)))
644+
626645
(defun php-derivation-major-mode ()
627646
"Return major mode for PHP file by file-name and its content."
628647
(let ((mode (assoc-default buffer-file-name
@@ -638,10 +657,19 @@ indentation."
638657
(when (php-buffer-has-html-tag)
639658
(setq mode php-html-template-major-mode)))))
640659
(when (and mode (not (fboundp mode)))
641-
(if (string-match-p "\\.blade\\." buffer-file-name)
642-
(warn "php-mode is NOT support blade template. %s"
643-
"Please install `web-mode' package")
644-
(setq mode nil)))
660+
;; A Blade template is not PHP, so `php-default-major-mode' cannot
661+
;; read it at all; degrade to an HTML mode instead. Every other
662+
;; template is PHP with HTML in it, and keeps falling back on
663+
;; `php-default-major-mode' below.
664+
(setq mode (when (string-match-p "\\.blade\\." buffer-file-name)
665+
(let ((fallback (php--blade-template-fallback-mode)))
666+
(warn "`%s' is not available for this Blade template; %s.
667+
Install the `web-mode' package for full Blade support."
668+
mode
669+
(if fallback
670+
(format "using `%s' instead" fallback)
671+
(format "falling back on `%s'" php-default-major-mode)))
672+
fallback))))
645673
(or mode php-default-major-mode)))
646674

647675
;;;###autoload

tests/php-mode-test.el

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -734,6 +734,55 @@ those going through `php-mode-maybe' did not: the same buffer became
734734
(set-buffer-modified-p nil)
735735
(setq buffer-file-name nil))))))
736736

737+
(defun php-mode-test--derive (file-name &rest body-text)
738+
"Return the mode `php-derivation-major-mode' picks for FILE-NAME.
739+
BODY-TEXT is inserted into the buffer first."
740+
(with-temp-buffer
741+
(setq buffer-file-name (expand-file-name file-name temporary-file-directory))
742+
(unwind-protect
743+
(progn
744+
(apply #'insert body-text)
745+
(php-derivation-major-mode))
746+
(set-buffer-modified-p nil)
747+
(setq buffer-file-name nil))))
748+
749+
(ert-deftest php-mode-test-blade-template-fallback ()
750+
"A Blade template degrades to an HTML mode when `web-mode' is missing.
751+
752+
A `.blade.php' file is mostly HTML plus Blade directives, so it is not
753+
PHP and `php-default-major-mode' cannot read it. Before the fallback
754+
existed, `php-derivation-major-mode' returned the unavailable
755+
`web-mode' anyway and `php-mode-maybe' then failed with
756+
`void-function'."
757+
(let ((php-blade-template-major-mode 'php-mode-test--absent-web-mode)
758+
(php-template-mode-alist '(("\\.blade" . php-mode-test--absent-web-mode))))
759+
(should-not (fboundp 'php-mode-test--absent-web-mode))
760+
;; The chosen fallback must be usable: this is what `php-mode-maybe'
761+
;; funcalls, so an unavailable mode would signal `void-function'.
762+
(let ((php-blade-template-major-mode-fallback '(html-mode)))
763+
(should (eq 'html-mode (php-mode-test--derive "welcome.blade.php" "@extends('x')\n"))))
764+
;; Unavailable entries are skipped.
765+
(let ((php-blade-template-major-mode-fallback '(php-mode-test--absent-web-mode html-mode)))
766+
(should (eq 'html-mode (php-mode-test--derive "welcome.blade.php" "@extends('x')\n"))))
767+
;; Opting out falls back on `php-default-major-mode'.
768+
(let ((php-blade-template-major-mode-fallback nil)
769+
(php-default-major-mode 'php-mode))
770+
(should (eq 'php-mode (php-mode-test--derive "welcome.blade.php" "@extends('x')\n"))))))
771+
772+
(ert-deftest php-mode-test-html-template-fallback-unchanged ()
773+
"A missing `php-html-template-major-mode' still derives to PHP.
774+
775+
Only Blade degrades to an HTML mode. `php-project-php-file-as-template'
776+
defaults to `auto', so any .php file holding an HTML tag reaches this
777+
path; sending those to an HTML mode would take most PHP files away from
778+
`php-mode' for anyone without `web-mode'."
779+
(let ((php-html-template-major-mode 'php-mode-test--absent-web-mode)
780+
(php-project-php-file-as-template 'auto)
781+
(php-default-major-mode 'php-mode)
782+
(php-template-mode-alist nil))
783+
(should-not (fboundp 'php-mode-test--absent-web-mode))
784+
(should (eq 'php-mode (php-mode-test--derive "page.php" "<div>\n<?php echo 'hi'; ?>\n</div>\n")))))
785+
737786
(ert-deftest php-mode-test-php74 ()
738787
"Test highlighting language constructs added in PHP 7.4."
739788
(with-php-mode-test ("7.4/arrow-function.php" :faces t))

0 commit comments

Comments
 (0)