diff --git a/lisp/progmodes/php-ts-mode.el b/lisp/progmodes/php-ts-mode.el index e23293bdd3b..3c9cb2ba8d0 100644 --- a/lisp/progmodes/php-ts-mode.el +++ b/lisp/progmodes/php-ts-mode.el @@ -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 "" (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 ()