Subject: bug#78994: closed (Re: bug#78994: 31.0.50; [PATCH] php-ts-mode: `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS and HTML.)

From: [email-masked] (GNU bug Tracking System)
Date: Sat, 09 Aug 2025 12:59:02 +0000

Your bug report

#78994: 31.0.50; [PATCH] php-ts-mode: `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS  and HTML.

which was filed against the emacs package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
78994: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=78994
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: bug#78994: 31.0.50; [PATCH] php-ts-mode: `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS and HTML.
From: Eli Zaretskii <[email-masked]>
To: Vincenzo Pupillo <[email-masked]>
Cc: [email-masked]
Date: Sat, 09 Aug 2025 15:58:39 +0300
List-Id: <debbugs-submit.debbugs.gnu.org>

> From: Vincenzo Pupillo <[email-masked]>
> Cc: [email-masked]
> Date: Thu, 07 Aug 2025 22:07:45 +0200
> 
> Ciao Eli, thank you. 
> Attached is the new version of the patch. 

Thanks, now installed on the master branch, and closing the bug.

Subject: 31.0.50; [PATCH] php-ts-mode: `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS and HTML.
From: Vincenzo Pupillo <[email-masked]>
To: [email-masked]
Date: Fri, 11 Jul 2025 17:35:38 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

Ciao,
this patch replaces the dependency on {js,css,html}-ts-mode with the 
dependency on ‘mhtml-ts-mode’. 
Code maintenance should now be easier. 
Additional benefits are:
1. Imenu now exposes symbols for all of all languages,
2. navigation now works correctly for all languages,
3. outline works for all languages.
Additional new features are:
1. indentation of PHP in mixed buffers with HTML now works much better and 
allows three different behaviors, an option allows you to choose the behavior.
2. a new feature shows where PHP ini files are both locally and remotely, if 
the buffer is associated with a remote PHP file.

The patch is a little long, but I hope it's okay.
Thank you.

Vincenzo
From 2b2f3c022f2096b112fc4a600f24200eed0ab1e0 Mon Sep 17 00:00:00 2001
From: Vincenzo Pupillo <[email-masked]>
Date: Thu, 10 Jul 2025 17:20:34 +0200
Subject: [PATCH] `php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS
 and HTML.
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

The direct dependence on js-ts-mode, css-ts-mode and html-ts-mode has
now been replaced by ‘mhtml-ts-mode’.
Additional benefits are:
1. Imenu now exposes symbols for all of all languages,
2. navigation now works correctly for all languages,
3. outline works for all languages.

Additional new features are:
1. indentation of PHP in mixed buffers with HTML now works much better
and allows three different behaviors, an option allows you to choose the
behavior.
2. a new feature shows where PHP ini files are both locally and
remotely, if the buffer is associated with a remote PHP file.

* etc/NEWS: Mention new command and options.
* lisp/textmodes/mhtml-ts-mode.el
(mhtml-ts-mode--range-settings): New variable that store range settings.
(mhtml-ts-mode--treesit-aggregated-outline-predicate): New variable that
store outline predicates.
(mhtml-ts-mode): Use those two new variables.
* lisp/progmodes/php-ts-mode.el Update doc string. Removed old
dependecies.
(php-ts-mode--language-source-alist): Updated parsers version.
(php-ts-mode-install-parsers): Removed old dependencies, now relies
on (mhtml-ts-mode-install-parsers).  Update doc string.
(php-ts-mode-js-css-indent-offset): Update doc string.
(php-ts-mode-css-fontify-colors): Removed option, now relies on
'php-ts-mode-css-fontify-colors'.
(php-ts-mode-html-indent-offset): New user option.
(php-ts-mode-html-relative-indent): New user option.
(php-ts-mode-php-default-executable): Assume \"php\" instead of
\"/usr/bin/php\" if 'executable-find' fails.
(php-ts-mode-php-config): Fix tag.
(php-ts-mode-find-sibling-rules): New user option with the rules for
finding siblings of a file.
(php-ts-mode-phpdoc-highlight-errors): New user option, if non-nil
highlight unkown PHPDOC nodes.
(php-ts-mode--indent-style-setter): Indentation fix.
(php-ts-mode-indent-style): Indentation fix.
(php-ts-mode-flymake-php): Indentation fix.
(php-ts-mode--executable): Indentation fix.
(php-ts-mode-show-ini): New command that show the locations of PHP ini
files.
(php-ts-mode--get-indent-style): Indentation fix.
(php-ts-mode--set-indent-property): Indentation fix.
(php-ts-mode-set-style): Indentation fix.
(php-ts-mode--get-parser-ranges): Indentation fix.
(php-ts-mode--possibly-braceless-keyword-re): Removed no longer used
constant.
(php-ts-mode--open-statement-group-heuristic): Removed no longer used
function.
(php-ts-mode--else-heuristic): Indentation fix.
(php-ts-mode--first-sibling): Indentation fix.
(php-ts-mode--js-css-tag-bol): Function removed, now using
'mhtml-ts-mode--js-css-tag-bol'.
(php-ts-mode--parent-html-bol): Removed no longer used
function.
(php-ts-mode--parent-html-heuristic): Now more reliably handles the
mixed indentation of PHP and HTML. The new option
'php-ts-mode-html-relative-indent' changes the behavior of the
function. When 'php-ts-mode-html-relative-indent' is 't' the new option
'php-ts-mode-html-indent-offset' controls the offset of PHP code
relative to HTML tags.
(php-ts-mode--array-element-heuristic): Indentation fix.
(php-ts-mode--anchor-prev-sibling): Indentation fix..
(php-ts-mode--indent-styles): The indentation rules for ‘program’ and
‘text_interpolation’ are now common to all indentation styles, thanks to
the new version of ‘php-ts-mode-arent-html-heuristic’.
New rules for 'namespace_use_clause', 'use_declaration',
'use_list', 'attribute_group' and 'string_content'.
(php-ts-mode--prettify-symbols-alist): New variable to support
'prettify-symbols-alist'.
(php-ts-mode--test-visibility-modifier-operation-clause-p): Rename ...
(php-ts-mode--test-visibility-modifier-operation-p): ... to this more
correct name.
(php-ts-mode--test-property-hook-clause-p): Rename ...
(php-ts-mode--test-property-hook-p): ... to this more correct name.
(php-ts-mode--test-relative-name-p): New function to test if
'relative_name' is supported by the PHP parser.
(php-ts-mode--font-lock-settings): Use the new function for highlight
'relative_name'. Highlight 'error_suppression_expression'. Refactoring
using the renamed functions. Indentation fix.
(php-ts-mode--custom-html-font-lock-settings): Replace the rules with
'mhtml-ts-mode--treesit-font-lock-settings'.  Fix docs string.
(php-ts-mode--phpdoc-font-lock-settings): Added support for "array" and
"list" array_type. New “phpdoc-error” feature to highlight unknown nodes
via new ‘php-ts-mode--phpdoc-fontify-error’ function.
(php-ts-mode--colorize-css-value): Removed function no longer used, now
highlighting is handled directly by 'mhtml-ts-mode'.
(php-ts-mode--phpdoc-fontify-error): New function used by
'php-ts-mode--phpdoc-font-lock-settings'.
(php-ts-mode--parent-object): Indentation fix.
(php-ts-mode--defun-name-separator): Indentation fix.
(php-ts-mode--defun-object-name): Indentation fix.
(php-ts-mode--defun-name): The function now also returns the defun name
of 'mhtml-ts-mode'.
(php-ts-mode--treesit-defun-type-regexp): New regexp for
'treesit-defun-type-regexp' support.
(php-ts-mode--indent-defun): Indentation fix.
(php-ts-mode--defun-valid-p): Fix indentation
(php-ts-mode--comment-indent-new-line): Indentation fix.
(php-ts-mode--comment-current-plist): New local variable that stores the state
of the PHP comment style.
(php-ts-mode--comment-setup): The function now handles changing, for
different languages, comment styles, using
“php-ts-mode--comment-current-plist” to store and retrieve the comment
style for the PHP language.
(php-ts-mode-comment-setup): Now store the PHP comment style in
'php-ts-mode--comment-current-plist'.
(php-ts-mode-menu): Indentation fix.
(php-ts-mode): Replace dependency from JS, CSS and HTML width
'mhtml-ts-mode'.  Navigation, Imenu and Outline now support PHP, HTML,
Javascript and CSS. Added support for 'prettify-symbol-mode'.
Added support for 'electric-layout-rules'.
(php-ts-mode-run-php-webserver)
(php-ts-mode--webserver-read-args)
(php-ts-mode--webserver-read-args)
(inferior-php-ts-mode)
(run-php)
(inferior-php-ts-mode-startup)
(php-ts-mode-inferior--write-history)
(php-ts-mode-send-region): Indentation fix.
---
 etc/NEWS                        |   28 +
 lisp/progmodes/php-ts-mode.el   | 1614 ++++++++++++++++---------------
 lisp/textmodes/mhtml-ts-mode.el |   78 +-
 3 files changed, 914 insertions(+), 806 deletions(-)

diff --git a/etc/NEWS b/etc/NEWS
index f4e64c0851f..3279387a4b1 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -855,6 +855,34 @@ the built-in Web server.  Interactively, when invoked with a prefix
 argument, 'php-ts-mode-run-php-webserver' prompts for the config file as
 well as for other connection parameters.
 
+---
+*** The option 'php-ts-mode-css-fontify-colors' has been removed.
+'mhtml-ts-mode-css-fontify-colors' replace this option.
+
+---
+*** New user option 'php-ts-mode-html-relative-indent'.
+In buffers containing both PHP and HTML, this option allows you to
+define how the PHP code should be indented relative to the position of
+the HTML tags.
+
+---
+*** New user option 'php-ts-mode-html-indent-offset'.
+Offset of PHP code block relative to HTML tags.
+
+---
+*** New user option 'php-ts-mode-find-sibling-rules'.
+Rules for finding siblings of a PHP files.
+
+---
+*** New user option 'php-ts-mode-phpdoc-highlight-errors'.
+When not null, it highlights unknown PHPDOC tags using
+‘font-lock-warning-face’ so that the user can identify them more easily.
+
+---
+*** New command 'php-ts-mode-show-ini' to show where are located the ini files.
+Show the location of the PHP ini files, if the buffer is associated to a remote
+PHP file show the remote PHP ini files.
+
 ** Rust-ts mode
 
 ---
diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el
index a01e4d66fba..04b59f062f3 100644
--- a/lisp/progmodes/php-ts-mode.el
+++ b/lisp/progmodes/php-ts-mode.el
@@ -25,12 +25,12 @@
 ;;; Tree-sitter language versions
 ;;
 ;; php-ts-mode has been tested with the following grammars and version:
-;; - tree-sitter-phpdoc: v0.1.5
+;; - tree-sitter-phpdoc: v0.1.6
 ;; - tree-sitter-css: v0.23.1-1-g6a442a3
 ;; - tree-sitter-jsdoc: v0.23.2
 ;; - tree-sitter-javascript: v0.23.1-2-g108b2d4
 ;; - tree-sitter-html: v0.23.2-1-gd9219ad
-;; - tree-sitter-php: v0.23.11
+;; - tree-sitter-php: v0.23.12
 ;;
 ;; We try our best to make builtin modes work with latest grammar
 ;; versions, so a more recent grammar has a good chance to work too.
@@ -42,7 +42,7 @@
 ;; for editing PHP files with embedded HTML, JavaScript, CSS and phpdoc.
 ;; Tree Sitter is used to parse each of these languages.
 ;;
-;; Please note that this package requires `html-ts-mode', which
+;; Please note that this package requires `mhtml-ts-mode', which
 ;; registers itself as the major mode for editing HTML.
 ;;
 ;; This package is compatible and has been tested with the following
