mirror of
https://git.savannah.gnu.org/git/emacs.git
synced 2024-11-21 06:55:39 +00:00
Fix 'php-ts-mode': better indentation and font locking
Incomplete compound_statement or colon_block (statement-group without a closing brace or closing keyword) that are not inside a function or method are not recognized as such by tree-sitter-php. A new function 'php-ts-mode--open-statement-group-heuristic' handles this case. Font locking of magic methods and better support for alternative control structure syntax. Support for latest grammar version. * lisp/progmodes/php-ts-mode.el (php-ts-mode--language-source-alist): Updated grammar version. (php-ts-mode--possibly-braceless-keyword-re): Regular expression for braceless keyword. (php-ts-mode--open-statement-group-heuristic): New function. (php-ts-mode--parent-html-bol): Use the new function and doc fix. (php-ts-mode--parent-html-heuristic): Use the new function and doc fix. (php-ts-mode--indent-styles): Use the new function and add 'colon_block' support. (php-ts-mode--class-magic-methods): New predefined magic methods list. (php-ts-mode--test-namespace-name-as-prefix-p): Doc fix. (php-ts-mode--test-namespace-aliasing-clause-p): Fix the test and doc. (php-ts-mode--test-namespace-use-group-clause-p): Doc fix. (php-ts-mode--test-visibility-modifier-operation-clause-p): New function for the new asymmetric property visibility feature of PHP 8.4. (php-ts-mode--font-lock-settings): Font lock for class magic methods and alternative syntax. Better font lock for 'instanceof'. Use 'font-lock-function-call-face' for scoped and member call expression. (bug#73779)
This commit is contained in:
parent
902029b141
commit
4e0cb960db
@ -84,7 +84,7 @@
|
||||
|
||||
;;; Install treesitter language parsers
|
||||
(defvar php-ts-mode--language-source-alist
|
||||
'((php . ("https://github.com/tree-sitter/tree-sitter-php" "v0.23.0" "php/src"))
|
||||
'((php . ("https://github.com/tree-sitter/tree-sitter-php" "v0.23.4" "php/src"))
|
||||
(phpdoc . ("https://github.com/claytonrcarter/tree-sitter-phpdoc"))
|
||||
(html . ("https://github.com/tree-sitter/tree-sitter-html" "v0.23.0"))
|
||||
(javascript . ("https://github.com/tree-sitter/tree-sitter-javascript" "v0.23.0"))
|
||||
@ -428,6 +428,27 @@ Useful for debugging."
|
||||
|
||||
;;; Indent
|
||||
|
||||
(defconst php-ts-mode--possibly-braceless-keyword-re
|
||||
(regexp-opt '("if" "for" "foreach" "while" "do") 'symbols)
|
||||
"Regexp matching keywords optionally followed by an opening brace.")
|
||||
|
||||
(defun php-ts-mode--open-statement-group-heuristic (node _parent bol &rest _)
|
||||
"Heuristic matcher for statement-group without closing bracket.
|
||||
|
||||
Return `php-ts-mode-indent-offset' plus 1 when BOL is after
|
||||
`php-ts-mode--possibly-braceless-keyword-re', otherwise return 0. It's
|
||||
usefull for matching incomplete compound_statement or colon_block.
|
||||
PARENT is NODE's parent, BOL is the beginning of non-whitespace
|
||||
characters of the current line."
|
||||
(and (null node)
|
||||
(save-excursion
|
||||
(forward-line -1)
|
||||
(if (re-search-forward
|
||||
php-ts-mode--possibly-braceless-keyword-re
|
||||
bol t)
|
||||
(+ 1 php-ts-mode-indent-offset)
|
||||
0))))
|
||||
|
||||
;; taken from c-ts-mode
|
||||
(defun php-ts-mode--else-heuristic (node parent bol &rest _)
|
||||
"Heuristic matcher for when \"else\" is followed by a closing bracket.
|
||||
@ -475,43 +496,50 @@ NODE is the node to match and PARENT is its parent."
|
||||
(goto-char (treesit-node-start parent))
|
||||
(line-end-position)))
|
||||
|
||||
(defun php-ts-mode--parent-html-bol (node parent _bol &rest _)
|
||||
(defun php-ts-mode--parent-html-bol (node parent bol &rest _)
|
||||
"Find the first non-space characters of the HTML tags before NODE.
|
||||
|
||||
When NODE is nil call `php-ts-mode--open-statement-group-heuristic'.
|
||||
PARENT is NODE's parent, BOL is the beginning of non-whitespace
|
||||
characters of the current line."
|
||||
(save-excursion
|
||||
(let ((html-node (treesit-search-forward node "text" t)))
|
||||
(if html-node
|
||||
(let ((end-html (treesit-node-end html-node)))
|
||||
(goto-char end-html)
|
||||
(backward-word)
|
||||
(back-to-indentation)
|
||||
(point))
|
||||
(treesit-node-start parent)))))
|
||||
(if (null node)
|
||||
;; If NODE is nil it could be an open statement-group.
|
||||
(php-ts-mode--open-statement-group-heuristic node parent bol)
|
||||
(save-excursion
|
||||
(let ((html-node (treesit-search-forward node "text" t)))
|
||||
(if html-node
|
||||
(let ((end-html (treesit-node-end html-node)))
|
||||
(goto-char end-html)
|
||||
(backward-word)
|
||||
(back-to-indentation)
|
||||
(point))
|
||||
(treesit-node-start parent))))))
|
||||
|
||||
(defun php-ts-mode--parent-html-heuristic (node parent _bol &rest _)
|
||||
(defun php-ts-mode--parent-html-heuristic (node parent bol &rest _)
|
||||
"Return position based on html indentation.
|
||||
|
||||
Returns 0 if the NODE is after the </html>, otherwise returns the
|
||||
indentation point of the last word before the NODE, plus the
|
||||
indentation offset. If there is no HTML tag, it returns the beginning
|
||||
of the parent.
|
||||
indentation point of the last word before the NODE, plus the indentation
|
||||
offset. If there is no HTML tag, it returns the beginning of the
|
||||
parent. When NODE is nil call `php-ts-mode--open-statement-group-heuristic'.
|
||||
It can be used when you want to indent PHP code relative to the HTML.
|
||||
PARENT is NODE's parent, BOL is the beginning of non-whitespace
|
||||
characters of the current line."
|
||||
(let ((html-node (treesit-search-forward node "text" t)))
|
||||
(if html-node
|
||||
(let ((end-html (treesit-node-end html-node)))
|
||||
(save-excursion
|
||||
(goto-char end-html)
|
||||
(backward-word)
|
||||
(back-to-indentation)
|
||||
(if (search-forward "</html>" end-html t 1)
|
||||
0
|
||||
(+ (point) php-ts-mode-indent-offset))))
|
||||
;; Maybe it's better to use bol?
|
||||
(treesit-node-start parent))))
|
||||
(if (null node)
|
||||
;; If NODE is nil it could be an open statement-group.
|
||||
(php-ts-mode--open-statement-group-heuristic node parent bol)
|
||||
(let ((html-node (treesit-search-forward node "text" t)))
|
||||
(if html-node
|
||||
(let ((end-html (treesit-node-end html-node)))
|
||||
(save-excursion
|
||||
(goto-char end-html)
|
||||
(backward-word)
|
||||
(back-to-indentation)
|
||||
(if (search-forward "</html>" end-html t 1)
|
||||
0
|
||||
(+ (point) php-ts-mode-indent-offset))))
|
||||
;; Maybe it's better to use bol?
|
||||
(treesit-node-start parent)))))
|
||||
|
||||
(defun php-ts-mode--array-element-heuristic (_node parent _bol &rest _)
|
||||
"Return of the position of the first element of the array.
|
||||
@ -648,16 +676,22 @@ characters of the current line."
|
||||
((parent-is "initializer_list") parent-bol php-ts-mode-indent-offset)
|
||||
|
||||
;; Statement in {} blocks.
|
||||
((or (and (parent-is "compound_statement")
|
||||
((or (and (or (parent-is "compound_statement")
|
||||
(parent-is "colon_block"))
|
||||
;; If the previous sibling(s) are not on their
|
||||
;; own line, indent as if this node is the first
|
||||
;; sibling
|
||||
php-ts-mode--first-sibling)
|
||||
(match null "compound_statement"))
|
||||
(or (match null "compound_statement")
|
||||
(match null "colon_block")))
|
||||
standalone-parent php-ts-mode-indent-offset)
|
||||
((parent-is "compound_statement") parent-bol php-ts-mode-indent-offset)
|
||||
((or (parent-is "compound_statement")
|
||||
(parent-is "colon_block"))
|
||||
parent-bol php-ts-mode-indent-offset)
|
||||
;; Opening bracket.
|
||||
((node-is "compound_statement") standalone-parent php-ts-mode-indent-offset)
|
||||
((or (node-is "compound_statement")
|
||||
(node-is "colon_block"))
|
||||
standalone-parent php-ts-mode-indent-offset)
|
||||
|
||||
((parent-is "match_block") parent-bol php-ts-mode-indent-offset)
|
||||
((parent-is "switch_block") parent-bol 0)
|
||||
@ -667,6 +701,7 @@ characters of the current line."
|
||||
;; rule for PHP alternative syntax
|
||||
((or (node-is "else_if_clause")
|
||||
(node-is "endif")
|
||||
(node-is "endfor")
|
||||
(node-is "endforeach")
|
||||
(node-is "endwhile"))
|
||||
parent-bol 0)
|
||||
@ -679,9 +714,13 @@ characters of the current line."
|
||||
(parent-is "switch_statement")
|
||||
(parent-is "case_statement")
|
||||
(parent-is "empty_statement"))
|
||||
parent-bol php-ts-mode-indent-offset))))
|
||||
parent-bol php-ts-mode-indent-offset)
|
||||
|
||||
;; Workaround: handle "for" open statement group. Currently
|
||||
;; the grammar handles it differently than other control structures.
|
||||
(no-node php-ts-mode--open-statement-group-heuristic 0))))
|
||||
`((psr2
|
||||
((parent-is "program") parent-bol 0)
|
||||
((parent-is "program") php-ts-mode--open-statement-group-heuristic 0)
|
||||
((parent-is "text_interpolation") column-0 0)
|
||||
((parent-is "function_call_expression") parent-bol php-ts-mode-indent-offset)
|
||||
,@common)
|
||||
@ -774,21 +813,32 @@ characters of the current line."
|
||||
"__FUNCTION__" "__LINE__" "__METHOD__" "__NAMESPACE__" "__TRAIT__")
|
||||
"PHP predefined constant.")
|
||||
|
||||
(defun php-ts-mode--test-namespace-name-as-prefix-p ()
|
||||
"Return t if namespace_name_as_prefix keyword is a named node, nil otherwise."
|
||||
(defconst php-ts-mode--class-magic-methods
|
||||
'("__construct" "__destruct" "__call" "__callStatic" "__get" "__set"
|
||||
"__isset" "__unset" "__sleep" "__wakeup" "__serialize" "__unserialize"
|
||||
"__toString" "__invoke" "__set_state" "__clone" "__debugInfo")
|
||||
"PHP predefined magic methods.")
|
||||
|
||||
(defun php-ts-mode--test-namespace-name-as-prefix-p ()
|
||||
"Return t if namespace_name_as_prefix is a named node, nil otherwise."
|
||||
(ignore-errors
|
||||
(progn (treesit-query-compile 'php "(namespace_name_as_prefix)" t) t)))
|
||||
|
||||
(defun php-ts-mode--test-namespace-aliasing-clause-p ()
|
||||
"Return t if namespace_name_as_prefix keyword is named node, nil otherwise."
|
||||
(defun php-ts-mode--test-namespace-aliasing-clause-p ()
|
||||
"Return t if namespace_aliasing_clause is a named node, nil otherwise."
|
||||
(ignore-errors
|
||||
(progn (treesit-query-compile 'php "(namespace_name_as_prefix)" t) t)))
|
||||
(progn (treesit-query-compile 'php "(namespace_aliasing_clause)" t) t)))
|
||||
|
||||
(defun php-ts-mode--test-namespace-use-group-clause-p ()
|
||||
"Return t if namespace_use_group_clause keyword is named node, nil otherwise."
|
||||
"Return t if namespace_use_group_clause is a named node, nil otherwise."
|
||||
(ignore-errors
|
||||
(progn (treesit-query-compile 'php "(namespace_use_group_clause)" t) t)))
|
||||
|
||||
(defun php-ts-mode--test-visibility-modifier-operation-clause-p ()
|
||||
"Return t if (visibility_modifier (operation)) is defined, nil otherwise."
|
||||
(ignore-errors
|
||||
(progn (treesit-query-compile 'php "(visibility_modifier (operation))" t) t)))
|
||||
|
||||
(defun php-ts-mode--font-lock-settings ()
|
||||
"Tree-sitter font-lock settings."
|
||||
(treesit-font-lock-rules
|
||||
@ -796,7 +846,10 @@ characters of the current line."
|
||||
:language 'php
|
||||
:feature 'keyword
|
||||
:override t
|
||||
`([,@php-ts-mode--keywords] @font-lock-keyword-face)
|
||||
`([,@php-ts-mode--keywords] @font-lock-keyword-face
|
||||
,@(when (php-ts-mode--test-visibility-modifier-operation-clause-p)
|
||||
'((visibility_modifier (operation) @font-lock-builtin-face)))
|
||||
(var_modifier) @font-lock-builtin-face)
|
||||
|
||||
:language 'php
|
||||
:feature 'comment
|
||||
@ -826,7 +879,6 @@ characters of the current line."
|
||||
(named_label_statement (name) @font-lock-constant-face))
|
||||
|
||||
:language 'php
|
||||
;;:override t
|
||||
:feature 'delimiter
|
||||
`((["," ":" ";" "\\"]) @font-lock-delimiter-face)
|
||||
|
||||
@ -850,7 +902,6 @@ characters of the current line."
|
||||
|
||||
:language 'php
|
||||
:feature 'string
|
||||
;;:override t
|
||||
`(("\"") @font-lock-string-face
|
||||
(encapsed_string) @font-lock-string-face
|
||||
(string_content) @font-lock-string-face
|
||||
@ -892,32 +943,37 @@ characters of the current line."
|
||||
name: (_) @font-lock-type-face)
|
||||
(trait_declaration
|
||||
name: (_) @font-lock-type-face)
|
||||
(property_declaration
|
||||
(visibility_modifier) @font-lock-keyword-face)
|
||||
(property_declaration
|
||||
(var_modifier) @font-lock-keyword-face)
|
||||
(enum_declaration
|
||||
name: (_) @font-lock-type-face)
|
||||
(function_definition
|
||||
name: (_) @font-lock-function-name-face)
|
||||
(method_declaration
|
||||
name: (_) @font-lock-function-name-face)
|
||||
(method_declaration
|
||||
name: (name) @font-lock-builtin-face
|
||||
(:match ,(rx-to-string
|
||||
`(: bos (or ,@php-ts-mode--class-magic-methods) eos))
|
||||
@font-lock-builtin-face))
|
||||
("=>") @font-lock-keyword-face
|
||||
(object_creation_expression
|
||||
(name) @font-lock-type-face)
|
||||
,@(when (php-ts-mode--test-namespace-name-as-prefix-p)
|
||||
'((namespace_name_as_prefix "\\" @font-lock-delimiter-face)
|
||||
(namespace_name_as_prefix
|
||||
(namespace_name (name)) @font-lock-type-face)))
|
||||
'((namespace_name_as_prefix "\\" @font-lock-delimiter-face)
|
||||
(namespace_name_as_prefix
|
||||
(namespace_name (name)) @font-lock-type-face)))
|
||||
,@(if (php-ts-mode--test-namespace-aliasing-clause-p)
|
||||
'((namespace_aliasing_clause (name) @font-lock-type-face))
|
||||
'((namespace_use_clause alias: (name) @font-lock-type-face)))
|
||||
'((namespace_aliasing_clause (name) @font-lock-type-face))
|
||||
'((namespace_use_clause alias: (name) @font-lock-type-face)))
|
||||
,@(when (not (php-ts-mode--test-namespace-use-group-clause-p))
|
||||
'((namespace_use_group
|
||||
(namespace_use_clause (name) @font-lock-type-face))))
|
||||
'((namespace_use_group
|
||||
(namespace_use_clause (name) @font-lock-type-face))))
|
||||
(namespace_name "\\" @font-lock-delimiter-face)
|
||||
(namespace_name (name) @font-lock-type-face)
|
||||
(use_declaration (name) @font-lock-property-use-face))
|
||||
(use_declaration (name) @font-lock-property-use-face)
|
||||
(use_instead_of_clause (name) @font-lock-type-face)
|
||||
(binary_expression
|
||||
operator: "instanceof"
|
||||
right: (name) @font-lock-type-face))
|
||||
|
||||
:language 'php
|
||||
:feature 'function-scope
|
||||
@ -932,9 +988,9 @@ characters of the current line."
|
||||
'((function_call_expression
|
||||
function: (name) @font-lock-function-call-face)
|
||||
(scoped_call_expression
|
||||
name: (_) @font-lock-function-name-face)
|
||||
name: (_) @font-lock-function-call-face)
|
||||
(member_call_expression
|
||||
name: (_) @font-lock-function-name-face)
|
||||
name: (_) @font-lock-function-call-face)
|
||||
(nullsafe_member_call_expression
|
||||
name: (_) @font-lock-constant-face))
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user