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

    Fix all byte compilation warnings and errors (for real this time (probably))
---
 benchmarks/parse-file.el       |  17 +-
 compile.bash                   |   9 +
 phpinspect-autoload.el         |  20 +-
 phpinspect-bmap.el             |   8 +
 phpinspect-buffer.el           |   3 +-
 phpinspect-cache.el            |  43 +++-
 phpinspect-changeset.el        |  14 +-
 phpinspect-class-struct.el     |  68 +++++++
 phpinspect-class.el            |  12 +-
 phpinspect-edtrack.el          | 170 ++++++++--------
 phpinspect-eldoc.el            |   3 +-
 phpinspect-imports.el          |   8 +-
 phpinspect-index.el            |  70 +------
 phpinspect-parse-context.el    |  21 +-
 phpinspect-parser.el           | 448 +++++++++++++++++++++--------------------
 phpinspect-pipeline.el         |   2 +-
 phpinspect-project.el          | 170 +++++++++-------
 phpinspect-resolve.el          |  10 +-
 phpinspect-resolvecontext.el   |  32 +--
 phpinspect-suggest.el          |   2 +-
 phpinspect-toc.el              |   3 +-
 phpinspect-token-predicates.el |  26 ++-
 phpinspect-type.el             |  69 +++++++
 phpinspect-util.el             |  73 +++----
 phpinspect-worker.el           |  89 +-------
 test/phpinspect-test.el        |   2 +-
 test/test-autoload.el          |  22 +-
 test/test-class.el             |   4 +-
 test/test-index.el             |   2 +-
 test/test-parse-context.el     |   2 +-
 test/test-parser.el            |   4 +-
 test/test-resolvecontext.el    |   2 +-
 32 files changed, 763 insertions(+), 665 deletions(-)

diff --git a/benchmarks/parse-file.el b/benchmarks/parse-file.el
index 604ba2e833..960b601aa4 100644
--- a/benchmarks/parse-file.el
+++ b/benchmarks/parse-file.el
@@ -12,7 +12,7 @@
     (insert-file-contents (concat here "/Response.php"))
 
     (message "Incremental parse (warmup):")