@@ -69,9 +69,7 @@
 
 (require 'treesit)
 (require 'c-ts-common) ;; For comment indent and filling.
-(require 'html-ts-mode) ;; for embed html
-(require 'css-mode) ;; for embed css into html
-(require 'js) ;; for embed javascript into html
+(require 'mhtml-ts-mode) ;; For embed html, css and js.
 (require 'comint)
 (treesit-declare-unavailable-functions)
 
@@ -83,10 +81,10 @@
 ;;; Install treesitter language parsers
 (defvar php-ts-mode--language-source-alist
   '((php "https://github.com/tree-sitter/tree-sitter-php"
-         :commit "43aad2b9a98aa8e603ea0cf5bb630728a5591ad8"
-         :source-dir "php/src")
+	 :commit "f7cf7348737d8cff1b13407a0bfedce02ee7b046"
+	 :source-dir "php/src")
     (phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc"
-            :commit "fe3202e468bc17332bec8969f2b50ff1f1da3a46"))
+	    :commit "03bb10330704b0b371b044e937d5cc7cd40b4999"))
   "Treesitter language parsers required by `php-ts-mode'.
 You can customize `treesit-language-source-alist' if you want
 to stick to a specific commit and/or use different parsers.")
@@ -97,9 +95,10 @@ php-ts-mode--language-source-alist
 (defun php-ts-mode-install-parsers ()
   "Install all the required treesitter parsers.
 `treesit-language-source-alist' defines which parsers to install.
-It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"."
+It's pre-filled by loading \"mhtml-ts-mode\"."
   (interactive)
-  (dolist (lang '(php phpdoc html css javascript jsdoc))
+  (mhtml-ts-mode-install-parsers)
+  (dolist (lang '(php phpdoc))
     (treesit-install-language-grammar lang)))
 
 ;;; Custom variables
@@ -118,25 +117,58 @@ php-ts-mode-indent-offset
 
 (defcustom php-ts-mode-js-css-indent-offset 2
   "JavaScript and CSS indent spaces related to the <script> and <style> HTML tags.
-By default should have same value as `html-ts-mode-indent-offset'."
+By default should have same value as `mhtml-ts-mode-js-css-indent-offset'."
   :tag "PHP javascript or css indent offset"
   :version "30.1"
   :type 'integer
   :safe 'integerp)
 
-(defcustom php-ts-mode-css-fontify-colors t
-  "Whether CSS colors should be fontified using the color as the background.
-If non-nil, text representing a CSS color will be fontified
-such that its background is the color itself.
-Works like `css--fontify-region'."
-  :tag "PHP colors the CSS properties values."
-  :version "30.1"
-  :type 'boolean
-  :safe 'booleanp)
+(defcustom php-ts-mode-html-indent-offset 2
+  "PHP indent spaces related to the HTML tags.
+By default should have same value as `mhtml-ts-mode-js-css-indent-offset'."
+  :tag "PHP indent offset relative to HTML"
+  :version "31.1"
+  :type 'integer
+  :safe 'integerp)
 
-(defcustom php-ts-mode-php-default-executable (or (executable-find "php") "/usr/bin/php")
+(defcustom php-ts-mode-html-relative-indent t
+  "How PHP code are indented relative to the HTML tags.
+
+When t, the default, indentation looks like:
+
+  <html>
+    ...
+    <div>
+       <?php
+       ?>
+    </div>
+
+When nil, indentation of the tag body starts just below the
+tag, like:
+
+  <html>
+    ...
+    <div>
+    <?php
+    ?>
+    </div>
+
+When `ignore', the tag body starts in the first column, like:
+
+  <html>
+    ...
+    <div>
+  <?php
+  ?>
+    </div>"
+  :type '(choice (const nil) (const t) (const ignore))
+  :safe 'symbolp
+  :tag "PHP indent relative to HTML"
+  :version "31.1")
+
+(defcustom php-ts-mode-php-default-executable (or (executable-find "php") "php")
   "The default PHP executable."
-  :tag "PHP Executable"
+  :tag "PHP executable"
   :version "30.1"
   :type 'file)
 
@@ -148,7 +180,7 @@ php-ts-mode-php-config
   "The location of php.ini file.
 If nil the default one is used to run the embedded webserver or
 inferior PHP process."
-  :tag "PHP Init file"
+  :tag "PHP init file"
   :version "30.1"
   :type '(choice (const :tag "Default init file" nil) file))
 
@@ -198,6 +230,28 @@ php-ts-mode-inferior-history
   :type '(choice (const :tag "None" nil) file)
   :safe 'string-or-null-p)
 
+(defcustom php-ts-mode-find-sibling-rules
+  (list (list (rx "src/" (group (+ not-newline) "/") (group (+ (not "/"))) ".php") "tests/\\1\\2Test.php")
+	(list (rx "tests/" (group (+ not-newline) "/") (group (+ (not "/"))) "Test.php") "src/\\1\\2.php"))
+  "Rules for finding sibling files as defined by `find-sibling-rules'.
+As a default, the rules try to find the corresponding test of the
+current source file and vice versa.  Source files are assumed to be in
+src/, and tests of in tests/.  Many frameworks have a folder
+arrangement similar to this."
+  :type '(alist :key-type (regexp :tag "Match")
+		:value-type (repeat (string :tag "Expansion")))
+  :tag "PHP find sibling rules"
+  :version "31.1")
+
+(defcustom php-ts-mode-phpdoc-highlight-errors nil
+  "Highlights tags unknown to the phpdoc parser.
+If nil try to highlight even incorrect nodes as if they were correct,
+otherwise show phpdoc error using a `font-lock-warning-face'."
+  :tag "PHPDOC face for unknown tags or errors."
+  :version "31.1"
+  :type 'boolean
+  :safe 'booleanp)
+
 (defvar php-ts-mode--inferior-prompt "php >"
   "Prompt used by PHP inferior process.")
 
@@ -210,9 +264,9 @@ php-ts-mode--indent-style-setter
 symbol."
   (set-default sym val)
   (dolist (buffer (buffer-list))
-      (with-current-buffer buffer
-        (when (derived-mode-p 'php-ts-mode)
-          (php-ts-mode-set-style val)))))
+    (with-current-buffer buffer
+      (when (derived-mode-p 'php-ts-mode)
+	(php-ts-mode-set-style val)))))
 
 ;; taken from c-ts-mode
 (defun php-ts-indent-style-safep (style)
@@ -235,12 +289,12 @@ php-ts-mode-indent-style
   :tag "PHP indent style"
   :version "30.1"
   :type '(choice (const :tag "PSR-2/PSR-12" psr2)
-                 (const :tag "PEAR" pear)
-                 (const :tag "Drupal" drupal)
-                 (const :tag "WordPress" wordpress)
-                 (const :tag "Symfony" symfony)
-                 (const :tag "Zend" zend)
-                 (function :tag "A function for user customized style" ignore))
+		 (const :tag "PEAR" pear)
+		 (const :tag "Drupal" drupal)
+		 (const :tag "WordPress" wordpress)
+		 (const :tag "Symfony" symfony)
+		 (const :tag "Zend" zend)
+		 (function :tag "A function for user customized style" ignore))
   :set #'php-ts-mode--indent-style-setter
   :safe #'php-ts-indent-style-safep)
 
@@ -258,52 +312,52 @@ php-ts-mode-flymake-php
   (when (process-live-p php-ts-mode--flymake-process)
     (kill-process php-ts-mode--flymake-process))
   (let ((source (current-buffer))
-        (diagnostics-pattern (eval-when-compile
-                               (rx bol (? "PHP ") ;; every diagnostic line start with PHP
-                                   (group (or "Fatal" "Parse")) ;; 1: type
-                                   " error:" (+ (syntax whitespace))
-                                   (group (+? nonl)) ;; 2: msg
-                                   " in " (group (+? nonl)) ;; 3: file
-                                   " on line " (group (+ num)) ;; 4: line
-                                   eol))))
+	(diagnostics-pattern (eval-when-compile
+			       (rx bol (? "PHP ") ;; every diagnostic line start with PHP
+				   (group (or "Fatal" "Parse")) ;; 1: type
+				   " error:" (+ (syntax whitespace))
+				   (group (+? nonl)) ;; 2: msg
+				   " in " (group (+? nonl)) ;; 3: file
+				   " on line " (group (+ num)) ;; 4: line
+				   eol))))
     (save-restriction
       (widen)
       (setq php-ts-mode--flymake-process
-            (make-process
-             :name "php-ts-mode-flymake"
-             :noquery t
-             :connection-type 'pipe
-             :buffer (generate-new-buffer " *php-ts-mode-flymake*")
-             :command `(,(php-ts-mode--executable)
-                        "-l" "-d" "display_errors=0")
-             :sentinel
-             (lambda (proc _event)
-               (when (eq 'exit (process-status proc))
-                 (unwind-protect
-                     (if (with-current-buffer source
-                           (eq proc php-ts-mode--flymake-process))
-                         (with-current-buffer (process-buffer proc)
-                           (goto-char (point-min))
-                           (let (diags)
-                             (while (search-forward-regexp
-                                     diagnostics-pattern
-                                     nil t)
-                               (let* ((beg
-                                       (car (flymake-diag-region
-                                             source
-                                             (string-to-number (match-string 4)))))
-                                      (end
-                                       (cdr (flymake-diag-region
-                                             source
-                                             (string-to-number (match-string 4)))))
-                                      (msg (match-string 2))
-                                      (type :error))
-                                 (push (flymake-make-diagnostic
-                                        source beg end type msg)
-                                       diags)))
-                             (funcall report-fn diags)))
-                       (flymake-log :warning "Canceling obsolete check %s" proc))
-                   (kill-buffer (process-buffer proc)))))))
+	    (make-process
+	     :name "php-ts-mode-flymake"
+	     :noquery t
+	     :connection-type 'pipe
+	     :buffer (generate-new-buffer " *php-ts-mode-flymake*")
+	     :command `(,(php-ts-mode--executable)
+			"-l" "-d" "display_errors=0")
+	     :sentinel
+	     (lambda (proc _event)
+	       (when (eq 'exit (process-status proc))
+		 (unwind-protect
+		     (if (with-current-buffer source
+			   (eq proc php-ts-mode--flymake-process))
+			 (with-current-buffer (process-buffer proc)
+			   (goto-char (point-min))
+			   (let (diags)
+			     (while (search-forward-regexp
+				     diagnostics-pattern
+				     nil t)
+			       (let* ((beg
+				       (car (flymake-diag-region
+					     source
+					     (string-to-number (match-string 4)))))
+				      (end
+				       (cdr (flymake-diag-region
+					     source
+					     (string-to-number (match-string 4)))))
+				      (msg (match-string 2))
+				      (type :error))
+				 (push (flymake-make-diagnostic
+					source beg end type msg)
+				       diags)))
+			     (funcall report-fn diags)))
+		       (flymake-log :warning "Canceling obsolete check %s" proc))
+		   (kill-buffer (process-buffer proc)))))))
       (process-send-region php-ts-mode--flymake-process (point-min) (point-max))
       (process-send-eof php-ts-mode--flymake-process))))
 
@@ -317,23 +371,55 @@ php-ts-mode--executable
 non-nil, it searches for this program instead of the usual `php'.
 If the search fails, it returns `php-ts-mode-php-default-executable'."
   (or (executable-find
-      (or php-ts-mode-alternative-php-program-name "php") t)
-    php-ts-mode-php-default-executable))
+       (or php-ts-mode-alternative-php-program-name "php") t)
+      php-ts-mode-php-default-executable))
+
+(defun php-ts-mode-show-ini ()
+  "Show PHP configuration file names."
+  (interactive)
+  (let ((buffer (get-buffer-create "*PHP ini*"))
+	(button-action (lambda (_) (find-file-at-point)))
+	(remote (file-remote-p default-directory)))
+    (with-current-buffer buffer
+      (view-mode -1)
+      (erase-buffer)
+      (shell-command (concat (php-ts-mode--executable) " --ini") buffer)
+      ;; Remove useless ',' at end of some rows.
+      (while (re-search-forward "," nil t)
+	(replace-match "" nil nil))
+      (goto-char (point-min))
+      (while (re-search-forward
+	      "[a-zA-Z]?[:]?[/\\].+"
+	      nil t)
+	(let ((start (match-beginning 0))
+	      (end (match-end 0)))
+	  ;; If PHP is on a remote machine, the path must be preceded
+	  ;; by the remote identifier.
+	  (when remote
+	    (goto-char start)
+	    (insert remote)
+	    (setq end (+ end (length remote)))
+	    (goto-char end))
+	  (make-button start end 'action button-action)))
+      (goto-char (point-min))
+      (view-mode +1))
+    (when buffer
+      (pop-to-buffer buffer))))
 
 (defun php-ts-mode--get-indent-style ()
   "Helper function to set indentation style.
 MODE can be `psr2', `pear', `drupal', `wordpress', `symfony', `zend'."
   (let ((style
-         (if (functionp php-ts-mode-indent-style)
-             (funcall php-ts-mode-indent-style)
-           (cl-case php-ts-mode-indent-style
-             (psr2 (alist-get 'psr2 (php-ts-mode--indent-styles)))
-             (pear (alist-get 'pear (php-ts-mode--indent-styles)))
-             (drupal (alist-get 'drupal (php-ts-mode--indent-styles)))
-             (wordpress (alist-get 'wordpress (php-ts-mode--indent-styles)))
-             (symfony (alist-get 'symfony (php-ts-mode--indent-styles)))
-             (zend (alist-get 'zend (php-ts-mode--indent-styles)))
-             (t (alist-get 'psr2 (php-ts-mode--indent-styles)))))))
+	 (if (functionp php-ts-mode-indent-style)
+	     (funcall php-ts-mode-indent-style)
+	   (cl-case php-ts-mode-indent-style
+	     (psr2 (alist-get 'psr2 (php-ts-mode--indent-styles)))
+	     (pear (alist-get 'pear (php-ts-mode--indent-styles)))
+	     (drupal (alist-get 'drupal (php-ts-mode--indent-styles)))
+	     (wordpress (alist-get 'wordpress (php-ts-mode--indent-styles)))
+	     (symfony (alist-get 'symfony (php-ts-mode--indent-styles)))
+	     (zend (alist-get 'zend (php-ts-mode--indent-styles)))
+	     (t (alist-get 'psr2 (php-ts-mode--indent-styles)))))))
     `((php ,@style))))
 
 (defun php-ts-mode--prompt-for-style ()
@@ -357,23 +443,23 @@ php-ts-mode--set-indent-property
   "Set the offset, tab, etc. according to STYLE."
   (cl-case style
     (psr2 (setq php-ts-mode-indent-offset 4
-                tab-width 4
-                indent-tabs-mode nil))
+		tab-width 4
+		indent-tabs-mode nil))
     (pear (setq php-ts-mode-indent-offset 4
-                tab-width 4
-                indent-tabs-mode nil))
+		tab-width 4
+		indent-tabs-mode nil))
     (drupal (setq php-ts-mode-indent-offset 2
-                  tab-width 2
-                  indent-tabs-mode nil))
+		  tab-width 2
+		  indent-tabs-mode nil))
     (wordpress (setq php-ts-mode-indent-offset 4
-                     tab-width 4
-                     indent-tabs-mode t))
+		     tab-width 4
+		     indent-tabs-mode t))
     (symfony (setq php-ts-mode-indent-offset 4
-                   tab-width 4
-                   indent-tabs-mode nil))
+		   tab-width 4
+		   indent-tabs-mode nil))
     (zend (setq php-ts-mode-indent-offset 4
-                tab-width 4
-                indent-tabs-mode nil))))
+		tab-width 4
+		indent-tabs-mode nil))))
 
 (defun php-ts-mode-set-style (style)
   "Set the PHP indent style of the current buffer to STYLE.
@@ -386,13 +472,13 @@ php-ts-mode-set-style
    ((equal php-ts-mode-indent-style style)
     (message "The style is already %s" style));; nothing to do
    (t (progn
-        (setq-local php-ts-mode-indent-style style)
-        (php-ts-mode--set-indent-property style)
-        (let ((rules (assq-delete-all 'php treesit-simple-indent-rules))
-              (new-style (car (treesit--indent-rules-optimize
-                               (php-ts-mode--get-indent-style)))))
-          (setq treesit-simple-indent-rules (cons new-style rules))
-          (message "Switch to %s style" style))))))
+	(setq-local php-ts-mode-indent-style style)
+	(php-ts-mode--set-indent-property style)
+	(let ((rules (assq-delete-all 'php treesit-simple-indent-rules))
+	      (new-style (car (treesit--indent-rules-optimize
+			       (php-ts-mode--get-indent-style)))))
+	  (setq treesit-simple-indent-rules (cons new-style rules))
+	  (message "Switch to %s style" style))))))
 
 (defun php-ts-mode--get-parser-ranges ()
   "Return the ranges covered by the parsers.
@@ -401,9 +487,9 @@ php-ts-mode--get-parser-ranges
 current buffer, the ranges covered by each parser.
 Useful for debugging."
   (let ((ranges)
-        (parsers (treesit-parser-list nil nil t)))
+	(parsers (treesit-parser-list nil nil t)))
     (if (not parsers)
-        (message "At least one parser must be initialized"))
+	(message "At least one parser must be initialized"))
     (cl-loop
      for parser in parsers
      do (push (list parser (treesit-parser-included-ranges parser)) ranges)
@@ -442,27 +528,6 @@ php-ts-mode--syntax-table
 
 ;;; Indent
 
-(defconst php-ts-mode--possibly-braceless-keyword-re
-  (regexp-opt '("if" "for" "foreach" "while" "do") 'symbols)
-  "Regexp matching keywords optionally followed by an opening brace.")
-
-(defun php-ts-mode--open-statement-group-heuristic (node _parent bol &rest _)
-  "Heuristic matcher for statement-group without closing bracket.
-
-Return `php-ts-mode-indent-offset' plus 1 when BOL is after
-`php-ts-mode--possibly-braceless-keyword-re', otherwise return 0.  It's
-useful for matching incomplete compound_statement or colon_block.
-PARENT is NODE's parent, BOL is the beginning of non-whitespace
-characters of the current line."
-  (and (null node)
-       (save-excursion
-	   (forward-line -1)
-	   (if (re-search-forward
-		php-ts-mode--possibly-braceless-keyword-re
-		bol t)
-	       (+ 1 php-ts-mode-indent-offset)
-	     0))))
-
 ;; taken from c-ts-mode
 (defun php-ts-mode--else-heuristic (node parent bol &rest _)
   "Heuristic matcher for when \"else\" is followed by a closing bracket.
@@ -471,10 +536,10 @@ php-ts-mode--else-heuristic
 characters of the current line."
   (and (null node)
        (save-excursion
-         (forward-line -1)
-         (looking-at (rx (* whitespace) "else" (* whitespace) eol)))
+	 (forward-line -1)
+	 (looking-at (rx (* whitespace) "else" (* whitespace) eol)))
        (let ((next-node (treesit-node-first-child-for-pos parent bol)))
-         (equal (treesit-node-type next-node) "}"))))
+	 (equal (treesit-node-type next-node) "}"))))
 
 ;; taken from c-ts-mode
 (defun php-ts-mode--first-sibling (node parent &rest _)
@@ -485,26 +550,11 @@ php-ts-mode--first-sibling
 PARENT is its parent."
   (let ((prev-sibling (treesit-node-prev-sibling node t)))
     (or (null prev-sibling)
-        (save-excursion
-          (goto-char (treesit-node-start prev-sibling))
-          (<= (line-beginning-position)
-              (treesit-node-start parent)
-              (line-end-position))))))
-
-(defun php-ts-mode--js-css-tag-bol (_node parent &rest _)
-  "Find the first non-space characters of html tags <script> or <style>.
-
-Return `line-beginning-position' when `treesit-node-at' is HTML or PHP.
-Otherwise go to the PARENT and search backward for <script> or <style> tags.
-Should be used only for Javascript or CSS indenting rules.
-NODE, ignored, is the node to match and PARENT is its parent."
-  (let ((lang (treesit-language-at (point))))
-    (if (or (eq lang 'javascript)
-	    (eq lang 'css))
 	(save-excursion
-	  (goto-char (treesit-node-start parent))
-	  (re-search-backward "<script.*>\\|<style.*>" nil t))
-      (line-beginning-position))))
+	  (goto-char (treesit-node-start prev-sibling))
+	  (<= (line-beginning-position)
+	      (treesit-node-start parent)
+	      (line-end-position))))))
 
 (defun php-ts-mode--parent-eol (_node parent &rest _)
   "Find the last non-space characters of the PARENT of the current NODE.
@@ -514,50 +564,33 @@ php-ts-mode--parent-eol
     (goto-char (treesit-node-start parent))
     (line-end-position)))
 
