branch: externals/phpinspect
commit 6784c90d5665aa6ee16ce4d697bb25e0f7647715
Author: Hugo Thunnissen <de...@hugot.nl>
Commit: Hugo Thunnissen <de...@hugot.nl>

    Shadow buffer in semi usable state
---
 phpinspect-buffer.el           | 332 +++++++++++++++++++++++++++--------------
 phpinspect-completion.el       |  49 +++---
 phpinspect-diagnose.el         |   1 -
 phpinspect-parser.el           |  80 +++++++++-
 phpinspect-pipeline.el         |   2 +-
 phpinspect-token-predicates.el |   9 +-
 phpinspect-util.el             |   6 +-
 test/test-buffer.el            |  58 ++++---
 test/test-parser.el            |   5 +
 9 files changed, 366 insertions(+), 176 deletions(-)

diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index f7a9d1e6b0..7c52a0d100 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -226,7 +226,6 @@ tokens that have been deleted from a buffer."
   (unless (phpinspect-probably-token-p token)
     (error "%s does not seem to be a token" token))
 
-  (phpinspect--log "Deleting index for token %s" token)
   (cond ((phpinspect-class-declaration-p token)
          (when-let ((typedef (gethash token (phpinspect-buffer-token-index 
buffer))))
            (if-let ((name (seq-find #'phpinspect-word-p token))
@@ -237,9 +236,17 @@ tokens that have been deleted from a buffer."
                (progn
                  ;; Declaration has been replaced and class name is unchanged.
                  ;; Update existing typedef.
-                 (phpinspect-buffer--index-class-declaration buffer 
new-declaration)
-                 ;; Delete old index ref
-                 (remhash token (phpinspect-buffer-token-index buffer)))
+                 (if (equal (phpinspect-meta-token new-declaration)
+                            token)
+                     (progn
+                       ;; tokens are equal, just update the index reference
+                       (phpinspect-buffer-update-index-reference-for-token
+                        buffer token (phpinspect-meta-token new-declaration)))
+
+                   ;; Tokens are different, update class declaration
+                   (phpinspect-buffer--index-class-declaration buffer 
new-declaration)
+                   ;; Delete old index ref
+                   (remhash token (phpinspect-buffer-token-index buffer))))
              (progn
                ;; Else: delete index ref AND associated typedef
                (remhash token (phpinspect-buffer-token-index buffer))
@@ -252,20 +259,49 @@ tokens that have been deleted from a buffer."
                               (car var))))
              (phpi-typedef-delete-property class (cdr var)))))
         ((phpinspect-function-p token)
-         (when-let ((func (gethash token (phpinspect-buffer-token-index 
buffer))))
-           (remhash token (phpinspect-buffer-token-index buffer))
-           (cond ((phpinspect-project-p (car func))
-                  (phpinspect-project-delete-function 
(phpinspect-buffer-project buffer) (phpinspect--function-name (cdr func))))
-                 ((phpinspect--type-p (car func))
-                  (when-let ((class (phpinspect-project-get-typedef
-                                     (phpinspect-buffer-project buffer)
-                                     (car func))))
-                    (phpi-typedef-delete-method class (cdr func))))
-                 (t (phpinspect-message "Invalid index location, reindexing 
buffer")
-                    (phpinspect-buffer-reindex buffer)
-                    (error "invalid index location")))))
+         (phpinspect-buffer--delete-function-index-reference buffer token))
         (t (error "Cannot delete index for token %s" token))))
 
