emacs-elpa-diffs
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[elpa] externals/phpinspect 223ca2bf54 12/30: Implement use of traits in


From: ELPA Syncer
Subject: [elpa] externals/phpinspect 223ca2bf54 12/30: Implement use of traits in live-edited buffers
Date: Sat, 31 Aug 2024 09:58:53 -0400 (EDT)

branch: externals/phpinspect
commit 223ca2bf542f09e6e10d20c76d9db48dd90b4508
Author: Hugo Thunnissen <devel@hugot.nl>
Commit: Hugo Thunnissen <devel@hugot.nl>

    Implement use of traits in live-edited buffers
---
 phpinspect-bmap.el             |  8 +++-
 phpinspect-buffer.el           | 93 ++++++++++++++++++++++++++++++++++++++++--
 phpinspect-method-cell.el      |  2 +-
 phpinspect-parser.el           | 30 ++++++++++----
 phpinspect-token-predicates.el | 10 ++++-
 phpinspect-typedef.el          |  5 ++-
 test/phpinspect-test-env.el    | 40 ++++++++++++++++++
 test/test-buffer.el            | 44 ++++++++++++++++++++
 test/test-parser.el            |  8 ++--
 test/test-project.el           | 43 -------------------
 10 files changed, 221 insertions(+), 62 deletions(-)

diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el
index b8bfd7d0a0..953d6d4825 100644
--- a/phpinspect-bmap.el
+++ b/phpinspect-bmap.el
@@ -59,6 +59,9 @@
   (imports (phpinspect-make-splayt)
            :type phpinspect-splayt
            :documentation "The import statements encountered.")
+  (used-traits (phpinspect-make-splayt)
+               :type phpinspect-splayt
+               :documentation "The trait use statements encountered.")
   (functions (phpinspect-make-splayt)
              :type phpinspect-splayt
              :documentation "The function definitions encountered.")
@@ -146,9 +149,12 @@
     (puthash start token-meta starts)
 
     (cond
-     ((phpinspect-use-p (phpinspect-meta-token token-meta))
+     ((phpinspect-use-import-p (phpinspect-meta-token token-meta))
       (phpinspect-splayt-insert
        (phpinspect-bmap-imports bmap) (phpinspect-meta-start token-meta) 
token-meta))
+     ((phpinspect-use-trait-p (phpinspect-meta-token token-meta))
+      (phpinspect-splayt-insert
+       (phpinspect-bmap-used-traits bmap) (phpinspect-meta-start token-meta) 
token-meta))
      ((phpinspect-class-p (phpinspect-meta-token token-meta))
       (phpinspect-splayt-insert
        (phpinspect-bmap-classes bmap) (phpinspect-meta-start token-meta) 
token-meta))
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index 2dafc97e59..e7bb71091d 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -54,6 +54,8 @@ emacs buffer."
   (-last-indexed-bmap nil)
   (imports nil
            :type phpinspect-toc)
+  (used-traits nil
+               :type phpinspect-toc)
   (namespaces nil
               :type phpinspect-toc)
   (classes nil
@@ -185,6 +187,74 @@ linked with."
                     point)))
     (and namespace (phpinspect-meta-overlaps-point namespace point) 
namespace)))
 