-(defun php-ts-mode--parent-html-bol (node parent bol &rest _)
-  "Find the first non-space characters of the HTML tags before NODE.
-
-When NODE is nil call `php-ts-mode--open-statement-group-heuristic'.
-PARENT is NODE's parent, BOL is the beginning of non-whitespace
-characters of the current line."
-  (if (null node)
-      ;; If NODE is nil it could be an open statement-group.
-      (php-ts-mode--open-statement-group-heuristic node parent bol)
-    (save-excursion
-      (let ((html-node (treesit-search-forward node "text" t)))
-	(if html-node
-            (let ((end-html (treesit-node-end html-node)))
-              (goto-char end-html)
-              (backward-word)
-              (back-to-indentation)
-              (point))
-          (treesit-node-start parent))))))
-
 (defun php-ts-mode--parent-html-heuristic (node parent bol &rest _)
   "Return position based on html indentation.
 
-Returns 0 if the NODE is after the </html>, otherwise returns the
-indentation point of the last word before the NODE, plus the indentation
-offset.  If there is no HTML tag, it returns the beginning of the
-parent.  When NODE is nil call `php-ts-mode--open-statement-group-heuristic'.
-It can be used when you want to indent PHP code relative to the HTML.
-PARENT is NODE's parent, BOL is the beginning of non-whitespace
-characters of the current line."
-  (if (null node)
-      ;; If NODE is nil it could be an open statement-group.
-      (php-ts-mode--open-statement-group-heuristic node parent bol)
-    (let ((html-node (treesit-search-forward node "text" t)))
-      (if html-node
-          (let ((end-html (treesit-node-end html-node)))
-            (save-excursion
-	      (goto-char end-html)
-	      (backward-word)
-	      (back-to-indentation)
-	      (if (search-forward "</html>" end-html t 1)
-                  0
-		(+ (point) php-ts-mode-indent-offset))))
-	;; Maybe it's better to use bol?
-	(treesit-node-start parent)))))
+Returns 0 if the NODE is after the </html>.  If
+`php-ts-mode-html-relative-indent' is not nil returns the indentation
+point of the last word before the NODE, plus the indentation offset,
+otherwise return only the indentation point.  If there is no HTML tag,
+it returns the beginning of the parent.  When NODE is nil it behave
+like \"prev-siblings\" of `treesit-simple-indent-presets'.  It can be
+used when you want to indent PHP code relative to the HTML.  PARENT is
+NODE's parent, BOL is the beginning of non-whitespace characters of
+the current line."
+  (save-excursion
+    (cond
+     ((eq php-ts-mode-html-relative-indent 'ignore) (line-beginning-position))
+     ((search-backward "</html>" (treesit-node-start parent) t 1) (line-beginning-position))
+     ((null node) (apply (alist-get 'prev-sibling treesit-simple-indent-presets) node parent bol nil))
+     (t (when-let* ((html-node (treesit-search-forward node "text" t))
+		    (end-html (treesit-node-end html-node)))
+	  (goto-char end-html)
+	  ;; go to the start of the last tag
+	  ;; of the "text" node
+	  (backward-word)
+	  (back-to-indentation)
+	  (if php-ts-mode-html-relative-indent
+	      (+ (point) php-ts-mode-html-indent-offset)
+	    (point)))))))
 
 (defun php-ts-mode--array-element-heuristic (_node parent _bol &rest _)
   "Return of the position of the first element of the array.
@@ -565,21 +598,20 @@ php-ts-mode--array-element-heuristic
 PARENT is NODE's parent, BOL is the beginning of non-whitespace
 characters of the current line."
   (let ((parent-start
-         (treesit-node-start parent))
-        (parent-first-child-start
-         (treesit-node-start (treesit-node-child parent 2))))
+	 (treesit-node-start parent))
+	(parent-first-child-start
+	 (treesit-node-start (treesit-node-child parent 2))))
     (if (equal
-         (line-number-at-pos parent-start)
-         (line-number-at-pos parent-first-child-start))
-        ;; if array_creation_expression and the first
-        ;; array_element_initializer are on the same same line
-        parent-first-child-start
+	 (line-number-at-pos parent-start)
+	 (line-number-at-pos parent-first-child-start))
+	;; if array_creation_expression and the first
+	;; array_element_initializer are on the same same line
+	parent-first-child-start
       ;; else return parent-bol plus the offset
       (save-excursion
-        (goto-char (treesit-node-start parent))
-        (back-to-indentation)
-        (+ (point) php-ts-mode-indent-offset)))))
-
+	(goto-char (treesit-node-start parent))
+	(back-to-indentation)
+	(+ (point) php-ts-mode-indent-offset)))))
 
 (defun php-ts-mode--anchor-first-sibling (_node parent _bol &rest _)
   "Return the start of the first child of a sibling of PARENT.
@@ -590,14 +622,14 @@ php-ts-mode--anchor-first-sibling
 PARENT is NODE's parent, BOL is the beginning of non-whitespace
 characters of the current line."
   (let ((first-sibling-start
-         (treesit-node-start (treesit-node-child parent 0)))
-        (first-sibling-child-start
-         (treesit-node-start (treesit-node-child parent 1))))
+	 (treesit-node-start (treesit-node-child parent 0)))
+	(first-sibling-child-start
+	 (treesit-node-start (treesit-node-child parent 1))))
     (if (equal
-         (line-number-at-pos first-sibling-start)
-         (line-number-at-pos first-sibling-child-start))
-        ;; if are on the same line return the child start
-        first-sibling-child-start
+	 (line-number-at-pos first-sibling-start)
+	 (line-number-at-pos first-sibling-child-start))
+	;; if are on the same line return the child start
+	first-sibling-child-start
       first-sibling-start)))
 
 ;; adapted from c-ts-mode--anchor-prev-sibling
