diff --git a/phpinspect-class.el b/phpinspect-class.el index 7b6e12b..dba4045 100644 --- a/phpinspect-class.el +++ b/phpinspect-class.el @@ -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) diff --git a/phpinspect-index.el b/phpinspect-index.el index cfa642d..4cb4b61 100644 --- a/phpinspect-index.el +++ b/phpinspect-index.el @@ -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 diff --git a/phpinspect-project.el b/phpinspect-project.el index 313dc07..372ce96 100644 --- a/phpinspect-project.el +++ b/phpinspect-project.el @@ -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) diff --git a/phpinspect-type.el b/phpinspect-type.el index 2cfa88c..7f55d42 100644 --- a/phpinspect-type.el +++ b/phpinspect-type.el @@ -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) diff --git a/phpinspect.el b/phpinspect.el index 226288f..be0872f 100644 --- a/phpinspect.el +++ b/phpinspect.el @@ -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."