+(defun phpinspect-buffer-get-type-resolver-for-class (buffer class-token)
+  (pcase-let* ((`(,imports ,namespace-name)
+                (phpinspect-get-token-index-context
+                 (phpinspect-buffer-namespaces buffer)
+                 (phpinspect-buffer-imports buffer)
+                 class-token)))
+    (phpinspect--make-type-resolver
+     imports
+     (phpinspect-class-block (phpinspect-meta-token class-token))
+     namespace-name)))
+
+(defun phpinspect-buffer-apply-use-trait-config (buffer class-token uses)
+  (pcase-let* ((`(,imports ,namespace-name)
+                (phpinspect-get-token-index-context
+                 (phpinspect-buffer-namespaces buffer)
+                 (phpinspect-buffer-imports buffer)
+                 class-token))
+               (type-resolver (phpinspect--make-type-resolver
+                               imports
+                               (phpinspect-class-block (phpinspect-meta-token 
class-token))
+                               namespace-name))
+               (config))
+
+    (dolist (use uses)
+      (setq config
+            (nconc config
+                   (phpinspect--index-trait-use
+                    (phpinspect-meta-token use) type-resolver nil))))
+
+    (when-let ((typedef (phpinspect-buffer-get-index-for-token buffer 
(phpinspect-meta-token class-token))))
+      (let ((new-extensions (seq-uniq (append 
(phpi-typedef-subscribed-to-types typedef)
+                                              (phpi-typedef-set-trait-config 
typedef config))
+                                      #'phpinspect--type=)))
+        (phpi-typedef-update-extensions typedef new-extensions)))))
+
+(cl-defmethod phpinspect-buffer-index-used-traits ((buffer phpinspect-buffer) 
(uses (head phpinspect-splayt)))
+  (let ((update t))
+    (if (phpinspect-buffer-used-traits buffer)
+        (pcase-let ((`(,new ,deleted)
+                     (phpinspect-toc-update
+                      (phpinspect-buffer-used-traits buffer) uses 
(phpinspect-buffer-root-meta buffer))))
+          (unless (or new deleted)
+            ;; Nothing changed, don't update
+            (setq update nil)))
+      (setf (phpinspect-buffer-used-traits buffer) (phpinspect-make-toc uses)))
+
+    (when update
+      (let ((class-toc (phpinspect-buffer-classes buffer)))
+        (phpinspect-splayt-traverse-lr (class (phpinspect-toc-tree class-toc))
+          (let ((config 
(phpinspect-buffer-get-trait-configuration-between-points
+                         buffer (phpinspect-meta-start class) 
(phpinspect-meta-end class)
+                         (phpinspect-buffer-get-type-resolver-for-class buffer 
class))))
+
+            (when-let ((typedef (phpinspect-buffer-get-index-for-token buffer 
(phpinspect-meta-token class))))
+              (let ((new-extensions (seq-uniq (append
+                                               (phpi-typedef-set-trait-config 
typedef config)
+                                               
(phpi-typedef-subscribed-to-types typedef))
+                                              #'phpinspect--type=)))
+                (phpi-typedef-update-extensions typedef new-extensions)))))))))
+
+(defun phpinspect-buffer-get-trait-configuration-between-points (buffer start 
end type-resolver)
+  (let ((uses (phpinspect-toc-tokens-in-region (phpinspect-buffer-used-traits 
buffer) start end))
+        config)
+    (dolist (use uses)
+      (setq config (nconc config (phpinspect--index-trait-use 
(phpinspect-meta-token use) type-resolver nil))))
+
+    config))
+
 (cl-defmethod phpinspect-buffer-index-imports ((buffer phpinspect-buffer) 
(imports (head phpinspect-splayt)))
   (let (to-be-indexed)
     (if (phpinspect-buffer-imports buffer)
@@ -224,6 +294,14 @@ linked with."
 
     (list imports namespace-name)))
 
+(defun phpinspect--buffer-update-type-declaration (buffer typedef declaration 
class-token imports namespace-name)
+  (phpi-typedef-update-declaration
+   typedef declaration imports namespace-name
+   (phpinspect-buffer-get-trait-configuration-between-points
+    buffer (phpinspect-meta-start class-token) (phpinspect-meta-end 
class-token)
+    (phpinspect--make-type-resolver
+     imports (phpinspect-class-block (phpinspect-meta-token class-token)) 
namespace-name))))
+
 (cl-defmethod phpinspect-buffer-index-classes ((buffer phpinspect-buffer) 
(classes (head phpinspect-splayt)))
   (let ((declarations (phpinspect-buffer-declarations buffer))
         (namespaces (phpinspect-buffer-namespaces buffer))
@@ -234,10 +312,12 @@ linked with."
                                                         
(phpinspect-buffer-classes buffer)
                                                         classes 
(phpinspect-buffer-root-meta buffer)))
                      (new-declarations) (declaration) (replaced) (indexed) 
(class))
+          ;; Collect declarations of new classes
           (dolist (class new-classes)
             (when (setq declaration (phpinspect-toc-token-at-or-after-point 
declarations (phpinspect-meta-start class)))
               (push (cons (phpinspect-meta-token declaration) class) 
new-declarations)))
 
+          ;; Delete no longer existing classes from the index
           (dolist (deleted deleted-classes)
             (if (and (setq class (phpinspect-buffer-get-index-for-token
                                   buffer (phpinspect-meta-token deleted)))
@@ -245,14 +325,16 @@ linked with."
                 (pcase-let ((`(,imports ,namespace-name) 
(phpinspect-get-token-index-context namespaces buffer-imports (cdr replaced))))
                   (phpinspect-buffer-update-index-reference-for-token
                    buffer (phpinspect-meta-token deleted) 
(phpinspect-meta-token (cdr replaced)))
-                  (phpi-typedef-update-declaration class (car replaced) 
imports namespace-name nil)
+                  (phpinspect--buffer-update-type-declaration
+                   buffer class (car replaced) (cdr replaced) imports 
namespace-name)
                   (push (cdr replaced) indexed))
               (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deleted))))
 
           (dolist (class new-declarations)
             (unless (memq (cdr class) indexed)
               (let (imports namespace-name class-name class-obj)
-                (pcase-setq `(,imports ,namespace-name) 
(phpinspect-get-token-index-context namespaces buffer-imports (cdr class))
+                (pcase-setq `(,imports ,namespace-name)
+                            (phpinspect-get-token-index-context namespaces 
buffer-imports (cdr class))
                             `(,class-name) (phpinspect--index-class-declaration
                                             (car class)
                                             (phpinspect--make-type-resolver
@@ -262,7 +344,8 @@ linked with."
                 (when class-name
                   (setq  class-obj (phpinspect-project-get-typedef-create 
project class-name 'no-enqueue))
                   (phpinspect-buffer-set-index-reference-for-token buffer 
(phpinspect-meta-token (cdr class)) class-obj)
-                  (phpi-typedef-update-declaration class-obj (car class) 
imports namespace-name nil))))))
+                  (phpinspect--buffer-update-type-declaration
+                   buffer class-obj (car class) (cdr class) imports 
namespace-name))))))
       ;; Else: Index all classes
       (setf (phpinspect-buffer-classes buffer) (phpinspect-make-toc classes))
       (phpinspect-splayt-traverse (class classes)
@@ -278,7 +361,8 @@ linked with."
           (when class-name
             (setq class-obj (phpinspect-project-get-typedef-create project 
class-name 'no-enqueue))
             (phpinspect-buffer-set-index-reference-for-token buffer 
(phpinspect-meta-token class) class-obj)
-            (phpi-typedef-update-declaration class-obj (phpinspect-meta-token 
declaration) imports namespace-name nil)))))))
+            (phpinspect--buffer-update-type-declaration
+             buffer class-obj (phpinspect-meta-token declaration) class 
imports namespace-name)))))))
 
 (cl-defmethod phpinspect-buffer-index-functions ((buffer phpinspect-buffer) 
(functions (head phpinspect-splayt)))
   (let ((classes (phpinspect-buffer-classes buffer))
@@ -485,6 +569,7 @@ continuing execution."
       (let ((map (phpinspect-buffer-map buffer)))
         (unless (eq map (phpinspect-buffer--last-indexed-bmap buffer))
           (phpinspect--log "Updating project index")
+          (phpinspect-buffer-index-used-traits buffer 
(phpinspect-bmap-used-traits map))
           (phpinspect-buffer-index-imports buffer (phpinspect-bmap-imports 
map))
           (phpinspect-buffer-index-declarations buffer 
(phpinspect-bmap-declarations map))
           (phpinspect-buffer-index-namespaces buffer 
(phpinspect-bmap-namespaces map))
diff --git a/phpinspect-method-cell.el b/phpinspect-method-cell.el
index 59c06aa799..d52ef1ef83 100644
--- a/phpinspect-method-cell.el
+++ b/phpinspect-method-cell.el
@@ -162,7 +162,7 @@ Also updates all members of MCOL with the same origin-type."
         ('trait (setf (phpi-mc-trait cell) method))
         ('interface (setf (phpi-mc-interface cell) method))
         ;; class or abstract class
-        (_ (setf (phpi-mc-inherited cell) method))))))
+        (_ (setf (phpi-mc-inherited cell) method) home-type)))))
 
 (defun phpi-mc-get-for-type-category (cell home-type type)
   (if (phpinspect--type= home-type type)
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index d5c7a96d0f..5ef32de767 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -40,14 +40,16 @@
      (progn
        (substring ,string 0 (- (length ,string) 1))))))
 
-(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
+(define-inline phpinspect-munch-token-without-attribs (string token-keyword)
   "Return a token of type TOKEN-KEYWORD with STRING as value.
 If STRING has text properties, they are stripped."
-  (let ((value (copy-sequence string))
-        (length (length string)))
-    (forward-char length)
-    (set-text-properties 0 length nil value)
-    (list token-keyword value)))
+  (inline-letevals (string token-keyword)
+    (inline-quote
+     (let ((value (copy-sequence ,string))
+           (length (length ,string)))
+       (forward-char length)
+       (set-text-properties 0 length nil value)
+       (list ,token-keyword value)))))
 
 (eval-and-compile
   (defun phpinspect-handler-func-name (handler-name)
@@ -602,6 +604,12 @@ nature like argument lists"
   :handlers '(comment word tag block-without-scopes comma terminator)
   :delimiter-predicate #'phpinspect-end-of-use-p)
 
+(phpinspect-defparser use-trait
+  :tree-keyword "use-trait"
+  :handlers '(comment word tag block-without-scopes comma terminator)
+  :delimiter-predicate #'phpinspect-end-of-use-p)
+
+
 (phpinspect-defhandler use-keyword (start-token max-point)
   "Handler for the use keyword and tokens that might follow to give it meaning"
   ((regexp . (concat "use" (phpinspect--word-end-regex))))
@@ -609,6 +617,14 @@ nature like argument lists"
   (forward-char (length start-token))
   (phpinspect--parse-use (current-buffer) max-point))
 
+(phpinspect-defhandler use-trait-keyword (start-token max-point)
+  "Handler for the use keyword within the body of a class."
+  ((regexp . (concat "use" (phpinspect--word-end-regex))))
+  (setq start-token (phpinspect--strip-word-end-space start-token))
+  (forward-char (length start-token))
+  (phpinspect--parse-use-trait (current-buffer) max-point))
+
+
 (phpinspect-defhandler attribute-reference (start-token &rest _ignored)
   "Handler for references to object attributes, or static class attributes."
   ((regexp . "->\\|::"))
@@ -694,7 +710,7 @@ static keywords with the same meaning as in a class block."
   :tree-keyword "block"
   :handlers '(array tag equals list comma string-concatenator 
attribute-reference
                     class-variable assignment-operator whitespace scope-keyword
-                    static-keyword const-keyword use-keyword function-keyword
+                    static-keyword const-keyword use-trait-keyword 
function-keyword
                     word terminator here-doc string comment block))
 
 (phpinspect-defhandler class-block (start-token max-point)
diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el
index 29690f0bcc..837d06cd07 100644
--- a/phpinspect-token-predicates.el
+++ b/phpinspect-token-predicates.el
@@ -246,8 +246,16 @@ Type can be any of the token types returned by
   (or (phpinspect-namespace-p object)
       (phpinspect-root-p object)))
 
+(define-inline phpinspect-use-import-p (token)
+  (inline-quote (phpinspect-token-type-p ,token :use)))
+
+(define-inline phpinspect-use-trait-p (token)
+  (inline-quote (phpinspect-token-type-p ,token :use-trait)))
+
 (define-inline phpinspect-use-p (object)
-  (inline-quote (phpinspect-token-type-p ,object :use)))
+  (inline-quote
+   (or (phpinspect-use-import-p ,object)
+       (phpinspect-use-trait-p ,object))))
 
 (defun phpinspect-comment-p (token)
   (or (phpinspect-token-type-p token :comment)
diff --git a/phpinspect-typedef.el b/phpinspect-typedef.el
index 1a59bc979a..2081483b0f 100644
--- a/phpinspect-typedef.el
+++ b/phpinspect-typedef.el
@@ -254,6 +254,7 @@ Note: this function returns all types found in CONFIG."
 
 This undoes any links and data sharing between this type and any
 extended classes, used traits or implemented interfaces."
+
   (setf (phpi-typedef-subscribed-types foreign-def)
         (cl-remove (phpi-typedef-name def) (phpi-typedef-subscribed-types 
foreign-def)
                    :test #'phpinspect--type=))
@@ -381,7 +382,9 @@ them, which are then incorporated into DEF's properties."
         (phpi-mcol-delete-for-type mcol home-type)
         (phpi-mcol-delete-for-type stmcol home-type)
 
-        ;; FIXME: delete variables
+        ;; FIXME: delete variables. This will require some way to keep track of
+        ;; a variables origin as we would otherwise risk deleting variables 
that
+        ;; originate from extended classes.
         )
 
       (dolist (method (alist-get 'methods index))
diff --git a/test/phpinspect-test-env.el b/test/phpinspect-test-env.el
index 393d0a9fb8..c360bd4991 100644
--- a/test/phpinspect-test-env.el
+++ b/test/phpinspect-test-env.el
@@ -30,6 +30,46 @@
 
     project))
 
+(defun phpinspect--make-dummy-composer-project-with-code ()
+  (let ((fs (phpinspect-make-virtual-fs)))
+    (phpinspect-virtual-fs-set-file
+      fs
+      "/project/root/composer.json"
+      "{ \"autoload\": { \"psr-4\": {\"App\\\\\": [\"src/\", \"lib\"]}}}")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Foo.php"
+      "<?php namespace App; trait Foo { public function do(): static {} public 
static function dont(): Baz {} }")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Baz.php"
+      "<?php namespace App; class Baz { public function amBaz(): bool {} }")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Bar.php"
+      "<?php namespace App; class Bar { use Foo; public function foo(): Foo {} 
}")
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Harry.php"
+      "<?php namespace App; class Harry { public function amBarry(): bool {} 
}")
+
+
+    (phpinspect-virtual-fs-set-file fs
+      "/project/root/src/Barry.php"
+      "<?php namespace App; class Barry { public function getHarry(): Harry {} 
}")
+
+
+    (let* ((project (phpinspect--make-dummy-project fs "/project/root"))
+           (autoload (phpinspect-project-autoload project))
+           result error)
+
+      (phpinspect-autoloader-refresh autoload (lambda (res err)
+                                                (setq result res error err)))
+
+      (while (not (or result error))
+        (thread-yield))
+
+      project)))
 
 (defvar phpinspect-test-php-file-directory
   (expand-file-name "fixtures" phpinspect-test-directory)
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 329703c58e..b50e29a3e7 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -691,3 +691,47 @@ class AAA {
 
     ;; We're just confirming that the above code doesn't error
     (should t))))
+
+(ert-deftest phpinspect-buffer-parse-incrementally-trait-config ()
+  (with-temp-buffer
+    (let* ((project (phpinspect--make-dummy-composer-project-with-code))
+           (buffer (phpinspect-make-buffer :-project project :buffer 
(current-buffer))))
+
+      (insert "<?php
+
+namespace Foo;
+
+class Bar {
+
+    function baz(): string {}
+}")
+
+      ;; Make sure trait typedef is loaded
+      (should (phpinspect-project-get-typedef-extra-or-create
+               project (phpinspect--make-type :name "\\App\\Foo") 'no-enqueue))
+
+      (setq-local phpinspect-current-buffer buffer)
+      (add-hook 'after-change-functions #'phpinspect-after-change-function)
+      (phpinspect-buffer-parse buffer 'no-interrupt)
+      (phpinspect-buffer-update-project-index buffer)
+
+      (let ((class (phpinspect-project-get-typedef
+                    project (phpinspect--make-type :name "\\Foo\\Bar"))))
+
+        (should class)
+        (should (= 1 (length (phpi-typedef-get-methods class))))
+
+        (goto-char 36)
+        (insert "use \\App\\Foo { \\App\\Foo::do as dooo }")
+
+        (phpinspect-buffer-parse buffer 'no-interrupt)
+        (phpinspect-buffer-update-project-index buffer)
+
+        (should (= 2 (length (phpi-typedef-get-methods class))))
+
+        (goto-char 36)
+        (kill-line)
+        (phpinspect-buffer-parse buffer 'no-interrupt)
+        (phpinspect-buffer-update-project-index buffer)
+
+        (should (= 1 (length (phpi-typedef-get-methods class))))))))
diff --git a/test/test-parser.el b/test/test-parser.el
index 6df6e53fab..b3195979e8 100644
--- a/test/test-parser.el
+++ b/test/test-parser.el
@@ -152,8 +152,8 @@ class TestClass {
     (should (equal
              '(:root (:class (:declaration (:word "class") (:word "A"))
                              (:block
-                              (:use (:word "B") (:comma ",") (:word "C")
-                                    (:block
-                                     (:word "C") (:static-attrib (:word "foo"))
-                                     (:word "insteadof") (:word "A"))))))
+                              (:use-trait (:word "B") (:comma ",") (:word "C")
+                                          (:block
+                                           (:word "C") (:static-attrib (:word 
"foo"))
+                                           (:word "insteadof") (:word "A"))))))
             tree))))
diff --git a/test/test-project.el b/test/test-project.el
index 432d153008..31e90340c1 100644
--- a/test/test-project.el
+++ b/test/test-project.el
@@ -25,7 +25,6 @@
 
 (require 'ert)
 (require 'phpinspect-project)
-
 (require 'phpinspect-test-env
          (expand-file-name "phpinspect-test-env.el"
                            (file-name-directory (macroexp-file-name))))
@@ -48,48 +47,6 @@
 
     (should (= 0 (length (hash-table-values (phpinspect-project-file-watchers 
project)))))))
 
-(defun phpinspect--make-dummy-composer-project-with-code ()
-  (let ((fs (phpinspect-make-virtual-fs)))
-    (phpinspect-virtual-fs-set-file
-      fs
-      "/project/root/composer.json"
-      "{ \"autoload\": { \"psr-4\": {\"App\\\\\": [\"src/\", \"lib\"]}}}")
-
-    (phpinspect-virtual-fs-set-file fs
-      "/project/root/src/Foo.php"
-      "<?php namespace App; trait Foo { public function do(): static {} public 
static function dont(): Baz {} }")
-
-    (phpinspect-virtual-fs-set-file fs
-      "/project/root/src/Baz.php"
-      "<?php namespace App; class Baz { public function amBaz(): bool {} }")
-
-    (phpinspect-virtual-fs-set-file fs
-      "/project/root/src/Bar.php"
-      "<?php namespace App; class Bar { use Foo; public function foo(): Foo {} 
}")
-
-    (phpinspect-virtual-fs-set-file fs
-      "/project/root/src/Harry.php"
-      "<?php namespace App; class Harry { public function amBarry(): bool {} 
}")
-
-
-    (phpinspect-virtual-fs-set-file fs
-      "/project/root/src/Barry.php"
-      "<?php namespace App; class Barry { public function getHarry(): Harry {} 
}")
-
-
-    (let* ((project (phpinspect--make-dummy-project fs "/project/root"))
-           (autoload (phpinspect-project-autoload project))
-           result error)
-
-      (phpinspect-autoloader-refresh autoload (lambda (res err)
-                                                (setq result res error err)))
-
-      (while (not (or result error))
-        (thread-yield))
-
-      project)))
-
-
 (ert-deftest phpinspect-project-no-enqueue ()
   (let* ((project (phpinspect--make-dummy-composer-project-with-code))
          (bar (phpinspect-project-get-typedef-extra-or-create



reply via email to

[Prev in Thread] Current Thread [Next in Thread]