@@ -610,150 +642,158 @@ php-ts-mode--anchor-prev-sibling
 PARENT is NODE's parent, BOL is the beginning of non-whitespace
 characters of the current line."
   (when-let* ((prev-sibling
-              (or (treesit-node-prev-sibling node t)
-                  (treesit-node-prev-sibling
-                   (treesit-node-first-child-for-pos parent bol) t)
-                  (treesit-node-child parent -1 t)))
-             (continue t))
+	       (or (treesit-node-prev-sibling node t)
+		   (treesit-node-prev-sibling
+		    (treesit-node-first-child-for-pos parent bol) t)
+		   (treesit-node-child parent -1 t)))
+	      (continue t))
     (save-excursion
       (while (and prev-sibling continue)
-        (goto-char (treesit-node-start prev-sibling))
-        (if (looking-back (rx bol (* whitespace))
-                          (line-beginning-position))
-            (setq continue nil)
-          (setq prev-sibling
-                (treesit-node-prev-sibling prev-sibling)))))
+	(goto-char (treesit-node-start prev-sibling))
+	(if (looking-back (rx bol (* whitespace))
+			  (line-beginning-position))
+	    (setq continue nil)
+	  (setq prev-sibling
+		(treesit-node-prev-sibling prev-sibling)))))
     (treesit-node-start prev-sibling)))
 
 (defun php-ts-mode--indent-styles ()
   "Indent rules supported by `php-ts-mode'."
   (let ((common
-         `((php-ts-mode--else-heuristic prev-line php-ts-mode-indent-offset)
-
-           ((query "(ERROR (ERROR)) @indent") column-0 0)
-
-           ((node-is ")") parent-bol 0)
-           ((node-is "]") parent-bol 0)
-           ((node-is "else_clause") parent-bol 0)
-           ((node-is "case_statement") parent-bol php-ts-mode-indent-offset)
-           ((node-is "default_statement") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "default_statement") parent-bol php-ts-mode-indent-offset)
-           ((and
-             (parent-is "expression_statement")
-             (node-is ";"))
-            parent-bol 0)
-           ((parent-is "expression_statement") parent-bol php-ts-mode-indent-offset)
-           ;; `c-ts-common-looking-at-star' has to come before
-           ;; `c-ts-common-comment-2nd-line-matcher'.
-           ((and (parent-is "comment") c-ts-common-looking-at-star)
-            c-ts-common-comment-start-after-first-star -1)
-           (c-ts-common-comment-2nd-line-matcher
-            c-ts-common-comment-2nd-line-anchor
-            1)
-           ((parent-is "comment") prev-adaptive-prefix 0)
-
-           ((parent-is "method_declaration") parent-bol 0)
-           ((node-is "class_interface_clause") parent-bol php-ts-mode-indent-offset)
-           ((query "(class_interface_clause (name) @indent)") php-ts-mode--parent-eol 1)
-           ((query "(class_interface_clause (qualified_name) @indent)")
-            parent-bol php-ts-mode-indent-offset)
-           ((parent-is "class_declaration") parent-bol 0)
+	 `(;; Handle indentation relatives to HTML.
+	   ;; ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
+	   ;; ((parent-is "text_interpolation") php-ts-mode--parent-html-heuristic 0)
+	   ((or (parent-is "program")
+		(parent-is "text_interpolation"))
+	    php-ts-mode--parent-html-heuristic 0)
+
+	   (php-ts-mode--else-heuristic prev-line php-ts-mode-indent-offset)
+
+	   ((query "(ERROR (ERROR)) @indent") parent-bol 0)
+
+	   ((node-is ")") parent-bol 0)
+	   ((node-is "]") parent-bol 0)
+	   ((node-is "else_clause") parent-bol 0)
+	   ((node-is "case_statement") parent-bol php-ts-mode-indent-offset)
+	   ((node-is "default_statement") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "default_statement") parent-bol php-ts-mode-indent-offset)
+	   ((and
+	     (parent-is "expression_statement")
+	     (node-is ";"))
+	    parent-bol 0)
+	   ((parent-is "expression_statement") parent-bol php-ts-mode-indent-offset)
+	   ;; `c-ts-common-looking-at-star' has to come before
+	   ;; `c-ts-common-comment-2nd-line-matcher'.
+	   ((and (parent-is "comment") c-ts-common-looking-at-star)
+	    c-ts-common-comment-start-after-first-star -1)
+	   (c-ts-common-comment-2nd-line-matcher
+	    c-ts-common-comment-2nd-line-anchor
+	    1)
+	   ((parent-is "comment") prev-adaptive-prefix 0)
+
+	   ((parent-is "method_declaration") parent-bol 0)
+	   ((node-is "class_interface_clause") parent-bol php-ts-mode-indent-offset)
+	   ((query "(class_interface_clause (name) @indent)") php-ts-mode--parent-eol 1)
+	   ((query "(class_interface_clause (qualified_name) @indent)")
+	    parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "class_declaration") parent-bol 0)
 	   ((parent-is "namespace_use_declaration") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "namespace_use_group") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "function_definition") parent-bol 0)
-           ((parent-is "member_call_expression") first-sibling php-ts-mode-indent-offset)
-           ((parent-is "conditional_expression") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "assignment_expression") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "array_creation_expression") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "parenthesized_expression") first-sibling 1)
-           ((parent-is "binary_expression") parent 0)
-           ((or (parent-is "arguments")
-                (parent-is "formal_parameters"))
-            parent-bol php-ts-mode-indent-offset)
-
-           ((query "(for_statement (assignment_expression left: (_)) @indent)")
-            parent-bol php-ts-mode-indent-offset)
-           ((query "(for_statement (binary_expression left: (_)) @indent)")
-            parent-bol php-ts-mode-indent-offset)
-           ((query "(for_statement (update_expression (_)) @indent)")
-            parent-bol php-ts-mode-indent-offset)
-           ((query "(function_call_expression arguments: (_) @indent)")
-            parent php-ts-mode-indent-offset)
-           ((query "(member_call_expression arguments: (_) @indent)")
-            parent php-ts-mode-indent-offset)
-           ((query "(scoped_call_expression name: (_) @indent)")
-            parent php-ts-mode-indent-offset)
-           ((parent-is "scoped_property_access_expression")
-            parent php-ts-mode-indent-offset)
-
-           ;; Closing bracket. Must stay here, the rule order matter.
-           ((node-is "}") standalone-parent 0)
-           ;; handle multiple single line comment that start at the and of a line
-           ((match "comment" "declaration_list") php-ts-mode--anchor-prev-sibling 0)
-           ((parent-is "declaration_list") column-0 php-ts-mode-indent-offset)
-
-           ((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset)
-
-           ;; Statement in {} blocks.
-           ((or (and (or (parent-is "compound_statement")
+	   ((node-is "namespace_use_clause") parent-bol php-ts-mode-indent-offset)
+	   ((or (parent-is "use_declaration")
+		(parent-is "use_list"))
+	    parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "function_definition") parent-bol 0)
+	   ((parent-is "member_call_expression") first-sibling php-ts-mode-indent-offset)
+	   ((parent-is "conditional_expression") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "assignment_expression") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "array_creation_expression") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "attribute_group") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "parenthesized_expression") first-sibling 1)
+	   ((parent-is "binary_expression") parent 0)
+	   ((or (parent-is "arguments")
+		(parent-is "formal_parameters"))
+	    parent-bol php-ts-mode-indent-offset)
+
+	   ((query "(for_statement (assignment_expression left: (_)) @indent)")
+	    parent-bol php-ts-mode-indent-offset)
+	   ((query "(for_statement (binary_expression left: (_)) @indent)")
+	    parent-bol php-ts-mode-indent-offset)
+	   ((query "(for_statement (update_expression (_)) @indent)")
+	    parent-bol php-ts-mode-indent-offset)
+	   ((query "(function_call_expression arguments: (_) @indent)")
+	    parent php-ts-mode-indent-offset)
+	   ((query "(member_call_expression arguments: (_) @indent)")
+	    parent php-ts-mode-indent-offset)
+	   ((query "(scoped_call_expression name: (_) @indent)")
+	    parent php-ts-mode-indent-offset)
+	   ((parent-is "scoped_property_access_expression")
+	    parent php-ts-mode-indent-offset)
+
+	   ;; Closing bracket. Must stay here, the rule order matter.
+	   ((node-is "}") standalone-parent 0)
+	   ;; handle multiple single line comment that start at the and of a line
+	   ((match "comment" "declaration_list") php-ts-mode--anchor-prev-sibling 0)
+	   ((parent-is "declaration_list") column-0 php-ts-mode-indent-offset)
+
+	   ((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset)
+
+	   ;; Statement in {} blocks.
+	   ((or (and (or (parent-is "compound_statement")
 			 (parent-is "colon_block"))
-                     ;; If the previous sibling(s) are not on their
-                     ;; own line, indent as if this node is the first
-                     ;; sibling
-                     php-ts-mode--first-sibling)
-                (or (match null "compound_statement")
+		     ;; If the previous sibling(s) are not on their
+		     ;; own line, indent as if this node is the first
+		     ;; sibling
+		     php-ts-mode--first-sibling)
+		(or (match null "compound_statement")
 		    (match null "colon_block")))
-            standalone-parent php-ts-mode-indent-offset)
-           ((or (parent-is "compound_statement")
+	    standalone-parent php-ts-mode-indent-offset)
+	   ((or (parent-is "compound_statement")
 		(parent-is "colon_block"))
 	    parent-bol php-ts-mode-indent-offset)
-           ;; Opening bracket.
-           ((or (node-is "compound_statement")
-                (node-is "colon_block"))
-                standalone-parent php-ts-mode-indent-offset)
-
-           ((parent-is "match_block") parent-bol php-ts-mode-indent-offset)
-           ((parent-is "switch_block") parent-bol 0)
-
-           ;; These rules are for cases where the body is bracketless.
-           ((match "while" "do_statement") parent-bol 0)
-           ;; rule for PHP alternative syntax
-           ((or (node-is "else_if_clause")
-                (node-is "endif")
-                (node-is "endfor")
-                (node-is "endforeach")
-                (node-is "endwhile"))
-            parent-bol 0)
-           ((or (parent-is "if_statement")
-                (parent-is "else_clause")
-                (parent-is "for_statement")
-                (parent-is "foreach_statement")
-                (parent-is "while_statement")
-                (parent-is "do_statement")
-                (parent-is "switch_statement")
-                (parent-is "case_statement")
-                (parent-is "empty_statement"))
-            parent-bol php-ts-mode-indent-offset)
+	   ;; Opening bracket.
+	   ((or (node-is "compound_statement")
+		(node-is "colon_block"))
+	    standalone-parent php-ts-mode-indent-offset)
+
+	   ((parent-is "match_block") parent-bol php-ts-mode-indent-offset)
+	   ((parent-is "switch_block") parent-bol 0)
+
+	   ;; These rules are for cases where the body is bracketless.
+	   ((match "while" "do_statement") parent-bol 0)
+	   ;; rule for PHP alternative syntax
+	   ((or (node-is "else_if_clause")
+		(node-is "endif")
+		(node-is "endfor")
+		(node-is "endforeach")
+		(node-is "endwhile"))
+	    parent-bol 0)
+	   ((or (parent-is "if_statement")
+		(parent-is "else_clause")
+		(parent-is "for_statement")
+		(parent-is "foreach_statement")
+		(parent-is "while_statement")
+		(parent-is "do_statement")
+		(parent-is "switch_statement")
+		(parent-is "case_statement")
+		(parent-is "empty_statement"))
+	    parent-bol php-ts-mode-indent-offset)
+
+	   ;; Do not touch the indentation inside a multiline the strings.
+	   ((parent-is "string_content") no-indent 0)
 
 	   ;; Workaround: handle "for" open statement group. Currently
-           ;; the grammar handles it differently than other control structures.
-	   (no-node php-ts-mode--open-statement-group-heuristic 0))))
+	   ;; the grammar handles it differently than other control structures.
+	   (no-node prev-sibling 0))))
     `((psr2
-       ((parent-is "program") php-ts-mode--open-statement-group-heuristic 0)
-       ((parent-is "text_interpolation") column-0 0)
        ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
        ,@common)
       (pear
-       ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
-       ((parent-is "text_interpolation") php-ts-mode--parent-html-heuristic 0)
        ((or (node-is "case_statement")
-            (node-is "default_statement"))
-        parent-bol 0)
+	    (node-is "default_statement"))
+	parent-bol 0)
        ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset)
        ,@common)
       (drupal
-       ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
-       ((parent-is "text_interpolation") php-ts-mode--parent-html-bol 0)
        ((parent-is "if_statement") parent-bol 0)
        ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset)
        ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
@@ -762,8 +802,6 @@ php-ts-mode--indent-styles
        ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
        ,@common)
       (wordpress
-       ((parent-is "program") php-ts-mode--parent-html-bol 0)
-       ((parent-is "text_interpolation") php-ts-mode--parent-html-bol 0)
        ,@common)
       (zend
        ((parent-is "class_interface_clause") php-ts-mode--anchor-first-sibling 0)
@@ -838,6 +876,14 @@ php-ts-mode--class-magic-methods
     "__toString" "__invoke" "__set_state" "__clone" "__debugInfo")
   "PHP predefined magic methods.")
 
+(defconst php-ts-mode--prettify-symbols-alist
+  '(("<=" . ?≤)
+    (">=" . ?≥)
+    ("->"  . ?→)
+    ("=>"  . ?⇒)
+    ("::" . ?∷))
+  "Value for `prettify-symbols-alist' in `php-ts-mode'.")
+
 (defun php-ts-mode--test-namespace-name-as-prefix-p ()
   "Return t if namespace_name_as_prefix is a named node, nil otherwise."
   (treesit-query-valid-p 'php "(namespace_name_as_prefix)"))
@@ -850,14 +896,18 @@ php-ts-mode--test-namespace-use-group-clause-p
   "Return t if namespace_use_group_clause is a named node, nil otherwise."
   (treesit-query-valid-p 'php "(namespace_use_group_clause)"))
 
-(defun php-ts-mode--test-visibility-modifier-operation-clause-p ()
+(defun php-ts-mode--test-visibility-modifier-operation-p ()
   "Return t if (visibility_modifier (operation)) is defined, nil otherwise."
   (treesit-query-valid-p 'php "(visibility_modifier (operation))"))
 
-(defun php-ts-mode--test-property-hook-clause-p ()
+(defun php-ts-mode--test-property-hook-p ()
   "Return t if property_hook is a named node, nil otherwise."
   (treesit-query-valid-p 'php "(property_hook)"))
 
+(defun php-ts-mode--test-relative-name-p ()
+  "Return t if relative_name is a named node, nil otherwise."
+  (treesit-query-valid-p 'php "(relative_name)"))
+
 (defun php-ts-mode--font-lock-settings ()
   "Tree-sitter font-lock settings."
   (treesit-font-lock-rules
@@ -866,7 +916,7 @@ php-ts-mode--font-lock-settings
    :feature 'keyword
    :override t
    `([,@php-ts-mode--keywords] @font-lock-keyword-face
-     ,@(when (php-ts-mode--test-visibility-modifier-operation-clause-p)
+     ,@(when (php-ts-mode--test-visibility-modifier-operation-p)
 	 '((visibility_modifier (operation) @font-lock-builtin-face)))
      (var_modifier) @font-lock-builtin-face)
 
@@ -882,8 +932,8 @@ php-ts-mode--font-lock-settings
      ;; predefined constant or built in constant (part of PHP core)
      ((name) @font-lock-builtin-face
       (:match ,(rx-to-string
-                `(: bos (or ,@php-ts-mode--predefined-constant) eos))
-              @font-lock-builtin-face))
+		`(: bos (or ,@php-ts-mode--predefined-constant) eos))
+	      @font-lock-builtin-face))
      ;; user defined constant
      ((name) @font-lock-constant-face
       (:match "\\`_*[A-Z][0-9A-Z_]+\\'" @font-lock-constant-face))
@@ -917,7 +967,8 @@ php-ts-mode--font-lock-settings
      (member_access_expression
       name: (_) @font-lock-variable-name-face)
      (scoped_property_access_expression
-      scope: (name) @font-lock-constant-face))
+      scope: (name) @font-lock-constant-face)
+     (error_suppression_expression (name) @font-lock-property-name-face))
 
    :language 'php
    :feature 'string
@@ -966,28 +1017,28 @@ php-ts-mode--font-lock-settings
       name: (_) @font-lock-type-face)
      (function_definition
       name: (_) @font-lock-function-name-face)
-     ,@(when (php-ts-mode--test-property-hook-clause-p)
+     ,@(when (php-ts-mode--test-property-hook-p)
 	 '((property_hook (name) @font-lock-function-name-face)))
      (method_declaration
       name: (_) @font-lock-function-name-face)
      (method_declaration
       name: (name) @font-lock-builtin-face
       (:match ,(rx-to-string
-                `(: bos (or ,@php-ts-mode--class-magic-methods) eos))
-              @font-lock-builtin-face))
+		`(: bos (or ,@php-ts-mode--class-magic-methods) eos))
+	      @font-lock-builtin-face))
      ("=>") @font-lock-keyword-face
      (object_creation_expression
       (name) @font-lock-type-face)
      ,@(when (php-ts-mode--test-namespace-name-as-prefix-p)
-           '((namespace_name_as_prefix "\\" @font-lock-delimiter-face)
-             (namespace_name_as_prefix
-              (namespace_name (name)) @font-lock-type-face)))
+	 '((namespace_name_as_prefix "\\" @font-lock-delimiter-face)
+	   (namespace_name_as_prefix
+	    (namespace_name (name)) @font-lock-type-face)))
      ,@(if (php-ts-mode--test-namespace-aliasing-clause-p)
-           '((namespace_aliasing_clause (name) @font-lock-type-face))
-         '((namespace_use_clause alias: (name) @font-lock-type-face)))
+	   '((namespace_aliasing_clause (name) @font-lock-type-face))
+	 '((namespace_use_clause alias: (name) @font-lock-type-face)))
      ,@(when (not (php-ts-mode--test-namespace-use-group-clause-p))
-         '((namespace_use_group
-            (namespace_use_clause (name) @font-lock-type-face))))
+	 '((namespace_use_group
+	    (namespace_use_clause (name) @font-lock-type-face))))
      (namespace_use_clause (name) @font-lock-type-face)
      (namespace_name "\\" @font-lock-delimiter-face)
      (namespace_name (name) @font-lock-type-face)
@@ -1035,7 +1086,9 @@ php-ts-mode--font-lock-settings
      (use_as_clause (name) @font-lock-property-use-face)
      ,@(when (not (php-ts-mode--test-namespace-name-as-prefix-p))
 	 '((qualified_name prefix: "\\" @font-lock-delimiter-face)))
-     (qualified_name (name) @font-lock-constant-face))
+     (qualified_name (name) @font-lock-constant-face)
+     ,@(when (php-ts-mode--test-relative-name-p)
+	 '((relative_name (name) @font-lock-constant-face))))
 
    :language 'php
    :feature 'property
@@ -1061,35 +1114,17 @@ php-ts-mode--font-lock-settings
 ;;; Font-lock helpers
 
 (defconst php-ts-mode--custom-html-font-lock-settings
-  (treesit-font-lock-rules
-   :language 'html
-   :override t
-   :feature 'comment
-   `((comment) @font-lock-comment-face
-     ;; handle shebang path and others type of comment
-     (document (text) @font-lock-comment-face))
-
-   :language 'html
-   :override t
-   :feature 'keyword
-   `("doctype" @font-lock-keyword-face)
-
-   :language 'html
-   :override t
-   :feature 'definition
-   `((tag_name) @font-lock-function-name-face)
-
-   :language 'html
-   :override 'append
-   :feature 'string
-   `((quoted_attribute_value) @font-lock-string-face)
-
-   :language 'html
-   :override t
-   :feature 'property
-   `((attribute_name) @font-lock-variable-name-face))
+  (treesit-replace-font-lock-feature-settings
+   (treesit-font-lock-rules
+    :language 'html
+    :override t
+    :feature 'comment
+    '((comment) @font-lock-comment-face
+      ;; handle shebang path and others type of comment
+      (document (text) @font-lock-comment-face)))
+   mhtml-ts-mode--treesit-font-lock-settings)
   "Tree-sitter Font-lock settings for HTML when embedded in PHP.
-Like `html-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.")
+Like `mhtml-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.")
 
 (defvar php-ts-mode--phpdoc-font-lock-settings
   (treesit-font-lock-rules
@@ -1102,8 +1137,9 @@ php-ts-mode--phpdoc-font-lock-settings
    :feature 'type
    :override t
    '((union_type
-      [(array_type) (primitive_type) (named_type) (optional_type)] @font-lock-type-face)
-     ([(array_type) (primitive_type) (named_type) (optional_type)] @font-lock-type-face)
+      [(primitive_type) (named_type) (optional_type)] @font-lock-type-face)
+     (array_type ["array" "list"] @font-lock-keyword-face)
+     ([(primitive_type) (named_type) (optional_type)] @font-lock-type-face)
      (fqsen (name) @font-lock-function-name-face))
 
    :language 'phpdoc
@@ -1118,27 +1154,13 @@ php-ts-mode--phpdoc-font-lock-settings
    :language 'phpdoc
    :feature 'variable
    :override t
-   '((variable_name (name) @font-lock-variable-name-face)))
-  "Tree-sitter font-lock settings for phpdoc.")
+   '((variable_name (name) @font-lock-variable-name-face))
 
-(defun php-ts-mode--colorize-css-value (node override start end &rest _)
-  "Colorize CSS property value like `css--fontify-region'.
-For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'."
-  (if (and php-ts-mode-css-fontify-colors
-           (string-equal "plain_value" (treesit-node-type node)))
-      (let ((color (css--compute-color start (treesit-node-text node t))))
-        (when color
-	  (with-silent-modifications
-	    (add-text-properties
-	     (treesit-node-start node) (treesit-node-end node)
-	     (list 'face (list :background color
-			       :foreground (readable-foreground-color
-                                            color)
-			       :box '(:line-width -1)))))))
-    (treesit-fontify-with-override
-     (treesit-node-start node) (treesit-node-end node)
-     'font-lock-variable-name-face
-     override start end)))
+   :language 'phpdoc
+   :feature 'phpdoc-error
+   :override t
+   '((ERROR) @php-ts-mode--phpdoc-fontify-error))
+  "Tree-sitter font-lock settings for phpdoc.")
 
 (defun php-ts-mode--fontify-error (node override start end &rest _)
   "Fontify the error nodes.
@@ -1148,6 +1170,13 @@ php-ts-mode--fontify-error
    'font-lock-warning-face
    override start end))
 
+(defun php-ts-mode--phpdoc-fontify-error (node override start end &rest _)
+  "Fontify the phpdoc error nodes.
+Most of the error are only tag non handled by the phpdoc parser.
+For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'."
+  (when php-ts-mode-phpdoc-highlight-errors
+    (php-ts-mode--fontify-error node override start end)))
+
 
 ;;; Imenu
 
@@ -1157,92 +1186,106 @@ php-ts-mode--parent-object
    node
    (lambda (n)
      (member (treesit-node-type n)
-             '("class_declaration"
-               "enum_declaration"
-               "function_definition"
-               "interface_declaration"
-               "method_declaration"
-               "namespace_definition"
-               "trait_declaration")))))
+	     '("class_declaration"
+	       "enum_declaration"
+	       "function_definition"
+	       "interface_declaration"
+	       "method_declaration"
+	       "namespace_definition"
+	       "trait_declaration")))))
 
 (defun php-ts-mode--defun-name-separator (node)
   "Return a separator to connect object name, based on NODE type."
   (let ((node-type (treesit-node-type node)))
     (cond ((member node-type '("function_definition" "method_declaration"))
-           "()::")
-          ((member node-type '("class_declaration" "enum_declaration" "trait_declaration"))
-           "::")
-          (t "\\"))))
+	   "()::")
+	  ((member node-type '("class_declaration" "enum_declaration" "trait_declaration"))
+	   "::")
+	  (t "\\"))))
 
 (defun php-ts-mode--defun-object-name (node node-text)
   "Compose the full name of a NODE that is a PHP variable, method, class etc.
 If the NODE has a parent, it recursively concat the parent names with NODE-TEXT,
 otherwise it returns NODE-TEXT."
   (let* ((parent-node (php-ts-mode--parent-object node))
-         (parent-node-text
-          (treesit-node-text
-           (treesit-node-child-by-field-name parent-node "name") t))
-         (parent-node-separator (php-ts-mode--defun-name-separator parent-node)))
+	 (parent-node-text
+	  (treesit-node-text
+	   (treesit-node-child-by-field-name parent-node "name") t))
+	 (parent-node-separator (php-ts-mode--defun-name-separator parent-node)))
     (if parent-node
-        (progn
-          (setq parent-node-text
-                (php-ts-mode--defun-object-name
-                 parent-node
-                 parent-node-text))
-          (concat parent-node-text parent-node-separator node-text))
+	(progn
+	  (setq parent-node-text
+		(php-ts-mode--defun-object-name
+		 parent-node
+		 parent-node-text))
+	  (concat parent-node-text parent-node-separator node-text))
       node-text)))
 
 (defun php-ts-mode--defun-name (node)
   "Return the defun name of NODE.
 Return nil if the NODE has no field “name” or if NODE is not a defun node."
-  (let ((child (treesit-node-child-by-field-name node "name")))
-    (cl-case (intern (treesit-node-type node))
-      (class_declaration (treesit-node-text child t))
-      (trait_declaration (treesit-node-text child t))
-      (interface_declaration (treesit-node-text child t))
-      (namespace_definition (treesit-node-text child t))
-      (enum_declaration (treesit-node-text child t))
-      (function_definition (treesit-node-text child t))
-      (method_declaration
-       (php-ts-mode--defun-object-name node (treesit-node-text child t)))
-      (variable_name
-       (php-ts-mode--defun-object-name node (treesit-node-text node t)))
-      (const_element
-       (php-ts-mode--defun-object-name
-        node
-        (treesit-node-text (treesit-node-child node 0) t))))))
+  (let ((lang (treesit-node-language node))
+	(child (treesit-node-child-by-field-name node "name")))
+    (if (eq lang 'php)
+	(cl-case (intern (treesit-node-type node))
+	  (class_declaration (treesit-node-text child t))
+	  (trait_declaration (treesit-node-text child t))
+	  (interface_declaration (treesit-node-text child t))
+	  (namespace_definition (treesit-node-text child t))
+	  (enum_declaration (treesit-node-text child t))
+	  (function_definition (treesit-node-text child t))
+	  (method_declaration
+	   (php-ts-mode--defun-object-name node (treesit-node-text child t)))
+	  (variable_name
+	   (php-ts-mode--defun-object-name node (treesit-node-text node t)))
+	  (const_element
+	   (php-ts-mode--defun-object-name
+	    node
+	    (treesit-node-text (treesit-node-child node 0) t))))
+      (mhtml-ts-mode--defun-name node))))
 
 
 ;;; Defun navigation
 