-    (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t)
+    (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap))
       (benchmark 1 '(phpinspect-parse-current-buffer)))
 
     (let ((bmap (phpinspect-make-bmap))
@@ -24,13 +24,19 @@
       (garbage-collect)
 
       (message "Incremental parse (no edits):")
-      (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t 
:bmap bmap2 :previous-bmap bmap :edtrack (phpinspect-make-edtrack))
+      (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
+                                                           :bmap bmap2
+                                                           :previous-bmap bmap
+                                                           :edtrack 
(phpinspect-make-edtrack))
         (benchmark 1 '(phpinspect-parse-current-buffer)))
 
       (garbage-collect)
 
       (message "Incremental parse repeat (no edits):")
-      (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t 
:previous-bmap bmap2 :edtrack (phpinspect-make-edtrack))
+      (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
+                                                           :bmap 
(phpinspect-make-bmap)
+                                                           :previous-bmap bmap2
+                                                           :edtrack 
(phpinspect-make-edtrack))
         (benchmark 1 '(phpinspect-parse-current-buffer)))
 
       (garbage-collect)
@@ -64,7 +70,10 @@
 
         ;;(profiler-start 'cpu)
         (message "Incremental parse after 2 more edits:")
-        (phpinspect-with-parse-context (phpinspect-make-pctx :incremental t 
:previous-bmap bmap-after :edtrack edtrack)
+        (phpinspect-with-parse-context (phpinspect-make-pctx :bmap 
(phpinspect-make-bmap)
+                                                             :incremental t
+                                                             :previous-bmap 
bmap-after
+                                                             :edtrack edtrack)
           (benchmark 1 '(phpinspect-parse-current-buffer)))
 
         ;; (save-current-buffer
diff --git a/compile.bash b/compile.bash
new file mode 100644
index 0000000000..32ca4cd2cb
--- /dev/null
+++ b/compile.bash
@@ -0,0 +1,9 @@
+#!/bin/bash
+
+for file in ./*.el; do
+    cask emacs -batch -L . --eval '(setq byte-compile-error-on-warn t)' -f 
batch-byte-compile "$file" || break
+done
+
+if ! [[ -n 'NO_REMOVE_ELC' ]]; then
+    rm ./*.elc
+fi
diff --git a/phpinspect-autoload.el b/phpinspect-autoload.el
index f0a2ceb6e8..995ec2f903 100644
--- a/phpinspect-autoload.el
+++ b/phpinspect-autoload.el
@@ -24,7 +24,6 @@
 ;;; Code:
 
 (require 'cl-lib)
-(require 'phpinspect-project)
 (require 'phpinspect-fs)
 (require 'phpinspect-util)
 (require 'phpinspect-pipeline)
@@ -72,9 +71,12 @@
                (:constructor phpinspect-make-autoloader))
   (refresh-thread nil
                   :type thread)
-  (project nil
-           :type phpinspect-project
-           :documentation "The project that this autoloader can find files 
for")
+  (fs nil
+      :type phpinspect-fs)
+  (file-indexer nil
+                :type function)
+  (project-root-resolver nil
+                         :type function)
   (own-types (make-hash-table :test 'eq :size 10000 :rehash-size 10000)
              :type hash-table
              :documentation "The internal types that can be
@@ -160,9 +162,9 @@ bareword typenames."))
 
 (cl-defmethod phpinspect-al-strategy-execute ((strat phpinspect-files))
   (phpinspect--log "indexing files list: %s" (phpinspect-files-list strat))
-  (let* ((project (phpinspect-autoloader-project (phpinspect-files-autoloader 
strat))))
+  (let* ((indexer (phpinspect-autoloader-file-indexer 
(phpinspect-files-autoloader strat))))
     (phpinspect-pipeline (phpinspect-files-list strat)
-      :into (phpinspect-project-add-file-index :with-context project))))
+      :into (funcall :with-context indexer))))
 
 (cl-defmethod phpinspect-autoloader-put-type-bag ((al phpinspect-autoloader) 
(type-fqn symbol))
   (let* ((type-name (phpinspect-intern-name
@@ -175,7 +177,7 @@ bareword typenames."))
 
 (cl-defmethod phpinspect-iterate-composer-jsons
   ((al phpinspect-autoloader) file)
-  (let* ((fs (phpinspect-project-fs (phpinspect-autoloader-project al)))
+  (let* ((fs (phpinspect-autoloader-fs  al))
          (project-root (file-name-directory (cdr file)))
          json autoload batch)
 
@@ -254,8 +256,8 @@ bareword typenames."))
 (cl-defmethod phpinspect-autoloader-refresh ((autoloader 
phpinspect-autoloader) &optional async-callback)
   "Refresh autoload definitions by reading composer.json files
   from the project and vendor folders."
-  (let* ((project-root (phpinspect-project-root (phpinspect-autoloader-project 
autoloader)))
-         (fs (phpinspect-project-fs (phpinspect-autoloader-project 
autoloader))))
+  (let* ((project-root (funcall (phpinspect-autoloader-project-root-resolver 
autoloader)))
+         (fs (phpinspect-autoloader-fs autoloader)))
     (setf (phpinspect-autoloader-type-name-fqn-bags autoloader)
           (make-hash-table :test 'eq :size 3000 :rehash-size 3000))
     (setf (phpinspect-autoloader-own-types autoloader)
diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el
index a2ad9ec39c..d38dd9b513 100644
--- a/phpinspect-bmap.el
+++ b/phpinspect-bmap.el
@@ -26,6 +26,7 @@
 (require 'phpinspect-splayt)
 (require 'phpinspect-meta)
 (require 'phpinspect-changeset)
+(require 'phpinspect-parse-context)
 (require 'phpinspect-util)
 (require 'compat)
 (require 'phpinspect-token-predicates)
@@ -217,6 +218,13 @@
     (setf (phpinspect-bmap-last-token-start bmap) start)
     (push token-meta (phpinspect-bmap-token-stack bmap))))
 
+(define-inline phpinspect-pctx-register-token (pctx token start end)
+  (inline-letevals (pctx)
+    (inline-quote
+     (phpinspect-bmap-register
+      (phpinspect-pctx-bmap ,pctx) ,start ,end ,token 
(phpinspect-pctx-consume-whitespace ,pctx)))))
+
+
 (defsubst phpinspect-overlay-p (overlay)
   (and (listp overlay)
        (eq 'overlay (car overlay))))
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index 67cf60f3f3..fee8228adc 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -23,6 +23,7 @@
 
 ;;; Code:
 
+(require 'phpinspect-class)
 (require 'phpinspect-parser)
 (require 'phpinspect-bmap)
 (require 'phpinspect-edtrack)
@@ -77,7 +78,7 @@ linked with."
         (let* ((map (phpinspect-make-bmap))
                (buffer-map (phpinspect-buffer-map buffer))
                (ctx (phpinspect-make-pctx
-                     :interrupt-predicate (unless no-interrupt 
#'input-pending-p)
+                     :interrupt-predicate (unless no-interrupt 
#'phpinspect--input-pending-p)
                      :bmap map
                      :incremental t
                      :previous-bmap buffer-map
diff --git a/phpinspect-cache.el b/phpinspect-cache.el
index 1b947744f5..9bc2e8ca21 100644
--- a/phpinspect-cache.el
+++ b/phpinspect-cache.el
@@ -65,6 +65,13 @@ currently opened projects."
   ((cache phpinspect--cache) (project-root string))
   (gethash project-root (phpinspect--cache-projects cache)))
 
+(defun phpinspect-get-or-create-cached-project-class (project-root class-fqn)
+  (when project-root
+    (let ((project (phpinspect--cache-get-project-create
+                    (phpinspect--get-or-create-global-cache)
+                    project-root)))
+      (phpinspect-project-get-class-create project class-fqn))))
+
 (cl-defmethod phpinspect--cache-get-project-create
   ((cache phpinspect--cache) (project-root string))
     "Get a project that is located in PROJECT-ROOT from CACHE.
@@ -78,11 +85,43 @@ then returned."
                               :root project-root
                               :worker (phpinspect-make-dynamic-worker))
                              (phpinspect--cache-projects cache)))
-      (let ((autoloader (phpinspect-make-autoloader :project project)))
-        (setf (phpinspect-project-autoload project) autoloader)
+      (let ((autoloader (phpinspect-make-autoloader
+                         :fs (phpinspect-project-fs project)
+                         :file-indexer (phpinspect-project-make-file-indexer 
project)
+                         :project-root-resolver 
(phpinspect-project-make-root-resolver project))))        (setf 
(phpinspect-project-autoload project) autoloader)
         (phpinspect-autoloader-refresh autoloader)
         (phpinspect-project-enqueue-include-dirs project)))
     project))
 
+(defun phpinspect-project-enqueue-include-dirs (project)
+  (interactive (list (phpinspect--cache-get-project-create
+                      (phpinspect--get-or-create-global-cache)
+                      (phpinspect-current-project-root))))
+  (let ((dirs (alist-get 'include-dirs
+                         (alist-get (phpinspect-project-root project)
+                                    phpinspect-projects
+                                    nil nil #'string=))))
+    (dolist (dir dirs)
+      (message "enqueueing dir %s" dir)
+      (phpinspect-worker-enqueue
+       (phpinspect-project-worker project)
+       (phpinspect-make-index-dir-task :dir dir :project project)))))
+
+(defun phpinspect-project-add-include-dir (dir)
+  "Configure DIR as an include dir for the current project."
+  (interactive (list (read-directory-name "Include Directory: ")))
+  (custom-set-variables '(phpinspect-projects))
+  (let ((existing
+         (alist-get (phpinspect-current-project-root) phpinspect-projects nil 
#'string=)))
+    (if existing
+        (push dir (alist-get 'include-dirs existing))
+      (push `(,(phpinspect-current-project-root) . ((include-dirs . (,dir)))) 
phpinspect-projects)))
+
+  (customize-save-variable 'phpinspect-projects phpinspect-projects)
+
+  (phpinspect-project-enqueue-include-dirs 
(phpinspect--cache-get-project-create
+                                            
(phpinspect--get-or-create-global-cache)
+                                            
(phpinspect-current-project-root))))
+
 (provide 'phpinspect-cache)
 ;;; phpinspect.el ends here
diff --git a/phpinspect-changeset.el b/phpinspect-changeset.el
index 7f19ed2201..406a9ba359 100644
--- a/phpinspect-changeset.el
+++ b/phpinspect-changeset.el
@@ -23,6 +23,9 @@
 
 ;;; Code:
 
+(eval-when-compile
+  (require 'phpinspect-meta))
+
 (define-inline phpinspect-make-changeset (meta)
   (inline-letevals (meta)
     (inline-quote
@@ -48,17 +51,6 @@
 (define-inline phpinspect-changeset-meta (set)
   (inline-quote (car (nthcdr 5 ,set))))
 
-(define-inline phpinspect-meta-with-changeset (meta &rest body)
-  (declare (indent 1))
-  (inline-letevals (meta)
-    (push 'progn body)
-    (inline-quote
-     (progn
-       (when phpinspect-parse-context
-         (phpinspect-pctx-register-changeset
-          phpinspect-parse-context (phpinspect-make-changeset ,meta)))
-       ,body))))
-
 (define-inline phpinspect-changeset-revert (changeset)
   (inline-letevals (changeset)
     (inline-quote
diff --git a/phpinspect-class-struct.el b/phpinspect-class-struct.el
new file mode 100644
index 0000000000..9b91aaa0c2
--- /dev/null
+++ b/phpinspect-class-struct.el
@@ -0,0 +1,68 @@
+;;; phpinspect-class-struct.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <de...@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+
+(cl-defstruct (phpinspect--class (:constructor 
phpinspect--make-class-generated))
+  (project nil
+           :type phpinspect-project
+           :documentaton
+           "The project that this class belongs to")
+  (index nil
+         :type phpinspect--indexed-class
+         :documentation
+         "The index that this class is derived from")
+  (methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
+           :type hash-table
+           :documentation
+           "All methods, including those from extended classes.")
+  (static-methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
+                  :type hash-table
+                  :documentation
+                  "All static methods this class provides,
+                  including those from extended classes.")
+  (name nil
+        :type phpinspect--type)
+  (variables nil
+             :type list
+             :documentation
+             "Variables that belong to this class.")
+  (extended-classes nil
+                    :type list
+                    :documentation
+                    "All extended/implemented classes.")
+  (subscriptions (make-hash-table :test #'eq :size 10 :rehash-size 1.5)
+                 :type hash-table
+                 :documentation
+                 "A list of subscription functions that should be
+                 called whenever anything about this class is
+                 updated")
+  (declaration nil)
+  (initial-index nil
+                 :type bool
+                 :documentation
+                 "A boolean indicating whether or not this class
+                 has been indexed yet."))
+
+(provide 'phpinspect-class-struct)
diff --git a/phpinspect-class.el b/phpinspect-class.el
index d5e7221bf3..d40a90282b 100644
--- a/phpinspect-class.el
+++ b/phpinspect-class.el
@@ -26,10 +26,11 @@
 (require 'phpinspect-type)
 
 (cl-defstruct (phpinspect--class (:constructor 
phpinspect--make-class-generated))
-  (project nil
-           :type phpinspect-project
-           :documentaton
-           "The project that this class belongs to")
+  (class-retriever nil
+                   :type lambda
+                   :documentaton
+                   "A function that returns classes for types
+(should accept `phpinspect--type' as argument)")
   (index nil
          :type phpinspect--indexed-class
          :documentation
@@ -76,8 +77,7 @@
          #'phpinspect--class-p
          (mapcar
           (lambda (class-name)
-            (phpinspect-project-get-class-create (phpinspect--class-project 
class)
-                                                 class-name))
+            (funcall (phpinspect--class-class-retriever class) class-name))
           extensions)))
 
   (dolist (extended (phpinspect--class-extended-classes class))
diff --git a/phpinspect-edtrack.el b/phpinspect-edtrack.el
index bd065932ca..45baa3acb7 100644
--- a/phpinspect-edtrack.el
+++ b/phpinspect-edtrack.el
@@ -26,6 +26,7 @@
 (require 'phpinspect-util)
 
 (eval-when-compile
+  (require 'phpinspect-meta)
   (phpinspect--declare-log-group 'edtrack))
 
 (cl-defstruct (phpinspect-edtrack (:constructor phpinspect-make-edtrack))
@@ -40,44 +41,9 @@
                    :type integer
                    :documentation "Last registered edit start position"))
 
-(defsubst phpinspect-edtrack-make-taint-iterator (track)
-  (cons (car (phpinspect-edtrack-taint-pool track))
-        (cl-copy-list (cdr (phpinspect-edtrack-taint-pool track)))))
-
-(define-inline phpinspect-taint-iterator-current (iter)
-  (inline-quote (car ,iter)))
-
-(define-inline phpinspect-taint-iterator-follow (iter pos)
-  (inline-letevals (iter pos)
-    (inline-quote
-     (or (while (and (phpinspect-taint-iterator-current ,iter)
-                     (> ,pos (phpinspect-taint-end
-                              (phpinspect-taint-iterator-current ,iter))))
-           (setf (phpinspect-taint-iterator-current ,iter) (pop (cdr ,iter))))
-         (phpinspect-taint-iterator-current ,iter)))))
-
-(define-inline phpinspect-taint-iterator-token-is-tainted-p (iter meta)
-  (inline-letevals (iter meta)
-    (inline-quote
-     (and (phpinspect-taint-iterator-follow ,iter (phpinspect-meta-start 
,meta))
-          (phpinspect-taint-overlaps-meta
-           (phpinspect-taint-iterator-current ,iter) ,meta)))))
-
-(define-inline phpinspect-taint-iterator-region-is-tainted-p (iter start end)
-  (inline-letevals (iter start end)
-    (inline-quote
-     (and (phpinspect-taint-iterator-follow ,iter ,start)
-          (phpinspect-taint-overlaps-region
-           (phpinspect-taint-iterator-current ,iter) ,start ,end)))))
-
 (defsubst phpinspect-edit-original-end (edit)
   (or (caar edit) 0))
 
-(defsubst phpinspect-edit-end (edit)
-  (let ((end (or (caar edit) 0))
-        (previous-edit (cdr edit)))
-    (+ end (phpinspect-edit-delta previous-edit))))
-
 (defsubst phpinspect-edit-delta (edit)
   (let ((delta (or (cdar edit) 0))
         (previous-edit edit))
@@ -85,6 +51,11 @@
       (setq delta (+ delta (cdar previous-edit))))
     delta))
 
+(defsubst phpinspect-edit-end (edit)
+  (let ((end (or (caar edit) 0))
+        (previous-edit (cdr edit)))
+    (+ end (phpinspect-edit-delta previous-edit))))
+
 (defsubst phpinspect-edtrack-original-position-at-point (track point)
   (let ((edit (phpinspect-edtrack-edits track))
         (encroached)
@@ -113,48 +84,14 @@
         (- pos encroached)
       pos)))
 
+(define-inline phpinspect-taint-start (taint)
+  (inline-quote (car ,taint)))
 
-(defsubst phpinspect-edtrack-register-edit (track start end pre-change-length)
-  (phpinspect--log
-   "Edtrack registered change: [start: %d, end: %d, pre-change-length: %d]"
-   start end pre-change-length)
-
-  (let ((original-start (phpinspect-edtrack-original-position-at-point track 
start)))
-    (phpinspect-edtrack-register-taint
-     track original-start (+ original-start pre-change-length)))
+(define-inline phpinspect-taint-end (taint)
+  (inline-quote (cdr ,taint)))
 
-  (let ((edit-before (phpinspect-edtrack-edits track)))
-    (while (and edit-before (< end (phpinspect-edit-end edit-before)))
-      (setq edit-before (cdr edit-before)))
-
-    (let ((delta ;; The delta of this edit.
-           (- (- end start) pre-change-length))
-          new-edit)
-      (setq new-edit (cons
-                        ;; The end location of the edited region, before being
-                        ;; edited, with the delta edits that happened at 
preceding
-                        ;; points in the buffer subtratted. This corresponds 
with
-                        ;; the original position of the region end before the
-                        ;; buffer was ever edited.
-                        (phpinspect-edtrack-original-position-at-point
-                         track (+ start pre-change-length))
-                        delta))
-        (if edit-before
-            (progn
-              (setcdr edit-before (cons (car edit-before) (cdr edit-before)))
-              (setcar edit-before new-edit))
-          (if (phpinspect-edtrack-edits track)
-              (push new-edit (cdr (last (phpinspect-edtrack-edits track))))
-            (push new-edit (phpinspect-edtrack-edits track)))))))
-
-(defsubst phpinspect-taint-start (taint)
-  (car taint))
-
-(defsubst phpinspect-taint-end (taint)
-  (cdr taint))
-
-(defsubst phpinspect-make-taint (start end)
-  (cons start end))
+(define-inline phpinspect-make-taint (start end)
+  (inline-quote (cons ,start ,end)))
 
 (defsubst phpinspect-taint-overlaps-point (taint point)
   (and (> (phpinspect-taint-end taint) point)
@@ -180,15 +117,6 @@
       (phpinspect-meta-overlaps-point meta (phpinspect-taint-start taint))
       (phpinspect-meta-overlaps-point meta (phpinspect-taint-end taint))))
 
-(defsubst phpinspect-edtrack-clear-taint-pool (track)
-  (setf (phpinspect-edtrack-taint-pool track) nil))
-
-(defsubst phpinspect-edtrack-clear (track)
-  (setf (phpinspect-edtrack-edits track) nil)
-  (setf (phpinspect-edtrack-last-edit track) nil)
-  (setf (phpinspect-edtrack-last-edit-start track) -1)
-  (phpinspect-edtrack-clear-taint-pool track))
-
 (defsubst phpinspect-edtrack-register-taint (track start end)
   (let ((pool (phpinspect-edtrack-taint-pool track))
         (idx 0)
@@ -229,5 +157,79 @@
           (t
            (push taint (phpinspect-edtrack-taint-pool track))))))
 
+(defsubst phpinspect-edtrack-register-edit (track start end pre-change-length)
+  (phpinspect--log
+   "Edtrack registered change: [start: %d, end: %d, pre-change-length: %d]"
+   start end pre-change-length)
+
+  (let ((original-start (phpinspect-edtrack-original-position-at-point track 
start)))
+    (phpinspect-edtrack-register-taint
+     track original-start (+ original-start pre-change-length)))
+
+  (let ((edit-before (phpinspect-edtrack-edits track)))
+    (while (and edit-before (< end (phpinspect-edit-end edit-before)))
+      (setq edit-before (cdr edit-before)))
+
+    (let ((delta ;; The delta of this edit.
+           (- (- end start) pre-change-length))
+          new-edit)
+      (setq new-edit (cons
+                        ;; The end location of the edited region, before being
+                        ;; edited, with the delta edits that happened at 
preceding
+                        ;; points in the buffer subtratted. This corresponds 
with
+                        ;; the original position of the region end before the
+                        ;; buffer was ever edited.
+                        (phpinspect-edtrack-original-position-at-point
+                         track (+ start pre-change-length))
+                        delta))
+        (if edit-before
+            (progn
+              (setcdr edit-before (cons (car edit-before) (cdr edit-before)))
+              (setcar edit-before new-edit))
+          (if (phpinspect-edtrack-edits track)
+              (push new-edit (cdr (last (phpinspect-edtrack-edits track))))
+            (push new-edit (phpinspect-edtrack-edits track)))))))
+
+
+(defsubst phpinspect-edtrack-clear-taint-pool (track)
+  (setf (phpinspect-edtrack-taint-pool track) nil))
+
+(defsubst phpinspect-edtrack-clear (track)
+  (setf (phpinspect-edtrack-edits track) nil)
+  (setf (phpinspect-edtrack-last-edit track) nil)
+  (setf (phpinspect-edtrack-last-edit-start track) -1)
+  (phpinspect-edtrack-clear-taint-pool track))
+
+
+(defsubst phpinspect-edtrack-make-taint-iterator (track)
+  (cons (car (phpinspect-edtrack-taint-pool track))
+        (cl-copy-list (cdr (phpinspect-edtrack-taint-pool track)))))
+
+(define-inline phpinspect-taint-iterator-current (iter)
+  (inline-quote (car ,iter)))
+
+(define-inline phpinspect-taint-iterator-follow (iter pos)
+  (inline-letevals (iter pos)
+    (inline-quote
+     (or (while (and (phpinspect-taint-iterator-current ,iter)
+                     (> ,pos (phpinspect-taint-end
+                              (phpinspect-taint-iterator-current ,iter))))
+           (setf (phpinspect-taint-iterator-current ,iter) (pop (cdr ,iter))))
+         (phpinspect-taint-iterator-current ,iter)))))
+
+(define-inline phpinspect-taint-iterator-token-is-tainted-p (iter meta)
+  (inline-letevals (iter meta)
+    (inline-quote
+     (and (phpinspect-taint-iterator-follow ,iter (phpinspect-meta-start 
,meta))
+          (phpinspect-taint-overlaps-meta
+           (phpinspect-taint-iterator-current ,iter) ,meta)))))
+
+(define-inline phpinspect-taint-iterator-region-is-tainted-p (iter start end)
+  (inline-letevals (iter start end)
+    (inline-quote
+     (and (phpinspect-taint-iterator-follow ,iter ,start)
+          (phpinspect-taint-overlaps-region
+           (phpinspect-taint-iterator-current ,iter) ,start ,end)))))
+
 (provide 'phpinspect-edtrack)
 ;;; phpinspect-edtrack.el ends here
diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el
index b60bb885d2..0de18be472 100644
--- a/phpinspect-eldoc.el
+++ b/phpinspect-eldoc.el
@@ -24,8 +24,9 @@
 ;;; Code:
 (require 'phpinspect-util)
 (require 'phpinspect-meta)
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 (require 'phpinspect-resolve)
+(require 'phpinspect-buffer)
 
 (defvar phpinspect-eldoc-word-width 14
   "The maximum width of words in eldoc strings.")
diff --git a/phpinspect-imports.el b/phpinspect-imports.el
index df37ec6195..fb42af3ffc 100644
--- a/phpinspect-imports.el
+++ b/phpinspect-imports.el
@@ -25,7 +25,7 @@
 
 ;;; Code:
 
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 (require 'phpinspect-index)
 (require 'phpinspect-autoload)
 (require 'phpinspect-buffer)
@@ -80,15 +80,13 @@ buffer position to insert the use statement at."
            (format "use %s;%c" fqn ?\n))
         (let* ((first-token (phpinspect-meta-first-child 
(phpinspect-buffer-root-meta buffer)))
                token-after)
-          (message "First token %s" (phpinspect-meta-string first-token))
           (when (and (phpinspect-word-p (phpinspect-meta-token first-token))
                      (string= "declare" (cadr (phpinspect-meta-token 
first-token))))
             (progn
               (setq token-after first-token)
               (while (and token-after (not (phpinspect-terminator-p
                                             (phpinspect-meta-token 
token-after))))
-                (setq token-after (phpinspect-meta-find-right-sibling 
token-after))
-                (message "Token after: %s" (phpinspect-meta-string 
token-after)))))
+                (setq token-after (phpinspect-meta-find-right-sibling 
token-after)))))
           (if token-after
               (phpinspect-insert-at-point
                (phpinspect-meta-end token-after) (format "%c%cuse %s;%c" ?\n 
?\n fqn ?\n))
@@ -155,8 +153,6 @@ that there are import (\"use\") statements for them."
                  (class-name (alist-get 'class-name class))
                  (region (alist-get 'location class))
                  token-meta)
-            (message "Region: %s" region)
-            (message "index: %s" index)
             (setq token-meta (phpinspect-meta-find-parent-matching-token
                               (phpinspect-bmap-last-token-before-point
                                (phpinspect-buffer-map buffer)
diff --git a/phpinspect-index.el b/phpinspect-index.el
index 6c76a526b5..a4b7c919bd 100644
--- a/phpinspect-index.el
+++ b/phpinspect-index.el
@@ -25,9 +25,8 @@
 
 (require 'cl-lib)
 (require 'phpinspect-util)
-(require 'phpinspect-project)
 (require 'phpinspect-type)
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 
 (defun phpinspect--function-from-scope (scope)
   (cond ((and (phpinspect-static-p (cadr scope))
@@ -158,15 +157,6 @@ function (think \"new\" statements, return types etc.)."
 (defun phpinspect-doc-block-p (token)
   (phpinspect-token-type-p token :doc-block))
 
-(defun phpinspect--get-class-name-from-token (class-token)
-  (let ((subtoken (seq-find (lambda (word)
-                              (and (phpinspect-word-p word)
-                                   (not (string-match
-                                         (concat "^" 
(phpinspect-handler-regexp class-keyword))
-                                         (concat (cadr word) " ")))))
-                            (cadr class-token))))
-    (cadr subtoken)))
-
 
 (defsubst phpinspect--index-method-annotations (type-resolver comment)
   (let ((annotations (seq-filter #'phpinspect-method-annotation-p comment))
@@ -195,40 +185,6 @@ function (think \"new\" statements, return types etc.)."
                   methods)))))
     methods))
 
-(defun phpinspect--index-class-declaration (decl type-resolver)
-  ;; Find out what the class extends or implements
-  (let (encountered-extends encountered-implements encountered-class
-        class-name extends implements used-types)
-    (dolist (word decl)
-      (if (phpinspect-word-p word)
-          (cond ((string= (cadr word) "extends")
-                 (phpinspect--log "Class %s extends other classes" class-name)
-                 (setq encountered-extends t))
-                ((string= (cadr word) "implements")
-                 (setq encountered-extends nil)
-                 (phpinspect--log "Class %s implements in interface" 
class-name)
-                 (setq encountered-implements t))
-                ((string= (cadr word) "class")
-                 (setq encountered-class t))
-                (t
-                 (phpinspect--log "Calling Resolver from index-class on %s" 
(cadr word))
-                 (cond (encountered-extends
-                        (push (funcall type-resolver (phpinspect--make-type
-                                                      :name (cadr word)))
-                              extends)
-                        (push (cadr word) used-types))
-                       (encountered-implements
-                        (push (funcall type-resolver (phpinspect--make-type
-                                                      :name (cadr word)))
-                              implements)
-                        (push (cadr word) used-types))
-                       (encountered-class
-                        (setq class-name (funcall type-resolver 
(phpinspect--make-type :name (cadr word)))
-                              encountered-class nil)))))))
-
-    (list class-name extends implements used-types)))
-
-
 (defun phpinspect--index-class (imports type-resolver location-resolver class 
&optional doc-block)
   "Create an alist with relevant attributes of a parsed class."
   (phpinspect--log "INDEXING CLASS")
@@ -395,22 +351,6 @@ NAMESPACE will be assumed the root namespace if not 
provided"
              (setq comment-before nil))))
     indexed))
 
-(defun phpinspect--use-to-type (use)
-  (let* ((fqn (cadr (cadr use)))
-         (type (phpinspect--make-type :name (if (string-match "^\\\\" fqn)
-                                                fqn
-                                              (concat "\\" fqn))
-                                      :fully-qualified t))
-         (type-name (if (and (phpinspect-word-p (caddr use))
-                             (string= "as" (cadr (caddr use))))
-                        (cadr (cadddr use))
-                      (progn (string-match "[^\\]+$" fqn)
-                             (match-string 0 fqn)))))
-    (cons (phpinspect-intern-name type-name) type)))
-
-(defun phpinspect--uses-to-types (uses)
-  (mapcar #'phpinspect--use-to-type uses))
-
 (defun phpinspect--index-namespace (namespace type-resolver-factory 
location-resolver)
   (let* (used-types
          (index
@@ -530,14 +470,6 @@ Return value is a list of the types that are \"newed\"."
       nil))
    '(phpinspect--root-index)))
 
-(defun phpinspect-get-or-create-cached-project-class (project-root class-fqn)
-  (when project-root
-    (let ((project (phpinspect--cache-get-project-create
-                    (phpinspect--get-or-create-global-cache)
-                    project-root)))
-      (phpinspect-project-get-class-create project class-fqn))))
-
-
 (cl-defmethod phpinspect-index-get-class
   ((index (head phpinspect--root-index)) (class-name phpinspect--type))
   (alist-get class-name (alist-get 'classes index)
diff --git a/phpinspect-parse-context.el b/phpinspect-parse-context.el
index bc0744416f..a42add3402 100644
--- a/phpinspect-parse-context.el
+++ b/phpinspect-parse-context.el
@@ -26,7 +26,6 @@
 (require 'phpinspect-util)
 (require 'phpinspect-meta)
 (require 'phpinspect-changeset)
-(require 'phpinspect-bmap)
 
 (defvar phpinspect-parse-context nil
   "An instance of `phpinspect-pctx' that is used when
@@ -55,7 +54,7 @@ thrown.")
               :documentation "Metadata change sets executed during this parse")
   (edtrack nil
            :type phpinspect-edtrack)
-  (bmap (phpinspect-make-bmap)
+  (bmap nil
         :type phpinspect-bmap)
   (previous-bmap nil
                  :type phpinspect-bmap)
@@ -94,6 +93,18 @@ thrown.")
    (progn
      (push ,changeset (phpinspect-pctx-changesets ,pctx)))))
 
+(define-inline phpinspect-meta-with-changeset (meta &rest body)
+  (declare (indent 1))
+  (inline-letevals (meta)
+    (push 'progn body)
+    (inline-quote
+     (progn
+       (when phpinspect-parse-context
+         (phpinspect-pctx-register-changeset
+          phpinspect-parse-context (phpinspect-make-changeset ,meta)))
+       ,body))))
+
+
 (define-inline phpinspect-pctx-check-interrupt (pctx)
   (inline-letevals (pctx)
     (inline-quote
@@ -108,12 +119,6 @@ thrown.")
          (phpinspect-pctx-cancel ,pctx)
          (throw 'phpinspect-parse-interrupted nil))))))
 
-(define-inline phpinspect-pctx-register-token (pctx token start end)
-  (inline-letevals (pctx)
-    (inline-quote
-     (phpinspect-bmap-register
-      (phpinspect-pctx-bmap ,pctx) ,start ,end ,token 
(phpinspect-pctx-consume-whitespace ,pctx)))))
-
 (define-inline phpinspect-pctx-register-whitespace (pctx whitespace)
   (inline-quote
    (setf (phpinspect-pctx-whitespace-before ,pctx) ,whitespace)))
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index 34561a25e5..59b9aa3220 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -53,14 +53,15 @@ If STRING has text properties, they are stripped."
     (set-text-properties 0 length nil value)
     (list token-keyword value)))
 
-(defun phpinspect-handler-func-name (handler-name)
-  (intern (concat "phpinspect--" (symbol-name handler-name) "-handler")))
+(eval-when-compile
+  (defun phpinspect-handler-func-name (handler-name)
+    (intern (concat "phpinspect--" (symbol-name handler-name) "-handler")))
 
-(defun phpinspect-handler-regexp-func-name (handler-name)
-  (intern (concat "phpinspect--" (symbol-name handler-name) 
"-handler-regexp")))
+  (defun phpinspect-handler-regexp-func-name (handler-name)
+    (intern (concat "phpinspect--" (symbol-name handler-name) 
"-handler-regexp")))
 
-(defun phpinspect-parser-func-name (name &optional suffix)
-  (intern (concat "phpinspect--parse-" (symbol-name name) (if suffix (concat 
"-" suffix) ""))))
+  (defun phpinspect-parser-func-name (name &optional suffix)
+    (intern (concat "phpinspect--parse-" (symbol-name name) (if suffix (concat 
"-" suffix) "")))))
 
 (defmacro phpinspect-defhandler (name arguments docstring attribute-alist 
&rest body)
   "Define a parser handler that becomes available for use with 
phpinspect-parse.
@@ -130,8 +131,9 @@ You can purge the parser cache with 
\\[phpinspect-purge-parser-cache]."
 
        (put (quote ,inline-name) 'phpinspect--handler t))))
 
-(defun phpinspect-make-parser-function (name tree-type handlers &optional 
delimiter-predicate)
-  "Create a parser function using the handlers by names defined in 
HANDLER-LIST.
+(eval-when-compile
+  (defun phpinspect-make-parser-function (name tree-type handlers &optional 
delimiter-predicate)
+    "Create a parser function using the handlers by names defined in 
HANDLER-LIST.
 
 See also `phpinspect-defhandler`.
 
@@ -148,170 +150,170 @@ loop exits.  An example use case of this is to 
determine the end
 of a statement.  You can use `phpinspect-terminator-p` as
 delimiter predicate and have parsing stop when the last parsed
 token is \";\", which marks the end of a statement in PHP."
-  (let ((delimiter-predicate (if (symbolp delimiter-predicate)
-                                 `(quote ,delimiter-predicate)
-                               delimiter-predicate)))
-    `(defsubst ,(phpinspect-parser-func-name name "simple") (buffer max-point 
&optional skip-over continue-condition &rest _ignored)
-       (with-current-buffer buffer
-         (let* ((tokens (cons ,tree-type nil))
-                (tokens-rear tokens)
-                token
-                (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
-           (when skip-over (forward-char skip-over))
-           (while (and (< (point) max-point)
-                       (if continue-condition (funcall continue-condition) t)
-                       (not (if delimiter-predicate
-                                (funcall delimiter-predicate (car (last 
tokens)))
-                              nil)))
-             (cond ,@(mapcar
-                      (lambda (handler)
-                        `((looking-at (,(phpinspect-handler-regexp-func-name 
handler)))
-                          (setq token (,(phpinspect-handler-func-name handler) 
(match-string 0) max-point))
-                          (when token
-                            (setq tokens-rear (setcdr tokens-rear (cons token 
nil))))))
-                      handlers)
-                   (t (forward-char))))
-
-           ;; Return
-           tokens)))))
-
-
-(defun phpinspect-make-incremental-parser-function (name tree-type handlers 
&optional delimiter-predicate)
-  "Like `phpinspect-make-parser-function', but returned function
+    (let ((delimiter-predicate (if (symbolp delimiter-predicate)
+                                   `(quote ,delimiter-predicate)
+                                 delimiter-predicate)))
+      `(defsubst ,(phpinspect-parser-func-name name "simple") (buffer 
max-point &optional skip-over continue-condition &rest _ignored)
+         (with-current-buffer buffer
+           (let* ((tokens (cons ,tree-type nil))
+                  (tokens-rear tokens)
+                  token
+                  (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
+             (when skip-over (forward-char skip-over))
+             (while (and (< (point) max-point)
+                         (if continue-condition (funcall continue-condition) t)
+                         (not (if delimiter-predicate
+                                  (funcall delimiter-predicate (car (last 
tokens)))
+                                nil)))
+               (cond ,@(mapcar
+                        (lambda (handler)
+                          `((looking-at (,(phpinspect-handler-regexp-func-name 
handler)))
+                            (setq token (,(phpinspect-handler-func-name 
handler) (match-string 0) max-point))
+                            (when token
+                              (setq tokens-rear (setcdr tokens-rear (cons 
token nil))))))
+                        handlers)
+                     (t (forward-char))))
+
+             ;; Return
+             tokens)))))
+
+
+  (defun phpinspect-make-incremental-parser-function (name tree-type handlers 
&optional delimiter-predicate)
+    "Like `phpinspect-make-parser-function', but returned function
 is able to reuse an already parsed tree."
-(let ((delimiter-predicate (if (symbolp delimiter-predicate)
-                                 `(quote ,delimiter-predicate)
-                               delimiter-predicate)))
-    `(defsubst ,(phpinspect-parser-func-name name "incremental") (context 
buffer max-point &optional skip-over continue-condition root)
-       (with-current-buffer buffer
-         (let* ((tokens (cons ,tree-type nil))
-                (tokens-rear tokens)
-                (root-start (point))
-                (bmap (phpinspect-pctx-bmap context))
-                (previous-bmap (phpinspect-pctx-previous-bmap context))
-                (edtrack (phpinspect-pctx-edtrack context))
-                (taint-iterator (when edtrack 
(phpinspect-edtrack-make-taint-iterator edtrack)))
-                (check-interrupt (phpinspect-pctx-interrupt-predicate context))
-
-                ;; Loop variables
-                (start-position)
-                (original-position)
-                (current-end-position)
-                (existing-meta)
-                (delta)
-                (token)
-                (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
-           (when skip-over (forward-char skip-over))
-           (phpinspect-pctx-save-whitespace context
-            (while (and (< (point) max-point)
-                        (if continue-condition (funcall continue-condition) t)
-                        (not (if delimiter-predicate
-                                 (funcall delimiter-predicate (car (last 
tokens)))
-                               nil)))
-              (when check-interrupt
-                (phpinspect-pctx-check-interrupt context))
-
-              (setq start-position (point))
-              (cond ((and previous-bmap edtrack
-                          (setq existing-meta
-                                (phpinspect-bmap-token-starting-at
-                                 previous-bmap
-                                 (setq original-position
-                                       
(phpinspect-edtrack-original-position-at-point edtrack start-position))))
-                          (not (or (phpinspect-root-p (phpinspect-meta-token 
existing-meta))
-                                   
(phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta))))
-                     (setq delta (- start-position original-position)
-                           current-end-position (+ (phpinspect-meta-end 
existing-meta) delta)
-                           token (phpinspect-meta-token existing-meta))
-
-                     ;;(message "Reusing token  %s at point %s" 
(phpinspect-meta-string existing-meta) (point))
-                     ;; Re-register existing token
-                     (phpinspect-bmap-overlay
-                      bmap previous-bmap existing-meta delta
-                      (phpinspect-pctx-consume-whitespace context))
-
-                     (goto-char current-end-position)
-
-                     ;; Skip over whitespace after so that we don't do a full
-                     ;; run down all of the handlers during the next iteration
-                     (when (looking-at (phpinspect-handler-regexp whitespace))
-                       (,(phpinspect-handler-func-name 'whitespace) 
(match-string 0))))
-                    ,@(mapcar
-                       (lambda (handler)
-                         `((looking-at (,(phpinspect-handler-regexp-func-name 
handler)))
-                           (setq token (,(phpinspect-handler-func-name 
handler) (match-string 0) max-point))
-                           (when token
-                             (phpinspect-pctx-register-token context token 
start-position (point)))))
-                       handlers)
-                    (t (forward-char)))
-              (when token
-                (setq tokens-rear (setcdr tokens-rear (cons token nil)))
-                (setq token nil))))
-           (when root
-             (phpinspect-pctx-register-token context tokens root-start 
(point)))
-
-         ;; Return
-         tokens)))))
-
-(cl-defstruct (phpinspect-parser (:constructor phpinspect-make-parser))
-  (name 'root
-        :type symbol
-        :read-only t)
-  (tree-keyword "root"
-                :type string
-                :read-only t
-                :documentation "Name of the keyword that is used as car of the
+    (let ((delimiter-predicate (if (symbolp delimiter-predicate)
+                                   `(quote ,delimiter-predicate)
+                                 delimiter-predicate)))
+      `(defsubst ,(phpinspect-parser-func-name name "incremental") (context 
buffer max-point &optional skip-over continue-condition root)
+         (with-current-buffer buffer
+           (let* ((tokens (cons ,tree-type nil))
+                  (tokens-rear tokens)
+                  (root-start (point))
+                  (bmap (phpinspect-pctx-bmap context))
+                  (previous-bmap (phpinspect-pctx-previous-bmap context))
+                  (edtrack (phpinspect-pctx-edtrack context))
+                  (taint-iterator (when edtrack 
(phpinspect-edtrack-make-taint-iterator edtrack)))
+                  (check-interrupt (phpinspect-pctx-interrupt-predicate 
context))
+
+                  ;; Loop variables
+                  (start-position)
+                  (original-position)
+                  (current-end-position)
+                  (existing-meta)
+                  (delta)
+                  (token)
+                  (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
+             (when skip-over (forward-char skip-over))
+             (phpinspect-pctx-save-whitespace context
+               (while (and (< (point) max-point)
+                           (if continue-condition (funcall continue-condition) 
t)
+                           (not (if delimiter-predicate
+                                    (funcall delimiter-predicate (car (last 
tokens)))
+                                  nil)))
+                 (when check-interrupt
+                   (phpinspect-pctx-check-interrupt context))
+
+                 (setq start-position (point))
+                 (cond ((and previous-bmap edtrack
+                             (setq existing-meta
+                                   (phpinspect-bmap-token-starting-at
+                                    previous-bmap
+                                    (setq original-position
+                                          
(phpinspect-edtrack-original-position-at-point edtrack start-position))))
+                             (not (or (phpinspect-root-p 
(phpinspect-meta-token existing-meta))
+                                      
(phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta))))
+                        (setq delta (- start-position original-position)
+                              current-end-position (+ (phpinspect-meta-end 
existing-meta) delta)
+                              token (phpinspect-meta-token existing-meta))
+
+                        ;;(message "Reusing token  %s at point %s" 
(phpinspect-meta-string existing-meta) (point))
+                        ;; Re-register existing token
+                        (phpinspect-bmap-overlay
+                         bmap previous-bmap existing-meta delta
+                         (phpinspect-pctx-consume-whitespace context))
+
+                        (goto-char current-end-position)
+
+                        ;; Skip over whitespace after so that we don't do a 
full
+                        ;; run down all of the handlers during the next 
iteration
+                        (when (looking-at (phpinspect-handler-regexp 
whitespace))
+                          (,(phpinspect-handler-func-name 'whitespace) 
(match-string 0))))
+                       ,@(mapcar
+                          (lambda (handler)
+                            `((looking-at 
(,(phpinspect-handler-regexp-func-name handler)))
+                              (setq token (,(phpinspect-handler-func-name 
handler) (match-string 0) max-point))
+                              (when token
+                                (phpinspect-pctx-register-token context token 
start-position (point)))))
+                          handlers)
+                       (t (forward-char)))
+                 (when token
+                   (setq tokens-rear (setcdr tokens-rear (cons token nil)))
+                   (setq token nil))))
+             (when root
+               (phpinspect-pctx-register-token context tokens root-start 
(point)))
+
+             ;; Return
+             tokens)))))
+
+  (cl-defstruct (phpinspect-parser (:constructor phpinspect-make-parser))
+    (name 'root
+          :type symbol
+          :read-only t)
+    (tree-keyword "root"
+                  :type string
+                  :read-only t
+                  :documentation "Name of the keyword that is used as car of 
the
 root token, in string form without \":\" prefix.")
-  (handlers '(array tag equals list comma
-                         attribute-reference variable
-                         assignment-operator whitespace scope-keyword
-                         static-keyword const-keyword use-keyword
-                         class-keyword function-keyword word terminator
-                         here-doc string comment block)
-            :type list
-            :read-only t
-            :documentation "A list of symbols referring to the
+    (handlers '(array tag equals list comma
+                      attribute-reference variable
+                      assignment-operator whitespace scope-keyword
+                      static-keyword const-keyword use-keyword
+                      class-keyword function-keyword word terminator
+                      here-doc string comment block)
+              :type list
+              :read-only t
+              :documentation "A list of symbols referring to the
 handlers that this parser uses.")
-  (delimiter-predicate nil
-                       :type function
-                       :read-only t
-                       :documentation "A predicate function that is passed each
+    (delimiter-predicate nil
+                         :type function
+                         :read-only t
+                         :documentation "A predicate function that is passed 
each
 parsed token. When the predicate returns a non-nil value, the parser stops
 executing.")
-  (func nil
-        :type function
-        :documentation "The parser function.")
-  (incremental-func nil
-                    :type function
-                    :documentation "Incemental parser function"))
-
-(cl-defmethod phpinspect-parser-compile ((parser phpinspect-parser))
-  "Create/return parser function."
-  (or (phpinspect-parser-func parser)
-      (setf (phpinspect-parser-func parser)
-            (phpinspect-make-parser-function
-             (phpinspect-parser-name parser)
-             (intern (concat ":" (phpinspect-parser-tree-keyword parser)))
-             (phpinspect-parser-handlers parser)
-             (phpinspect-parser-delimiter-predicate parser)))))
-
-(cl-defmethod phpinspect-parser-compile-incremental ((parser 
phpinspect-parser))
-  "Like `phpinspect-parser-compile', but for an incremental
+    (func nil
+          :type function
+          :documentation "The parser function.")
+    (incremental-func nil
+                      :type function
+                      :documentation "Incemental parser function"))
+
+  (cl-defmethod phpinspect-parser-compile ((parser phpinspect-parser))
+    "Create/return parser function."
+    (or (phpinspect-parser-func parser)
+        (setf (phpinspect-parser-func parser)
+              (phpinspect-make-parser-function
+               (phpinspect-parser-name parser)
+               (intern (concat ":" (phpinspect-parser-tree-keyword parser)))
+               (phpinspect-parser-handlers parser)
+               (phpinspect-parser-delimiter-predicate parser)))))
+
+  (cl-defmethod phpinspect-parser-compile-incremental ((parser 
phpinspect-parser))
+    "Like `phpinspect-parser-compile', but for an incremental
 version of the parser function."
-  (or (phpinspect-parser-incremental-func parser)
-      (setf (phpinspect-parser-incremental-func parser)
-            (phpinspect-make-incremental-parser-function
-             (phpinspect-parser-name parser)
-             (intern (concat ":" (phpinspect-parser-tree-keyword parser)))
-             (phpinspect-parser-handlers parser)
-             (phpinspect-parser-delimiter-predicate 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"))
-        (simple-name (phpinspect-parser-func-name (phpinspect-parser-name 
parser) "simple")))
-    `(defun ,func-name (buffer max-point &optional skip-over 
continue-condition root)
-       "Parse BUFFER, starting at point and ending at MAX-POINT.
+    (or (phpinspect-parser-incremental-func parser)
+        (setf (phpinspect-parser-incremental-func parser)
+              (phpinspect-make-incremental-parser-function
+               (phpinspect-parser-name parser)
+               (intern (concat ":" (phpinspect-parser-tree-keyword parser)))
+               (phpinspect-parser-handlers parser)
+               (phpinspect-parser-delimiter-predicate 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"))
+          (simple-name (phpinspect-parser-func-name (phpinspect-parser-name 
parser) "simple")))
+      `(defun ,func-name (buffer max-point &optional skip-over 
continue-condition root)
+         "Parse BUFFER, starting at point and ending at MAX-POINT.
 
 If SKIP-OVER is non-nil, it must be a number of characters that
 to skip over before starting to parse.
@@ -326,10 +328,12 @@ returned token tree. So the parser should register the 
metadata
 of the root of its returned tree itself, before
 returning. Currently, token metadata is only registered when
 parsing incrementally."
-       (if (and phpinspect-parse-context
-                (phpinspect-pctx-incremental phpinspect-parse-context))
-           (,incremental-name phpinspect-parse-context buffer max-point 
skip-over continue-condition root)
-         (,simple-name buffer max-point skip-over continue-condition root)))))
+         (if (and phpinspect-parse-context
+                  (phpinspect-pctx-incremental phpinspect-parse-context))
+             (,incremental-name phpinspect-parse-context buffer max-point 
skip-over continue-condition root)
+           (,simple-name buffer max-point skip-over continue-condition 
root)))))
+
+  ) ;; End of eval-when-compile
 
 (defmacro phpinspect-defparser (name &rest parameters)
   (declare (indent 1))
@@ -342,19 +346,20 @@ parsing incrementally."
          (simple-name (phpinspect-parser-func-name name "simple"))
          (incremental-name (phpinspect-parser-func-name name "incremental")))
 
-    `(let ((parser (phpinspect-make-parser ,@parameters)))
-       (defconst ,simple-name nil)
-       (defconst ,incremental-name nil)
+    `(eval-when-compile
+       (let ((parser (phpinspect-make-parser ,@parameters)))
+         (defconst ,simple-name nil)
+         (defconst ,incremental-name nil)
 
-       (put (quote ,simple-name) 'phpinspect--parser t)
-       (put (quote ,incremental-name) 'phpinspect--incremental-parser t)
+         (put (quote ,simple-name) 'phpinspect--parser t)
+         (put (quote ,incremental-name) 'phpinspect--incremental-parser t)
 
-       (setf ,simple-name parser)
-       (setf ,incremental-name parser)
+         (setf ,simple-name parser)
+         (setf ,incremental-name parser)
 
-       ;; Stub function to please the byte compiler (real function will be
-       ;; defined by `phpinspect-define-parser-functions'.
-       (defun ,func-name (_buffer _max-point &optional _continue-condition 
_root)))))
+         ;; Stub function to please the byte compiler (real function will be
+         ;; defined by `phpinspect-define-parser-functions'.
+         (defun ,func-name (_buffer _max-point &optional _skip-over 
_continue-condition _root))))))
 
 (define-inline phpinspect-parser-func-bound-p (symbol)
   (inline-quote (get ,symbol 'phpinspect--parser)))
@@ -410,6 +415,40 @@ parsing incrementally."
   (let ((name (phpinspect-handler-regexp-func-name handler-name)))
     `(,name)))
 
+(phpinspect-defhandler variable (start-token &rest _ignored)
+  "Handler for tokens indicating reference to a variable"
+  ((regexp . "\\$"))
+  (forward-char (length start-token))
+  (if (looking-at (phpinspect-handler-regexp word))
+      (phpinspect-munch-token-without-attribs (match-string 0) :variable)
+    (list :variable nil)))
+
+(phpinspect-defparser list
+  :tree-keyword "list"
+  :handlers '(array tag equals list comma
+                    attribute-reference variable assignment-operator
+                    whitespace function-keyword word terminator here-doc
+                    string comment block-without-scopes))
+
+(phpinspect-defhandler list (start-token max-point)
+  "Handler for php syntactic lists (Note: this does not include
+datatypes like arrays, merely lists that are of a syntactic
+nature like argument lists"
+  ((regexp . "("))
+  (let* ((complete-list nil)
+         (php-list (phpinspect--parse-list
+                    (current-buffer)
+                    max-point
+                    (length start-token)
+                    (lambda () (not (and (char-equal (char-after) ?\)) (setq 
complete-list t)))))))
+
+    (if complete-list
+        ;; Prevent parent-lists (if any) from exiting by skipping over the
+        ;; ")" character
+        (forward-char)
+      (setcar php-list :incomplete-list))
+    php-list))
+
 (defsubst phpinspect--parse-annotation-parameters (parameter-amount)
   (let* ((words)
          (list-regexp (phpinspect-handler-regexp list))
@@ -498,13 +537,6 @@ parsing incrementally."
          (let ((end-position (line-end-position)))
            (phpinspect--parse-comment (current-buffer) end-position)))))
 
-(phpinspect-defhandler variable (start-token &rest _ignored)
-  "Handler for tokens indicating reference to a variable"
-  ((regexp . "\\$"))
-  (forward-char (length start-token))
-  (if (looking-at (phpinspect-handler-regexp word))
-      (phpinspect-munch-token-without-attribs (match-string 0) :variable)
-    (list :variable nil)))
 
 (phpinspect-defhandler class-variable (start-token &rest _ignored)
   "Handler for tokens indicating reference to a variable"
@@ -703,31 +735,6 @@ Returns the consumed text string without face properties."
                                    nil t)
                 (buffer-substring-no-properties start-point (- (point) 
1)))))))))
 
-(phpinspect-defparser list
-  :tree-keyword "list"
-  :handlers '(array tag equals list comma
-                    attribute-reference variable assignment-operator
-                    whitespace function-keyword word terminator here-doc
-                    string comment block-without-scopes))
-
-(phpinspect-defhandler list (start-token max-point)
-  "Handler for php syntactic lists (Note: this does not include
-datatypes like arrays, merely lists that are of a syntactic
-nature like argument lists"
-  ((regexp . "("))
-  (let* ((complete-list nil)
-         (php-list (phpinspect--parse-list
-                    (current-buffer)
-                    max-point
-                    (length start-token)
-                    (lambda () (not (and (char-equal (char-after) ?\)) (setq 
complete-list t)))))))
-
-    (if complete-list
-        ;; Prevent parent-lists (if any) from exiting by skipping over the
-        ;; ")" character
-        (forward-char)
-      (setcar php-list :incomplete-list))
-    php-list))
 
 (phpinspect-defparser declaration
   :tree-keyword "declaration"
@@ -855,13 +862,6 @@ the properties of the class"
                         function-keyword word terminator here-doc string 
comment
                         tag block))
 
-(defun phpinspect-parse-buffer-until-point (buffer point)
-  (with-current-buffer buffer
-    (save-excursion
-      (goto-char (point-min))
-      (re-search-forward "<\\?php\\|<\\?" nil t)
-      (phpinspect--parse-root (current-buffer) point nil nil 'root))))
-
 (defun phpinspect-parse-current-buffer ()
   (phpinspect-parse-buffer-until-point
    (current-buffer)
@@ -878,7 +878,15 @@ the properties of the class"
     (phpinspect-parse-current-buffer)))
 
 ;; Define all registered parser functions
-(phpinspect-define-parser-functions)
+(eval-and-compile
+  (phpinspect-define-parser-functions))
+
+(defun phpinspect-parse-buffer-until-point (buffer point)
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-min))
+      (re-search-forward "<\\?php\\|<\\?" nil t)
+      (phpinspect--parse-root (current-buffer) point nil nil 'root))))
 
 (provide 'phpinspect-parser)
 ;;; phpinspect-parser.el ends here
diff --git a/phpinspect-pipeline.el b/phpinspect-pipeline.el
index 6084c6550d..d13c66725f 100644
--- a/phpinspect-pipeline.el
+++ b/phpinspect-pipeline.el
@@ -129,7 +129,7 @@ directories."
 (define-inline phpinspect-pipeline-pause ()
   "Pause the current pipeline thread"
   (inline-quote
-   (if (input-pending-p)
+   (if (phpinspect--input-pending-p)
        (let ((mx (make-mutex)))
          (phpinspect-thread-pause
           phpinspect-pipeline-pause-time mx (make-condition-variable mx 
"phpinspect-pipeline-pause")))
diff --git a/phpinspect-project.el b/phpinspect-project.el
index 6f8b228fff..dded436c0c 100644
--- a/phpinspect-project.el
+++ b/phpinspect-project.el
@@ -23,6 +23,10 @@
 
 ;;; Code:
 
+(require 'phpinspect-project-struct)
+(require 'phpinspect-autoload)
+(require 'phpinspect-worker)
+(require 'phpinspect-index)
 (require 'phpinspect-class)
 (require 'phpinspect-type)
 (require 'phpinspect-fs)
@@ -45,47 +49,6 @@ serious performance hits. Enable at your own risk (:")
     (set (make-local-variable 'phpinspect--buffer-project) (funcall 
phpinspect-project-root-function)))
   phpinspect--buffer-project)
 
-(cl-defstruct (phpinspect-project (:constructor phpinspect--make-project))
-  (class-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5)
-               :type hash-table
-               :documentation
-               "A `hash-table` that contains all of the currently
-indexed classes in the project")
-  (function-index (make-hash-table :test 'eq :size 100 :rehash-size 2.0)
-                  :type hash-table
-                  :documentation
-                  "A hash able that contains all of the currently indexed 
functions
-in the project")
-  (function-token-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
-  (fs nil
-      :type phpinspect-fs
-      :documentation
-      "The filesystem object through which this project's files
-can be accessed.")
-  (autoload nil
-    :type phpinspect-autoload
-    :documentation
-    "The autoload object through which this project's type
-definitions can be retrieved")
-  (worker (progn
-            (unless (featurep 'phpinspect-worker)
-              (require 'phpinspect-worker))
-            (phpinspect-make-dynamic-worker))
-          :type phpinspect-worker
-          :documentation
-          "The worker that this project may queue tasks for")
-  (root nil
-        :type string
-        :documentation
-        "The root directory of this project")
-  (purged nil
-          :type boolean
-          :documentation "Whether or not the project has been purged or not.
-Projects get purged when they are removed from the global cache.")
-  (file-watchers (make-hash-table :test #'equal :size 10000 :rehash-size 10000)
-                 :type hash-table
-                 :documentation "All active file watchers in this project,
-indexed by the absolute paths of the files they're watching."))
 
 (cl-defmethod phpinspect-project-purge ((project phpinspect-project))
   "Disable all background processes for project and put it in a `purged` 
state."
@@ -197,7 +160,8 @@ indexed by the absolute paths of the files they're 
watching."))
            (class (gethash class-name
                            (phpinspect-project-class-index project))))
       (unless class
-        (setq class (phpinspect--make-class-generated :project project)))
+        (setq class (phpinspect--make-class-generated
+                     :class-retriever (phpinspect-project-make-class-retriever 
project))))
 
       (when index-imports
         (phpinspect-project-enqueue-imports
@@ -215,7 +179,8 @@ indexed by the absolute paths of the files they're 
watching."))
 
 (cl-defmethod phpinspect-project-create-class
   ((project phpinspect-project) (class-fqn phpinspect--type))
-  (let ((class (phpinspect--make-class-generated :project project)))
+  (let ((class (phpinspect--make-class-generated
+                :class-retriever (phpinspect-project-make-class-retriever 
project))))
     (phpinspect-project-set-class project class-fqn class)
     class))
 
@@ -283,20 +248,6 @@ before the search is executed."
 (cl-defmethod phpinspect-project-add-file-index ((project phpinspect-project) 
(filename string))
   (phpinspect-project-add-index project (phpinspect-project-index-file project 
filename)))
 
-(defun phpinspect-project-enqueue-include-dirs (project)
-  (interactive (list (phpinspect--cache-get-project-create
-                      (phpinspect--get-or-create-global-cache)
-                      (phpinspect-current-project-root))))
-  (let ((dirs (alist-get 'include-dirs
-                         (alist-get (phpinspect-project-root project)
-                                    phpinspect-projects
-                                    nil nil #'string=))))
-    (dolist (dir dirs)
-      (message "enqueueing dir %s" dir)
-      (phpinspect-worker-enqueue
-       (phpinspect-project-worker project)
-       (phpinspect-make-index-dir-task :dir dir :project project)))))
-
 (defcustom phpinspect-projects nil
   "PHPInspect Projects."
   :type '(alist :key-type string
@@ -304,22 +255,95 @@ before the search is executed."
                                    :options ((include-dirs (repeat string)))))
   :group 'phpinspect)
 
-(defun phpinspect-project-add-include-dir (dir)
-  "Configure DIR as an include dir for the current project."
-  (interactive (list (read-directory-name "Include Directory: ")))
-  (custom-set-variables '(phpinspect-projects))
-  (let ((existing
-         (alist-get (phpinspect-current-project-root) phpinspect-projects nil 
#'string=)))
-    (if existing
-        (push dir (alist-get 'include-dirs existing))
-      (push `(,(phpinspect-current-project-root) . ((include-dirs . (,dir)))) 
phpinspect-projects)))
-
-  (customize-save-variable 'phpinspect-projects phpinspect-projects)
-
-  (phpinspect-project-enqueue-include-dirs 
(phpinspect--cache-get-project-create
-                                            
(phpinspect--get-or-create-global-cache)
-                                            
(phpinspect-current-project-root))))
-
+(defun phpinspect-project-make-file-indexer (project)
+  (lambda (filename)
+    (phpinspect-project-add-file-index project filename)))
+
+(defun phpinspect-project-make-root-resolver (project)
+  (lambda () (phpinspect-project-root project)))
+
+(defun phpinspect-project-make-class-retriever (project)
+  (lambda (type) (phpinspect-project-get-class-create project type)))
+
+;;; INDEX TASK
+(cl-defstruct (phpinspect-index-task
+               (:constructor phpinspect-make-index-task-generated))
+  "Represents an index task that can be executed by a `phpinspect-worker`."
+  (project nil
+           :type phpinspect-project
+           :documentation
+           "The project that the task should be executed for.")
+  (type nil
+        :type phpinspect--type
+        :documentation
+        "The type whose file should be indexed."))
+
+(cl-defgeneric phpinspect-make-index-task ((project phpinspect-project)
+                                          (type phpinspect--type))
+  (phpinspect-make-index-task-generated
+   :project project
+   :type type))
+
+(cl-defmethod phpinspect-task-project ((task phpinspect-index-task))
+  (phpinspect-index-task-project task))
+
+
+(cl-defmethod phpinspect-task= ((task1 phpinspect-index-task) (task2 
phpinspect-index-task))
+  (and (eq (phpinspect-index-task-project task1)
+           (phpinspect-index-task-project task2))
+       (phpinspect--type= (phpinspect-index-task-type task1) 
(phpinspect-index-task-type task2))))
+
+(cl-defmethod phpinspect-task-execute ((task phpinspect-index-task)
+                                       (worker phpinspect-worker))
+  "Execute index TASK for WORKER."
+  (let ((project (phpinspect-index-task-project task))
+        (is-native-type (phpinspect--type-is-native
+                         (phpinspect-index-task-type task))))
+    (phpinspect--log "Indexing class %s for project in %s as task."
+                     (phpinspect-index-task-type task)
+                     (phpinspect-project-root project))
+
+    (cond (is-native-type
+           (phpinspect--log "Skipping indexation of native type %s as task"
+                            (phpinspect-index-task-type task))
+
+           ;; We can skip pausing when a native type is encountered
+           ;; and skipped, as we haven't done any intensive work that
+           ;; may cause hangups.
+           (setf (phpinspect-worker-skip-next-pause worker) t))
+          (t
+           (let* ((type (phpinspect-index-task-type task))
+                  (root-index (phpinspect-project-index-type-file project 
type)))
+             (when root-index
+               (phpinspect-project-add-index project root-index)))))))
+
+;;; INDEX FILE TASK
+(cl-defstruct (phpinspect-index-dir-task (:constructor 
phpinspect-make-index-dir-task))
+  "A task for the indexation of files"
+  (project nil
+           :type phpinspect-project)
+  (dir nil
+       :type string))
+
+(cl-defmethod phpinspect-task=
+  ((task1 phpinspect-index-dir-task) (task2 phpinspect-index-dir-task))
+  (and (eq (phpinspect-index-dir-task-project task1)
+           (phpinspect-index-dir-task-project task2))
+       (string= (phpinspect-index-dir-task-dir task1)
+                (phpinspect-index-dir-task-dir task2))))
+
+(cl-defmethod phpinspect-task-project ((task phpinspect-index-dir-task))
+  (phpinspect-index-dir-task-project task))
+
+(cl-defmethod phpinspect-task-execute ((task phpinspect-index-dir-task)
+                                       (_worker phpinspect-worker))
+  (phpinspect--log "Entering..")
+  (let* ((project (phpinspect-index-dir-task-project task))
+         (fs (phpinspect-project-fs project))
+         (dir (phpinspect-index-dir-task-dir task)))
+    (phpinspect--log "Indexing directory %s" dir)
+    (phpinspect-pipeline (phpinspect-fs-directory-files-recursively fs dir 
"\\.php$")
+      :into (phpinspect-project-add-file-index :with-context project))))
 
 (provide 'phpinspect-project)
 ;;; phpinspect-project.el ends here
diff --git a/phpinspect-resolve.el b/phpinspect-resolve.el
index 76e2159249..2c722c77ae 100644
--- a/phpinspect-resolve.el
+++ b/phpinspect-resolve.el
@@ -24,8 +24,9 @@
 ;;; Code:
 
 (require 'phpinspect-resolvecontext)
+(require 'phpinspect-cache)
 (require 'phpinspect-type)
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 
 (cl-defstruct (phpinspect--assignment
                (:constructor phpinspect--make-assignment))
@@ -36,13 +37,6 @@
         :type phpinspect-token
         :documentation "The token that is assigned from"))
 
-(define-inline phpinspect-statement-introduction-p (token)
-  (inline-letevals (token)
-    (inline-quote
-     (or (phpinspect-return-p ,token)
-         (phpinspect-end-of-statement-p ,token)
-         (phpinspect-function-p ,token)))))
-
 (defsubst phpinspect-block-or-list-p (token)
   (or (phpinspect-block-p token)
       (phpinspect-list-p token)))
diff --git a/phpinspect-resolvecontext.el b/phpinspect-resolvecontext.el
index 2a95575d99..557069eba4 100644
--- a/phpinspect-resolvecontext.el
+++ b/phpinspect-resolvecontext.el
@@ -24,12 +24,31 @@
 ;;; Code:
 
 (require 'phpinspect-bmap)
+(require 'phpinspect-cache)
 (require 'phpinspect-project)
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 (require 'phpinspect-type)
 (require 'phpinspect-meta)
 (require 'phpinspect-util)
 
+
+(defsubst phpinspect-blocklike-p (token)
+  (or (phpinspect-block-p token)
+      (phpinspect-function-p token)
+      (phpinspect-class-p token)
+      (phpinspect-namespace-p token)))
+
+(defsubst phpinspect-return-p (token)
+  (and (phpinspect-word-p token)
+       (string= "return" (cadr token))))
+
+(define-inline phpinspect-statement-introduction-p (token)
+  (inline-letevals (token)
+    (inline-quote
+     (or (phpinspect-return-p ,token)
+         (phpinspect-end-of-statement-p ,token)
+         (phpinspect-function-p ,token)))))
+
 (cl-defstruct (phpinspect--resolvecontext
             (:constructor phpinspect--make-resolvecontext))
   (subject nil
@@ -55,17 +74,6 @@
   (push enclosing-token (phpinspect--resolvecontext-enclosing-tokens
                          resolvecontext)))
 
-
-(defsubst phpinspect-blocklike-p (token)
-  (or (phpinspect-block-p token)
-      (phpinspect-function-p token)
-      (phpinspect-class-p token)
-      (phpinspect-namespace-p token)))
-
-(defsubst phpinspect-return-p (token)
-  (and (phpinspect-word-p token)
-       (string= "return" (cadr token))))
-
 (defun phpinspect-find-statement-before-point (bmap meta point)
   (let ((children (reverse (phpinspect-meta-find-children-before meta point)))
         token previous-siblings)
diff --git a/phpinspect-suggest.el b/phpinspect-suggest.el
index aff9c81312..d94b8854e8 100644
--- a/phpinspect-suggest.el
+++ b/phpinspect-suggest.el
@@ -25,7 +25,7 @@
 
 (require 'phpinspect-resolvecontext)
 (require 'phpinspect-resolve)
-(require 'phpinspect-parser)
+(require 'phpinspect-token-predicates)
 (require 'phpinspect-type)
 (require 'phpinspect-project)
 (require 'phpinspect-class)
diff --git a/phpinspect-toc.el b/phpinspect-toc.el
index f27f6dfda0..b8f7d4e89a 100644
--- a/phpinspect-toc.el
+++ b/phpinspect-toc.el
@@ -24,7 +24,8 @@
 ;;; Code:
 
 (require 'phpinspect-splayt)
-(require 'phpinspect-parser)
+(eval-when-compile
+  (require 'phpinspect-meta))
 
 (defun phpinspect-make-toc (&optional tree)
   (let ((table (make-hash-table :test #'eq :size 20 :rehash-size 2.0)))
diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el
index f03883deb2..9ae912dc3e 100644
--- a/phpinspect-token-predicates.el
+++ b/phpinspect-token-predicates.el
@@ -56,10 +56,6 @@ Type can be any of the token types returned by
       (phpinspect-comma-p token)
       (phpinspect-html-p token)))
 
-(defsubst phpinspect-end-of-statement-p (token)
-  (or (phpinspect-end-of-token-p token)
-      (phpinspect-block-p token)))
-
 (defsubst phpinspect-incomplete-block-p (token)
   (phpinspect-token-type-p token :incomplete-block))
 
@@ -67,6 +63,10 @@ Type can be any of the token types returned by
   (or (phpinspect-token-type-p token :block)
       (phpinspect-incomplete-block-p token)))
 
+(defsubst phpinspect-end-of-statement-p (token)
+  (or (phpinspect-end-of-token-p token)
+      (phpinspect-block-p token)))
+
 (defun phpinspect-end-of-use-p (token)
   (or (phpinspect-block-p token)
       (phpinspect-end-of-token-p token)))
@@ -170,11 +170,10 @@ Type can be any of the token types returned by
   (or (phpinspect-token-type-p token :array)
       (phpinspect-incomplete-array-p token)))
 
-(defsubst phpinspect-incomplete-root-p (token)
-  (and (phpinspect-root-p token)
-       (seq-find #'phpinspect-incomplete-token-p (cdr token))))
+(defsubst phpinspect-root-p (object)
+  (phpinspect-token-type-p object :root))
 
-(defsubst phpinspect-incomplete-token-p (token)
+(defun phpinspect-incomplete-token-p (token)
   (or (phpinspect-incomplete-root-p token)
       (phpinspect-incomplete-class-p token)
       (phpinspect-incomplete-block-p token)
@@ -185,6 +184,10 @@ Type can be any of the token types returned by
       (phpinspect-incomplete-method-p token)
       (phpinspect-incomplete-namespace-p token)))
 
+(defun phpinspect-incomplete-root-p (token)
+  (and (phpinspect-root-p token)
+       (seq-find #'phpinspect-incomplete-token-p (cdr token))))
+
 (defun phpinspect--static-terminator-p (token)
   (or (phpinspect-function-p token)
       (phpinspect-end-of-token-p token)))
@@ -214,9 +217,6 @@ Type can be any of the token types returned by
   (and (phpinspect-word-p token) (string= (car (last token)) "use")))
 
 
-(defsubst phpinspect-root-p (object)
-  (phpinspect-token-type-p object :root))
-
 (defsubst phpinspect-namespace-or-root-p (object)
   (or (phpinspect-namespace-p object)
       (phpinspect-root-p object)))
@@ -247,4 +247,8 @@ Type can be any of the token types returned by
   "Apply inverse of `phpinspect-class-p' to TOKEN."
   (not (phpinspect-class-p token)))
 
+(defsubst phpinspect-probably-token-p (token)
+  (and (listp token)
+       (keywordp (car token))))
+
 (provide 'phpinspect-token-predicates)
diff --git a/phpinspect-type.el b/phpinspect-type.el
index fbc941524e..abb2e44a54 100644
--- a/phpinspect-type.el
+++ b/phpinspect-type.el
@@ -24,6 +24,11 @@
 ;;; Code:
 
 (require 'phpinspect-util)
+(require 'phpinspect-token-predicates)
+
+(eval-when-compile
+  (require 'phpinspect-parser))
+
 
 (cl-defstruct (phpinspect--type
                (:constructor phpinspect--make-type-generated)
@@ -279,5 +284,69 @@ mutability of the variable")
   (not (or (phpinspect--variable-static-p variable)
            (phpinspect--variable-const-p variable))))
 
+(defun phpinspect--use-to-type (use)
+  (let* ((fqn (cadr (cadr use)))
+         (type (phpinspect--make-type :name (if (string-match "^\\\\" fqn)
+                                                fqn
+                                              (concat "\\" fqn))
+                                      :fully-qualified t))
+         (type-name (if (and (phpinspect-word-p (caddr use))
+                             (string= "as" (cadr (caddr use))))
+                        (cadr (cadddr use))
+                      (progn (string-match "[^\\]+$" fqn)
+                             (match-string 0 fqn)))))
+    (cons (phpinspect-intern-name type-name) type)))
+
+(defun phpinspect--uses-to-types (uses)
+  (mapcar #'phpinspect--use-to-type uses))
+
+(defun phpinspect--get-class-name-from-token (class-token)
+  (let ((subtoken (seq-find (lambda (word)
+                              (and (phpinspect-word-p word)
+                                   (not (string-match
+                                         (concat "^" 
(phpinspect--class-keyword-handler-regexp))
+                                         (concat (cadr word) " ")))))
+                            (cadr class-token))))
+    (cadr subtoken)))
+
+(defun phpinspect--index-class-declaration (decl type-resolver)
+  ;; Find out what the class extends or implements
+  (let (encountered-extends encountered-implements encountered-class
+        class-name extends implements used-types)
+    (dolist (word decl)
+      (if (phpinspect-word-p word)
+          (cond ((string= (cadr word) "extends")
+                 (phpinspect--log "Class %s extends other classes" class-name)
+                 (setq encountered-extends t))
+                ((string= (cadr word) "implements")
+                 (setq encountered-extends nil)
+                 (phpinspect--log "Class %s implements in interface" 
class-name)
+                 (setq encountered-implements t))
+                ((string= (cadr word) "class")
+                 (setq encountered-class t))
+                (t
+                 (phpinspect--log "Calling Resolver from index-class on %s" 
(cadr word))
+                 (cond (encountered-extends
+                        (push (funcall type-resolver (phpinspect--make-type
+                                                      :name (cadr word)))
+                              extends)
+                        (push (cadr word) used-types))
+                       (encountered-implements
+                        (push (funcall type-resolver (phpinspect--make-type
+                                                      :name (cadr word)))
+                              implements)
+                        (push (cadr word) used-types))
+                       (encountered-class
+                        (setq class-name (funcall type-resolver 
(phpinspect--make-type :name (cadr word)))
+                              encountered-class nil)))))))
+
+    (list class-name extends implements used-types)))
+
+(defun phpinspect-namespace-name (namespace)
+  (or (and (phpinspect-namespace-p namespace)
+           (phpinspect-word-p (cadr namespace))
+           (cadadr namespace))
+      ""))
+
 (provide 'phpinspect-type)
 ;;; phpinspect-type.el ends here
diff --git a/phpinspect-util.el b/phpinspect-util.el
index ca5b971243..0f6e6ad279 100644
--- a/phpinspect-util.el
+++ b/phpinspect-util.el
@@ -31,31 +31,6 @@ PHP. Used to optimize string comparison.")
   '("composer.json" "composer.lock" ".git" ".svn" ".hg")
   "List of files that could indicate a project root directory.")
 
-(defun phpinspect--find-project-root (&optional start-file)
-  "(Attempt to) Find the root directory of the visited PHP project.
-If a found project root has a parent directory called \"vendor\",
-the search continues upwards. See also
-`phpinspect--locate-dominating-project-file'.
-
-If START-FILE is provided, searching starts at the directory
-level of START-FILE in stead of `default-directory`."
-  (let ((project-file (phpinspect--locate-dominating-project-file
-                       (or start-file default-directory))))
-    (phpinspect--log "Checking for project root at  %s" project-file)
-    (when project-file
-      (let* ((directory (file-name-directory project-file))
-             (directory-slugs (split-string (expand-file-name directory) "/")))
-        (if (not (member "vendor" directory-slugs))
-            (expand-file-name directory)
-          ;; else. Only continue if the parent directory is not "/"
-          (let ((parent-without-vendor
-                 (string-join (seq-take-while (lambda (s) (not (string= s 
"vendor" )))
-                                              directory-slugs)
-                              "/")))
-            (when (not (or (string= parent-without-vendor "/")
-                           (string= parent-without-vendor "")))
-              (phpinspect--find-project-root parent-without-vendor))))))))
-
 (defvar phpinspect--debug nil
   "Enable debug logs for phpinspect by setting this variable to true")
 
@@ -65,10 +40,10 @@ level of START-FILE in stead of `default-directory`."
       (message "Enabled phpinspect logging.")
     (message "Disabled phpinspect logging.")))
 
-(defvar phpinspect-log-groups nil)
-(defvar phpinspect-enabled-log-groups nil)
-
-(defvar-local phpinspect--current-log-group nil)
+(eval-and-compile
+  (defvar phpinspect-log-groups nil)
+  (defvar phpinspect-enabled-log-groups nil)
+  (defvar-local phpinspect--current-log-group nil))
 
 (define-inline phpinspect--declare-log-group (group)
   (unless (and (inline-const-p group) (symbolp (inline-const-val group)))
@@ -109,6 +84,31 @@ level of START-FILE in stead of `default-directory`."
   (interactive)
   (setq phpinspect-enabled-log-groups nil))
 
+(defun phpinspect--find-project-root (&optional start-file)
+  "(Attempt to) Find the root directory of the visited PHP project.
+If a found project root has a parent directory called \"vendor\",
+the search continues upwards. See also
+`phpinspect--locate-dominating-project-file'.
+
+If START-FILE is provided, searching starts at the directory
+level of START-FILE in stead of `default-directory`."
+  (let ((project-file (phpinspect--locate-dominating-project-file
+                       (or start-file default-directory))))
+    (phpinspect--log "Checking for project root at  %s" project-file)
+    (when project-file
+      (let* ((directory (file-name-directory project-file))
+             (directory-slugs (split-string (expand-file-name directory) "/")))
+        (if (not (member "vendor" directory-slugs))
+            (expand-file-name directory)
+          ;; else. Only continue if the parent directory is not "/"
+          (let ((parent-without-vendor
+                 (string-join (seq-take-while (lambda (s) (not (string= s 
"vendor" )))
+                                              directory-slugs)
+                              "/")))
+            (when (not (or (string= parent-without-vendor "/")
+                           (string= parent-without-vendor "")))
+              (phpinspect--find-project-root parent-without-vendor))))))))
+
 (defsubst phpinspect-intern-name (name)
   (intern name phpinspect-name-obarray))
 
@@ -251,6 +251,10 @@ context for completion."
             (json-key-type 'string))
      ,@body))
 
+(defun phpinspect--input-pending-p (&optional check-timers)
+  (unless noninteractive
+    (input-pending-p check-timers)))
+
 (defun phpinspect-thread-pause (pause-time mx continue)
   "Pause current thread using MX and CONTINUE for PAUSE-TIME idle seconds.
 
@@ -265,16 +269,5 @@ CONTINUE must be a condition-variable"
   (with-mutex mx (condition-wait continue))
   (phpinspect--log "Thread '%s' continuing execution" (thread-name 
(current-thread))))
 
-(defun phpinspect-namespace-name (namespace)
-  (or (and (phpinspect-namespace-p namespace)
-           (phpinspect-word-p (cadr namespace))
-           (cadadr namespace))
-      ""))
-
-
-(defsubst phpinspect-probably-token-p (token)
-  (and (listp token)
-       (keywordp (car token))))
-
 (provide 'phpinspect-util)
 ;;; phpinspect-util.el ends here
diff --git a/phpinspect-worker.el b/phpinspect-worker.el
index 60977c0e48..b9bfe4fc39 100644
--- a/phpinspect-worker.el
+++ b/phpinspect-worker.el
@@ -1,4 +1,4 @@
-;;; phpinspect-worker.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
+;; phpinspect-worker.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
 
 ;; Copyright (C) 2021  Free Software Foundation, Inc
 
@@ -25,7 +25,7 @@
 
 (require 'cl-lib)
 (require 'phpinspect-util)
-(require 'phpinspect-project)
+(require 'phpinspect-project-struct)
 (require 'phpinspect-index)
 (require 'phpinspect-class)
 (require 'phpinspect-queue)
@@ -75,7 +75,7 @@ on the worker independent of dynamic variables during 
testing.")
 (cl-defmethod phpinspect-resolve-dynamic-worker ((_worker 
phpinspect-dynamic-worker))
   phpinspect-worker)
 
-(defsubst phpinspect-make-dynamic-worker ()
+(defun phpinspect-make-dynamic-worker ()
   (phpinspect-make-dynamic-worker-generated))
 
 (defsubst phpinspect-make-worker ()
@@ -148,7 +148,7 @@ already present in the queue."
 
               ;; Pause for a second after indexing something, to allow user 
input to
               ;; interrupt the thread.
-              (unless (or (not (input-pending-p))
+              (unless (or (not (phpinspect--input-pending-p))
                           (phpinspect-worker-skip-next-pause worker))
                 (phpinspect-thread-pause phpinspect-worker-pause-time mx 
continue))
               (setf (phpinspect-worker-skip-next-pause worker) nil)))
@@ -225,86 +225,5 @@ already present in the queue."
 (cl-defgeneric phpinspect-task-project (task)
   "The project that this task belongs to.")
 
-
-;;; INDEX TASK
-(cl-defstruct (phpinspect-index-task
-               (:constructor phpinspect-make-index-task-generated))
-  "Represents an index task that can be executed by a `phpinspect-worker`."
-  (project nil
-           :type phpinspect-project
-           :documentation
-           "The project that the task should be executed for.")
-  (type nil
-        :type phpinspect--type
-        :documentation
-        "The type whose file should be indexed."))
-
-(cl-defgeneric phpinspect-make-index-task ((project phpinspect-project)
-                                          (type phpinspect--type))
-  (phpinspect-make-index-task-generated
-   :project project
-   :type type))
-
-(cl-defmethod phpinspect-task-project ((task phpinspect-index-task))
-  (phpinspect-index-task-project task))
-
-
-(cl-defmethod phpinspect-task= ((task1 phpinspect-index-task) (task2 
phpinspect-index-task))
-  (and (eq (phpinspect-index-task-project task1)
-           (phpinspect-index-task-project task2))
-       (phpinspect--type= (phpinspect-index-task-type task1) 
(phpinspect-index-task-type task2))))
-
-(cl-defmethod phpinspect-task-execute ((task phpinspect-index-task)
-                                       (worker phpinspect-worker))
-  "Execute index TASK for WORKER."
-  (let ((project (phpinspect-index-task-project task))
-        (is-native-type (phpinspect--type-is-native
-                         (phpinspect-index-task-type task))))
-    (phpinspect--log "Indexing class %s for project in %s as task."
-                     (phpinspect-index-task-type task)
-                     (phpinspect-project-root project))
-
-    (cond (is-native-type
-           (phpinspect--log "Skipping indexation of native type %s as task"
-                            (phpinspect-index-task-type task))
-
-           ;; We can skip pausing when a native type is encountered
-           ;; and skipped, as we haven't done any intensive work that
-           ;; may cause hangups.
-           (setf (phpinspect-worker-skip-next-pause worker) t))
-          (t
-           (let* ((type (phpinspect-index-task-type task))
-                  (root-index (phpinspect-project-index-type-file project 
type)))
-             (when root-index
-               (phpinspect-project-add-index project root-index)))))))
-
-;;; INDEX FILE TASK
-(cl-defstruct (phpinspect-index-dir-task (:constructor 
phpinspect-make-index-dir-task))
-  "A task for the indexation of files"
-  (project nil
-           :type phpinspect-project)
-  (dir nil
-       :type string))
-
-(cl-defmethod phpinspect-task=
-  ((task1 phpinspect-index-dir-task) (task2 phpinspect-index-dir-task))
-  (and (eq (phpinspect-index-dir-task-project task1)
-           (phpinspect-index-dir-task-project task2))
-       (string= (phpinspect-index-dir-task-dir task1)
-                (phpinspect-index-dir-task-dir task2))))
-
-(cl-defmethod phpinspect-task-project ((task phpinspect-index-dir-task))
-  (phpinspect-index-dir-task-project task))
-
-(cl-defmethod phpinspect-task-execute ((task phpinspect-index-dir-task)
-                                       (_worker phpinspect-worker))
-  (phpinspect--log "Entering..")
-  (let* ((project (phpinspect-index-dir-task-project task))
-         (fs (phpinspect-project-fs project))
-         (dir (phpinspect-index-dir-task-dir task)))
-    (phpinspect--log "Indexing directory %s" dir)
-    (phpinspect-pipeline (phpinspect-fs-directory-files-recursively fs dir 
"\\.php$")
-      :into (phpinspect-project-add-file-index :with-context project))))
-
 (provide 'phpinspect-worker)
 ;;; phpinspect-worker.el ends here
diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el
index 88f2070ca0..c1ca0725d9 100644
--- a/test/phpinspect-test.el
+++ b/test/phpinspect-test.el
@@ -217,7 +217,7 @@
 
 
 (ert-deftest phpinspect-resolve-type-from-context ()
-  (let* ((pctx (phpinspect-make-pctx :incremental t))
+  (let* ((pctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap)))
          (code "
 namespace Amazing;
 
diff --git a/test/test-autoload.el b/test/test-autoload.el
index 876f12add7..481c06e337 100644
--- a/test/test-autoload.el
+++ b/test/test-autoload.el
@@ -1,4 +1,4 @@
-;; test-autoload.el --- Unit tests for phpinspect.el  -*- lexical-binding: t; 
-*-
+; test-autoload.el --- Unit tests for phpinspect.el  -*- lexical-binding: t; 
-*-
 
 ;; Copyright (C) 2021 Free Software Foundation, Inc.
 
@@ -31,7 +31,11 @@
 (ert-deftest phpinspect-find-composer-json-files ()
   (let* ((fs (phpinspect-make-virtual-fs))
          (autoloader (phpinspect-make-autoloader
-                     :project (phpinspect--make-project :root "/root" :fs 
fs))))
+                      :fs fs
+                      :project-root-resolver (lambda () "/root")
+                      :file-indexer
+                      (phpinspect-project-make-file-indexer
+                       (phpinspect--make-project :root "/root" :fs fs)))))
     (phpinspect-virtual-fs-set-file fs
       "/root/composer.json"
       "{ \"autoload\": { \"psr-4\": {\"WoW\\\\Dwarves\\\\\": \"src/\"}}}")
@@ -57,9 +61,14 @@
 (ert-deftest phpinspect-autoload-composer-json-iterator ()
   (let* ((fs (phpinspect-make-virtual-fs))
          (autoloader (phpinspect-make-autoloader
-                      :project (phpinspect--make-project :root "/root" :fs 
fs)))
+                      :fs fs
+                      :project-root-resolver (lambda () "/root")
+                      :file-indexer
+                      (phpinspect-project-make-file-indexer
+                       (phpinspect--make-project :root "/root" :fs fs))))
          result error)
 
+
       (phpinspect-virtual-fs-set-file fs
       "/root/composer.json"
       "{ \"autoload\": { \"psr-4\": {\"WoW\\\\Dwarves\\\\\": \"src/\"}}}")
@@ -92,7 +101,10 @@
 (ert-deftest phpinspect-al-strategy-execute ()
   (let* ((fs (phpinspect-make-virtual-fs))
          (project (phpinspect--make-project :root "/project/root" :fs fs))
-         (autoloader (phpinspect-make-autoloader :project project))
+         (autoloader (phpinspect-make-autoloader
+                      :fs fs
+                      :project-root-resolver (lambda () "/project/root")
+                      :file-indexer (phpinspect-project-make-file-indexer 
project)))
          result error)
 
     (setf (phpinspect-project-autoload project) autoloader)
@@ -131,7 +143,7 @@
        "<?php class FilesList { function list() {} }")
 
      (phpinspect-virtual-fs-set-file
-      fs "/project/root/vendor/not-runescape/wow/src/TestClass.php" "")
+       fs "/project/root/vendor/not-runescape/wow/src/TestClass.php" "")
 
     (phpinspect-pipeline (phpinspect-find-composer-json-files fs 
"/project/root")
       :async (lambda (res err)
diff --git a/test/test-class.el b/test/test-class.el
index 0dbfb88319..35c0ea7f36 100644
--- a/test/test-class.el
+++ b/test/test-class.el
@@ -101,7 +101,9 @@
     (should (= 1 (length (hash-table-values (phpinspect--class-subscriptions 
other-class)))))))
 
 (ert-deftest phpinspect--class-update-declaration ()
-  (let ((class (phpinspect--make-class-generated :project 
(phpinspect--make-project))))
+  (let ((class (phpinspect--make-class-generated
+                :class-retriever (phpinspect-project-make-class-retriever
+                                  (phpinspect--make-project)))))
     (phpinspect--class-update-declaration class '(:declaration (:word "class") 
(:word "TestClass")
                                                                (:word 
"extends") (:word "OtherClass")
                                                                (:word 
"implements") (:word "ImplClass"))
diff --git a/test/test-index.el b/test/test-index.el
index 7bee75d9bd..ffea7fb505 100644
--- a/test/test-index.el
+++ b/test/test-index.el
@@ -169,7 +169,7 @@ return StaticThing::create(new 
ThingFactory())->makeThing((((new Potato())->anti
                      (alist-get key index2-class))))))
 
 (ert-deftest phpinspect-index-bmap-class ()
-  (let* ((pctx (phpinspect-make-pctx :incremental t))
+  (let* ((pctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap)))
          (tree))
     (with-temp-buffer
       (insert-file-contents (concat phpinspect-test-php-file-directory 
"/IndexClass1.php"))
diff --git a/test/test-parse-context.el b/test/test-parse-context.el
index b79db41183..82ae1cff77 100644
--- a/test/test-parse-context.el
+++ b/test/test-parse-context.el
@@ -29,7 +29,7 @@
 
 (ert-deftest phpinspect-pctx-cancel ()
   (let ((meta (phpinspect-make-meta nil 10 20 "    " 'token 'overlay nil))
-        (pctx (phpinspect-make-pctx)))
+        (pctx (phpinspect-make-pctx :bmap (phpinspect-make-bmap))))
     (phpinspect-with-parse-context pctx
       (phpinspect-meta-with-changeset meta
         (setf (phpinspect-meta-absolute-start meta) 222)
diff --git a/test/test-parser.el b/test/test-parser.el
index d2ecf947c3..83e5fca60f 100644
--- a/test/test-parser.el
+++ b/test/test-parser.el
@@ -42,7 +42,7 @@
 
 
 (ert-deftest phpinspect-parse-bmap ()
-  (let* ((ctx (phpinspect-make-pctx :incremental t))
+  (let* ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap)))
          (code "
 class TestClass {
     public function getCurrentStatisticAction(): JsonResponse
@@ -73,7 +73,7 @@ class TestClass {
 
 (ert-deftest phpinspect-parse-comma ()
   (let* ((code "(,)")
-         (ctx (phpinspect-make-pctx :incremental t))
+         (ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap)))
          (parsed (phpinspect-with-parse-context ctx
                    (phpinspect-parse-string code)))
          (comma (cadadr parsed))
diff --git a/test/test-resolvecontext.el b/test/test-resolvecontext.el
index 75c6806af4..894f684080 100644
--- a/test/test-resolvecontext.el
+++ b/test/test-resolvecontext.el
@@ -2,7 +2,7 @@
 (require 'phpinspect-resolvecontext)
 
 (ert-deftest phinspect-get-resolvecontext ()
-  (let* ((ctx (phpinspect-make-pctx :incremental t))
+  (let* ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap)))
          (code "
 class TestClass {
     public function getCurrentStatisticAction(): JsonResponse

Reply via email to