aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorVincenzo Pupillo2025-07-10 17:20:34 +0200
committerEli Zaretskii2025-08-09 15:57:04 +0300
commite1fd7b5d89ec7f2df40fa01bf4041c0d1df147b4 (patch)
tree80b3644ccfaacc1b0c5c11eb1b350432d0099f87
parent3fb8ffe61eacb73df17f2a4a6c2ac240951a289b (diff)
downloademacs-e1fd7b5d89ec7f2df40fa01bf4041c0d1df147b4.tar.gz
emacs-e1fd7b5d89ec7f2df40fa01bf4041c0d1df147b4.zip
`php-ts-mode' depends on `mhtml-ts-mode' instead of JS,CSS and HTML
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 the rationale for those changes, the new command, and the new 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 dependencies. (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 fixes. (Bug#78994)
-rw-r--r--etc/NEWS34
-rw-r--r--lisp/progmodes/php-ts-mode.el1622
-rw-r--r--lisp/textmodes/mhtml-ts-mode.el78
3 files changed, 927 insertions, 807 deletions
diff --git a/etc/NEWS b/etc/NEWS
index d6bf37f3eb5..d541d805d6b 100644
--- a/etc/NEWS
+++ b/etc/NEWS
@@ -932,6 +932,12 @@ option controls how much is indented for method chaining.
932** PHP-ts mode 932** PHP-ts mode
933 933
934--- 934---
935*** 'php-ts-mode' now depends on 'mhtml-ts-mode'.
936The direct dependency on js-ts-mode, css-ts-mode and html-ts-mode has
937now been replaced by ‘mhtml-ts-mode’. Navigation, Outline and Imenu
938work for all languages and code maintenance is easier.
939
940---
935*** 'php-ts-mode-run-php-webserver' can now accept a custom "php.ini" file. 941*** 'php-ts-mode-run-php-webserver' can now accept a custom "php.ini" file.
936You can use the new optional argument CONFIG when calling 942You can use the new optional argument CONFIG when calling
937'php-ts-mode-run-php-webserver' to pass an alternative "php.ini" file to 943'php-ts-mode-run-php-webserver' to pass an alternative "php.ini" file to
@@ -939,6 +945,34 @@ the built-in Web server. Interactively, when invoked with a prefix
939argument, 'php-ts-mode-run-php-webserver' prompts for the config file as 945argument, 'php-ts-mode-run-php-webserver' prompts for the config file as
940well as for other connection parameters. 946well as for other connection parameters.
941 947
948---
949*** The option 'php-ts-mode-css-fontify-colors' has been removed.
950'mhtml-ts-mode-css-fontify-colors' replaces this option.
951
952---
953*** New user option 'php-ts-mode-html-relative-indent'.
954In buffers containing both PHP and HTML, this option allows you to
955define how the PHP code should be indented relative to the position of
956the HTML tags.
957
958---
959*** New user option 'php-ts-mode-html-indent-offset'.
960Offset of PHP code block relative to HTML tags.
961
962---
963*** New user option 'php-ts-mode-find-sibling-rules'.
964Rules for finding siblings of a PHP file.
965
966---
967*** New user option 'php-ts-mode-phpdoc-highlight-errors'.
968When non nil, it highlights unknown PHPDOC tags using
969'font-lock-warning-face' so that the user can identify them more easily.
970
971---
972*** New command 'php-ts-mode-show-ini'.
973Show the location of the PHP ini files, if the buffer is associated to a remote
974PHP file show the remote PHP ini files.
975
942** Rust-ts mode 976** Rust-ts mode
943 977
944--- 978---
diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el
index a01e4d66fba..d45226ad5e9 100644
--- a/lisp/progmodes/php-ts-mode.el
+++ b/lisp/progmodes/php-ts-mode.el
@@ -25,12 +25,12 @@
25;;; Tree-sitter language versions 25;;; Tree-sitter language versions
26;; 26;;
27;; php-ts-mode has been tested with the following grammars and version: 27;; php-ts-mode has been tested with the following grammars and version:
28;; - tree-sitter-phpdoc: v0.1.5 28;; - tree-sitter-phpdoc: v0.1.6
29;; - tree-sitter-css: v0.23.1-1-g6a442a3 29;; - tree-sitter-css: v0.23.1-1-g6a442a3
30;; - tree-sitter-jsdoc: v0.23.2 30;; - tree-sitter-jsdoc: v0.23.2
31;; - tree-sitter-javascript: v0.23.1-2-g108b2d4 31;; - tree-sitter-javascript: v0.23.1-2-g108b2d4
32;; - tree-sitter-html: v0.23.2-1-gd9219ad 32;; - tree-sitter-html: v0.23.2-1-gd9219ad
33;; - tree-sitter-php: v0.23.11 33;; - tree-sitter-php: v0.23.12
34;; 34;;
35;; We try our best to make builtin modes work with latest grammar 35;; We try our best to make builtin modes work with latest grammar
36;; versions, so a more recent grammar has a good chance to work too. 36;; versions, so a more recent grammar has a good chance to work too.
@@ -42,7 +42,7 @@
42;; for editing PHP files with embedded HTML, JavaScript, CSS and phpdoc. 42;; for editing PHP files with embedded HTML, JavaScript, CSS and phpdoc.
43;; Tree Sitter is used to parse each of these languages. 43;; Tree Sitter is used to parse each of these languages.
44;; 44;;
45;; Please note that this package requires `html-ts-mode', which 45;; Please note that this package requires `mhtml-ts-mode', which
46;; registers itself as the major mode for editing HTML. 46;; registers itself as the major mode for editing HTML.
47;; 47;;
48;; This package is compatible and has been tested with the following 48;; This package is compatible and has been tested with the following
@@ -69,9 +69,7 @@
69 69
70(require 'treesit) 70(require 'treesit)
71(require 'c-ts-common) ;; For comment indent and filling. 71(require 'c-ts-common) ;; For comment indent and filling.
72(require 'html-ts-mode) ;; for embed html 72(require 'mhtml-ts-mode) ;; For embed html, css and js.
73(require 'css-mode) ;; for embed css into html
74(require 'js) ;; for embed javascript into html
75(require 'comint) 73(require 'comint)
76(treesit-declare-unavailable-functions) 74(treesit-declare-unavailable-functions)
77 75
@@ -83,10 +81,10 @@
83;;; Install treesitter language parsers 81;;; Install treesitter language parsers
84(defvar php-ts-mode--language-source-alist 82(defvar php-ts-mode--language-source-alist
85 '((php "https://github.com/tree-sitter/tree-sitter-php" 83 '((php "https://github.com/tree-sitter/tree-sitter-php"
86 :commit "43aad2b9a98aa8e603ea0cf5bb630728a5591ad8" 84 :commit "f7cf7348737d8cff1b13407a0bfedce02ee7b046"
87 :source-dir "php/src") 85 :source-dir "php/src")
88 (phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc" 86 (phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc"
89 :commit "fe3202e468bc17332bec8969f2b50ff1f1da3a46")) 87 :commit "03bb10330704b0b371b044e937d5cc7cd40b4999"))
90 "Treesitter language parsers required by `php-ts-mode'. 88 "Treesitter language parsers required by `php-ts-mode'.
91You can customize `treesit-language-source-alist' if you want 89You can customize `treesit-language-source-alist' if you want
92to stick to a specific commit and/or use different parsers.") 90to stick to a specific commit and/or use different parsers.")
@@ -97,9 +95,10 @@ to stick to a specific commit and/or use different parsers.")
97(defun php-ts-mode-install-parsers () 95(defun php-ts-mode-install-parsers ()
98 "Install all the required treesitter parsers. 96 "Install all the required treesitter parsers.
99`treesit-language-source-alist' defines which parsers to install. 97`treesit-language-source-alist' defines which parsers to install.
100It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"." 98It's pre-filled by loading \"mhtml-ts-mode\"."
101 (interactive) 99 (interactive)
102 (dolist (lang '(php phpdoc html css javascript jsdoc)) 100 (mhtml-ts-mode-install-parsers)
101 (dolist (lang '(php phpdoc))
103 (treesit-install-language-grammar lang))) 102 (treesit-install-language-grammar lang)))
104 103
105;;; Custom variables 104;;; Custom variables
@@ -118,25 +117,63 @@ It's pre-filled by loading \"html-ts-mode\", \"css-mode\", \"js\"."
118 117
119(defcustom php-ts-mode-js-css-indent-offset 2 118(defcustom php-ts-mode-js-css-indent-offset 2
120 "JavaScript and CSS indent spaces related to the <script> and <style> HTML tags. 119 "JavaScript and CSS indent spaces related to the <script> and <style> HTML tags.
121By default should have same value as `html-ts-mode-indent-offset'." 120It is advisable to have this have the same value as
121`mhtml-ts-mode-js-css-indent-offset'."
122 :tag "PHP javascript or css indent offset" 122 :tag "PHP javascript or css indent offset"
123 :version "30.1" 123 :version "30.1"
124 :type 'integer 124 :type 'integer
125 :safe 'integerp) 125 :safe 'integerp)
126 126
127(defcustom php-ts-mode-css-fontify-colors t 127(defcustom php-ts-mode-html-indent-offset 2
128 "Whether CSS colors should be fontified using the color as the background. 128 "The number of spaces to indent PHP code relative to HTML tags.
129If non-nil, text representing a CSS color will be fontified 129It is advisable to have this have the same value as
130such that its background is the color itself. 130`mhtml-ts-mode-js-css-indent-offset'."
131Works like `css--fontify-region'." 131 :tag "PHP indent offset relative to HTML"
132 :tag "PHP colors the CSS properties values." 132 :version "31.1"
133 :version "30.1" 133 :type 'integer
134 :type 'boolean 134 :safe 'integerp)
135 :safe 'booleanp)
136 135
137(defcustom php-ts-mode-php-default-executable (or (executable-find "php") "/usr/bin/php") 136(defcustom php-ts-mode-html-relative-indent t
138 "The default PHP executable." 137 "How PHP code is indented relative to the HTML tags.
139 :tag "PHP Executable" 138
139When t, the default, indentation looks like:
140
141 <html>
142 ...
143 <div>
144 <?php
145 ?>
146 </div>
147
148When nil, indentation of the tag body starts just below the
149tag, like:
150
151 <html>
152 ...
153 <div>
154 <?php
155 ?>
156 </div>
157
158When `ignore', the tag body starts in the first column, like:
159
160 <html>
161 ...
162 <div>
163 <?php
164 ?>
165 </div>"
166 :type '(choice
167 (const :tag "Indent starts just below the HTML tag" nil)
168 (const :tag "Indent relative to the HTML tag" t)
169 (const :tag "Always in the first column" ignore))
170 :safe 'symbolp
171 :tag "PHP indent relative to HTML"
172 :version "31.1")
173
174(defcustom php-ts-mode-php-default-executable (or (executable-find "php") "php")
175 "The default file name of the PHP executable."
176 :tag "PHP executable"
140 :version "30.1" 177 :version "30.1"
141 :type 'file) 178 :type 'file)
142 179
@@ -148,7 +185,7 @@ If non-nil, `php-ts-mode--executable' looks for this instead of \"php\".")
148 "The location of php.ini file. 185 "The location of php.ini file.
149If nil the default one is used to run the embedded webserver or 186If nil the default one is used to run the embedded webserver or
150inferior PHP process." 187inferior PHP process."
151 :tag "PHP Init file" 188 :tag "PHP init file"
152 :version "30.1" 189 :version "30.1"
153 :type '(choice (const :tag "Default init file" nil) file)) 190 :type '(choice (const :tag "Default init file" nil) file))
154 191
@@ -198,6 +235,29 @@ Useful for testing code against multiple simultaneous requests."
198 :type '(choice (const :tag "None" nil) file) 235 :type '(choice (const :tag "None" nil) file)
199 :safe 'string-or-null-p) 236 :safe 'string-or-null-p)
200 237
238(defcustom php-ts-mode-find-sibling-rules
239 (list (list (rx "src/" (group (+ not-newline) "/") (group (+ (not "/"))) ".php") "tests/\\1\\2Test.php")
240 (list (rx "tests/" (group (+ not-newline) "/") (group (+ (not "/"))) "Test.php") "src/\\1\\2.php"))
241 "Rules for finding sibling files. See `find-sibling-rules' for the
242 form of the value.
243As a default, the rules try to find the corresponding test of the
244current source file and vice versa. Source files are assumed to be in
245src/, and tests of in tests/. Many frameworks have a folder
246arrangement similar to this."
247 :type '(alist :key-type (regexp :tag "Match")
248 :value-type (repeat (string :tag "Expansion")))
249 :tag "PHP find sibling rules"
250 :version "31.1")
251
252(defcustom php-ts-mode-phpdoc-highlight-errors nil
253 "Highlight tags unknown to the phpdoc parser.
254If nil try to highlight even incorrect nodes as if they were correct,
255otherwise show phpdoc error using a `font-lock-warning-face'."
256 :tag "PHPDOC face for unknown tags or errors."
257 :version "31.1"
258 :type 'boolean
259 :safe 'booleanp)
260
201(defvar php-ts-mode--inferior-prompt "php >" 261(defvar php-ts-mode--inferior-prompt "php >"
202 "Prompt used by PHP inferior process.") 262 "Prompt used by PHP inferior process.")
203 263
@@ -210,9 +270,9 @@ SYM should be `php-ts-mode-indent-style', and VAL should be a style
210symbol." 270symbol."
211 (set-default sym val) 271 (set-default sym val)
212 (dolist (buffer (buffer-list)) 272 (dolist (buffer (buffer-list))
213 (with-current-buffer buffer 273 (with-current-buffer buffer
214 (when (derived-mode-p 'php-ts-mode) 274 (when (derived-mode-p 'php-ts-mode)
215 (php-ts-mode-set-style val))))) 275 (php-ts-mode-set-style val)))))
216 276
217;; taken from c-ts-mode 277;; taken from c-ts-mode
218(defun php-ts-indent-style-safep (style) 278(defun php-ts-indent-style-safep (style)
@@ -235,12 +295,12 @@ follows the form of `treesit-simple-indent-rules'."
235 :tag "PHP indent style" 295 :tag "PHP indent style"
236 :version "30.1" 296 :version "30.1"
237 :type '(choice (const :tag "PSR-2/PSR-12" psr2) 297 :type '(choice (const :tag "PSR-2/PSR-12" psr2)
238 (const :tag "PEAR" pear) 298 (const :tag "PEAR" pear)
239 (const :tag "Drupal" drupal) 299 (const :tag "Drupal" drupal)
240 (const :tag "WordPress" wordpress) 300 (const :tag "WordPress" wordpress)
241 (const :tag "Symfony" symfony) 301 (const :tag "Symfony" symfony)
242 (const :tag "Zend" zend) 302 (const :tag "Zend" zend)
243 (function :tag "A function for user customized style" ignore)) 303 (function :tag "A function for user customized style" ignore))
244 :set #'php-ts-mode--indent-style-setter 304 :set #'php-ts-mode--indent-style-setter
245 :safe #'php-ts-indent-style-safep) 305 :safe #'php-ts-indent-style-safep)
246 306
@@ -258,52 +318,52 @@ Calls REPORT-FN directly."
258 (when (process-live-p php-ts-mode--flymake-process) 318 (when (process-live-p php-ts-mode--flymake-process)
259 (kill-process php-ts-mode--flymake-process)) 319 (kill-process php-ts-mode--flymake-process))
260 (let ((source (current-buffer)) 320 (let ((source (current-buffer))
261 (diagnostics-pattern (eval-when-compile 321 (diagnostics-pattern (eval-when-compile
262 (rx bol (? "PHP ") ;; every diagnostic line start with PHP 322 (rx bol (? "PHP ") ;; every diagnostic line start with PHP
263 (group (or "Fatal" "Parse")) ;; 1: type 323 (group (or "Fatal" "Parse")) ;; 1: type
264 " error:" (+ (syntax whitespace)) 324 " error:" (+ (syntax whitespace))
265 (group (+? nonl)) ;; 2: msg 325 (group (+? nonl)) ;; 2: msg
266 " in " (group (+? nonl)) ;; 3: file 326 " in " (group (+? nonl)) ;; 3: file
267 " on line " (group (+ num)) ;; 4: line 327 " on line " (group (+ num)) ;; 4: line
268 eol)))) 328 eol))))
269 (save-restriction 329 (save-restriction
270 (widen) 330 (widen)
271 (setq php-ts-mode--flymake-process 331 (setq php-ts-mode--flymake-process
272 (make-process 332 (make-process
273 :name "php-ts-mode-flymake" 333 :name "php-ts-mode-flymake"
274 :noquery t 334 :noquery t
275 :connection-type 'pipe 335 :connection-type 'pipe
276 :buffer (generate-new-buffer " *php-ts-mode-flymake*") 336 :buffer (generate-new-buffer " *php-ts-mode-flymake*")
277 :command `(,(php-ts-mode--executable) 337 :command `(,(php-ts-mode--executable)
278 "-l" "-d" "display_errors=0") 338 "-l" "-d" "display_errors=0")
279 :sentinel 339 :sentinel
280 (lambda (proc _event) 340 (lambda (proc _event)
281 (when (eq 'exit (process-status proc)) 341 (when (eq 'exit (process-status proc))
282 (unwind-protect 342 (unwind-protect
283 (if (with-current-buffer source 343 (if (with-current-buffer source
284 (eq proc php-ts-mode--flymake-process)) 344 (eq proc php-ts-mode--flymake-process))
285 (with-current-buffer (process-buffer proc) 345 (with-current-buffer (process-buffer proc)
286 (goto-char (point-min)) 346 (goto-char (point-min))
287 (let (diags) 347 (let (diags)
288 (while (search-forward-regexp 348 (while (search-forward-regexp
289 diagnostics-pattern 349 diagnostics-pattern
290 nil t) 350 nil t)
291 (let* ((beg 351 (let* ((beg
292 (car (flymake-diag-region 352 (car (flymake-diag-region
293 source 353 source
294 (string-to-number (match-string 4))))) 354 (string-to-number (match-string 4)))))
295 (end 355 (end
296 (cdr (flymake-diag-region 356 (cdr (flymake-diag-region
297 source 357 source
298 (string-to-number (match-string 4))))) 358 (string-to-number (match-string 4)))))
299 (msg (match-string 2)) 359 (msg (match-string 2))
300 (type :error)) 360 (type :error))
301 (push (flymake-make-diagnostic 361 (push (flymake-make-diagnostic
302 source beg end type msg) 362 source beg end type msg)
303 diags))) 363 diags)))
304 (funcall report-fn diags))) 364 (funcall report-fn diags)))
305 (flymake-log :warning "Canceling obsolete check %s" proc)) 365 (flymake-log :warning "Canceling obsolete check %s" proc))
306 (kill-buffer (process-buffer proc))))))) 366 (kill-buffer (process-buffer proc)))))))
307 (process-send-region php-ts-mode--flymake-process (point-min) (point-max)) 367 (process-send-region php-ts-mode--flymake-process (point-min) (point-max))
308 (process-send-eof php-ts-mode--flymake-process)))) 368 (process-send-eof php-ts-mode--flymake-process))))
309 369
@@ -317,23 +377,55 @@ it searches locally. If `php-ts-mode-alternative-php-program-name' is
317non-nil, it searches for this program instead of the usual `php'. 377non-nil, it searches for this program instead of the usual `php'.
318If the search fails, it returns `php-ts-mode-php-default-executable'." 378If the search fails, it returns `php-ts-mode-php-default-executable'."
319 (or (executable-find 379 (or (executable-find
320 (or php-ts-mode-alternative-php-program-name "php") t) 380 (or php-ts-mode-alternative-php-program-name "php") t)
321 php-ts-mode-php-default-executable)) 381 php-ts-mode-php-default-executable))
382
383(defun php-ts-mode-show-ini ()
384 "Pop a buffer showing the PHP configuration file names."
385 (interactive)
386 (let ((buffer (get-buffer-create "*PHP ini*"))
387 (button-action (lambda (_) (find-file-at-point)))
388 (remote (file-remote-p default-directory)))
389 (with-current-buffer buffer
390 (view-mode -1)
391 (erase-buffer)
392 (shell-command (concat (php-ts-mode--executable) " --ini") buffer)
393 ;; Remove useless ',' at end of some rows.
394 (while (re-search-forward "," nil t)
395 (replace-match "" nil nil))
396 (goto-char (point-min))
397 (while (re-search-forward
398 "[a-zA-Z]?[:]?[/\\].+"
399 nil t)
400 (let ((start (match-beginning 0))
401 (end (match-end 0)))
402 ;; If PHP is on a remote machine, the path must be preceded
403 ;; by the remote identifier.
404 (when remote
405 (goto-char start)
406 (insert remote)
407 (setq end (+ end (length remote)))
408 (goto-char end))
409 (make-button start end 'action button-action)))
410 (goto-char (point-min))
411 (view-mode +1))
412 (when buffer
413 (pop-to-buffer buffer))))
322 414
323(defun php-ts-mode--get-indent-style () 415(defun php-ts-mode--get-indent-style ()
324 "Helper function to set indentation style. 416 "Helper function to set indentation style.
325MODE can be `psr2', `pear', `drupal', `wordpress', `symfony', `zend'." 417MODE can be `psr2', `pear', `drupal', `wordpress', `symfony', `zend'."
326 (let ((style 418 (let ((style
327 (if (functionp php-ts-mode-indent-style) 419 (if (functionp php-ts-mode-indent-style)
328 (funcall php-ts-mode-indent-style) 420 (funcall php-ts-mode-indent-style)
329 (cl-case php-ts-mode-indent-style 421 (cl-case php-ts-mode-indent-style
330 (psr2 (alist-get 'psr2 (php-ts-mode--indent-styles))) 422 (psr2 (alist-get 'psr2 (php-ts-mode--indent-styles)))
331 (pear (alist-get 'pear (php-ts-mode--indent-styles))) 423 (pear (alist-get 'pear (php-ts-mode--indent-styles)))
332 (drupal (alist-get 'drupal (php-ts-mode--indent-styles))) 424 (drupal (alist-get 'drupal (php-ts-mode--indent-styles)))
333 (wordpress (alist-get 'wordpress (php-ts-mode--indent-styles))) 425 (wordpress (alist-get 'wordpress (php-ts-mode--indent-styles)))
334 (symfony (alist-get 'symfony (php-ts-mode--indent-styles))) 426 (symfony (alist-get 'symfony (php-ts-mode--indent-styles)))
335 (zend (alist-get 'zend (php-ts-mode--indent-styles))) 427 (zend (alist-get 'zend (php-ts-mode--indent-styles)))
336 (t (alist-get 'psr2 (php-ts-mode--indent-styles))))))) 428 (t (alist-get 'psr2 (php-ts-mode--indent-styles)))))))
337 `((php ,@style)))) 429 `((php ,@style))))
338 430
339(defun php-ts-mode--prompt-for-style () 431(defun php-ts-mode--prompt-for-style ()
@@ -357,23 +449,23 @@ in this Emacs session."
357 "Set the offset, tab, etc. according to STYLE." 449 "Set the offset, tab, etc. according to STYLE."
358 (cl-case style 450 (cl-case style
359 (psr2 (setq php-ts-mode-indent-offset 4 451 (psr2 (setq php-ts-mode-indent-offset 4
360 tab-width 4 452 tab-width 4
361 indent-tabs-mode nil)) 453 indent-tabs-mode nil))
362 (pear (setq php-ts-mode-indent-offset 4 454 (pear (setq php-ts-mode-indent-offset 4
363 tab-width 4 455 tab-width 4
364 indent-tabs-mode nil)) 456 indent-tabs-mode nil))
365 (drupal (setq php-ts-mode-indent-offset 2 457 (drupal (setq php-ts-mode-indent-offset 2
366 tab-width 2 458 tab-width 2
367 indent-tabs-mode nil)) 459 indent-tabs-mode nil))
368 (wordpress (setq php-ts-mode-indent-offset 4 460 (wordpress (setq php-ts-mode-indent-offset 4
369 tab-width 4 461 tab-width 4
370 indent-tabs-mode t)) 462 indent-tabs-mode t))
371 (symfony (setq php-ts-mode-indent-offset 4 463 (symfony (setq php-ts-mode-indent-offset 4
372 tab-width 4 464 tab-width 4
373 indent-tabs-mode nil)) 465 indent-tabs-mode nil))
374 (zend (setq php-ts-mode-indent-offset 4 466 (zend (setq php-ts-mode-indent-offset 4
375 tab-width 4 467 tab-width 4
376 indent-tabs-mode nil)))) 468 indent-tabs-mode nil))))
377 469
378(defun php-ts-mode-set-style (style) 470(defun php-ts-mode-set-style (style)
379 "Set the PHP indent style of the current buffer to STYLE. 471 "Set the PHP indent style of the current buffer to STYLE.
@@ -386,13 +478,13 @@ To set the default indent style globally, use
386 ((equal php-ts-mode-indent-style style) 478 ((equal php-ts-mode-indent-style style)
387 (message "The style is already %s" style));; nothing to do 479 (message "The style is already %s" style));; nothing to do
388 (t (progn 480 (t (progn
389 (setq-local php-ts-mode-indent-style style) 481 (setq-local php-ts-mode-indent-style style)
390 (php-ts-mode--set-indent-property style) 482 (php-ts-mode--set-indent-property style)
391 (let ((rules (assq-delete-all 'php treesit-simple-indent-rules)) 483 (let ((rules (assq-delete-all 'php treesit-simple-indent-rules))
392 (new-style (car (treesit--indent-rules-optimize 484 (new-style (car (treesit--indent-rules-optimize
393 (php-ts-mode--get-indent-style))))) 485 (php-ts-mode--get-indent-style)))))
394 (setq treesit-simple-indent-rules (cons new-style rules)) 486 (setq treesit-simple-indent-rules (cons new-style rules))
395 (message "Switch to %s style" style)))))) 487 (message "Switch to %s style" style))))))
396 488
397(defun php-ts-mode--get-parser-ranges () 489(defun php-ts-mode--get-parser-ranges ()
398 "Return the ranges covered by the parsers. 490 "Return the ranges covered by the parsers.
@@ -401,9 +493,9 @@ To set the default indent style globally, use
401current buffer, the ranges covered by each parser. 493current buffer, the ranges covered by each parser.
402Useful for debugging." 494Useful for debugging."
403 (let ((ranges) 495 (let ((ranges)
404 (parsers (treesit-parser-list nil nil t))) 496 (parsers (treesit-parser-list nil nil t)))
405 (if (not parsers) 497 (if (not parsers)
406 (message "At least one parser must be initialized")) 498 (message "At least one parser must be initialized"))
407 (cl-loop 499 (cl-loop
408 for parser in parsers 500 for parser in parsers
409 do (push (list parser (treesit-parser-included-ranges parser)) ranges) 501 do (push (list parser (treesit-parser-included-ranges parser)) ranges)
@@ -442,27 +534,6 @@ Useful for debugging."
442 534
443;;; Indent 535;;; Indent
444 536
445(defconst php-ts-mode--possibly-braceless-keyword-re
446 (regexp-opt '("if" "for" "foreach" "while" "do") 'symbols)
447 "Regexp matching keywords optionally followed by an opening brace.")
448
449(defun php-ts-mode--open-statement-group-heuristic (node _parent bol &rest _)
450 "Heuristic matcher for statement-group without closing bracket.
451
452Return `php-ts-mode-indent-offset' plus 1 when BOL is after
453`php-ts-mode--possibly-braceless-keyword-re', otherwise return 0. It's
454useful for matching incomplete compound_statement or colon_block.
455PARENT is NODE's parent, BOL is the beginning of non-whitespace
456characters of the current line."
457 (and (null node)
458 (save-excursion
459 (forward-line -1)
460 (if (re-search-forward
461 php-ts-mode--possibly-braceless-keyword-re
462 bol t)
463 (+ 1 php-ts-mode-indent-offset)
464 0))))
465
466;; taken from c-ts-mode 537;; taken from c-ts-mode
467(defun php-ts-mode--else-heuristic (node parent bol &rest _) 538(defun php-ts-mode--else-heuristic (node parent bol &rest _)
468 "Heuristic matcher for when \"else\" is followed by a closing bracket. 539 "Heuristic matcher for when \"else\" is followed by a closing bracket.
@@ -471,10 +542,10 @@ PARENT is NODE's parent, BOL is the beginning of non-whitespace
471characters of the current line." 542characters of the current line."
472 (and (null node) 543 (and (null node)
473 (save-excursion 544 (save-excursion
474 (forward-line -1) 545 (forward-line -1)
475 (looking-at (rx (* whitespace) "else" (* whitespace) eol))) 546 (looking-at (rx (* whitespace) "else" (* whitespace) eol)))
476 (let ((next-node (treesit-node-first-child-for-pos parent bol))) 547 (let ((next-node (treesit-node-first-child-for-pos parent bol)))
477 (equal (treesit-node-type next-node) "}")))) 548 (equal (treesit-node-type next-node) "}"))))
478 549
479;; taken from c-ts-mode 550;; taken from c-ts-mode
480(defun php-ts-mode--first-sibling (node parent &rest _) 551(defun php-ts-mode--first-sibling (node parent &rest _)
@@ -485,26 +556,11 @@ such that it's on its own line. NODE is the node to match and
485PARENT is its parent." 556PARENT is its parent."
486 (let ((prev-sibling (treesit-node-prev-sibling node t))) 557 (let ((prev-sibling (treesit-node-prev-sibling node t)))
487 (or (null prev-sibling) 558 (or (null prev-sibling)
488 (save-excursion
489 (goto-char (treesit-node-start prev-sibling))
490 (<= (line-beginning-position)
491 (treesit-node-start parent)
492 (line-end-position))))))
493
494(defun php-ts-mode--js-css-tag-bol (_node parent &rest _)
495 "Find the first non-space characters of html tags <script> or <style>.
496
497Return `line-beginning-position' when `treesit-node-at' is HTML or PHP.
498Otherwise go to the PARENT and search backward for <script> or <style> tags.
499Should be used only for Javascript or CSS indenting rules.
500NODE, ignored, is the node to match and PARENT is its parent."
501 (let ((lang (treesit-language-at (point))))
502 (if (or (eq lang 'javascript)
503 (eq lang 'css))
504 (save-excursion 559 (save-excursion
505 (goto-char (treesit-node-start parent)) 560 (goto-char (treesit-node-start prev-sibling))
506 (re-search-backward "<script.*>\\|<style.*>" nil t)) 561 (<= (line-beginning-position)
507 (line-beginning-position)))) 562 (treesit-node-start parent)
563 (line-end-position))))))
508 564
509(defun php-ts-mode--parent-eol (_node parent &rest _) 565(defun php-ts-mode--parent-eol (_node parent &rest _)
510 "Find the last non-space characters of the PARENT of the current NODE. 566 "Find the last non-space characters of the PARENT of the current NODE.
@@ -514,50 +570,33 @@ NODE is the node to match and PARENT is its parent."
514 (goto-char (treesit-node-start parent)) 570 (goto-char (treesit-node-start parent))
515 (line-end-position))) 571 (line-end-position)))
516 572
517(defun php-ts-mode--parent-html-bol (node parent bol &rest _)
518 "Find the first non-space characters of the HTML tags before NODE.
519
520When NODE is nil call `php-ts-mode--open-statement-group-heuristic'.
521PARENT is NODE's parent, BOL is the beginning of non-whitespace
522characters of the current line."
523 (if (null node)
524 ;; If NODE is nil it could be an open statement-group.
525 (php-ts-mode--open-statement-group-heuristic node parent bol)
526 (save-excursion
527 (let ((html-node (treesit-search-forward node "text" t)))
528 (if html-node
529 (let ((end-html (treesit-node-end html-node)))
530 (goto-char end-html)
531 (backward-word)
532 (back-to-indentation)
533 (point))
534 (treesit-node-start parent))))))
535
536(defun php-ts-mode--parent-html-heuristic (node parent bol &rest _) 573(defun php-ts-mode--parent-html-heuristic (node parent bol &rest _)
537 "Return position based on html indentation. 574 "Return position based on html indentation.
538 575
539Returns 0 if the NODE is after the </html>, otherwise returns the 576Return 0 if the NODE is after the </html>. If
540indentation point of the last word before the NODE, plus the indentation 577`php-ts-mode-html-relative-indent' is not nil return the indentation
541offset. If there is no HTML tag, it returns the beginning of the 578point of the last word before the NODE, plus the indentation offset,
542parent. When NODE is nil call `php-ts-mode--open-statement-group-heuristic'. 579otherwise return only the indentation point. If there is no HTML tag,
543It can be used when you want to indent PHP code relative to the HTML. 580it returns the beginning of the parent. When NODE is nil it behaves
544PARENT is NODE's parent, BOL is the beginning of non-whitespace 581like \"prev-siblings\" of `treesit-simple-indent-presets'. It can be
545characters of the current line." 582used when you want to indent PHP code relative to the HTML. PARENT is
546 (if (null node) 583NODE's parent, BOL is the beginning of non-whitespace characters of
547 ;; If NODE is nil it could be an open statement-group. 584the current line."
548 (php-ts-mode--open-statement-group-heuristic node parent bol) 585 (save-excursion
549 (let ((html-node (treesit-search-forward node "text" t))) 586 (cond
550 (if html-node 587 ((eq php-ts-mode-html-relative-indent 'ignore) (line-beginning-position))
551 (let ((end-html (treesit-node-end html-node))) 588 ((search-backward "</html>" (treesit-node-start parent) t 1) (line-beginning-position))
552 (save-excursion 589 ((null node) (apply (alist-get 'prev-sibling treesit-simple-indent-presets) node parent bol nil))
553 (goto-char end-html) 590 (t (when-let* ((html-node (treesit-search-forward node "text" t))
554 (backward-word) 591 (end-html (treesit-node-end html-node)))
555 (back-to-indentation) 592 (goto-char end-html)
556 (if (search-forward "</html>" end-html t 1) 593 ;; go to the start of the last tag
557 0 594 ;; of the "text" node
558 (+ (point) php-ts-mode-indent-offset)))) 595 (backward-word)
559 ;; Maybe it's better to use bol? 596 (back-to-indentation)
560 (treesit-node-start parent))))) 597 (if php-ts-mode-html-relative-indent
598 (+ (point) php-ts-mode-html-indent-offset)
599 (point)))))))
561 600
562(defun php-ts-mode--array-element-heuristic (_node parent _bol &rest _) 601(defun php-ts-mode--array-element-heuristic (_node parent _bol &rest _)
563 "Return of the position of the first element of the array. 602 "Return of the position of the first element of the array.
@@ -565,21 +604,20 @@ characters of the current line."
565PARENT is NODE's parent, BOL is the beginning of non-whitespace 604PARENT is NODE's parent, BOL is the beginning of non-whitespace
566characters of the current line." 605characters of the current line."
567 (let ((parent-start 606 (let ((parent-start
568 (treesit-node-start parent)) 607 (treesit-node-start parent))
569 (parent-first-child-start 608 (parent-first-child-start
570 (treesit-node-start (treesit-node-child parent 2)))) 609 (treesit-node-start (treesit-node-child parent 2))))
571 (if (equal 610 (if (equal
572 (line-number-at-pos parent-start) 611 (line-number-at-pos parent-start)
573 (line-number-at-pos parent-first-child-start)) 612 (line-number-at-pos parent-first-child-start))
574 ;; if array_creation_expression and the first 613 ;; if array_creation_expression and the first
575 ;; array_element_initializer are on the same same line 614 ;; array_element_initializer are on the same same line
576 parent-first-child-start 615 parent-first-child-start
577 ;; else return parent-bol plus the offset 616 ;; else return parent-bol plus the offset
578 (save-excursion 617 (save-excursion
579 (goto-char (treesit-node-start parent)) 618 (goto-char (treesit-node-start parent))
580 (back-to-indentation) 619 (back-to-indentation)
581 (+ (point) php-ts-mode-indent-offset))))) 620 (+ (point) php-ts-mode-indent-offset)))))
582
583 621
584(defun php-ts-mode--anchor-first-sibling (_node parent _bol &rest _) 622(defun php-ts-mode--anchor-first-sibling (_node parent _bol &rest _)
585 "Return the start of the first child of a sibling of PARENT. 623 "Return the start of the first child of a sibling of PARENT.
@@ -590,14 +628,14 @@ sibling. Otherwise return the start of the first sibling.
590PARENT is NODE's parent, BOL is the beginning of non-whitespace 628PARENT is NODE's parent, BOL is the beginning of non-whitespace
591characters of the current line." 629characters of the current line."
592 (let ((first-sibling-start 630 (let ((first-sibling-start
593 (treesit-node-start (treesit-node-child parent 0))) 631 (treesit-node-start (treesit-node-child parent 0)))
594 (first-sibling-child-start 632 (first-sibling-child-start
595 (treesit-node-start (treesit-node-child parent 1)))) 633 (treesit-node-start (treesit-node-child parent 1))))
596 (if (equal 634 (if (equal
597 (line-number-at-pos first-sibling-start) 635 (line-number-at-pos first-sibling-start)
598 (line-number-at-pos first-sibling-child-start)) 636 (line-number-at-pos first-sibling-child-start))
599 ;; if are on the same line return the child start 637 ;; if are on the same line return the child start
600 first-sibling-child-start 638 first-sibling-child-start
601 first-sibling-start))) 639 first-sibling-start)))
602 640
603;; adapted from c-ts-mode--anchor-prev-sibling 641;; adapted from c-ts-mode--anchor-prev-sibling
@@ -610,150 +648,158 @@ doesn't have a child.
610PARENT is NODE's parent, BOL is the beginning of non-whitespace 648PARENT is NODE's parent, BOL is the beginning of non-whitespace
611characters of the current line." 649characters of the current line."
612 (when-let* ((prev-sibling 650 (when-let* ((prev-sibling
613 (or (treesit-node-prev-sibling node t) 651 (or (treesit-node-prev-sibling node t)
614 (treesit-node-prev-sibling 652 (treesit-node-prev-sibling
615 (treesit-node-first-child-for-pos parent bol) t) 653 (treesit-node-first-child-for-pos parent bol) t)
616 (treesit-node-child parent -1 t))) 654 (treesit-node-child parent -1 t)))
617 (continue t)) 655 (continue t))
618 (save-excursion 656 (save-excursion
619 (while (and prev-sibling continue) 657 (while (and prev-sibling continue)
620 (goto-char (treesit-node-start prev-sibling)) 658 (goto-char (treesit-node-start prev-sibling))
621 (if (looking-back (rx bol (* whitespace)) 659 (if (looking-back (rx bol (* whitespace))
622 (line-beginning-position)) 660 (line-beginning-position))
623 (setq continue nil) 661 (setq continue nil)
624 (setq prev-sibling 662 (setq prev-sibling
625 (treesit-node-prev-sibling prev-sibling))))) 663 (treesit-node-prev-sibling prev-sibling)))))
626 (treesit-node-start prev-sibling))) 664 (treesit-node-start prev-sibling)))
627 665
628(defun php-ts-mode--indent-styles () 666(defun php-ts-mode--indent-styles ()
629 "Indent rules supported by `php-ts-mode'." 667 "Indent rules supported by `php-ts-mode'."
630 (let ((common 668 (let ((common
631 `((php-ts-mode--else-heuristic prev-line php-ts-mode-indent-offset) 669 `(;; Handle indentation relatives to HTML.
632 670 ;; ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
633 ((query "(ERROR (ERROR)) @indent") column-0 0) 671 ;; ((parent-is "text_interpolation") php-ts-mode--parent-html-heuristic 0)
634 672 ((or (parent-is "program")
635 ((node-is ")") parent-bol 0) 673 (parent-is "text_interpolation"))
636 ((node-is "]") parent-bol 0) 674 php-ts-mode--parent-html-heuristic 0)
637 ((node-is "else_clause") parent-bol 0) 675
638 ((node-is "case_statement") parent-bol php-ts-mode-indent-offset) 676 (php-ts-mode--else-heuristic prev-line php-ts-mode-indent-offset)
639 ((node-is "default_statement") parent-bol php-ts-mode-indent-offset) 677
640 ((parent-is "default_statement") parent-bol php-ts-mode-indent-offset) 678 ((query "(ERROR (ERROR)) @indent") parent-bol 0)
641 ((and 679
642 (parent-is "expression_statement") 680 ((node-is ")") parent-bol 0)
643 (node-is ";")) 681 ((node-is "]") parent-bol 0)
644 parent-bol 0) 682 ((node-is "else_clause") parent-bol 0)
645 ((parent-is "expression_statement") parent-bol php-ts-mode-indent-offset) 683 ((node-is "case_statement") parent-bol php-ts-mode-indent-offset)
646 ;; `c-ts-common-looking-at-star' has to come before 684 ((node-is "default_statement") parent-bol php-ts-mode-indent-offset)
647 ;; `c-ts-common-comment-2nd-line-matcher'. 685 ((parent-is "default_statement") parent-bol php-ts-mode-indent-offset)
648 ((and (parent-is "comment") c-ts-common-looking-at-star) 686 ((and
649 c-ts-common-comment-start-after-first-star -1) 687 (parent-is "expression_statement")
650 (c-ts-common-comment-2nd-line-matcher 688 (node-is ";"))
651 c-ts-common-comment-2nd-line-anchor 689 parent-bol 0)
652 1) 690 ((parent-is "expression_statement") parent-bol php-ts-mode-indent-offset)
653 ((parent-is "comment") prev-adaptive-prefix 0) 691 ;; `c-ts-common-looking-at-star' has to come before
654 692 ;; `c-ts-common-comment-2nd-line-matcher'.
655 ((parent-is "method_declaration") parent-bol 0) 693 ((and (parent-is "comment") c-ts-common-looking-at-star)
656 ((node-is "class_interface_clause") parent-bol php-ts-mode-indent-offset) 694 c-ts-common-comment-start-after-first-star -1)
657 ((query "(class_interface_clause (name) @indent)") php-ts-mode--parent-eol 1) 695 (c-ts-common-comment-2nd-line-matcher
658 ((query "(class_interface_clause (qualified_name) @indent)") 696 c-ts-common-comment-2nd-line-anchor
659 parent-bol php-ts-mode-indent-offset) 697 1)
660 ((parent-is "class_declaration") parent-bol 0) 698 ((parent-is "comment") prev-adaptive-prefix 0)
699
700 ((parent-is "method_declaration") parent-bol 0)
701 ((node-is "class_interface_clause") parent-bol php-ts-mode-indent-offset)
702 ((query "(class_interface_clause (name) @indent)") php-ts-mode--parent-eol 1)
703 ((query "(class_interface_clause (qualified_name) @indent)")
704 parent-bol php-ts-mode-indent-offset)
705 ((parent-is "class_declaration") parent-bol 0)
661 ((parent-is "namespace_use_declaration") parent-bol php-ts-mode-indent-offset) 706 ((parent-is "namespace_use_declaration") parent-bol php-ts-mode-indent-offset)
662 ((parent-is "namespace_use_group") parent-bol php-ts-mode-indent-offset) 707 ((node-is "namespace_use_clause") parent-bol php-ts-mode-indent-offset)
663 ((parent-is "function_definition") parent-bol 0) 708 ((or (parent-is "use_declaration")
664 ((parent-is "member_call_expression") first-sibling php-ts-mode-indent-offset) 709 (parent-is "use_list"))
665 ((parent-is "conditional_expression") parent-bol php-ts-mode-indent-offset) 710 parent-bol php-ts-mode-indent-offset)
666 ((parent-is "assignment_expression") parent-bol php-ts-mode-indent-offset) 711 ((parent-is "function_definition") parent-bol 0)
667 ((parent-is "array_creation_expression") parent-bol php-ts-mode-indent-offset) 712 ((parent-is "member_call_expression") first-sibling php-ts-mode-indent-offset)
668 ((parent-is "parenthesized_expression") first-sibling 1) 713 ((parent-is "conditional_expression") parent-bol php-ts-mode-indent-offset)
669 ((parent-is "binary_expression") parent 0) 714 ((parent-is "assignment_expression") parent-bol php-ts-mode-indent-offset)
670 ((or (parent-is "arguments") 715 ((parent-is "array_creation_expression") parent-bol php-ts-mode-indent-offset)
671 (parent-is "formal_parameters")) 716 ((parent-is "attribute_group") parent-bol php-ts-mode-indent-offset)
672 parent-bol php-ts-mode-indent-offset) 717 ((parent-is "parenthesized_expression") first-sibling 1)
673 718 ((parent-is "binary_expression") parent 0)
674 ((query "(for_statement (assignment_expression left: (_)) @indent)") 719 ((or (parent-is "arguments")
675 parent-bol php-ts-mode-indent-offset) 720 (parent-is "formal_parameters"))
676 ((query "(for_statement (binary_expression left: (_)) @indent)") 721 parent-bol php-ts-mode-indent-offset)
677 parent-bol php-ts-mode-indent-offset) 722
678 ((query "(for_statement (update_expression (_)) @indent)") 723 ((query "(for_statement (assignment_expression left: (_)) @indent)")
679 parent-bol php-ts-mode-indent-offset) 724 parent-bol php-ts-mode-indent-offset)
680 ((query "(function_call_expression arguments: (_) @indent)") 725 ((query "(for_statement (binary_expression left: (_)) @indent)")
681 parent php-ts-mode-indent-offset) 726 parent-bol php-ts-mode-indent-offset)
682 ((query "(member_call_expression arguments: (_) @indent)") 727 ((query "(for_statement (update_expression (_)) @indent)")
683 parent php-ts-mode-indent-offset) 728 parent-bol php-ts-mode-indent-offset)
684 ((query "(scoped_call_expression name: (_) @indent)") 729 ((query "(function_call_expression arguments: (_) @indent)")
685 parent php-ts-mode-indent-offset) 730 parent php-ts-mode-indent-offset)
686 ((parent-is "scoped_property_access_expression") 731 ((query "(member_call_expression arguments: (_) @indent)")
687 parent php-ts-mode-indent-offset) 732 parent php-ts-mode-indent-offset)
688 733 ((query "(scoped_call_expression name: (_) @indent)")
689 ;; Closing bracket. Must stay here, the rule order matter. 734 parent php-ts-mode-indent-offset)
690 ((node-is "}") standalone-parent 0) 735 ((parent-is "scoped_property_access_expression")
691 ;; handle multiple single line comment that start at the and of a line 736 parent php-ts-mode-indent-offset)
692 ((match "comment" "declaration_list") php-ts-mode--anchor-prev-sibling 0) 737
693 ((parent-is "declaration_list") column-0 php-ts-mode-indent-offset) 738 ;; Closing bracket. Must stay here, the rule order matter.
694 739 ((node-is "}") standalone-parent 0)
695 ((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset) 740 ;; handle multiple single line comment that start at the and of a line
696 741 ((match "comment" "declaration_list") php-ts-mode--anchor-prev-sibling 0)
697 ;; Statement in {} blocks. 742 ((parent-is "declaration_list") column-0 php-ts-mode-indent-offset)
698 ((or (and (or (parent-is "compound_statement") 743
744 ((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset)
745
746 ;; Statement in {} blocks.
747 ((or (and (or (parent-is "compound_statement")
699 (parent-is "colon_block")) 748 (parent-is "colon_block"))
700 ;; If the previous sibling(s) are not on their 749 ;; If the previous sibling(s) are not on their
701 ;; own line, indent as if this node is the first 750 ;; own line, indent as if this node is the first
702 ;; sibling 751 ;; sibling
703 php-ts-mode--first-sibling) 752 php-ts-mode--first-sibling)
704 (or (match null "compound_statement") 753 (or (match null "compound_statement")
705 (match null "colon_block"))) 754 (match null "colon_block")))
706 standalone-parent php-ts-mode-indent-offset) 755 standalone-parent php-ts-mode-indent-offset)
707 ((or (parent-is "compound_statement") 756 ((or (parent-is "compound_statement")
708 (parent-is "colon_block")) 757 (parent-is "colon_block"))
709 parent-bol php-ts-mode-indent-offset) 758 parent-bol php-ts-mode-indent-offset)
710 ;; Opening bracket. 759 ;; Opening bracket.
711 ((or (node-is "compound_statement") 760 ((or (node-is "compound_statement")
712 (node-is "colon_block")) 761 (node-is "colon_block"))
713 standalone-parent php-ts-mode-indent-offset) 762 standalone-parent php-ts-mode-indent-offset)
714 763
715 ((parent-is "match_block") parent-bol php-ts-mode-indent-offset) 764 ((parent-is "match_block") parent-bol php-ts-mode-indent-offset)
716 ((parent-is "switch_block") parent-bol 0) 765 ((parent-is "switch_block") parent-bol 0)
717 766
718 ;; These rules are for cases where the body is bracketless. 767 ;; These rules are for cases where the body is bracketless.
719 ((match "while" "do_statement") parent-bol 0) 768 ((match "while" "do_statement") parent-bol 0)
720 ;; rule for PHP alternative syntax 769 ;; rule for PHP alternative syntax
721 ((or (node-is "else_if_clause") 770 ((or (node-is "else_if_clause")
722 (node-is "endif") 771 (node-is "endif")
723 (node-is "endfor") 772 (node-is "endfor")
724 (node-is "endforeach") 773 (node-is "endforeach")
725 (node-is "endwhile")) 774 (node-is "endwhile"))
726 parent-bol 0) 775 parent-bol 0)
727 ((or (parent-is "if_statement") 776 ((or (parent-is "if_statement")
728 (parent-is "else_clause") 777 (parent-is "else_clause")
729 (parent-is "for_statement") 778 (parent-is "for_statement")
730 (parent-is "foreach_statement") 779 (parent-is "foreach_statement")
731 (parent-is "while_statement") 780 (parent-is "while_statement")
732 (parent-is "do_statement") 781 (parent-is "do_statement")
733 (parent-is "switch_statement") 782 (parent-is "switch_statement")
734 (parent-is "case_statement") 783 (parent-is "case_statement")
735 (parent-is "empty_statement")) 784 (parent-is "empty_statement"))
736 parent-bol php-ts-mode-indent-offset) 785 parent-bol php-ts-mode-indent-offset)
786
787 ;; Do not touch the indentation inside a multiline the strings.
788 ((parent-is "string_content") no-indent 0)
737 789
738 ;; Workaround: handle "for" open statement group. Currently 790 ;; Workaround: handle "for" open statement group. Currently
739 ;; the grammar handles it differently than other control structures. 791 ;; the grammar handles it differently than other control structures.
740 (no-node php-ts-mode--open-statement-group-heuristic 0)))) 792 (no-node prev-sibling 0))))
741 `((psr2 793 `((psr2
742 ((parent-is "program") php-ts-mode--open-statement-group-heuristic 0)
743 ((parent-is "text_interpolation") column-0 0)
744 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset) 794 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
745 ,@common) 795 ,@common)
746 (pear 796 (pear
747 ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
748 ((parent-is "text_interpolation") php-ts-mode--parent-html-heuristic 0)
749 ((or (node-is "case_statement") 797 ((or (node-is "case_statement")
750 (node-is "default_statement")) 798 (node-is "default_statement"))
751 parent-bol 0) 799 parent-bol 0)
752 ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset) 800 ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset)
753 ,@common) 801 ,@common)
754 (drupal 802 (drupal
755 ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
756 ((parent-is "text_interpolation") php-ts-mode--parent-html-bol 0)
757 ((parent-is "if_statement") parent-bol 0) 803 ((parent-is "if_statement") parent-bol 0)
758 ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset) 804 ((parent-is "binary_expression") parent-bol php-ts-mode-indent-offset)
759 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset) 805 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
@@ -762,8 +808,6 @@ characters of the current line."
762 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset) 808 ((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
763 ,@common) 809 ,@common)
764 (wordpress 810 (wordpress
765 ((parent-is "program") php-ts-mode--parent-html-bol 0)
766 ((parent-is "text_interpolation") php-ts-mode--parent-html-bol 0)
767 ,@common) 811 ,@common)
768 (zend 812 (zend
769 ((parent-is "class_interface_clause") php-ts-mode--anchor-first-sibling 0) 813 ((parent-is "class_interface_clause") php-ts-mode--anchor-first-sibling 0)
@@ -838,6 +882,14 @@ characters of the current line."
838 "__toString" "__invoke" "__set_state" "__clone" "__debugInfo") 882 "__toString" "__invoke" "__set_state" "__clone" "__debugInfo")
839 "PHP predefined magic methods.") 883 "PHP predefined magic methods.")
840 884
885(defconst php-ts-mode--prettify-symbols-alist
886 '(("<=" . ?≤)
887 (">=" . ?≥)
888 ("->" . ?→)
889 ("=>" . ?⇒)
890 ("::" . ?∷))
891 "Value for `prettify-symbols-alist' in `php-ts-mode'.")
892
841(defun php-ts-mode--test-namespace-name-as-prefix-p () 893(defun php-ts-mode--test-namespace-name-as-prefix-p ()
842 "Return t if namespace_name_as_prefix is a named node, nil otherwise." 894 "Return t if namespace_name_as_prefix is a named node, nil otherwise."
843 (treesit-query-valid-p 'php "(namespace_name_as_prefix)")) 895 (treesit-query-valid-p 'php "(namespace_name_as_prefix)"))
@@ -850,14 +902,18 @@ characters of the current line."
850 "Return t if namespace_use_group_clause is a named node, nil otherwise." 902 "Return t if namespace_use_group_clause is a named node, nil otherwise."
851 (treesit-query-valid-p 'php "(namespace_use_group_clause)")) 903 (treesit-query-valid-p 'php "(namespace_use_group_clause)"))
852 904
853(defun php-ts-mode--test-visibility-modifier-operation-clause-p () 905(defun php-ts-mode--test-visibility-modifier-operation-p ()
854 "Return t if (visibility_modifier (operation)) is defined, nil otherwise." 906 "Return t if (visibility_modifier (operation)) is defined, nil otherwise."
855 (treesit-query-valid-p 'php "(visibility_modifier (operation))")) 907 (treesit-query-valid-p 'php "(visibility_modifier (operation))"))
856 908
857(defun php-ts-mode--test-property-hook-clause-p () 909(defun php-ts-mode--test-property-hook-p ()
858 "Return t if property_hook is a named node, nil otherwise." 910 "Return t if property_hook is a named node, nil otherwise."
859 (treesit-query-valid-p 'php "(property_hook)")) 911 (treesit-query-valid-p 'php "(property_hook)"))
860 912
913(defun php-ts-mode--test-relative-name-p ()
914 "Return t if relative_name is a named node, nil otherwise."
915 (treesit-query-valid-p 'php "(relative_name)"))
916
861(defun php-ts-mode--font-lock-settings () 917(defun php-ts-mode--font-lock-settings ()
862 "Tree-sitter font-lock settings." 918 "Tree-sitter font-lock settings."
863 (treesit-font-lock-rules 919 (treesit-font-lock-rules
@@ -866,7 +922,7 @@ characters of the current line."
866 :feature 'keyword 922 :feature 'keyword
867 :override t 923 :override t
868 `([,@php-ts-mode--keywords] @font-lock-keyword-face 924 `([,@php-ts-mode--keywords] @font-lock-keyword-face
869 ,@(when (php-ts-mode--test-visibility-modifier-operation-clause-p) 925 ,@(when (php-ts-mode--test-visibility-modifier-operation-p)
870 '((visibility_modifier (operation) @font-lock-builtin-face))) 926 '((visibility_modifier (operation) @font-lock-builtin-face)))
871 (var_modifier) @font-lock-builtin-face) 927 (var_modifier) @font-lock-builtin-face)
872 928
@@ -882,8 +938,8 @@ characters of the current line."
882 ;; predefined constant or built in constant (part of PHP core) 938 ;; predefined constant or built in constant (part of PHP core)
883 ((name) @font-lock-builtin-face 939 ((name) @font-lock-builtin-face
884 (:match ,(rx-to-string 940 (:match ,(rx-to-string
885 `(: bos (or ,@php-ts-mode--predefined-constant) eos)) 941 `(: bos (or ,@php-ts-mode--predefined-constant) eos))
886 @font-lock-builtin-face)) 942 @font-lock-builtin-face))
887 ;; user defined constant 943 ;; user defined constant
888 ((name) @font-lock-constant-face 944 ((name) @font-lock-constant-face
889 (:match "\\`_*[A-Z][0-9A-Z_]+\\'" @font-lock-constant-face)) 945 (:match "\\`_*[A-Z][0-9A-Z_]+\\'" @font-lock-constant-face))
@@ -917,7 +973,8 @@ characters of the current line."
917 (member_access_expression 973 (member_access_expression
918 name: (_) @font-lock-variable-name-face) 974 name: (_) @font-lock-variable-name-face)
919 (scoped_property_access_expression 975 (scoped_property_access_expression
920 scope: (name) @font-lock-constant-face)) 976 scope: (name) @font-lock-constant-face)
977 (error_suppression_expression (name) @font-lock-property-name-face))
921 978
922 :language 'php 979 :language 'php
923 :feature 'string 980 :feature 'string
@@ -966,28 +1023,28 @@ characters of the current line."
966 name: (_) @font-lock-type-face) 1023 name: (_) @font-lock-type-face)
967 (function_definition 1024 (function_definition
968 name: (_) @font-lock-function-name-face) 1025 name: (_) @font-lock-function-name-face)
969 ,@(when (php-ts-mode--test-property-hook-clause-p) 1026 ,@(when (php-ts-mode--test-property-hook-p)
970 '((property_hook (name) @font-lock-function-name-face))) 1027 '((property_hook (name) @font-lock-function-name-face)))
971 (method_declaration 1028 (method_declaration
972 name: (_) @font-lock-function-name-face) 1029 name: (_) @font-lock-function-name-face)
973 (method_declaration 1030 (method_declaration
974 name: (name) @font-lock-builtin-face 1031 name: (name) @font-lock-builtin-face
975 (:match ,(rx-to-string 1032 (:match ,(rx-to-string
976 `(: bos (or ,@php-ts-mode--class-magic-methods) eos)) 1033 `(: bos (or ,@php-ts-mode--class-magic-methods) eos))
977 @font-lock-builtin-face)) 1034 @font-lock-builtin-face))
978 ("=>") @font-lock-keyword-face 1035 ("=>") @font-lock-keyword-face
979 (object_creation_expression 1036 (object_creation_expression
980 (name) @font-lock-type-face) 1037 (name) @font-lock-type-face)
981 ,@(when (php-ts-mode--test-namespace-name-as-prefix-p) 1038 ,@(when (php-ts-mode--test-namespace-name-as-prefix-p)
982 '((namespace_name_as_prefix "\\" @font-lock-delimiter-face) 1039 '((namespace_name_as_prefix "\\" @font-lock-delimiter-face)
983 (namespace_name_as_prefix 1040 (namespace_name_as_prefix
984 (namespace_name (name)) @font-lock-type-face))) 1041 (namespace_name (name)) @font-lock-type-face)))
985 ,@(if (php-ts-mode--test-namespace-aliasing-clause-p) 1042 ,@(if (php-ts-mode--test-namespace-aliasing-clause-p)
986 '((namespace_aliasing_clause (name) @font-lock-type-face)) 1043 '((namespace_aliasing_clause (name) @font-lock-type-face))
987 '((namespace_use_clause alias: (name) @font-lock-type-face))) 1044 '((namespace_use_clause alias: (name) @font-lock-type-face)))
988 ,@(when (not (php-ts-mode--test-namespace-use-group-clause-p)) 1045 ,@(when (not (php-ts-mode--test-namespace-use-group-clause-p))
989 '((namespace_use_group 1046 '((namespace_use_group
990 (namespace_use_clause (name) @font-lock-type-face)))) 1047 (namespace_use_clause (name) @font-lock-type-face))))
991 (namespace_use_clause (name) @font-lock-type-face) 1048 (namespace_use_clause (name) @font-lock-type-face)
992 (namespace_name "\\" @font-lock-delimiter-face) 1049 (namespace_name "\\" @font-lock-delimiter-face)
993 (namespace_name (name) @font-lock-type-face) 1050 (namespace_name (name) @font-lock-type-face)
@@ -1035,7 +1092,9 @@ characters of the current line."
1035 (use_as_clause (name) @font-lock-property-use-face) 1092 (use_as_clause (name) @font-lock-property-use-face)
1036 ,@(when (not (php-ts-mode--test-namespace-name-as-prefix-p)) 1093 ,@(when (not (php-ts-mode--test-namespace-name-as-prefix-p))
1037 '((qualified_name prefix: "\\" @font-lock-delimiter-face))) 1094 '((qualified_name prefix: "\\" @font-lock-delimiter-face)))
1038 (qualified_name (name) @font-lock-constant-face)) 1095 (qualified_name (name) @font-lock-constant-face)
1096 ,@(when (php-ts-mode--test-relative-name-p)
1097 '((relative_name (name) @font-lock-constant-face))))
1039 1098
1040 :language 'php 1099 :language 'php
1041 :feature 'property 1100 :feature 'property
@@ -1061,35 +1120,17 @@ characters of the current line."
1061;;; Font-lock helpers 1120;;; Font-lock helpers
1062 1121
1063(defconst php-ts-mode--custom-html-font-lock-settings 1122(defconst php-ts-mode--custom-html-font-lock-settings
1064 (treesit-font-lock-rules 1123 (treesit-replace-font-lock-feature-settings
1065 :language 'html 1124 (treesit-font-lock-rules
1066 :override t 1125 :language 'html
1067 :feature 'comment 1126 :override t
1068 `((comment) @font-lock-comment-face 1127 :feature 'comment
1069 ;; handle shebang path and others type of comment 1128 '((comment) @font-lock-comment-face
1070 (document (text) @font-lock-comment-face)) 1129 ;; handle shebang path and others type of comment
1071 1130 (document (text) @font-lock-comment-face)))
1072 :language 'html 1131 mhtml-ts-mode--treesit-font-lock-settings)
1073 :override t
1074 :feature 'keyword
1075 `("doctype" @font-lock-keyword-face)
1076
1077 :language 'html
1078 :override t
1079 :feature 'definition
1080 `((tag_name) @font-lock-function-name-face)
1081
1082 :language 'html
1083 :override 'append
1084 :feature 'string
1085 `((quoted_attribute_value) @font-lock-string-face)
1086
1087 :language 'html
1088 :override t
1089 :feature 'property
1090 `((attribute_name) @font-lock-variable-name-face))
1091 "Tree-sitter Font-lock settings for HTML when embedded in PHP. 1132 "Tree-sitter Font-lock settings for HTML when embedded in PHP.
1092Like `html-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.") 1133Like `mhtml-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.")
1093 1134
1094(defvar php-ts-mode--phpdoc-font-lock-settings 1135(defvar php-ts-mode--phpdoc-font-lock-settings
1095 (treesit-font-lock-rules 1136 (treesit-font-lock-rules
@@ -1102,8 +1143,9 @@ Like `html-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.")
1102 :feature 'type 1143 :feature 'type
1103 :override t 1144 :override t
1104 '((union_type 1145 '((union_type
1105 [(array_type) (primitive_type) (named_type) (optional_type)] @font-lock-type-face) 1146 [(primitive_type) (named_type) (optional_type)] @font-lock-type-face)
1106 ([(array_type) (primitive_type) (named_type) (optional_type)] @font-lock-type-face) 1147 (array_type ["array" "list"] @font-lock-keyword-face)
1148 ([(primitive_type) (named_type) (optional_type)] @font-lock-type-face)
1107 (fqsen (name) @font-lock-function-name-face)) 1149 (fqsen (name) @font-lock-function-name-face))
1108 1150
1109 :language 'phpdoc 1151 :language 'phpdoc
@@ -1118,27 +1160,13 @@ Like `html-ts-mode--font-lock-settings' but adapted for `php-ts-mode'.")
1118 :language 'phpdoc 1160 :language 'phpdoc
1119 :feature 'variable 1161 :feature 'variable
1120 :override t 1162 :override t
1121 '((variable_name (name) @font-lock-variable-name-face))) 1163 '((variable_name (name) @font-lock-variable-name-face))
1122 "Tree-sitter font-lock settings for phpdoc.")
1123 1164
1124(defun php-ts-mode--colorize-css-value (node override start end &rest _) 1165 :language 'phpdoc
1125 "Colorize CSS property value like `css--fontify-region'. 1166 :feature 'phpdoc-error
1126For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'." 1167 :override t
1127 (if (and php-ts-mode-css-fontify-colors 1168 '((ERROR) @php-ts-mode--phpdoc-fontify-error))
1128 (string-equal "plain_value" (treesit-node-type node))) 1169 "Tree-sitter font-lock settings for phpdoc.")
1129 (let ((color (css--compute-color start (treesit-node-text node t))))
1130 (when color
1131 (with-silent-modifications
1132 (add-text-properties
1133 (treesit-node-start node) (treesit-node-end node)
1134 (list 'face (list :background color
1135 :foreground (readable-foreground-color
1136 color)
1137 :box '(:line-width -1)))))))
1138 (treesit-fontify-with-override
1139 (treesit-node-start node) (treesit-node-end node)
1140 'font-lock-variable-name-face
1141 override start end)))
1142 1170
1143(defun php-ts-mode--fontify-error (node override start end &rest _) 1171(defun php-ts-mode--fontify-error (node override start end &rest _)
1144 "Fontify the error nodes. 1172 "Fontify the error nodes.
@@ -1148,6 +1176,13 @@ For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'."
1148 'font-lock-warning-face 1176 'font-lock-warning-face
1149 override start end)) 1177 override start end))
1150 1178
1179(defun php-ts-mode--phpdoc-fontify-error (node override start end &rest _)
1180 "Fontify the phpdoc error nodes.
1181Most of the error are only tag non handled by the phpdoc parser.
1182For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'."
1183 (when php-ts-mode-phpdoc-highlight-errors
1184 (php-ts-mode--fontify-error node override start end)))
1185
1151 1186
1152;;; Imenu 1187;;; Imenu
1153 1188
@@ -1157,92 +1192,106 @@ For NODE, OVERRIDE, START, and END, see `treesit-font-lock-rules'."
1157 node 1192 node
1158 (lambda (n) 1193 (lambda (n)
1159 (member (treesit-node-type n) 1194 (member (treesit-node-type n)
1160 '("class_declaration" 1195 '("class_declaration"
1161 "enum_declaration" 1196 "enum_declaration"
1162 "function_definition" 1197 "function_definition"
1163 "interface_declaration" 1198 "interface_declaration"
1164 "method_declaration" 1199 "method_declaration"
1165 "namespace_definition" 1200 "namespace_definition"
1166 "trait_declaration"))))) 1201 "trait_declaration")))))
1167 1202
1168(defun php-ts-mode--defun-name-separator (node) 1203(defun php-ts-mode--defun-name-separator (node)
1169 "Return a separator to connect object name, based on NODE type." 1204 "Return a separator to connect object name, based on NODE type."
1170 (let ((node-type (treesit-node-type node))) 1205 (let ((node-type (treesit-node-type node)))
1171 (cond ((member node-type '("function_definition" "method_declaration")) 1206 (cond ((member node-type '("function_definition" "method_declaration"))
1172 "()::") 1207 "()::")
1173 ((member node-type '("class_declaration" "enum_declaration" "trait_declaration")) 1208 ((member node-type '("class_declaration" "enum_declaration" "trait_declaration"))
1174 "::") 1209 "::")
1175 (t "\\")))) 1210 (t "\\"))))
1176 1211
1177(defun php-ts-mode--defun-object-name (node node-text) 1212(defun php-ts-mode--defun-object-name (node node-text)
1178 "Compose the full name of a NODE that is a PHP variable, method, class etc. 1213 "Compose the full name of a NODE that is a PHP variable, method, class etc.
1179If the NODE has a parent, it recursively concat the parent names with NODE-TEXT, 1214If the NODE has a parent, it recursively concat the parent names with NODE-TEXT,
1180otherwise it returns NODE-TEXT." 1215otherwise it returns NODE-TEXT."
1181 (let* ((parent-node (php-ts-mode--parent-object node)) 1216 (let* ((parent-node (php-ts-mode--parent-object node))
1182 (parent-node-text 1217 (parent-node-text
1183 (treesit-node-text 1218 (treesit-node-text
1184 (treesit-node-child-by-field-name parent-node "name") t)) 1219 (treesit-node-child-by-field-name parent-node "name") t))
1185 (parent-node-separator (php-ts-mode--defun-name-separator parent-node))) 1220 (parent-node-separator (php-ts-mode--defun-name-separator parent-node)))
1186 (if parent-node 1221 (if parent-node
1187 (progn 1222 (progn
1188 (setq parent-node-text 1223 (setq parent-node-text
1189 (php-ts-mode--defun-object-name 1224 (php-ts-mode--defun-object-name
1190 parent-node 1225 parent-node
1191 parent-node-text)) 1226 parent-node-text))
1192 (concat parent-node-text parent-node-separator node-text)) 1227 (concat parent-node-text parent-node-separator node-text))
1193 node-text))) 1228 node-text)))
1194 1229
1195(defun php-ts-mode--defun-name (node) 1230(defun php-ts-mode--defun-name (node)
1196 "Return the defun name of NODE. 1231 "Return the defun name of NODE.
1197Return nil if the NODE has no field “name” or if NODE is not a defun node." 1232Return nil if the NODE has no field “name” or if NODE is not a defun node."
1198 (let ((child (treesit-node-child-by-field-name node "name"))) 1233 (let ((lang (treesit-node-language node))
1199 (cl-case (intern (treesit-node-type node)) 1234 (child (treesit-node-child-by-field-name node "name")))
1200 (class_declaration (treesit-node-text child t)) 1235 (if (eq lang 'php)
1201 (trait_declaration (treesit-node-text child t)) 1236 (cl-case (intern (treesit-node-type node))
1202 (interface_declaration (treesit-node-text child t)) 1237 (class_declaration (treesit-node-text child t))
1203 (namespace_definition (treesit-node-text child t)) 1238 (trait_declaration (treesit-node-text child t))
1204 (enum_declaration (treesit-node-text child t)) 1239 (interface_declaration (treesit-node-text child t))
1205 (function_definition (treesit-node-text child t)) 1240 (namespace_definition (treesit-node-text child t))
1206 (method_declaration 1241 (enum_declaration (treesit-node-text child t))
1207 (php-ts-mode--defun-object-name node (treesit-node-text child t))) 1242 (function_definition (treesit-node-text child t))
1208 (variable_name 1243 (method_declaration
1209 (php-ts-mode--defun-object-name node (treesit-node-text node t))) 1244 (php-ts-mode--defun-object-name node (treesit-node-text child t)))
1210 (const_element 1245 (variable_name
1211 (php-ts-mode--defun-object-name 1246 (php-ts-mode--defun-object-name node (treesit-node-text node t)))
1212 node 1247 (const_element
1213 (treesit-node-text (treesit-node-child node 0) t)))))) 1248 (php-ts-mode--defun-object-name
1249 node
1250 (treesit-node-text (treesit-node-child node 0) t))))
1251 (mhtml-ts-mode--defun-name node))))
1214 1252
1215 1253
1216;;; Defun navigation 1254;;; Defun navigation
1217 1255
1256(defvar php-ts-mode--treesit-defun-type-regexp
1257 (rx bos (or "class_declaration"
1258 "enum_declaration"
1259 "function_definition"
1260 "interface_declaration"
1261 "method_declaration"
1262 "namespace_definition"
1263 "trait_declaration")
1264 eos)
1265 "Settings for `treesit-defun-type-regexp'.")
1266
1218(defun php-ts-mode--indent-defun () 1267(defun php-ts-mode--indent-defun ()
1219 "Indent the current top-level declaration syntactically. 1268 "Indent the current top-level declaration syntactically.
1220`treesit-defun-type-regexp' defines what constructs to indent." 1269`treesit-defun-type-regexp' defines what constructs to indent."
1221 (interactive "*") 1270 (interactive "*")
1222 (when-let* ((orig-point (point-marker)) 1271 (when-let* ((orig-point (point-marker))
1223 (node (treesit-defun-at-point))) 1272 (node (treesit-defun-at-point)))
1224 (indent-region (treesit-node-start node) 1273 (indent-region (treesit-node-start node)
1225 (treesit-node-end node)) 1274 (treesit-node-end node))
1226 (goto-char orig-point))) 1275 (goto-char orig-point)))
1227 1276
1228(defun php-ts-mode--defun-valid-p (node) 1277(defun php-ts-mode--defun-valid-p (node)
1229 "Return non-nil if NODE is a valid defun node. 1278 "Return non-nil if NODE is a valid defun node.
1230Ie, NODE is not nested." 1279Ie, NODE is not nested."
1231 (not (and (member (treesit-node-type node) 1280 (not (and (member (treesit-node-type node)
1232 '("variable_name" 1281 '("variable_name"
1233 "const_element" 1282 "const_element"
1234 "enum_declaration" 1283 "enum_declaration"
1235 "union_declaration" 1284 "union_declaration"
1236 "declaration")) 1285 "declaration"))
1237 ;; If NODE's type is one of the above, make sure it is 1286 ;; If NODE's type is one of the above, make sure it is
1238 ;; top-level. 1287 ;; top-level.
1239 (treesit-node-top-level 1288 (treesit-node-top-level
1240 node (rx (or "variable_name" 1289 node (rx (or "variable_name"
1241 "const_element" 1290 "const_element"
1242 "function_definition" 1291 "function_definition"
1243 "enum_declaration" 1292 "enum_declaration"
1244 "union_declaration" 1293 "union_declaration"
1245 "declaration")))))) 1294 "declaration"))))))
1246 1295
1247 1296
1248;;; Filling 1297;;; Filling
@@ -1253,39 +1302,63 @@ Like `c-ts-common-comment-indent-new-line', but handle the
1253less common PHP-style # comment. SOFT works the same as in 1302less common PHP-style # comment. SOFT works the same as in
1254`comment-indent-new-line'." 1303`comment-indent-new-line'."
1255 (if (save-excursion 1304 (if (save-excursion
1256 ;; Line start with # or ## or ###... 1305 ;; Line start with # or ## or ###...
1257 (beginning-of-line) 1306 (beginning-of-line)
1258 (re-search-forward 1307 (re-search-forward
1259 (rx "#" (group (* (any "#")) (* " "))) 1308 (rx "#" (group (* (any "#")) (* " ")))
1260 (line-end-position) 1309 (line-end-position)
1261 t nil)) 1310 t nil))
1262 (let ((offset (- (match-beginning 0) (line-beginning-position))) 1311 (let ((offset (- (match-beginning 0) (line-beginning-position)))
1263 (comment-prefix (match-string 0)) 1312 (comment-prefix (match-string 0))
1264 (insert-line-break 1313 (insert-line-break
1265 (lambda () 1314 (lambda ()
1266 (delete-horizontal-space) 1315 (delete-horizontal-space)
1267 (if soft 1316 (if soft
1268 (insert-and-inherit ?\n) 1317 (insert-and-inherit ?\n)
1269 (newline 1))))) 1318 (newline 1)))))
1270 (funcall insert-line-break) 1319 (funcall insert-line-break)
1271 (delete-region (line-beginning-position) (point)) 1320 (delete-region (line-beginning-position) (point))
1272 (insert 1321 (insert
1273 (make-string offset ?\s) 1322 (make-string offset ?\s)
1274 comment-prefix)) 1323 comment-prefix))
1275 ;; other style of comments 1324 ;; other style of comments
1276 (c-ts-common-comment-indent-new-line soft))) 1325 (c-ts-common-comment-indent-new-line soft)))
1277 1326
1278(defun php-ts-mode-comment-setup () 1327(defvar-local php-ts-mode--comment-current-plist
1279 "Set up local variables for PHP comment. 1328 '(:php nil :comment-start "// " :comment-end "")
1280Depends on `c-ts-common-comment-setup'." 1329 "Store the current state of PHP comment configuration.
1281 (c-ts-common-comment-setup) 1330`mhtml-ts-mode' changes the comment configuration when switching
1282 (setq-local c-ts-common--comment-regexp "comment" 1331between HTML, Css and Javascript, so we have to do the same when
1283 comment-line-break-function #'php-ts-mode--comment-indent-new-line 1332switching back to PHP.
1284 comment-style 'extra-line 1333The prop :php must be t if we are in a PHP or PHPDOC range.")
1285 comment-start-skip (rx (or (seq "#" (not (any "["))) 1334
1286 (seq "/" (+ "/")) 1335(defun php-ts-mode--comment-setup (&optional force)
1287 (seq "/" (+ "*"))) 1336 "Set up local variables for comments.
1288 (* (syntax whitespace))))) 1337If FORCE is t setup comment for PHP. Depends on
1338`mhtml-ts-mode--comment-setup' and
1339`mhtml-ts-mode--comment-current-lang'."
1340 (let ((lang (treesit-language-at (point))))
1341 (if (and (or (eq lang 'php) (eq lang 'phpdoc) force)
1342 (eq (plist-get php-ts-mode--comment-current-plist :php) nil))
1343 ;; do setup only if the previous language isn't PHP
1344 (progn
1345 (plist-put php-ts-mode--comment-current-plist :php t)
1346 (c-ts-common-comment-setup)
1347 (setq-local c-ts-common--comment-regexp "comment"
1348 comment-line-break-function #'php-ts-mode--comment-indent-new-line
1349 comment-style 'extra-line
1350 comment-start (plist-get php-ts-mode--comment-current-plist :comment-start)
1351 comment-end (plist-get php-ts-mode--comment-current-plist :comment-end)
1352 comment-start-skip (rx (or (seq "#" (not (any "[")))
1353 (seq "/" (+ "/"))
1354 (seq "/" (+ "*")))
1355 (* (syntax whitespace)))
1356 ;; reset the state of mhtml-ts-mode--comment-setup
1357 mhtml-ts-mode--comment-current-lang nil))
1358 ;; otherwise set comment style for other languages.
1359 (plist-put php-ts-mode--comment-current-plist :php nil)
1360 (setq-local comment-style 'indent) ; this is the default for mhtml-ts-mode.
1361 (mhtml-ts-mode--comment-setup))))
1289 1362
1290 1363
1291;;; Modes 1364;;; Modes
@@ -1293,15 +1366,20 @@ Depends on `c-ts-common-comment-setup'."
1293(defun php-ts-mode-set-comment-style () 1366(defun php-ts-mode-set-comment-style ()
1294 "Set a different comment style." 1367 "Set a different comment style."
1295 (interactive) 1368 (interactive)
1296 (setq-local comment-start 1369 (let ((lang (treesit-language-at (point))))
1297 (completing-read 1370 (when (or (eq lang 'php) (eq lang 'phpdoc))
1298 "Choose comment style: " 1371 (setq-local comment-start
1299 '("/**" "//" "/*" "#") nil t nil nil "// ")) 1372 (completing-read
1300 (cond 1373 "Choose comment style: "
1301 ((equal comment-start "/*") (setq-local comment-end "*/")) 1374 '("/**" "// " "/*" "# ") nil t nil nil "// "))
1302 ((equal comment-start "//") (setq-local comment-end "")) 1375 (cond
1303 ((equal comment-start "#") (setq-local comment-end "")) 1376 ((equal comment-start "/*") (setq-local comment-end "*/"))
1304 ((equal comment-start "/**") (setq-local comment-end "*/")))) 1377 ((equal comment-start "// ") (setq-local comment-end ""))
1378 ((equal comment-start "# ") (setq-local comment-end ""))
1379 ((equal comment-start "/**") (setq-local comment-end "*/")))
1380 ;; store the value in order to recover when switch to php from other languages.
1381 (plist-put php-ts-mode--comment-current-plist :comment-start comment-start)
1382 (plist-put php-ts-mode--comment-current-plist :comment-end comment-end))))
1305 1383
1306(defvar-keymap php-ts-mode-map 1384(defvar-keymap php-ts-mode-map
1307 :doc "Keymap for `php-ts-mode' buffers." 1385 :doc "Keymap for `php-ts-mode' buffers."
@@ -1321,7 +1399,7 @@ Depends on `c-ts-common-comment-setup'."
1321 :enable mark-active 1399 :enable mark-active
1322 :help "Comment out the region between the mark and point"] 1400 :help "Comment out the region between the mark and point"]
1323 ["Uncomment Region" (comment-region (region-beginning) 1401 ["Uncomment Region" (comment-region (region-beginning)
1324 (region-end) '(4)) 1402 (region-end) '(4))
1325 :enable mark-active 1403 :enable mark-active
1326 :help "Uncomment the region between the mark and point"] 1404 :help "Uncomment the region between the mark and point"]
1327 ["Indent Top-level Expression" php-ts-mode--indent-defun 1405 ["Indent Top-level Expression" php-ts-mode--indent-defun
@@ -1336,7 +1414,7 @@ Depends on `c-ts-common-comment-setup'."
1336 ["Set Indentation Style..." php-ts-mode-set-style 1414 ["Set Indentation Style..." php-ts-mode-set-style
1337 :help "Set PHP indentation style for current buffer"] 1415 :help "Set PHP indentation style for current buffer"]
1338 ["Show Current Style Name"(message "Indentation Style: %s" 1416 ["Show Current Style Name"(message "Indentation Style: %s"
1339 php-ts-mode-indent-style) 1417 php-ts-mode-indent-style)
1340 :help "Show the name of the PHP indentation style for current buffer"] 1418 :help "Show the name of the PHP indentation style for current buffer"]
1341 ["Set Comment Style" php-ts-mode-set-comment-style 1419 ["Set Comment Style" php-ts-mode-set-comment-style
1342 :help "Choose PHP comment style between block and line comments"]) 1420 :help "Choose PHP comment style between block and line comments"])
@@ -1377,114 +1455,98 @@ Depends on `c-ts-common-comment-setup'."
1377 function))) 1455 function)))
1378 1456
1379;;;###autoload 1457;;;###autoload
1380(define-derived-mode php-ts-mode prog-mode "PHP" 1458(define-derived-mode php-ts-mode prog-mode
1459 '("PHP"
1460 (:eval (let ((lang (treesit-language-at (point))))
1461 (cond ((eq lang 'html) "+HTML")
1462 ((eq lang 'javascript) "+HTML+JS")
1463 ((eq lang 'css) "+HTML+CSS")))))
1381 "Major mode for editing PHP, powered by tree-sitter." 1464 "Major mode for editing PHP, powered by tree-sitter."
1382 :syntax-table php-ts-mode--syntax-table 1465 :syntax-table php-ts-mode--syntax-table
1383 1466
1467 ;; TODO: wouldn't better to delegate the check and the
1468 ;; init of the mhtml parser to mhtml-ts-mode?
1384 (if (not (and 1469 (if (not (and
1385 (treesit-ensure-installed 'php) 1470 (treesit-ensure-installed 'php)
1386 (treesit-ensure-installed 'phpdoc) 1471 (treesit-ensure-installed 'phpdoc)
1387 (treesit-ensure-installed 'html) 1472 (treesit-ensure-installed 'html)
1388 (treesit-ensure-installed 'javascript) 1473 (treesit-ensure-installed 'javascript)
1389 (treesit-ensure-installed 'jsdoc) 1474 (treesit-ensure-installed 'jsdoc)
1390 (treesit-ensure-installed 'css))) 1475 (treesit-ensure-installed 'css)))
1391 (error "Tree-sitter for PHP isn't 1476 (error "Tree-sitter for PHP isn't
1392 available. You can install the parsers with M-x 1477 available. You can install the parsers with M-x
1393 `php-ts-mode-install-parsers'") 1478 `php-ts-mode-install-parsers'")
1394 1479
1395 ;; phpdoc is a local parser, don't create a parser for it 1480 ;; phpdoc is a local parser, don't create a parser for it
1481 ;; create the parser needed by mhtml-ts-mode
1396 (treesit-parser-create 'html) 1482 (treesit-parser-create 'html)
1397 (treesit-parser-create 'css) 1483 (treesit-parser-create 'css)
1398 (treesit-parser-create 'javascript) 1484 (treesit-parser-create 'javascript)
1399 1485
1400 ;; define the injected parser ranges 1486 ;; define the injected parser ranges
1401 (setq-local treesit-range-settings 1487 (setq-local treesit-range-settings
1402 (treesit-range-rules 1488 (append
1403 :embed 'phpdoc 1489 (treesit-range-rules
1404 :host 'php 1490 :embed 'phpdoc
1405 :local t 1491 :host 'php
1406 '(((comment) @cap 1492 :local t
1407 (:match "/\\*\\*" @cap))) 1493 '(((comment) @cap
1408 1494 (:match "/\\*\\*" @cap)))
1409 :embed 'html 1495
1410 :host 'php 1496 :embed 'html
1411 '((program (text) @cap) 1497 :host 'php
1412 (text_interpolation (text) @cap)) 1498 '((program (text) @cap)
1413 1499 (text_interpolation (text) @cap)))
1414 :embed 'javascript 1500 ;; inject the range rules from mhtml-ts-mode
1415 :host 'html 1501 mhtml-ts-mode--range-settings))
1416 :offset '(1 . -1)
1417 '((script_element
1418 (start_tag (tag_name))
1419 (raw_text) @cap))
1420
1421 :embed 'jsdoc
1422 :host 'javascript
1423 :local t
1424 `(((comment) @cap
1425 (:match ,js--treesit-jsdoc-beginning-regexp @cap)))
1426
1427 :embed 'css
1428 :host 'html
1429 :offset '(1 . -1)
1430 '((style_element
1431 (start_tag (tag_name))
1432 (raw_text) @cap))))
1433 1502
1434 ;; Navigation. 1503 ;; Navigation.
1435 (setq-local treesit-defun-type-regexp
1436 (regexp-opt '("class_declaration"
1437 "enum_declaration"
1438 "function_definition"
1439 "interface_declaration"
1440 "method_declaration"
1441 "namespace_definition"
1442 "trait_declaration")))
1443
1444 (setq-local treesit-defun-name-function #'php-ts-mode--defun-name) 1504 (setq-local treesit-defun-name-function #'php-ts-mode--defun-name)
1445 1505
1446 (setq-local treesit-thing-settings 1506 (setq-local treesit-thing-settings
1447 `((php 1507 (append
1448 (defun ,treesit-defun-type-regexp) 1508 `((php
1449 (sexp (not (or (and named 1509 (defun ,php-ts-mode--treesit-defun-type-regexp)
1450 ,(rx bos (or "program" 1510 (sexp (not (or (and named
1451 "comment") 1511 ,(rx bos (or "program"
1452 eos)) 1512 "comment")
1453 (and anonymous 1513 eos))
1454 ,(rx bos (or "{" "}" "[" "]" 1514 (and anonymous
1455 "(" ")" ",") 1515 ,(rx bos (or "{" "}" "[" "]"
1456 eos))))) 1516 "(" ")" ",")
1457 (list 1517 eos)))))
1458 ,(rx bos (or "namespace_use_group" 1518 (list
1459 "enum_declaration_list" 1519 ,(rx bos (or "namespace_use_group"
1460 "declaration_list" 1520 "enum_declaration_list"
1461 "property_hook_list" 1521 "declaration_list"
1462 "use_list" 1522 "property_hook_list"
1463 "anonymous_function_use_clause" 1523 "use_list"
1464 "formal_parameters" 1524 "anonymous_function_use_clause"
1465 "match_block" 1525 "formal_parameters"
1466 "switch_block" 1526 "match_block"
1467 "compound_statement" 1527 "switch_block"
1468 "parenthesized_expression" 1528 "compound_statement"
1469 "_array_destructing" 1529 "parenthesized_expression"
1470 "arguments" 1530 "_array_destructing"
1471 "_complex_string_part") 1531 "arguments"
1472 eos)) 1532 "_complex_string_part")
1473 (sentence ,(regexp-opt 1533 eos))
1474 '("break_statement" 1534 (sentence ,(regexp-opt
1475 "case_statement" 1535 '("break_statement"
1476 "continue_statement" 1536 "case_statement"
1477 "declaration" 1537 "continue_statement"
1478 "default_statement" 1538 "declaration"
1479 "do_statement" 1539 "default_statement"
1480 "expression_statement" 1540 "do_statement"
1481 "for_statement" 1541 "expression_statement"
1482 "if_statement" 1542 "for_statement"
1483 "return_statement" 1543 "if_statement"
1484 "switch_statement" 1544 "return_statement"
1485 "while_statement" 1545 "switch_statement"
1486 "statement"))) 1546 "while_statement"
1487 (text ,(regexp-opt '("comment" "text")))))) 1547 "statement")))
1548 (text ,(regexp-opt '("comment" "text")))))
1549 mhtml-ts-mode--treesit-thing-settings))
1488 1550
1489 ;; Indent. 1551 ;; Indent.
1490 (when (eq php-ts-mode-indent-style 'wordpress) 1552 (when (eq php-ts-mode-indent-style 'wordpress)
@@ -1493,75 +1555,91 @@ Depends on `c-ts-common-comment-setup'."
1493 (setq-local c-ts-common-indent-offset 'php-ts-mode-indent-offset) 1555 (setq-local c-ts-common-indent-offset 'php-ts-mode-indent-offset)
1494 (setq-local treesit-simple-indent-rules (php-ts-mode--get-indent-style)) 1556 (setq-local treesit-simple-indent-rules (php-ts-mode--get-indent-style))
1495 (setq-local treesit-simple-indent-rules 1557 (setq-local treesit-simple-indent-rules
1496 (append treesit-simple-indent-rules 1558 (append treesit-simple-indent-rules
1497 php-ts-mode--phpdoc-indent-rules 1559 php-ts-mode--phpdoc-indent-rules
1498 html-ts-mode--indent-rules 1560 `((html ,@(alist-get 'html
1499 ;; Extended rules for js and css, to 1561 (mhtml-ts-mode--treesit-indent-rules))))
1500 ;; indent appropriately when injected 1562 ;; Replace rules for js and css, to
1501 ;; into html 1563 ;; indent appropriately when injected
1502 `((javascript ((parent-is "program") 1564 ;; into html.
1503 php-ts-mode--js-css-tag-bol 1565 (treesit-simple-indent-modify-rules
1504 php-ts-mode-js-css-indent-offset) 1566 'javascript
1505 ,@(cdr (car js--treesit-indent-rules)))) 1567 `((javascript ((parent-is "program")
1506 `((css ((parent-is "stylesheet") 1568 mhtml-ts-mode--js-css-tag-bol
1507 php-ts-mode--js-css-tag-bol 1569 php-ts-mode-js-css-indent-offset)))
1508 php-ts-mode-js-css-indent-offset) 1570 `((javascript
1509 ,@(cdr (car css--treesit-indent-rules)))))) 1571 ,@(alist-get 'javascript
1572 (mhtml-ts-mode--treesit-indent-rules)))))
1573
1574 (treesit-simple-indent-modify-rules
1575 'css
1576 `((css ((parent-is "stylesheet")
1577 mhtml-ts-mode--js-css-tag-bol
1578 php-ts-mode-js-css-indent-offset)))
1579 `((css
1580 ,@(alist-get 'css
1581 (mhtml-ts-mode--treesit-indent-rules)))))
1582 ))
1510 1583
1511 ;; Comment 1584 ;; Comment
1512 (php-ts-mode-comment-setup) 1585 (php-ts-mode--comment-setup t) ; Init the comment setup for PHP
1586 ;; Allow the comment machinery to automatically switch between the
1587 ;; various comment styles supported by `php-ts-mode'.
1588 (setq-local comment-setup-function #'php-ts-mode--comment-setup)
1513 1589
1514 ;; PHP vars are case-sensitive 1590 ;; PHP vars are case-sensitive
1515 (setq-local case-fold-search t) 1591 (setq-local case-fold-search t)
1516 1592
1517 ;; Electric 1593 ;; Electric
1518 (setq-local electric-indent-chars 1594 (setq-local electric-indent-chars
1519 (append "{}():;," electric-indent-chars)) 1595 (append "{}():;," electric-indent-chars))
1596
1597 (setq-local electric-layout-rules
1598 '((?\; . after) (?\{ . after) (?\} . before)))
1520 1599
1521 ;; Imenu/Which-function 1600 ;; Imenu/Which-function
1522 (setq-local treesit-simple-imenu-settings 1601 (setq-local treesit-aggregated-simple-imenu-settings
1523 '(("Class" "\\`class_declaration\\'" nil nil) 1602 (append
1524 ("Enum" "\\`enum_declaration\\'" nil nil) 1603 '((php ("Class" "\\`class_declaration\\'" nil nil)
1525 ("Function" "\\`function_definition\\'" nil nil) 1604 ("Enum" "\\`enum_declaration\\'" nil nil)
1526 ("Interface" "\\`interface_declaration\\'" nil nil) 1605 ("Function" "\\`function_definition\\'" nil nil)
1527 ("Method" "\\`method_declaration\\'" nil nil) 1606 ("Interface" "\\`interface_declaration\\'" nil nil)
1528 ("Namespace" "\\`namespace_definition\\'" nil nil) 1607 ("Method" "\\`method_declaration\\'" nil nil)
1529 ("Trait" "\\`trait_declaration\\'" nil nil) 1608 ("Namespace" "\\`namespace_definition\\'" nil nil)
1530 ("Variable" "\\`variable_name\\'" nil nil) 1609 ("Trait" "\\`trait_declaration\\'" nil nil)
1531 ("Constant" "\\`const_element\\'" nil nil))) 1610 ("Variable" "\\`variable_name\\'" nil nil)
1611 ("Constant" "\\`const_element\\'" nil nil)))
1612 mhtml-ts-mode--treesit-aggregated-simple-imenu-settings))
1532 1613
1533 ;; Outline 1614 ;; Outline
1534 (setq-local treesit-outline-predicate 1615 (setq-local treesit-aggregated-outline-predicate
1535 (rx bos (or "class_declaration" 1616 (append
1536 "function_definition" 1617 `((php . ,(rx bos (or "class_declaration"
1537 "interface_declaration" 1618 "function_definition"
1538 "method_declaration" 1619 "interface_declaration"
1539 "namespace_definition" 1620 "method_declaration"
1540 "trait_declaration") 1621 "namespace_definition"
1541 eos)) 1622 "trait_declaration")
1623 eos))
1624 (phpdoc . ,(rx bos "document" eos)))
1625 mhtml-ts-mode--treesit-aggregated-outline-predicate))
1542 1626
1543 ;; Font-lock. 1627 ;; Font-lock.
1544 (setq-local treesit-font-lock-settings 1628 (setq-local treesit-font-lock-settings
1545 (append (php-ts-mode--font-lock-settings) 1629 (append
1546 php-ts-mode--custom-html-font-lock-settings 1630 (php-ts-mode--font-lock-settings)
1547 js--treesit-font-lock-settings 1631 php-ts-mode--custom-html-font-lock-settings
1548 (append 1632 php-ts-mode--phpdoc-font-lock-settings))
1549 ;; Rule for coloring CSS property values.
1550 ;; Placed before `css--treesit-settings'
1551 ;; to win against the same rule contained therein.
1552 (treesit-font-lock-rules
1553 :language 'css
1554 :override t
1555 :feature 'variable
1556 '((plain_value) @php-ts-mode--colorize-css-value))
1557 css--treesit-settings)
1558 php-ts-mode--phpdoc-font-lock-settings))
1559 1633
1560 (setq-local treesit-font-lock-feature-list php-ts-mode--feature-list) 1634 (setq-local treesit-font-lock-feature-list php-ts-mode--feature-list)
1635 (setq-local prettify-symbols-alist php-ts-mode--prettify-symbols-alist)
1561 1636
1562 ;; Align. 1637 ;; Align.
1563 (setq-local align-indent-before-aligning t) 1638 (setq-local align-indent-before-aligning t)
1564 1639
1640 ;; Find files
1641 (setq-local find-sibling-rules php-ts-mode-find-sibling-rules)
1642
1565 ;; should be the last one 1643 ;; should be the last one
1566 (setq-local treesit-primary-parser (treesit-parser-create 'php)) 1644 (setq-local treesit-primary-parser (treesit-parser-create 'php))
1567 (treesit-major-mode-setup) 1645 (treesit-major-mode-setup)
@@ -1570,11 +1648,11 @@ Depends on `c-ts-common-comment-setup'."
1570 1648
1571;;;###autoload 1649;;;###autoload
1572(defun php-ts-mode-run-php-webserver (&optional port 1650(defun php-ts-mode-run-php-webserver (&optional port
1573 hostname 1651 hostname
1574 document-root 1652 document-root
1575 router-script 1653 router-script
1576 num-of-workers 1654 num-of-workers
1577 config) 1655 config)
1578 "Run PHP built-in web server. 1656 "Run PHP built-in web server.
1579 1657
1580PORT: Port number of built-in web server, default `php-ts-mode-ws-port'. 1658PORT: Port number of built-in web server, default `php-ts-mode-ws-port'.
@@ -1595,46 +1673,46 @@ Interactively, when invoked with prefix argument, always prompt for
1595PORT, HOSTNAME, DOCUMENT-ROOT, ROUTER-SCRIPT, NUM-OF-WORKERS and 1673PORT, HOSTNAME, DOCUMENT-ROOT, ROUTER-SCRIPT, NUM-OF-WORKERS and
1596CONFIG." 1674CONFIG."
1597 (interactive (when current-prefix-arg 1675 (interactive (when current-prefix-arg
1598 (php-ts-mode--webserver-read-args))) 1676 (php-ts-mode--webserver-read-args)))
1599 (let* ((port (or 1677 (let* ((port (or
1600 port 1678 port
1601 php-ts-mode-ws-port 1679 php-ts-mode-ws-port
1602 (php-ts-mode--webserver-read-args 'port))) 1680 (php-ts-mode--webserver-read-args 'port)))
1603 (hostname (or 1681 (hostname (or
1604 hostname 1682 hostname
1605 php-ts-mode-ws-hostname 1683 php-ts-mode-ws-hostname
1606 (php-ts-mode--webserver-read-args 'hostname))) 1684 (php-ts-mode--webserver-read-args 'hostname)))
1607 (document-root (or 1685 (document-root (or
1608 document-root 1686 document-root
1609 php-ts-mode-ws-document-root 1687 php-ts-mode-ws-document-root
1610 (php-ts-mode--webserver-read-args 'document-root))) 1688 (php-ts-mode--webserver-read-args 'document-root)))
1611 (config (or config 1689 (config (or config
1612 (when php-ts-mode-php-config 1690 (when php-ts-mode-php-config
1613 (expand-file-name php-ts-mode-php-config)))) 1691 (expand-file-name php-ts-mode-php-config))))
1614 (host (format "%s:%d" hostname port)) 1692 (host (format "%s:%d" hostname port))
1615 (name (format "PHP web server on: %s" host)) 1693 (name (format "PHP web server on: %s" host))
1616 (buf-name (format "*%s*" name)) 1694 (buf-name (format "*%s*" name))
1617 (args (delq 1695 (args (delq
1618 nil 1696 nil
1619 (list "-S" host 1697 (list "-S" host
1620 "-t" document-root 1698 "-t" document-root
1621 (when config 1699 (when config
1622 (format "-c %s" config)) 1700 (format "-c %s" config))
1623 router-script))) 1701 router-script)))
1624 (process-environment 1702 (process-environment
1625 (nconc (cond 1703 (nconc (cond
1626 (num-of-workers 1704 (num-of-workers
1627 (list 1705 (list
1628 (format "PHP_CLI_SERVER_WORKERS=%d" num-of-workers))) 1706 (format "PHP_CLI_SERVER_WORKERS=%d" num-of-workers)))
1629 (php-ts-mode-ws-workers 1707 (php-ts-mode-ws-workers
1630 (list 1708 (list
1631 (format "PHP_CLI_SERVER_WORKERS=%d" php-ts-mode-ws-workers)))) 1709 (format "PHP_CLI_SERVER_WORKERS=%d" php-ts-mode-ws-workers))))
1632 process-environment))) 1710 process-environment)))
1633 (if (get-buffer buf-name) 1711 (if (get-buffer buf-name)
1634 (message "Switch to already running web server into buffer %s" buf-name) 1712 (message "Switch to already running web server into buffer %s" buf-name)
1635 (message "Run PHP built-in web server with args %s into buffer %s" 1713 (message "Run PHP built-in web server with args %s into buffer %s"
1636 (string-join args " ") 1714 (string-join args " ")
1637 buf-name) 1715 buf-name)
1638 (apply #'make-comint name (php-ts-mode--executable) nil args)) 1716 (apply #'make-comint name (php-ts-mode--executable) nil args))
1639 (funcall 1717 (funcall
1640 (if (called-interactively-p 'interactive) #'display-buffer #'get-buffer) 1718 (if (called-interactively-p 'interactive) #'display-buffer #'get-buffer)
@@ -1647,45 +1725,45 @@ CONFIG."
1647The optional TYPE can be the symbol \"port\", \"hostname\", \"document-root\", 1725The optional TYPE can be the symbol \"port\", \"hostname\", \"document-root\",
1648\"router-script\", \"num-workers\" or \"config\", otherwise it requires all of them." 1726\"router-script\", \"num-workers\" or \"config\", otherwise it requires all of them."
1649 (let ((ask-port (lambda () 1727 (let ((ask-port (lambda ()
1650 (read-number "Port: " (or 1728 (read-number "Port: " (or
1651 php-ts-mode-ws-port 1729 php-ts-mode-ws-port
1652 3000)))) 1730 3000))))
1653 (ask-hostname (lambda () 1731 (ask-hostname (lambda ()
1654 (read-string "Hostname: " 1732 (read-string "Hostname: "
1655 (or 1733 (or
1656 php-ts-mode-ws-hostname 1734 php-ts-mode-ws-hostname
1657 "localhost")))) 1735 "localhost"))))
1658 (ask-document-root (lambda () 1736 (ask-document-root (lambda ()
1659 (expand-file-name 1737 (expand-file-name
1660 (read-directory-name "Document root: " 1738 (read-directory-name "Document root: "
1661 (file-name-directory 1739 (file-name-directory
1662 (or (buffer-file-name) 1740 (or (buffer-file-name)
1663 default-directory)))))) 1741 default-directory))))))
1664 (ask-router-script (lambda () 1742 (ask-router-script (lambda ()
1665 (expand-file-name 1743 (expand-file-name
1666 (read-file-name "Router script: " 1744 (read-file-name "Router script: "
1667 (file-name-directory 1745 (file-name-directory
1668 (or (buffer-file-name) 1746 (or (buffer-file-name)
1669 default-directory)))))) 1747 default-directory))))))
1670 (ask-num-workers (lambda () 1748 (ask-num-workers (lambda ()
1671 (let ((num-workers 1749 (let ((num-workers
1672 (read-number 1750 (read-number
1673 "Number of workers (less then 2 means no workers): " 1751 "Number of workers (less then 2 means no workers): "
1674 (or php-ts-mode-ws-workers 0)))) 1752 (or php-ts-mode-ws-workers 0))))
1675 ;; num-workers must be >= 2 or nil 1753 ;; num-workers must be >= 2 or nil
1676 ;; otherwise PHP's built-in web server will not start. 1754 ;; otherwise PHP's built-in web server will not start.
1677 (if (> num-workers 1) 1755 (if (> num-workers 1)
1678 num-workers 1756 num-workers
1679 nil)))) 1757 nil))))
1680 (ask-config (lambda() 1758 (ask-config (lambda()
1681 (let ((file-name (expand-file-name 1759 (let ((file-name (expand-file-name
1682 (read-file-name "Alternative php.ini: " 1760 (read-file-name "Alternative php.ini: "
1683 (file-name-directory 1761 (file-name-directory
1684 (or (buffer-file-name) 1762 (or (buffer-file-name)
1685 default-directory)))))) 1763 default-directory))))))
1686 (if (string= "" (file-name-directory file-name)) 1764 (if (string= "" (file-name-directory file-name))
1687 nil 1765 nil
1688 file-name))))) 1766 file-name)))))
1689 (cl-case type 1767 (cl-case type
1690 (port (funcall ask-port)) 1768 (port (funcall ask-port))
1691 (hostname (funcall ask-hostname)) 1769 (hostname (funcall ask-hostname))
@@ -1694,21 +1772,21 @@ The optional TYPE can be the symbol \"port\", \"hostname\", \"document-root\",
1694 (num-of-workers (funcall ask-num-workers)) 1772 (num-of-workers (funcall ask-num-workers))
1695 (config (funcall ask-config)) 1773 (config (funcall ask-config))
1696 (t (list 1774 (t (list
1697 (funcall ask-port) 1775 (funcall ask-port)
1698 (funcall ask-hostname) 1776 (funcall ask-hostname)
1699 (funcall ask-document-root) 1777 (funcall ask-document-root)
1700 (funcall ask-router-script) 1778 (funcall ask-router-script)
1701 (funcall ask-num-workers) 1779 (funcall ask-num-workers)
1702 (funcall ask-config)))))) 1780 (funcall ask-config))))))
1703 1781
1704(define-derived-mode inferior-php-ts-mode comint-mode "Inferior PHP" 1782(define-derived-mode inferior-php-ts-mode comint-mode "Inferior PHP"
1705 "Major mode for PHP inferior process." 1783 "Major mode for PHP inferior process."
1706 (setq-local scroll-conservatively 1 1784 (setq-local scroll-conservatively 1
1707 comint-input-ring-file-name php-ts-mode-inferior-history 1785 comint-input-ring-file-name php-ts-mode-inferior-history
1708 comint-input-ignoredups t 1786 comint-input-ignoredups t
1709 comint-prompt-read-only t 1787 comint-prompt-read-only t
1710 comint-use-prompt-regexp t 1788 comint-use-prompt-regexp t
1711 comint-prompt-regexp (concat "^" php-ts-mode--inferior-prompt " ")) 1789 comint-prompt-regexp (concat "^" php-ts-mode--inferior-prompt " "))
1712 (comint-read-input-ring t)) 1790 (comint-read-input-ring t))
1713 1791
1714 1792
@@ -1726,24 +1804,24 @@ and `php-ts-mode-php-config' respectively, control which PHP interpreter is run.
1726Prompt for CMD if `php-ts-mode-php-executable' is nil. 1804Prompt for CMD if `php-ts-mode-php-executable' is nil.
1727Optional CONFIG, if supplied, is the php.ini file to use." 1805Optional CONFIG, if supplied, is the php.ini file to use."
1728 (interactive (when current-prefix-arg 1806 (interactive (when current-prefix-arg
1729 (list 1807 (list
1730 (read-string "Run PHP: " (php-ts-mode--executable)) 1808 (read-string "Run PHP: " (php-ts-mode--executable))
1731 (expand-file-name 1809 (expand-file-name
1732 (read-file-name "With config: " php-ts-mode-php-config))))) 1810 (read-file-name "With config: " php-ts-mode-php-config)))))
1733 (let* ((php-prog (php-ts-mode--executable)) 1811 (let* ((php-prog (php-ts-mode--executable))
1734 (buffer (get-buffer-create php-ts-mode-inferior-php-buffer)) 1812 (buffer (get-buffer-create php-ts-mode-inferior-php-buffer))
1735 (cmd (or 1813 (cmd (or
1736 cmd 1814 cmd
1737 php-prog 1815 php-prog
1738 (read-string "Run PHP: " php-prog))) 1816 (read-string "Run PHP: " php-prog)))
1739 (config (or 1817 (config (or
1740 config 1818 config
1741 (and php-ts-mode-php-config 1819 (and php-ts-mode-php-config
1742 (expand-file-name php-ts-mode-php-config))))) 1820 (expand-file-name php-ts-mode-php-config)))))
1743 (unless (comint-check-proc buffer) 1821 (unless (comint-check-proc buffer)
1744 (with-current-buffer buffer 1822 (with-current-buffer buffer
1745 (inferior-php-ts-mode-startup cmd config) 1823 (inferior-php-ts-mode-startup cmd config)
1746 (inferior-php-ts-mode))) 1824 (inferior-php-ts-mode)))
1747 (when buffer 1825 (when buffer
1748 (pop-to-buffer buffer)))) 1826 (pop-to-buffer buffer))))
1749 1827
@@ -1752,39 +1830,39 @@ Optional CONFIG, if supplied, is the php.ini file to use."
1752CMD is the command to run. Optional CONFIG, if supplied, is the php.ini 1830CMD is the command to run. Optional CONFIG, if supplied, is the php.ini
1753file to use." 1831file to use."
1754 (setq-local php-ts-mode--inferior-php-process 1832 (setq-local php-ts-mode--inferior-php-process
1755 (apply #'make-comint-in-buffer 1833 (apply #'make-comint-in-buffer
1756 (string-replace "*" "" php-ts-mode-inferior-php-buffer) 1834 (string-replace "*" "" php-ts-mode-inferior-php-buffer)
1757 php-ts-mode-inferior-php-buffer 1835 php-ts-mode-inferior-php-buffer
1758 cmd 1836 cmd
1759 nil 1837 nil
1760 (delq 1838 (delq
1761 nil 1839 nil
1762 (list 1840 (list
1763 (when config 1841 (when config
1764 (format "-c %s" config)) 1842 (format "-c %s" config))
1765 "-a")))) 1843 "-a"))))
1766 (add-hook 'comint-preoutput-filter-functions 1844 (add-hook 'comint-preoutput-filter-functions
1767 (lambda (string) 1845 (lambda (string)
1768 (let ((prompt (concat php-ts-mode--inferior-prompt " "))) 1846 (let ((prompt (concat php-ts-mode--inferior-prompt " ")))
1769 (if (member 1847 (if (member
1770 string 1848 string
1771 (list prompt "php { " "php ( " "/* > " "Interactive shell\n\n")) 1849 (list prompt "php { " "php ( " "/* > " "Interactive shell\n\n"))
1772 string 1850 string
1773 (let (;; Filter out prompts characters that accumulate when sending 1851 (let (;; Filter out prompts characters that accumulate when sending
1774 ;; regions to the inferior process. 1852 ;; regions to the inferior process.
1775 (clean-string 1853 (clean-string
1776 (replace-regexp-in-string 1854 (replace-regexp-in-string
1777 (rx-to-string `(or 1855 (rx-to-string `(or
1778 (+ "php >" (opt space)) 1856 (+ "php >" (opt space))
1779 (+ "php {" (opt space)) 1857 (+ "php {" (opt space))
1780 (+ "php (" (opt space)) 1858 (+ "php (" (opt space))
1781 (+ "/*" (1+ space) (1+ ">") (opt space)))) 1859 (+ "/*" (1+ space) (1+ ">") (opt space))))
1782 "" string))) 1860 "" string)))
1783 ;; Re-add the prompt for the next line, if isn't empty. 1861 ;; Re-add the prompt for the next line, if isn't empty.
1784 (if (string= clean-string "") 1862 (if (string= clean-string "")
1785 "" 1863 ""
1786 (concat (string-chop-newline clean-string) "\n" prompt)))))) 1864 (concat (string-chop-newline clean-string) "\n" prompt))))))
1787 nil t) 1865 nil t)
1788 (when php-ts-mode-inferior-history 1866 (when php-ts-mode-inferior-history
1789 (set-process-sentinel 1867 (set-process-sentinel
1790 (get-buffer-process php-ts-mode-inferior-php-buffer) 1868 (get-buffer-process php-ts-mode-inferior-php-buffer)
@@ -1796,7 +1874,7 @@ file to use."
1796 ;; Depending on how the process is killed the buffer may not be 1874 ;; Depending on how the process is killed the buffer may not be
1797 ;; around anymore; e.g. `kill-buffer'. 1875 ;; around anymore; e.g. `kill-buffer'.
1798 (when-let* ((buffer (process-buffer process)) 1876 (when-let* ((buffer (process-buffer process))
1799 ((buffer-live-p (process-buffer process)))) 1877 ((buffer-live-p (process-buffer process))))
1800 (with-current-buffer buffer (comint-write-input-ring)))) 1878 (with-current-buffer buffer (comint-write-input-ring))))
1801 1879
1802(defun php-ts-mode-send-region (beg end) 1880(defun php-ts-mode-send-region (beg end)
@@ -1804,12 +1882,12 @@ file to use."
1804 (interactive "r") 1882 (interactive "r")
1805 (if (buffer-live-p php-ts-mode--inferior-php-process) 1883 (if (buffer-live-p php-ts-mode--inferior-php-process)
1806 (progn 1884 (progn
1807 (php-ts-mode-show-process-buffer) 1885 (php-ts-mode-show-process-buffer)
1808 (comint-send-string php-ts-mode--inferior-php-process "\n") 1886 (comint-send-string php-ts-mode--inferior-php-process "\n")
1809 (comint-send-string 1887 (comint-send-string
1810 php-ts-mode--inferior-php-process 1888 php-ts-mode--inferior-php-process
1811 (buffer-substring-no-properties beg end)) 1889 (buffer-substring-no-properties beg end))
1812 (comint-send-string php-ts-mode--inferior-php-process "\n")) 1890 (comint-send-string php-ts-mode--inferior-php-process "\n"))
1813 (message "Invoke run-php first!"))) 1891 (message "Invoke run-php first!")))
1814 1892
1815(defun php-ts-mode-send-buffer () 1893(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 @@ NODE and PARENT are ignored."
333 "function_declaration" 333 "function_declaration"
334 "lexical_declaration" 334 "lexical_declaration"
335 "element" 335 "element"
336 "rule_set")) 336 "rule_set"
337 "keyframe_block"))
337 "Settings for `treesit-defun-type-regexp'.") 338 "Settings for `treesit-defun-type-regexp'.")
338 339
339;; In order to support `prettify-symbols-mode', just `append' the prettify 340;; In order to support `prettify-symbols-mode', just `append' the prettify
@@ -348,13 +349,11 @@ NODE and PARENT are ignored."
348(defun mhtml-ts-mode--defun-name (node) 349(defun mhtml-ts-mode--defun-name (node)
349 "Return the defun name of NODE. 350 "Return the defun name of NODE.
350Return nil if there is no name or if NODE is not a defun node." 351Return nil if there is no name or if NODE is not a defun node."
351 (let ((html-name (html-ts-mode--defun-name node)) 352 (let ((lang (treesit-node-language node)))
352 (js-name (js--treesit-defun-name node))
353 (css-name (css--treesit-defun-name node)))
354 (cond 353 (cond
355 (html-name html-name) 354 ((eq lang 'html) (html-ts-mode--defun-name node))
356 (js-name js-name) 355 ((eq lang 'javascript) (js--treesit-defun-name node))
357 (css-name css-name)))) 356 ((eq lang 'css) (css--treesit-defun-name node)))))
358 357
359(defvar-local mhtml-ts-mode--comment-current-lang nil) 358(defvar-local mhtml-ts-mode--comment-current-lang nil)
360 359
@@ -432,6 +431,41 @@ Calls REPORT-FN directly. Requires tidy."
432 (process-send-region mhtml-ts-mode--flymake-process (point-min) (point-max)) 431 (process-send-region mhtml-ts-mode--flymake-process (point-min) (point-max))
433 (process-send-eof mhtml-ts-mode--flymake-process))))) 432 (process-send-eof mhtml-ts-mode--flymake-process)))))
434 433
434(defvar mhtml-ts-mode--range-settings
435 (append
436 (treesit-range-rules
437 :embed 'javascript
438 :host 'html
439 '((script_element
440 (start_tag (tag_name))
441 (raw_text) @cap))
442
443 ;; Another rule could be added that when it matches an
444 ;; attribute_value that has as its parent an
445 ;; attribute_name "style" it captures it and then
446 ;; passes it to the css parser.
447 :embed 'css
448 :host 'html
449 '((style_element
450 (start_tag (tag_name))
451 (raw_text) @cap)))
452
453 ;; jsdoc is not mandatory for js-ts-mode, so we respect this by
454 ;; adding jsdoc range rules only when jsdoc is available.
455 (when (treesit-ensure-installed 'jsdoc)
456 (treesit-range-rules
457 :embed 'jsdoc
458 :host 'javascript
459 :local t
460 `(((comment) @cap
461 (:match ,js--treesit-jsdoc-beginning-regexp @cap)))))))
462
463(defvar mhtml-ts-mode--treesit-aggregated-outline-predicate
464 `((html . ,#'html-ts-mode--outline-predicate)
465 (javascript . ,js-ts-mode--outline-predicate)
466 (css . ,css-ts-mode--outline-predicate))
467 "Settings for `treesit-aggregated-outline-predicate'.")
468
435;;;###autoload 469;;;###autoload
436(define-derived-mode mhtml-ts-mode html-ts-mode 470(define-derived-mode mhtml-ts-mode html-ts-mode
437 '("HTML+" (:eval (let ((lang (treesit-language-at (point)))) 471 '("HTML+" (:eval (let ((lang (treesit-language-at (point))))
@@ -475,35 +509,11 @@ Powered by tree-sitter."
475 ;; Multi-language modes must set the primary parser. 509 ;; Multi-language modes must set the primary parser.
476 (setq-local treesit-primary-parser (treesit-parser-create 'html)) 510 (setq-local treesit-primary-parser (treesit-parser-create 'html))
477 511
478 (setq-local treesit-range-settings 512 (setq-local treesit-range-settings mhtml-ts-mode--range-settings)
479 (treesit-range-rules
480 :embed 'javascript
481 :host 'html
482 '((script_element
483 (start_tag (tag_name))
484 (raw_text) @cap))
485
486 ;; Another rule could be added that when it matches an
487 ;; attribute_value that has as its parent an
488 ;; attribute_name "style" it captures it and then
489 ;; passes it to the css parser.
490 :embed 'css
491 :host 'html
492 '((style_element
493 (start_tag (tag_name))
494 (raw_text) @cap))))
495 513
496 ;; jsdoc is not mandatory for js-ts-mode, so we respect this by 514 ;; jsdoc is not mandatory for js-ts-mode, so we respect this by
497 ;; adding jsdoc range rules only when jsdoc is available. 515 ;; adding jsdoc range rules only when jsdoc is available.
498 (when (treesit-ensure-installed 'jsdoc) 516 (when (treesit-ensure-installed 'jsdoc)
499 (setq-local treesit-range-settings
500 (append treesit-range-settings
501 (treesit-range-rules
502 :embed 'jsdoc
503 :host 'javascript
504 :local t
505 `(((comment) @cap
506 (:match ,js--treesit-jsdoc-beginning-regexp @cap))))))
507 (setq-local c-ts-common--comment-regexp 517 (setq-local c-ts-common--comment-regexp
508 js--treesit-jsdoc-comment-regexp)) 518 js--treesit-jsdoc-comment-regexp))
509 519
@@ -563,9 +573,7 @@ Powered by tree-sitter."
563 mhtml-ts-mode--treesit-aggregated-simple-imenu-settings) 573 mhtml-ts-mode--treesit-aggregated-simple-imenu-settings)
564 574
565 (setq-local treesit-aggregated-outline-predicate 575 (setq-local treesit-aggregated-outline-predicate
566 `((html . ,#'html-ts-mode--outline-predicate) 576 mhtml-ts-mode--treesit-aggregated-outline-predicate)
567 (javascript . ,js-ts-mode--outline-predicate)
568 (css . ,css-ts-mode--outline-predicate)))
569 577
570 (treesit-major-mode-setup) 578 (treesit-major-mode-setup)
571 579