+(defun phpinspect-buffer--delete-function-index-reference (buffer token)
+  (message "Deleting func %s" token)
+  (when-let ((func (gethash token (phpinspect-buffer-token-index buffer))))
+    (let ((arg-list (phpinspect-function-argument-list token)))
+      (if-let ((arg-list-meta (phpinspect-buffer-token-meta buffer arg-list))
+               ((eq (phpinspect-buffer-root-meta buffer)
+                    (phpinspect-meta-find-root arg-list-meta)))
+               ;; Arg-list has been adopted into current metadata tree, check
+               ;; if declarations are equal.
+               (new-declaration (phpinspect-meta-find-parent-matching-token
+                                 arg-list-meta #'phpinspect-declaration-p))
+               ((equal (phpinspect-meta-token new-declaration)
+                       (phpinspect-function-declaration token)))
+               ((thread-last (phpinspect-meta-parent new-declaration)
+                             (phpinspect-meta-token)
+                             (phpinspect-function-p))))
+          (progn
+            ;; Declaration is equal, update index reference
+            (message "updating index ref, %s was equal to %s"
+                     (phpinspect-meta-token new-declaration)
+                     (phpinspect-function-declaration token))
+            (phpinspect-buffer-update-index-reference-for-token
+             buffer token (phpinspect-meta-token (phpinspect-meta-parent 
new-declaration))))
+
+        (progn
+          (message "Deleting index")
+          ;; Declaration is not equal, delete index
+          (remhash token (phpinspect-buffer-token-index buffer))
+          (cond ((phpinspect-project-p (car func))
+                 (phpinspect-project-delete-function 
(phpinspect-buffer-project buffer) (phpinspect--function-name (cdr func))))
+                ((phpinspect--type-p (car func))
+                 (message "Getting typedef for %s" (car func))
+                 (when-let ((class (phpinspect-project-get-typedef
+                                    (phpinspect-buffer-project buffer)
+                                    (car func))))
+                   (phpi-typedef-delete-method class (cdr func))))
+                (t (phpinspect-message "Invalid index location, reindexing 
buffer")
+                   (phpinspect-buffer-reindex buffer)
+                   (error "invalid index location"))))))))
+
 (cl-defmethod phpinspect-buffer-reset ((buffer phpinspect-buffer))
   "Clear all metadata stored in BUFFER."
   (phpinspect-buffer--clear-query-cache buffer)
@@ -369,9 +405,10 @@ tokens that have been deleted from a buffer."
         (type-resolver (phpinspect-buffer-get-type-resolver-for-class buffer 
class))
         config)
     (dolist (use uses)
-      (setq config (nconc config
-                          (thread-first (phpinspect-meta-token use)
-                                        (phpinspect--index-trait-use 
type-resolver nil)))))
+      (when-let ((part (thread-first (phpinspect-meta-token use)
+                                     (phpinspect--index-trait-use 
type-resolver nil))))
+        (setq config (nconc config part))))
+
     config))
 
 (defun phpinspect-buffer--index-trait-use (buffer trait-use)
@@ -584,51 +621,53 @@ and a map is available from the previous parse, this is 
used. If
 none is available `phpinspect-buffer-parse' is called before
 continuing execution."
 
-  ;; Parse buffer if no map is available.
-  (unless (phpinspect-buffer-map buffer)
-    (phpinspect-buffer-parse buffer))
-
-  (phpinspect--log "Preparing to update project index")
-  ;; Use inhibit-quit to prevent index corruption though partial index
-  ;; application.
-  (let ((inhibit-quit t))
-    (when (phpinspect-buffer-project buffer)
-      (let ((map (phpinspect-buffer-map buffer)))
-        (unless (eq map (phpinspect-buffer--last-indexed-bmap buffer))
-          (phpinspect--log "Updating project index")
-
-          ;; Process deleted tokens
-          (dolist (deletion (phpinspect-buffer--deletions buffer))
-            (pcase (phpinspect-meta-token deletion)
-              ((pred phpinspect--can-delete-buffer-index-for-token)
-               (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deletion)))
-              ((pred phpinspect-use-trait-p)
-               (when-let ((class (seq-find (phpinspect-meta-token-predicate 
#'phpinspect-class-p)
-                                           
(phpinspect-buffer-tokens-enclosing-point
-                                            buffer (phpinspect-meta-start 
deletion))))
-                          (declaration 
(phpinspect-meta-find-first-child-matching-token
-                                        class 
#'phpinspect-class-declaration-p)))
-                 (phpinspect-buffer--index-class-declaration buffer 
declaration 'force)))))
-
-          ;; Process newly parsed tokens
-          (dolist (addition (phpinspect-buffer--additions buffer))
-            (pcase (phpinspect-meta-token addition)
-              ((pred phpinspect-class-declaration-p)
-               (phpinspect-buffer--index-class-declaration buffer addition))
-              ((pred phpinspect-function-p)
-               (phpinspect-buffer--index-function buffer addition))
-              ((pred phpinspect-use-trait-p)
-               (phpinspect-buffer--index-trait-use buffer addition))
-              ((pred phpinspect-this-p)
-               (phpinspect-buffer--index-this buffer addition))
-              ((or (pred phpinspect-class-variable-p)
-                   (pred phpinspect-const-p))
-               (phpinspect-buffer--index-class-variable buffer addition))))
-
-          (setf (phpinspect-buffer--additions buffer) nil
-                (phpinspect-buffer--deletions buffer) nil))
-
-          (setf (phpinspect-buffer--last-indexed-bmap buffer) map)))))
+  (phpi-shadow-await-index-synced (phpinspect-buffer-shadow buffer) t))
+
+  ;; ;; Parse buffer if no map is available.
+  ;; (unless (phpinspect-buffer-map buffer)
+  ;;   (phpinspect-buffer-parse buffer))
+
+  ;; (phpinspect--log "Preparing to update project index")
+  ;; ;; Use inhibit-quit to prevent index corruption though partial index
+  ;; ;; application.
+  ;; (let ((inhibit-quit t))
+  ;;   (when (phpinspect-buffer-project buffer)
+  ;;     (let ((map (phpinspect-buffer-map buffer)))
+  ;;       (unless (eq map (phpinspect-buffer--last-indexed-bmap buffer))
+  ;;         (phpinspect--log "Updating project index")
+
+  ;;         ;; Process deleted tokens
+  ;;         (dolist (deletion (phpinspect-buffer--deletions buffer))
+  ;;           (pcase (phpinspect-meta-token deletion)
+  ;;             ((pred phpinspect--can-delete-buffer-index-for-token)
+  ;;              (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deletion)))
+  ;;             ((pred phpinspect-use-trait-p)
+  ;;              (when-let ((class (seq-find (phpinspect-meta-token-predicate 
#'phpinspect-class-p)
+  ;;                                          
(phpinspect-buffer-tokens-enclosing-point
+  ;;                                           buffer (phpinspect-meta-start 
deletion))))
+  ;;                         (declaration 
(phpinspect-meta-find-first-child-matching-token
+  ;;                                       class 
#'phpinspect-class-declaration-p)))
+  ;;                (phpinspect-buffer--index-class-declaration buffer 
declaration 'force)))))
+
+  ;;         ;; Process newly parsed tokens
+  ;;         (dolist (addition (phpinspect-buffer--additions buffer))
+  ;;           (pcase (phpinspect-meta-token addition)
+  ;;             ((pred phpinspect-class-declaration-p)
+  ;;              (phpinspect-buffer--index-class-declaration buffer addition))
+  ;;             ((pred phpinspect-function-p)
+  ;;              (phpinspect-buffer--index-function buffer addition))
+  ;;             ((pred phpinspect-use-trait-p)
+  ;;              (phpinspect-buffer--index-trait-use buffer addition))
+  ;;             ((pred phpinspect-this-p)
+  ;;              (phpinspect-buffer--index-this buffer addition))
+  ;;             ((or (pred phpinspect-class-variable-p)
+  ;;                  (pred phpinspect-const-p))
+  ;;              (phpinspect-buffer--index-class-variable buffer addition))))
+
+  ;;         (setf (phpinspect-buffer--additions buffer) nil
+  ;;               (phpinspect-buffer--deletions buffer) nil))
+
+  ;;         (setf (phpinspect-buffer--last-indexed-bmap buffer) map)))))
 
 (defun phpinspect-buffer-parse-map (buffer)
   (phpinspect-buffer-parse buffer)
@@ -651,7 +690,6 @@ continuing execution."
       (setq start (- start (length (match-string 0))))
       (setq pre-change-length (+ pre-change-length (length (match-string 
0))))))
 
-  ;;(message "Registering edit: %d, %d, %d" start end pre-change-length)
   (phpi-shadow-enqueue-task
    (phpinspect-buffer-shadow buffer)
    (phpi-change-create (phpinspect-buffer-buffer buffer)
@@ -684,7 +722,7 @@ use."
                               (phpinspect-meta-end meta)))))
 
 (defun phpinspect-buffer-root-meta (buffer)
-  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer))
+  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer) t)
   (phpinspect-bmap-root-meta (phpinspect-buffer-map buffer)))
 
 (defun phpinspect-display-buffer-tree ()
@@ -760,6 +798,7 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 (cl-defstruct (phpinspect-shadow (:constructor 
phpinspect-make-shadow-generated)
                                  (:conc-name phpi-shadow-))
   (synced-p t :type boolean)
+  (index-synced-p t :type boolean)
   (origin nil :type phpinspect-buffer)
   (buffer nil :type buffer)
   (queue nil :type phpinspect--queue)
@@ -770,17 +809,17 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
   (thread-signal (phpi-shadow-thread shadow) 'phpinspect-wakeup-shadow nil))
 
 (defun phpi-shadow-thread-check-pause ()
-  (ignore-error phpinspect-wakeup-shadow
-    (if (or (phpinspect--input-pending-p)
-            quit-flag)
-        (let* ((mx (make-mutex))
-               (continue (make-condition-variable mx)))
-          (phpinspect-thread-pause phpinspect-shadow-pause-time mx continue))
-      (thread-yield))))
+  (if (or (phpinspect--input-pending-p)
+          quit-flag)
+      (let* ((mx (make-mutex))
+             (continue (make-condition-variable mx)))
+        (phpinspect-thread-pause phpinspect-shadow-pause-time mx continue))
+    (ignore-errors (thread-yield))))
 
 (defun phpi-shadow-make-queue-subscription (shadow)
   (lambda ()
     (setf (phpi-shadow-synced-p shadow) nil)
+    (setf (phpi-shadow-index-synced-p shadow) nil)
     (phpi-shadow-wakeup-thread shadow)))
 
 (defun phpi-shadow--thread-make-parser-interrupt-predicate ()
@@ -790,8 +829,6 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
   (with-current-buffer (phpi-shadow-buffer shadow)
     (phpi-change-apply change (current-buffer))
 
-    ;;(message "parsing")
-
     (let* ((buffer (phpi-shadow-origin shadow))
            (pctx (phpinspect-make-pctx
                   :incremental t
@@ -808,13 +845,11 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 
         (setf (phpinspect-buffer-tree buffer) result))
 
-        ;;(message "setting map")
         (phpinspect-buffer--set-map
          buffer (phpinspect-pctx-bmap pctx) (phpinspect-pctx-previous-bmap 
pctx)))))
 
 (defun phpi-shadow-parse-fresh (shadow)
   (with-current-buffer (phpi-shadow-buffer shadow)
-    ;;(message "parsing fresh")
     (let* ((buffer (phpi-shadow-origin shadow))
            (pctx (phpinspect-make-pctx
                   :incremental t
@@ -829,8 +864,6 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 
         (setf (phpinspect-buffer-tree buffer) result)
 
-        ;;(message "Result: %s" result)
-
         (phpinspect-buffer--set-map
          buffer (phpinspect-pctx-bmap pctx) nil)))))
 
@@ -840,47 +873,126 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
   (pop-to-buffer (phpi-shadow-buffer (phpinspect-buffer-shadow buffer))))
 
 (defun phpi-shadow-make-thread-function (shadow)
-  ;;(message "making thread function")
   (lambda ()
     (let ((inhibit-quit t))
-      ;;(message "Func starting")
       (while t
-        ;;(message "shadow thread working")
-        (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue shadow))))
-            (progn
-              (pcase task
-                ((pred phpinspect-change-p)
-                 (phpi-shadow-process-change shadow task))
-                ('parse-fresh
-                 (phpi-shadow-parse-fresh shadow))
-                (_
-                 (phpinspect-message
-                  "Shadow thread received unsupported task type: %s"
-                  (type-of task))))
-
-              ;; Rest after task completion
-              (phpi-shadow-thread-check-pause))
-
-          ;; No work to do, join main thread
-          (setf (phpi-shadow-synced-p shadow) t)
-          (ignore-error 'phpinspect-wakeup-shadow
-            (thread-join main-thread)))))))
+        (condition-case err
+            (ignore-error phpinspect-wakeup-shadow
+              (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue 
shadow))))
+                  (progn
+                    (pcase task
+                      ((pred phpinspect-change-p)
+                       (phpi-shadow-process-change shadow task))
+                      ('parse-fresh
+                       (phpi-shadow-parse-fresh shadow))
+                      (_
+                       (phpinspect-message
+                        "Shadow thread received unsupported task type: %s"
+                        (type-of task))))
+
+                    ;; Rest after task completion
+                    (phpi-shadow-thread-check-pause))
+
+                ;; No parsing work to do, we're in sync with the origin buffer
+                (setf (phpi-shadow-synced-p shadow) t)
+
+                (if (phpi-shadow-index-synced-p shadow)
+                    ;; Index has been synced, we can join the main thread and
+                    ;; wait for more tasks.
+                    (ignore-errors
+                      (thread-join main-thread))
+                  ;; Update project index
+                  (phpi-shadow-update-project-index shadow)
+
+                  ;; Only flag index as synced when no additional tasks have 
been
+                  ;; enqueued while indexing.
+                  (unless (phpinspect-queue-first (phpi-shadow-queue shadow))
+                    (setf (phpi-shadow-index-synced-p shadow) t)))))
+          (error
+           (phpinspect-message "%s encountered unexpected error: %s"
+                               (thread-name (current-thread))
+                               err)))))))
+        ;; (error
+        ;;  (phpinspect-message "%s exited: %s"
+        ;;                      (thread-name (current-thread))
+        ;;                      err))))))
 
 (defun phpi-shadow-thread-live-p (shadow)
   (thread-live-p (phpi-shadow-thread shadow)))
 
-(defun phpi-shadow-await-synced (shadow &optional allow-interrupt)
-  (cl-assert (phpi-shadow-thread-live-p shadow))
+(defun phpi-shadow-assert-live-p (shadow)
+  (unless (phpi-shadow-thread-live-p shadow)
+      (error "Shadow thread exited: %s"
+             (thread-last-error (phpi-shadow-thread shadow)))))
 
-  (while (not (phpi-shadow-synced-p shadow))
-    (when (and allow-interrupt (phpinspect--input-pending-p))
-      (throw 'phpinspect-interrupted nil))
+(defun phpi-shadow-is-me-p (shadow)
+  (eq (current-thread) (phpi-shadow-thread shadow)))
 
-    (unless (phpi-shadow-thread-live-p shadow)
-      (error "Shadow thread exited: %s"
-             (thread-last-error (phpi-shadow-thread shadow))))
+(defun phpi-shadow-await-predicate (shadow predicate allow-interrupt)
+  (phpi-shadow-assert-live-p shadow)
+
+  (unless (phpi-shadow-is-me-p shadow)
+
+    (while (not (or (funcall predicate shadow) quit-flag))
+      (when (and (phpinspect--input-pending-p t) allow-interrupt)
+        (throw 'phpinspect-interrupted nil))
 
-    (sleep-for 0.005)))
+      (phpi-shadow-assert-live-p shadow)
+
+      (phpi-shadow-wakeup-thread shadow)
+      (thread-yield))))
+
+(defun phpi-shadow-await-synced (shadow &optional allow-interrupt)
+  (phpi-shadow-await-predicate shadow #'phpi-shadow-synced-p allow-interrupt))
+
+(defun phpi-shadow-await-index-synced (shadow &optional allow-interrupt)
+   (phpi-shadow-await-predicate shadow #'phpi-shadow-index-synced-p 
allow-interrupt))
+
+(defun phpi-shadow-process-deletions (shadow)
+  (let ((buffer (phpi-shadow-origin shadow)))
+    ;; Process deleted tokens
+    (dolist (deletion (phpinspect-buffer--deletions buffer))
+      (pcase (phpinspect-meta-token deletion)
+        ((pred phpinspect--can-delete-buffer-index-for-token)
+         (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deletion)))
+        ((pred phpinspect-use-trait-p)
+         (when-let ((class (seq-find (phpinspect-meta-token-predicate 
#'phpinspect-class-p)
+                                     (phpinspect-buffer-tokens-enclosing-point
+                                      buffer (phpinspect-meta-start 
deletion))))
+                    (declaration 
(phpinspect-meta-find-first-child-matching-token
+                                  class #'phpinspect-class-declaration-p)))
+           (phpinspect-buffer--index-class-declaration buffer declaration 
'force)))))
+
+    (setf (phpinspect-buffer--deletions buffer) nil)))
+
+(defun phpi-shadow-process-additions (shadow)
+  ;; Process newly parsed tokens
+  (let ((buffer (phpi-shadow-origin shadow)))
+    (dolist (addition (phpinspect-buffer--additions buffer))
+      (pcase (phpinspect-meta-token addition)
+        ((pred phpinspect-class-declaration-p)
+         (phpinspect-buffer--index-class-declaration buffer addition))
+        ((pred phpinspect-function-p)
+         (phpinspect-buffer--index-function buffer addition))
+        ((pred phpinspect-use-trait-p)
+         (phpinspect-buffer--index-trait-use buffer addition))
+        ((pred phpinspect-this-p)
+         (phpinspect-buffer--index-this buffer addition))
+        ((or (pred phpinspect-class-variable-p)
+             (pred phpinspect-const-p))
+         (phpinspect-buffer--index-class-variable buffer addition)))
+
+      ;; Pause in between potentially expensive indexing operations.
+      (phpi-shadow-thread-check-pause))
+
+    (setf (phpinspect-buffer--additions buffer) nil)))
+
+(defun phpi-shadow-update-project-index (shadow)
+  (when (phpinspect-buffer-project (phpi-shadow-origin shadow))
+    (phpinspect--log "Updating project index")
+    (phpi-shadow-process-deletions shadow)
+    (phpi-shadow-thread-check-pause)
+    (phpi-shadow-process-additions shadow)))
 
 (defun phpi-shadow-make-thread (shadow)
   (make-thread
diff --git a/phpinspect-completion.el b/phpinspect-completion.el
index 08f633edf8..5bdeedbf6a 100644
--- a/phpinspect-completion.el
+++ b/phpinspect-completion.el
@@ -42,7 +42,7 @@
   (target nil
           :documentation
           "The object that this completion is aimed at/completing towards")
-  (value nil :type string)
+  (name nil :type string)
   (meta nil :type string)
   (annotation nil :type string)
   (kind nil :type symbol))
@@ -58,8 +58,8 @@ candidate. Candidates can be indexed functions and 
variables.")
                     :type integer)
   (completion-end nil
                   :type integer)
-  (completions (obarray-make)
-               :type obarray
+  (completions (make-hash-table :test #'equal :size 10000 :rehash-size 2)
+               :type hash-table
                :documentation
                "A list of completion strings")
   (has-candidates nil))
@@ -70,29 +70,23 @@ candidate. Candidates can be indexed functions and 
variables.")
 
 (cl-defmethod phpinspect--completion-list-add
   ((comp-list phpinspect--completion-list) (completion phpinspect--completion))
-  ;; Ignore completions in an invalid state (nil values)
-  (when (phpinspect--completion-value completion)
+  ;; Ignore completions in an invalid state (nil names)
+  (when (phpinspect--completion-name completion)
     (setf (phpinspect--completion-list-has-candidates comp-list) t)
-    (unless (intern-soft (phpinspect--completion-value completion)
-                         (phpinspect--completion-list-completions comp-list))
-      (set (intern (phpinspect--completion-value completion)
-                   (phpinspect--completion-list-completions comp-list))
-           completion))))
+    (unless (gethash
+             (phpinspect--completion-name completion)
+             (phpinspect--completion-list-completions comp-list))
+      (puthash (phpinspect--completion-name completion)
+               completion
+               (phpinspect--completion-list-completions comp-list)))))
 
 (cl-defmethod phpinspect--completion-list-get-metadata
   ((comp-list phpinspect--completion-list) (completion-name string))
-  (let ((comp-sym (intern-soft completion-name
-                               (phpinspect--completion-list-completions 
comp-list))))
-    (when comp-sym
-      (symbol-value comp-sym))))
-
+  (gethash completion-name (phpinspect--completion-list-completions 
comp-list)))
 
 (cl-defmethod phpinspect--completion-list-strings
   ((comp-list phpinspect--completion-list))
-  (let (strings)
-    (obarray-map (lambda (sym) (push (symbol-name sym) strings))
-                 (phpinspect--completion-list-completions comp-list))
-    strings))
+  (hash-table-keys (phpinspect--completion-list-completions comp-list)))
 
 (cl-defstruct (phpinspect-completion-query (:constructor 
phpinspect-make-completion-query))
   (completion-point 0
@@ -406,7 +400,7 @@ Returns list of `phpinspect--completion'."
 ;; on less powerful machines, consider implementing an LRU.
 (defun phpinspect-make-fn-completion (completion-candidate)
   (phpinspect--construct-completion
-   :value (phpi-fn-name completion-candidate)
+   :name (phpi-fn-name completion-candidate)
    :meta (concat "(" (mapconcat (lambda (arg)
                                   (concat "$" (if (> (length (car arg)) 8)
                                                   (truncate-string-to-width 
(car arg) 8 nil)
@@ -437,7 +431,7 @@ Returns list of `phpinspect--completion'."
 
 (cl-defmethod phpinspect--make-completion ((type phpinspect--type))
   (phpinspect--construct-completion
-   :value (propertize (phpinspect--type-base-name type) 'face 
'font-lock-type-face)
+   :name (propertize (phpinspect--type-base-name type) 'face 
'font-lock-type-face)
    :meta (phpinspect--format-type-name type)
    :target type
    :kind 'class))
@@ -445,7 +439,7 @@ Returns list of `phpinspect--completion'."
 (cl-defmethod phpinspect--make-completion
   ((completion-candidate phpinspect--variable))
   (phpinspect--construct-completion
-   :value (phpinspect--variable-name completion-candidate)
+   :name (phpinspect--variable-name completion-candidate)
    :meta (phpinspect--display-format-type-name
           (or (phpinspect--variable-type completion-candidate)
               phpinspect--null-type))
@@ -458,7 +452,7 @@ Returns list of `phpinspect--completion'."
 
 (cl-defmethod phpinspect--make-completion ((keyword 
phpinspect-suggest-keyword))
   (phpinspect--construct-completion
-   :value (propertize (phpinspect-suggest-keyword-word keyword) 'face 
'font-lock-keyword-face)
+   :name (propertize (phpinspect-suggest-keyword-word keyword) 'face 
'font-lock-keyword-face)
    :target keyword
    :kind 'keyword
    :meta "keyword"))
@@ -473,14 +467,13 @@ Returns list of `phpinspect--completion'."
 
 (defun phpinspect-complete-at-point ()
   (catch 'phpinspect-interrupted
-    (let ((comp-list (phpinspect-completion-query-execute 
(phpinspect--get-completion-query)))
-          strings)
-      (phpinspect--log "Completion list: %s" comp-list)
-      (obarray-map (lambda (sym) (push (symbol-name sym) strings)) 
(phpinspect--completion-list-completions comp-list))
+    (let ((comp-list (phpinspect-completion-query-execute 
(phpinspect--get-completion-query))))
+
       (and (phpinspect--completion-list-has-candidates comp-list)
            (list (phpinspect--completion-list-completion-start comp-list)
                  (phpinspect--completion-list-completion-end comp-list)
-                 strings
+                 (completion-table-dynamic (lambda (_)
+                                             
(phpinspect--completion-list-strings comp-list)))
                  :affixation-function
                  (lambda (completions)
                    (let (affixated completion)
diff --git a/phpinspect-diagnose.el b/phpinspect-diagnose.el
index b49f80eb9a..0b5b7f1de9 100644
--- a/phpinspect-diagnose.el
+++ b/phpinspect-diagnose.el
@@ -65,7 +65,6 @@
   (when meta
     (let ((stack (list meta)))
       (while-let ((current (pop stack)))
-        (message "checking")
         ;; A token cannot be a parent of itself.
         (when (eq (phpinspect-meta-parent current) current)
           (let ((message
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index cfdcdae59b..def9f99fe4 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -154,6 +154,7 @@ text at point and returns the resulting token."
           ;;   (setq token-meta (phpinspect-meta-find-child-starting-at 
token-meta original-point)))
 
           (if (or (not token-meta)
+                  (not (phpinspect--token-recyclable-p (phpinspect-meta-token 
token-meta)))
                   (phpinspect-root-p (phpinspect-meta-token token-meta))
                   (phpinspect-incomplete-token-p (phpinspect-meta-token 
token-meta))
                   (phpi-change-tainted-token-p change token-meta))
@@ -281,6 +282,7 @@ is able to reuse an already parsed tree."
              (when (looking-at (phpinspect-handler-regexp whitespace))
                (,(phpinspect-handler-func-name 'whitespace) (match-string 0)))
 
+
              (while (and (< (point) max-point)
                          (if continue-condition (funcall continue-condition) t)
                          (not ,(if delimiter-predicate
@@ -383,6 +385,9 @@ is able to reuse an already parsed tree."
                   :read-only t
                   :documentation "Name of the keyword that is used as car of 
the
 root token, in string form without \":\" prefix.")
+    (recycle-only-delimited
+     nil
+     :type boolean)
     (handlers '(array tag equals list comma
                       attribute-reference static-attribute-reference variable
                       assignment-operator whitespace scope-keyword
@@ -437,6 +442,7 @@ version of the parser function."
                (phpinspect-parser-delimiter-predicate parser)
                (phpinspect-parser-delimiter-condition parser)))))
 
+
   (cl-defmethod phpinspect-parser-compile-entry ((parser phpinspect-parser))
     (let ((func-name (phpinspect-parser-func-name (phpinspect-parser-name 
parser)))
           (incremental-name (phpinspect-parser-func-name 
(phpinspect-parser-name parser) "incremental"))
@@ -499,14 +505,49 @@ parsing incrementally."
 (defun phpinspect-handler-bound-p (symbol)
   (get symbol 'phpinspect--handler))
 
+(eval-and-compile
+  (defun phpinspect-make-recycle-predicate (predicate-name names)
+    (let (cases
+          (token-sym (gensym)))
+      (push `(t t) cases)
+      (dolist (name names)
+        (catch 'phpinspect--continue
+          (let ((parser (symbol-value name))
+                body)
+            (unless (or (and (phpinspect-parser-delimiter-condition parser)
+                             (phpinspect-parser-delimiter-predicate parser))
+                        (and (phpinspect-parser-delimiter-predicate parser)
+                             (phpinspect-parser-recycle-only-delimited 
parser)))
+              (throw 'phpinspect--continue nil))
+
+            (setq body `(,(phpinspect-parser-delimiter-predicate parser) (car 
(last ,token-sym))))
+
+            (when-let ((condition (phpinspect-parser-delimiter-condition 
parser)))
+              (setq body `(if (,(phpinspect-parser-delimiter-condition parser) 
,token-sym)
+                              ,body
+                            ,(not (phpinspect-parser-recycle-only-delimited 
parser)))))
+
+            (push `((eq (car ,token-sym) ,(intern (concat ":" 
(phpinspect-parser-tree-keyword parser))))
+                    ,body)
+                  cases))))
+
+      `(defun ,predicate-name (,token-sym)
+         (cond ,@cases)))))
+
+(eval-and-compile
+  (defun phpinspect--get-parser-function-syms ()
+    (let (names incremental-names)
+      (obarray-map (lambda (sym)
+                     (cond ((phpinspect-parser-func-bound-p sym)
+                            (push sym names))
+                           ((phpinspect-incremental-parser-func-bound-p sym)
+                            (push sym incremental-names))))
+                   obarray)
+      (list names incremental-names))))
+
 (defmacro phpinspect-define-parser-functions ()
-   (let (names incremental-names function-definitions)
-     (obarray-map (lambda (sym)
-                    (cond ((phpinspect-parser-func-bound-p sym)
-                           (push sym names))
-                          ((phpinspect-incremental-parser-func-bound-p sym)
-                           (push sym incremental-names))))
-                  obarray)
+  (pcase-let ((`(,names ,incremental-names) 
(phpinspect--get-parser-function-syms))
+              (function-definitions))
 
      (dolist (name names)
        (push (phpinspect-parser-compile-entry (symbol-value name))
@@ -522,7 +563,10 @@ parsing incrementally."
 
      (push 'progn function-definitions)
 
-     function-definitions))
+     `(progn
+        ,function-definitions
+        ,(phpinspect-make-recycle-predicate
+          'phpinspect--token-recyclable-p names))))
 
 (phpinspect-defhandler comma (comma &rest _ignored)
   "Handler for comma tokens"
@@ -739,11 +783,13 @@ nature like argument lists"
 
 (phpinspect-defparser use
   :tree-keyword "use"
+  :recycle-only-delimited t
   :handlers '(comment word tag block-without-scopes comma terminator)
   :delimiter-predicate #'phpinspect-end-of-use-p)
 
 (phpinspect-defparser use-trait
   :tree-keyword "use-trait"
+  :recycle-only-delimited t
   :handlers '(comment word tag block-without-scopes comma terminator)
   :delimiter-predicate #'phpinspect-end-of-use-p)
 
@@ -803,6 +849,7 @@ To parse trait use statements in class bodies, see
 
 (phpinspect-defparser namespace
   :tree-keyword "namespace"
+  :recycle-only-delimited t
   :delimiter-condition #'phpinspect--namespace-should-end-at-block-p
   :delimiter-predicate #'phpinspect-block-p)
 
@@ -824,6 +871,7 @@ To parse trait use statements in class bodies, see
 
 (phpinspect-defparser const
   :tree-keyword "const"
+  :recycle-only-delimited t
   :handlers '(word comment assignment-operator string string-concatenator 
array terminator)
   :delimiter-predicate #'phpinspect-end-of-token-p)
 
@@ -963,6 +1011,7 @@ Returns the consumed text string without face properties."
 (phpinspect-defparser declaration
   :tree-keyword "declaration"
   :handlers '(comment word list terminator tag)
+  :recycle-only-delimited t
   :delimiter-predicate #'phpinspect-end-of-token-p)
 
 (phpinspect-defhandler function-keyword (start-token max-point)
@@ -987,6 +1036,7 @@ Returns the consumed text string without face properties."
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
                                string string-concatenator terminator tag 
comment
                                assignment-operator array word)
+  :recycle-only-delimited t
   :delimiter-predicate #'phpinspect--scope-terminator-p)
 
 (phpinspect-defparser scope-private
@@ -994,10 +1044,12 @@ Returns the consumed text string without face 
properties."
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
                                string string-concatenator terminator tag 
comment
                                assignment-operator array word)
+  :recycle-only-delimited t
   :delimiter-predicate #'phpinspect--scope-terminator-p)
 
 (phpinspect-defparser scope-protected
   :tree-keyword "protected"
+  :recycle-only-delimited t
   :handlers '(function-keyword static-keyword const-keyword class-variable 
here-doc
                                string string-concatenator terminator tag 
comment
                                assignment-operator array word)
@@ -1017,6 +1069,7 @@ Returns the consumed text string without face properties."
 
 (phpinspect-defparser static
   :tree-keyword "static"
+  :recycle-only-delimited t
   :handlers '(comment function-keyword class-variable array word terminator 
tag)
   :delimiter-predicate #'phpinspect--static-terminator-p)
 
@@ -1069,6 +1122,17 @@ Returns the consumed text string without face 
properties."
     (lambda () (not (char-equal (char-after) ?{)))
     'root)))
 
+(phpinspect-defhandler type-declaration (_start-token max-point)
+  "Handler for class, interface, trait declarations."
+  ((regexp . (phpinspect--word-handler-regexp))
+   (inline . t))
+  (inline-quote
+   (phpinspect--parse-class-declaration
+    (current-buffer)
+    ,max-point
+    nil
+    (lambda () (not (char-equal (char-after) ?{))))))
+
 (define-inline phpinspect--skip-over-word (word-plus-whitespace)
   (inline-quote
    (forward-char (length (phpinspect--strip-word-end-space 
,word-plus-whitespace)))))
diff --git a/phpinspect-pipeline.el b/phpinspect-pipeline.el
index 2579d1f83f..4ffd54fec6 100644
--- a/phpinspect-pipeline.el
+++ b/phpinspect-pipeline.el
@@ -134,7 +134,7 @@ directories."
        (let ((mx (make-mutex)))
          (phpinspect-thread-pause
           phpinspect-pipeline-pause-time mx (make-condition-variable mx 
"phpinspect-pipeline-pause")))
-     (thread-yield))))
+     (ignore-errors (thread-yield)))))
 
 (define-inline phpinspect--read-pipeline-emission (&rest body)
   (push 'progn body)
diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el
index b21cbfeeef..339d476e2f 100644
--- a/phpinspect-token-predicates.el
+++ b/phpinspect-token-predicates.el
@@ -123,14 +123,15 @@ Type can be any of the token types returned by
   (inline-letevals (token)
     (inline-quote
      (and (phpinspect-class-p ,token)
-         (phpinspect-incomplete-block-p (car (last ,token)))))))
+          (or (not (phpinspect-block-p (car (last ,token))))
+                 (phpinspect-incomplete-block-p (car (last ,token))))))))
 
 (define-inline phpinspect-incomplete-namespace-p (token)
   (inline-letevals (token)
     (inline-quote
      (and (phpinspect-namespace-p ,token)
          (or (phpinspect-incomplete-block-p (car (last ,token)))
-              (phpinspect-incomplete-class-p (car (last ,token))))))))
+          (phpinspect-incomplete-class-p (car (last ,token))))))))
 
 (define-inline phpinspect-function-p (token)
   (inline-quote (phpinspect-token-type-p ,token :function)))
@@ -165,6 +166,10 @@ Type can be any of the token types returned by
   (inline-quote
    (phpinspect-token-type-p ,token :declaration :class-declaration)))
 
+(define-inline phpinspect-function-declaration-p (token)
+  (inline-quote
+   (phpinspect-token-type-p ,token :declaration)))
+
 (define-inline phpinspect-class-declaration-p (token)
   (inline-quote
    (phpinspect-token-type-p ,token :class-declaration)))
diff --git a/phpinspect-util.el b/phpinspect-util.el
index aa6d03c154..3d50a7866c 100644
--- a/phpinspect-util.el
+++ b/phpinspect-util.el
@@ -271,8 +271,8 @@ context for completion."
      ,@body))
 
 (defun phpinspect--input-pending-p (&optional check-timers)
-  (unless noninteractive
-    (input-pending-p check-timers)))
+  (and (input-pending-p check-timers)
+       (not noninteractive)))
 
 (defun phpinspect-thread-pause (pause-time mx continue)
   "Pause current thread using MX and CONTINUE for PAUSE-TIME idle seconds.
@@ -285,7 +285,7 @@ CONTINUE must be a condition-variable"
    pause-time
    nil
    (lambda () (with-mutex mx (condition-notify continue))))
-  (with-mutex mx (condition-wait continue))
+  (ignore-errors (with-mutex mx (condition-wait continue)))
   (phpinspect--log "Thread '%s' continuing execution" (thread-name 
(current-thread))))
 
 (provide 'phpinspect-util)
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 7de760b468..26724f87d3 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -638,7 +638,6 @@ class Bar {
 
       (let ((class (phpinspect-project-get-typedef
                     project (phpinspect--make-type :name "\\Foo\\Bar"))))
-
         (should class)
         (should (= 1 (length (phpi-typedef-get-methods class))))
 
@@ -648,7 +647,6 @@ class Bar {
 
         (phpinspect-buffer-parse buffer 'no-interrupt)
         (phpinspect-buffer-update-project-index buffer)
-
         (should (= 2 (length (phpi-typedef-get-methods class))))
 
         (goto-char 36)
@@ -743,32 +741,46 @@ class TestClass
        (should (equal '(:root (:comment))
                           (phpinspect-buffer-parse buffer))))))
 
-(ert-deftest phpinspect-buffer-deletion-registration ()
-  "Check if buffer accuratly registers token deletions/alterations."
+;; (ert-deftest phpinspect-buffer-deletion-registration ()
+;;   "Check if buffer accuratly registers token deletions/alterations."
 
-  (with-temp-buffer
-    (let ((buffer (phpinspect-claim-buffer (current-buffer) 
(phpinspect--make-dummy-project))))
+;;   (with-temp-buffer
+;;     (let ((buffer (phpinspect-claim-buffer (current-buffer) 
(phpinspect--make-dummy-project))))
+
+;;       (insert "<?php aaa bbb ccc")
+
+;;       (should (equal '(:root (:word "aaa") (:word "bbb") (:word "ccc")) 
(phpinspect-buffer-parse buffer)))
+;;       (should-not (phpinspect-buffer--deletions buffer))
 
-      (insert "<?php aaa bbb ccc")
+;;       (should (length= (phpinspect-buffer--additions buffer) 4))
 
-      (should (equal '(:root (:word "aaa") (:word "bbb") (:word "ccc")) 
(phpinspect-buffer-parse buffer)))
-      (should-not (phpinspect-buffer--deletions buffer))
+;;       (goto-char (- (point-max) 5))
+;;       (insert "a")
+;;       (should (equal '(:root (:word "aaa") (:word "bbab") (:word "ccc")) 
(phpinspect-buffer-parse buffer)))
 
-      (should (length= (phpinspect-buffer--additions buffer) 4))
+;;       (should (length= (phpinspect-buffer--deletions buffer) 2))
+;;       (should (length= (phpinspect-buffer--additions buffer) 6))
 
-      (goto-char (- (point-max) 5))
-      (insert "a")
-      (should (equal '(:root (:word "aaa") (:word "bbab") (:word "ccc")) 
(phpinspect-buffer-parse buffer)))
+;;       (goto-char 6)
+;;       (insert "{")
+;;       (should (equal '(:root (:incomplete-block (:word "aaa") (:word 
"bbab") (:word "ccc")))
+;;                      (phpinspect-buffer-parse buffer)))
+;;       (should (length= (phpinspect-buffer--deletions buffer) 3))
+;;       (should (length= (phpinspect-buffer--additions buffer) 8))
 
-      (should (length= (phpinspect-buffer--deletions buffer) 2))
-      (should (length= (phpinspect-buffer--additions buffer) 6))
+;;       (should (seq-every-p #'phpinspect-meta-p 
(phpinspect-buffer--additions buffer)))
+;;       (should (seq-every-p #'phpinspect-meta-p 
(phpinspect-buffer--deletions buffer))))))
 
-      (goto-char 6)
-      (insert "{")
-      (should (equal '(:root (:incomplete-block (:word "aaa") (:word "bbab") 
(:word "ccc")))
-                     (phpinspect-buffer-parse buffer)))
-      (should (length= (phpinspect-buffer--deletions buffer) 3))
-      (should (length= (phpinspect-buffer--additions buffer) 8))
+(ert-deftest phpinspect-buffer-parse-class-insertion ()
+  (with-temp-buffer
+    (let ((buffer (phpinspect-claim-buffer (current-buffer))))
+      (insert "<?php ")
+
+      (insert "class")
+      (phpinspect-buffer-parse buffer)
+      (insert " Name")
+      (phpinspect-buffer-parse buffer)
+      (insert " {} ")
 
-      (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--additions 
buffer)))
-      (should (seq-every-p #'phpinspect-meta-p (phpinspect-buffer--deletions 
buffer))))))
+      (should (equal '(:root (:class (:class-declaration (:word "Name")) 
(:block)))
+                     (phpinspect-buffer-parse buffer))))))
diff --git a/test/test-parser.el b/test/test-parser.el
index 224aa485ba..b2a87855fb 100644
--- a/test/test-parser.el
+++ b/test/test-parser.el
@@ -273,3 +273,8 @@ interface Test
   (dolist (code (list "//" "/*" "// " "/* "))
 
     (should (phpinspect-parse-string code))))
+
+(ert-deftest phpinspect-parser-token-recycle-predicate ()
+  (should (phpinspect--token-recyclable-p '(:list)))
+  (should-not (phpinspect--token-recyclable-p '(:namespace)))
+  (should (phpinspect--token-recyclable-p '(:namespace (:word "bla") 
(:block)))))


Reply via email to