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

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

[elpa] externals/phpinspect e07e1ed9e6 033/126: WIP: Split code up into


From: ELPA Syncer
Subject: [elpa] externals/phpinspect e07e1ed9e6 033/126: WIP: Split code up into separate files
Date: Sat, 12 Aug 2023 00:58:38 -0400 (EDT)

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

    WIP: Split code up into separate files
---
 phpinspect-cache.el   |   63 ++
 phpinspect-class.el   |  168 +++++
 phpinspect-index.el   |  395 ++++++++++++
 phpinspect-parser.el  |  828 +++++++++++++++++++++++++
 phpinspect-project.el |   92 +++
 phpinspect-type.el    |  187 ++++++
 phpinspect-util.el    |   66 ++
 phpinspect.el         | 1641 +------------------------------------------------
 8 files changed, 1806 insertions(+), 1634 deletions(-)

diff --git a/phpinspect-cache.el b/phpinspect-cache.el
new file mode 100644
index 0000000000..f546068b0a
--- /dev/null
+++ b/phpinspect-cache.el
@@ -0,0 +1,63 @@
+;;; phpinspect.el --- PHP parsing and completion package  -*- lexical-binding: 
t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(require 'phpinspect-project)
+
+(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
+  (active-projects nil
+                   :type alist
+                   :documentation
+                   "An `alist` that contains the root directory
+                   paths of all currently active phpinspect
+                   projects")
+  (projects (make-hash-table :test 'equal :size 10)
+            :type hash-table
+            :documentation
+            "A `hash-table` with the root directories of projects
+as keys and project caches as values."))
+
+(cl-defgeneric phpinspect--cache-getproject
+    ((cache phpinspect--cache) (project-name string))
+  "Get project by PROJECT-NAME that is located in CACHE.")
+
+(cl-defmethod phpinspect--cache-getproject
+  ((cache phpinspect--cache) (project-root string))
+  (gethash project-root (phpinspect--cache-projects cache)))
+
+(cl-defgeneric phpinspect--cache-get-project-create
+    ((cache phpinspect--cache) (project-root string))
+  "Get a project that is located in PROJECT-ROOT from CACHE.
+If no such project exists in the cache yet, it is created and
+then returned.")
+
+(cl-defmethod phpinspect--cache-get-project-create
+  ((cache phpinspect--cache) (project-root string))
+  (or (phpinspect--cache-getproject cache project-root)
+      (puthash project-root
+               (phpinspect--make-project-cache)
+               (phpinspect--cache-projects cache))))
+
+(provide 'phpinspect-cache)
+;;; phpinspect.el ends here
diff --git a/phpinspect-class.el b/phpinspect-class.el
new file mode 100644
index 0000000000..d03a21a336
--- /dev/null
+++ b/phpinspect-class.el
@@ -0,0 +1,168 @@
+;;; phpinspect-class.el --- PHP parsing module  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(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")
+  (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.")
+  (variables nil
+             :type list
+             :documentation
+             "Variables that belong to this class.")
+  (extended-classes (make-hash-table :test 'eq)
+                    :type hash-table
+                    :documentation
+                    "All extended/implemented classes.")
+  (subscriptions nil
+                 :type list
+                 :documentation
+                 "A list of subscription functions that should be
+                 called whenever anything about this class is
+                 updated"))
+
+(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
+  (dolist (sub (phpinspect--class-subscriptions class))
+    (funcall sub class)))
+
+(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
+                                           (index (head 
phpinspect--indexed-class)))
+  (setf (phpinspect--class-index class) index)
+  (dolist (method (alist-get 'methods index))
+    (phpinspect--class-update-method class method))
+
+  (dolist (method (alist-get 'static-methods index))
+    (phpinspect--class-update-static-method class method))
+
+  (setf (phpinspect--class-variables class)
+        (alist-get 'variables index))
+
+  (setf (phpinspect--class-extended-classes class)
+        (seq-filter
+         #'phpinspect--class-p
+         (mapcar
+          (lambda (class-name)
+            (phpinspect--project-get-class-create (phpinspect--class-project 
class)
+                                           class-name))
+          `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))))
+
+  (dolist (extended (phpinspect--class-extended-classes class))
+    (phpinspect--class-incorporate class extended)
+    (phpinspect--class-subscribe class extended))
+
+  (phpinspect--class-trigger-update class))
+
+(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) 
method-name)
+  (gethash method-name (phpinspect--class-methods class)))
+
+(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) 
method-name)
+  (gethash method-name (phpinspect--class-static-methods class)))
+
+(cl-defmethod phpinspect--class-set-method ((class phpinspect--class)
+                                            (method phpinspect--function))
+  (phpinspect--log "Adding method by name %s to class"
+                   (phpinspect--function-name method))
+  (puthash (phpinspect--function-name-symbol method)
+           method
+           (phpinspect--class-methods class)))
+
+(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
+                                                   (method 
phpinspect--function))
+  (puthash (phpinspect--function-name-symbol method)
+           method
+           (phpinspect--class-static-methods class)))
+
+(cl-defmethod phpinspect--class-get-method-return-type
+  ((class phpinspect--class) (method-name symbol))
+  (let ((method (phpinspect--class-get-method class method-name)))
+    (when method
+      (phpinspect--function-return-type method))))
+
+(cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class))
+  (let ((methods))
+    (maphash (lambda (key method)
+               (push method methods))
+             (phpinspect--class-methods class))
+    methods))
+
+(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
+                                                      (method 
phpinspect--function))
+  (let ((existing (gethash (phpinspect--function-name-symbol method)
+                           (phpinspect--class-static-methods class))))
+    (if existing
+        (progn
+          (unless (eq (phpinspect--function-return-type method)
+                    phpinspect--null-type)
+          (setf (phpinspect--function-return-type existing)
+                (phpinspect--function-return-type method))
+          (setf (phpinspect--function-arguments existing)
+                (phpinspect--function-arguments method))))
+      (phpinspect--class-set-static-method class method))))
+
+(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
+                                               (method phpinspect--function))
+  (let ((existing (gethash (phpinspect--function-name-symbol method)
+                           (phpinspect--class-methods class))))
+    (if existing
+        (progn
+          (unless (eq (phpinspect--function-return-type method)
+                    phpinspect--null-type)
+          (setf (phpinspect--function-return-type existing)
+                (phpinspect--function-return-type method))
+          (setf (phpinspect--function-arguments existing)
+                (phpinspect--function-arguments method))))
+      (phpinspect--class-set-method class method))))
+
+(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
+                                             (other-class phpinspect--class))
+  (maphash (lambda (k method)
+             (phpinspect--class-update-method class method))
+           (phpinspect--class-methods other-class)))
+
+(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
+                                           (subscription-class 
phpinspect--class))
+  (let ((update-function
+         (lambda (new-class)
+           (phpinspect--class-incorporate class new-class)
+           (phpinspect--class-trigger-update class))))
+    (push update-function (phpinspect--class-subscriptions 
subscription-class))))
+
+(provide 'phpinspect-class)
+;;; phpinspect-class.el ends here
diff --git a/phpinspect-index.el b/phpinspect-index.el
new file mode 100644
index 0000000000..9211ed9f47
--- /dev/null
+++ b/phpinspect-index.el
@@ -0,0 +1,395 @@
+;;; phpinspect-index.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(require 'phpinspect-util)
+(require 'phpinspect-type)
+
+(defun phpinspect--function-from-scope (scope)
+  (cond ((and (phpinspect-static-p (cadr scope))
+              (phpinspect-function-p (caddr scope)))
+         (caddr scope))
+        ((phpinspect-function-p (cadr scope))
+         (cadr scope))
+        (t nil)))
+
+(defun phpinspect-var-annotation-p (token)
+  (phpinspect-token-type-p token :var-annotation))
+
+(defun phpinspect-return-annotation-p (token)
+  (phpinspect-token-type-p token :return-annotation))
+
+(defun phpinspect--index-function-arg-list (type-resolver arg-list)
+  (let ((arg-index)
+        (current-token)
+        (arg-list (cl-copy-list arg-list)))
+    (while (setq current-token (pop arg-list))
+      (cond ((and (phpinspect-word-p current-token)
+               (phpinspect-variable-p (car arg-list)))
+          (push `(,(cadr (pop arg-list))
+                  ,(funcall type-resolver (phpinspect--make-type :name (cadr 
current-token))))
+                arg-index))
+            ((phpinspect-variable-p (car arg-list))
+             (push `(,(cadr (pop arg-list))
+                     nil)
+                   arg-index))))
+    (nreverse arg-index)))
+
+(defun phpinspect--index-function-from-scope (type-resolver scope 
comment-before)
+  (let* ((php-func (cadr scope))
+         (declaration (cadr php-func))
+         (type (if (phpinspect-word-p (car (last declaration)))
+                   (phpinspect--make-type :name (cadar (last declaration))))))
+
+    ;; @return annotation. Has precedence over typehint when dealing with a 
collection.
+    (let* ((is-collection
+           (when type
+             (member (phpinspect--type-name
+                      (funcall type-resolver type)) 
phpinspect-collection-types)))
+           (return-annotation-type
+            (when (or (not type) is-collection)
+              (cadadr
+               (seq-find #'phpinspect-return-annotation-p
+                         comment-before)))))
+      (phpinspect--log "found return annotation %s" return-annotation-type)
+
+      (when return-annotation-type
+        (cond ((not type)
+               (setq type (funcall type-resolver
+                                   (phpinspect--make-type :name 
return-annotation-type))))
+              (is-collection
+               (phpinspect--log "Detected collection type in: %s" scope)
+               (setf (phpinspect--type-contains type)
+                     (funcall type-resolver
+                              (phpinspect--make-type :name 
return-annotation-type)))
+               (setf (phpinspect--type-collection type) t)))))
+
+    (phpinspect--make-function
+     :scope `(,(car scope))
+     :name (cadadr (cdr declaration))
+     :return-type (if type (funcall type-resolver type)
+                    phpinspect--null-type)
+     :arguments (phpinspect--index-function-arg-list
+                 type-resolver
+                 (phpinspect-function-argument-list php-func)))))
+
+(defun phpinspect--index-const-from-scope (scope)
+  (phpinspect--make-variable
+   :scope `(,(car scope))
+   :name (cadr (cadr (cadr scope)))))
+
+(defun phpinspect--var-annotations-from-token (token)
+  (seq-filter #'phpinspect-var-annotation-p token))
+
+(defun phpinspect--index-variable-from-scope (type-resolver scope 
comment-before)
+  "Index the variable inside `scope`."
+  (let* ((var-annotations (phpinspect--var-annotations-from-token 
comment-before))
+         (variable-name (cadr (cadr scope)))
+         (type (if var-annotations
+                   ;; Find the right annotation by variable name
+                   (or (cadr (cadr (seq-find (lambda (annotation)
+                                               (string= (cadr (caddr 
annotation)) variable-name))
+                                             var-annotations)))
+                       ;; Give up and just use the last one encountered
+                       (cadr (cadr (car (last var-annotations))))))))
+    (phpinspect--log "calling resolver from index-variable-from-scope")
+    (phpinspect--make-variable
+     :name variable-name
+     :scope `(,(car scope))
+     :type (if type (funcall type-resolver (phpinspect--make-type :name 
type))))))
+
+(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)))
+
+(defun phpinspect--index-class (type-resolver class)
+  "Create an alist with relevant attributes of a parsed class."
+  (phpinspect--log "INDEXING CLASS")
+  (let ((methods)
+        (static-methods)
+        (static-variables)
+        (variables)
+        (constants)
+        (extends)
+        (implements)
+        (class-name (phpinspect--get-class-name-from-token class))
+        ;; Keep track of encountered comments to be able to use type
+        ;; annotations.
+        (comment-before))
+
+    ;; Find out what the class extends or implements
+    (let ((enc-extends nil)
+          (enc-implements nil))
+      (dolist (word (cadr class))
+        (if (phpinspect-word-p word)
+            (cond ((string= (cadr word) "extends")
+                   (phpinspect--log "Class %s extends other classes" 
class-name)
+                   (setq enc-extends t))
+                  ((string= (cadr word) "implements")
+                   (setq enc-extends nil)
+                   (phpinspect--log "Class %s implements in interface" 
class-name)
+                   (setq enc-implements t))
+                  (t
+                   (phpinspect--log "Calling Resolver from index-class on %s" 
(cadr word))
+                   (cond (enc-extends
+                          (push (funcall type-resolver (phpinspect--make-type
+                                                        :name (cadr word)))
+                                extends))
+                         (enc-implements
+                          (push (funcall type-resolver (phpinspect--make-type
+                                                        :name (cadr word)))
+                                implements))))))))
+
+    (dolist (token (caddr class))
+      (cond ((phpinspect-scope-p token)
+             (cond ((phpinspect-const-p (cadr token))
+                    (push (phpinspect--index-const-from-scope token) 
constants))
+
+                   ((phpinspect-variable-p (cadr token))
+                    (push (phpinspect--index-variable-from-scope type-resolver
+                                                                 token
+                                                                 
comment-before)
+                          variables))
+
+                   ((phpinspect-static-p (cadr token))
+                    (cond ((phpinspect-function-p (cadadr token))
+                           (push (phpinspect--index-function-from-scope 
type-resolver
+                                                                        (list 
(car token)
+                                                                              
(cadadr token))
+                                                                        
comment-before)
+                                 static-methods))
+
+                          ((phpinspect-variable-p (cadadr token))
+                           (push (phpinspect--index-variable-from-scope 
type-resolver
+                                                                        (list 
(car token)
+                                                                              
(cadadr token))
+                                                                        
comment-before)
+                                 static-variables))))
+                   (t
+                    (phpinspect--log "comment-before is: %s" comment-before)
+                    (push (phpinspect--index-function-from-scope type-resolver
+                                                                 token
+                                                                 
comment-before)
+                          methods))))
+            ((phpinspect-static-p token)
+             (cond ((phpinspect-function-p (cadr token))
+                    (push (phpinspect--index-function-from-scope type-resolver
+                                                                 `(:public
+                                                                   ,(cadr 
token))
+                                                                 
comment-before)
+                          static-methods))
+
+                   ((phpinspect-variable-p (cadr token))
+                    (push (phpinspect--index-variable-from-scope type-resolver
+                                                                 `(:public
+                                                                   ,(cadr 
token))
+                                                                 
comment-before)
+                          static-variables))))
+            ((phpinspect-const-p token)
+             ;; Bare constants are always public
+             (push (phpinspect--index-const-from-scope (list :public token))
+                   constants))
+            ((phpinspect-function-p token)
+             ;; Bare functions are always public
+             (push (phpinspect--index-function-from-scope type-resolver
+                                                          (list :public token)
+                                                          comment-before)
+                   methods))
+            ((phpinspect-doc-block-p token)
+             (phpinspect--log "setting comment-before %s" token)
+             (setq comment-before token))
+
+            ;; Prevent comments from sticking around too long
+            (t
+             (phpinspect--log "Unsetting comment-before")
+             (setq comment-before nil))))
+
+    ;; Dirty hack that assumes the constructor argument names to be the same 
as the object
+    ;; attributes' names.
+    ;;;
+    ;; TODO: actually check the types of the variables assigned to object 
attributes
+    (let* ((constructor-sym (phpinspect-intern-name "__construct"))
+           (constructor (seq-find (lambda (method)
+                                   (eq (phpinspect--function-name-symbol 
method)
+                                            constructor-sym))
+                                 methods)))
+      (when constructor
+        (phpinspect--log "Constructor was found")
+        (dolist (variable variables)
+          (when (not (phpinspect--variable-type variable))
+            (phpinspect--log "Looking for variable type in constructor 
arguments (%s)"
+                             variable)
+            (let ((constructor-parameter-type
+                   (car (alist-get (phpinspect--variable-name variable)
+                                   (phpinspect--function-arguments constructor)
+                                   nil nil #'string=))))
+              (if constructor-parameter-type
+                  (setf (phpinspect--variable-type variable)
+                        (funcall type-resolver 
constructor-parameter-type))))))))
+
+    (let ((class-name (funcall type-resolver (phpinspect--make-type :name 
class-name))))
+      `(,class-name .
+                    (phpinspect--indexed-class
+                     (methods . ,methods)
+                     (class-name . ,class-name)
+                     (static-methods . ,static-methods)
+                     (static-variables . ,static-variables)
+                     (variables . ,variables)
+                     (constants . ,constants)
+                     (extends . ,extends)
+                     (implements . ,implements))))))
+
+(defsubst phpinspect-namespace-body (namespace)
+  "Return the nested tokens in NAMESPACE tokens' body.
+Accounts for namespaces that are defined with '{}' blocks."
+  (if (phpinspect-block-p (caddr namespace))
+      (cdaddr namespace)
+    (cdr namespace)))
+
+(defun phpinspect--index-classes (types classes &optional namespace indexed)
+  "Index the class tokens in `classes`, using the types in `types`
+as Fully Qualified names. `namespace` will be assumed the root
+namespace if not provided"
+  (if classes
+      (let ((class (pop classes)))
+        (push (phpinspect--index-class
+               (phpinspect--make-type-resolver types class namespace)
+               class)
+              indexed)
+        (phpinspect--index-classes types classes namespace indexed))
+    (nreverse indexed)))
+
+(defun phpinspect--use-to-type (use)
+  (let* ((fqn (cadr (cadr use)))
+         (type (phpinspect--make-type :name 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)
+  (phpinspect--index-classes
+   (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
+   (seq-filter #'phpinspect-class-p namespace)
+   (cadadr namespace)))
+
+(defun phpinspect--index-namespaces (namespaces &optional indexed)
+  (if namespaces
+      (progn
+        (push (phpinspect--index-namespace (pop namespaces)) indexed)
+        (phpinspect--index-namespaces namespaces indexed))
+    (apply #'append (nreverse indexed))))
+
+(defun phpinspect--index-functions (&rest _args)
+  "TODO: implement function indexation. This is a stub function.")
+
+(defun phpinspect--index-tokens (tokens)
+  "Index TOKENS as returned by `phpinspect--parse-current-buffer`."
+  `(phpinspect--root-index
+    ,(append
+      (append '(classes)
+              (phpinspect--index-namespaces (seq-filter 
#'phpinspect-namespace-p tokens))
+              (phpinspect--index-classes
+               (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p 
tokens))
+               (seq-filter #'phpinspect-class-p tokens))))
+    (functions))
+  ;; TODO: Implement function indexation
+  )
+
+;; (defun phpinspect--get-or-create-index-for-class-file (class-fqn)
+;;   (phpinspect--log "Getting or creating index for %s" class-fqn)
+;;   (phpinspect-get-or-create-cached-project-class
+;;    (phpinspect-project-root)
+;;    class-fqn))
+
+(defun phpinspect-index-file (file-name)
+  (phpinspect--index-tokens (phpinspect-parse-file file-name)))
+
+(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))))
+
+    ;; (let ((existing-index (phpinspect-get-cached-project-class
+    ;;                        project-root
+    ;;                        class-fqn)))
+    ;;   (or
+    ;;    existing-index
+    ;;    (progn
+    ;;      (let* ((class-file (phpinspect-class-filepath class-fqn))
+    ;;             (visited-buffer (when class-file (find-buffer-visiting 
class-file)))
+    ;;             (new-index))
+
+    ;;        (phpinspect--log "No existing index for FQN: %s" class-fqn)
+    ;;        (phpinspect--log "filepath: %s" class-file)
+    ;;        (when class-file
+    ;;          (if visited-buffer
+    ;;              (setq new-index (with-current-buffer visited-buffer
+    ;;                                (phpinspect--index-current-buffer)))
+    ;;            (setq new-index (phpinspect-index-file class-file)))
+    ;;          (dolist (class (alist-get 'classes new-index))
+    ;;            (when class
+    ;;              (phpinspect-cache-project-class
+    ;;               project-root
+    ;;               (cdr class))))
+    ;;          (alist-get class-fqn (alist-get 'classes new-index)
+    ;;                     nil
+    ;;                     nil
+    ;;                     #'phpinspect--type=))))))))
+
+
+(defun phpinspect--index-current-buffer ()
+  (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
+
+(defun phpinspect-index-current-buffer ()
+  "Index a PHP file for classes and the methods they have"
+  (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
+
+;; (defun phpinspect--get-variables-for-class (buffer-classes class &optional 
static)
+;;   (let ((class-index (or (assoc-default class buffer-classes 
#'phpinspect--type=)
+;;                          (phpinspect--get-or-create-index-for-class-file 
class))))
+;;     (when class-index
+;;       (if static
+;;           (append (alist-get 'static-variables class-index)
+;;                   (alist-get 'constants class-index))
+;;         (alist-get 'variables class-index)))))
+
+
+(provide 'phpinspect-index)
+;;; phpinspect-index.el ends here
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
new file mode 100644
index 0000000000..9d2609bcbc
--- /dev/null
+++ b/phpinspect-parser.el
@@ -0,0 +1,828 @@
+;;; phpinspect-parser.el --- PHP parsing module  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(defvar phpinspect-parser-obarray (obarray-make)
+  "An obarray containing symbols for all phpinspect (sub)parsers.")
+
+(defvar phpinspect-handler-obarray (obarray-make)
+  "An obarray containing symbols for all phpinspect parser handlers.")
+
+(eval-when-compile
+  (define-inline phpinspect--word-end-regex ()
+    (inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)")))
+
+(defun phpinspect-list-handlers ()
+  (let ((handlers))
+    (mapatoms (lambda (handler)
+                (push (symbol-name handler) handlers))
+              phpinspect-handler-obarray)
+    handlers))
+
+(defun phpinspect-describe-handler (handler-name)
+  "Display a buffer containing HANDLER-NAMEs docstring and attribute-plist."
+  (interactive (list (completing-read "Pick a handler:" 
(phpinspect-list-handlers))))
+  (with-current-buffer (get-buffer-create "phpinspect-handler-description")
+    (insert (concat
+             (pp (symbol-value (intern handler-name 
phpinspect-handler-obarray)))
+             "\n"
+             (documentation (intern handler-name phpinspect-handler-obarray))))
+    (pop-to-buffer (current-buffer))))
+
+(defsubst phpinspect--strip-last-char (string)
+  (substring string 0 (- (length string) 1)))
+
+(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
+  "Return a token of type TOKEN-KEYWORD with STRING as value.
+If STRING has text properties, they are stripped."
+  (let ((value (copy-sequence string))
+        (length (length string)))
+    (forward-char length)
+    (set-text-properties 0 length nil value)
+    (list token-keyword value)))
+
+
+(defsubst phpinspect-token-type-p (object type)
+  "Returns t if OBJECT is a token of type TYPE.
+Type can be any of the token types returned by
+`phpinspect-parse-buffer-until-point`"
+  (and (listp object) (eq (car object) type)))
+
+(defsubst phpinspect-object-attrib-p (token)
+  (phpinspect-token-type-p token :object-attrib))
+
+(defsubst phpinspect-static-attrib-p (token)
+  (phpinspect-token-type-p token :static-attrib))
+
+(defsubst phpinspect-attrib-p (token)
+  (or (phpinspect-object-attrib-p token)
+      (phpinspect-static-attrib-p token)))
+
+(defun phpinspect-html-p (token)
+  (phpinspect-token-type-p token :html))
+
+(defun phpinspect-comma-p (token)
+  (phpinspect-token-type-p token :comma))
+
+(defsubst phpinspect-terminator-p (token)
+  (phpinspect-token-type-p token :terminator))
+
+(defsubst phpinspect-end-of-token-p (token)
+  (or (phpinspect-terminator-p token)
+      (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))
+
+(defsubst phpinspect-block-p (token)
+  (or (phpinspect-token-type-p token :block)
+      (phpinspect-incomplete-block-p token)))
+
+(defun phpinspect-end-of-use-p (token)
+  (or (phpinspect-block-p token)
+      (phpinspect-end-of-token-p token)))
+
+(defun phpinspect-static-p (token)
+  (phpinspect-token-type-p token :static))
+
+(defsubst phpinspect-incomplete-const-p (token)
+  (phpinspect-token-type-p token :incomplete-const))
+
+(defsubst phpinspect-const-p (token)
+  (or (phpinspect-token-type-p token :const)
+      (phpinspect-incomplete-const-p token)))
+
+(defsubst phpinspect-scope-p (token)
+  (or (phpinspect-token-type-p token :public)
+      (phpinspect-token-type-p token :private)
+      (phpinspect-token-type-p token :protected)))
+
+(defsubst phpinspect-namespace-p (object)
+  (phpinspect-token-type-p object :namespace))
+
+(defun phpinspect-incomplete-class-p (token)
+  (and (phpinspect-class-p token)
+       (phpinspect-incomplete-block-p (car (last token)))))
+
+(defun phpinspect-incomplete-namespace-p (token)
+  (and (phpinspect-namespace-p token)
+       (or (phpinspect-incomplete-block-p (car (last token)))
+           (phpinspect-incomplete-class-p (car (last token))))))
+
+(defun phpinspect-function-p (token)
+  (phpinspect-token-type-p token :function))
+
+
+(defun phpinspect-class-p (token)
+  (phpinspect-token-type-p token :class))
+
+(defun phpinspect-incomplete-method-p (token)
+  (or (phpinspect-incomplete-function-p token)
+      (and (phpinspect-scope-p token)
+           (phpinspect-incomplete-function-p (car (last token))))
+      (and (phpinspect-scope-p token)
+           (phpinspect-static-p (car (last token)))
+           (phpinspect-incomplete-function-p (car (last (car (last token))))))
+      (and (phpinspect-scope-p token)
+           (phpinspect-function-p (car (last token))))))
+
+(defun phpinspect-incomplete-function-p (token)
+  (and (phpinspect-function-p token)
+       (phpinspect-incomplete-block-p (car (last token)))))
+
+(defsubst phpinspect-incomplete-list-p (token)
+  (phpinspect-token-type-p token :incomplete-list))
+
+(defsubst phpinspect-list-p (token)
+  (or (phpinspect-token-type-p token :list)
+      (phpinspect-incomplete-list-p token)))
+
+(defun phpinspect-declaration-p (token)
+  (phpinspect-token-type-p token :declaration))
+
+(defsubst phpinspect-assignment-p (token)
+  (phpinspect-token-type-p token :assignment))
+
+(defun phpinspect-function-argument-list (php-func)
+  "Get the argument list of a function"
+  (seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func 
nil) nil))
+
+(defsubst phpinspect-variable-p (token)
+  (phpinspect-token-type-p token :variable))
+
+(defsubst phpinspect-word-p (token)
+  (phpinspect-token-type-p token :word))
+
+(defsubst phpinspect-incomplete-array-p (token)
+  (phpinspect-token-type-p token :incomplete-array))
+
+(defsubst phpinspect-array-p (token)
+  (or (phpinspect-token-type-p token :array)
+      (phpinspect-incomplete-array-p token)))
+
+(defsubst phpinspect-incomplete-token-p (token)
+  (or (phpinspect-incomplete-class-p token)
+      (phpinspect-incomplete-block-p token)
+      (phpinspect-incomplete-list-p token)
+      (phpinspect-incomplete-array-p token)
+      (phpinspect-incomplete-const-p token)
+      (phpinspect-incomplete-function-p token)
+      (phpinspect-incomplete-method-p token)
+      (phpinspect-incomplete-namespace-p token)))
+
+(defun phpinspect--static-terminator-p (token)
+  (or (phpinspect-function-p token)
+      (phpinspect-end-of-token-p token)))
+
+(defun phpinspect--scope-terminator-p (token)
+  (or (phpinspect-function-p token)
+      (phpinspect-end-of-token-p token)
+      (phpinspect-const-p token)
+      (phpinspect-static-p token)))
+
+(defun phpinspect-namespace-keyword-p (token)
+  (and (phpinspect-word-p token) (string= (car (last token)) "namespace")))
+
+(defun phpinspect-use-keyword-p (token)
+  (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)))
+
+(defun phpinspect-use-p (object)
+  (phpinspect-token-type-p object :use))
+
+(defun phpinspect-comment-p (token)
+  (phpinspect-token-type-p token :comment))
+
+(defsubst phpinspect-class-block (class)
+  (caddr class))
+
+(defsubst phpinspect-namespace-block (namespace)
+  (when (and (= (length namespace) 3)
+             (phpinspect-block-p (caddr namespace)))
+    (caddr namespace)))
+
+(defsubst phpinspect-function-block (php-func)
+  (caddr php-func))
+
+(defsubst phpinspect-not-class-p (token)
+  "Apply inverse of `phpinspect-class-p' to TOKEN."
+  (not (phpinspect-class-p token)))
+
+(defmacro phpinspect-defhandler (name arguments docstring attribute-plist 
&rest body)
+  "Define a parser handler that becomes available for use with 
phpinspect-parse.
+
+A parser handler is a function that is able to identify and parse
+tokens from PHP code at `point` in the current buffer.  It's
+return value must be the resulting token.  Aside from parsing it
+has to manage the state of `point` in a way that it skips over
+the tokens it has parsed.  That way the next handler can
+correctly pick up from where it has left off.
+
+Parser handlers are unrolled in a `cond` statement by
+`phpinspect-make-parser`.  The resulting code is something akin
+to the following:
+
+(while ...
+  (cond (((looking-at \"{\")
+          (funcall block-handler (match-string 0) max-point)
+         ((looking-at \"\\$\")
+          (funcall variable-handler ...
+etc.
+
+NAME must be a symbol.  It does not need to be prefixed with a
+\"namespace\" because parser handlers are stored in their own
+obarray (`phpinspect-handler-obarray`).
+
+ARGUMENTS must an argument list as accepted by `lambda`.  A
+handler must be able to accept 2 arguments: START-TOKEN and
+MAX-POINT.  START-TOKEN is the match string that resulted from
+the comparison of the handlers' `regexp` attribute with the text
+at `point`.  MAX-POINT is the point in the current buffer up
+until which the parser is supposed to parse.  For some tokens you
+may not want/need to respect MAX-POINT, in which case you can
+ignore it.
+
+DOCSTRING is mandatory.  It should contain a description of the
+tokens the handler is able to process and (if present) any
+particularities of the handler.
+
+ATTRIBUTE-PLIST is a plist that must contain at least a `regexp` key.
+    Possible keys:
+        - regexp: The regular expression that marks the start of the token.
+
+BODY is a function body as accepted by `lambda` that parses the
+text at point and returns the resulting token.
+
+When altering/adding handlers during runtime, make sure to purge
+the parser cache to make sure that your new handler functions are used.
+You can purge the parser cache with \\[phpinspect-purge-parser-cache]."
+  (declare (indent defun))
+  (when (not (symbolp name))
+    (error "In definition of phpinspect handler %s: NAME bust be a symbol" 
name))
+
+  (when (not (plist-member attribute-plist 'regexp))
+    (error "In definition of phpinspect handler %s ATTRIBUTE-PLIST must 
contain key `regexp`"
+           name))
+
+  ;; Eval regexp. It might be a `concat` statement and we don't want to be 
executing that
+  ;; every time the parser advances one character and has to check for the 
regexp
+  ;; occurence.
+  (setq attribute-plist (plist-put attribute-plist 'regexp
+                                   (eval (plist-get attribute-plist 'regexp)
+                                         t)))
+  (let ((name (symbol-name name)))
+    `(progn
+       (set (intern ,name phpinspect-handler-obarray) (quote ,attribute-plist))
+       (defalias (intern ,name phpinspect-handler-obarray)
+         #'(lambda (,@arguments)
+             ,docstring
+             ,@body))
+       (unless (byte-code-function-p
+                (symbol-function
+                 (intern ,name phpinspect-handler-obarray)))
+         (byte-compile (intern ,name phpinspect-handler-obarray))))))
+
+(defun phpinspect-get-parser-create (tree-type &rest parser-parameters)
+  "Retrieve a parser for TREE-TYPE from `phpinspect-parser-obarray'.
+
+TREE-TYPE must be a symbol or keyword representing the type of
+the token the parser is able to parse.
+
+If a parser by TREE-TYPE doesn't exist, it is created by callng
+`phpinspect-make-parser` with TREE-TYPE as first argument and
+PARSER-PARAMETERS as the rest of the arguments.  The resulting
+parser function is then returned in byte-compiled form."
+  (let* ((parser-name (symbol-name tree-type))
+         (parser-symbol (intern-soft parser-name phpinspect-parser-obarray)))
+    (or (and parser-symbol (symbol-function parser-symbol))
+        (defalias (intern parser-name phpinspect-parser-obarray)
+          (byte-compile (apply #'phpinspect-make-parser
+                               `(,tree-type ,@parser-parameters)))))))
+
+(defun phpinspect-purge-parser-cache ()
+  "Empty `phpinspect-parser-obarray`.
+
+This is useful when you need to change parser handlers or parsers
+during runtime.  Parsers are implemented with macros, so changing
+handler functions without calling this function will often not
+have any effect."
+  (interactive)
+  (setq phpinspect-parser-obarray (obarray-make)))
+
+(defun phpinspect-make-parser (tree-type handler-list &optional 
delimiter-predicate)
+  "Create a parser function using the handlers by names defined in 
HANDLER-LIST.
+
+See also `phpinspect-defhandler`.
+
+TREE-TYPE must be a symbol or a keyword representing the token
+type.
+
+HANDLER-LIST must be a list of either symbol or string
+representation of handler symbols which can be found in
+`phpinspect-handler-obarray`.
+
+DELIMITER-PREDICATE must be a function.  It is passed the last
+parsed token after every handler iteration.  If it evaluates to
+something other than nil, parsing is deemed completed and the
+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 ((handlers (mapcar
+                   (lambda (handler-name)
+                     (let* ((handler-name (symbol-name handler-name))
+                            (handler (intern-soft handler-name 
phpinspect-handler-obarray)))
+                       (if handler
+                           handler
+                         (error "No handler found by name \"%s\"" 
handler-name))))
+                   handler-list))
+        (delimiter-predicate (if (symbolp delimiter-predicate)
+                                 `(quote ,delimiter-predicate)
+                               delimiter-predicate)))
+    `(lambda (buffer max-point &optional continue-condition)
+       (with-current-buffer buffer
+         (let ((tokens)
+               (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
+           (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 ,(plist-get (symbol-value handler) 
'regexp))
+                          (let ((token (funcall ,(symbol-function handler)
+                                                (match-string 0)
+                                                max-point)))
+                            (when token
+                              (if (null tokens)
+                                  (setq tokens (list token))
+                                (nconc tokens (list token)))))))
+                      handlers)
+                   (t (forward-char))))
+           (push ,tree-type tokens))))))
+
+(phpinspect-defhandler comma (comma &rest _ignored)
+  "Handler for comma tokens"
+  (regexp ",")
+  (phpinspect-munch-token-without-attribs comma :comma))
+
+(phpinspect-defhandler word (word &rest _ignored)
+  "Handler for bareword tokens"
+  (regexp "[A-Za-z_\\][\\A-Za-z_0-9]*")
+  (let ((length (length word)))
+    (forward-char length)
+    (set-text-properties 0 length nil word)
+    (list :word word)))
+
+(defsubst phpinspect-handler (handler-name)
+  (intern-soft (symbol-name handler-name) phpinspect-handler-obarray))
+
+(defsubst phpinspect-handler-regexp (handler-name)
+  (plist-get (symbol-value (phpinspect-handler handler-name)) 'regexp))
+
+(defsubst phpinspect--parse-annotation-parameters (parameter-amount)
+  (let* ((words)
+         (word-handler (phpinspect-handler 'word))
+         (word-regexp (phpinspect-handler-regexp 'word))
+         (variable-handler (phpinspect-handler 'variable))
+         (variable-regexp (phpinspect-handler-regexp 'variable)))
+    (while (not (or (looking-at "\\*/") (= (length words) parameter-amount)))
+      (forward-char)
+      (cond ((looking-at word-regexp)
+             (push (funcall word-handler (match-string 0)) words))
+            ((looking-at variable-regexp)
+             (push (funcall variable-handler (match-string 0)) words))))
+    (nreverse words)))
+
+(phpinspect-defhandler annotation (start-token &rest _ignored)
+  "Handler for in-comment @annotations"
+  (regexp "@")
+  (forward-char (length start-token))
+  (if (looking-at (phpinspect-handler-regexp 'word))
+      (let ((annotation-name (match-string 0)))
+        (forward-char (length annotation-name))
+        (cond ((string= annotation-name "var")
+               ;; The @var annotation accepts 2 parameters:
+               ;; the type and the $variable name
+               (append (list :var-annotation)
+                       (phpinspect--parse-annotation-parameters 2)))
+              ((string= annotation-name "return")
+               ;; The @return annotation only accepts 1 word as parameter:
+               ;; The return type
+               (append (list :return-annotation)
+                       (phpinspect--parse-annotation-parameters 1)))
+              ((string= annotation-name "param")
+                 ;; The @param annotation accepts 2 parameters:
+                 ;; The type of the param, and the param's $name
+                 (append (list :param-annotation)
+                         (phpinspect--parse-annotation-parameters 2)))
+              (t
+               (list :annotation annotation-name))))
+    (list :annotation nil)))
+
+(phpinspect-defhandler tag (start-token max-point)
+  "Handler that discards any inline HTML it encounters"
+  (regexp "\\?>")
+  (forward-char (length start-token))
+  (or (re-search-forward "<\\?php\\|<\\?" nil t)
+      (goto-char max-point))
+  (list :html))
+
+(phpinspect-defhandler comment (start-token max-point)
+  "Handler for comments and doc blocks"
+  (regexp "#\\|//\\|/\\*")
+  (forward-char (length start-token))
+
+  (cond ((string-match "/\\*" start-token)
+         (let* ((continue-condition (lambda () (not (looking-at "\\*/"))))
+                (parser (phpinspect-get-parser-create
+                         :doc-block
+                         '(annotation whitespace)))
+                (doc-block (funcall parser (current-buffer) max-point 
continue-condition)))
+           (forward-char 2)
+           doc-block))
+        (t
+         (let ((parser (phpinspect-get-parser-create :comment '(tag) 
#'phpinspect-html-p))
+               (end-position (line-end-position)))
+           (funcall parser (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 whitespace (whitespace &rest _ignored)
+  "Handler that discards whitespace"
+  (regexp "[[:blank:]]+")
+  (forward-char (length whitespace)))
+
+(phpinspect-defhandler equals (equals &rest _ignored)
+  "Handler for strict and unstrict equality comparison tokens."
+  (regexp "===?")
+  (phpinspect-munch-token-without-attribs equals :equals))
+
+(phpinspect-defhandler assignment-operator (operator &rest _ignored)
+  "Handler for tokens indicating that an assignment is taking place"
+  (regexp "[+-]?=")
+  (phpinspect-munch-token-without-attribs operator :assignment))
+
+(phpinspect-defhandler terminator (terminator &rest _ignored)
+  "Handler for statement terminators."
+  (regexp ";")
+  (phpinspect-munch-token-without-attribs terminator :terminator))
+
+(phpinspect-defhandler use-keyword (start-token max-point)
+  "Handler for the use keyword and tokens that might follow to give it meaning"
+  (regexp (concat "use" (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (forward-char (length start-token))
+
+  (let ((parser (phpinspect-get-parser-create
+                 :use
+                 '(word tag block-without-scopes terminator)
+                 #'phpinspect-end-of-use-p)))
+    (funcall parser (current-buffer) max-point)))
+
+(phpinspect-defhandler attribute-reference (start-token &rest _ignored)
+  "Handler for references to object attributes, or static class attributes."
+  (regexp "->\\|::")
+  (forward-char (length start-token))
+  (looking-at (phpinspect-handler-regexp 'word))
+  (let ((name (if (looking-at (phpinspect-handler-regexp 'word))
+                  (funcall (phpinspect-handler 'word) (match-string 0))
+                nil)))
+    (cond
+     ((string= start-token "::")
+      (list :static-attrib name))
+     ((string= start-token "->")
+      (list :object-attrib name)))))
+
+(phpinspect-defhandler namespace (start-token max-point)
+  "Handler for the namespace keyword. This is a special one
+ because it is not always delimited by a block like classes or
+ functions. This handler parses the namespace declaration, and
+ then continues to parse subsequent tokens, only stopping when
+ either a block has been parsed or another namespace keyword has
+ been encountered."
+  (regexp (concat "namespace" (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (forward-char (length start-token))
+  (phpinspect-parse-with-handler-list
+   (current-buffer)
+   :namespace
+   max-point
+   (lambda () (not (looking-at (phpinspect-handler-regexp 'namespace))))
+   #'phpinspect-block-p))
+
+(phpinspect-defhandler const-keyword (start-token max-point)
+  "Handler for the const keyword."
+  (regexp (concat "const" (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (forward-char (length start-token))
+  (let* ((parser (phpinspect-get-parser-create
+                  :const
+                  '(word comment assignment-operator string array
+                         terminator)
+                  #'phpinspect-end-of-token-p))
+         (token (funcall parser (current-buffer) max-point)))
+    (when (phpinspect-incomplete-token-p (car (last token)))
+      (setcar token :incomplete-const))
+    token))
+
+(phpinspect-defhandler string (start-token &rest _ignored)
+  "Handler for strings"
+  (regexp "\"\\|'")
+  (list :string (phpinspect--munch-string start-token)))
+
+(phpinspect-defhandler block-without-scopes (start-token max-point)
+  "Handler for code blocks that cannot contain scope, const or
+static keywords with the same meaning as in a class block."
+  (regexp "{")
+  (forward-char (length start-token))
+  (let* ((complete-block nil)
+         (parser (phpinspect-get-parser-create
+                  :block
+                  '(array tag equals list comma
+                          attribute-reference variable
+                          assignment-operator whitespace
+                          function-keyword word terminator here-doc
+                          string comment block-without-scopes)))
+         (continue-condition (lambda ()
+                               (not (and (char-equal (char-after) ?})
+                                         (setq complete-block t)))))
+         (parsed (funcall parser (current-buffer) max-point 
continue-condition)))
+    (if complete-block
+        (forward-char)
+      (setcar parsed :incomplete-block))
+    parsed))
+
+
+(phpinspect-defhandler class-block (start-token max-point)
+  "Handler for code blocks that cannot contain classes"
+  (regexp "{")
+  (forward-char (length start-token))
+  (let* ((complete-block nil)
+         (parser (phpinspect-get-parser-create
+                  :block
+                  '(array tag equals list comma
+                          attribute-reference variable
+                          assignment-operator whitespace scope-keyword
+                          static-keyword const-keyword use-keyword
+                          function-keyword word terminator here-doc
+                          string comment block)))
+         (continue-condition (lambda ()
+                               (not (and (char-equal (char-after) ?})
+                                         (setq complete-block t)))))
+         (parsed (funcall parser (current-buffer) max-point 
continue-condition)))
+    (if complete-block
+        (forward-char)
+      (setcar parsed :incomplete-block))
+    parsed))
+
+(phpinspect-defhandler block (start-token max-point)
+  "Handler for code blocks"
+  (regexp "{")
+  (forward-char (length start-token))
+  (let* ((complete-block nil)
+         (continue-condition (lambda ()
+                               ;; When we encounter a closing brace for this
+                               ;; block, we can mark the block as complete.
+                               (not (and (char-equal (char-after) ?})
+                                         (setq complete-block t)))))
+         (parsed (phpinspect-parse-with-handler-list
+                  (current-buffer) :block max-point continue-condition)))
+    (if complete-block
+        ;; After meeting the char-after requirement above, we need to move
+        ;; one char forward to prevent parent-blocks from exiting because
+        ;; of the same char.
+        (forward-char)
+      (setcar parsed :incomplete-block))
+    parsed))
+
+(phpinspect-defhandler here-doc (start-token &rest _ignored)
+  "Handler for heredocs. Discards their contents."
+  (regexp "<<<")
+  (forward-char (length start-token))
+  (if (looking-at "[A-Za-z0-9'\"\\_]+")
+      (re-search-forward (concat "^" (regexp-quote (match-string 0))) nil t))
+  (list :here-doc))
+
+
+(defun phpinspect--munch-string (start-token)
+  "Consume text at point until a non-escaped `START-TOKEN` is found.
+
+Returns the consumed text string without face properties."
+  (forward-char (length start-token))
+  (let ((start-point (point)))
+    (cond ((looking-at start-token)
+           (forward-char)
+           "")
+          ((looking-at (concat "\\([\\][\\]\\)+" (regexp-quote start-token)))
+           (let ((match (match-string 0)))
+             (forward-char (length match))
+             (buffer-substring-no-properties start-point
+                                             (+ start-point (- (length match)
+                                                               (length 
start-token))))))
+          (t
+           (re-search-forward (format "\\([^\\]\\([\\][\\]\\)+\\|[^\\]\\)%s"
+                                      (regexp-quote start-token))
+                              nil t)
+           (buffer-substring-no-properties start-point (- (point) 1))))))
+
+(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 "(")
+  (forward-char (length start-token))
+  (let* ((complete-list nil)
+         (php-list (funcall
+                    (phpinspect-get-parser-create
+                     :list
+                     '(array tag equals list comma
+                             attribute-reference variable
+                             assignment-operator whitespace
+                             function-keyword word terminator here-doc
+                             string comment block-without-scopes))
+                    (current-buffer)
+                    max-point
+                    (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))
+
+;; TODO: Look into using different names for function and class :declaration 
tokens. They
+;; don't necessarily require the same handlers to parse.
+(defsubst phpinspect-get-or-create-declaration-parser ()
+  (phpinspect-get-parser-create :declaration
+                                '(comment word list terminator tag)
+                                #'phpinspect-end-of-token-p))
+
+
+(phpinspect-defhandler function-keyword (start-token max-point)
+  "Handler for the function keyword and tokens that follow to give it meaning"
+  (regexp (concat "function" (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (let* ((parser (phpinspect-get-or-create-declaration-parser))
+         (continue-condition (lambda () (not (char-equal (char-after) ?{))))
+         (declaration (funcall parser (current-buffer) max-point 
continue-condition)))
+    (if (phpinspect-end-of-token-p (car (last declaration)))
+        (list :function declaration)
+      (list :function
+            declaration
+            (funcall (phpinspect-handler 'block-without-scopes)
+                     (char-to-string (char-after)) max-point)))))
+
+(phpinspect-defhandler scope-keyword (start-token max-point)
+  "Handler for scope keywords"
+  (regexp (mapconcat (lambda (word)
+                       (concat word (phpinspect--word-end-regex)))
+                     (list "public" "private" "protected")
+                     "\\|"))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (forward-char (length start-token))
+  (funcall (phpinspect-get-parser-create
+            (cond ((string= start-token "public") :public)
+                  ((string= start-token "private") :private)
+                  ((string= start-token "protected") :protected))
+            '(function-keyword static-keyword const-keyword
+                               variable here-doc string terminator tag comment)
+            #'phpinspect--scope-terminator-p)
+           (current-buffer)
+           max-point))
+
+(phpinspect-defhandler static-keyword (start-token max-point)
+  "Handler for the static keyword"
+  (regexp (concat "static" (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (forward-char (length start-token))
+  (funcall (phpinspect-get-parser-create
+            :static
+            '(comment function-keyword variable array word
+                      terminator tag)
+            #'phpinspect--static-terminator-p)
+           (current-buffer)
+           max-point))
+
+(phpinspect-defhandler fat-arrow (arrow &rest _ignored)
+  "Handler for the \"fat arrow\" in arrays and foreach expressions"
+  (regexp "=>")
+  (phpinspect-munch-token-without-attribs arrow :fat-arrow))
+
+(phpinspect-defhandler array (start-token max-point)
+  "Handler for arrays, in the bracketet as well as the list notation"
+  (regexp "\\[\\|array(")
+  (forward-char (length start-token))
+  (let* ((end-char (cond ((string= start-token "[") ?\])
+                         ((string= start-token "array(") ?\))))
+         (end-char-reached nil)
+         (token (funcall (phpinspect-get-parser-create
+                          :array
+                          '(comment comma list here-doc string
+                                    array variable attribute-reference
+                                    word fat-arrow))
+                         (current-buffer)
+                         max-point
+                         (lambda () (not (and (char-equal (char-after) 
end-char)
+                                              (setq end-char-reached t)))))))
+
+    ;; Skip over the end char to prevent enclosing arrays or lists
+    ;; from terminating.
+    (if end-char-reached
+        (forward-char)
+      ;; Signal incompleteness when terminated because of max-point
+      (setcar token :incomplete-array))
+    token))
+
+(phpinspect-defhandler class-keyword (start-token max-point)
+  "Handler for the class keyword, and tokens that follow to define
+the properties of the class"
+  (regexp (concat "\\(abstract\\|final\\|class\\|interface\\|trait\\)"
+                  (phpinspect--word-end-regex)))
+  (setq start-token (phpinspect--strip-last-char start-token))
+  (list :class (funcall (phpinspect-get-or-create-declaration-parser)
+                        (current-buffer)
+                        max-point
+                        (lambda () (not (char-equal (char-after) ?{))))
+        (funcall (phpinspect-handler 'class-block)
+                 (char-to-string (char-after)) max-point)))
+
+(defun phpinspect-parse-with-handler-list
+    (buffer tree-type max-point &optional continue-condition 
delimiter-predicate)
+  "Parse BUFFER for TREE-TYPE tokens until MAX-POINT.
+
+Stop at CONTINUE-CONDITION or DELIMITER-PREDICATE.
+
+This just calls `phpinspect-get-parser-create` to make a parser
+that contains all handlers necessary to parse code."
+  (let ((parser (phpinspect-get-parser-create
+                 tree-type
+                 '(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)
+                 delimiter-predicate)))
+    (funcall parser buffer max-point continue-condition)))
+
+
+(defun phpinspect-parse-buffer-until-point (buffer point)
+  (with-current-buffer buffer
+    (save-excursion
+      (goto-char (point-min))
+      (re-search-forward "<\\?php\\|<\\?" nil t)
+      (funcall (phpinspect-get-parser-create
+                :root
+                '(namespace array 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 tag block))
+               (current-buffer)
+               point))))
+
+(provide 'phpinspect-parser)
+;;; phpinspect-parser.el ends here
diff --git a/phpinspect-project.el b/phpinspect-project.el
new file mode 100644
index 0000000000..2dfa5efce1
--- /dev/null
+++ b/phpinspect-project.el
@@ -0,0 +1,92 @@
+;;; phpinspect-project.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(require 'phpinspect-class)
+(require 'phpinspect-type)
+
+(cl-defstruct (phpinspect--project (:constructor 
phpinspect--make-project-cache))
+  (class-index (make-hash-table :test 'eq :size 100 :rehash-size 40)
+               :type hash-table
+               :documentation
+               "A `hash-table` that contains all of the currently
+indexed classes in the project"))
+
+(cl-defgeneric phpinspect--project-add-class
+    ((project phpinspect--project) (class (head phpinspect--indexed-class)))
+  "Add an indexed CLASS to PROJECT.")
+
+(cl-defmethod phpinspect--project-add-class
+  ((project phpinspect--project) (indexed-class (head 
phpinspect--indexed-class)))
+  (let* ((class-name (phpinspect--type-name-symbol
+                      (alist-get 'class-name (cdr indexed-class))))
+         (existing-class (gethash class-name
+                                  (phpinspect--project-class-index project))))
+    (if existing-class
+        (phpinspect--class-set-index existing-class indexed-class)
+      (let ((new-class (phpinspect--make-class-generated :project project)))
+        (phpinspect--class-set-index new-class indexed-class)
+        (puthash class-name new-class (phpinspect--project-class-index 
project))))))
+
+(cl-defgeneric phpinspect--project-get-class
+    ((project phpinspect--project) (class-fqn phpinspect--type))
+  "Get indexed class by name of CLASS-FQN stored in PROJECT.")
+
+(cl-defmethod phpinspect--project-get-class-create
+  ((project phpinspect--project) (class-fqn phpinspect--type))
+  (let ((class (phpinspect--project-get-class project class-fqn)))
+    (unless class
+      (setq class (phpinspect--make-class-generated :project project))
+      (puthash (phpinspect--type-name-symbol class-fqn)
+               class
+               (phpinspect--project-class-index project))
+
+      (let* ((class-file (phpinspect-class-filepath class-fqn))
+             (visited-buffer (when class-file (find-buffer-visiting 
class-file)))
+             (new-index)
+             (class-index))
+
+        (phpinspect--log "No existing index for FQN: %s" class-fqn)
+        (phpinspect--log "filepath: %s" class-file)
+        (when class-file
+          (if visited-buffer
+              (setq new-index (with-current-buffer visited-buffer
+                                (phpinspect--index-current-buffer)))
+            (setq new-index (phpinspect-index-file class-file)))
+          (setq class-index
+                (alist-get class-fqn (alist-get 'classes new-index)
+                           nil
+                           nil
+                           #'phpinspect--type=))
+          (when class-index
+            (phpinspect--class-set-index class class-index)))))
+    class))
+
+(cl-defmethod phpinspect--project-get-class
+  ((project phpinspect--project) (class-fqn phpinspect--type))
+  (gethash (phpinspect--type-name-symbol class-fqn)
+           (phpinspect--project-class-index project)))
+
+(provide 'phpinspect-project)
+;;; phpinspect-project.el ends here
diff --git a/phpinspect-type.el b/phpinspect-type.el
new file mode 100644
index 0000000000..0816fa04a0
--- /dev/null
+++ b/phpinspect-type.el
@@ -0,0 +1,187 @@
+;;; phpinspect-type.el --- Data structures that represent phpinspect types  
-*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(require 'phpinspect-util)
+
+(cl-defstruct (phpinspect--type
+               (:constructor phpinspect--make-type-generated))
+  "Represents an instance of a PHP type in the phpinspect syntax tree."
+  (name-symbol nil
+               :type symbol
+               :documentation
+               "Symbol representation of the type name.")
+  (collection nil
+              :type bool
+              :documentation
+              "Whether or not the type is a collection")
+  (contains nil
+            :type phpinspect--type
+            :documentation
+            "When the type is a collection, this attribute is set to the type 
that the collection is expected to contain")
+  (fully-qualified nil
+                   :type bool
+                   :documentation
+                   "Whether or not the type name is fully qualified"))
+
+(defmacro phpinspect--make-type (&rest property-list)
+  `(phpinspect--make-type-generated
+    ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
+
+(defconst phpinspect--null-type (phpinspect--make-type :name "\\null"))
+
+(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
+  (setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
+
+(cl-defmethod phpinspect--type-name ((type phpinspect--type))
+  (symbol-name (phpinspect--type-name-symbol type)))
+
+(defun phpinspect--get-bare-class-name-from-fqn (fqn)
+  (car (last (split-string fqn "\\\\"))))
+
+(cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
+  (phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
+
+(cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2 
phpinspect--type))
+  (eq (phpinspect--type-name-symbol type1) (phpinspect--type-name-symbol 
type2)))
+
+(defun phpinspect--resolve-type-name (types namespace type)
+  "Get the FQN for TYPE, using TYPES and NAMESPACE as context.
+
+TYPES must be an alist with class names as cars and FQNs as cdrs.
+NAMESPACE may be nil, or a string with a namespace FQN."
+  (phpinspect--log "Resolving %s from namespace %s" type namespace)
+  ;; Absolute FQN
+  (cond ((string-match "^\\\\" type)
+         type)
+
+        ;; Native type
+        ((member type phpinspect-native-types)
+         (concat "\\" type))
+
+        ;; Relative FQN
+        ((and namespace (string-match "\\\\" type))
+         (concat "\\" namespace "\\" type))
+
+        ;; Clas|interface|trait name
+        (t (let ((from-types (assoc-default (phpinspect-intern-name type) 
types #'eq)))
+             (concat "\\" (cond (from-types
+                                 (phpinspect--type-name from-types))
+                                (namespace
+                                 (concat namespace "\\" type))
+                                (t type)))))))
+
+(cl-defmethod phpinspect--type-resolve (types namespace (type 
phpinspect--type))
+  (unless (phpinspect--type-fully-qualified type)
+    (phpinspect--type-set-name
+     type
+     (phpinspect--resolve-type-name types namespace (phpinspect--type-name 
type)))
+    (setf (phpinspect--type-fully-qualified type) t))
+  type)
+
+(defun phpinspect--make-type-resolver (types &optional token-tree namespace)
+  "Little wrapper closure to pass around and resolve types with."
+
+  (let* ((inside-class
+          (if token-tree (or (phpinspect--find-innermost-incomplete-class 
token-tree)
+                             (phpinspect--find-class-token token-tree))))
+         (inside-class-name (if inside-class 
(phpinspect--get-class-name-from-token
+                                              inside-class)))
+         (self-type (phpinspect--make-type :name "self"))
+         (static-type (phpinspect--make-type :name "static")))
+    (lambda (type)
+      (phpinspect--type-resolve
+       types
+       namespace
+       (if (and inside-class-name (or (phpinspect--type= type self-type)
+                                      (phpinspect--type= type static-type)))
+           (progn
+             (phpinspect--log "Returning inside class name for %s : %s"
+                              type inside-class-name)
+             (phpinspect--make-type :name inside-class-name))
+         ;; else
+         type)))))
+
+(cl-defgeneric phpinspect--format-type-name (name)
+  (string-remove-prefix "\\" name))
+
+(cl-defmethod phpinspect--format-type-name ((type phpinspect--type))
+  (phpinspect--format-type-name (phpinspect--type-name type)))
+
+(cl-defstruct (phpinspect--function (:constructor 
phpinspect--make-function-generated))
+  "A PHP function."
+  (name-symbol nil
+               :type symbol
+               :documentation
+               "A symbol associated with the name of the function")
+  (scope nil
+         :type phpinspect-scope
+         :documentation
+         "When the function is a method, this should contain the
+scope of the function as returned by `phpinspect-parse-scope`.")
+  (arguments nil
+             :type list
+             :documentation
+             "A simple list with function arguments and their
+types in tuples. Each list should have the name of the variable
+as first element and the type as second element.")
+  (return-type nil
+               :type phpinspect--type
+               :documentation
+               "A phpinspect--type object representing the the
+return type of the function."))
+
+(defmacro phpinspect--make-function (&rest property-list)
+  `(phpinspect--make-function-generated
+    ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
+
+(cl-defmethod phpinspect--function-set-name ((func phpinspect--function) (name 
string))
+  (setf (phpinspect--function-name-symbol func) (intern name 
phpinspect-name-obarray)))
+
+(cl-defgeneric phpinspect--function-name ((func phpinspect--function)))
+
+(cl-defmethod phpinspect--function-name ((func phpinspect--function))
+  (symbol-name (phpinspect--function-name-symbol func)))
+
+
+(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
+  "A PHP Variable."
+  (name nil
+        :type string
+        :documentation
+        "A string containing the name of the variable.")
+  (scope nil
+         :type phpinspect-scope
+         :documentation
+         "When the variable is an object attribute, this should
+contain the scope of the variable as returned by
+`phpinspect-parse-scope`")
+  (type nil
+        :type string
+        :documentation
+        "A string containing the FQN of the variable's type"))
+
+
+(provide 'phpinspect-type)
+;;; phpinspect-type.el ends here
diff --git a/phpinspect-util.el b/phpinspect-util.el
new file mode 100644
index 0000000000..56920f506b
--- /dev/null
+++ b/phpinspect-util.el
@@ -0,0 +1,66 @@
+;;; phpinspect-util.el --- PHP parsing and completion package  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2021  Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@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:
+
+(defvar phpinspect-name-obarray (obarray-make)
+  "An obarray containing symbols for all encountered names in
+PHP. Used to optimize string comparison.")
+
+(defvar phpinspect--debug nil
+  "Enable debug logs for phpinspect by setting this variable to true")
+
+(defsubst phpinspect-intern-name (name)
+  (intern name phpinspect-name-obarray))
+
+(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
+  (let ((new-plist)
+        (wrap-value))
+    (dolist (item property-list)
+      (when wrap-value
+        (setq item `(phpinspect-intern-name ,item))
+        (setq wrap-value nil))
+      (when (eq item :name)
+        (setq item :name-symbol)
+        (setq wrap-value t))
+      (push item new-plist))
+    (nreverse new-plist)))
+
+(defun phpinspect-toggle-logging ()
+  (interactive)
+  (if (setq phpinspect--debug (not phpinspect--debug))
+      (message "Enabled phpinspect logging.")
+    (message "Disabled phpinspect logging.")))
+
+
+(defsubst phpinspect--log (&rest args)
+  (when phpinspect--debug
+    (with-current-buffer (get-buffer-create "**phpinspect-logs**")
+      (unless window-point-insertion-type
+        (set (make-local-variable 'window-point-insertion-type) t))
+      (goto-char (buffer-end 1))
+      (insert (concat "[" (format-time-string "%H:%M:%S") "]: "
+                           (apply #'format args) "\n")))))
+
+(provide 'phpinspect-util)
+;;; phpinspect-util.el ends here
diff --git a/phpinspect.el b/phpinspect.el
index d69169656c..1ceb74e6ab 100644
--- a/phpinspect.el
+++ b/phpinspect.el
@@ -29,23 +29,18 @@
 (require 'json)
 (require 'obarray)
 
-(defvar phpinspect-parser-obarray (obarray-make)
-  "An obarray containing symbols for all phpinspect (sub)parsers.")
-
-(defvar phpinspect-handler-obarray (obarray-make)
-  "An obarray containing symbols for all phpinspect parser handlers.")
-
-(defvar phpinspect-name-obarray (obarray-make)
-  "An obarray containing symbols for all encountered names in
-PHP. Used to optimize string comparison.")
+;; internal dependencies
+(require 'phpinspect-cache)
+(require 'phpinspect-parser)
+(require 'phpinspect-project)
+(require 'phpinspect-util)
+(require 'phpinspect-type)
+(require 'phpinspect-index)
 
 (defvar-local phpinspect--buffer-index nil
   "The result of the last successfull parse + index action
   executed by phpinspect for the current buffer")
 
-(defvar phpinspect--debug nil
-  "Enable debug logs for phpinspect by setting this variable to true")
-
 (defvar phpinspect-cache ()
   "In-memory nested key-value store used for caching by
 phpinspect")
@@ -88,905 +83,6 @@ Should normally be set to \"phpinspect-index.bash\" in the 
source
   '("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed")
   "FQNs of types that should be treated as collecitons when inferring types.")
 
-(defsubst phpinspect-intern-name (name)
-  (intern name phpinspect-name-obarray))
-
-(eval-when-compile
-  (define-inline phpinspect--word-end-regex ()
-    (inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)")))
-
-(defun phpinspect-list-handlers ()
-  (let ((handlers))
-    (mapatoms (lambda (handler)
-                (push (symbol-name handler) handlers))
-              phpinspect-handler-obarray)
-    handlers))
-
-(defun phpinspect-describe-handler (handler-name)
-  "Display a buffer containing HANDLER-NAMEs docstring and attribute-plist."
-  (interactive (list (completing-read "Pick a handler:" 
(phpinspect-list-handlers))))
-  (with-current-buffer (get-buffer-create "phpinspect-handler-description")
-    (insert (concat
-             (pp (symbol-value (intern handler-name 
phpinspect-handler-obarray)))
-             "\n"
-             (documentation (intern handler-name phpinspect-handler-obarray))))
-    (pop-to-buffer (current-buffer))))
-
-(defsubst phpinspect--strip-last-char (string)
-  (substring string 0 (- (length string) 1)))
-
-(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
-  "Return a token of type TOKEN-KEYWORD with STRING as value.
-If STRING has text properties, they are stripped."
-  (let ((value (copy-sequence string))
-        (length (length string)))
-    (forward-char length)
-    (set-text-properties 0 length nil value)
-    (list token-keyword value)))
-
-
-(defsubst phpinspect-token-type-p (object type)
-  "Returns t if OBJECT is a token of type TYPE.
-Type can be any of the token types returned by
-`phpinspect-parse-buffer-until-point`"
-  (and (listp object) (eq (car object) type)))
-
-(defsubst phpinspect-object-attrib-p (token)
-  (phpinspect-token-type-p token :object-attrib))
-
-(defsubst phpinspect-static-attrib-p (token)
-  (phpinspect-token-type-p token :static-attrib))
-
-(defsubst phpinspect-attrib-p (token)
-  (or (phpinspect-object-attrib-p token)
-      (phpinspect-static-attrib-p token)))
-
-(defun phpinspect-html-p (token)
-  (phpinspect-token-type-p token :html))
-
-(defun phpinspect-comma-p (token)
-  (phpinspect-token-type-p token :comma))
-
-(defsubst phpinspect-terminator-p (token)
-  (phpinspect-token-type-p token :terminator))
-
-(defsubst phpinspect-end-of-token-p (token)
-  (or (phpinspect-terminator-p token)
-      (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))
-
-(defsubst phpinspect-block-p (token)
-  (or (phpinspect-token-type-p token :block)
-      (phpinspect-incomplete-block-p token)))
-
-(defun phpinspect-end-of-use-p (token)
-  (or (phpinspect-block-p token)
-      (phpinspect-end-of-token-p token)))
-
-(defun phpinspect-static-p (token)
-  (phpinspect-token-type-p token :static))
-
-(defsubst phpinspect-incomplete-const-p (token)
-  (phpinspect-token-type-p token :incomplete-const))
-
-(defsubst phpinspect-const-p (token)
-  (or (phpinspect-token-type-p token :const)
-      (phpinspect-incomplete-const-p token)))
-
-(defsubst phpinspect-scope-p (token)
-  (or (phpinspect-token-type-p token :public)
-      (phpinspect-token-type-p token :private)
-      (phpinspect-token-type-p token :protected)))
-
-(defsubst phpinspect-namespace-p (object)
-  (phpinspect-token-type-p object :namespace))
-
-(defun phpinspect-incomplete-class-p (token)
-  (and (phpinspect-class-p token)
-       (phpinspect-incomplete-block-p (car (last token)))))
-
-(defun phpinspect-incomplete-namespace-p (token)
-  (and (phpinspect-namespace-p token)
-       (or (phpinspect-incomplete-block-p (car (last token)))
-           (phpinspect-incomplete-class-p (car (last token))))))
-
-(defun phpinspect-function-p (token)
-  (phpinspect-token-type-p token :function))
-
-
-(defun phpinspect-class-p (token)
-  (phpinspect-token-type-p token :class))
-
-(defun phpinspect-incomplete-method-p (token)
-  (or (phpinspect-incomplete-function-p token)
-      (and (phpinspect-scope-p token)
-           (phpinspect-incomplete-function-p (car (last token))))
-      (and (phpinspect-scope-p token)
-           (phpinspect-static-p (car (last token)))
-           (phpinspect-incomplete-function-p (car (last (car (last token))))))
-      (and (phpinspect-scope-p token)
-           (phpinspect-function-p (car (last token))))))
-
-(defun phpinspect-incomplete-function-p (token)
-  (and (phpinspect-function-p token)
-       (phpinspect-incomplete-block-p (car (last token)))))
-
-(defsubst phpinspect-incomplete-list-p (token)
-  (phpinspect-token-type-p token :incomplete-list))
-
-(defsubst phpinspect-list-p (token)
-  (or (phpinspect-token-type-p token :list)
-      (phpinspect-incomplete-list-p token)))
-
-(defun phpinspect-declaration-p (token)
-  (phpinspect-token-type-p token :declaration))
-
-(defsubst phpinspect-assignment-p (token)
-  (phpinspect-token-type-p token :assignment))
-
-(defun phpinspect-function-argument-list (php-func)
-  "Get the argument list of a function"
-  (seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func 
nil) nil))
-
-(defsubst phpinspect-variable-p (token)
-  (phpinspect-token-type-p token :variable))
-
-(defsubst phpinspect-word-p (token)
-  (phpinspect-token-type-p token :word))
-
-(defsubst phpinspect-incomplete-array-p (token)
-  (phpinspect-token-type-p token :incomplete-array))
-
-(defsubst phpinspect-array-p (token)
-  (or (phpinspect-token-type-p token :array)
-      (phpinspect-incomplete-array-p token)))
-
-(defsubst phpinspect-incomplete-token-p (token)
-  (or (phpinspect-incomplete-class-p token)
-      (phpinspect-incomplete-block-p token)
-      (phpinspect-incomplete-list-p token)
-      (phpinspect-incomplete-array-p token)
-      (phpinspect-incomplete-const-p token)
-      (phpinspect-incomplete-function-p token)
-      (phpinspect-incomplete-method-p token)
-      (phpinspect-incomplete-namespace-p token)))
-
-(defun phpinspect--static-terminator-p (token)
-  (or (phpinspect-function-p token)
-      (phpinspect-end-of-token-p token)))
-
-(defun phpinspect--scope-terminator-p (token)
-  (or (phpinspect-function-p token)
-      (phpinspect-end-of-token-p token)
-      (phpinspect-const-p token)
-      (phpinspect-static-p token)))
-
-(defun phpinspect-namespace-keyword-p (token)
-  (and (phpinspect-word-p token) (string= (car (last token)) "namespace")))
-
-(defun phpinspect-use-keyword-p (token)
-  (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)))
-
-(defun phpinspect-use-p (object)
-  (phpinspect-token-type-p object :use))
-
-(defun phpinspect-comment-p (token)
-  (phpinspect-token-type-p token :comment))
-
-(defsubst phpinspect-class-block (class)
-  (caddr class))
-
-(defsubst phpinspect-namespace-block (namespace)
-  (when (and (= (length namespace) 3)
-             (phpinspect-block-p (caddr namespace)))
-    (caddr namespace)))
-
-(defsubst phpinspect-function-block (php-func)
-  (caddr php-func))
-
-(defsubst phpinspect-not-class-p (token)
-  "Apply inverse of `phpinspect-class-p' to TOKEN."
-  (not (phpinspect-class-p token)))
-
-(defmacro phpinspect-defhandler (name arguments docstring attribute-plist 
&rest body)
-  "Define a parser handler that becomes available for use with 
phpinspect-parse.
-
-A parser handler is a function that is able to identify and parse
-tokens from PHP code at `point` in the current buffer.  It's
-return value must be the resulting token.  Aside from parsing it
-has to manage the state of `point` in a way that it skips over
-the tokens it has parsed.  That way the next handler can
-correctly pick up from where it has left off.
-
-Parser handlers are unrolled in a `cond` statement by
-`phpinspect-make-parser`.  The resulting code is something akin
-to the following:
-
-(while ...
-  (cond (((looking-at \"{\")
-          (funcall block-handler (match-string 0) max-point)
-         ((looking-at \"\\$\")
-          (funcall variable-handler ...
-etc.
-
-NAME must be a symbol.  It does not need to be prefixed with a
-\"namespace\" because parser handlers are stored in their own
-obarray (`phpinspect-handler-obarray`).
-
-ARGUMENTS must an argument list as accepted by `lambda`.  A
-handler must be able to accept 2 arguments: START-TOKEN and
-MAX-POINT.  START-TOKEN is the match string that resulted from
-the comparison of the handlers' `regexp` attribute with the text
-at `point`.  MAX-POINT is the point in the current buffer up
-until which the parser is supposed to parse.  For some tokens you
-may not want/need to respect MAX-POINT, in which case you can
-ignore it.
-
-DOCSTRING is mandatory.  It should contain a description of the
-tokens the handler is able to process and (if present) any
-particularities of the handler.
-
-ATTRIBUTE-PLIST is a plist that must contain at least a `regexp` key.
-    Possible keys:
-        - regexp: The regular expression that marks the start of the token.
-
-BODY is a function body as accepted by `lambda` that parses the
-text at point and returns the resulting token.
-
-When altering/adding handlers during runtime, make sure to purge
-the parser cache to make sure that your new handler functions are used.
-You can purge the parser cache with \\[phpinspect-purge-parser-cache]."
-  (declare (indent defun))
-  (when (not (symbolp name))
-    (error "In definition of phpinspect handler %s: NAME bust be a symbol" 
name))
-
-  (when (not (plist-member attribute-plist 'regexp))
-    (error "In definition of phpinspect handler %s ATTRIBUTE-PLIST must 
contain key `regexp`"
-           name))
-
-  ;; Eval regexp. It might be a `concat` statement and we don't want to be 
executing that
-  ;; every time the parser advances one character and has to check for the 
regexp
-  ;; occurence.
-  (setq attribute-plist (plist-put attribute-plist 'regexp
-                                   (eval (plist-get attribute-plist 'regexp)
-                                         t)))
-  (let ((name (symbol-name name)))
-    `(progn
-       (set (intern ,name phpinspect-handler-obarray) (quote ,attribute-plist))
-       (defalias (intern ,name phpinspect-handler-obarray)
-         #'(lambda (,@arguments)
-             ,docstring
-             ,@body))
-       (unless (byte-code-function-p
-                (symbol-function
-                 (intern ,name phpinspect-handler-obarray)))
-         (byte-compile (intern ,name phpinspect-handler-obarray))))))
-
-(defun phpinspect-get-parser-create (tree-type &rest parser-parameters)
-  "Retrieve a parser for TREE-TYPE from `phpinspect-parser-obarray'.
-
-TREE-TYPE must be a symbol or keyword representing the type of
-the token the parser is able to parse.
-
-If a parser by TREE-TYPE doesn't exist, it is created by callng
-`phpinspect-make-parser` with TREE-TYPE as first argument and
-PARSER-PARAMETERS as the rest of the arguments.  The resulting
-parser function is then returned in byte-compiled form."
-  (let* ((parser-name (symbol-name tree-type))
-         (parser-symbol (intern-soft parser-name phpinspect-parser-obarray)))
-    (or (and parser-symbol (symbol-function parser-symbol))
-        (defalias (intern parser-name phpinspect-parser-obarray)
-          (byte-compile (apply #'phpinspect-make-parser
-                               `(,tree-type ,@parser-parameters)))))))
-
-(defun phpinspect-purge-parser-cache ()
-  "Empty `phpinspect-parser-obarray`.
-
-This is useful when you need to change parser handlers or parsers
-during runtime.  Parsers are implemented with macros, so changing
-handler functions without calling this function will often not
-have any effect."
-  (interactive)
-  (setq phpinspect-parser-obarray (obarray-make)))
-
-(defun phpinspect-make-parser (tree-type handler-list &optional 
delimiter-predicate)
-  "Create a parser function using the handlers by names defined in 
HANDLER-LIST.
-
-See also `phpinspect-defhandler`.
-
-TREE-TYPE must be a symbol or a keyword representing the token
-type.
-
-HANDLER-LIST must be a list of either symbol or string
-representation of handler symbols which can be found in
-`phpinspect-handler-obarray`.
-
-DELIMITER-PREDICATE must be a function.  It is passed the last
-parsed token after every handler iteration.  If it evaluates to
-something other than nil, parsing is deemed completed and the
-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 ((handlers (mapcar
-                   (lambda (handler-name)
-                     (let* ((handler-name (symbol-name handler-name))
-                            (handler (intern-soft handler-name 
phpinspect-handler-obarray)))
-                       (if handler
-                           handler
-                         (error "No handler found by name \"%s\"" 
handler-name))))
-                   handler-list))
-        (delimiter-predicate (if (symbolp delimiter-predicate)
-                                 `(quote ,delimiter-predicate)
-                               delimiter-predicate)))
-    `(lambda (buffer max-point &optional continue-condition)
-       (with-current-buffer buffer
-         (let ((tokens)
-               (delimiter-predicate (when (functionp ,delimiter-predicate) 
,delimiter-predicate)))
-           (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 ,(plist-get (symbol-value handler) 
'regexp))
-                          (let ((token (funcall ,(symbol-function handler)
-                                                (match-string 0)
-                                                max-point)))
-                            (when token
-                              (if (null tokens)
-                                  (setq tokens (list token))
-                                (nconc tokens (list token)))))))
-                      handlers)
-                   (t (forward-char))))
-           (push ,tree-type tokens))))))
-
-(phpinspect-defhandler comma (comma &rest _ignored)
-  "Handler for comma tokens"
-  (regexp ",")
-  (phpinspect-munch-token-without-attribs comma :comma))
-
-(phpinspect-defhandler word (word &rest _ignored)
-  "Handler for bareword tokens"
-  (regexp "[A-Za-z_\\][\\A-Za-z_0-9]*")
-  (let ((length (length word)))
-    (forward-char length)
-    (set-text-properties 0 length nil word)
-    (list :word word)))
-
-(defsubst phpinspect-handler (handler-name)
-  (intern-soft (symbol-name handler-name) phpinspect-handler-obarray))
-
-(defsubst phpinspect-handler-regexp (handler-name)
-  (plist-get (symbol-value (phpinspect-handler handler-name)) 'regexp))
-
-(defsubst phpinspect--parse-annotation-parameters (parameter-amount)
-  (let* ((words)
-         (word-handler (phpinspect-handler 'word))
-         (word-regexp (phpinspect-handler-regexp 'word))
-         (variable-handler (phpinspect-handler 'variable))
-         (variable-regexp (phpinspect-handler-regexp 'variable)))
-    (while (not (or (looking-at "\\*/") (= (length words) parameter-amount)))
-      (forward-char)
-      (cond ((looking-at word-regexp)
-             (push (funcall word-handler (match-string 0)) words))
-            ((looking-at variable-regexp)
-             (push (funcall variable-handler (match-string 0)) words))))
-    (nreverse words)))
-
-(phpinspect-defhandler annotation (start-token &rest _ignored)
-  "Handler for in-comment @annotations"
-  (regexp "@")
-  (forward-char (length start-token))
-  (if (looking-at (phpinspect-handler-regexp 'word))
-      (let ((annotation-name (match-string 0)))
-        (forward-char (length annotation-name))
-        (cond ((string= annotation-name "var")
-               ;; The @var annotation accepts 2 parameters:
-               ;; the type and the $variable name
-               (append (list :var-annotation)
-                       (phpinspect--parse-annotation-parameters 2)))
-              ((string= annotation-name "return")
-               ;; The @return annotation only accepts 1 word as parameter:
-               ;; The return type
-               (append (list :return-annotation)
-                       (phpinspect--parse-annotation-parameters 1)))
-              ((string= annotation-name "param")
-                 ;; The @param annotation accepts 2 parameters:
-                 ;; The type of the param, and the param's $name
-                 (append (list :param-annotation)
-                         (phpinspect--parse-annotation-parameters 2)))
-              (t
-               (list :annotation annotation-name))))
-    (list :annotation nil)))
-
-(phpinspect-defhandler tag (start-token max-point)
-  "Handler that discards any inline HTML it encounters"
-  (regexp "\\?>")
-  (forward-char (length start-token))
-  (or (re-search-forward "<\\?php\\|<\\?" nil t)
-      (goto-char max-point))
-  (list :html))
-
-(phpinspect-defhandler comment (start-token max-point)
-  "Handler for comments and doc blocks"
-  (regexp "#\\|//\\|/\\*")
-  (forward-char (length start-token))
-
-  (cond ((string-match "/\\*" start-token)
-         (let* ((continue-condition (lambda () (not (looking-at "\\*/"))))
-                (parser (phpinspect-get-parser-create
-                         :doc-block
-                         '(annotation whitespace)))
-                (doc-block (funcall parser (current-buffer) max-point 
continue-condition)))
-           (forward-char 2)
-           doc-block))
-        (t
-         (let ((parser (phpinspect-get-parser-create :comment '(tag) 
#'phpinspect-html-p))
-               (end-position (line-end-position)))
-           (funcall parser (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 whitespace (whitespace &rest _ignored)
-  "Handler that discards whitespace"
-  (regexp "[[:blank:]]+")
-  (forward-char (length whitespace)))
-
-(phpinspect-defhandler equals (equals &rest _ignored)
-  "Handler for strict and unstrict equality comparison tokens."
-  (regexp "===?")
-  (phpinspect-munch-token-without-attribs equals :equals))
-
-(phpinspect-defhandler assignment-operator (operator &rest _ignored)
-  "Handler for tokens indicating that an assignment is taking place"
-  (regexp "[+-]?=")
-  (phpinspect-munch-token-without-attribs operator :assignment))
-
-(phpinspect-defhandler terminator (terminator &rest _ignored)
-  "Handler for statement terminators."
-  (regexp ";")
-  (phpinspect-munch-token-without-attribs terminator :terminator))
-
-(phpinspect-defhandler use-keyword (start-token max-point)
-  "Handler for the use keyword and tokens that might follow to give it meaning"
-  (regexp (concat "use" (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (forward-char (length start-token))
-
-  (let ((parser (phpinspect-get-parser-create
-                 :use
-                 '(word tag block-without-scopes terminator)
-                 #'phpinspect-end-of-use-p)))
-    (funcall parser (current-buffer) max-point)))
-
-(phpinspect-defhandler attribute-reference (start-token &rest _ignored)
-  "Handler for references to object attributes, or static class attributes."
-  (regexp "->\\|::")
-  (forward-char (length start-token))
-  (looking-at (phpinspect-handler-regexp 'word))
-  (let ((name (if (looking-at (phpinspect-handler-regexp 'word))
-                  (funcall (phpinspect-handler 'word) (match-string 0))
-                nil)))
-    (cond
-     ((string= start-token "::")
-      (list :static-attrib name))
-     ((string= start-token "->")
-      (list :object-attrib name)))))
-
-(phpinspect-defhandler namespace (start-token max-point)
-  "Handler for the namespace keyword. This is a special one
- because it is not always delimited by a block like classes or
- functions. This handler parses the namespace declaration, and
- then continues to parse subsequent tokens, only stopping when
- either a block has been parsed or another namespace keyword has
- been encountered."
-  (regexp (concat "namespace" (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (forward-char (length start-token))
-  (phpinspect-parse-with-handler-list
-   (current-buffer)
-   :namespace
-   max-point
-   (lambda () (not (looking-at (phpinspect-handler-regexp 'namespace))))
-   #'phpinspect-block-p))
-
-(phpinspect-defhandler const-keyword (start-token max-point)
-  "Handler for the const keyword."
-  (regexp (concat "const" (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (forward-char (length start-token))
-  (let* ((parser (phpinspect-get-parser-create
-                  :const
-                  '(word comment assignment-operator string array
-                         terminator)
-                  #'phpinspect-end-of-token-p))
-         (token (funcall parser (current-buffer) max-point)))
-    (when (phpinspect-incomplete-token-p (car (last token)))
-      (setcar token :incomplete-const))
-    token))
-
-(phpinspect-defhandler string (start-token &rest _ignored)
-  "Handler for strings"
-  (regexp "\"\\|'")
-  (list :string (phpinspect--munch-string start-token)))
-
-(phpinspect-defhandler block-without-scopes (start-token max-point)
-  "Handler for code blocks that cannot contain scope, const or
-static keywords with the same meaning as in a class block."
-  (regexp "{")
-  (forward-char (length start-token))
-  (let* ((complete-block nil)
-         (parser (phpinspect-get-parser-create
-                  :block
-                  '(array tag equals list comma
-                          attribute-reference variable
-                          assignment-operator whitespace
-                          function-keyword word terminator here-doc
-                          string comment block-without-scopes)))
-         (continue-condition (lambda ()
-                               (not (and (char-equal (char-after) ?})
-                                         (setq complete-block t)))))
-         (parsed (funcall parser (current-buffer) max-point 
continue-condition)))
-    (if complete-block
-        (forward-char)
-      (setcar parsed :incomplete-block))
-    parsed))
-
-
-(phpinspect-defhandler class-block (start-token max-point)
-  "Handler for code blocks that cannot contain classes"
-  (regexp "{")
-  (forward-char (length start-token))
-  (let* ((complete-block nil)
-         (parser (phpinspect-get-parser-create
-                  :block
-                  '(array tag equals list comma
-                          attribute-reference variable
-                          assignment-operator whitespace scope-keyword
-                          static-keyword const-keyword use-keyword
-                          function-keyword word terminator here-doc
-                          string comment block)))
-         (continue-condition (lambda ()
-                               (not (and (char-equal (char-after) ?})
-                                         (setq complete-block t)))))
-         (parsed (funcall parser (current-buffer) max-point 
continue-condition)))
-    (if complete-block
-        (forward-char)
-      (setcar parsed :incomplete-block))
-    parsed))
-
-(phpinspect-defhandler block (start-token max-point)
-  "Handler for code blocks"
-  (regexp "{")
-  (forward-char (length start-token))
-  (let* ((complete-block nil)
-         (continue-condition (lambda ()
-                               ;; When we encounter a closing brace for this
-                               ;; block, we can mark the block as complete.
-                               (not (and (char-equal (char-after) ?})
-                                         (setq complete-block t)))))
-         (parsed (phpinspect-parse-with-handler-list
-                  (current-buffer) :block max-point continue-condition)))
-    (if complete-block
-        ;; After meeting the char-after requirement above, we need to move
-        ;; one char forward to prevent parent-blocks from exiting because
-        ;; of the same char.
-        (forward-char)
-      (setcar parsed :incomplete-block))
-    parsed))
-
-(phpinspect-defhandler here-doc (start-token &rest _ignored)
-  "Handler for heredocs. Discards their contents."
-  (regexp "<<<")
-  (forward-char (length start-token))
-  (if (looking-at "[A-Za-z0-9'\"\\_]+")
-      (re-search-forward (concat "^" (regexp-quote (match-string 0))) nil t))
-  (list :here-doc))
-
-
-(defun phpinspect--munch-string (start-token)
-  "Consume text at point until a non-escaped `START-TOKEN` is found.
-
-Returns the consumed text string without face properties."
-  (forward-char (length start-token))
-  (let ((start-point (point)))
-    (cond ((looking-at start-token)
-           (forward-char)
-           "")
-          ((looking-at (concat "\\([\\][\\]\\)+" (regexp-quote start-token)))
-           (let ((match (match-string 0)))
-             (forward-char (length match))
-             (buffer-substring-no-properties start-point
-                                             (+ start-point (- (length match)
-                                                               (length 
start-token))))))
-          (t
-           (re-search-forward (format "\\([^\\]\\([\\][\\]\\)+\\|[^\\]\\)%s"
-                                      (regexp-quote start-token))
-                              nil t)
-           (buffer-substring-no-properties start-point (- (point) 1))))))
-
-(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 "(")
-  (forward-char (length start-token))
-  (let* ((complete-list nil)
-         (php-list (funcall
-                    (phpinspect-get-parser-create
-                     :list
-                     '(array tag equals list comma
-                             attribute-reference variable
-                             assignment-operator whitespace
-                             function-keyword word terminator here-doc
-                             string comment block-without-scopes))
-                    (current-buffer)
-                    max-point
-                    (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))
-
-;; TODO: Look into using different names for function and class :declaration 
tokens. They
-;; don't necessarily require the same handlers to parse.
-(defsubst phpinspect-get-or-create-declaration-parser ()
-  (phpinspect-get-parser-create :declaration
-                                '(comment word list terminator tag)
-                                #'phpinspect-end-of-token-p))
-
-
-(phpinspect-defhandler function-keyword (start-token max-point)
-  "Handler for the function keyword and tokens that follow to give it meaning"
-  (regexp (concat "function" (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (let* ((parser (phpinspect-get-or-create-declaration-parser))
-         (continue-condition (lambda () (not (char-equal (char-after) ?{))))
-         (declaration (funcall parser (current-buffer) max-point 
continue-condition)))
-    (if (phpinspect-end-of-token-p (car (last declaration)))
-        (list :function declaration)
-      (list :function
-            declaration
-            (funcall (phpinspect-handler 'block-without-scopes)
-                     (char-to-string (char-after)) max-point)))))
-
-(phpinspect-defhandler scope-keyword (start-token max-point)
-  "Handler for scope keywords"
-  (regexp (mapconcat (lambda (word)
-                       (concat word (phpinspect--word-end-regex)))
-                     (list "public" "private" "protected")
-                     "\\|"))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (forward-char (length start-token))
-  (funcall (phpinspect-get-parser-create
-            (cond ((string= start-token "public") :public)
-                  ((string= start-token "private") :private)
-                  ((string= start-token "protected") :protected))
-            '(function-keyword static-keyword const-keyword
-                               variable here-doc string terminator tag comment)
-            #'phpinspect--scope-terminator-p)
-           (current-buffer)
-           max-point))
-
-(phpinspect-defhandler static-keyword (start-token max-point)
-  "Handler for the static keyword"
-  (regexp (concat "static" (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (forward-char (length start-token))
-  (funcall (phpinspect-get-parser-create
-            :static
-            '(comment function-keyword variable array word
-                      terminator tag)
-            #'phpinspect--static-terminator-p)
-           (current-buffer)
-           max-point))
-
-(phpinspect-defhandler fat-arrow (arrow &rest _ignored)
-  "Handler for the \"fat arrow\" in arrays and foreach expressions"
-  (regexp "=>")
-  (phpinspect-munch-token-without-attribs arrow :fat-arrow))
-
-(phpinspect-defhandler array (start-token max-point)
-  "Handler for arrays, in the bracketet as well as the list notation"
-  (regexp "\\[\\|array(")
-  (forward-char (length start-token))
-  (let* ((end-char (cond ((string= start-token "[") ?\])
-                         ((string= start-token "array(") ?\))))
-         (end-char-reached nil)
-         (token (funcall (phpinspect-get-parser-create
-                          :array
-                          '(comment comma list here-doc string
-                                    array variable attribute-reference
-                                    word fat-arrow))
-                         (current-buffer)
-                         max-point
-                         (lambda () (not (and (char-equal (char-after) 
end-char)
-                                              (setq end-char-reached t)))))))
-
-    ;; Skip over the end char to prevent enclosing arrays or lists
-    ;; from terminating.
-    (if end-char-reached
-        (forward-char)
-      ;; Signal incompleteness when terminated because of max-point
-      (setcar token :incomplete-array))
-    token))
-
-(phpinspect-defhandler class-keyword (start-token max-point)
-  "Handler for the class keyword, and tokens that follow to define
-the properties of the class"
-  (regexp (concat "\\(abstract\\|final\\|class\\|interface\\|trait\\)"
-                  (phpinspect--word-end-regex)))
-  (setq start-token (phpinspect--strip-last-char start-token))
-  (list :class (funcall (phpinspect-get-or-create-declaration-parser)
-                        (current-buffer)
-                        max-point
-                        (lambda () (not (char-equal (char-after) ?{))))
-        (funcall (phpinspect-handler 'class-block)
-                 (char-to-string (char-after)) max-point)))
-
-(defun phpinspect-parse-with-handler-list
-    (buffer tree-type max-point &optional continue-condition 
delimiter-predicate)
-  "Parse BUFFER for TREE-TYPE tokens until MAX-POINT.
-
-Stop at CONTINUE-CONDITION or DELIMITER-PREDICATE.
-
-This just calls `phpinspect-get-parser-create` to make a parser
-that contains all handlers necessary to parse code."
-  (let ((parser (phpinspect-get-parser-create
-                 tree-type
-                 '(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)
-                 delimiter-predicate)))
-    (funcall parser buffer max-point continue-condition)))
-
-
-(defun phpinspect-parse-buffer-until-point (buffer point)
-  (with-current-buffer buffer
-    (save-excursion
-      (goto-char (point-min))
-      (re-search-forward "<\\?php\\|<\\?" nil t)
-      (funcall (phpinspect-get-parser-create
-                :root
-                '(namespace array 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 tag block))
-               (current-buffer)
-               point))))
-
-(cl-defstruct (phpinspect--type
-               (:constructor phpinspect--make-type-generated))
-  "Represents an instance of a PHP type in the phpinspect syntax tree."
-  (name-symbol nil
-               :type symbol
-               :documentation
-               "Symbol representation of the type name.")
-  (collection nil
-              :type bool
-              :documentation
-              "Whether or not the type is a collection")
-  (contains nil
-            :type phpinspect--type
-            :documentation
-            "When the type is a collection, this attribute is set to the type 
that the collection is expected to contain")
-  (fully-qualified nil
-                   :type bool
-                   :documentation
-                   "Whether or not the type name is fully qualified"))
-
-(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
-  (let ((new-plist)
-        (wrap-value))
-    (dolist (item property-list)
-      (when wrap-value
-        (setq item `(phpinspect-intern-name ,item))
-        (setq wrap-value nil))
-      (when (eq item :name)
-        (setq item :name-symbol)
-        (setq wrap-value t))
-      (push item new-plist))
-    (nreverse new-plist)))
-
-(defmacro phpinspect--make-type (&rest property-list)
-  `(phpinspect--make-type-generated
-    ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
-
-(defconst phpinspect--null-type (phpinspect--make-type :name "\\null"))
-
-(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
-  (setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
-
-(cl-defmethod phpinspect--type-name ((type phpinspect--type))
-  (symbol-name (phpinspect--type-name-symbol type)))
-
-(cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2 
phpinspect--type))
-  (eq (phpinspect--type-name-symbol type1) (phpinspect--type-name-symbol 
type2)))
-
-(cl-defstruct (phpinspect--function (:constructor 
phpinspect--make-function-generated))
-  "A PHP function."
-  (name-symbol nil
-               :type symbol
-               :documentation
-               "A symbol associated with the name of the function")
-  (scope nil
-         :type phpinspect-scope
-         :documentation
-         "When the function is a method, this should contain the
-scope of the function as returned by `phpinspect-parse-scope`.")
-  (arguments nil
-             :type list
-             :documentation
-             "A simple list with function arguments and their
-types in tuples. Each list should have the name of the variable
-as first element and the type as second element.")
-  (return-type nil
-               :type phpinspect--type
-               :documentation
-               "A phpinspect--type object representing the the
-return type of the function."))
-
-(defmacro phpinspect--make-function (&rest property-list)
-  `(phpinspect--make-function-generated
-    ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
-
-(cl-defmethod phpinspect--function-set-name ((func phpinspect--function) (name 
string))
-  (setf (phpinspect--function-name-symbol func) (intern name 
phpinspect-name-obarray)))
-
-(cl-defgeneric phpinspect--function-name ((func phpinspect--function)))
-
-(cl-defmethod phpinspect--function-name ((func phpinspect--function))
-  (symbol-name (phpinspect--function-name-symbol func)))
-
-
-(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
-  "A PHP Variable."
-  (name nil
-        :type string
-        :documentation
-        "A string containing the name of the variable.")
-  (scope nil
-         :type phpinspect-scope
-         :documentation
-         "When the variable is an object attribute, this should
-contain the scope of the variable as returned by
-`phpinspect-parse-scope`")
-  (type nil
-        :type string
-        :documentation
-        "A string containing the FQN of the variable's type"))
-
 (cl-defstruct (phpinspect--completion
                (:constructor phpinspect--construct-completion))
   "Contains a possible completion value with all it's attributes."
@@ -995,11 +91,6 @@ contain the scope of the variable as returned by
   (annotation nil :type string)
   (kind nil :type symbol))
 
-(cl-defgeneric phpinspect--format-type-name (name)
-  (string-remove-prefix "\\" name))
-
-(cl-defmethod phpinspect--format-type-name ((type phpinspect--type))
-  (phpinspect--format-type-name (phpinspect--type-name type)))
 
 (cl-defgeneric phpinspect--make-completion (completion-candidate)
   "Creates a `phpinspect--completion` for a possible completion
@@ -1078,21 +169,6 @@ accompanied by all of its enclosing tokens."
                     resolvecontext))
     resolvecontext)))
 
-(defun phpinspect-toggle-logging ()
-  (interactive)
-  (if (setq phpinspect--debug (not phpinspect--debug))
-      (message "Enabled phpinspect logging.")
-    (message "Disabled phpinspect logging.")))
-
-
-(defsubst phpinspect--log (&rest args)
-  (when phpinspect--debug
-    (with-current-buffer (get-buffer-create "**phpinspect-logs**")
-      (unless window-point-insertion-type
-        (set (make-local-variable 'window-point-insertion-type) t))
-      (goto-char (buffer-end 1))
-      (insert (concat "[" (format-time-string "%H:%M:%S") "]: "
-                           (apply #'format args) "\n")))))
 
 (defsubst phpinspect-cache-project-class (project-root indexed-class)
   (when project-root
@@ -1126,33 +202,6 @@ accompanied by all of its enclosing tokens."
 
     (seq-uniq classes #'eq)))
 
-
-;; (defun phpinspect-get-cached-project-class-methods
-;;     (project-root class-fqn &optional static)
-;;   (phpinspect--log "Getting cached project class methods for %s (%s)"
-;;                    project-root class-fqn)
-;;   (when project-root
-;;     (let ((index (phpinspect-get-or-create-cached-project-class
-;;                   project-root
-;;                   class-fqn)))
-;;       (when index
-;;         (phpinspect--log "Retrieved class index, starting method collection 
%s (%s)"
-;;                          project-root class-fqn)
-
-;;         (let ((methods-key (if static 'static-methods 'methods))
-;;               (methods))
-
-;;           (dolist (class (phpinspect-get-project-class-inherit-classes
-;;                           project-root
-;;                           index))
-;;             (dolist (method (alist-get methods-key class))
-;;               (push method methods)))
-
-;;           (dolist (method (alist-get methods-key index))
-;;             (push method methods))
-
-;;           (nreverse methods))))))
-
 (defun phpinspect-get-cached-project-class-methods (project-root class-fqn 
&optional static)
     (phpinspect--log "Getting cached project class methods for %s (%s)"
                    project-root class-fqn)
@@ -1175,19 +224,6 @@ accompanied by all of its enclosing tokens."
                      ,method-name-sym)
              (throw (quote ,break-sym) func)))))))
 
-;; (defsubst phpinspect-get-cached-project-class-method-type
-;;   (project-root class-fqn method-name)
-;;   (when project-root
-;;     (phpinspect--log "Getting cached project class method type for %s 
(%s::%s)"
-;;                      project-root class-fqn method-name)
-;;     (let ((found-method
-;;             (phpinspect-find-function-in-list
-;;              method-name
-;;              (phpinspect-get-cached-project-class-methods project-root 
class-fqn))))
-;;       (when found-method
-;;         (phpinspect--log "Found method: %s" found-method)
-;;         (phpinspect--function-return-type found-method)))))
-
 (defsubst phpinspect-get-cached-project-class-method-type
   (project-root class-fqn method-name)
     (when project-root
@@ -1636,427 +672,6 @@ resolve types of function argument variables."
                     (phpinspect-function-argument-list enclosing-token))))))
     type))
 
-(defun phpinspect--function-from-scope (scope)
-  (cond ((and (phpinspect-static-p (cadr scope))
-              (phpinspect-function-p (caddr scope)))
-         (caddr scope))
-        ((phpinspect-function-p (cadr scope))
-         (cadr scope))
-        (t nil)))
-
-(defun phpinspect--make-type-resolver (types &optional token-tree namespace)
-  "Little wrapper closure to pass around and resolve types with."
-
-  (let* ((inside-class
-          (if token-tree (or (phpinspect--find-innermost-incomplete-class 
token-tree)
-                             (phpinspect--find-class-token token-tree))))
-         (inside-class-name (if inside-class 
(phpinspect--get-class-name-from-token
-                                              inside-class)))
-         (self-type (phpinspect--make-type :name "self"))
-         (static-type (phpinspect--make-type :name "static")))
-    (lambda (type)
-      (phpinspect--type-resolve
-       types
-       namespace
-       (if (and inside-class-name (or (phpinspect--type= type self-type)
-                                      (phpinspect--type= type static-type)))
-           (progn
-             (phpinspect--log "Returning inside class name for %s : %s"
-                              type inside-class-name)
-             (phpinspect--make-type :name inside-class-name))
-         ;; else
-         type)))))
-
-
-(defun phpinspect--resolve-type-name (types namespace type)
-  "Get the FQN for TYPE, using TYPES and NAMESPACE as context.
-
-TYPES must be an alist with class names as cars and FQNs as cdrs.
-NAMESPACE may be nil, or a string with a namespace FQN."
-  (phpinspect--log "Resolving %s from namespace %s" type namespace)
-  ;; Absolute FQN
-  (cond ((string-match "^\\\\" type)
-         type)
-
-        ;; Native type
-        ((member type phpinspect-native-types)
-         (concat "\\" type))
-
-        ;; Relative FQN
-        ((and namespace (string-match "\\\\" type))
-         (concat "\\" namespace "\\" type))
-
-        ;; Clas|interface|trait name
-        (t (let ((from-types (assoc-default (phpinspect-intern-name type) 
types #'eq)))
-             (concat "\\" (cond (from-types
-                                 (phpinspect--type-name from-types))
-                                (namespace
-                                 (concat namespace "\\" type))
-                                (t type)))))))
-
-(cl-defmethod phpinspect--type-resolve (types namespace (type 
phpinspect--type))
-  (unless (phpinspect--type-fully-qualified type)
-    (phpinspect--type-set-name
-     type
-     (phpinspect--resolve-type-name types namespace (phpinspect--type-name 
type)))
-    (setf (phpinspect--type-fully-qualified type) t))
-  type)
-
-(defun phpinspect-var-annotation-p (token)
-  (phpinspect-token-type-p token :var-annotation))
-
-(defun phpinspect-return-annotation-p (token)
-  (phpinspect-token-type-p token :return-annotation))
-
-(defun phpinspect--index-function-arg-list (type-resolver arg-list)
-  (let ((arg-index)
-        (current-token)
-        (arg-list (cl-copy-list arg-list)))
-    (while (setq current-token (pop arg-list))
-      (cond ((and (phpinspect-word-p current-token)
-               (phpinspect-variable-p (car arg-list)))
-          (push `(,(cadr (pop arg-list))
-                  ,(funcall type-resolver (phpinspect--make-type :name (cadr 
current-token))))
-                arg-index))
-            ((phpinspect-variable-p (car arg-list))
-             (push `(,(cadr (pop arg-list))
-                     nil)
-                   arg-index))))
-    (nreverse arg-index)))
-
-(defun phpinspect--index-function-from-scope (type-resolver scope 
comment-before)
-  (let* ((php-func (cadr scope))
-         (declaration (cadr php-func))
-         (type (if (phpinspect-word-p (car (last declaration)))
-                   (phpinspect--make-type :name (cadar (last declaration))))))
-
-    ;; @return annotation. Has precedence over typehint when dealing with a 
collection.
-    (let* ((is-collection
-           (when type
-             (member (phpinspect--type-name
-                      (funcall type-resolver type)) 
phpinspect-collection-types)))
-           (return-annotation-type
-            (when (or (not type) is-collection)
-              (cadadr
-               (seq-find #'phpinspect-return-annotation-p
-                         comment-before)))))
-      (phpinspect--log "found return annotation %s" return-annotation-type)
-
-      (when return-annotation-type
-        (cond ((not type)
-               (setq type (funcall type-resolver
-                                   (phpinspect--make-type :name 
return-annotation-type))))
-              (is-collection
-               (phpinspect--log "Detected collection type in: %s" scope)
-               (setf (phpinspect--type-contains type)
-                     (funcall type-resolver
-                              (phpinspect--make-type :name 
return-annotation-type)))
-               (setf (phpinspect--type-collection type) t)))))
-
-    (phpinspect--make-function
-     :scope `(,(car scope))
-     :name (cadadr (cdr declaration))
-     :return-type (if type (funcall type-resolver type)
-                    phpinspect--null-type)
-     :arguments (phpinspect--index-function-arg-list
-                 type-resolver
-                 (phpinspect-function-argument-list php-func)))))
-
-(defun phpinspect--index-const-from-scope (scope)
-  (phpinspect--make-variable
-   :scope `(,(car scope))
-   :name (cadr (cadr (cadr scope)))))
-
-(defun phpinspect--var-annotations-from-token (token)
-  (seq-filter #'phpinspect-var-annotation-p token))
-
-(defun phpinspect--index-variable-from-scope (type-resolver scope 
comment-before)
-  "Index the variable inside `scope`."
-  (let* ((var-annotations (phpinspect--var-annotations-from-token 
comment-before))
-         (variable-name (cadr (cadr scope)))
-         (type (if var-annotations
-                   ;; Find the right annotation by variable name
-                   (or (cadr (cadr (seq-find (lambda (annotation)
-                                               (string= (cadr (caddr 
annotation)) variable-name))
-                                             var-annotations)))
-                       ;; Give up and just use the last one encountered
-                       (cadr (cadr (car (last var-annotations))))))))
-    (phpinspect--log "calling resolver from index-variable-from-scope")
-    (phpinspect--make-variable
-     :name variable-name
-     :scope `(,(car scope))
-     :type (if type (funcall type-resolver (phpinspect--make-type :name 
type))))))
-
-(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)))
-
-(defun phpinspect--index-class (type-resolver class)
-  "Create an alist with relevant attributes of a parsed class."
-  (phpinspect--log "INDEXING CLASS")
-  (let ((methods)
-        (static-methods)
-        (static-variables)
-        (variables)
-        (constants)
-        (extends)
-        (implements)
-        (class-name (phpinspect--get-class-name-from-token class))
-        ;; Keep track of encountered comments to be able to use type
-        ;; annotations.
-        (comment-before))
-
-    ;; Find out what the class extends or implements
-    (let ((enc-extends nil)
-          (enc-implements nil))
-      (dolist (word (cadr class))
-        (if (phpinspect-word-p word)
-            (cond ((string= (cadr word) "extends")
-                   (phpinspect--log "Class %s extends other classes" 
class-name)
-                   (setq enc-extends t))
-                  ((string= (cadr word) "implements")
-                   (setq enc-extends nil)
-                   (phpinspect--log "Class %s implements in interface" 
class-name)
-                   (setq enc-implements t))
-                  (t
-                   (phpinspect--log "Calling Resolver from index-class on %s" 
(cadr word))
-                   (cond (enc-extends
-                          (push (funcall type-resolver (phpinspect--make-type
-                                                        :name (cadr word)))
-                                extends))
-                         (enc-implements
-                          (push (funcall type-resolver (phpinspect--make-type
-                                                        :name (cadr word)))
-                                implements))))))))
-
-    (dolist (token (caddr class))
-      (cond ((phpinspect-scope-p token)
-             (cond ((phpinspect-const-p (cadr token))
-                    (push (phpinspect--index-const-from-scope token) 
constants))
-
-                   ((phpinspect-variable-p (cadr token))
-                    (push (phpinspect--index-variable-from-scope type-resolver
-                                                                 token
-                                                                 
comment-before)
-                          variables))
-
-                   ((phpinspect-static-p (cadr token))
-                    (cond ((phpinspect-function-p (cadadr token))
-                           (push (phpinspect--index-function-from-scope 
type-resolver
-                                                                        (list 
(car token)
-                                                                              
(cadadr token))
-                                                                        
comment-before)
-                                 static-methods))
-
-                          ((phpinspect-variable-p (cadadr token))
-                           (push (phpinspect--index-variable-from-scope 
type-resolver
-                                                                        (list 
(car token)
-                                                                              
(cadadr token))
-                                                                        
comment-before)
-                                 static-variables))))
-                   (t
-                    (phpinspect--log "comment-before is: %s" comment-before)
-                    (push (phpinspect--index-function-from-scope type-resolver
-                                                                 token
-                                                                 
comment-before)
-                          methods))))
-            ((phpinspect-static-p token)
-             (cond ((phpinspect-function-p (cadr token))
-                    (push (phpinspect--index-function-from-scope type-resolver
-                                                                 `(:public
-                                                                   ,(cadr 
token))
-                                                                 
comment-before)
-                          static-methods))
-
-                   ((phpinspect-variable-p (cadr token))
-                    (push (phpinspect--index-variable-from-scope type-resolver
-                                                                 `(:public
-                                                                   ,(cadr 
token))
-                                                                 
comment-before)
-                          static-variables))))
-            ((phpinspect-const-p token)
-             ;; Bare constants are always public
-             (push (phpinspect--index-const-from-scope (list :public token))
-                   constants))
-            ((phpinspect-function-p token)
-             ;; Bare functions are always public
-             (push (phpinspect--index-function-from-scope type-resolver
-                                                          (list :public token)
-                                                          comment-before)
-                   methods))
-            ((phpinspect-doc-block-p token)
-             (phpinspect--log "setting comment-before %s" token)
-             (setq comment-before token))
-
-            ;; Prevent comments from sticking around too long
-            (t
-             (phpinspect--log "Unsetting comment-before")
-             (setq comment-before nil))))
-
-    ;; Dirty hack that assumes the constructor argument names to be the same 
as the object
-    ;; attributes' names.
-    ;;;
-    ;; TODO: actually check the types of the variables assigned to object 
attributes
-    (let* ((constructor-sym (phpinspect-intern-name "__construct"))
-           (constructor (seq-find (lambda (method)
-                                   (eq (phpinspect--function-name-symbol 
method)
-                                            constructor-sym))
-                                 methods)))
-      (when constructor
-        (phpinspect--log "Constructor was found")
-        (dolist (variable variables)
-          (when (not (phpinspect--variable-type variable))
-            (phpinspect--log "Looking for variable type in constructor 
arguments (%s)"
-                             variable)
-            (let ((constructor-parameter-type
-                   (car (alist-get (phpinspect--variable-name variable)
-                                   (phpinspect--function-arguments constructor)
-                                   nil nil #'string=))))
-              (if constructor-parameter-type
-                  (setf (phpinspect--variable-type variable)
-                        (funcall type-resolver 
constructor-parameter-type))))))))
-
-    (let ((class-name (funcall type-resolver (phpinspect--make-type :name 
class-name))))
-      `(,class-name .
-                    (phpinspect--indexed-class
-                     (methods . ,methods)
-                     (class-name . ,class-name)
-                     (static-methods . ,static-methods)
-                     (static-variables . ,static-variables)
-                     (variables . ,variables)
-                     (constants . ,constants)
-                     (extends . ,extends)
-                     (implements . ,implements))))))
-
-(defsubst phpinspect-namespace-body (namespace)
-  "Return the nested tokens in NAMESPACE tokens' body.
-Accounts for namespaces that are defined with '{}' blocks."
-  (if (phpinspect-block-p (caddr namespace))
-      (cdaddr namespace)
-    (cdr namespace)))
-
-(defun phpinspect--index-classes (types classes &optional namespace indexed)
-  "Index the class tokens in `classes`, using the types in `types`
-as Fully Qualified names. `namespace` will be assumed the root
-namespace if not provided"
-  (if classes
-      (let ((class (pop classes)))
-        (push (phpinspect--index-class
-               (phpinspect--make-type-resolver types class namespace)
-               class)
-              indexed)
-        (phpinspect--index-classes types classes namespace indexed))
-    (nreverse indexed)))
-
-(defun phpinspect--use-to-type (use)
-  (let* ((fqn (cadr (cadr use)))
-         (type (phpinspect--make-type :name 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)
-  (phpinspect--index-classes
-   (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
-   (seq-filter #'phpinspect-class-p namespace)
-   (cadadr namespace)))
-
-(defun phpinspect--index-namespaces (namespaces &optional indexed)
-  (if namespaces
-      (progn
-        (push (phpinspect--index-namespace (pop namespaces)) indexed)
-        (phpinspect--index-namespaces namespaces indexed))
-    (apply #'append (nreverse indexed))))
-
-(defun phpinspect--index-functions (&rest _args)
-  "TODO: implement function indexation. This is a stub function.")
-
-(defun phpinspect--index-tokens (tokens)
-  "Index TOKENS as returned by `phpinspect--parse-current-buffer`."
-  `(phpinspect--root-index
-    ,(append
-      (append '(classes)
-              (phpinspect--index-namespaces (seq-filter 
#'phpinspect-namespace-p tokens))
-              (phpinspect--index-classes
-               (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p 
tokens))
-               (seq-filter #'phpinspect-class-p tokens))))
-    (functions))
-  ;; TODO: Implement function indexation
-  )
-
-;; (defun phpinspect--get-or-create-index-for-class-file (class-fqn)
-;;   (phpinspect--log "Getting or creating index for %s" class-fqn)
-;;   (phpinspect-get-or-create-cached-project-class
-;;    (phpinspect-project-root)
-;;    class-fqn))
-
-(defun phpinspect-index-file (file-name)
-  (phpinspect--index-tokens (phpinspect-parse-file file-name)))
-
-(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))))
-
-    ;; (let ((existing-index (phpinspect-get-cached-project-class
-    ;;                        project-root
-    ;;                        class-fqn)))
-    ;;   (or
-    ;;    existing-index
-    ;;    (progn
-    ;;      (let* ((class-file (phpinspect-class-filepath class-fqn))
-    ;;             (visited-buffer (when class-file (find-buffer-visiting 
class-file)))
-    ;;             (new-index))
-
-    ;;        (phpinspect--log "No existing index for FQN: %s" class-fqn)
-    ;;        (phpinspect--log "filepath: %s" class-file)
-    ;;        (when class-file
-    ;;          (if visited-buffer
-    ;;              (setq new-index (with-current-buffer visited-buffer
-    ;;                                (phpinspect--index-current-buffer)))
-    ;;            (setq new-index (phpinspect-index-file class-file)))
-    ;;          (dolist (class (alist-get 'classes new-index))
-    ;;            (when class
-    ;;              (phpinspect-cache-project-class
-    ;;               project-root
-    ;;               (cdr class))))
-    ;;          (alist-get class-fqn (alist-get 'classes new-index)
-    ;;                     nil
-    ;;                     nil
-    ;;                     #'phpinspect--type=))))))))
-
-
-(defun phpinspect--index-current-buffer ()
-  (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
-
-(defun phpinspect-index-current-buffer ()
-  "Index a PHP file for classes and the methods they have"
-  (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
-
-;; (defun phpinspect--get-variables-for-class (buffer-classes class &optional 
static)
-;;   (let ((class-index (or (assoc-default class buffer-classes 
#'phpinspect--type=)
-;;                          (phpinspect--get-or-create-index-for-class-file 
class))))
-;;     (when class-index
-;;       (if static
-;;           (append (alist-get 'static-variables class-index)
-;;                   (alist-get 'constants class-index))
-;;         (alist-get 'variables class-index)))))
 
 (defun phpinspect--get-variables-for-class (buffer-classes class-name 
&optional static)
   (let ((class (phpinspect-get-or-create-cached-project-class
@@ -2261,12 +876,6 @@ level of a token. Nested variables are ignored."
 (defsubst phpinspect-not-variable-p (token)
   (not (phpinspect-variable-p token)))
 
-(defun phpinspect--get-bare-class-name-from-fqn (fqn)
-  (car (last (split-string fqn "\\\\"))))
-
-(cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
-  (phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
-
 (cl-defmethod phpinspect--make-completion
   ((completion-candidate phpinspect--variable))
   (phpinspect--construct-completion
@@ -2488,242 +1097,6 @@ static variables and static methods."
     (phpinspect--completion-meta
               (phpinspect--completion-list-get-metadata 
phpinspect--last-completion-list arg)))))
 
-
-(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
-  (active-projects nil
-                   :type alist
-                   :documentation
-                   "An `alist` that contains the root directory
-                   paths of all currently active phpinspect
-                   projects")
-  (projects (make-hash-table :test 'equal :size 10)
-            :type hash-table
-            :documentation
-            "A `hash-table` with the root directories of projects
-as keys and project caches as values."))
-
-(cl-defstruct (phpinspect--project (:constructor 
phpinspect--make-project-cache))
-  (class-index (make-hash-table :test 'eq :size 100 :rehash-size 40)
-               :type hash-table
-               :documentation
-               "A `hash-table` that contains all of the currently
-indexed classes in the project"))
-
-(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.")
-  (variables nil
-             :type list
-             :documentation
-             "Variables that belong to this class.")
-  (extended-classes (make-hash-table :test 'eq)
-                    :type hash-table
-                    :documentation
-                    "All extended/implemented classes.")
-  (subscriptions nil
-                 :type list
-                 :documentation
-                 "A list of subscription functions that should be
-                 called whenever anything about this class is
-                 updated"))
-
-(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
-  (dolist (sub (phpinspect--class-subscriptions class))
-    (funcall sub class)))
-
-(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
-                                           (index (head 
phpinspect--indexed-class)))
-  (setf (phpinspect--class-index class) index)
-  (dolist (method (alist-get 'methods index))
-    (phpinspect--class-update-method class method))
-
-  (dolist (method (alist-get 'static-methods index))
-    (phpinspect--class-update-static-method class method))
-
-  (setf (phpinspect--class-variables class)
-        (alist-get 'variables index))
-
-  (setf (phpinspect--class-extended-classes class)
-        (seq-filter
-         #'phpinspect--class-p
-         (mapcar
-          (lambda (class-name)
-            (phpinspect--project-get-class-create (phpinspect--class-project 
class)
-                                           class-name))
-          `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))))
-
-  (dolist (extended (phpinspect--class-extended-classes class))
-    (phpinspect--class-incorporate class extended)
-    (phpinspect--class-subscribe class extended))
-
-  (phpinspect--class-trigger-update class))
-
-(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) 
method-name)
-  (gethash method-name (phpinspect--class-methods class)))
-
-(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) 
method-name)
-  (gethash method-name (phpinspect--class-static-methods class)))
-
-(cl-defmethod phpinspect--class-set-method ((class phpinspect--class)
-                                            (method phpinspect--function))
-  (phpinspect--log "Adding method by name %s to class"
-                   (phpinspect--function-name method))
-  (puthash (phpinspect--function-name-symbol method)
-           method
-           (phpinspect--class-methods class)))
-
-(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
-                                                   (method 
phpinspect--function))
-  (puthash (phpinspect--function-name-symbol method)
-           method
-           (phpinspect--class-static-methods class)))
-
-(cl-defmethod phpinspect--class-get-method-return-type
-  ((class phpinspect--class) (method-name symbol))
-  (let ((method (phpinspect--class-get-method class method-name)))
-    (when method
-      (phpinspect--function-return-type method))))
-
-(cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class))
-  (let ((methods))
-    (maphash (lambda (key method)
-               (push method methods))
-             (phpinspect--class-methods class))
-    methods))
-
-(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
-                                                      (method 
phpinspect--function))
-  (let ((existing (gethash (phpinspect--function-name-symbol method)
-                           (phpinspect--class-static-methods class))))
-    (if existing
-        (progn
-          (unless (eq (phpinspect--function-return-type method)
-                    phpinspect--null-type)
-          (setf (phpinspect--function-return-type existing)
-                (phpinspect--function-return-type method))
-          (setf (phpinspect--function-arguments existing)
-                (phpinspect--function-arguments method))))
-      (phpinspect--class-set-static-method class method))))
-
-(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
-                                               (method phpinspect--function))
-  (let ((existing (gethash (phpinspect--function-name-symbol method)
-                           (phpinspect--class-methods class))))
-    (if existing
-        (progn
-          (unless (eq (phpinspect--function-return-type method)
-                    phpinspect--null-type)
-          (setf (phpinspect--function-return-type existing)
-                (phpinspect--function-return-type method))
-          (setf (phpinspect--function-arguments existing)
-                (phpinspect--function-arguments method))))
-      (phpinspect--class-set-method class method))))
-
-(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
-                                             (other-class phpinspect--class))
-  (maphash (lambda (k method)
-             (phpinspect--class-update-method class method))
-           (phpinspect--class-methods other-class)))
-
-(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
-                                           (subscription-class 
phpinspect--class))
-  (let ((update-function
-         (lambda (new-class)
-           (phpinspect--class-incorporate class new-class)
-           (phpinspect--class-trigger-update class))))
-    (push update-function (phpinspect--class-subscriptions 
subscription-class))))
-1
-(cl-defgeneric phpinspect--cache-getproject
-    ((cache phpinspect--cache) (project-name string))
-  "Get project by PROJECT-NAME that is located in CACHE.")
-
-(cl-defmethod phpinspect--cache-getproject
-  ((cache phpinspect--cache) (project-root string))
-  (gethash project-root (phpinspect--cache-projects cache)))
-
-(cl-defgeneric phpinspect--cache-get-project-create
-    ((cache phpinspect--cache) (project-root string))
-  "Get a project that is located in PROJECT-ROOT from CACHE.
-If no such project exists in the cache yet, it is created and
-then returned.")
-
-(cl-defmethod phpinspect--cache-get-project-create
-  ((cache phpinspect--cache) (project-root string))
-  (or (phpinspect--cache-getproject cache project-root)
-      (puthash project-root
-               (phpinspect--make-project-cache)
-               (phpinspect--cache-projects cache))))
-
-(cl-defgeneric phpinspect--project-add-class
-    ((project phpinspect--project) (class (head phpinspect--indexed-class)))
-  "Add an indexed CLASS to PROJECT.")
-
-(cl-defmethod phpinspect--project-add-class
-  ((project phpinspect--project) (indexed-class (head 
phpinspect--indexed-class)))
-  (let* ((class-name (phpinspect--type-name-symbol
-                      (alist-get 'class-name (cdr indexed-class))))
-         (existing-class (gethash class-name
-                                  (phpinspect--project-class-index project))))
-    (if existing-class
-        (phpinspect--class-set-index existing-class indexed-class)
-      (let ((new-class (phpinspect--make-class-generated :project project)))
-        (phpinspect--class-set-index new-class indexed-class)
-        (puthash class-name new-class (phpinspect--project-class-index 
project))))))
-
-(cl-defgeneric phpinspect--project-get-class
-    ((project phpinspect--project) (class-fqn phpinspect--type))
-  "Get indexed class by name of CLASS-FQN stored in PROJECT.")
-
-(cl-defmethod phpinspect--project-get-class-create
-  ((project phpinspect--project) (class-fqn phpinspect--type))
-  (let ((class (phpinspect--project-get-class project class-fqn)))
-    (unless class
-      (setq class (phpinspect--make-class-generated :project project))
-      (puthash (phpinspect--type-name-symbol class-fqn)
-               class
-               (phpinspect--project-class-index project))
-
-      (let* ((class-file (phpinspect-class-filepath class-fqn))
-             (visited-buffer (when class-file (find-buffer-visiting 
class-file)))
-             (new-index)
-             (class-index))
-
-        (phpinspect--log "No existing index for FQN: %s" class-fqn)
-        (phpinspect--log "filepath: %s" class-file)
-        (when class-file
-          (if visited-buffer
-              (setq new-index (with-current-buffer visited-buffer
-                                (phpinspect--index-current-buffer)))
-            (setq new-index (phpinspect-index-file class-file)))
-          (setq class-index
-                (alist-get class-fqn (alist-get 'classes new-index)
-                           nil
-                           nil
-                           #'phpinspect--type=))
-          (when class-index
-            (phpinspect--class-set-index class class-index)))))
-    class))
-
-(cl-defmethod phpinspect--project-get-class
-  ((project phpinspect--project) (class-fqn phpinspect--type))
-  (gethash (phpinspect--type-name-symbol class-fqn)
-           (phpinspect--project-class-index project)))
-
 (defun phpinspect--get-or-create-global-cache ()
   "Get `phpinspect-cache'.
 If its value is nil, it is created and then returned."



reply via email to

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