branch: elpa/php-mode
commit 78e2c8b9a21bb0f4db3f22dbcba42b283cd5b2ef
Merge: deab4812fd d89e90d401
Author: USAMI Kenta <[email protected]>
Commit: GitHub <[email protected]>

    Merge pull request #817 from emacs-php/fix/blade-fallback
    
    Fall back on an HTML mode for Blade when web-mode is missing
---
 lisp/php.el            | 36 ++++++++++++++++++++++++++++++++----
 tests/php-mode-test.el | 49 +++++++++++++++++++++++++++++++++++++++++++++++++
 2 files changed, 81 insertions(+), 4 deletions(-)

diff --git a/lisp/php.el b/lisp/php.el
index 2e072376f2..b02fb41a85 100644
--- a/lisp/php.el
+++ b/lisp/php.el
@@ -180,6 +180,20 @@ a completion list."
   :tag "PHP Blade Template Major Mode"
   :type 'function)
 
+(defcustom php-blade-template-major-mode-fallback '(mhtml-mode html-mode)
+  "Major modes to fall back on for a Blade template.
+
+Used when `php-blade-template-major-mode' — `web-mode' by default — is
+not installed.  The first entry whose function is defined wins; when
+none is, `php-default-major-mode' is used.
+
+A Blade template is mostly HTML plus Blade's own directives, so an HTML
+mode reads it far better than `php-mode' does.  Set this to nil to opt
+out and get `php-default-major-mode' instead."
+  :group 'php
+  :tag "PHP Blade Template Major Mode Fallback"
+  :type '(repeat function))
+
 (defcustom php-template-mode-alist
   `(("\\.blade" . ,php-blade-template-major-mode)
     ("\\.phpt\\'" . ,(if (fboundp 'phpt-mode) 'phpt-mode 
php-default-major-mode))
@@ -623,6 +637,11 @@ indentation."
         (setq php--buffer-has-html-tag-cache (cons tick result))
         result))))
 
+(defun php--blade-template-fallback-mode ()
+  "Return the first available mode of 
`php-blade-template-major-mode-fallback'."
+  (cl-loop for mode in php-blade-template-major-mode-fallback
+           thereis (and (fboundp mode) mode)))
+
 (defun php-derivation-major-mode ()
   "Return major mode for PHP file by file-name and its content."
   (let ((mode (assoc-default buffer-file-name
@@ -638,10 +657,19 @@ indentation."
         (when (php-buffer-has-html-tag)
           (setq mode php-html-template-major-mode)))))
     (when (and mode (not (fboundp mode)))
-      (if (string-match-p "\\.blade\\." buffer-file-name)
-          (warn "php-mode is NOT support blade template. %s"
-                "Please install `web-mode' package")
-        (setq mode nil)))
+      ;; A Blade template is not PHP, so `php-default-major-mode' cannot
+      ;; read it at all; degrade to an HTML mode instead.  Every other
+      ;; template is PHP with HTML in it, and keeps falling back on
+      ;; `php-default-major-mode' below.
+      (setq mode (when (string-match-p "\\.blade\\." buffer-file-name)
+                   (let ((fallback (php--blade-template-fallback-mode)))
+                     (warn "`%s' is not available for this Blade template; %s.
+Install the `web-mode' package for full Blade support."
+                           mode
+                           (if fallback
+                               (format "using `%s' instead" fallback)
+                             (format "falling back on `%s'" 
php-default-major-mode)))
+                     fallback))))
     (or mode php-default-major-mode)))
 
 ;;;###autoload
diff --git a/tests/php-mode-test.el b/tests/php-mode-test.el
index da662856b1..09a33754f2 100644
--- a/tests/php-mode-test.el
+++ b/tests/php-mode-test.el
@@ -734,6 +734,55 @@ those going through `php-mode-maybe' did not: the same 
buffer became
           (set-buffer-modified-p nil)
           (setq buffer-file-name nil))))))
 
+(defun php-mode-test--derive (file-name &rest body-text)
+  "Return the mode `php-derivation-major-mode' picks for FILE-NAME.
+BODY-TEXT is inserted into the buffer first."
+  (with-temp-buffer
+    (setq buffer-file-name (expand-file-name file-name 
temporary-file-directory))
+    (unwind-protect
+        (progn
+          (apply #'insert body-text)
+          (php-derivation-major-mode))
+      (set-buffer-modified-p nil)
+      (setq buffer-file-name nil))))
+
+(ert-deftest php-mode-test-blade-template-fallback ()
+  "A Blade template degrades to an HTML mode when `web-mode' is missing.
+
+A `.blade.php' file is mostly HTML plus Blade directives, so it is not
+PHP and `php-default-major-mode' cannot read it.  Before the fallback
+existed, `php-derivation-major-mode' returned the unavailable
+`web-mode' anyway and `php-mode-maybe' then failed with
+`void-function'."
+  (let ((php-blade-template-major-mode 'php-mode-test--absent-web-mode)
+        (php-template-mode-alist '(("\\.blade" . 
php-mode-test--absent-web-mode))))
+    (should-not (fboundp 'php-mode-test--absent-web-mode))
+    ;; The chosen fallback must be usable: this is what `php-mode-maybe'
+    ;; funcalls, so an unavailable mode would signal `void-function'.
+    (let ((php-blade-template-major-mode-fallback '(html-mode)))
+      (should (eq 'html-mode (php-mode-test--derive "welcome.blade.php" 
"@extends('x')\n"))))
+    ;; Unavailable entries are skipped.
+    (let ((php-blade-template-major-mode-fallback 
'(php-mode-test--absent-web-mode html-mode)))
+      (should (eq 'html-mode (php-mode-test--derive "welcome.blade.php" 
"@extends('x')\n"))))
+    ;; Opting out falls back on `php-default-major-mode'.
+    (let ((php-blade-template-major-mode-fallback nil)
+          (php-default-major-mode 'php-mode))
+      (should (eq 'php-mode (php-mode-test--derive "welcome.blade.php" 
"@extends('x')\n"))))))
+
+(ert-deftest php-mode-test-html-template-fallback-unchanged ()
+  "A missing `php-html-template-major-mode' still derives to PHP.
+
+Only Blade degrades to an HTML mode.  `php-project-php-file-as-template'
+defaults to `auto', so any .php file holding an HTML tag reaches this
+path; sending those to an HTML mode would take most PHP files away from
+`php-mode' for anyone without `web-mode'."
+  (let ((php-html-template-major-mode 'php-mode-test--absent-web-mode)
+        (php-project-php-file-as-template 'auto)
+        (php-default-major-mode 'php-mode)
+        (php-template-mode-alist nil))
+    (should-not (fboundp 'php-mode-test--absent-web-mode))
+    (should (eq 'php-mode (php-mode-test--derive "page.php" "<div>\n<?php echo 
'hi'; ?>\n</div>\n")))))
+
 (ert-deftest php-mode-test-php74 ()
   "Test highlighting language constructs added in PHP 7.4."
   (with-php-mode-test ("7.4/arrow-function.php" :faces t))

Reply via email to