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
"A list of subscription functions that should be
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))
(dolist (sub (phpinspect--class-subscriptions class))
@ -64,6 +74,7 @@
(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
(index (head phpinspect--indexed-class)))
(setf (phpinspect--class-initial-index class) t)
(setf (phpinspect--class-index class) index)
(dolist (method (alist-get 'methods index))
(phpinspect--class-update-method class method))
@ -124,6 +135,12 @@
(when 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))
(let ((methods))
(maphash (lambda (key method)

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

@ -44,22 +44,30 @@ indexed classes in the project")
(cl-defmethod phpinspect--project-add-return-types-to-index-queueue
((project phpinspect--project) methods)
(dolist (method methods)
(when (not (phpinspect--project-get-class project (phpinspect--function-return-type method)))
(phpinspect--queue-enqueue-noduplicate phpinspect--index-queue
(phpinspect--make-index-task
(phpinspect--project-root project)
(phpinspect--function-return-type method))
#'phpinspect--index-task=))))
(when (phpinspect--function-return-type method)
(phpinspect--project-enqueue-if-not-present
project
(phpinspect--function-return-type method)))))
(cl-defmethod phpinspect--project-add-variable-types-to-index-queue
((project phpinspect--project) variables)
(dolist (var variables)
(when (phpinspect--variable-type var)
(phpinspect--queue-enqueue-noduplicate phpinspect--index-queue
(phpinspect--make-index-task
(phpinspect--project-root project)
(phpinspect--variable-type var))
#'phpinspect--index-task=))))
(phpinspect--project-enqueue-if-not-present project (phpinspect--variable-type var)))))
(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--index-thread-enqueue (phpinspect--make-index-task
(phpinspect--project-root project)
type)))))
(cl-defmethod phpinspect--project-add-class-attribute-types-to-index-queue
((project phpinspect--project) (class phpinspect--class))
@ -73,43 +81,53 @@ indexed classes in the project")
project
(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
((project phpinspect--project) (indexed-class (head phpinspect--indexed-class)))
(let* ((class-name (phpinspect--type-name-symbol
(alist-get 'class-name (cdr indexed-class))))
(existing-class (gethash class-name
(phpinspect--project-class-index project))))
(if existing-class
(progn
(phpinspect--class-set-index existing-class indexed-class)
(phpinspect--project-add-class-attribute-types-to-index-queue
project
existing-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)))))
(class (gethash class-name
(phpinspect--project-class-index project))))
(unless class
(setq class (phpinspect--make-class-generated :project project)))
(phpinspect--class-set-index class indexed-class)
(puthash class-name class (phpinspect--project-class-index project))
(phpinspect--project-add-class-attribute-types-to-index-queue project class)))
(cl-defgeneric phpinspect--project-get-class
((project phpinspect--project) (class-fqn phpinspect--type))
"Get indexed class by name of CLASS-FQN stored in PROJECT.")
(cl-defmethod phpinspect--project-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
((project phpinspect--project) (class-fqn phpinspect--type))
(let ((class (phpinspect--project-get-class project class-fqn)))
(unless class
(setq class (phpinspect--make-class-generated :project project))
(puthash (phpinspect--type-name-symbol class-fqn)
class
(phpinspect--project-class-index project))
(phpinspect--queue-enqueue-noduplicate
phpinspect--index-queue
(phpinspect--make-index-task (phpinspect--project-root project)
class-fqn)
#'phpinspect--index-task=))
(setq class (phpinspect--project-create-class project class-fqn))
(phpinspect--project-enqueue-if-not-present project class-fqn))
class))
(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
(t (let ((from-types (assoc-default (phpinspect-intern-name type) types #'eq)))
(concat "\\" (cond (from-types
(phpinspect--type-name from-types))
(namespace
(concat namespace "\\" type))
(t type)))))))
(cond (from-types
(phpinspect--type-name from-types))
(namespace
(concat "\\" namespace "\\" type))
(t (concat "\\" type)))))))
(cl-defmethod phpinspect--type-resolve (types namespace (type phpinspect--type))
(unless (phpinspect--type-fully-qualified type)

@ -353,12 +353,13 @@ TODO:
(let* ((type-of-previous-statement
(phpinspect-resolve-type-from-context resolvecontext type-resolver))
(method-name-sym (phpinspect-intern-name (cadr (cadar (last statement 2)))))
(class-methods (phpinspect-get-cached-project-class-methods
(phpinspect--resolvecontext-project-root resolvecontext)
type-of-previous-statement
static))
(method (and class-methods
(phpinspect-find-function-in-list method-name class-methods))))
(class (phpinspect--project-get-class-create
(phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
(phpinspect--resolvecontext-project-root resolvecontext))
type-of-previous-statement))
(method (when class
(phpinspect--class-get-method class method-name-sym))))
(phpinspect--log "Eldoc method name: %s" method-name-sym)
(phpinspect--log "Eldoc type of previous statement: %s"
type-of-previous-statement)
@ -716,8 +717,8 @@ more recent"
(eldoc-add-command 'c-electric-paren)
(eldoc-add-command 'c-electric-backspace)
(phpinspect--after-save-action)
(phpinspect--ensure-index-thread)
(phpinspect--after-save-action)
(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]."
(when (and (boundp 'phpinspect-mode) phpinspect-mode)
(setq phpinspect--buffer-index (phpinspect--index-current-buffer))
(dolist (class (alist-get 'classes phpinspect--buffer-index))
(when class
(phpinspect-cache-project-class (phpinspect-project-root)
(cdr class))))))
(let ((imports (alist-get 'imports phpinspect--buffer-index))
(project (phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
(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 ()
"Clean up the buffer environment for the mode to be disabled."

Loading…
Cancel
Save