WIP: Index every possibly required type ahead of time.

- Changed project and after-save-action implementation to include imports in
  opened files. This way any types that are used in a file will have completion
  available for it ahead of time.
- Extra attributes have been added to phpinspect--class to check whether or not
  its file has been indexed/is queued for indexation yet.
- Misc: Fixed eldoc function bug that was caused by a previous commit
WIP-incremental-parsing
Hugo Thunnissen 2 years ago
parent 2e487e7810
commit c0786db131

@ -56,7 +56,17 @@
:documentation :documentation
"A list of subscription functions that should be "A list of subscription functions that should be
called whenever anything about this class is called whenever anything about this class is
updated")) updated")
(initial-index nil
: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"))
(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class)) (cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
(dolist (sub (phpinspect--class-subscriptions class)) (dolist (sub (phpinspect--class-subscriptions class))
@ -64,6 +74,7 @@
(cl-defmethod phpinspect--class-set-index ((class phpinspect--class) (cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
(index (head phpinspect--indexed-class))) (index (head phpinspect--indexed-class)))
(setf (phpinspect--class-initial-index class) t)
(setf (phpinspect--class-index class) index) (setf (phpinspect--class-index class) index)
(dolist (method (alist-get 'methods index)) (dolist (method (alist-get 'methods index))
(phpinspect--class-update-method class method)) (phpinspect--class-update-method class method))
@ -124,6 +135,12 @@
(when method (when method
(phpinspect--function-return-type method)))) (phpinspect--function-return-type method))))
(cl-defmethod phpinspect--class-get-static-method-return-type
((class phpinspect--class) (method-name symbol))
(let ((method (phpinspect--class-get-static-method class method-name)))
(when method
(phpinspect--function-return-type method))))
(cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class)) (cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class))
(let ((methods)) (let ((methods))
(maphash (lambda (key method) (maphash (lambda (key method)

@ -46,10 +46,10 @@
(arg-list (cl-copy-list arg-list))) (arg-list (cl-copy-list arg-list)))
(while (setq current-token (pop arg-list)) (while (setq current-token (pop arg-list))
(cond ((and (phpinspect-word-p current-token) (cond ((and (phpinspect-word-p current-token)
(phpinspect-variable-p (car arg-list))) (phpinspect-variable-p (car arg-list)))
(push `(,(cadr (pop arg-list)) (push `(,(cadr (pop arg-list))
,(funcall type-resolver (phpinspect--make-type :name (cadr current-token)))) ,(funcall type-resolver (phpinspect--make-type :name (cadr current-token))))
arg-index)) arg-index))
((phpinspect-variable-p (car arg-list)) ((phpinspect-variable-p (car arg-list))
(push `(,(cadr (pop arg-list)) (push `(,(cadr (pop arg-list))
nil) nil)
@ -71,8 +71,8 @@
;; @return annotation. When dealing with a collection, we want to store the ;; @return annotation. When dealing with a collection, we want to store the
;; type of its members. ;; type of its members.
(let* ((is-collection (let* ((is-collection
(when type (when type
(member (phpinspect--type-name type) phpinspect-collection-types))) (member (phpinspect--type-name type) phpinspect-collection-types)))
(return-annotation-type (return-annotation-type
(when (or (phpinspect--should-prefer-return-annotation type) is-collection) (when (or (phpinspect--should-prefer-return-annotation type) is-collection)
(cadadr (cadadr
@ -139,7 +139,7 @@
(cadr class-token)))) (cadr class-token))))
(cadr subtoken))) (cadr subtoken)))
(defun phpinspect--index-class (type-resolver class) (defun phpinspect--index-class (imports type-resolver class)
"Create an alist with relevant attributes of a parsed class." "Create an alist with relevant attributes of a parsed class."
(phpinspect--log "INDEXING CLASS") (phpinspect--log "INDEXING CLASS")
(let ((methods) (let ((methods)
@ -247,9 +247,9 @@
;; TODO: actually check the types of the variables assigned to object attributes ;; TODO: actually check the types of the variables assigned to object attributes
(let* ((constructor-sym (phpinspect-intern-name "__construct")) (let* ((constructor-sym (phpinspect-intern-name "__construct"))
(constructor (seq-find (lambda (method) (constructor (seq-find (lambda (method)
(eq (phpinspect--function-name-symbol method) (eq (phpinspect--function-name-symbol method)
constructor-sym)) constructor-sym))
methods))) methods)))
(when constructor (when constructor
(phpinspect--log "Constructor was found") (phpinspect--log "Constructor was found")
(dolist (variable variables) (dolist (variable variables)
@ -267,6 +267,7 @@
(let ((class-name (funcall type-resolver (phpinspect--make-type :name class-name)))) (let ((class-name (funcall type-resolver (phpinspect--make-type :name class-name))))
`(,class-name . `(,class-name .
(phpinspect--indexed-class (phpinspect--indexed-class
(imports . ,imports)
(methods . ,methods) (methods . ,methods)
(class-name . ,class-name) (class-name . ,class-name)
(static-methods . ,static-methods) (static-methods . ,static-methods)
@ -283,22 +284,26 @@ Accounts for namespaces that are defined with '{}' blocks."
(cdaddr namespace) (cdaddr namespace)
(cdr namespace))) (cdr namespace)))
(defun phpinspect--index-classes (types classes &optional namespace indexed) (defun phpinspect--index-classes (imports classes &optional namespace indexed)
"Index the class tokens in `classes`, using the types in `types` "Index the class tokens in `classes`, using the imports in `imports`
as Fully Qualified names. `namespace` will be assumed the root as Fully Qualified names. `namespace` will be assumed the root
namespace if not provided" namespace if not provided"
(if classes (if classes
(let ((class (pop classes))) (let ((class (pop classes)))
(push (phpinspect--index-class (push (phpinspect--index-class
(phpinspect--make-type-resolver types class namespace) imports
(phpinspect--make-type-resolver imports class namespace)
class) class)
indexed) indexed)
(phpinspect--index-classes types classes namespace indexed)) (phpinspect--index-classes imports classes namespace indexed))
(nreverse indexed))) (nreverse indexed)))
(defun phpinspect--use-to-type (use) (defun phpinspect--use-to-type (use)
(let* ((fqn (cadr (cadr use))) (let* ((fqn (cadr (cadr use)))
(type (phpinspect--make-type :name fqn :fully-qualified t)) (type (phpinspect--make-type :name (if (string-match "^\\\\" fqn)
fqn
(concat "\\" fqn))
:fully-qualified t))
(type-name (if (and (phpinspect-word-p (caddr use)) (type-name (if (and (phpinspect-word-p (caddr use))
(string= "as" (cadr (caddr use)))) (string= "as" (cadr (caddr use))))
(cadr (cadddr use)) (cadr (cadddr use))
@ -327,16 +332,18 @@ namespace if not provided"
(defun phpinspect--index-tokens (tokens) (defun phpinspect--index-tokens (tokens)
"Index TOKENS as returned by `phpinspect--parse-current-buffer`." "Index TOKENS as returned by `phpinspect--parse-current-buffer`."
`(phpinspect--root-index (let ((imports (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p tokens))))
,(append `(phpinspect--root-index
(append '(classes) (imports . ,imports)
(phpinspect--index-namespaces (seq-filter #'phpinspect-namespace-p tokens)) ,(append
(phpinspect--index-classes (append '(classes)
(phpinspect--uses-to-types (seq-filter #'phpinspect-use-p tokens)) (phpinspect--index-namespaces (seq-filter #'phpinspect-namespace-p tokens))
(seq-filter #'phpinspect-class-p tokens)))) (phpinspect--index-classes
(functions)) imports
;; TODO: Implement function indexation (seq-filter #'phpinspect-class-p tokens))))
) (functions))
;; TODO: Implement function indexation
))
(defun phpinspect-index-file (file-name) (defun phpinspect-index-file (file-name)
(phpinspect--index-tokens (phpinspect-parse-file file-name))) (phpinspect--index-tokens (phpinspect-parse-file file-name)))
@ -377,43 +384,45 @@ namespace if not provided"
enqueued.")) enqueued."))
(defsubst phpinspect--make-queue (&optional subscription) (defsubst phpinspect--make-queue (&optional subscription)
(phpinspect--make-queue-item :subscription subscription)) (phpinspect--make-queue-item :subscription subscription))
;; Recursion causes max-eval-depth error here for long queues. Hence the loop
;; implementation for these two functions.
(cl-defmethod phpinspect--queue-last ((item phpinspect--queue-item)) (cl-defmethod phpinspect--queue-last ((item phpinspect--queue-item))
(if (phpinspect--queue-item-next item) (while (phpinspect--queue-item-next item)
(phpinspect--queue-last (phpinspect--queue-item-next item)) (setq item (phpinspect--queue-item-next item)))
item)) item)
(cl-defmethod phpinspect--queue-first ((item phpinspect--queue-item)) (cl-defmethod phpinspect--queue-first ((item phpinspect--queue-item))
(if (phpinspect--queue-item-previous item) (while (phpinspect--queue-item-previous item)
(phpinspect--queue-first (phpinspect--queue-item-previous item)) (setq item (phpinspect--queue-item-previous item)))
item)) item)
(cl-defmethod phpinspect--queue-enqueue ((item phpinspect--queue-item) thing) (cl-defmethod phpinspect--queue-enqueue ((item phpinspect--queue-item) thing)
(let ((last (phpinspect--queue-last item))) (let ((last (phpinspect--queue-last item)))
(if (not (phpinspect--queue-item-thing last)) (if (not (phpinspect--queue-item-thing last))
(setf (phpinspect--queue-item-thing last) thing) (setf (phpinspect--queue-item-thing last) thing)
(setf (phpinspect--queue-item-next last) (setf (phpinspect--queue-item-next last)
(phpinspect--make-queue-item (phpinspect--make-queue-item
:previous last :previous last
:thing thing :thing thing
:subscription (phpinspect--queue-item-subscription item))))) :subscription (phpinspect--queue-item-subscription item)))))
(funcall (phpinspect--queue-item-subscription item))) (funcall (phpinspect--queue-item-subscription item)))
(cl-defmethod phpinspect--queue-dequeue ((item phpinspect--queue-item)) (cl-defmethod phpinspect--queue-dequeue ((item phpinspect--queue-item))
(let* ((first (phpinspect--queue-first item)) (let* ((first (phpinspect--queue-first item))
(thing (phpinspect--queue-item-thing first)) (thing (phpinspect--queue-item-thing first))
(next (phpinspect--queue-item-next first))) (next (phpinspect--queue-item-next first)))
(when next (setf (phpinspect--queue-item-previous next) nil)) (when next (setf (phpinspect--queue-item-previous next) nil))
(cond ((and (eq item first) (not next)) (cond ((and (eq item first) (not next))
(setf (phpinspect--queue-item-thing item) (setf (phpinspect--queue-item-thing item)
nil)) nil))
((eq item first) ((eq item first)
(setf (phpinspect--queue-item-thing item) (setf (phpinspect--queue-item-thing item)
(phpinspect--queue-item-thing next)) (phpinspect--queue-item-thing next))
(setf (phpinspect--queue-item-next item) (setf (phpinspect--queue-item-next item)
(phpinspect--queue-item-next next)))) (phpinspect--queue-item-next next))))
thing)) thing))
(cl-defmethod phpinspect--queue-find (cl-defmethod phpinspect--queue-find
((item phpinspect--queue-item) thing comparison-func) ((item phpinspect--queue-item) thing comparison-func)
@ -478,10 +487,10 @@ namespace if not provided"
;; and skipped, as we haven't done any intensive work that ;; and skipped, as we haven't done any intensive work that
;; may cause hangups. ;; may cause hangups.
(setq skip-pause t)) (setq skip-pause t))
(let ((type-index (phpinspect--index-type (let* ((type (phpinspect--index-task-type task))
project (root-index (phpinspect--index-type-file project type)))
(phpinspect--index-task-type task)))) (when root-index
(when type-index (phpinspect--project-add-class project type-index)))))) (phpinspect--project-add-index project root-index))))))
;; else: join with the main thread until wakeup is signaled ;; else: join with the main thread until wakeup is signaled
(thread-join main-thread)) (thread-join main-thread))
@ -535,23 +544,27 @@ namespace if not provided"
(defsubst phpinspect--make-index-task (project-root type) (defsubst phpinspect--make-index-task (project-root type)
(list project-root type)) (list project-root type))
(cl-defmethod phpinspect--index-type ((project phpinspect--project) (cl-defmethod phpinspect--index-type-file ((project phpinspect--project)
(type phpinspect--type)) (type phpinspect--type))
(let* ((class-file (with-temp-buffer (condition-case error
(cd (phpinspect--project-root project)) (let* ((class-file (with-temp-buffer
(phpinspect-type-filepath type))) (cd (phpinspect--project-root project))
(visited-buffer (when class-file (find-buffer-visiting class-file))) (phpinspect-type-filepath type)))
(new-index) (visited-buffer (when class-file (find-buffer-visiting class-file)))
(class-index)) (new-index)
(when class-file (class-index))
(if visited-buffer (when class-file
(setq new-index (with-current-buffer visited-buffer (if visited-buffer
(phpinspect--index-current-buffer))) (with-current-buffer visited-buffer (phpinspect--index-current-buffer))
(setq new-index (phpinspect-index-file class-file))) (phpinspect-index-file class-file))))
(alist-get type (alist-get 'classes new-index) (file-missing
nil (phpinspect--log "Failed to find file for type %s: %s" type error)
nil nil)))
#'phpinspect--type=))))
(defsubst phpinspect--index-thread-enqueue (task)
(phpinspect--queue-enqueue-noduplicate phpinspect--index-queue
task
#'phpinspect--index-task=))
(provide 'phpinspect-index) (provide 'phpinspect-index)
;;; phpinspect-index.el ends here ;;; phpinspect-index.el ends here

@ -44,22 +44,30 @@ indexed classes in the project")
(cl-defmethod phpinspect--project-add-return-types-to-index-queueue (cl-defmethod phpinspect--project-add-return-types-to-index-queueue
((project phpinspect--project) methods) ((project phpinspect--project) methods)
(dolist (method methods) (dolist (method methods)
(when (not (phpinspect--project-get-class project (phpinspect--function-return-type method))) (when (phpinspect--function-return-type method)
(phpinspect--queue-enqueue-noduplicate phpinspect--index-queue (phpinspect--project-enqueue-if-not-present
(phpinspect--make-index-task project
(phpinspect--project-root project) (phpinspect--function-return-type method)))))
(phpinspect--function-return-type method))
#'phpinspect--index-task=))))
(cl-defmethod phpinspect--project-add-variable-types-to-index-queue (cl-defmethod phpinspect--project-add-variable-types-to-index-queue
((project phpinspect--project) variables) ((project phpinspect--project) variables)
(dolist (var variables) (dolist (var variables)
(when (phpinspect--variable-type var) (when (phpinspect--variable-type var)
(phpinspect--queue-enqueue-noduplicate phpinspect--index-queue (phpinspect--project-enqueue-if-not-present project (phpinspect--variable-type var)))))
(phpinspect--make-index-task
(phpinspect--project-root project) (cl-defmethod phpinspect--project-enqueue-if-not-present
(phpinspect--variable-type var)) ((project phpinspect--project) (type phpinspect--type))
#'phpinspect--index-task=)))) (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--index-thread-enqueue (phpinspect--make-index-task
(phpinspect--project-root project)
type)))))
(cl-defmethod phpinspect--project-add-class-attribute-types-to-index-queue (cl-defmethod phpinspect--project-add-class-attribute-types-to-index-queue
((project phpinspect--project) (class phpinspect--class)) ((project phpinspect--project) (class phpinspect--class))
@ -73,43 +81,53 @@ indexed classes in the project")
project project
(phpinspect--class-variables class))) (phpinspect--class-variables class)))
(cl-defmethod phpinspect--project-add-index
((project phpinspect--project) (index (head phpinspect--root-index)))
(dolist (indexed-class (alist-get 'classes (cdr index)))
(phpinspect--project-add-class project (cdr indexed-class))))
(cl-defmethod phpinspect--project-enqueue-imports
((project phpinspect--project) imports)
(dolist (import imports)
(when import
(phpinspect--log "Adding import to index queue: %s" import)
(phpinspect--project-enqueue-if-not-present project (cdr import)))))
(cl-defmethod phpinspect--project-add-class (cl-defmethod phpinspect--project-add-class
((project phpinspect--project) (indexed-class (head phpinspect--indexed-class))) ((project phpinspect--project) (indexed-class (head phpinspect--indexed-class)))
(let* ((class-name (phpinspect--type-name-symbol (let* ((class-name (phpinspect--type-name-symbol
(alist-get 'class-name (cdr indexed-class)))) (alist-get 'class-name (cdr indexed-class))))
(existing-class (gethash class-name (class (gethash class-name
(phpinspect--project-class-index project)))) (phpinspect--project-class-index project))))
(if existing-class (unless class
(progn (setq class (phpinspect--make-class-generated :project project)))
(phpinspect--class-set-index existing-class indexed-class)
(phpinspect--project-add-class-attribute-types-to-index-queue (phpinspect--class-set-index class indexed-class)
project (puthash class-name class (phpinspect--project-class-index project))
existing-class)) (phpinspect--project-add-class-attribute-types-to-index-queue project class)))
(let ((new-class (phpinspect--make-class-generated :project project)))
(phpinspect--class-set-index new-class indexed-class)
(puthash class-name new-class (phpinspect--project-class-index project))
(phpinspect--project-add-class-attribute-types-to-index-queue
project
new-class)))))
(cl-defgeneric phpinspect--project-get-class (cl-defgeneric phpinspect--project-get-class
((project phpinspect--project) (class-fqn phpinspect--type)) ((project phpinspect--project) (class-fqn phpinspect--type))
"Get indexed class by name of CLASS-FQN stored in PROJECT.") "Get indexed class by name of CLASS-FQN stored in PROJECT.")
(cl-defmethod phpinspect--project-set-class
((project phpinspect--project) (class-fqn phpinspect--type) (class phpinspect--class))
(puthash (phpinspect--type-name-symbol class-fqn)
class
(phpinspect--project-class-index project)))
(cl-defmethod phpinspect--project-create-class
((project phpinspect--project) (class-fqn phpinspect--type))
(let ((class (phpinspect--make-class-generated :project project)))
(phpinspect--project-set-class project class-fqn class)
class))
(cl-defmethod phpinspect--project-get-class-create (cl-defmethod phpinspect--project-get-class-create
((project phpinspect--project) (class-fqn phpinspect--type)) ((project phpinspect--project) (class-fqn phpinspect--type))
(let ((class (phpinspect--project-get-class project class-fqn))) (let ((class (phpinspect--project-get-class project class-fqn)))
(unless class (unless class
(setq class (phpinspect--make-class-generated :project project)) (setq class (phpinspect--project-create-class project class-fqn))
(puthash (phpinspect--type-name-symbol class-fqn) (phpinspect--project-enqueue-if-not-present project class-fqn))
class
(phpinspect--project-class-index project))
(phpinspect--queue-enqueue-noduplicate
phpinspect--index-queue
(phpinspect--make-index-task (phpinspect--project-root project)
class-fqn)
#'phpinspect--index-task=))
class)) class))
(defalias 'phpinspect--project-add-class-if-missing #'phpinspect--project-get-class-create) (defalias 'phpinspect--project-add-class-if-missing #'phpinspect--project-get-class-create)

@ -122,11 +122,11 @@ NAMESPACE may be nil, or a string with a namespace FQN."
;; Clas|interface|trait name ;; Clas|interface|trait name
(t (let ((from-types (assoc-default (phpinspect-intern-name type) types #'eq))) (t (let ((from-types (assoc-default (phpinspect-intern-name type) types #'eq)))
(concat "\\" (cond (from-types (cond (from-types
(phpinspect--type-name from-types)) (phpinspect--type-name from-types))
(namespace (namespace
(concat namespace "\\" type)) (concat "\\" namespace "\\" type))
(t type))))))) (t (concat "\\" type)))))))
(cl-defmethod phpinspect--type-resolve (types namespace (type phpinspect--type)) (cl-defmethod phpinspect--type-resolve (types namespace (type phpinspect--type))
(unless (phpinspect--type-fully-qualified type) (unless (phpinspect--type-fully-qualified type)

@ -353,12 +353,13 @@ TODO:
(let* ((type-of-previous-statement (let* ((type-of-previous-statement
(phpinspect-resolve-type-from-context resolvecontext type-resolver)) (phpinspect-resolve-type-from-context resolvecontext type-resolver))
(method-name-sym (phpinspect-intern-name (cadr (cadar (last statement 2))))) (method-name-sym (phpinspect-intern-name (cadr (cadar (last statement 2)))))
(class-methods (phpinspect-get-cached-project-class-methods (class (phpinspect--project-get-class-create
(phpinspect--resolvecontext-project-root resolvecontext) (phpinspect--cache-get-project-create
type-of-previous-statement (phpinspect--get-or-create-global-cache)
static)) (phpinspect--resolvecontext-project-root resolvecontext))
(method (and class-methods type-of-previous-statement))
(phpinspect-find-function-in-list method-name class-methods)))) (method (when class
(phpinspect--class-get-method class method-name-sym))))
(phpinspect--log "Eldoc method name: %s" method-name-sym) (phpinspect--log "Eldoc method name: %s" method-name-sym)
(phpinspect--log "Eldoc type of previous statement: %s" (phpinspect--log "Eldoc type of previous statement: %s"
type-of-previous-statement) type-of-previous-statement)
@ -716,8 +717,8 @@ more recent"
(eldoc-add-command 'c-electric-paren) (eldoc-add-command 'c-electric-paren)
(eldoc-add-command 'c-electric-backspace) (eldoc-add-command 'c-electric-backspace)
(phpinspect--after-save-action)
(phpinspect--ensure-index-thread) (phpinspect--ensure-index-thread)
(phpinspect--after-save-action)
(add-hook 'after-save-hook #'phpinspect--after-save-action nil 'local)) (add-hook 'after-save-hook #'phpinspect--after-save-action nil 'local))
@ -732,10 +733,22 @@ keeps the cache valid. If changes are made outside of Emacs,
users will have to use \\[phpinspect-purge-cache]." users will have to use \\[phpinspect-purge-cache]."
(when (and (boundp 'phpinspect-mode) phpinspect-mode) (when (and (boundp 'phpinspect-mode) phpinspect-mode)
(setq phpinspect--buffer-index (phpinspect--index-current-buffer)) (setq phpinspect--buffer-index (phpinspect--index-current-buffer))
(dolist (class (alist-get 'classes phpinspect--buffer-index)) (let ((imports (alist-get 'imports phpinspect--buffer-index))
(when class (project (phpinspect--cache-get-project-create
(phpinspect-cache-project-class (phpinspect-project-root) (phpinspect--get-or-create-global-cache)
(cdr class)))))) (phpinspect-project-root))))
(dolist (class (alist-get 'classes phpinspect--buffer-index))
(when class
(phpinspect--project-add-class project (cdr class))
(let ((imports (alist-get 'imports (cdr class))))
(when imports
(phpinspect--project-enqueue-imports project imports)))))
(when imports (phpinspect--project-enqueue-imports project imports)))))
(defun phpinspect--disable-mode () (defun phpinspect--disable-mode ()
"Clean up the buffer environment for the mode to be disabled." "Clean up the buffer environment for the mode to be disabled."

Loading…
Cancel
Save