Add support for PHP 8.5 in 'php-ts-mode.el'

* lisp/progmodes/php-ts-mode.el: Doc string update.
(php-ts-mode--language-source-alist): PHP grammar update.
(php-ts-mode-find-sibling-rules): Doc string fix.
(php-ts-mode--parent-html-heuristic): Handled the case where there
is no HTML between two blocks of PHP code.
(php-ts-mode--array-element-heuristic): Fix typo.
(php-ts-mode--pipe-heuristic): New function that handle PHP's new
pipe operator.
(php-ts-mode--indent-styles): Removed commented code.  More
accurate indentation of ‘member_call_expression’.  Use of new
function to handle pipe operator indentation.  New rule for
indenting ‘property_hook_list’.
(php-ts-mode--test-yield-from-p): New predicate to check whether
the 'yield from' keyword is supported by the PHP grammar.
(php-ts-mode--test-pipe-p): New predicate to check whether the
‘pipe’ operator is supported by the PHP grammar.
(php-ts-mode--keywords): Use the new predicate
'php-ts-mode--test-yield-from-p'.  Now it is a var instead of a
const.
(php-ts-mode--operators): Use the new predicate
'php-ts-mode--test-pipe-p'.  Now it is a var instead of a const.
(php-ts-mode--font-lock-settings): New rules for
'nullsafe_member_access_expression', 'callable' primitive type.
New rule for the 'php_end_tag' if supported by the grammar.
More precise rules for 'scoped_call_expression' and
'member_call_expression'.
(php-ts-mode--inferior-php-process): Removed this unnecessary
variable.
(inferior-php-ts-mode-startup): Removed the use of
'php-ts-mode--inferior-php-process'.
(php-ts-mode-inferior--write-history): Removed the use of
'php-ts-mode--inferior-php-process'.
(Bug#79339)
This commit is contained in:
Vincenzo Pupillo 2025-08-28 23:29:30 +02:00 committed by Eli Zaretskii
parent d02181e39f
commit abfd8a454a

View file

@ -30,7 +30,7 @@
;; - tree-sitter-jsdoc: v0.23.2
;; - tree-sitter-javascript: v0.23.1-2-g108b2d4
;; - tree-sitter-html: v0.23.2-1-gd9219ad
;; - tree-sitter-php: v0.23.12
;; - tree-sitter-php: v0.24.2
;;
;; We try our best to make builtin modes work with latest grammar
;; versions, so a more recent grammar has a good chance to work too.
@ -81,7 +81,7 @@
;;; Install treesitter language parsers
(defvar php-ts-mode--language-source-alist
'((php "https://github.com/tree-sitter/tree-sitter-php"
:commit "f7cf7348737d8cff1b13407a0bfedce02ee7b046"
:commit "5b5627faaa290d89eb3d01b9bf47c3bb9e797dea"
:source-dir "php/src")
(phpdoc "https://github.com/claytonrcarter/tree-sitter-phpdoc"
:commit "03bb10330704b0b371b044e937d5cc7cd40b4999"))
@ -238,8 +238,8 @@ Useful for testing code against multiple simultaneous requests."
(defcustom php-ts-mode-find-sibling-rules
(list (list (rx "src/" (group (+ not-newline) "/") (group (+ (not "/"))) ".php") "tests/\\1\\2Test.php")
(list (rx "tests/" (group (+ not-newline) "/") (group (+ (not "/"))) "Test.php") "src/\\1\\2.php"))
"Rules for finding sibling files. See `find-sibling-rules' for the
form of the value.
"Rules for finding sibling files.
See `find-sibling-rules' for the form of the value.
As a default, the rules try to find the corresponding test of the
current source file and vice versa. Source files are assumed to be in
src/, and tests of in tests/. Many frameworks have a folder
@ -587,7 +587,11 @@ the current line."
((eq php-ts-mode-html-relative-indent 'ignore) (line-beginning-position))
((search-backward "</html>" (treesit-node-start parent) t 1) (line-beginning-position))
((null node) (apply (alist-get 'prev-sibling treesit-simple-indent-presets) node parent bol nil))
(t (when-let* ((html-node (treesit-search-forward node "text" t))
(t (when-let* ((html-node (treesit-search-forward
node
(lambda (node)
(equal (treesit-node-type node) "text"))
t))
(end-html (treesit-node-end html-node)))
(goto-char end-html)
;; go to the start of the last tag
@ -622,7 +626,7 @@ characters of the current line."
(defun php-ts-mode--anchor-first-sibling (_node parent _bol &rest _)
"Return the start of the first child of a sibling of PARENT.
If the fist sibling of PARENT and the first child of the sibling are
If the first sibling of PARENT and the first child of the sibling are
on the same line return the start position of the first child of the
sibling. Otherwise return the start of the first sibling.
PARENT is NODE's parent, BOL is the beginning of non-whitespace
@ -663,12 +667,27 @@ characters of the current line."
(treesit-node-prev-sibling prev-sibling)))))
(treesit-node-start prev-sibling)))
(defun php-ts-mode--pipe-heuristic (node parent _bol &rest _)
"Return the start of the previous pipe to the current pipe NODE.
Otherwise return the beginning of line of the previous non pipe NODE.
PARENT is NODE's parent, BOL is the beginning of non-whitespace
characters of the current line."
(save-excursion
(let* ((parent-start (treesit-node-start parent))
(node-start (treesit-node-start node))
(bound (progn
(goto-char parent-start)
(beginning-of-line 1)
(point))))
(goto-char node-start)
(let ((previous-pipe (search-backward "|>" bound t nil)))
(or previous-pipe (+ bound php-ts-mode-indent-offset))))))
(defun php-ts-mode--indent-styles ()
"Indent rules supported by `php-ts-mode'."
(let ((common
`(;; Handle indentation relatives to HTML.
;; ((parent-is "program") php-ts-mode--parent-html-heuristic 0)
;; ((parent-is "text_interpolation") php-ts-mode--parent-html-heuristic 0)
((or (parent-is "program")
(parent-is "text_interpolation"))
php-ts-mode--parent-html-heuristic 0)
@ -709,12 +728,14 @@ characters of the current line."
(parent-is "use_list"))
parent-bol php-ts-mode-indent-offset)
((parent-is "function_definition") parent-bol 0)
((parent-is "member_call_expression") first-sibling php-ts-mode-indent-offset)
((parent-is "member_call_expression") parent-bol php-ts-mode-indent-offset)
((parent-is "conditional_expression") parent-bol php-ts-mode-indent-offset)
((parent-is "assignment_expression") parent-bol php-ts-mode-indent-offset)
((parent-is "array_creation_expression") parent-bol php-ts-mode-indent-offset)
((parent-is "attribute_group") parent-bol php-ts-mode-indent-offset)
((parent-is "parenthesized_expression") first-sibling 1)
((node-is "|>") php-ts-mode--pipe-heuristic 0)
((parent-is "binary_expression") parent 0)
((or (parent-is "arguments")
(parent-is "formal_parameters"))
@ -742,6 +763,7 @@ characters of the current line."
((parent-is "declaration_list") column-0 php-ts-mode-indent-offset)
((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset)
((parent-is "property_hook_list") parent-bol php-ts-mode-indent-offset)
;; Statement in {} blocks.
((or (and (or (parent-is "compound_statement")
@ -827,24 +849,66 @@ characters of the current line."
;;; Font-lock
(defconst php-ts-mode--keywords
'("abstract" "and" "array" "as" "break" "callable" "case" "catch"
"class" "clone" "const" "continue" "declare" "default" "do" "echo"
"else" "elseif" "enddeclare" "endfor" "endforeach" "endif"
"endswitch" "endwhile" "enum" "exit" "extends" "final" "finally" "fn"
"for" "foreach" "from" "function" "global" "goto" "if" "implements"
"include" "include_once" "instanceof" "insteadof" "interface"
"list" "match" "namespace" "new" "null" "or" "print" "private"
"protected" "public" "readonly" "require" "require_once" "return"
"static" "switch" "throw" "trait" "try" "unset" "use" "while" "xor"
"yield")
(defun php-ts-mode--test-namespace-name-as-prefix-p ()
"Return t if namespace_name_as_prefix is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_name_as_prefix)"))
(defun php-ts-mode--test-namespace-aliasing-clause-p ()
"Return t if namespace_aliasing_clause is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_aliasing_clause)"))
(defun php-ts-mode--test-namespace-use-group-clause-p ()
"Return t if namespace_use_group_clause is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_use_group_clause)"))
(defun php-ts-mode--test-visibility-modifier-operation-p ()
"Return t if (visibility_modifier (operation)) is defined, nil otherwise."
(treesit-query-valid-p 'php "(visibility_modifier (operation))"))
(defun php-ts-mode--test-property-hook-p ()
"Return t if property_hook is a named node, nil otherwise."
(treesit-query-valid-p 'php "(property_hook)"))
(defun php-ts-mode--test-relative-name-p ()
"Return t if relative_name is a named node, nil otherwise."
(treesit-query-valid-p 'php "(relative_name)"))
(defun php-ts-mode--test-php-end-tag-p ()
"Return t if php_end_tag is a named node, nil otherwise."
(treesit-query-valid-p 'php "(php_end_tag)"))
(defun php-ts-mode--test-yield-from-p ()
"Return t if the keyword `yield from' is defined, nil otherwise."
(treesit-query-valid-p 'php '("yield from")))
(defun php-ts-mode--test-pipe-p ()
"Return t if the operator '|>' is defined, nil otherwise."
(treesit-query-valid-p 'php '("|>")))
(defvar php-ts-mode--keywords
(when (treesit-available-p)
(append
'("abstract" "and" "array" "as" "break" "case" "catch"
"class" "clone" "const" "continue" "declare" "default" "do" "echo"
"else" "elseif" "enddeclare" "endfor" "endforeach" "endif"
"endswitch" "endwhile" "enum" "exit" "extends" "final" "finally" "fn"
"for" "foreach" "function" "global" "goto" "if" "implements"
"include" "include_once" "instanceof" "insteadof" "interface"
"list" "match" "namespace" "new" "null" "or" "print" "private"
"protected" "public" "readonly" "require" "require_once" "return"
"static" "switch" "throw" "trait" "try" "unset" "use" "while" "xor"
"yield")
(if (php-ts-mode--test-yield-from-p) '("yield from") '("from"))))
"PHP keywords for tree-sitter font-locking.")
(defconst php-ts-mode--operators
'("--" "**=" "*=" "/=" "%=" "+=" "-=" ".=" "<<=" ">>=" "&=" "^="
"|=" "??" "??=" "||" "&&" "|" "^" "&" "==" "!=" "<>" "===" "!=="
"<" ">" "<=" ">=" "<=>" "<<" ">>" "+" "-" "." "*" "**" "/" "%"
"->" "?->" "...")
(defvar php-ts-mode--operators
(when (treesit-available-p)
(append
'("--" "**=" "*=" "/=" "%=" "+=" "-=" ".=" "<<=" ">>=" "&=" "^="
"|=" "??" "??=" "||" "&&" "|" "^" "&" "==" "!=" "<>" "===" "!=="
"<" ">" "<=" ">=" "<=>" "<<" ">>" "+" "-" "." "*" "**" "/" "%"
"->" "?->" "...")
(when (php-ts-mode--test-pipe-p) '("|>"))))
"PHP operators for tree-sitter font-locking.")
(defconst php-ts-mode--predefined-constant
@ -890,30 +954,6 @@ characters of the current line."
("::" . ?∷))
"Value for `prettify-symbols-alist' in `php-ts-mode'.")
(defun php-ts-mode--test-namespace-name-as-prefix-p ()
"Return t if namespace_name_as_prefix is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_name_as_prefix)"))
(defun php-ts-mode--test-namespace-aliasing-clause-p ()
"Return t if namespace_aliasing_clause is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_aliasing_clause)"))
(defun php-ts-mode--test-namespace-use-group-clause-p ()
"Return t if namespace_use_group_clause is a named node, nil otherwise."
(treesit-query-valid-p 'php "(namespace_use_group_clause)"))
(defun php-ts-mode--test-visibility-modifier-operation-p ()
"Return t if (visibility_modifier (operation)) is defined, nil otherwise."
(treesit-query-valid-p 'php "(visibility_modifier (operation))"))
(defun php-ts-mode--test-property-hook-p ()
"Return t if property_hook is a named node, nil otherwise."
(treesit-query-valid-p 'php "(property_hook)"))
(defun php-ts-mode--test-relative-name-p ()
"Return t if relative_name is a named node, nil otherwise."
(treesit-query-valid-p 'php "(relative_name)"))
(defun php-ts-mode--font-lock-settings ()
"Tree-sitter font-lock settings."
(treesit-font-lock-rules
@ -974,6 +1014,7 @@ characters of the current line."
name: (_) @font-lock-variable-name-face)
(scoped_property_access_expression
scope: (name) @font-lock-constant-face)
(nullsafe_member_access_expression (name) @font-lock-variable-name-face)
(error_suppression_expression (name) @font-lock-property-name-face))
:language 'php
@ -1002,6 +1043,8 @@ characters of the current line."
(union_type) @font-lock-type-face
(bottom_type) @font-lock-type-face
(primitive_type) @font-lock-type-face
((primitive_type) @font-lock-keyword-face
(:equal "callable" @font-lock-keyword-face))
(cast_type) @font-lock-type-face
(named_type) @font-lock-type-face
(optional_type) @font-lock-type-face)
@ -1010,7 +1053,9 @@ characters of the current line."
:feature 'definition
:override t
`((php_tag) @font-lock-preprocessor-face
("?>") @font-lock-preprocessor-face
,@(if (php-ts-mode--test-php-end-tag-p)
'((php_end_tag) @font-lock-preprocessor-face)
'(("?>") @font-lock-preprocessor-face))
;; Highlights identifiers in declarations.
(class_declaration
name: (_) @font-lock-type-face)
@ -1067,9 +1112,9 @@ characters of the current line."
'((function_call_expression
function: (name) @font-lock-function-call-face)
(scoped_call_expression
name: (_) @font-lock-function-call-face)
name: (name) @font-lock-function-call-face)
(member_call_expression
name: (_) @font-lock-function-call-face)
name: (name) @font-lock-function-call-face)
(nullsafe_member_call_expression
name: (_) @font-lock-function-call-face))
@ -1792,9 +1837,6 @@ The optional TYPE can be the symbol \"port\", \"hostname\", \"document-root\",
;;; Inferior PHP process.
(defvar php-ts-mode--inferior-php-process nil
"The PHP inferior process associated to `php-ts-mode-inferior-php-buffer'.")
;;;###autoload
(defun run-php (&optional cmd config)
"Run an PHP interpreter as a inferior process.
@ -1829,8 +1871,7 @@ Optional CONFIG, if supplied, is the php.ini file to use."
"Start an inferior PHP process with command CMD and init file CONFIG.
CMD is the command to run. Optional CONFIG, if supplied, is the php.ini
file to use."
(setq-local php-ts-mode--inferior-php-process
(apply #'make-comint-in-buffer
(apply #'make-comint-in-buffer
(string-replace "*" "" php-ts-mode-inferior-php-buffer)
php-ts-mode-inferior-php-buffer
cmd
@ -1840,7 +1881,7 @@ file to use."
(list
(when config
(format "-c %s" config))
"-a"))))
"-a")))
(add-hook 'comint-preoutput-filter-functions
(lambda (string)
(let ((prompt (concat php-ts-mode--inferior-prompt " ")))
@ -1865,7 +1906,7 @@ file to use."
nil t)
(when php-ts-mode-inferior-history
(set-process-sentinel
(get-buffer-process php-ts-mode-inferior-php-buffer)
(get-buffer-process php-ts-mode-inferior-php-buffer)
'php-ts-mode-inferior--write-history)))
;; taken and adapted from lua-ts-mode
@ -1880,14 +1921,15 @@ file to use."
(defun php-ts-mode-send-region (beg end)
"Send region between BEG and END to the inferior PHP process."
(interactive "r")
(if (buffer-live-p php-ts-mode--inferior-php-process)
(if-let* ((php-process
(get-buffer-process php-ts-mode-inferior-php-buffer)))
(progn
(php-ts-mode-show-process-buffer)
(comint-send-string php-ts-mode--inferior-php-process "\n")
(comint-send-string php-process "\n")
(comint-send-string
php-ts-mode--inferior-php-process
php-process
(buffer-substring-no-properties beg end))
(comint-send-string php-ts-mode--inferior-php-process "\n"))
(comint-send-string php-process "\n"))
(message "Invoke run-php first!")))
(defun php-ts-mode-send-buffer ()