+(defvar php-ts-mode--treesit-defun-type-regexp
+  (rx bos (or "class_declaration"
+	      "enum_declaration"
+	      "function_definition"
+	      "interface_declaration"
+	      "method_declaration"
+	      "namespace_definition"
+	      "trait_declaration")
+      eos)
+  "Settings for `treesit-defun-type-regexp'.")
+
 (defun php-ts-mode--indent-defun ()
   "Indent the current top-level declaration syntactically.
 `treesit-defun-type-regexp' defines what constructs to indent."
   (interactive "*")
   (when-let* ((orig-point (point-marker))
-             (node (treesit-defun-at-point)))
+	      (node (treesit-defun-at-point)))
     (indent-region (treesit-node-start node)
-                   (treesit-node-end node))
+		   (treesit-node-end node))
     (goto-char orig-point)))
 
 (defun php-ts-mode--defun-valid-p (node)
   "Return non-nil if NODE is a valid defun node.
 Ie, NODE is not nested."
   (not (and (member (treesit-node-type node)
-                    '("variable_name"
-                      "const_element"
-                      "enum_declaration"
-                      "union_declaration"
-                      "declaration"))
-            ;; If NODE's type is one of the above, make sure it is
-            ;; top-level.
-            (treesit-node-top-level
-             node (rx (or "variable_name"
-                          "const_element"
-                          "function_definition"
-                          "enum_declaration"
-                          "union_declaration"
-                          "declaration"))))))
+		    '("variable_name"
+		      "const_element"
+		      "enum_declaration"
+		      "union_declaration"
+		      "declaration"))
+	    ;; If NODE's type is one of the above, make sure it is
+	    ;; top-level.
+	    (treesit-node-top-level
+	     node (rx (or "variable_name"
+			  "const_element"
+			  "function_definition"
+			  "enum_declaration"
+			  "union_declaration"
+			  "declaration"))))))
 
 
 ;;; Filling
@@ -1253,39 +1296,63 @@ php-ts-mode--comment-indent-new-line
 less common PHP-style # comment.  SOFT works the same as in
 `comment-indent-new-line'."
   (if (save-excursion
-        ;; Line start with # or ## or ###...
-        (beginning-of-line)
-        (re-search-forward
-         (rx "#" (group (* (any "#")) (* " ")))
-         (line-end-position)
-         t nil))
+	;; Line start with # or ## or ###...
+	(beginning-of-line)
+	(re-search-forward
+	 (rx "#" (group (* (any "#")) (* " ")))
+	 (line-end-position)
+	 t nil))
       (let ((offset (- (match-beginning 0) (line-beginning-position)))
-            (comment-prefix (match-string 0))
+	    (comment-prefix (match-string 0))
 	    (insert-line-break
-             (lambda ()
+	     (lambda ()
 	       (delete-horizontal-space)
 	       (if soft
 		   (insert-and-inherit ?\n)
 		 (newline  1)))))
 	(funcall insert-line-break)
-        (delete-region (line-beginning-position) (point))
-        (insert
-         (make-string offset ?\s)
-         comment-prefix))
+	(delete-region (line-beginning-position) (point))
+	(insert
+	 (make-string offset ?\s)
+	 comment-prefix))
     ;; other style of comments
     (c-ts-common-comment-indent-new-line soft)))
 
