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

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

[elpa] externals/phpinspect dbf0ec0390 051/126: Transition from index sc


From: ELPA Syncer
Subject: [elpa] externals/phpinspect dbf0ec0390 051/126: Transition from index script to autoloader
Date: Sat, 12 Aug 2023 00:58:41 -0400 (EDT)

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

    Transition from index script to autoloader
---
 README.md               |   2 +-
 phpinspect-autoload.el  |  56 +++-
 phpinspect-buffer.el    |  81 +++++
 phpinspect-cache.el     |  25 +-
 phpinspect-class.el     |   7 +-
 phpinspect-imports.el   | 155 +++++++++
 phpinspect-index.bash   | 853 ------------------------------------------------
 phpinspect-index.el     | 131 ++++++--
 phpinspect-parser.el    |  17 +-
 phpinspect-project.el   |  58 +++-
 phpinspect-type.el      |   2 +-
 phpinspect-worker.el    |  73 +++--
 phpinspect.el           | 172 ++++------
 test/phpinspect-test.el |   6 +-
 test/test-buffer.el     |  68 ++++
 test/test-index.el      | 103 ++++++
 test/test-project.el    |  46 +++
 17 files changed, 782 insertions(+), 1073 deletions(-)

diff --git a/README.md b/README.md
index cb50b05541..b5013d5a5b 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ WIP. More documentation is in the making.
   (setq-local company-backends '(phpinspect-company-backend))
 
   ;; Shortcut to add use statements for classes you use.
-  (define-key php-mode-map (kbd "C-c u") 'phpinspect-fix-uses-interactive)
+  (define-key php-mode-map (kbd "C-c u") 'phpinspect-fix-imports)
 
   ;; Shortcuts to quickly search/open files of PHP classes.
   (global-set-key (kbd "C-c a") 'phpinspect-find-class-file)
diff --git a/phpinspect-autoload.el b/phpinspect-autoload.el
index 51efa36f95..fb921f3713 100644
--- a/phpinspect-autoload.el
+++ b/phpinspect-autoload.el
@@ -66,7 +66,13 @@
   (types (make-hash-table :test 'eq :size 10000 :rehash-size 10000)
          :type hash-table
          :documentation
-         "The external types that can be autoloaded through this autoloader."))
+         "The external types that can be autoloaded through this autoloader.")
+  (type-name-fqn-bags (make-hash-table :test 'eq :size 3000 :rehash-size 3000)
+                      :type hash-table
+                      :documentation
+                      "Hash table that contains lists of fully
+qualified names congruent with a bareword type name. Keyed by
+bareword typenames."))
 
 (defun phpinspect-make-autoload-definition-closure (project-root fs typehash)
   "Create a closure that can be used to `maphash' the autoload section of a 
composer-json."
@@ -108,13 +114,22 @@
   (let* ((project-root (phpinspect--project-root 
(phpinspect-autoloader-project autoloader)))
          (fs (phpinspect--project-fs (phpinspect-autoloader-project 
autoloader)))
          (vendor-dir (concat project-root "/vendor"))
-         (composer-json (phpinspect--read-json-file
-                         fs
-                         (concat project-root "/composer.json")))
-         (project-autoload (gethash "autoload" composer-json))
+         (composer-json-path (concat project-root "/composer.json"))
+         (composer-json)
+         (project-autoload )
+         (type-name-fqn-bags (make-hash-table :test 'eq :size 3000 
:rehash-size 3000))
          (own-types (make-hash-table :test 'eq :size 10000 :rehash-size 10000))
          (types (make-hash-table :test 'eq :size 10000 :rehash-size 10000)))
 
+    (when (phpinspect-fs-file-exists-p fs composer-json-path)
+      (setq composer-json (phpinspect--read-json-file fs composer-json-path))
+
+      (if (hash-table-p composer-json)
+          (setq project-autoload (gethash "autoload" composer-json))
+        (phpinspect--log "Error: Parsing %s did not return a hashmap."
+                         composer-json-path)))
+
+
     (when project-autoload
       (maphash (phpinspect-make-autoload-definition-closure project-root fs 
own-types)
                project-autoload)
@@ -137,8 +152,18 @@
                             dependency-dir fs types)
                            dependency-autoload))))))))
 
-      (setf (phpinspect-autoloader-own-types autoloader) own-types)
-      (setf (phpinspect-autoloader-types autoloader) types)))
+    (maphash (lambda (type-fqn _)
+               (let* ((type-name (phpinspect-intern-name
+                                  (car (last (split-string (symbol-name 
type-fqn) "\\\\")))))
+                      (bag (gethash type-name type-name-fqn-bags)))
+                 (push type-fqn bag)
+                 (puthash type-name bag type-name-fqn-bags)))
+             types)
+
+    (setf (phpinspect-autoloader-own-types autoloader) own-types)
+    (setf (phpinspect-autoloader-types autoloader) types)
+    (setf (phpinspect-autoloader-type-name-fqn-bags autoloader)
+          type-name-fqn-bags)))
 
 (cl-defmethod phpinspect-autoloader-resolve ((autoloader phpinspect-autoloader)
                                             typename-symbol)
@@ -152,13 +177,16 @@
 
 (defsubst phpinspect-filename-to-typename (dir filename &optional prefix)
   (phpinspect-intern-name
-   (concat "\\"
-           (or prefix "")
-           (replace-regexp-in-string
-            "/" "\\\\"
-            (string-remove-suffix
-             ".php"
-             (string-remove-prefix dir filename))))))
+   (replace-regexp-in-string
+    "[\\\\]+"
+    "\\\\"
+    (concat "\\"
+            (or prefix "")
+            (replace-regexp-in-string
+             "/" "\\\\"
+             (string-remove-suffix
+              ".php"
+              (string-remove-prefix dir filename)))))))
 
 (cl-defmethod phpinspect-al-strategy-fill-typehash ((strategy phpinspect-psr0)
                                                     fs
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
new file mode 100644
index 0000000000..21e9d4e422
--- /dev/null
+++ b/phpinspect-buffer.el
@@ -0,0 +1,81 @@
+;;; phpinspect-buffer.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-local phpinspect-current-buffer nil
+  "An instance of `phpinspect-buffer' local to the active
+buffer. This variable is only set for buffers where
+`phpinspect-mode' is active. Also see `phpinspect-buffer'.")
+
+(defsubst phpinspect-make-region (start end)
+  (list start end))
+
+(defalias 'phpinspect-region-start #'car)
+(defalias 'phpinspect-region-end #'cadr)
+
+(cl-defstruct (phpinspect-buffer (:constructor phpinspect-make-buffer))
+  "An object containing phpinspect related metadata linked to an
+emacs buffer."
+  (buffer nil
+          :type buffer
+          :documentation "The underlying emacs buffer")
+  (location-map (make-hash-table :test 'eq :size 400 :rehash-size 400)
+                :type hash-table
+                :documentation
+                "A map that lets us look up the character
+positions of a token within this buffer.")
+  (tree nil
+        :type list
+        :documentation
+        "An instance of a token tree as returned by
+`phpinspect--index-tokens'. Meant to be eventually consistent
+with the contents of the buffer."))
+
+(cl-defmethod phpinspect-buffer-parse ((buffer phpinspect-buffer))
+  "Parse the PHP code in the the emacs buffer that this object is
+linked with."
+  (with-current-buffer (phpinspect-buffer-buffer buffer)
+    (setf (phpinspect-buffer-location-map buffer)
+          (make-hash-table :test 'eq
+                           :size 400
+                           :rehash-size 400))
+
+    (let ((tree (phpinspect-parse-current-buffer)))
+      (setf (phpinspect-buffer-tree buffer) tree)
+      tree)))
+
+(cl-defmethod phpinspect-buffer-token-location ((buffer phpinspect-buffer) 
token)
+  (gethash token (phpinspect-buffer-location-map buffer)))
+
+(cl-defmethod phpinspect-buffer-tokens-enclosing-point ((buffer 
phpinspect-buffer) point)
+  (let ((tokens))
+    (maphash
+     (lambda (token region)
+       (when (and (<= (phpinspect-region-start region) point)
+                  (>= (phpinspect-region-end region) point))
+         (push token tokens)))
+     (phpinspect-buffer-location-map buffer))
+    tokens))
+
+(provide 'phpinspect-buffer)
diff --git a/phpinspect-cache.el b/phpinspect-cache.el
index c23535866c..a401b50d41 100644
--- a/phpinspect-cache.el
+++ b/phpinspect-cache.el
@@ -24,14 +24,9 @@
 ;;; Code:
 
 (require 'phpinspect-project)
+(require 'phpinspect-autoload)
 
 (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
@@ -54,12 +49,18 @@ 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
-                :root project-root
-                :worker (phpinspect-make-dynamic-worker))
-               (phpinspect--cache-projects cache))))
+  (let ((project (phpinspect--cache-getproject cache project-root)))
+    (unless project
+      (setq project (puthash project-root
+                             (phpinspect--make-project
+                              :fs (phpinspect-make-fs)
+                              :root project-root
+                              :worker (phpinspect-make-dynamic-worker))
+                             (phpinspect--cache-projects cache)))
+      (let ((autoload (phpinspect-make-autoloader :project project)))
+        (setf (phpinspect--project-autoload project) autoload)
+        (phpinspect-autoloader-refresh autoload)))
+    project))
 
 (provide 'phpinspect-cache)
 ;;; phpinspect.el ends here
diff --git a/phpinspect-class.el b/phpinspect-class.el
index c524ad128b..199b1fb015 100644
--- a/phpinspect-class.el
+++ b/phpinspect-class.el
@@ -61,12 +61,7 @@
                  :type bool
                  :documentation
                  "A boolean indicating whether or not this class
-                 has been indexed yet.")
-  (index-queued nil
-                :type bool
-                :documentation
-                "A boolean indicating whether the class type has
-                been queued for indexation"))
+                 has been indexed yet."))
 
 (cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
   (dolist (sub (phpinspect--class-subscriptions class))
diff --git a/phpinspect-imports.el b/phpinspect-imports.el
new file mode 100644
index 0000000000..c6c7d0957d
--- /dev/null
+++ b/phpinspect-imports.el
@@ -0,0 +1,155 @@
+; phpinspect-imports.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:
+
+;; See docstrings for documentation, starting with `phpinspect-mode'.
+
+;;; Code:
+
+(require 'phpinspect-parser)
+(require 'phpinspect-index)
+(require 'phpinspect-autoload)
+(require 'phpinspect-buffer)
+
+(defun phpinspect-insert-at-point (point data)
+  (save-excursion
+    (goto-char point)
+    (insert data)))
+
+(defun phpinspect-add-use (fqn buffer &optional namespace-token)
+  "Add use statement for FQN to BUFFER.
+
+If NAMESPACE-TOKEN is non-nil, it is assumed to be a token that
+was parsed from BUFFER and its location will be used to find a
+buffer position to insert the use statement at."
+  (when (string-match "^\\\\" fqn)
+    (setq fqn (string-trim-left fqn "\\\\")))
+
+  (if namespace-token
+      (let* ((region (gethash
+                      namespace-token (phpinspect-buffer-location-map buffer)))
+             (existing-use (seq-find #'phpinspect-use-p
+                                     (phpinspect-namespace-body 
namespace-token)))
+             (namespace-block (phpinspect-namespace-block namespace-token)))
+        (if existing-use
+            (phpinspect-insert-at-point
+             (phpinspect-region-start
+              (phpinspect-buffer-token-location buffer existing-use))
+             (format "use %s;%c" fqn ?\n))
+          (if namespace-block
+              (phpinspect-insert-at-point
+               (+ 1 (phpinspect-region-start
+                     (phpinspect-buffer-token-location buffer 
namespace-block)))
+               (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
+            (phpinspect-insert-at-point
+             (phpinspect-region-end
+                   (phpinspect-buffer-token-location
+                    buffer (seq-find #'phpinspect-terminator-p 
namespace-token)))
+             (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)))))
+    ;; else
+    (let ((existing-use (seq-find #'phpinspect-use-p
+                                  (phpinspect-buffer-tree buffer))))
+      (if existing-use
+          (phpinspect-insert-at-point
+           (phpinspect-region-start
+            (phpinspect-buffer-token-location buffer existing-use))
+           (format "use %s;%c" fqn ?\n))
+        (let ((first-token (cadr (phpinspect-buffer-tree buffer))))
+          (if (and (phpinspect-word-p first-token)
+                   (string= "declare" (cadr first-token)))
+              (phpinspect-insert-at-point
+               (phpinspect-region-end
+                (phpinspect-buffer-token-location
+                 buffer (seq-find #'phpinspect-terminator-p 
(phpinspect-buffer-tree buffer))))
+                (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
+            (phpinspect-insert-at-point
+             (phpinspect-region-start
+              (phpinspect-buffer-token-location buffer first-token))
+             (format "%c%cuse %s;%c%c" ?\n ?\n fqn ?\n ?\n))))))))
+
+(defun phpinspect-add-use-interactive (typename buffer project &optional 
namespace-token)
+  (let* ((autoloader (phpinspect--project-autoload project))
+         (fqn-bags (phpinspect-autoloader-type-name-fqn-bags autoloader)))
+
+    (let ((fqns (gethash typename fqn-bags)))
+      (cond ((= 1 (length fqns))
+             (phpinspect-add-use (symbol-name (car fqns)) buffer 
namespace-token))
+            ((> (length fqns) 1)
+             (phpinspect-add-use (symbol-name (completing-read "Class: " fqns))
+                                 buffer namespace-token))
+            (t (message "No import found for type %s" typename))))))
+
+(defun phpinspect-namespace-part-of-typename (typename)
+  (string-trim-right typename "\\\\?[^\\\\]+"))
+
+(defalias 'phpinspect-fix-uses-interactive #'phpinspect-fix-imports
+  "Alias for backwards compatibility")
+
+(defun phpinspect-fix-imports ()
+  "Find types that are used in the current buffer and make sure
+that there are import (\"use\") statements for them."
+  (interactive)
+  (if phpinspect-current-buffer
+      (let* ((tree (phpinspect-buffer-parse phpinspect-current-buffer))
+             (location-map (phpinspect-buffer-location-map 
phpinspect-current-buffer))
+             (index (phpinspect--index-tokens
+                     tree nil (lambda (token) (gethash token location-map))))
+             (classes (alist-get 'classes index))
+             (imports (alist-get 'imports index))
+             (project (phpinspect--cache-get-project-create
+                       (phpinspect--get-or-create-global-cache)
+                       (phpinspect-project-root))))
+        (dolist (class classes)
+          (let* ((class-imports (alist-get 'imports class))
+                 (used-types (alist-get 'used-types class))
+                 (region (alist-get 'location class)))
+            (dolist (type used-types)
+              (let ((namespace
+                     (seq-find #'phpinspect-namespace-p
+                               (phpinspect-buffer-tokens-enclosing-point
+                                phpinspect-current-buffer 
(phpinspect-region-start region)))))
+                ;; Add use statements for types that aren't imported.
+
+                (unless (or (or (alist-get type class-imports)
+                                (alist-get type imports))
+                            (gethash (phpinspect-intern-name
+                                      (concat 
(phpinspect-namespace-part-of-typename
+                                               (phpinspect--type-name 
(alist-get 'class-name class)))
+                                              "\\"
+                                              (symbol-name type)))
+                                     (phpinspect-autoloader-types
+                                      (phpinspect--project-autoload project))))
+                  (phpinspect-add-use-interactive
+                   type phpinspect-current-buffer project namespace)
+                  ;; Buffer has been modified by adding type, update tree +
+                  ;; location map. This is not optimal but will have to do 
until
+                  ;; partial parsing is implemented.
+                  ;;
+                  ;; Note: this basically implements a bug where the locations
+                  ;; of classes are no longer congruent with their location in
+                  ;; the buffer's code. In files that contain multiple 
namespace
+                  ;; blocks this could cause problems as a namespace may grow 
by
+                  ;; added import statements and start envelopping the classes
+                  ;; below it.
+                  (phpinspect-buffer-parse phpinspect-current-buffer)))))))))
+
+(provide 'phpinspect-imports)
diff --git a/phpinspect-index.bash b/phpinspect-index.bash
deleted file mode 100755
index 357444f611..0000000000
--- a/phpinspect-index.bash
+++ /dev/null
@@ -1,853 +0,0 @@
-#!/bin/bash
-##
-# phpinspect-index.bash - Resolve namespaces and fix missing use statements in 
your PHP
-# scripts.
-###
-# This script is derived from phpns, an earlier project that had a much wider 
scope than
-# just index files for phpinspect.el. Much of the code and command line 
argument options
-# can be removed.
-# TODO: remove whatever functionality is not required for phpinspect.el
-
-# shellcheck disable=SC2155
-declare CACHE_DIR=./.cache/phpinspect
-declare INFO=1
-
-# Cache locations
-declare CLASSES="$CACHE_DIR/classes"
-declare NAMESPACES="$CACHE_DIR/namespaces"
-declare USES="$CACHE_DIR/uses"
-declare USES_OWN="$CACHE_DIR/uses_own"
-declare USES_LOOKUP="$CACHE_DIR/uses_lookup"
-declare USES_LOOKUP_OWN="$CACHE_DIR/uses_lookup_own"
-declare FILE_PATHS="$CACHE_DIR/file_paths"
-declare NAMESPACE_FILE_PATHS="$CACHE_DIR/namespace_file_paths"
-declare INDEXED="$CACHE_DIR/indexed"
-
-[[ $DEBUG -eq 2 ]] && set -x
-shopt -s extglob
-shopt -so pipefail
-
-read -rd '' USAGE <<'EOF'
-    phpns - Resolve namespaces and fix missing use statements in your PHP 
scripts.
-
-    USAGE:
-        phpns COMMAND [ ARGUMENTS ] [ OPTIONS ]
-
-    COMMANDS:
-        i,   index                             Index the PHP project in the 
current directory
-        fu,  find-use             CLASS_NAME   Echo the FQN of a class
-        fxu, fix-uses             FILE         Add needed use statements to 
FILE
-        cns, classes-in-namespace NAMESPACE    Echo the classes that reside in 
NAMESPACE
-        fp,  filepath             FQN          Echo the filepath of the class 
by the name of FQN.
-
-    TO BE IMPLEMENTED:
-        rmuu, remove-unneeded-uses FILE: Remove all use statements for classes 
that are not being used.
-
-    OPTIONS FOR ALL COMMANDS:
-        -s --silent     Don't print info.
-
-    UNIQUE OPTIONS PER COMMAND:
-        index:
-            -d, --diff                Show differences between the files in 
the index and the files in the project directory.
-            -N, --new                 Only index new files
-        find-use:
-            -j, --json                Provide possible use FQN's as a json 
array.
-            -p, --prefer-own          If there are matches inside the "src" 
dir, only use those.
-            -a, --auto-pick           Use first encountered match, don't 
provide a choice.
-            -b. --bare                Print FQN's without any additives.
-        fix-uses:
-            -j, --json                Provide possible use FQN's per class as 
a json object with the class names as keys.
-            -p, --prefer-own          If there are matches inside the "src" 
dir, only use those.
-            -a, --auto-pick           Use first encountered match, for every 
class, don't provide a choice.
-            -o, --stdout              Print to stdout in stead of printing to 
the selected file.
-        filepath: -
-
-EOF
-
-execute() {
-    declare command="$1" INFO="$INFO"
-    declare -a CONFIG=()
-    shift
-
-    if [[ $command == @(-h|--help|help) ]]; then
-        echo "$USAGE" >&2
-        exit 0
-    fi
-
-    if ! [[ -f ./composer.json ]] && ! [[ -d ./.git ]]; then
-        echo "No composer.json or .git file found, not in root of poject, 
exiting." >&2
-        exit 1
-    fi
-
-    case "$command" in
-        i | index)
-            handleArguments index "$@" || return $?
-
-            # The arguments to grep need to be dynamic here, because the diff 
option
-            # requires different arguments to be passed to grep.
-            declare -a grep_args=(
-                -H
-                
'^\(class\|abstract[[:blank:]]\+class\|\(final[[:blank:]]\+\|/\*[[:blank:]]*final[[:blank:]]*\*/[[:blank:]]*\)class\|namespace\|interface\|trait\)[[:blank:]]\+[A-Za-z]\+'
-                --exclude-dir={.cache,var,bin}
-                --binary-files=without-match
-            )
-
-            # Only index new files
-            if [[ ${CONFIG[$INDEX_NEW]} == '--new' ]]; then
-                declare -a new_files=() deleted_files=()
-
-                # Extract new files from diff.
-                while IFS=':' read -ra diff_file; do
-                    if [[ ${diff_file[0]} == '-' ]]; then
-                        deleted_files=("${diff_file[1]}" "${deleted_files[@]}")
-                    elif [[ ${diff_file[0]} == '+' ]]; then
-                        new_files=("${diff_file[1]}" "${new_files[@]}")
-                    fi
-                done < <(diffIndex)
-
-                # Inform the user if non-existent files were found. Right now 
the only
-                # way to fix this is to reindex entirely.
-                if [[ ${#deleted_files[@]} -gt 0 ]]; then
-                    info "There are ${#deleted_files[@]} non-existent files in 
your index. Consider reindexing to prevent incorrect results."
-                    info 'Some of these none existent files are:'
-                    for i in {0..19}; do
-                        [[ $i -ge ${#deleted_files[@]} ]] && break
-                        infof '    - "%s"\n' "${deleted_files[$i]}"
-                    done
-                fi
-
-                if [[ ${#new_files[@]} -eq 0 ]]; then
-                    info 'No new files were found.'
-                    return 0
-                else
-                    info "${#new_files[@]} new files found to index."
-                fi
-
-                # To exclusively index new files, add the filenames to the 
arguments array
-                grep_args=("${grep_args[@]}" "${new_files[@]}")
-            elif [[ ${CONFIG[$INDEX_DIFF]} == '--diff' ]]; then
-                diffIndex
-                return $?
-            else
-                grep_args=("${grep_args[@]}" '-r' '--include=*.php')
-            fi
-
-            # Index matching files
-            grep -m 2 "${grep_args[@]}" | grep -v '^vendor/bin' | fillIndex
-
-            # Add non-matching files to the file with indexed files.
-            # This is necessary to be able to diff the index.
-            grep -L "${grep_args[@]}" | grep -v '^vendor/bin' >> "$INDEXED"
-            ;;
-        fu | find-use)
-            checkCache
-            handleArguments find-use "$@" || return $?
-            declare use_path='' class_name="${CONFIG[$CLASS_NAME]}"
-            if [[ "$class_name" == @(array|string|float|int|void|mixed) ]]; 
then
-                infof 'Type "%s" is not a class, but a primitive type.\n' 
"$class_name"
-                return 1
-            fi
-
-            findUsePathForClass "$class_name"
-            ;;
-        fxu | fix-uses)
-            checkCache
-            handleArguments fix-uses "$@" || return $?
-
-            declare file="${CONFIG[$FILE]}"
-
-            if ! [[ -f $file ]]; then
-                infof 'File "%s" does not exist or is not a regular file.\n' 
"$file"
-
-                return 1
-            elif [[ ${CONFIG[$STDOUT]} == '--stdout' ]]; then
-                fixMissingUseStatements "$file"
-            else
-                # shellcheck disable=SC2005
-                echo "$(fixMissingUseStatements "$file")" > "$file"
-            fi
-            ;;
-        ns | namespace)
-            checkCache
-            declare file="$1"
-
-            # Try the index, if that doesn't work, attempt to extract the 
namespace from the file itself.
-            if ! grep "(?<=$file:).*" "$NAMESPACE_FILE_PATHS"; then
-                grep -Po '(?<=^namespace[[:blank:]])[A-Za-z_\\]+' "$file"
-            fi
-            ;;
-        cns | classes-in-namespace)
-            handleArguments classes-in-namespace "$@" || return $?
-            checkCache
-
-            declare namespace="${CONFIG[$NAMESPACE]}\\"
-            debug "Checking for namespace $namespace"
-
-            awk -F ':' "/:${namespace//\\/\\\\}"'[^\\]+$/{ print $1; }' 
"$USES_LOOKUP"
-            ;;
-        fp | filepath)
-            handleArguments filepath "$@" || return $?
-            checkCache
-
-            grep -Po "^.*(?=:${CONFIG[$CLASS_PATH]//\\/\\\\}$)" "$FILE_PATHS"
-            ;;
-        *)
-            printf 'Command "%s" is not a valid subcommand.\n' "$command" >&2
-            exit 1
-            ;;
-    esac
-}
-
-# shellcheck disable=SC2034
-fixMissingUseStatements() {
-    declare check_uses='false' check_needs='false' file="$1" namespace="$2"
-    declare -A uses=() needs=() namespace=()
-    declare -a classes=()
-
-    classes=($(execute cns "$(execute ns "$file")"))
-    for class in "${classes[@]}"; do
-        namespace["$class"]='in_namespace'
-    done
-
-    findUsesAndNeeds < "$file"
-    addUseStatements "${!needs[@]}" < "$file"
-}
-
-findUsePathForClass() {
-    declare class="$1"
-    if [[ ${CONFIG[$PREFER_OWN]} == '--prefer-own' ]]; then
-        declare -a possibilities=($(grep -Po "(?<=^${CONFIG[$CLASS_NAME]}:).*" 
"$USES_LOOKUP_OWN"))
-    else
-        declare -a possibilities=($(grep -Po "(?<=^${CONFIG[$CLASS_NAME]}:).*" 
"$USES_LOOKUP"))
-    fi
-
-    if [[ ${#possibilities[@]} -eq 1 ]]; then
-        use_path="${possibilities[0]}"
-        debugf 'Single use path "%s" found' "${possibilities[0]}"
-
-        # Provide an escaped string for json output if requested.
-        [[ ${CONFIG[$JSON]} == '--json' ]] && printf -v use_path '"%s"' 
"${use_path//\\/\\\\}"
-    elif [[ ${#possibilities[@]} -eq 0 ]]; then
-        _handle_no_use
-        return $?
-    else
-        _handle_multiple_uses
-    fi
-
-    infof 'Found use statement for "%s"\n' "$use_path" >&2
-    if [[ ${CONFIG[$JSON]} == '--json' ]]; then
-        echo '['
-        echo "$use_path"
-        printf ']'
-    elif [[ ${CONFIG[$BARE]} ]]; then
-        echo "$use_path"
-    else
-        echo "use $use_path;"
-    fi
-}
-
-_handle_no_use() {
-    declare tried_index_new="$1"
-
-    if [[ $tried_index_new != true ]]; then
-        execute index --silent --new
-        execute fu "${CONFIG[@]}"
-        return $?
-    elif [[ ${CONFIG[$PREFER_OWN]} == '--prefer-own' ]]; then
-        CONFIG[$PREFER_OWN]=
-        execute fu "${CONFIG[@]}"
-        return $?
-    else
-        infof 'No match found for class "%s"\n' "$class_name" >&2
-        [[ ${CONFIG[$JSON]} == '--json' ]] && printf '[]'
-    fi
-    return 1
-}
-
-_handle_multiple_uses() {
-    if [[ ${CONFIG[$AUTO_PICK]} == '--auto-pick' ]]; then
-        use_path="${possibilities[0]}"
-
-        return 0
-    elif [[ ${CONFIG[$BARE]} == '--bare' ]]; then
-        use_path="$(printf '%s\n' "${possibilities[@]}")"
-
-        return 0
-    elif [[ ${CONFIG[$JSON]} == '--json' ]]; then
-        use_path="$(
-        for i in "${!possibilities[@]}"; do
-            printf '"%s"' "${possibilities[$i]//\\/\\\\}"
-            [[ $i -lt $((${#possibilities[@]}-1)) ]] && printf ','
-            echo
-        done
-        )"
-
-        return 0
-    fi
-
-    infof 'Multiple matches for class "%s", please pick one.\n' "$class_name" 
>&2
-    select match in "${possibilities[@]}"; do
-        use_path="$match"
-        break
-    done < /dev/tty
-}
-
-addUseStatements() {
-    declare -a needs=("$@")
-    declare use_statements=''
-    if [[ ${CONFIG[$JSON]} == '--json' ]]; then
-        declare -i length="$((${#needs[@]}-1))" current=0
-        echo '{'
-        for needed in "${needs[@]}"; do
-            printf '"%s": ' "$needed"
-            execute fu --json "$needed" "${CONFIG[$PREFER_OWN]}" 
"${CONFIG[$AUTO_PICK]}"
-            [[ $((current++)) -lt $length ]] && printf ','
-            echo
-        done
-        echo '}'
-
-        return 0
-    fi
-
-    while IFS='' read -r line; do
-        echo "$line"
-
-        if [[ $line == namespace* ]]; then
-            IFS='' read -r line && echo "$line"
-
-            use_statements="$(
-            for needed in "${needs[@]}"; do
-                execute fu "$needed" "${CONFIG[$PREFER_OWN]}" 
"${CONFIG[$AUTO_PICK]}"
-            done | sort
-            )"
-
-            [[ -n $use_statements ]] && echo "$use_statements"
-        fi
-    done
-
-    declare -i added_uses=0
-    added_uses="$(echo -n "$use_statements" | wc -l)"
-    [[ -n $use_statements ]] && ((added_uses++))
-    info "$added_uses use statements added out of ${#needs[@]} needed types. 
Types that were needed:" >&2
-    infof '           - "%s"\n' "${needs[@]}" >&2
-}
-
-debug() {
-    if [[ $DEBUG -ge 1 ]]; then
-        echo "[DEBUG] => $1" >&2
-    fi
-}
-
-# shellcheck disable=SC2059
-debugf() {
-    if [[ $DEBUG -ge 1 ]]; then
-        declare format_string="$1"
-        shift
-        printf "[DEBUG] => $format_string" "$@" >&2
-    fi
-}
-
-info() {
-    if [[ $INFO -eq 1 ]]; then
-        echo "[INFO] => $1" >&2
-    fi
-}
-
-# shellcheck disable=SC2059
-infof() {
-    if [[ $INFO -eq 1 ]]; then
-        declare format_string="$1"
-        shift
-        printf "[INFO] => $format_string" "$@" >&2
-    fi
-}
-
-##
-# Functions for parameter parsing
-
-# Enum for config
-declare -gri CLASS_NAME=0
-declare -gri PREFER_OWN=1
-declare -gri AUTO_PICK=2
-declare -gri STDOUT=3
-declare -gri JSON=4
-declare -gri BARE=5
-declare -gri WORD=6
-declare -gri EXPAND_CLASSES=7
-declare -gri NO_CLASSES=8
-declare -gri NAMESPACE=9
-declare -gri CLASS_PATH=10
-declare -gri INDEX_DIFF=11
-declare -gri NO_VENDOR=12 # Keep this around as it might be used later on
-declare -gri INDEX_NEW=13
-declare -gri FILE=14
-
-handleArguments() {
-    declare -p CONFIG &>>/dev/null || return 1
-    declare command="$1"
-    shift
-    case "$command" in
-        find-use)
-            _handle_find_use_arguments "$@" || return $?
-            ;;
-        fix-uses)
-            _handle_fix_uses_arguments "$@" || return $?
-            ;;
-        index)
-            _handle_index_arguments "$@" || return $?
-            ;;
-        classes-in-namespace)
-            _handle_classes_in_namespace_arguments "$@" || return $?
-            ;;
-        filepath)
-            _handle_filepath_arguments "$@" || return $?
-            ;;
-        *)
-            printf 'handleArguments (line %s): Unknown command "%s" passed.\n' 
"$(caller)" "$command">&2
-            return 1
-            ;;
-    esac
-}
-
-_handle_filepath_arguments() {
-    declare arg="$1"
-    while shift; do
-        case "$arg" in
-            -s | --silent)
-                INFO=0
-                ;;
-            --*)
-                printf 'Unknown option: "%s"\n' "${arg}" >&2
-                return 1
-                ;;
-            -*)
-                if [[ ${#arg} -gt 2 ]]; then
-
-                    declare -i i=1
-                    while [[ $i -lt ${#arg} ]]; do
-                        _handle_filepath_arguments "-${arg:$i:1}"
-                        ((i++))
-                    done
-                else
-                    printf 'Unknown option: "%s"\n' "${arg}" >&2
-                    return 1
-                fi
-                ;;
-            '')
-                :
-                ;;
-            *)
-                if [[ -n ${CONFIG[$CLASS_PATH]} ]]; then
-                    printf 'Unexpected argument: "%s"\n' "$arg" >&2
-                    return 1
-                fi
-                CONFIG[$CLASS_PATH]="$arg"
-        esac
-        arg="$1"
-    done
-}
-
-_handle_classes_in_namespace_arguments() {
-    declare arg="$1"
-    while shift; do
-        case "$arg" in
-            -s | --silent)
-                INFO=0
-                ;;
-            --*)
-                printf 'Unknown option: "%s"\n' "${arg}" >&2
-                return 1
-                ;;
-            -*)
-                if [[ ${#arg} -gt 2 ]]; then
-                    declare -i i=1
-                    while [[ $i -lt ${#arg} ]]; do
-                        _handle_classes_in_namespace_arguments "-${arg:$i:1}"
-                        ((i++))
-                    done
-                else
-                    printf 'Unknown option: "%s"\n' "${arg}" >&2
-                    return 1
-                fi
-                ;;
-            '')
-                :
-                ;;
-            *)
-                if [[ -n ${CONFIG[$NAMESPACE]} ]]; then
-                    printf 'Unexpected argument: "%s"\n' "$arg" >&2
-                    return 1
-                fi
-                CONFIG[$NAMESPACE]="$arg"
-        esac
-        arg="$1"
-    done
-}
-
-_handle_index_arguments() {
-    declare arg="$1"
-    while shift; do
-        case "$arg" in
-            -s | --silent)
-                INFO=0
-                ;;
-            -d | --diff)
-                CONFIG[$INDEX_DIFF]='--diff'
-                ;;
-            -N | --new)
-                CONFIG[$INDEX_NEW]='--new'
-                ;;
-            --*)
-                printf 'Unknown option: "%s"\n' "${arg}" >&2
-                return 1
-                ;;
-            -*)
-                if [[ ${#arg} -gt 2 ]]; then
-                    declare -i i=1
-                    while [[ $i -lt ${#arg} ]]; do
-                        _handle_index_arguments "-${arg:$i:1}"
-                        ((i++))
-                    done
-                else
-                    printf 'Unknown option: "%s"\n' "${arg}" >&2
-                    return 1
-                fi
-                ;;
-            *)
-                printf 'Unexpected argument: "%s"\n' "$arg" >&2
-                return 1
-                ;;
-        esac
-        arg="$1"
-    done
-}
-
-_handle_fix_uses_arguments() {
-    declare arg="$1"
-    while shift; do
-        case "$arg" in
-            -s | --silent)
-                INFO=0
-                ;;
-            -p | --prefer-own)
-                CONFIG[$PREFER_OWN]='--prefer-own'
-                ;;
-            -a | --auto-pick)
-                CONFIG[$AUTO_PICK]='--auto-pick'
-                ;;
-            -o | --stdout)
-                CONFIG[$STDOUT]='--stdout'
-                INFO=0
-                ;;
-            -j | --json)
-                CONFIG[$STDOUT]='--stdout'
-                CONFIG[$JSON]='--json'
-                ;;
-            --*)
-                printf 'Unknown option: "%s"\n' "${arg}" >&2
-                return 1
-                ;;
-            -*)
-                if [[ ${#arg} -gt 2 ]]; then
-                    declare -i i=1
-                    while [[ $i -lt ${#arg} ]]; do
-                        _handle_fix_uses_arguments "-${arg:$i:1}"
-                        ((i++))
-                    done
-                else
-                    printf 'Unknown option: "%s"\n' "${arg}" >&2
-                    return 1
-                fi
-                ;;
-            '')
-                :
-                ;;
-            *)
-                if [[ -n ${CONFIG[$FILE]} ]]; then
-                    printf 'Unexpected argument: "%s"\n' "$arg" >&2
-                    return 1
-                fi
-                CONFIG[$FILE]="$arg"
-        esac
-        arg="$1"
-    done
-}
-
-# shellcheck disable=SC2034
-_handle_find_use_arguments() {
-    declare arg="$1"
-    while shift; do
-        case "$arg" in
-            -s | --silent)
-                INFO=0
-                ;;
-            -b | --bare)
-                CONFIG[$BARE]='--bare'
-                ;;
-            -p | --prefer-own)
-                CONFIG[$PREFER_OWN]='--prefer-own'
-                ;;
-            -a | --auto-pick)
-                CONFIG[$AUTO_PICK]='--auto-pick'
-                ;;
-            -j | --json) CONFIG[$STDOUT]='--stdout'
-                CONFIG[$JSON]='--json'
-                INFO=0
-                ;;
-            --*)
-                printf 'Unknown option: "%s"\n' "${arg}" >&2
-                return 1
-                ;;
-            -*)
-                if [[ ${#arg} -gt 2 ]]; then
-                    declare -i i=1
-                    while [[ $i -lt ${#arg} ]]; do
-                        _handle_find_use_arguments "-${arg:$i:1}"
-                        ((i++))
-                    done
-                else
-                    printf 'Unknown option: "%s"\n' "${arg}" >&2
-                    return 1
-                fi
-                ;;
-            '')
-                :
-                ;;
-            *)
-                if [[ -n ${CONFIG[$CLASS_NAME]} ]]; then
-                    printf 'Unexpected argument: "%s"\n' "$arg" >&2
-                    return 1
-                fi
-                CONFIG[$CLASS_NAME]="$arg"
-        esac
-        arg="$1"
-    done
-}
-
-##
-# This function outputs the difference between the files that are present in 
the
-# index and the files that are present in the project directory. The output 
format is:
-# +:NEW_FILE        (**Not in index but exists on disk**)
-# -:DELETED_FILE    (**In index but does not exist on disk**)
-##
-diffIndex() {
-    diff --unchanged-line-format='' --new-line-format='+:%L' 
--old-line-format='-:%L' \
-        <(sort -u < "$INDEXED" | sed '/^[[:blank:]]*$/d') \
-        <(find ./ -name '*.php' -type f | sed 
's!^\./\|^./\(var\|.cache\|vendor/bin\)/.\+$!!g; /^[[:blank:]]*$/d' | sort)
-}
-
-##
-# This function reads the output of a grep command with the option -H or
-# --with-filename enabled. The lines containing class and namespace 
declarations
-# will be parsed and added to the index.
-#
-# shellcheck disable=SC2153
-##
-fillIndex() {
-    [[ -n $CACHE_DIR ]]            || return 1
-    [[ -n $CLASSES ]]              || return 1
-    [[ -n $NAMESPACES ]]           || return 1
-    [[ -n $USES ]]                 || return 1
-    [[ -n $USES_LOOKUP ]]          || return 1
-    [[ -n $USES_LOOKUP_OWN ]]      || return 1
-    [[ -n $FILE_PATHS ]]           || return 1
-    [[ -n $NAMESPACE_FILE_PATHS ]] || return 1
-    [[ -n $INDEXED ]]              || return 1
-
-    [[ -d $CACHE_DIR ]] || mkdir -p "$CACHE_DIR"
-
-    # Clean up index files if not diffing.
-    if [[ ${CONFIG[$INDEX_NEW]} != '--new' ]]; then
-        echo > "$NAMESPACES"
-        echo > "$CLASSES"
-        echo > "$USES"
-        echo > "$USES_LOOKUP"
-        echo > "$FILE_PATHS"
-        echo > "$USES_OWN"
-        echo > "$USES_LOOKUP_OWN"
-        echo > "$NAMESPACE_FILE_PATHS"
-        echo > "$INDEXED"
-    fi
-
-    declare -A namespaces=() classes=()
-    while IFS=':' read -ra line; do
-        declare file="${line[0]}"
-
-        # Save the namespace or class to add to the FQN cache later on.
-        if [[ "${line[1]}" =~ (class|trait|interface)[[:blank:]]+([A-Za-z_]+) 
]]; then
-            classes[$file]="${BASH_REMATCH[2]}"
-        elif [[ "${line[1]}" =~ namespace[[:blank:]]+([A-Za-z_\\]+) ]]; then
-            namespaces[$file]="${BASH_REMATCH[1]}"
-        else
-            debugf 'No class or namespace found in line "%s"' "${line[0]}"
-        fi
-
-        # Add filename to file with indexed filenames. This is required
-        # for diffing the index.
-        echo "$file" >> "$INDEXED"
-
-        if [[ $((++lines%500)) -eq 0 ]]; then
-            info "indexed $lines lines."
-        fi
-    done
-
-    # Fill up the index
-    declare -i uses=0
-    for file in "${!classes[@]}"; do
-        declare namespace="${namespaces[$file]}"
-        declare class="${classes[$file]}"
-
-        if [[ -z $class ]]; then
-            debugf 'Class is missing for file "%s"\n' "$file"
-            debugf 'Namespace: "%s"\n' "$namespace"
-            continue
-        fi
-
-        ((uses++))
-        [[ $((uses%500)) -eq 0 ]] && info "Found FQN's for $uses classes."
-
-        echo "$namespace"                >> "$NAMESPACES"
-        echo "$class"                    >> "$CLASSES"
-        echo "$namespace\\$class"        >> "$USES"
-        echo "$class:$namespace\\$class" >> "$USES_LOOKUP"
-        echo "$file:$namespace\\$class"  >> "$FILE_PATHS"
-        echo "$file:$namespace"          >> "$NAMESPACE_FILE_PATHS"
-
-        if [[ $file != 'vendor/'* ]]; then
-            echo "$namespace\\$class"        >> "$USES_OWN"
-            echo "$class:$namespace\\$class" >> "$USES_LOOKUP_OWN"
-        fi
-
-    done
-
-    # This keeps the index of class names unique, so that completing class 
names takes as little
-    # time as possible.
-    # Use echo and a subshell here to prevent changing the file before the 
command is done.
-    # shellcheck disable=SC2005
-    echo "$(sort -u < "$CLASSES")" > "$CLASSES"
-
-    # Ditto for the namespaces index
-    # shellcheck disable=SC2005
-    echo "$(sort -u < "$NAMESPACES")" > "$NAMESPACES"
-
-    info "Finished indexing. Indexed ${lines} lines and found FQN's for $uses 
classes." >&2
-}
-
-checkCache() {
-    if ! [[ -d "$CACHE_DIR" ]]; then
-        info "No cache dir found, indexing." >&2
-        execute index
-    fi
-}
-
-##
-# Find use statements and needed classes in a file.
-
-findUsesAndNeeds() {
-    declare -p needs &>>/dev/null || return 1
-    declare -p uses &>>/dev/null || return 1
-    # shellcheck disable=SC2154
-    declare -p namespace &>>/dev/null || return 1
-
-    while read -r line; do
-        [[ $line == namespace* ]] && check_uses='true'
-        if [[ $line == ?(@(abstract|final) )@(class|interface|trait)* ]]; then
-            check_uses='false'
-            check_needs='true'
-
-            read -ra line_array <<<"$line"
-            set -- "${line_array[@]}"
-            while shift && [[ "$1" != @(extends|implements) ]]; do :; done;
-            while shift && [[ -n $1 ]]; do
-                [[ $1 == 'implements' ]] && shift
-                [[ $1 == \\* ]] || _set_needed_if_not_used "$1"
-            done
-        fi
-
-        if $check_uses; then
-            if [[ $line == use* ]]; then
-                declare class_name="${line##*\\}"
-                [[ $class_name == *as* ]] && class_name="${class_name##*as }"
-                debug "Class name: $class_name"
-                class_name="${class_name%%[^a-zA-Z]*}"
-                uses["$class_name"]='used'
-            fi
-        fi
-
-        if $check_needs; then
-            if [[ $line == *function*([[:space:]])*([[:alnum:]_])\(* ]]; then
-                _check_function_needs "$line"
-                continue
-            fi
-            _check_needs "$line"
-        fi
-    done
-}
-
-_check_function_needs() {
-    # Strip everything up until function name and argument declaration.
-    declare line="${1#*function}" function_declaration="${1#*function}"
-
-    # Collect the entire argument declaration
-    while [[ $line != *'{'* ]] && read -r line; do
-        function_declaration="$function_declaration $line"
-    done
-
-    declare -a words=()
-    read -ra words <<<"$function_declaration"
-    for i in "${!words[@]}"; do
-        if [[ "${words[$i]}" =~ ^'$'[a-zA-Z_]+ ]]; then
-            declare prev_word="${words[$((i-1))]}"
-            if [[ $prev_word =~ ^([^\(]*\()?([A-Za-z]+)$ ]]; then
-                declare class_name="${BASH_REMATCH[2]}"
-                debugf 'Found parameter type "%s" for function "%s"\n' 
"$class_name" "$function_declaration"
-                _set_needed_if_not_used "$class_name"
-            fi
-        fi
-    done
-    if [[ "$function_declaration" =~ \):[[:space:]]+([a-zA-Z]+) ]]; then
-        declare class_name="${BASH_REMATCH[1]}"
-        debugf 'Found return type "%s" for function "%s"\n' "$class_name" 
"$function_declaration"
-        _set_needed_if_not_used "$class_name"
-    fi
-}
-
-_check_needs() {
-    declare line="$1" match=''
-    if _line_matches "$line"; then
-        declare class_name="${match//[^a-zA-Z]/}"
-
-        debugf 'Extracted type "%s" from line "%s". Entire match: "%s"\n' 
"$class_name" "$line" "${BASH_REMATCH[0]}"
-        _set_needed_if_not_used "$class_name"
-
-        line="${line/"${BASH_REMATCH[0]/}"}"
-        _check_needs "$line"
-    fi
-}
-
-# shellcheck disable=SC2049
-_line_matches() {
-    if [[ $line =~ 'new'[[:space:]]+([^\\][A-Za-z]+)\( ]] \
-        || [[ $line =~ 'instanceof'[[:space:]]+([A-Za-z]+) ]] \
-        || [[ $line =~ catch[[:space:]]*\(([A-Za-z]+) ]] \
-        || [[ $line =~ \*[[:blank:]]*@([A-Z][a-zA-Z]*) ]]; then
-        match="${BASH_REMATCH[1]}"
-        return $?
-    elif [[ $line =~ @(var|param|return|throws)[[:space:]]+([A-Za-z]+) ]] \
-        || [[ $line =~ (^|[\(\[\{[:blank:]])([A-Za-z]+)'::' ]]; then
-        match="${BASH_REMATCH[2]}"
-        return $?
-    fi
-    return 1
-}
-
-_set_needed_if_not_used() {
-    declare class_name="$1"
-    if [[ -z ${uses["$class_name"]} ]] \
-        && [[ -z ${namespace["$class_name"]} ]] \
-        && [[ "$class_name" != 
@(static|self|string|int|float|array|object|bool|mixed|parent|void) ]]; then
-        needs["$class_name"]='needed'
-    fi
-}
-
-execute "$@"
diff --git a/phpinspect-index.el b/phpinspect-index.el
index dd164dca83..c952c4e625 100644
--- a/phpinspect-index.el
+++ b/phpinspect-index.el
@@ -42,7 +42,7 @@
 (defun phpinspect-return-annotation-p (token)
   (phpinspect-token-type-p token :return-annotation))
 
-(defun phpinspect--index-function-arg-list (type-resolver arg-list)
+(defun phpinspect--index-function-arg-list (type-resolver arg-list &optional 
add-used-types)
   (let ((arg-index)
         (current-token)
         (arg-list (cl-copy-list arg-list)))
@@ -51,7 +51,8 @@
                   (phpinspect-variable-p (car arg-list)))
              (push `(,(cadr (pop arg-list))
                      ,(funcall type-resolver (phpinspect--make-type :name 
(cadr current-token))))
-                   arg-index))
+                   arg-index)
+             (when add-used-types (funcall add-used-types (list (cadr 
current-token)))))
             ((phpinspect-variable-p (car arg-list))
              (push `(,(cadr (pop arg-list))
                      nil)
@@ -63,7 +64,12 @@
   (or (not type)
       (phpinspect--type= type phpinspect--object-type)))
 
-(defun phpinspect--index-function-from-scope (type-resolver scope 
comment-before)
+(defun phpinspect--index-function-from-scope (type-resolver scope 
comment-before &optional add-used-types)
+  "Index a function inside SCOPE token using phpdoc metadata in COMMENT-BEFORE.
+
+If ADD-USED-TYPES is set, it must be a function and will be
+called with a list of the types that are used within the
+function (think \"new\" statements, return types etc.)."
   (let* ((php-func (cadr scope))
          (declaration (cadr php-func))
          (type (if (phpinspect-word-p (car (last declaration)))
@@ -95,6 +101,12 @@
                               (phpinspect--make-type :name 
return-annotation-type)))
                (setf (phpinspect--type-collection type) t)))))
 
+    (when add-used-types
+      (let ((used-types (phpinspect--find-used-types-in-tokens
+                         `(,(seq-find #'phpinspect-block-p php-func)))))
+        (when type (push (phpinspect--type-bare-name type) used-types))
+        (funcall add-used-types used-types)))
+
     (phpinspect--make-function
      :scope `(,(car scope))
      :name (cadadr (cdr declaration))
@@ -102,7 +114,8 @@
                     phpinspect--null-type)
      :arguments (phpinspect--index-function-arg-list
                  type-resolver
-                 (phpinspect-function-argument-list php-func)))))
+                 (phpinspect-function-argument-list php-func)
+                 add-used-types))))
 
 (defun phpinspect--index-const-from-scope (scope)
   (phpinspect--make-variable
@@ -141,7 +154,7 @@
                             (cadr class-token))))
     (cadr subtoken)))
 
-(defun phpinspect--index-class (imports type-resolver class)
+(defun phpinspect--index-class (imports type-resolver location-resolver class)
   "Create an alist with relevant attributes of a parsed class."
   (phpinspect--log "INDEXING CLASS")
   (let ((methods)
@@ -154,7 +167,15 @@
         (class-name (phpinspect--get-class-name-from-token class))
         ;; Keep track of encountered comments to be able to use type
         ;; annotations.
-        (comment-before))
+        (comment-before)
+        ;; The types that are used within the code of this class' methods.
+        (used-types)
+        (add-used-types))
+    (setq add-used-types
+          (lambda (additional-used-types)
+            (if used-types
+                (nconc used-types additional-used-types)
+              (setq used-types additional-used-types))))
 
     ;; Find out what the class extends or implements
     (let ((enc-extends nil)
@@ -195,7 +216,8 @@
                            (push (phpinspect--index-function-from-scope 
type-resolver
                                                                         (list 
(car token)
                                                                               
(cadadr token))
-                                                                        
comment-before)
+                                                                        
comment-before
+                                                                        
add-used-types)
                                  static-methods))
 
                           ((phpinspect-variable-p (cadadr token))
@@ -208,14 +230,16 @@
                     (phpinspect--log "comment-before is: %s" comment-before)
                     (push (phpinspect--index-function-from-scope type-resolver
                                                                  token
-                                                                 
comment-before)
+                                                                 comment-before
+                                                                 
add-used-types)
                           methods))))
             ((phpinspect-static-p token)
              (cond ((phpinspect-function-p (cadr token))
                     (push (phpinspect--index-function-from-scope type-resolver
                                                                  `(:public
                                                                    ,(cadr 
token))
-                                                                 
comment-before)
+                                                                 comment-before
+                                                                 
add-used-types)
                           static-methods))
 
                    ((phpinspect-variable-p (cadr token))
@@ -232,7 +256,8 @@
              ;; Bare functions are always public
              (push (phpinspect--index-function-from-scope type-resolver
                                                           (list :public token)
-                                                          comment-before)
+                                                          comment-before
+                                                          add-used-types)
                    methods))
             ((phpinspect-doc-block-p token)
              (phpinspect--log "setting comment-before %s" token)
@@ -270,6 +295,7 @@
       `(,class-name .
                     (phpinspect--indexed-class
                      (class-name . ,class-name)
+                     (location . ,(funcall location-resolver class))
                      (imports . ,imports)
                      (methods . ,methods)
                      (static-methods . ,static-methods)
@@ -277,7 +303,9 @@
                      (variables . ,variables)
                      (constants . ,constants)
                      (extends . ,extends)
-                     (implements . ,implements))))))
+                     (implements . ,implements)
+                     (used-types . ,(mapcar #'phpinspect-intern-name
+                                            (seq-uniq used-types 
#'string=))))))))
 
 (defsubst phpinspect-namespace-body (namespace)
   "Return the nested tokens in NAMESPACE tokens' body.
@@ -286,18 +314,19 @@ Accounts for namespaces that are defined with '{}' 
blocks."
       (cdaddr namespace)
     (cdr namespace)))
 
-(defun phpinspect--index-classes (imports classes &optional namespace indexed)
+(defun phpinspect--index-classes
+    (imports classes type-resolver-factory location-resolver &optional 
namespace indexed)
   "Index the class tokens in `classes`, using the imports in `imports`
 as Fully Qualified names. `namespace` will be assumed the root
 namespace if not provided"
   (if classes
       (let ((class (pop classes)))
         (push (phpinspect--index-class
-               imports
-               (phpinspect--make-type-resolver imports class namespace)
-               class)
+               imports (funcall type-resolver-factory imports class namespace)
+               location-resolver class)
               indexed)
-        (phpinspect--index-classes imports classes namespace indexed))
+        (phpinspect--index-classes imports classes type-resolver-factory
+                                   location-resolver namespace indexed))
     (nreverse indexed)))
 
 (defun phpinspect--use-to-type (use)
@@ -316,33 +345,82 @@ namespace if not provided"
 (defun phpinspect--uses-to-types (uses)
   (mapcar #'phpinspect--use-to-type uses))
 
-(defun phpinspect--index-namespace (namespace)
+(defun phpinspect--index-namespace (namespace type-resolver-factory 
location-resolver)
   (phpinspect--index-classes
    (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
    (seq-filter #'phpinspect-class-p namespace)
-   (cadadr namespace)))
+   type-resolver-factory location-resolver (cadadr namespace) nil))
 
-(defun phpinspect--index-namespaces (namespaces &optional indexed)
+(defun phpinspect--index-namespaces
+    (namespaces type-resolver-factory location-resolver &optional indexed)
   (if namespaces
       (progn
-        (push (phpinspect--index-namespace (pop namespaces)) indexed)
-        (phpinspect--index-namespaces namespaces indexed))
+        (push (phpinspect--index-namespace (pop namespaces)
+                                           type-resolver-factory
+                                           location-resolver)
+              indexed)
+        (phpinspect--index-namespaces namespaces type-resolver-factory
+                                      location-resolver 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)
+(defun phpinspect--find-used-types-in-tokens (tokens)
+  "Find usage of the \"new\" keyword in TOKENS.
+
+Return value is a list of the types that are \"newed\"."
+  (let ((previous-tokens)
+        (used-types))
+    (while tokens
+      (let ((token (pop tokens))
+            (previous-token (car previous-tokens)))
+        (cond ((and (phpinspect-word-p previous-token)
+                    (string= "new" (cadr previous-token))
+                    (phpinspect-word-p token))
+               (let ((type (cadr token)))
+                 (when (not (string-match-p "\\\\" type))
+                   (push type used-types))))
+              ((and (phpinspect-static-attrib-p token)
+                    (phpinspect-word-p previous-token))
+               (let ((type (cadr previous-token)))
+                 (when (not (string-match-p "\\\\" type))
+                   (push type used-types))))
+              ((phpinspect-object-attrib-p token)
+               (let ((lists (seq-filter #'phpinspect-list-p token)))
+                 (dolist (list lists)
+                   (setq used-types (append 
(phpinspect--find-used-types-in-tokens (cdr list))
+                                            used-types)))))
+              ((or (phpinspect-list-p token) (phpinspect-block-p token))
+               (setq used-types (append (phpinspect--find-used-types-in-tokens 
(cdr token))
+                                        used-types))))
+
+        (push token previous-tokens)))
+    used-types))
+
+(defun phpinspect--index-tokens (tokens &optional type-resolver-factory 
location-resolver)
   "Index TOKENS as returned by `phpinspect--parse-current-buffer`."
+  (unless type-resolver-factory
+    (setq type-resolver-factory #'phpinspect--make-type-resolver))
+
+  (unless location-resolver
+    (setq location-resolver (lambda (_) (list 0 0))))
+
   (let ((imports (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p 
tokens))))
     `(phpinspect--root-index
       (imports . ,imports)
       ,(append
         (append '(classes)
-                (phpinspect--index-namespaces (seq-filter 
#'phpinspect-namespace-p tokens))
+                (phpinspect--index-namespaces (seq-filter 
#'phpinspect-namespace-p tokens)
+                                              type-resolver-factory
+                                              location-resolver)
                 (phpinspect--index-classes
                  imports
-                 (seq-filter #'phpinspect-class-p tokens))))
+                 (seq-filter #'phpinspect-class-p tokens)
+                 type-resolver-factory
+                 location-resolver)))
+      ,(append '(used-types)
+               (phpinspect--find-used-types-in-tokens tokens))
       (functions))
     ;; TODO: Implement function indexation
     ))
@@ -381,10 +459,5 @@ namespace if not provided"
      (phpinspect--log "Failed to find file for type %s:  %s" type error)
      nil)))
 
-(defsubst phpinspect--index-thread-enqueue (task)
-  (phpinspect--queue-enqueue-noduplicate phpinspect--index-queue
-                                         task
-                                         #'phpinspect--index-task=))
-
 (provide 'phpinspect-index)
 ;;; phpinspect-index.el ends here
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index 9d2609bcbc..8471a908e1 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -384,13 +384,26 @@ token is \";\", which marks the end of a statement in 
PHP."
              (cond ,@(mapcar
                       (lambda (handler)
                         `((looking-at ,(plist-get (symbol-value handler) 
'regexp))
-                          (let ((token (funcall ,(symbol-function handler)
+                          (let ((start-position (point))
+                                (token (funcall ,(symbol-function handler)
                                                 (match-string 0)
                                                 max-point)))
                             (when token
                               (if (null tokens)
                                   (setq tokens (list token))
-                                (nconc tokens (list token)))))))
+                                (progn
+                                  (nconc tokens (list token))))
+
+                                  ;; When parsing within a buffer that has
+                                  ;; `phpinspect-current-buffer` set, update 
the
+                                  ;; token location map. Usually, this variable
+                                  ;; is set when `phpinspect-mode` is active.
+                                  (when phpinspect-current-buffer
+                                    (puthash token
+                                             (phpinspect-make-region 
start-position
+                                                                     (point))
+                                             (phpinspect-buffer-location-map
+                                              phpinspect-current-buffer)))))))
                       handlers)
                    (t (forward-char))))
            (push ,tree-type tokens))))))
diff --git a/phpinspect-project.el b/phpinspect-project.el
index 26e12cac95..0f73122f2b 100644
--- a/phpinspect-project.el
+++ b/phpinspect-project.el
@@ -25,13 +25,25 @@
 
 (require 'phpinspect-class)
 (require 'phpinspect-type)
+(require 'phpinspect-fs)
+(require 'filenotify)
 
-(cl-defstruct (phpinspect--project (:constructor 
phpinspect--make-project-cache))
+(cl-defstruct (phpinspect--project (:constructor phpinspect--make-project))
   (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")
+  (fs nil
+      :type phpinspect-fs
+      :documentation
+      "The filesystem object through which this project's files
+can be accessed.")
+  (autoload nil
+    :type phpinspect-autoload
+    :documentation
+    "The autoload object through which this project's type
+definitions can be retrieved")
   (worker nil
           :type phpinspect-worker
           :documentation
@@ -39,12 +51,35 @@ indexed classes in the project")
   (root nil
         :type string
         :documentation
-        "The root directory of this project"))
+        "The root directory of this project")
+  (purged nil
+          :type boolean
+          :documentation "Whether or not the project has been purged or not.
+Projects get purged when they are removed from the global cache.")
+  (file-watchers (make-hash-table :test #'equal :size 10000 :rehash-size 10000)
+                 :type hash-table
+                 :documentation "All active file watchers in this project,
+indexed by the absolute paths of the files they're watching."))
 
 (cl-defgeneric phpinspect--project-add-class
     ((project phpinspect--project) (class (head phpinspect--indexed-class)))
   "Add an indexed CLASS to PROJECT.")
 
+(cl-defmethod phpinspect--project-purge ((project phpinspect--project))
+  "Disable all background processes for project and put it in a `purged` 
state."
+  (maphash (lambda (_ watcher) (file-notify-rm-watch watcher))
+           (phpinspect--project-file-watchers project))
+
+  (setf (phpinspect--project-file-watchers project)
+         (make-hash-table :test #'equal :size 10000 :rehash-size 10000))
+  (setf (phpinspect--project-purged project) t))
+
+(cl-defmethod phpinspect--project-watch-file ((project phpinspect--project)
+                                              filepath
+                                              callback)
+  (let ((watcher (file-notify-add-watch filepath '(change) callback)))
+    (puthash filepath watcher (phpinspect--project-file-watchers project))))
+
 (cl-defmethod phpinspect--project-add-return-types-to-index-queueue
   ((project phpinspect--project) methods)
   (dolist (method methods)
@@ -61,16 +96,15 @@ indexed classes in the project")
 
 (cl-defmethod phpinspect--project-enqueue-if-not-present
   ((project phpinspect--project) (type phpinspect--type))
-  (let ((class (phpinspect--project-get-class project type)))
-    (when (or (not class)
-              (not (or (phpinspect--class-initial-index class)
-                       (phpinspect--class-index-queued class))))
-      (when (not class)
-        (setq class (phpinspect--project-create-class project type)))
-      (phpinspect--log "Adding unpresent class %s to index queue" type)
-      (setf (phpinspect--class-index-queued class) t)
-      (phpinspect-worker-enqueue (phpinspect--project-worker project)
-                                 (phpinspect-make-index-task project type)))))
+  (unless (phpinspect--type-is-native type)
+    (let ((class (phpinspect--project-get-class project type)))
+      (when (or (not class)
+                (not (or (phpinspect--class-initial-index class))))
+        (when (not class)
+          (setq class (phpinspect--project-create-class project type)))
+        (phpinspect--log "Adding unpresent class %s to index queue" type)
+        (phpinspect-worker-enqueue (phpinspect--project-worker project)
+                                   (phpinspect-make-index-task project 
type))))))
 
 (cl-defmethod phpinspect--project-add-class-attribute-types-to-index-queue
   ((project phpinspect--project) (class phpinspect--class))
diff --git a/phpinspect-type.el b/phpinspect-type.el
index c197cea9ec..550d22d50f 100644
--- a/phpinspect-type.el
+++ b/phpinspect-type.el
@@ -104,6 +104,7 @@ See https://wiki.php.net/rfc/static_return_type ."
   (car (last (split-string fqn "\\\\"))))
 
 (cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
+  "Return just the name, without namespace part, of TYPE."
   (phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
 
 (cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2 
phpinspect--type))
@@ -145,7 +146,6 @@ NAMESPACE may be nil, or a string with a namespace FQN."
 
 (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))))
diff --git a/phpinspect-worker.el b/phpinspect-worker.el
index 5945245ea7..f63d97b0a0 100644
--- a/phpinspect-worker.el
+++ b/phpinspect-worker.el
@@ -31,6 +31,18 @@
 (defvar phpinspect-worker nil
   "Contains the phpinspect worker that is used by all projects.")
 
+(cl-defstruct (phpinspect-index-task
+               (:constructor phpinspect-make-index-task-generated))
+  "Represents an index task that can be executed by a `phpinspect-worker`."
+  (project nil
+           :type phpinspect--project
+           :documentation
+           "The project that the task should be executed for.")
+  (type nil
+        :type phpinspect--type
+        :documentation
+        "The type whose file should be indexed."))
+
 (cl-defstruct (phpinspect-queue-item
                (:constructor phpinspect-make-queue-item))
   (next nil
@@ -210,6 +222,18 @@ on the worker independent of dynamic variables during 
testing.")
 (cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-worker) task)
   (phpinspect-queue-enqueue (phpinspect-worker-queue worker) task))
 
+(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-worker)
+                                         (task phpinspect-index-task))
+  "Specialized enqueuement method for index tasks. Prevents
+indexation tasks from being added when there are identical tasks
+already present in the queue."
+  (phpinspect-queue-enqueue-noduplicate (phpinspect-worker-queue worker) task 
#'phpinspect-index-task=))
+
+(cl-defmethod phpinspect-index-task= ((task1 phpinspect-index-task) (task2 
phpinspect-index-task))
+  (and (eq (phpinspect-index-task-project task1)
+           (phpinspect-index-task-project task2))
+       (phpinspect--type= (phpinspect-index-task-type task1) 
(phpinspect-index-task-type task2))))
+
 (cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-dynamic-worker) 
task)
   (phpinspect-worker-enqueue (phpinspect-resolve-dynamic-worker worker)
                              task))
@@ -241,7 +265,10 @@ CONTINUE must be a condition-variable"
                (mx (make-mutex))
                (continue (make-condition-variable mx)))
           (if task
-              (phpinspect-task-execute task worker)
+              ;; Execute task if it belongs to a project that has not been
+              ;; purged (meaning that it is still actively used).
+              (unless (phpinspect--project-purged (phpinspect-task-project 
task))
+                (phpinspect-task-execute task worker))
             ;; else: join with the main thread until wakeup is signaled
             (thread-join main-thread))
 
@@ -292,24 +319,18 @@ CONTINUE must be a condition-variable"
   (interactive)
   (phpinspect-worker-stop phpinspect-worker))
 
-(cl-defstruct (phpinspect-index-task
-               (:constructor phpinspect-make-index-task-generated))
-  "Represents an index task that can be executed by a `phpinspect-worker`."
-  (project nil
-           :type phpinspect--project
-           :documentation
-           "The project that the task should be executed for.")
-  (type nil
-        :type phpinspect--type
-        :documentation
-        "The type whose file should be indexed."))
-
 (cl-defgeneric phpinspect-make-index-task ((project phpinspect--project)
                                           (type phpinspect--type))
   (phpinspect-make-index-task-generated
    :project project
    :type type))
 
+(cl-defgeneric phpinspect-task-project (task)
+  "The project that this task belongs to.")
+
+(cl-defmethod phpinspect-task-project ((task phpinspect-index-task))
+  (phpinspect-index-task-project task))
+
 (cl-defgeneric phpinspect-task-execute (task worker)
   "Execute TASK for WORKER.")
 
@@ -323,19 +344,19 @@ CONTINUE must be a condition-variable"
                      (phpinspect-index-task-type task)
                      (phpinspect--project-root project))
 
-    (if is-native-type
-        (progn
-          (phpinspect--log "Skipping indexation of native type %s"
-                           (phpinspect-index-task-type task))
-
-          ;; We can skip pausing when a native type is encountered
-          ;; and skipped, as we haven't done any intensive work that
-          ;; may cause hangups.
-          (setf (phpinspect-worker-skip-next-pause worker) t))
-      (let* ((type (phpinspect-index-task-type task))
-             (root-index (phpinspect--index-type-file project type)))
-        (when root-index
-          (phpinspect--project-add-index project root-index))))))
+    (cond (is-native-type
+           (phpinspect--log "Skipping indexation of native type %s"
+                            (phpinspect-index-task-type task))
+
+           ;; We can skip pausing when a native type is encountered
+           ;; and skipped, as we haven't done any intensive work that
+           ;; may cause hangups.
+           (setf (phpinspect-worker-skip-next-pause worker) t))
+          (t
+           (let* ((type (phpinspect-index-task-type task))
+                  (root-index (phpinspect--index-type-file project type)))
+             (when root-index
+               (phpinspect--project-add-index project root-index)))))))
 
 
 (provide 'phpinspect-worker)
diff --git a/phpinspect.el b/phpinspect.el
index 0e39cb0cda..fb1b59012c 100644
--- a/phpinspect.el
+++ b/phpinspect.el
@@ -1,4 +1,4 @@
-;; phpinspect.el --- PHP parsing and completion package  -*- lexical-binding: 
t; -*-
+; phpinspect.el --- PHP parsing and completion package  -*- lexical-binding: 
t; -*-
 
 ;; Copyright (C) 2021  Free Software Foundation, Inc
 
@@ -38,6 +38,9 @@
 (require 'phpinspect-index)
 (require 'phpinspect-class)
 (require 'phpinspect-worker)
+(require 'phpinspect-autoload)
+(require 'phpinspect-imports)
+(require 'phpinspect-buffer)
 
 (defvar-local phpinspect--buffer-index nil
   "The result of the last successfull parse + index action
@@ -67,16 +70,6 @@ phpinspect")
 (defvar phpinspect-eldoc-word-width 14
   "The maximum width of words in eldoc strings.")
 
-(defvar phpinspect-index-executable
-  (concat (file-name-directory
-           (or load-file-name
-               buffer-file-name))
-          "/phpinspect-index.bash")
-  "The path to the exexutable file that indexes class file names.
-Should normally be set to \"phpinspect-index.bash\" in the source
-  file directory.")
-
-
 (cl-defstruct (phpinspect--completion
                (:constructor phpinspect--construct-completion))
   "Contains a possible completion value with all it's attributes."
@@ -708,7 +701,7 @@ more recent"
 
 (defun phpinspect--init-mode ()
   "Initialize the phpinspect minor mode for the current buffer."
-
+  (setq phpinspect-current-buffer (phpinspect-make-buffer :buffer 
(current-buffer)))
   (make-local-variable 'company-backends)
   (add-to-list 'company-backends #'phpinspect-company-backend)
 
@@ -755,6 +748,7 @@ users will have to use \\[phpinspect-purge-cache]."
 
 (defun phpinspect--disable-mode ()
   "Clean up the buffer environment for the mode to be disabled."
+  (setq phpinspect-current-buffer nil)
   (kill-local-variable 'phpinspect--buffer-project)
   (kill-local-variable 'phpinspect--buffer-index)
   (kill-local-variable 'company-backends)
@@ -784,8 +778,8 @@ For finding/opening class files see
  `phpinspect-find-class-file' (bound to \\[phpinspect-find-class-file]).
 
 To automatically add missing use statements for used classes to a
-visited file, use `phpinspect-fix-uses-interactive'
-(bound to \\[phpinspect-fix-uses-interactive]].)
+visited file, use `phpinspect-fix-imports'
+(bound to \\[phpinspect-fix-imports]].)
 
 Example configuration:
 
@@ -806,7 +800,7 @@ Example configuration:
     (setq-local company-backends '(phpinspect-company-backend))
 
     ;; Shortcut to add use statements for classes you use.
-    (define-key php-mode-map (kbd \"C-c u\") 'phpinspect-fix-uses-interactive)
+    (define-key php-mode-map (kbd \"C-c u\") 'phpinspect-fix-imports)
 
     ;; Shortcuts to quickly search/open files of PHP classes.
     ;; You can make these local to php-mode, but making them global
@@ -1094,6 +1088,13 @@ If its value is nil, it is created and then returned."
 This effectively purges any cached code information from all
 currently opened projects."
   (interactive)
+  (when phpinspect-cache
+    ;; Allow currently known cached projects to cleanup after themselves
+    (maphash (lambda (_ project)
+               (phpinspect--project-purge project))
+             (phpinspect--cache-projects phpinspect-cache)))
+
+  ;; Assign a fresh cache object
   (setq phpinspect-cache (phpinspect--make-cache)))
 
 (defun phpinspect--locate-dominating-project-file (start-file)
@@ -1146,69 +1147,26 @@ level of START-FILE in stead of `default-directory`."
             (json-key-type 'string))
      ,@body))
 
-;; Use statements
-;;;###autoload
-(defun phpinspect-fix-uses-interactive ()
-  "Add missing use statements to the currently visited PHP file."
-  (interactive)
-  (let ((project-root (phpinspect-project-root)))
-    (when project-root
-      (save-buffer)
-      (let* ((phpinspect-json (shell-command-to-string
-                                          (format "cd %s && %s fxu --json %s"
-                                                      (shell-quote-argument 
project-root)
-                                       (shell-quote-argument 
phpinspect-index-executable)
-                                                      (shell-quote-argument 
buffer-file-name)))))
-           (let* ((json-object-type 'hash-table)
-                      (json-array-type 'list)
-                      (json-key-type 'string)
-                      (phpinspect-json-data (json-read-from-string 
phpinspect-json)))
-             (maphash #'phpinspect-handle-phpinspect-json 
phpinspect-json-data))))))
-
-(defun phpinspect-handle-phpinspect-json (class-name candidates)
-  "Handle key value pair of classname and FQN's"
-  (let ((ncandidates (length candidates)))
-    (cond ((= 1 ncandidates)
-           (phpinspect-add-use (pop candidates)))
-          ((= 0 ncandidates)
-           (message "No use statement found for class \"%s\"" class-name))
-          (t
-           (phpinspect-add-use (completing-read "Class: " candidates))))))
-
-;; TODO: Implement this using the parser in stead of regexes.
-(defun phpinspect-add-use (fqn) "Add use statement to a php file"
-       (save-excursion
-         (let ((current-char (point)))
-          (goto-char (point-min))
-          (cond
-           ((re-search-forward "^use" nil t) (forward-line 1))
-           ((re-search-forward "^namespace" nil t) (forward-line 2))
-           ((re-search-forward
-             "^\\(abstract \\|/\\* final \\*/ ?\\|final 
\\|\\)\\(class\\|trait\\|interface\\)"
-              nil )
-            (forward-line -1)
-            (phpinspect-goto-first-line-no-comment-up)))
-
-          (insert (format "use %s;%c" fqn ?\n))
-          (goto-char current-char))))
-
-(defun phpinspect-goto-first-line-no-comment-up ()
-  "Go up until a line is encountered that does not start with a comment."
-  (when (string-match "^\\( ?\\*\\|/\\)" (thing-at-point 'line t))
-       (forward-line -1)
-       (phpinspect-goto-first-line-no-comment-up)))
-
 (defsubst phpinspect-insert-file-contents (&rest args)
   "Call `phpinspect-insert-file-contents-function' with ARGS as arguments."
   (apply phpinspect-insert-file-contents-function args))
 
-(defun phpinspect-get-all-fqns (&optional fqn-file)
-  (unless fqn-file
-    (setq fqn-file "uses"))
-  (with-temp-buffer
-    (phpinspect-insert-file-contents
-     (concat (phpinspect-project-root) "/.cache/phpinspect/" fqn-file))
-    (split-string (buffer-string) (char-to-string ?\n))))
+(defun phpinspect-get-all-fqns (&optional filter)
+  "Return a list of all FQNS congruent with FILTER in the currently active 
project.
+
+FILTER must be nil or the symbol 'own' if FILTER is 'own', only
+fully qualified names from the project's source, and not its
+dependencies, are returned."
+  (let* ((project (phpinspect--cache-get-project-create
+                   (phpinspect--get-or-create-global-cache)
+                   (phpinspect-project-root)))
+         (autoloader (phpinspect--project-autoload project)))
+    (let ((fqns))
+      (maphash (lambda (type _) (push (symbol-name type) fqns))
+               (if (eq 'own filter)
+                   (phpinspect-autoloader-own-types autoloader)
+                 (phpinspect-autoloader-types autoloader)))
+      fqns)))
 
 ;;;###autoload
 (defun phpinspect-find-class-file (fqn)
@@ -1230,7 +1188,7 @@ available FQNs for classes in the current project, which 
aren't
 located in \"vendor\" folder."
   (interactive (list (phpinspect--make-type
                       :name
-                      (completing-read "Class: " (phpinspect-get-all-fqns 
"uses_own")))))
+                      (completing-read "Class: " (phpinspect-get-all-fqns 
'own)))))
   (find-file (phpinspect-type-filepath fqn)))
 
 (defsubst phpinspect-type-filepath (fqn)
@@ -1242,35 +1200,22 @@ located in \"vendor\" folder."
 
 when INDEX-NEW is non-nil, new files are added to the index
 before the search is executed."
-  (when (eq index-new 'index-new)
-    (with-temp-buffer
-      (call-process phpinspect-index-executable nil (current-buffer) nil 
"index" "--new")))
-  (let* ((default-directory (phpinspect-project-root))
-         (result (with-temp-buffer
-                   (phpinspect--log "dir: %s" default-directory)
-                   (phpinspect--log "class: %s" (string-remove-prefix
-                                                 "\\"
-                                                 (phpinspect--type-name 
class)))
-                   (list (call-process phpinspect-index-executable
-                                       nil
-                                       (current-buffer)
-                                       nil
-                                       "fp" (string-remove-prefix
-                                             "\\"
-                                             (phpinspect--type-name class)))
-                           (buffer-string)))))
-    (if (not (= (car result) 0))
-        (progn
-          (phpinspect--log "Got non-zero return value %d Retrying with 
reindex. output: \"%s\""
-                           (car result)
-                           (cadr result))
+  (let* ((project (phpinspect--cache-get-project-create
+                   (phpinspect--get-or-create-global-cache)
+                   (phpinspect-project-root)))
+         (autoloader (phpinspect--project-autoload project)))
+    (when (eq index-new 'index-new)
+      (phpinspect-autoloader-refresh autoloader))
+    (let* ((result (phpinspect-autoloader-resolve autoloader 
(phpinspect--type-name-symbol class))))
+      (if (not result)
           ;; Index new files and try again if not done already.
           (if (eq index-new 'index-new)
               nil
-            (phpinspect-get-class-filepath class 'index-new)))
-      (concat (string-remove-suffix "/" default-directory)
-              "/"
-              (string-remove-prefix "/" (string-trim (cadr result)))))))
+            (progn
+              (phpinspect--log "Failed finding filepath for type %s. Retrying 
with reindex."
+                               (phpinspect--type-name class))
+              (phpinspect-get-class-filepath class 'index-new)))
+        result))))
 
 (defun phpinspect-unique-strings (strings)
   (seq-filter
@@ -1283,22 +1228,17 @@ before the search is executed."
    strings))
 
 (defun phpinspect-index-current-project ()
-  "Index all available FQNs in the current project.
-
-Index is stored in files in the .cache directory of
-the project root."
+  "Index all available FQNs in the current project."
   (interactive)
-  (let* ((default-directory (phpinspect-project-root)))
-    (with-current-buffer (get-buffer-create "**phpinspect-index**")
-      (goto-char (point-max))
-      (make-process
-       :command `(,phpinspect-index-executable "index")
-       :name "phpinspect-index-current-project"
-       :buffer (current-buffer))
-
-      (display-buffer (current-buffer) `(display-buffer-at-bottom 
(window-height . 10)))
-      (set-window-point (get-buffer-window (current-buffer) nil)
-                        (point-max)))))
+  (let* ((project (phpinspect--cache-get-project-create
+                  (phpinspect--get-or-create-global-cache)
+                  (phpinspect-project-root)))
+         (autoloader (phpinspect--project-autoload project)))
+    (phpinspect-autoloader-refresh autoloader)
+    (message (concat "Refreshed project autoloader. Found %d types within 
project,"
+                     " %d types total.")
+             (hash-table-count (phpinspect-autoloader-own-types autoloader))
+             (hash-table-count (phpinspect-autoloader-types autoloader)))))
 
 (defun phpinspect-unique-lines ()
   (let ((unique-lines (phpinspect-unique-strings (split-string (buffer-string) 
"\n" nil nil))))
diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el
index 4c4ba1c908..0c48f2c1ee 100644
--- a/test/phpinspect-test.el
+++ b/test/phpinspect-test.el
@@ -1,4 +1,4 @@
-;;; phpinspect-test.el --- Unit tests for phpinslect.el  -*- lexical-binding: 
t; -*-
+;;; phpinspect-test.el --- Unit tests for phpinspect.el  -*- lexical-binding: 
t; -*-
 
 ;; Copyright (C) 2021 Free Software Foundation, Inc.
 
@@ -500,6 +500,10 @@ class Thing
 (load-file (concat phpinspect-test-directory "/test-worker.el"))
 (load-file (concat phpinspect-test-directory "/test-autoload.el"))
 (load-file (concat phpinspect-test-directory "/test-fs.el"))
+(load-file (concat phpinspect-test-directory "/test-project.el"))
+(load-file (concat phpinspect-test-directory "/test-buffer.el"))
+(load-file (concat phpinspect-test-directory "/test-index.el"))
+
 
 (provide 'phpinspect-test)
 ;;; phpinspect-test.el ends here
diff --git a/test/test-buffer.el b/test/test-buffer.el
new file mode 100644
index 0000000000..ed36ae522f
--- /dev/null
+++ b/test/test-buffer.el
@@ -0,0 +1,68 @@
+;;; test-buffer.el --- Unit tests for phpinspect.el  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+
+;; 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 'ert)
+(require 'phpinspect-parser)
+(require 'phpinspect-buffer)
+
+(ert-deftest phpinspect-parse-buffer-location-map ()
+  "Confirm that the location map of `phpinspect-current-buffer' is
+populated when the variable is set and the data in it is accurate."
+  (let* ((location-map)
+         (parsed)
+         (class))
+    (with-temp-buffer
+      (insert-file-contents (concat phpinspect-test-php-file-directory 
"/NamespacedClass.php"))
+      (setq phpinspect-current-buffer
+            (phpinspect-make-buffer :buffer (current-buffer)))
+      (setq parsed (phpinspect-buffer-parse phpinspect-current-buffer))
+      (setq location-map
+            (phpinspect-buffer-location-map phpinspect-current-buffer)))
+
+    (let* ((class (seq-find #'phpinspect-class-p
+                            (seq-find #'phpinspect-namespace-p parsed)))
+           (class-region (gethash class location-map))
+           (classname-region (gethash (car (cddadr class)) location-map)))
+      (should class)
+      (should class-region)
+      (should classname-region)
+      ;; Character position of the start of the class token.
+      (should (= 417 (phpinspect-region-start class-region)))
+      (should (= 2173 (phpinspect-region-end class-region)))
+
+      (should (= 423 (phpinspect-region-start classname-region)))
+      (should (= 440 (phpinspect-region-end classname-region))))))
+
+(ert-deftest phpinspect-parse-buffer-no-current ()
+  "Confirm that the parser is still functional with
+`phpinspect-current-buffer' unset."
+  (let*((buffer)
+        (parsed))
+    (with-temp-buffer
+      (should-not phpinspect-current-buffer)
+      (insert-file-contents (concat phpinspect-test-php-file-directory 
"/NamespacedClass.php"))
+      (setq parsed (phpinspect-parse-current-buffer)))
+
+    (should (cdr parsed))))
diff --git a/test/test-index.el b/test/test-index.el
new file mode 100644
index 0000000000..e254ac4c8f
--- /dev/null
+++ b/test/test-index.el
@@ -0,0 +1,103 @@
+;;; test-index.el --- Unit tests for phpinspect.el  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+
+;; 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 'ert)
+(require 'phpinspect-index)
+
+(ert-deftest phpinspect-index-static-methods ()
+  (let* ((class-tokens
+          `(:root
+            (:class
+             (:declaration (:word "class") (:word "Potato"))
+             (:block
+              (:static
+               (:function (:declaration (:word "function")
+                                        (:word "staticMethod")
+                                        (:list (:variable "untyped")
+                                               (:comma)
+                                               (:word "array")
+                                               (:variable "things")))
+                          (:block)))))))
+         (index (phpinspect--index-tokens class-tokens))
+         (expected-index
+          `(phpinspect--root-index
+            (imports)
+            (classes
+             (,(phpinspect--make-type :name"\\Potato" :fully-qualified t)
+              phpinspect--indexed-class
+              (class-name . ,(phpinspect--make-type :name "\\Potato" 
:fully-qualified t))
+              (location . (0 0))
+              (imports)
+              (methods)
+              (static-methods . (,(phpinspect--make-function
+                                   :name "staticMethod"
+                                   :scope '(:public)
+                                   :arguments `(("untyped" nil)
+                                                ("things" 
,(phpinspect--make-type :name "\\array"
+                                                                               
   :fully-qualified t)))
+                                   :return-type phpinspect--null-type)))
+              (static-variables)
+              (variables)
+              (constants)
+              (extends)
+              (implements)
+              (used-types)))
+            (used-types)
+            (functions))))
+    (should (equal expected-index index))))
+
+(ert-deftest phpinspect-index-used-types-in-class ()
+  (let* ((result (phpinspect--index-tokens
+                  (phpinspect-parse-string
+                   "<?php namespace Field; class Potato {
+public function makeThing(): Thing
+{
+if ((new Monkey())->tree() === true) {
+   return new ExtendedThing();
+}
+return StaticThing::create(new ThingFactory())->makeThing((((new 
Potato())->antiPotato(new OtherThing()))));
+}")))
+         (used-types (alist-get 'used-types (car (alist-get 'classes 
result)))))
+    (should (equal
+             (mapcar #'phpinspect-intern-name
+                     (sort
+                      '("Monkey" "ExtendedThing" "StaticThing" "Thing" 
"ThingFactory" "Potato" "OtherThing")
+                      #'string<))
+             (sort used-types (lambda (s1 s2) (string< (symbol-name s1) 
(symbol-name s2))))))))
+
+(ert-deftest phpinspect--find-used-types-in-tokens ()
+  (let ((blocks `(
+                  ((:block (:word "return")
+                           (:word "new")
+                           (:word "Response")
+                           (:list))
+                   ("Response"))
+                  ((:block (:list (:word "new") (:word "Response"))
+                           (:object-attrib (:word "someMethod")
+                                           (:list (:word "new")
+                                                  (:word "Request"))))
+                   ("Request" "Response")))))
+    (dolist (set blocks)
+      (let ((result (phpinspect--find-used-types-in-tokens (car set))))
+        (should (equal (cadr set) result))))))
diff --git a/test/test-project.el b/test/test-project.el
new file mode 100644
index 0000000000..fa0ee2701e
--- /dev/null
+++ b/test/test-project.el
@@ -0,0 +1,46 @@
+;; test-project.el --- Unit tests for phpinspect.el  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc.
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+
+;; 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 'ert)
+(require 'phpinspect-project)
+
+(ert-deftest phpinspect-project-purge ()
+  (let ((project (phpinspect--make-project)))
+    (phpinspect--project-purge project)
+
+    (should (eq t (phpinspect--project-purged project)))))
+
+(ert-deftest phpinspect-project-watch-file-and-purge ()
+  (let* ((root (make-temp-file "phpinspect-test" 'dir))
+         (fs (phpinspect-make-fs))
+         (worker (phpinspect-make-worker))
+         (watch-file (concat root "/watch1"))
+         (project (phpinspect--make-project :fs fs :root root)))
+    (phpinspect--project-watch-file project watch-file
+                                    (lambda (&rest ignored)))
+
+    (phpinspect--project-purge project)
+
+    (should (= 0 (length (hash-table-values (phpinspect--project-file-watchers 
project)))))))



reply via email to

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