-(defun php-ts-mode-comment-setup ()
-  "Set up local variables for PHP comment.
-Depends on `c-ts-common-comment-setup'."
-  (c-ts-common-comment-setup)
-  (setq-local c-ts-common--comment-regexp "comment"
-              comment-line-break-function #'php-ts-mode--comment-indent-new-line
-              comment-style 'extra-line
-              comment-start-skip (rx (or (seq "#" (not (any "[")))
-                                         (seq "/" (+ "/"))
-                                         (seq "/" (+ "*")))
-                                     (* (syntax whitespace)))))
+(defvar-local php-ts-mode--comment-current-plist
+  '(:php nil :comment-start "// " :comment-end "")
+  "Store the current state of PHP comment configuration.
+`mhtml-ts-mode' changes the comment configuration when switching
+between HTML, Css and Javascript, so we have to do the same when
+switching back to PHP.
+The prop :php must be t if we are in a PHP or PHPDOC range.")
+
+(defun php-ts-mode--comment-setup (&optional force)
+  "Set up local variables for comments.
+If FORCE is t setup comment for PHP.  Depends on
+`mhtml-ts-mode--comment-setup' and
+`mhtml-ts-mode--comment-current-lang'."
+  (let ((lang (treesit-language-at (point))))
+    (if (and (or (eq lang 'php) (eq lang 'phpdoc) force)
+	     (eq (plist-get php-ts-mode--comment-current-plist :php) nil))
+	;; do setup only if the previous language isn't PHP
+	(progn
+	  (plist-put php-ts-mode--comment-current-plist :php t)
+	  (c-ts-common-comment-setup)
+	  (setq-local c-ts-common--comment-regexp "comment"
+		      comment-line-break-function #'php-ts-mode--comment-indent-new-line
+		      comment-style 'extra-line
+		      comment-start (plist-get php-ts-mode--comment-current-plist :comment-start)
+		      comment-end (plist-get php-ts-mode--comment-current-plist :comment-end)
+		      comment-start-skip (rx (or (seq "#" (not (any "[")))
+						 (seq "/" (+ "/"))
+						 (seq "/" (+ "*")))
+					     (* (syntax whitespace)))
+		      ;; reset the state of mhtml-ts-mode--comment-setup
+		      mhtml-ts-mode--comment-current-lang nil))
+      ;; otherwise set comment style for other languages.
+      (plist-put php-ts-mode--comment-current-plist :php nil)
+      (setq-local comment-style 'indent) ; this is the default for mhtml-ts-mode.
+      (mhtml-ts-mode--comment-setup))))
 
 
 ;;; Modes
@@ -1293,15 +1360,20 @@ php-ts-mode-comment-setup
 (defun php-ts-mode-set-comment-style ()
   "Set a different comment style."
   (interactive)
-  (setq-local comment-start
-              (completing-read
-               "Choose comment style: "
-               '("/**" "//" "/*" "#") nil t nil nil "// "))
-  (cond
-   ((equal comment-start "/*") (setq-local comment-end "*/"))
-   ((equal comment-start "//") (setq-local comment-end ""))
-   ((equal comment-start "#") (setq-local comment-end ""))
-   ((equal comment-start "/**") (setq-local comment-end "*/"))))
+  (let ((lang (treesit-language-at (point))))
+    (when (or (eq lang 'php) (eq lang 'phpdoc))
+      (setq-local comment-start
+		  (completing-read
+		   "Choose comment style: "
+		   '("/**" "// " "/*" "# ") nil t nil nil "// "))
+      (cond
+       ((equal comment-start "/*") (setq-local comment-end "*/"))
+       ((equal comment-start "// ") (setq-local comment-end ""))
+       ((equal comment-start "# ") (setq-local comment-end ""))
+       ((equal comment-start "/**") (setq-local comment-end "*/")))
+      ;; store the value in order to recover when switch to php from other languages.
+      (plist-put php-ts-mode--comment-current-plist :comment-start comment-start)
+      (plist-put php-ts-mode--comment-current-plist :comment-end comment-end))))
 
 (defvar-keymap php-ts-mode-map
   :doc "Keymap for `php-ts-mode' buffers."
@@ -1321,7 +1393,7 @@ php-ts-mode-menu
      :enable mark-active
      :help "Comment out the region between the mark and point"]
     ["Uncomment Region" (comment-region (region-beginning)
-                                        (region-end) '(4))
+					(region-end) '(4))
      :enable mark-active
      :help "Uncomment the region between the mark and point"]
     ["Indent Top-level Expression" php-ts-mode--indent-defun
@@ -1336,7 +1408,7 @@ php-ts-mode-menu
      ["Set Indentation Style..." php-ts-mode-set-style
       :help "Set PHP indentation style for current buffer"]
      ["Show Current Style Name"(message "Indentation Style: %s"
-                                        php-ts-mode-indent-style)
+					php-ts-mode-indent-style)
       :help "Show the name of the PHP indentation style for current buffer"]
      ["Set Comment Style" php-ts-mode-set-comment-style
       :help "Choose PHP comment style between block and line comments"])
@@ -1377,114 +1449,98 @@ php-ts-mode--feature-list
      function)))
 
 ;;;###autoload
-(define-derived-mode php-ts-mode prog-mode "PHP"
+(define-derived-mode php-ts-mode prog-mode
+  '("PHP"
+    (:eval (let ((lang (treesit-language-at (point))))
+	     (cond ((eq lang 'html) "+HTML")
+		   ((eq lang 'javascript) "+HTML+JS")
+		   ((eq lang 'css) "+HTML+CSS")))))
   "Major mode for editing PHP, powered by tree-sitter."
   :syntax-table php-ts-mode--syntax-table
 
+  ;; TODO: wouldn't better to delegate the check and the
+  ;; init of the mhtml parser to mhtml-ts-mode?
   (if (not (and
-            (treesit-ensure-installed 'php)
-            (treesit-ensure-installed 'phpdoc)
-            (treesit-ensure-installed 'html)
-            (treesit-ensure-installed 'javascript)
-            (treesit-ensure-installed 'jsdoc)
-            (treesit-ensure-installed 'css)))
+	    (treesit-ensure-installed 'php)
+	    (treesit-ensure-installed 'phpdoc)
+	    (treesit-ensure-installed 'html)
+	    (treesit-ensure-installed 'javascript)
+	    (treesit-ensure-installed 'jsdoc)
+	    (treesit-ensure-installed 'css)))
       (error "Tree-sitter for PHP isn't
     available.  You can install the parsers with M-x
     `php-ts-mode-install-parsers'")
 
     ;; phpdoc is a local parser, don't create a parser for it
+    ;; create the parser needed by mhtml-ts-mode
     (treesit-parser-create 'html)
     (treesit-parser-create 'css)
     (treesit-parser-create 'javascript)
 
     ;; define the injected parser ranges
     (setq-local treesit-range-settings
-                (treesit-range-rules
-                 :embed 'phpdoc
-                 :host 'php
-                 :local t
-                 '(((comment) @cap
-                    (:match "/\\*\\*" @cap)))
-
-                 :embed 'html
-                 :host 'php
-                 '((program (text) @cap)
-                   (text_interpolation (text) @cap))
-
-                 :embed 'javascript
-                 :host 'html
-                 :offset '(1 . -1)
-                 '((script_element
-                    (start_tag (tag_name))
-                    (raw_text) @cap))
-
-                 :embed 'jsdoc
-                 :host 'javascript
-                 :local t
-                 `(((comment) @cap
-                    (:match ,js--treesit-jsdoc-beginning-regexp @cap)))
-
-                 :embed 'css
-                 :host 'html
-                 :offset '(1 . -1)
-                 '((style_element
-                    (start_tag (tag_name))
-                    (raw_text) @cap))))
+		(append
+		 (treesit-range-rules
+		  :embed 'phpdoc
+		  :host 'php
+		  :local t
+		  '(((comment) @cap
+		     (:match "/\\*\\*" @cap)))
+
+		  :embed 'html
+		  :host 'php
+		  '((program (text) @cap)
+		    (text_interpolation (text) @cap)))
+		 ;; inject the range rules from mhtml-ts-mode
+		 mhtml-ts-mode--range-settings))
 
     ;; Navigation.
-    (setq-local treesit-defun-type-regexp
-                (regexp-opt '("class_declaration"
-                              "enum_declaration"
-                              "function_definition"
-                              "interface_declaration"
-                              "method_declaration"
-                              "namespace_definition"
-                              "trait_declaration")))
-
     (setq-local treesit-defun-name-function #'php-ts-mode--defun-name)
 
     (setq-local treesit-thing-settings
-                `((php
-                   (defun ,treesit-defun-type-regexp)
-                   (sexp (not (or (and named
-                                       ,(rx bos (or "program"
-                                                    "comment")
-                                            eos))
-                                  (and anonymous
-                                       ,(rx bos (or "{" "}" "[" "]"
-                                                    "(" ")" ",")
-                                            eos)))))
-                   (list
-                    ,(rx bos (or "namespace_use_group"
-                                 "enum_declaration_list"
-                                 "declaration_list"
-                                 "property_hook_list"
-                                 "use_list"
-                                 "anonymous_function_use_clause"
-                                 "formal_parameters"
-                                 "match_block"
-                                 "switch_block"
-                                 "compound_statement"
-                                 "parenthesized_expression"
-                                 "_array_destructing"
-                                 "arguments"
-                                 "_complex_string_part")
-                         eos))
-                   (sentence  ,(regexp-opt
-                                '("break_statement"
-                                  "case_statement"
-                                  "continue_statement"
-                                  "declaration"
-                                  "default_statement"
-                                  "do_statement"
-                                  "expression_statement"
-                                  "for_statement"
-                                  "if_statement"
-                                  "return_statement"
-                                  "switch_statement"
-                                  "while_statement"
-                                  "statement")))
-                   (text ,(regexp-opt '("comment" "text"))))))
+		(append
+		 `((php
+		    (defun ,php-ts-mode--treesit-defun-type-regexp)
+		    (sexp (not (or (and named
+					,(rx bos (or "program"
+						     "comment")
+					     eos))
+				   (and anonymous
+					,(rx bos (or "{" "}" "[" "]"
+						     "(" ")" ",")
+					     eos)))))
+		    (list
+		     ,(rx bos (or "namespace_use_group"
+				  "enum_declaration_list"
+				  "declaration_list"
+				  "property_hook_list"
+				  "use_list"
+				  "anonymous_function_use_clause"
+				  "formal_parameters"
+				  "match_block"
+				  "switch_block"
+				  "compound_statement"
+				  "parenthesized_expression"
+				  "_array_destructing"
+				  "arguments"
+				  "_complex_string_part")
+			  eos))
+		    (sentence  ,(regexp-opt
+				 '("break_statement"
+				   "case_statement"
+				   "continue_statement"
+				   "declaration"
+				   "default_statement"
+				   "do_statement"
+				   "expression_statement"
+				   "for_statement"
+				   "if_statement"
+				   "return_statement"
+				   "switch_statement"
+				   "while_statement"
+				   "statement")))
+		    (text ,(regexp-opt '("comment" "text")))))
+		 mhtml-ts-mode--treesit-thing-settings))
 
     ;; Indent.
     (when (eq php-ts-mode-indent-style 'wordpress)
@@ -1493,75 +1549,91 @@ php-ts-mode
     (setq-local c-ts-common-indent-offset 'php-ts-mode-indent-offset)
     (setq-local treesit-simple-indent-rules (php-ts-mode--get-indent-style))
     (setq-local treesit-simple-indent-rules
-                (append treesit-simple-indent-rules
-                        php-ts-mode--phpdoc-indent-rules
-                        html-ts-mode--indent-rules
-                        ;; Extended rules for js and css, to
-                        ;; indent appropriately when injected
-                        ;; into html
-                        `((javascript ((parent-is "program")
-                                       php-ts-mode--js-css-tag-bol
-                                       php-ts-mode-js-css-indent-offset)
-                                      ,@(cdr (car js--treesit-indent-rules))))
-                        `((css ((parent-is "stylesheet")
-                                php-ts-mode--js-css-tag-bol
-                                php-ts-mode-js-css-indent-offset)
-                               ,@(cdr (car css--treesit-indent-rules))))))
+		(append treesit-simple-indent-rules
+			php-ts-mode--phpdoc-indent-rules
+			`((html ,@(alist-get 'html
+					     (mhtml-ts-mode--treesit-indent-rules))))
+			;; Replace rules for js and css, to
+			;; indent appropriately when injected
+			;; into html.
+			(treesit-simple-indent-modify-rules
+			 'javascript
+			 `((javascript ((parent-is "program")
+					mhtml-ts-mode--js-css-tag-bol
+					php-ts-mode-js-css-indent-offset)))
+			 `((javascript
+			    ,@(alist-get 'javascript
+					 (mhtml-ts-mode--treesit-indent-rules)))))
+
+			(treesit-simple-indent-modify-rules
+			 'css
+			 `((css ((parent-is "stylesheet")
+				 mhtml-ts-mode--js-css-tag-bol
+				 php-ts-mode-js-css-indent-offset)))
+			 `((css
+			    ,@(alist-get 'css
+					 (mhtml-ts-mode--treesit-indent-rules)))))
+			))
 
     ;; Comment
-    (php-ts-mode-comment-setup)
+    (php-ts-mode--comment-setup t) ; Init the comment setup for PHP
+    ;; Allow the comment machinery to automatically switch between the
+    ;; various comment styles supported by `php-ts-mode'.
+    (setq-local comment-setup-function #'php-ts-mode--comment-setup)
 
     ;; PHP vars are case-sensitive
     (setq-local case-fold-search t)
 
     ;; Electric
     (setq-local electric-indent-chars
-                (append "{}():;," electric-indent-chars))
+		(append "{}():;," electric-indent-chars))
+
+    (setq-local electric-layout-rules
+	        '((?\; . after) (?\{ . after) (?\} . before)))
 
     ;; Imenu/Which-function
-    (setq-local treesit-simple-imenu-settings
-                '(("Class" "\\`class_declaration\\'" nil nil)
-                  ("Enum" "\\`enum_declaration\\'" nil nil)
-                  ("Function" "\\`function_definition\\'" nil nil)
-                  ("Interface" "\\`interface_declaration\\'" nil nil)
-                  ("Method" "\\`method_declaration\\'" nil nil)
-                  ("Namespace" "\\`namespace_definition\\'" nil nil)
-                  ("Trait" "\\`trait_declaration\\'" nil nil)
-                  ("Variable" "\\`variable_name\\'" nil nil)
-                  ("Constant" "\\`const_element\\'" nil nil)))
+    (setq-local treesit-aggregated-simple-imenu-settings
+		(append
+		 '((php ("Class" "\\`class_declaration\\'" nil nil)
+			("Enum" "\\`enum_declaration\\'" nil nil)
+			("Function" "\\`function_definition\\'" nil nil)
+			("Interface" "\\`interface_declaration\\'" nil nil)
+			("Method" "\\`method_declaration\\'" nil nil)
+			("Namespace" "\\`namespace_definition\\'" nil nil)
+			("Trait" "\\`trait_declaration\\'" nil nil)
+			("Variable" "\\`variable_name\\'" nil nil)
+			("Constant" "\\`const_element\\'" nil nil)))
+		 mhtml-ts-mode--treesit-aggregated-simple-imenu-settings))
 
     ;; Outline
-    (setq-local treesit-outline-predicate
-                (rx bos (or "class_declaration"
-                            "function_definition"
-                            "interface_declaration"
-                            "method_declaration"
-                            "namespace_definition"
-                            "trait_declaration")
-                    eos))
+    (setq-local treesit-aggregated-outline-predicate
+		(append
+		 `((php . ,(rx bos (or "class_declaration"
+				       "function_definition"
+				       "interface_declaration"
+				       "method_declaration"
+				       "namespace_definition"
+				       "trait_declaration")
+			       eos))
+		   (phpdoc . ,(rx bos "document" eos)))
+		 mhtml-ts-mode--treesit-aggregated-outline-predicate))
 
     ;; Font-lock.
     (setq-local treesit-font-lock-settings
-                (append (php-ts-mode--font-lock-settings)
-                        php-ts-mode--custom-html-font-lock-settings
-                        js--treesit-font-lock-settings
-                        (append
-                         ;; Rule for coloring CSS property values.
-                         ;; Placed before `css--treesit-settings'
-                         ;; to win against the same rule contained therein.
-                         (treesit-font-lock-rules
-                          :language 'css
-                          :override t
-                          :feature 'variable
-                          '((plain_value) @php-ts-mode--colorize-css-value))
-                         css--treesit-settings)
-                        php-ts-mode--phpdoc-font-lock-settings))
+		(append
+		 (php-ts-mode--font-lock-settings)
+		 php-ts-mode--custom-html-font-lock-settings
+		 php-ts-mode--phpdoc-font-lock-settings))
 
     (setq-local treesit-font-lock-feature-list php-ts-mode--feature-list)
+    (setq-local prettify-symbols-alist php-ts-mode--prettify-symbols-alist)
 
     ;; Align.
     (setq-local align-indent-before-aligning t)
 
+    ;; Find files
+    (setq-local find-sibling-rules php-ts-mode-find-sibling-rules)
+
     ;; should be the last one
     (setq-local treesit-primary-parser (treesit-parser-create 'php))
     (treesit-major-mode-setup)
@@ -1570,11 +1642,11 @@ php-ts-mode
 
 ;;;###autoload
 (defun php-ts-mode-run-php-webserver (&optional port
-                                                hostname
-                                                document-root
-                                                router-script
-                                                num-of-workers
-                                                config)
+						hostname
+						document-root
+						router-script
+						num-of-workers
+						config)
   "Run PHP built-in web server.
 
 PORT: Port number of built-in web server, default `php-ts-mode-ws-port'.
@@ -1595,46 +1667,46 @@ php-ts-mode-run-php-webserver
 PORT, HOSTNAME, DOCUMENT-ROOT, ROUTER-SCRIPT, NUM-OF-WORKERS and
 CONFIG."
   (interactive (when current-prefix-arg
-                 (php-ts-mode--webserver-read-args)))
+		 (php-ts-mode--webserver-read-args)))
   (let* ((port (or
-                port
-                php-ts-mode-ws-port
-                (php-ts-mode--webserver-read-args 'port)))
-         (hostname (or
-                    hostname
-                    php-ts-mode-ws-hostname
-                    (php-ts-mode--webserver-read-args 'hostname)))
-         (document-root (or
-                         document-root
-                         php-ts-mode-ws-document-root
-                         (php-ts-mode--webserver-read-args 'document-root)))
-         (config (or config
-                     (when php-ts-mode-php-config
-                       (expand-file-name php-ts-mode-php-config))))
-         (host (format "%s:%d" hostname port))
-         (name (format "PHP web server on: %s" host))
-         (buf-name (format "*%s*" name))
-         (args (delq
-                nil
-                (list "-S" host
-                      "-t" document-root
-                      (when config
+		port
+		php-ts-mode-ws-port
+		(php-ts-mode--webserver-read-args 'port)))
+	 (hostname (or
+		    hostname
+		    php-ts-mode-ws-hostname
+		    (php-ts-mode--webserver-read-args 'hostname)))
+	 (document-root (or
+			 document-root
+			 php-ts-mode-ws-document-root
+			 (php-ts-mode--webserver-read-args 'document-root)))
+	 (config (or config
+		     (when php-ts-mode-php-config
+		       (expand-file-name php-ts-mode-php-config))))
+	 (host (format "%s:%d" hostname port))
+	 (name (format "PHP web server on: %s" host))
+	 (buf-name (format "*%s*" name))
+	 (args (delq
+		nil
+		(list "-S" host
+		      "-t" document-root
+		      (when config
 			(format "-c %s" config))
-                      router-script)))
-         (process-environment
-          (nconc (cond
-                  (num-of-workers
-                   (list
-                    (format "PHP_CLI_SERVER_WORKERS=%d" num-of-workers)))
-                  (php-ts-mode-ws-workers
-                   (list
-                    (format "PHP_CLI_SERVER_WORKERS=%d" php-ts-mode-ws-workers))))
-                 process-environment)))
+		      router-script)))
+	 (process-environment
+	  (nconc (cond
+		  (num-of-workers
+		   (list
+		    (format "PHP_CLI_SERVER_WORKERS=%d" num-of-workers)))
+		  (php-ts-mode-ws-workers
+		   (list
+		    (format "PHP_CLI_SERVER_WORKERS=%d" php-ts-mode-ws-workers))))
+		 process-environment)))
     (if (get-buffer buf-name)
-        (message "Switch to already running web server into buffer %s" buf-name)
+	(message "Switch to already running web server into buffer %s" buf-name)
       (message "Run PHP built-in web server with args %s into buffer %s"
-               (string-join args " ")
-               buf-name)
+	       (string-join args " ")
+	       buf-name)
       (apply #'make-comint name (php-ts-mode--executable) nil args))
     (funcall
      (if (called-interactively-p 'interactive) #'display-buffer #'get-buffer)
@@ -1647,45 +1719,45 @@ php-ts-mode--webserver-read-args
 The optional TYPE can be the symbol \"port\", \"hostname\", \"document-root\",
 \"router-script\", \"num-workers\" or \"config\", otherwise it requires all of them."
   (let ((ask-port (lambda ()
-                    (read-number "Port: " (or
-                                           php-ts-mode-ws-port
-                                           3000))))
-        (ask-hostname (lambda ()
-                        (read-string "Hostname: "
-                                     (or
-                                      php-ts-mode-ws-hostname
-                                      "localhost"))))
-        (ask-document-root (lambda ()
-                             (expand-file-name
-                              (read-directory-name "Document root: "
-                                                   (file-name-directory
-                                                    (or (buffer-file-name)
-                                                        default-directory))))))
-        (ask-router-script (lambda ()
-                             (expand-file-name
-                              (read-file-name "Router script: "
-                                              (file-name-directory
-                                               (or (buffer-file-name)
-                                                   default-directory))))))
-        (ask-num-workers (lambda ()
-                           (let ((num-workers
-                                  (read-number
-                                   "Number of workers (less then 2 means no workers): "
-                                   (or php-ts-mode-ws-workers 0))))
-                             ;; num-workers must be >= 2 or nil
-                             ;; otherwise PHP's built-in web server will not start.
-                             (if (> num-workers 1)
-                                 num-workers
-                               nil))))
-        (ask-config (lambda()
-                      (let ((file-name (expand-file-name
-                                        (read-file-name "Alternative php.ini: "
-                                                        (file-name-directory
-                                                         (or (buffer-file-name)
-                                                             default-directory))))))
-                        (if (string= "" (file-name-directory file-name))
-                            nil
-                          file-name)))))
+		    (read-number "Port: " (or
+					   php-ts-mode-ws-port
+					   3000))))
+	(ask-hostname (lambda ()
+			(read-string "Hostname: "
+				     (or
+				      php-ts-mode-ws-hostname
+				      "localhost"))))
+	(ask-document-root (lambda ()
+			     (expand-file-name
+			      (read-directory-name "Document root: "
+						   (file-name-directory
+						    (or (buffer-file-name)
+							default-directory))))))
+	(ask-router-script (lambda ()
+			     (expand-file-name
+			      (read-file-name "Router script: "
+					      (file-name-directory
+					       (or (buffer-file-name)
+						   default-directory))))))
+	(ask-num-workers (lambda ()
+			   (let ((num-workers
+				  (read-number
+				   "Number of workers (less then 2 means no workers): "
+				   (or php-ts-mode-ws-workers 0))))
+			     ;; num-workers must be >= 2 or nil
+			     ;; otherwise PHP's built-in web server will not start.
+			     (if (> num-workers 1)
+				 num-workers
+			       nil))))
+	(ask-config (lambda()
+		      (let ((file-name (expand-file-name
+					(read-file-name "Alternative php.ini: "
+							(file-name-directory
+							 (or (buffer-file-name)
+							     default-directory))))))
+			(if (string= "" (file-name-directory file-name))
+			    nil
+			  file-name)))))
     (cl-case type
       (port (funcall ask-port))
       (hostname (funcall ask-hostname))
@@ -1694,21 +1766,21 @@ php-ts-mode--webserver-read-args
       (num-of-workers (funcall ask-num-workers))
       (config (funcall ask-config))
       (t (list
-          (funcall ask-port)
-          (funcall ask-hostname)
-          (funcall ask-document-root)
-          (funcall ask-router-script)
-          (funcall ask-num-workers)
-          (funcall ask-config))))))
+	  (funcall ask-port)
+	  (funcall ask-hostname)
+	  (funcall ask-document-root)
+	  (funcall ask-router-script)
+	  (funcall ask-num-workers)
+	  (funcall ask-config))))))
 
 (define-derived-mode inferior-php-ts-mode comint-mode "Inferior PHP"
   "Major mode for PHP inferior process."
   (setq-local scroll-conservatively 1
-              comint-input-ring-file-name php-ts-mode-inferior-history
-              comint-input-ignoredups t
-              comint-prompt-read-only t
-              comint-use-prompt-regexp t
-              comint-prompt-regexp (concat "^" php-ts-mode--inferior-prompt " "))
+	      comint-input-ring-file-name php-ts-mode-inferior-history
+	      comint-input-ignoredups t
+	      comint-prompt-read-only t
+	      comint-use-prompt-regexp t
+	      comint-prompt-regexp (concat "^" php-ts-mode--inferior-prompt " "))
   (comint-read-input-ring t))
 
 
@@ -1726,24 +1798,24 @@ run-php
 Prompt for CMD if `php-ts-mode-php-executable' is nil.
 Optional CONFIG, if supplied, is the php.ini file to use."
   (interactive (when current-prefix-arg
-                 (list
-                  (read-string "Run PHP: " (php-ts-mode--executable))
-                  (expand-file-name
-                   (read-file-name "With config: " php-ts-mode-php-config)))))
+		 (list
+		  (read-string "Run PHP: " (php-ts-mode--executable))
+		  (expand-file-name
+		   (read-file-name "With config: " php-ts-mode-php-config)))))
   (let* ((php-prog (php-ts-mode--executable))
-         (buffer (get-buffer-create php-ts-mode-inferior-php-buffer))
-         (cmd (or
-               cmd
-               php-prog
-               (read-string "Run PHP: " php-prog)))
-         (config (or
-                  config
-                  (and php-ts-mode-php-config
-                       (expand-file-name php-ts-mode-php-config)))))
+	 (buffer (get-buffer-create php-ts-mode-inferior-php-buffer))
+	 (cmd (or
+	       cmd
+	       php-prog
+	       (read-string "Run PHP: " php-prog)))
+	 (config (or
+		  config
+		  (and php-ts-mode-php-config
+		       (expand-file-name php-ts-mode-php-config)))))
     (unless (comint-check-proc buffer)
       (with-current-buffer buffer
-        (inferior-php-ts-mode-startup cmd config)
-        (inferior-php-ts-mode)))
+	(inferior-php-ts-mode-startup cmd config)
+	(inferior-php-ts-mode)))
     (when buffer
       (pop-to-buffer buffer))))
 
@@ -1752,39 +1824,39 @@ inferior-php-ts-mode-startup
 CMD is the command to run.  Optional CONFIG, if supplied, is the php.ini
 file to use."
   (setq-local php-ts-mode--inferior-php-process
-              (apply #'make-comint-in-buffer
-                     (string-replace "*" "" php-ts-mode-inferior-php-buffer)
-                     php-ts-mode-inferior-php-buffer
-                     cmd
-                     nil
-                     (delq
-                      nil
-                      (list
-                       (when config
-                         (format "-c %s" config))
-                       "-a"))))
+	      (apply #'make-comint-in-buffer
+		     (string-replace "*" "" php-ts-mode-inferior-php-buffer)
+		     php-ts-mode-inferior-php-buffer
+		     cmd
+		     nil
+		     (delq
+		      nil
+		      (list
+		       (when config
+			 (format "-c %s" config))
+		       "-a"))))
   (add-hook 'comint-preoutput-filter-functions
-            (lambda (string)
-              (let ((prompt (concat php-ts-mode--inferior-prompt " ")))
-                (if (member
-                     string
-                     (list prompt "php { " "php ( " "/* > " "Interactive shell\n\n"))
-                    string
-                  (let (;; Filter out prompts characters that accumulate when sending
-                        ;; regions to the inferior process.
-                        (clean-string
-                         (replace-regexp-in-string
-                          (rx-to-string `(or
-                                          (+ "php >" (opt space))
-                                          (+ "php {" (opt space))
-                                          (+ "php (" (opt space))
-                                          (+ "/*" (1+ space) (1+ ">") (opt space))))
-                          "" string)))
-                    ;; Re-add the prompt for the next line, if isn't empty.
-                    (if (string= clean-string "")
-                        ""
-                      (concat (string-chop-newline clean-string) "\n" prompt))))))
-            nil t)
+	    (lambda (string)
+	      (let ((prompt (concat php-ts-mode--inferior-prompt " ")))
+		(if (member
+		     string
+		     (list prompt "php { " "php ( " "/* > " "Interactive shell\n\n"))
+		    string
+		  (let (;; Filter out prompts characters that accumulate when sending
+			;; regions to the inferior process.
+			(clean-string
+			 (replace-regexp-in-string
+			  (rx-to-string `(or
+					  (+ "php >" (opt space))
+					  (+ "php {" (opt space))
+					  (+ "php (" (opt space))
+					  (+ "/*" (1+ space) (1+ ">") (opt space))))
+			  "" string)))
+		    ;; Re-add the prompt for the next line, if isn't empty.
+		    (if (string= clean-string "")
+			""
+		      (concat (string-chop-newline clean-string) "\n" prompt))))))
+	    nil t)
   (when php-ts-mode-inferior-history
     (set-process-sentinel
      (get-buffer-process  php-ts-mode-inferior-php-buffer)
@@ -1796,7 +1868,7 @@ php-ts-mode-inferior--write-history
   ;; Depending on how the process is killed the buffer may not be
   ;; around anymore; e.g. `kill-buffer'.
   (when-let* ((buffer (process-buffer process))
-              ((buffer-live-p (process-buffer process))))
+	      ((buffer-live-p (process-buffer process))))
     (with-current-buffer buffer (comint-write-input-ring))))
 
 (defun php-ts-mode-send-region (beg end)
@@ -1804,12 +1876,12 @@ php-ts-mode-send-region
   (interactive "r")
   (if (buffer-live-p php-ts-mode--inferior-php-process)
       (progn
-        (php-ts-mode-show-process-buffer)
-        (comint-send-string php-ts-mode--inferior-php-process "\n")
-        (comint-send-string
-         php-ts-mode--inferior-php-process
-         (buffer-substring-no-properties beg end))
-        (comint-send-string php-ts-mode--inferior-php-process "\n"))
+	(php-ts-mode-show-process-buffer)
+	(comint-send-string php-ts-mode--inferior-php-process "\n")
+	(comint-send-string
+	 php-ts-mode--inferior-php-process
+	 (buffer-substring-no-properties beg end))
+	(comint-send-string php-ts-mode--inferior-php-process "\n"))
     (message "Invoke run-php first!")))
 
 (defun php-ts-mode-send-buffer ()
diff --git a/lisp/textmodes/mhtml-ts-mode.el b/lisp/textmodes/mhtml-ts-mode.el
index 6b261205409..66bb27ced10 100644
--- a/lisp/textmodes/mhtml-ts-mode.el
+++ b/lisp/textmodes/mhtml-ts-mode.el
@@ -333,7 +333,8 @@ mhtml-ts-mode--treesit-defun-type-regexp
                 "function_declaration"
                 "lexical_declaration"
                 "element"
-                "rule_set"))
+                "rule_set"
+		"keyframe_block"))
   "Settings for `treesit-defun-type-regexp'.")
 
 ;; In order to support `prettify-symbols-mode', just `append' the prettify
@@ -348,13 +349,11 @@ mhtml-ts-mode--prettify-symbols-alist
 (defun mhtml-ts-mode--defun-name (node)
   "Return the defun name of NODE.
 Return nil if there is no name or if NODE is not a defun node."
-  (let ((html-name (html-ts-mode--defun-name node))
-        (js-name (js--treesit-defun-name node))
-        (css-name (css--treesit-defun-name node)))
+  (let ((lang (treesit-node-language node)))
     (cond
-     (html-name html-name)
-     (js-name js-name)
-     (css-name css-name))))
+     ((eq lang 'html) (html-ts-mode--defun-name node))
+     ((eq lang 'javascript) (js--treesit-defun-name node))
+     ((eq lang 'css) (css--treesit-defun-name node)))))
 
 (defvar-local mhtml-ts-mode--comment-current-lang nil)
 
@@ -432,6 +431,41 @@ mhtml-ts-mode-flymake-mhtml
         (process-send-region mhtml-ts-mode--flymake-process (point-min) (point-max))
         (process-send-eof mhtml-ts-mode--flymake-process)))))
 
+(defvar mhtml-ts-mode--range-settings
+  (append
+   (treesit-range-rules
+    :embed 'javascript
+    :host 'html
+    '((script_element
+       (start_tag (tag_name))
+       (raw_text) @cap))
+
+    ;; Another rule could be added that when it matches an
+    ;; attribute_value that has as its parent an
+    ;; attribute_name "style" it captures it and then
+    ;; passes it to the css parser.
+    :embed 'css
+    :host 'html
+    '((style_element
+       (start_tag (tag_name))
+       (raw_text) @cap)))
+
+   ;; jsdoc is not mandatory for js-ts-mode, so we respect this by
+   ;; adding jsdoc range rules only when jsdoc is available.
+   (when (treesit-ensure-installed 'jsdoc)
+     (treesit-range-rules
+      :embed 'jsdoc
+      :host 'javascript
+      :local t
+      `(((comment) @cap
+         (:match ,js--treesit-jsdoc-beginning-regexp @cap)))))))
+
+(defvar mhtml-ts-mode--treesit-aggregated-outline-predicate
+  `((html . ,#'html-ts-mode--outline-predicate)
+    (javascript . ,js-ts-mode--outline-predicate)
+    (css . ,css-ts-mode--outline-predicate))
+  "Settings for `treesit-aggregated-outline-predicate'.")
+
 ;;;###autoload
 (define-derived-mode mhtml-ts-mode html-ts-mode
   '("HTML+" (:eval (let ((lang (treesit-language-at (point))))
@@ -475,35 +509,11 @@ mhtml-ts-mode
     ;; Multi-language modes must set the  primary parser.
     (setq-local treesit-primary-parser (treesit-parser-create 'html))
 
-    (setq-local treesit-range-settings
-                (treesit-range-rules
-                 :embed 'javascript
-                 :host 'html
-                 '((script_element
-                    (start_tag (tag_name))
-                    (raw_text) @cap))
-
-                 ;; Another rule could be added that when it matches an
-                 ;; attribute_value that has as its parent an
-                 ;; attribute_name "style" it captures it and then
-                 ;; passes it to the css parser.
-                 :embed 'css
-                 :host 'html
-                 '((style_element
-                    (start_tag (tag_name))
-                    (raw_text) @cap))))
+    (setq-local treesit-range-settings mhtml-ts-mode--range-settings)
 
     ;; jsdoc is not mandatory for js-ts-mode, so we respect this by
     ;; adding jsdoc range rules only when jsdoc is available.
     (when (treesit-ensure-installed 'jsdoc)
-      (setq-local treesit-range-settings
-                  (append treesit-range-settings
-                          (treesit-range-rules
-                           :embed 'jsdoc
-                           :host 'javascript
-                           :local t
-                           `(((comment) @cap
-                              (:match ,js--treesit-jsdoc-beginning-regexp @cap))))))
       (setq-local c-ts-common--comment-regexp
                   js--treesit-jsdoc-comment-regexp))
 
@@ -563,9 +573,7 @@ mhtml-ts-mode
                 mhtml-ts-mode--treesit-aggregated-simple-imenu-settings)
 
     (setq-local treesit-aggregated-outline-predicate
-                `((html . ,#'html-ts-mode--outline-predicate)
-                  (javascript . ,js-ts-mode--outline-predicate)
-                  (css . ,css-ts-mode--outline-predicate)))
+		mhtml-ts-mode--treesit-aggregated-outline-predicate)
 
     (treesit-major-mode-setup)
 
-- 
2.50.1