Implement stub index for builtin functions and types

Misc:
- Removed Cask in favor of dependency install script
- Rework makefile to provide simple build/install process
WIP-cache
Hugo Thunnissen 9 months ago
parent 9f7026455a
commit 81919175ca

4
.gitignore vendored

@ -1,6 +1,8 @@
*.elc
/benchmarks/profile.txt
/.cask
/.deps
/data
# ELPA-generated files
/phpinspect-autoloads.el

@ -1,4 +0,0 @@
(source gnu)
(source melpa)
(package-file "phpinspect.el")

@ -1,27 +1,49 @@
export EMACS ?= $(shell which emacs)
CASK_DIR := $(shell cask package-directory)
$(CASK_DIR): Cask
cask install
@touch $(CASK_DIR)
ELC_FILES = $(patsubst %.el, %.elc, $(shell ls -1 ./*.el ./test/*.el ./benchmarks/*.el))
DEP_DIRECTORY = $(CURDIR)/.deps
RUN_EMACS := emacs -batch -L $(CURDIR) --eval '(package-initialize)'
.PHONY: cask
cask: $(CASK_DIR)
export HOME = ${DEP_DIRECTORY}
$(CURDIR): deps
$(CURDIR):$(ELC_FILES)
$(CURDIR): ./data/builtin-stubs-index.eld.gz
./.deps: ./phpinspect.el
./.deps:
emacs -batch -l ./scripts/install-deps.el
./stubs/builtins.php: ./scripts/generate-builtin-stubs.php
mkdir -p ./stubs/
php ./scripts/generate-builtin-stubs.php > ./stubs/builtins.php
./data/builtin-stubs-index.eld.gz: ./stubs/builtins.php | ./.deps
mkdir -p ./data/
$(RUN_EMACS) -l phpinspect-cache -f phpinspect-dump-stub-index
%.elc: %.el
$(RUN_EMACS) --eval '(setq byte-compile-error-on-warn t)' -f batch-byte-compile $<
.PHONY: deps
deps: ./.deps
.PHONY: stub-index
stub-index: ./data/builtin-stubs-index.eld.gz
.PHONY: clean
clean:
rm -f $(ELC_FILES) ./stubs/builtins.php ./data/builtin-stubs-index.eld.gz
.PHONY: compile
compile: cask
compile: generate-stubs
bash ./scripts/compile.bash
compile: ./.deps
compile: $(ELC_FILES)
.PHONY: compile-native
compile-native: cask
compile-native: generate-stubs
compile-native: ./.deps
bash ./scripts/native-compile.bash
.PHONY: generate-stubs
generate-stubs: cask
php ./scripts/generate-builtin-stubs.php > ./stubs/builtins.php
.PHONY: test
test: compile
cask emacs --batch -L . -L test -l ./test/phpinspect-test.el e -f ert-run-tests-batch
test: deps
$(RUN_EMACS) -L ./test -l ./test/phpinspect-test e -f ert-run-tests-batch

@ -0,0 +1,28 @@
(require 'phpinspect-cache)
(let (result)
(message "Building and loading stub cache")
(garbage-collect)
(setq result
(benchmark-run 1 (phpinspect-build-stub-cache)))
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
(message "Building stub cache")
(garbage-collect)
(setq result
(benchmark-run 1 (phpinspect-build-stub-index)))
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
(message "Building and dumping stub cache")
(garbage-collect)
(setq result
(benchmark-run 1 (phpinspect-dump-stub-index)))
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
(message "Loading stub cache")
(garbage-collect)
(setq result
(benchmark-run 1 (phpinspect-load-stub-index)))
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result)))

@ -167,9 +167,9 @@ bareword typenames."))
(phpinspect-pipeline (phpinspect-files-list strat)
:into (funcall :with-context indexer))))
(cl-defmethod phpinspect-autoloader-put-type-bag ((al phpinspect-autoloader) (type-fqn symbol))
(cl-defmethod phpinspect-autoloader-put-type-bag ((al phpinspect-autoloader) (type-fqn (head phpinspect-name)))
(let* ((type-name (phpinspect-intern-name
(car (last (split-string (symbol-name type-fqn) "\\\\")))))
(car (last (split-string (phpinspect-name-string type-fqn) "\\\\")))))
(bag (gethash type-name (phpinspect-autoloader-type-name-fqn-bags al))))
(if bag
(push type-fqn bag)
@ -239,7 +239,7 @@ bareword typenames."))
(cl-defmethod phpinspect-autoloader-resolve ((autoloader phpinspect-autoloader)
typename-symbol)
(typename (head phpinspect-name)))
;; Wait for pending refresh if not running in main thread.
(unless (eq main-thread (current-thread))
(when (and (phpinspect-autoloader-refresh-thread autoloader)
@ -251,8 +251,8 @@ bareword typenames."))
(phpinspect--log "Autoload refresh completed, continuing waiting thread %s"
(thread-name (current-thread)))))
(or (gethash typename-symbol (phpinspect-autoloader-own-types autoloader))
(gethash typename-symbol (phpinspect-autoloader-types autoloader))))
(or (gethash typename (phpinspect-autoloader-own-types autoloader))
(gethash typename (phpinspect-autoloader-types autoloader))))
(cl-defmethod phpinspect-autoloader-refresh ((autoloader phpinspect-autoloader) &optional async-callback)
"Refresh autoload definitions by reading composer.json files

@ -63,11 +63,17 @@ emacs buffer."
(functions nil
:type phpinspect-toc)
(token-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
(project nil
:type phpinspect-project)
(-project nil
:type phpinspect-project)
(edit-tracker (phpinspect-make-edtrack)
:type phpinspect-edtrack))
(defun phpinspect-buffer-project (buffer)
(or (phpinspect-buffer--project buffer)
(with-current-buffer (phpinspect-buffer-buffer buffer)
(phpinspect--cache-get-project-create (phpinspect--get-or-create-global-cache)
(phpinspect-current-project-root)))))
(cl-defmethod phpinspect-buffer-parse ((buffer phpinspect-buffer) &optional no-interrupt)
"Parse the PHP code in the the emacs buffer that this object is
linked with."
@ -96,8 +102,6 @@ linked with."
(setq tree (phpinspect-buffer-tree buffer)))
tree))
(cl-defmethod phpinspect-buffer-get-index-for-token ((buffer phpinspect-buffer) token)
(gethash token (phpinspect-buffer-token-index buffer)))
@ -388,7 +392,8 @@ linked with."
:enclosing-metadata (list class))))
(setf (phpinspect--variable-type indexed)
(phpinspect-get-pattern-type-in-block
rctx (phpinspect--make-pattern :m `(:variable "this") :m `(:object-attrib (:word ,(cadr var))))
rctx (phpinspect--make-pattern :m `(:variable "this")
:m `(:object-attrib (:word ,(cadr (phpinspect-meta-token var)))))
(phpinspect-function-block (phpinspect--function-token constructor))
type-resolver
(phpinspect-function-argument-list (phpinspect--function-token constructor))))))
@ -400,12 +405,17 @@ linked with."
buffer (phpinspect-meta-token var)
(cons (phpinspect--class-name class-obj) indexed)))))))
(cl-defmethod phpinspect-buffer-reparse ((buffer phpinspect-buffer))
(cl-defmethod phpinspect-buffer-reset ((buffer phpinspect-buffer))
(setf (phpinspect-buffer-tree buffer) nil)
(setf (phpinspect-buffer-map buffer) (phpinspect-make-bmap))
(setf (phpinspect-buffer-declarations buffer) nil)
(setf (phpinspect-buffer-imports buffer) nil)
(phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker buffer))
(setf (phpinspect-buffer-token-index buffer)
(make-hash-table :test 'eq :size 100 :rehash-size 1.5))
(phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker buffer)))
(cl-defmethod phpinspect-buffer-reparse ((buffer phpinspect-buffer))
(phpinspect-buffer-reset buffer)
(phpinspect-buffer-parse buffer 'no-interrupt))
(cl-defmethod phpinspect-buffer-update-project-index ((buffer phpinspect-buffer))

@ -27,21 +27,110 @@
(require 'phpinspect-autoload)
(require 'phpinspect-worker)
(defcustom phpinspect-load-stubs t
"If and when phpinspect should load code stubs."
:type '(choice
(const
:tag
"Load stubs on first mode init." t)
(const
:tag
"Never load stubs." nil))
:group 'phpinspect)
(defvar phpinspect-buffers (make-hash-table :test #'eq)
"All buffers for which `phpinspect-mode' is currently active.
Hash table with buffer (native emacs buffer object, `bufferp') as
key, and a reset-function as value. The reset-function is called
without arguments when the cache is purged (see
`phpinspect-purge-cache'.")
(defun phpinspect-register-current-buffer (reset-func)
(puthash (current-buffer) reset-func phpinspect-buffers))
(defun phpinspect-unregister-current-buffer ()
(remhash (current-buffer) phpinspect-buffers))
(defvar phpinspect-stub-cache nil
"An instance of `phpinspect--cache' containing an index of PHP
functions and classes which phpinspect preloads. This index is
not supposed to be mutated after initial creation.")
(defmacro phpinspect--cache-edit (cache &rest body)
(declare (indent 1))
`(unless (phpinspect--cache-read-only-p ,cache)
,@body))
(defvar phpinspect-cache nil
"An object used to store and access metadata of PHP projects.")
(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
(read-only-p nil
:type boolean
:documentation
"Whether this cache instance is read-only, meaning that it's data
should never be changed.
When the value of this slot is non-nil:
- Actions that would normally mutate it's data should become
no-ops.
- All projects that are retrieved from it should be marked as read-only as well.")
(extra-class-retriever nil
:type lambda
:documentation
"A function that should accept a `phpinspect--type' and return
matching `phpinspect--class' instances or nil. Used to discover
classes that are defined outside of code that this cache knows about.")
(extra-function-retriever nil
:type lambda
:documentation
"A function that should accept a `phpinspect-name' (see
`phpinspect-intern-name') and return matching
`phpinspect--function' instances or nil. Used to discover
functions that are defined outside of code that this cache knows
about.")
(projects (make-hash-table :test 'equal :size 10)
:type hash-table
:documentation
"A `hash-table` with the root directories of projects
as keys and project caches as values."))
(defun phpinspect--get-stub-class (fqn)
(when phpinspect-stub-cache
(phpinspect--log "Getting stub class")
(catch 'return
(maphash (lambda (_name project)
(when-let ((class (phpinspect-project-get-class project fqn)))
(throw 'return class)))
(phpinspect--cache-projects phpinspect-stub-cache)))))
(defun phpinspect--get-stub-function (name)
(when phpinspect-stub-cache
(if name
(catch 'return
(phpinspect--log "Getting stub function by name %s" name)
(maphash (lambda (_name project)
(when-let ((class (phpinspect-project-get-function project name)))
(throw 'return class)))
(phpinspect--cache-projects phpinspect-stub-cache)))
(let* ((funcs (cons nil nil))
(funcs-rear funcs))
(phpinspect--log "Retrieving all stub functions for nil name")
(maphash (lambda (_name project)
(setq funcs-rear (last (nconc funcs-rear (phpinspect-project-get-functions project)))))
(phpinspect--cache-projects phpinspect-stub-cache))
(cdr funcs)))))
(defun phpinspect--get-or-create-global-cache ()
"Get `phpinspect-cache'.
If its value is nil, it is created and then returned."
(or phpinspect-cache
(setq phpinspect-cache (phpinspect--make-cache))))
(setq phpinspect-cache
(phpinspect--make-cache
:extra-class-retriever #'phpinspect--get-stub-class
:extra-function-retriever #'phpinspect--get-stub-function))))
(defun phpinspect-purge-cache ()
"Assign a fresh, empty cache object to `phpinspect-cache'.
@ -54,54 +143,73 @@ currently opened projects."
(phpinspect-project-purge project))
(phpinspect--cache-projects phpinspect-cache)))
(maphash (lambda (buffer reset-hook)
(with-current-buffer buffer
(funcall reset-hook)))
phpinspect-buffers)
;; Assign a fresh cache object
(setq phpinspect-cache (phpinspect--make-cache)))
(setq phpinspect-cache (phpinspect--get-or-create-global-cache))
(setq phpinspect-names (phpinspect-make-name-hash))
(phpinspect-define-standard-types))
(cl-defmethod phpinspect--cache-getproject
(cl-defmethod phpinspect--cache-get-project
((cache phpinspect--cache) (project-root string))
(gethash project-root (phpinspect--cache-projects cache)))
(let ((project (gethash project-root (phpinspect--cache-projects cache))))
(when (and project (phpinspect--cache-read-only-p cache)
(not (phpinspect-project-read-only-p project)))
(setf (phpinspect-project-read-only-p project) t))
project))
(defun phpinspect-get-or-create-cached-project-class (project-root class-fqn)
(when project-root
(let ((project (phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
project-root)))
(phpinspect-project-get-class-create project class-fqn))))
(phpinspect-project-get-class-extra-or-create project class-fqn))))
(cl-defmethod phpinspect--cache-get-project-create
((cache phpinspect--cache) (project-root string))
"Get a project that is located in PROJECT-ROOT from CACHE.
If no such project exists in the cache yet, it is created and
then returned."
(let ((project (phpinspect--cache-getproject cache project-root)))
(let ((project (phpinspect--cache-get-project 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 ((autoloader (phpinspect-make-autoloader
:fs (phpinspect-project-fs project)
:file-indexer (phpinspect-project-make-file-indexer project)
:project-root-resolver (phpinspect-project-make-root-resolver project)))) (setf (phpinspect-project-autoload project) autoloader)
(phpinspect-autoloader-refresh autoloader)
(phpinspect-project-enqueue-include-dirs project)))
(phpinspect--cache-edit cache
(setq project
(puthash project-root
(phpinspect--make-project
:fs (phpinspect-make-fs)
:root project-root
:extra-class-retriever (phpinspect--cache-extra-class-retriever cache)
:extra-function-retriever (phpinspect--cache-extra-function-retriever cache)
:worker (phpinspect-make-dynamic-worker))
(phpinspect--cache-projects cache)))
(let ((autoloader (phpinspect-make-autoloader
:fs (phpinspect-project-fs project)
:file-indexer (phpinspect-project-make-file-indexer project)
:project-root-resolver (phpinspect-project-make-root-resolver project))))
(setf (phpinspect-project-autoload project) autoloader)
(phpinspect-autoloader-refresh autoloader)
(phpinspect-project-enqueue-include-dirs project))))
project))
(defun phpinspect-project-enqueue-include-dirs (project)
(interactive (list (phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
(phpinspect-current-project-root))))
(let ((dirs (alist-get 'include-dirs
(alist-get (phpinspect-project-root project)
phpinspect-projects
nil nil #'string=))))
(dolist (dir dirs)
(message "enqueueing dir %s" dir)
(phpinspect-worker-enqueue
(phpinspect-project-worker project)
(phpinspect-make-index-dir-task :dir dir :project project)))))
(phpinspect-project-edit project
(let ((dirs (alist-get 'include-dirs
(alist-get (phpinspect-project-root project)
phpinspect-projects
nil nil #'string=))))
(dolist (dir dirs)
(message "enqueueing dir %s" dir)
(phpinspect-worker-enqueue
(phpinspect-project-worker project)
(phpinspect-make-index-dir-task :dir dir :project project))))))
(defun phpinspect-project-add-include-dir (dir)
"Configure DIR as an include dir for the current project."
@ -119,5 +227,64 @@ then returned."
(phpinspect--get-or-create-global-cache)
(phpinspect-current-project-root))))
(provide 'phpinspect-cache)
(defconst phpinspect-stub-directory
(expand-file-name "stubs" (file-name-directory (macroexp-file-name)))
"Directory where PHP stub files are located.")
(defconst phpinspect-data-directory
(expand-file-name "data" (file-name-directory (macroexp-file-name)))
"Directory for data distributed with phpinspect.")
(defconst phpinspect-stub-cache-file
(expand-file-name "builtin-stubs.eld" phpinspect-data-directory)
"")
(defconst phpinspect-builtin-index-file
(expand-file-name (concat "builtin-stubs-index.eld" (if (zlib-available-p) ".gz" ""))
phpinspect-data-directory)
"")
(defun phpinspect-build-stub-cache ()
(let* ((cache (phpinspect--make-cache))
(builtin-project (phpinspect--cache-get-project-create cache "builtins"))
(phpinspect-worker 'nil-worker))
(phpinspect-project-add-index builtin-project (phpinspect-build-stub-index))))
(defun phpinspect-build-stub-index ()
(phpinspect--index-tokens (phpinspect-parse-file (expand-file-name "builtins.php" phpinspect-stub-directory))))
(defun phpinspect-dump-stub-index ()
(interactive)
(let* ((phpinspect-names (phpinspect-make-name-hash))
(index (phpinspect-build-stub-index)))
(with-temp-buffer
(let ((print-length nil)
(print-level nil)
(print-circle t))
(prin1 (list (cons 'names phpinspect-names)
(cons 'index index))
(current-buffer))
(write-file phpinspect-builtin-index-file)))))
(defun phpinspect-load-stub-index ()
(interactive)
(unless (file-exists-p phpinspect-builtin-index-file)
(phpinspect-message "No stub index dump found, dumping stub index ...")
(phpinspect-dump-stub-index))
(let* ((data (with-temp-buffer
(insert-file-contents phpinspect-builtin-index-file)
(goto-char (point-min))
(read (current-buffer))))
(project (phpinspect--make-project :worker 'nil-worker)))
(phpinspect-purge-cache)
(setq phpinspect-names (alist-get 'names data))
(phpinspect-define-standard-types)
(setq phpinspect-stub-cache (phpinspect--make-cache))
(phpinspect-project-add-index project (alist-get 'index data))
(puthash "builtins" project (phpinspect--cache-projects phpinspect-stub-cache))
(setf (phpinspect--cache-read-only-p phpinspect-stub-cache) t)))
;;; phpinspect.el ends here
(provide 'phpinspect-cache)

@ -25,10 +25,19 @@
(cl-defstruct (phpinspect--class (:constructor phpinspect--make-class-generated))
(project nil
:type phpinspect-project
:documentaton
"The project that this class belongs to")
(class-retriever nil
:type lambda
:documentaton
"A function that returns classes for types
(should accept `phpinspect--type' as argument)")
(read-only-p nil
:type boolean
:documentation
"Whether this class instance is read-only, meaning that its data
should never be changed. Methods and functions that are meant to
manipulate class data should become no-ops when this slot has a
non-nil value.")
(index nil
:type phpinspect--indexed-class
:documentation
@ -65,4 +74,5 @@
"A boolean indicating whether or not this class
has been indexed yet."))
(provide 'phpinspect-class-struct)

@ -24,85 +24,55 @@
;;; Code:
(require 'phpinspect-type)
(require 'phpinspect-class-struct)
(cl-defstruct (phpinspect--class (:constructor phpinspect--make-class-generated))
(class-retriever nil
:type lambda
:documentaton
"A function that returns classes for types
(should accept `phpinspect--type' as argument)")
(index nil
:type phpinspect--indexed-class
:documentation
"The index that this class is derived from")
(methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
:type hash-table
:documentation
"All methods, including those from extended classes.")
(static-methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
:type hash-table
:documentation
"All static methods this class provides,
including those from extended classes.")
(name nil
:type phpinspect--type)
(variables nil
:type list
:documentation
"Variables that belong to this class.")
(extended-classes nil
:type list
:documentation
"All extended/implemented classes.")
(subscriptions (make-hash-table :test #'eq :size 10 :rehash-size 1.5)
:type hash-table
:documentation
"A list of subscription functions that should be
called whenever anything about this class is
updated")
(declaration nil)
(initial-index nil
:type bool
:documentation
"A boolean indicating whether or not this class
has been indexed yet."))
(defmacro phpinspect--class-edit (class &rest body)
"Declare intent to edit CLASS in BODY.
Conditionally executes BODY depending on
`phpinspect--class-read-only-p' value."
(declare (indent 1))
`(unless (phpinspect--class-read-only-p ,class)
,@body))
(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
(dolist (sub (hash-table-values (phpinspect--class-subscriptions class)))
(funcall sub class)))
(cl-defmethod phpinspect--class-update-extensions ((class phpinspect--class) extensions)
(setf (phpinspect--class-extended-classes class)
(seq-filter
#'phpinspect--class-p
(mapcar
(lambda (class-name)
(funcall (phpinspect--class-class-retriever class) class-name))
extensions)))
(phpinspect--class-edit class
(setf (phpinspect--class-extended-classes class)
(seq-filter
#'phpinspect--class-p
(mapcar
(lambda (class-name)
(funcall (phpinspect--class-class-retriever class) class-name))
extensions)))
(dolist (extended (phpinspect--class-extended-classes class))
(phpinspect--class-incorporate class extended)))
(dolist (extended (phpinspect--class-extended-classes class))
(phpinspect--class-incorporate class extended))))
(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
(index (head phpinspect--indexed-class)))
(setf (phpinspect--class-declaration class) (alist-get 'declaration index))
(setf (phpinspect--class-name class) (alist-get 'class-name index))
(phpinspect--class-edit class
(setf (phpinspect--class-declaration class) (alist-get 'declaration index))
(setf (phpinspect--class-name class) (alist-get 'class-name index))
;; Override methods when class seems syntactically correct (has balanced braces)
(when (alist-get 'complete index)
(let ((methods (phpinspect--class-methods class))
(static-methods (phpinspect--class-static-methods class)))
;; Override methods when class seems syntactically correct (has balanced braces)
(when (alist-get 'complete index)
(let ((methods (phpinspect--class-methods class))
(static-methods (phpinspect--class-static-methods class)))
(dolist (method (hash-table-values methods))
(unless (phpinspect--function--inherited method)
(remhash (phpinspect--function-name-symbol method) methods)))
(dolist (method (hash-table-values static-methods))
(unless (phpinspect--function--inherited method)
(remhash (phpinspect--function-name-symbol method) static-methods)))))
(dolist (method (hash-table-values methods))
(unless (phpinspect--function--inherited method)
(remhash (phpinspect--function-name-symbol method) methods)))
(dolist (method (hash-table-values static-methods))
(unless (phpinspect--function--inherited method)
(remhash (phpinspect--function-name-symbol method) static-methods)))))
(setf (phpinspect--class-initial-index class) t)
(setf (phpinspect--class-index class) index)
(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))
@ -118,22 +88,23 @@
(phpinspect--class-update-extensions
class `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))
(phpinspect--class-trigger-update class))
(phpinspect--class-trigger-update class)))
(cl-defmethod phpinspect--class-update-declaration
((class phpinspect--class) declaration imports namespace-name)
(pcase-let ((`(,class-name ,extends ,implements ,_used-types)
(phpinspect--index-class-declaration
declaration (phpinspect--make-type-resolver
(phpinspect--uses-to-types imports) nil namespace-name))))
(setf (phpinspect--class-name class) class-name)
(setf (phpinspect--class-declaration class) declaration)
(phpinspect--class-update-extensions class `(,@extends ,@implements))))
(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) (method-name symbol))
(phpinspect--class-edit class
(pcase-let ((`(,class-name ,extends ,implements ,_used-types)
(phpinspect--index-class-declaration
declaration (phpinspect--make-type-resolver
(phpinspect--uses-to-types imports) nil namespace-name))))
(setf (phpinspect--class-name class) class-name)
(setf (phpinspect--class-declaration class) declaration)
(phpinspect--class-update-extensions class `(,@extends ,@implements)))))
(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) (method-name (head phpinspect-name)))
(gethash method-name (phpinspect--class-methods class)))
(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) (method-name symbol))
(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) (method-name (head phpinspect-name)))
(gethash method-name (phpinspect--class-static-methods class)))
(cl-defmethod phpinspect--class-get-variable
@ -145,13 +116,15 @@
(cl-defmethod phpinspect--class-set-variable ((class phpinspect--class)
(var phpinspect--variable))
(push var (phpinspect--class-variables class)))
(phpinspect--class-edit class
(push var (phpinspect--class-variables class))))
(cl-defmethod phpinspect--class-delete-variable ((class phpinspect--class)
(var phpinspect--variable))
(setf (phpinspect--class-variables class)
(seq-filter (lambda (clvar) (not (eq var clvar)))
(phpinspect--class-variables class))))
(phpinspect--class-edit class
(setf (phpinspect--class-variables class)
(seq-filter (lambda (clvar) (not (eq var clvar)))
(phpinspect--class-variables class)))))
(cl-defmethod phpinspect--class-get-variables ((class phpinspect--class))
(seq-filter #'phpinspect--variable-vanilla-p (phpinspect--class-variables class)))
@ -179,34 +152,35 @@
(cl-defmethod phpinspect--class-set-method ((class phpinspect--class)
(method phpinspect--function))
(phpinspect--log "Adding method by name %s to class"
(phpinspect--function-name method))
(phpinspect--add-method-copy-to-map
(phpinspect--class-methods class)
(phpinspect--class-name class)
method))
(phpinspect--class-edit class
(phpinspect--log "Adding method by name %s to class"
(phpinspect--function-name method))
(phpinspect--add-method-copy-to-map
(phpinspect--class-methods class)
(phpinspect--class-name class)
method)))
(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
(method phpinspect--function))
(phpinspect--add-method-copy-to-map
(phpinspect--class-static-methods class)
(phpinspect--class-name class)
method))
(phpinspect--class-edit class
(phpinspect--add-method-copy-to-map
(phpinspect--class-static-methods class)
(phpinspect--class-name class)
method)))
(cl-defmethod phpinspect--class-delete-method ((class phpinspect--class) (method phpinspect--function))
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-static-methods class))
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-methods class)))
(phpinspect--class-edit class
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-static-methods class))
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-methods class))))
(cl-defmethod phpinspect--class-get-method-return-type
((class phpinspect--class) (method-name symbol))
((class phpinspect--class) (method-name (head phpinspect-name)))
(let ((method (phpinspect--class-get-method class method-name)))
(when method
(phpinspect--function-return-type method))))
(cl-defmethod phpinspect--class-get-static-method-return-type
((class phpinspect--class) (method-name symbol))
((class phpinspect--class) (method-name (head phpinspect-name)))
(let ((method (phpinspect--class-get-static-method class method-name)))
(when method
(phpinspect--function-return-type method))))
@ -240,49 +214,54 @@
(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
(method phpinspect--function)
&optional extended)
(let ((existing (gethash (phpinspect--function-name-symbol method)
(phpinspect--class-static-methods class))))
(if existing
(phpinspect--merge-method
(alist-get 'class-name (phpinspect--class-index class))
existing method extended)
(setf (phpinspect--function--inherited method) extended)
(phpinspect--class-set-static-method class method))))
(phpinspect--class-edit class
(let ((existing (gethash (phpinspect--function-name-symbol method)
(phpinspect--class-static-methods class))))
(if existing
(phpinspect--merge-method
(alist-get 'class-name (phpinspect--class-index class))
existing method extended)
(setf (phpinspect--function--inherited method) extended)
(phpinspect--class-set-static-method class method)))))
(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
(method phpinspect--function)
&optional extended)
(let* ((existing (gethash (phpinspect--function-name-symbol method)
(phpinspect--class-methods class))))
(phpinspect--class-edit class
(let* ((existing (gethash (phpinspect--function-name-symbol method)
(phpinspect--class-methods class))))
(if existing
(phpinspect--merge-method
(alist-get 'class-name (phpinspect--class-index class))
existing method extended)
(setf (phpinspect--function--inherited method) extended)
(phpinspect--class-set-method class method))))
(if existing
(phpinspect--merge-method
(alist-get 'class-name (phpinspect--class-index class))
existing method extended)
(setf (phpinspect--function--inherited method) extended)
(phpinspect--class-set-method class method)))))
;; FIXME: Remove inherited methods when they no longer exist in parent classes
;; (and/or the current class in the case of abstract methods).
(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
(other-class phpinspect--class))
(dolist (method (phpinspect--class-get-method-list other-class))
(phpinspect--class-update-method class method 'extended))
(phpinspect--class-edit class
(dolist (method (phpinspect--class-get-method-list other-class))
(phpinspect--class-update-method class method 'extended))
(dolist (method (phpinspect--class-get-static-method-list other-class))
(phpinspect--class-update-static-method class method 'extended))
(dolist (method (phpinspect--class-get-static-method-list other-class))
(phpinspect--class-update-static-method class method 'extended))
(phpinspect--class-subscribe class other-class))
(phpinspect--class-subscribe class other-class)))
(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
(subscription-class phpinspect--class))
(unless (gethash subscription-class (phpinspect--class-subscriptions class))
(let ((update-function
(lambda (new-class)
(phpinspect--class-incorporate class new-class)
(phpinspect--class-trigger-update class))))
(puthash subscription-class update-function
(phpinspect--class-subscriptions subscription-class)))))
(phpinspect--class-edit class
(unless (gethash subscription-class (phpinspect--class-subscriptions class))
(let ((update-function
(lambda (new-class)
(phpinspect--class-edit class
(phpinspect--class-incorporate class new-class)
(phpinspect--class-trigger-update class)))))
(puthash subscription-class update-function
(phpinspect--class-subscriptions subscription-class))))))
(provide 'phpinspect-class)
;;; phpinspect-class.el ends here

@ -23,6 +23,8 @@
;;; Code:
(require 'obarray)
(require 'phpinspect-bmap)
(require 'phpinspect-buffer)
(require 'phpinspect-resolvecontext)

@ -28,6 +28,9 @@
(require 'phpinspect-resolve)
(require 'phpinspect-buffer)
(eval-when-compile
(phpinspect--declare-log-group 'eldoc))
(defvar phpinspect-eldoc-word-width 14
"The maximum width of words in eldoc strings.")
@ -65,7 +68,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'")
(setq type-before (phpinspect-resolve-type-from-context rctx))
(when type-before
(let ((class (phpinspect-project-get-class-create
(let ((class (phpinspect-project-get-class-extra-or-create
(phpinspect--resolvecontext-project rctx)
type-before))
(attribute-name (cadadr attrib))
@ -167,15 +170,18 @@ be implemented for return values of `phpinspect-eld-strategy-execute'")
(setf (phpinspect--resolvecontext-subject rctx)
(mapcar #'phpinspect-meta-token (butlast statement 2)))
(when-let* ((type-of-previous-statement
(phpinspect-resolve-type-from-context rctx))
(method-name-sym (phpinspect-intern-name (cadadr (phpinspect-meta-token (car match-result)))))
(class (phpinspect-project-get-class-create
(class (phpinspect-project-get-class-extra-or-create
(phpinspect--resolvecontext-project rctx)
type-of-previous-statement))
(method (if static
(phpinspect--class-get-static-method class method-name-sym)
(phpinspect--class-get-method class method-name-sym))))
(when method
(phpinspect-make-function-doc :fn method :arg-pos arg-pos))))
((setq match-result (phpinspect--match-sequence (last statement 2)
@ -191,7 +197,7 @@ be implemented for return values of `phpinspect-eld-strategy-execute'")
count))
(phpinspect-meta-find-children-before arg-list (phpinspect-eldoc-query-point q)) 0))
(let ((func (phpinspect-project-get-function
(let ((func (phpinspect-project-get-function-or-extra
(phpinspect--resolvecontext-project rctx)
(phpinspect-intern-name (cadr (phpinspect-meta-token (car match-result)))))))
(phpinspect--log "Got past that")

@ -100,7 +100,7 @@ buffer position to insert the use statement at."
(let ((fqns (gethash typename fqn-bags)))
(cond ((= 1 (length fqns))
(phpinspect-add-use (symbol-name (car fqns)) buffer namespace-token))
(phpinspect-add-use (phpinspect-name-string (car fqns)) buffer namespace-token))
((> (length fqns) 1)
(phpinspect-add-use (completing-read "Class: " fqns)
buffer namespace-token))
@ -122,7 +122,7 @@ buffer position to insert the use statement at."
;; with a fully qualified name.
(unless (or (or (alist-get type imports))
(gethash (phpinspect-intern-name
(concat namespace-name "\\" (symbol-name type)))
(concat namespace-name "\\" (phpinspect-name-string type)))
(phpinspect-autoloader-types
(phpinspect-project-autoload project))))
(phpinspect-add-use-interactive type buffer project namespace)

@ -26,8 +26,31 @@
(eval-when-compile
(declare-function phpinspect-make-dynamic-worker "phpinspect-worker.el"))
(cl-defstruct (phpinspect-project (:constructor phpinspect--make-project))
(read-only-p nil
:type boolean
:documentation
"Whether this project instance is read-only, meaning that its data
should never be changed.
When this slot has a non-nil value:
- Methods and functions that are meant to manipulate class data
should become no-ops.
- All classes retrieved from it should be marked as read-only as well.")
(extra-class-retriever nil
:type lambda
:documentation
"A function that should accept a `phpinspect--type' and return
matching `phpinspect--class' instances or nil. Used to discover
classes that are defined outside of project code.")
(extra-function-retriever nil
:type lambda
:documentation
"A function that should accept a `phpinspect-name' (see
`phpinspect-intern-name') and return matching `phpinspect--function'
instances or nil. Used to discover functions that are defined
outside of project code.")
(class-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5)
:type hash-table
:documentation

@ -43,13 +43,17 @@ serious performance hits. Enable at your own risk (:")
(defvar-local phpinspect--buffer-project nil
"The root directory of the PHP project that this buffer belongs to")
(defmacro phpinspect-project-edit (project &rest body)
(declare (indent 1))
`(unless (phpinspect-project-read-only-p ,project)
,@body))
(defsubst phpinspect-current-project-root ()
"Call `phpinspect-project-root-function' with ARGS as arguments."
(unless (and (boundp 'phpinspect--buffer-project) phpinspect--buffer-project)
(set (make-local-variable 'phpinspect--buffer-project) (funcall phpinspect-project-root-function)))
phpinspect--buffer-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))
@ -62,72 +66,87 @@ serious performance hits. Enable at your own risk (:")
(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))))
(phpinspect-project-edit project
(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)
(when (phpinspect--function-return-type method)
(phpinspect-project-enqueue-if-not-present
project
(phpinspect--function-return-type method)))))
(phpinspect-project-edit project
(dolist (method methods)
(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-project-enqueue-if-not-present project (phpinspect--variable-type var)))))
(phpinspect-project-edit project
(dolist (var variables)
(when (phpinspect--variable-type var)
(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))
(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)))
(unless (or (phpinspect--type= phpinspect--null-type type)
(phpinspect--type-is-native type))
(phpinspect--log "Adding unpresent class %s to index queue" type)
(phpinspect-worker-enqueue (phpinspect-project-worker project)
(phpinspect-make-index-task project type)))))))
(phpinspect-project-edit project
(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)))
(unless (or (phpinspect--type= phpinspect--null-type type)
(phpinspect--type-is-native 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))
(phpinspect-project-add-return-types-to-index-queueue
project
(phpinspect--class-get-method-list class))
(phpinspect-project-add-return-types-to-index-queueue
project
(phpinspect--class-get-static-method-list class))
(phpinspect-project-add-variable-types-to-index-queue
project
(phpinspect--class-variables class)))
(phpinspect-project-edit project
(phpinspect-project-add-return-types-to-index-queueue
project
(phpinspect--class-get-method-list class))
(phpinspect-project-add-return-types-to-index-queueue
project
(phpinspect--class-get-static-method-list class))
(phpinspect-project-add-variable-types-to-index-queue
project
(phpinspect--class-variables class))))
(cl-defmethod phpinspect-project-add-index
((project phpinspect-project) (index (head phpinspect--root-index)) &optional index-imports)
(when index-imports
(phpinspect-project-enqueue-imports project (alist-get 'imports (cdr index))))
(phpinspect-project-edit project
(when index-imports
(phpinspect-project-enqueue-imports project (alist-get 'imports (cdr index))))
(dolist (indexed-class (alist-get 'classes (cdr index)))
(phpinspect-project-add-class project (cdr indexed-class) index-imports))
(dolist (indexed-class (alist-get 'classes (cdr index)))
(phpinspect-project-add-class project (cdr indexed-class) index-imports))
(dolist (func (alist-get 'functions (cdr index)))
(phpinspect-project-set-function project func)))
(dolist (func (alist-get 'functions (cdr index)))
(phpinspect-project-set-function project func))))
(cl-defmethod phpinspect-project-set-function
((project phpinspect-project) (func phpinspect--function))
(puthash (phpinspect--function-name-symbol func) func
(phpinspect-project-function-index project)))
(phpinspect-project-edit project
(puthash (phpinspect--function-name-symbol func) func
(phpinspect-project-function-index project))))
(cl-defmethod phpinspect-project-get-function
((project phpinspect-project) (name symbol))
((project phpinspect-project) (name (head phpinspect-name)))
(gethash name (phpinspect-project-function-index project)))
(cl-defmethod phpinspect-project-get-function-or-extra
((project phpinspect-project) (name (head phpinspect-name)))
(or (phpinspect-project-get-function project name)
(and (phpinspect-project-extra-function-retriever project)
(funcall (phpinspect-project-extra-function-retriever project)
name))))
(cl-defmethod phpinspect-project-delete-function
((project phpinspect-project) (name symbol))
(remhash name (phpinspect-project-function-index project)))
((project phpinspect-project) (name (head phpinspect-name)))
(phpinspect-project-edit project
(remhash name (phpinspect-project-function-index project))))
(cl-defmethod phpinspect-project-get-functions ((project phpinspect-project))
(let ((funcs))
@ -137,69 +156,102 @@ serious performance hits. Enable at your own risk (:")
funcs))
(cl-defmethod phpinspect-project-get-functions-with-extra ((project phpinspect-project))
(let ((funcs))
(maphash
(lambda (_name func) (push func funcs))
(phpinspect-project-function-index project))
(if (phpinspect-project-extra-function-retriever project)
(nconc funcs (funcall (phpinspect-project-extra-function-retriever project) nil))
funcs)))
(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)))))
(phpinspect-project-edit project
(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-delete-class ((project phpinspect-project) (class phpinspect--class))
(phpinspect-project-delete-class project (phpinspect--class-name class)))
(cl-defmethod phpinspect-project-delete-class ((project phpinspect-project) (class-name phpinspect--type))
(remhash (phpinspect--type-name-symbol class-name) (phpinspect-project-class-index project)))
(phpinspect-project-edit project
(remhash (phpinspect--type-name-symbol class-name) (phpinspect-project-class-index project))))
(cl-defmethod phpinspect-project-add-class
((project phpinspect-project) (indexed-class (head phpinspect--indexed-class)) &optional index-imports)
(if (not (alist-get 'class-name (cdr indexed-class)))
(phpinspect--log "Error: Class with declaration %s does not have a name" (alist-get 'declaration indexed-class))
;; Else
(let* ((class-name (phpinspect--type-name-symbol
(alist-get 'class-name (cdr indexed-class))))
(class (gethash class-name
(phpinspect-project-class-index project))))
(unless class
(setq class (phpinspect--make-class-generated
:class-retriever (phpinspect-project-make-class-retriever project))))
(when index-imports
(phpinspect-project-enqueue-imports
project (alist-get 'imports (cdr indexed-class))))
(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))))
(phpinspect-project-edit project
(if (not (alist-get 'class-name (cdr indexed-class)))
(phpinspect--log "Error: Class with declaration %s does not have a name" (alist-get 'declaration indexed-class))
;; Else
(let* ((class-name (phpinspect--type-name-symbol
(alist-get 'class-name (cdr indexed-class))))
(class (gethash class-name
(phpinspect-project-class-index project))))
(unless class
(setq class (phpinspect--make-class-generated
:class-retriever (phpinspect-project-make-class-retriever project))))
(when index-imports
(phpinspect-project-enqueue-imports
project (alist-get 'imports (cdr indexed-class))))
(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-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)))
(phpinspect-project-edit project
(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
:class-retriever (phpinspect-project-make-class-retriever project))))
(phpinspect-project-set-class project class-fqn class)
class))
(phpinspect-project-edit project
(let ((class (phpinspect--make-class-generated
:class-retriever (phpinspect-project-make-class-retriever project))))
(phpinspect-project-set-class project class-fqn class)
class)))
(cl-defmethod phpinspect-project-get-class-create
((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue)
(let ((class (phpinspect-project-get-class project class-fqn)))
(unless class
(setq class (phpinspect-project-create-class project class-fqn))
(unless no-enqueue
(phpinspect-project-enqueue-if-not-present project class-fqn)))
(phpinspect-project-edit project
(setq class (phpinspect-project-create-class project class-fqn))
(unless no-enqueue
(phpinspect-project-enqueue-if-not-present project class-fqn))))
class))
(cl-defmethod phpinspect-project-get-class-extra-or-create
((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue)
(or (phpinspect-project-get-class-or-extra project class-fqn)
(phpinspect-project-get-class-create project class-fqn no-enqueue)))
(defalias 'phpinspect-project-add-class-if-missing #'phpinspect-project-get-class-create)
(cl-defmethod phpinspect-project-get-class
((project phpinspect-project) (class-fqn phpinspect--type))
"Get indexed class by name of CLASS-FQN stored in PROJECT."
(gethash (phpinspect--type-name-symbol class-fqn)
(phpinspect-project-class-index project)))
(let ((class (gethash (phpinspect--type-name-symbol class-fqn)
(phpinspect-project-class-index project))))
(when (and class (phpinspect-project-read-only-p project)
(not (phpinspect--class-read-only-p class)))
(setf (phpinspect--class-read-only-p class) t))
class))
(cl-defmethod phpinspect-project-get-class-or-extra
((project phpinspect-project) (class-fqn phpinspect--type))
(or (phpinspect-project-get-class project class-fqn)
(and (phpinspect-project-extra-class-retriever project)
(funcall (phpinspect-project-extra-class-retriever project)
class-fqn))))
(cl-defmethod phpinspect-project-get-type-filepath
((project phpinspect-project) (type phpinspect--type) &optional index-new)
@ -209,7 +261,8 @@ when INDEX-NEW is non-nil, new files are added to the index
before the search is executed."
(let* ((autoloader (phpinspect-project-autoload project)))
(when (eq index-new 'index-new)
(phpinspect-autoloader-refresh autoloader))
(phpinspect-project-edit project
(phpinspect-autoloader-refresh autoloader)))
(let* ((result (phpinspect-autoloader-resolve
autoloader (phpinspect--type-name-symbol type))))
(if (not result)
@ -263,7 +316,9 @@ before the search is executed."
(lambda () (phpinspect-project-root project)))
(defun phpinspect-project-make-class-retriever (project)
(lambda (type) (phpinspect-project-get-class-create project type)))
(lambda (type)
(or (phpinspect-project-get-class-or-extra project type)
(phpinspect-project-get-class-create project type))))
;;; INDEX TASK
(cl-defstruct (phpinspect-index-task

@ -124,7 +124,7 @@
;; object through global variables.
(defsubst phpinspect-get-cached-project-class (project-root class-fqn)
(when project-root
(phpinspect-project-get-class
(phpinspect-project-get-class-or-extra
(phpinspect--cache-get-project-create (phpinspect--get-or-create-global-cache)
project-root)
class-fqn)))
@ -143,16 +143,6 @@
(phpinspect--class-get-static-method-list class)
(phpinspect--class-get-method-list class))))))
(defmacro phpinspect-find-function-in-list (method-name list)
(let ((break-sym (gensym))
(method-name-sym (gensym)))
`(let ((,method-name-sym (phpinspect-intern-name ,method-name)))
(catch (quote ,break-sym)
(dolist (func ,list)
(when (eq (phpinspect--function-name-symbol func)
,method-name-sym)
(throw (quote ,break-sym) func)))))))
(defsubst phpinspect-get-cached-project-class-method-type
(project-root class-fqn method-name)
(when project-root

@ -26,6 +26,9 @@
(require 'phpinspect-type)
(require 'phpinspect-class)
(cl-defgeneric phpinspect--serialize-type (_type)
nil)
(cl-defmethod phpinspect--serialize-type ((type phpinspect--type))
`(phpinspect--make-type
:name ,(phpinspect--type-name type)
@ -34,6 +37,9 @@
(phpinspect--serialize-type (phpinspect--type-contains type)))
:fully-qualified ,(phpinspect--type-fully-qualified type)))
;; (cl-defmethod phpinspect--serialize-function (_func)
;; nil)
(cl-defmethod phpinspect--serialize-function ((func phpinspect--function))
`(phpinspect--make-function
:name ,(phpinspect--function-name func)

@ -32,7 +32,7 @@
(defun phpinspect-suggest-functions (rctx)
(let* ((project (phpinspect--resolvecontext-project rctx)))
(phpinspect-project-get-functions project)))
(phpinspect-project-get-functions-with-extra project)))
(defun phpinspect-suggest-variables-at-point (resolvecontext)
(phpinspect--log "Suggesting variables at point")
@ -72,9 +72,12 @@
(let ((class (phpinspect-get-or-create-cached-project-class
project-root
class-fqn)))
(phpinspect--log (if class
"Retrieved class index, starting method collection %s (%s)"
"No class index found in %s for %s")
project-root class-fqn)
(when class
(phpinspect--log "Retrieved class index, starting method collection %s (%s)"
project-root class-fqn)
(if static
(phpinspect--class-get-static-method-list class)
(phpinspect--class-get-method-list class))))))

@ -29,7 +29,6 @@
(eval-when-compile
(require 'phpinspect-parser))
(cl-defstruct (phpinspect--type
(:constructor phpinspect--make-type-generated)
(:copier phpinspect--copy-type))
@ -82,6 +81,18 @@ that the collection is expected to contain")
(defconst phpinspect--this-type (phpinspect--make-type :name "\\this" :fully-qualified t))
(defconst phpinspect--null-type (phpinspect--make-type :name "\\null" :fully-qualified t))
(defun phpinspect-define-standard-types ()
(setq phpinspect-native-types
(phpinspect--make-types (mapcar (lambda (name) (concat "\\" name))
phpinspect-native-typenames))
phpinspect-collection-types (phpinspect--make-types
'("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed"))
phpinspect--object-type (phpinspect--make-type :name "\\object" :fully-qualified t)
phpinspect--static-type (phpinspect--make-type :name "\\static" :fully-qualified t)
phpinspect--self-type (phpinspect--make-type :name "\\self" :fully-qualified t)
phpinspect--this-type (phpinspect--make-type :name "\\this" :fully-qualified t)
phpinspect--null-type (phpinspect--make-type :name "\\null" :fully-qualified t)))
(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
(setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
@ -112,7 +123,7 @@ See https://wiki.php.net/rfc/static_return_type ."
(cl-defmethod phpinspect--type-name ((type phpinspect--type))
(symbol-name (phpinspect--type-name-symbol type)))
(phpinspect-name-string (phpinspect--type-name-symbol type)))
(defun phpinspect--get-bare-class-name-from-fqn (fqn)
(car (last (split-string fqn "\\\\"))))
@ -242,10 +253,10 @@ return type of the function."))
,@(phpinspect--wrap-plist-name-in-symbol property-list)))
(cl-defmethod phpinspect--function-set-name ((func phpinspect--function) (name string))
(setf (phpinspect--function-name-symbol func) (intern name phpinspect-name-obarray)))
(setf (phpinspect--function-name-symbol func) (intern name phpinspect-names)))
(define-inline phpinspect--function-name (func)
(inline-quote (symbol-name (phpinspect--function-name-symbol ,func))))
(inline-quote (phpinspect-name-string (phpinspect--function-name-symbol ,func))))
(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
"A PHP Variable."

@ -23,9 +23,15 @@
;;; Code:
(defvar phpinspect-name-obarray (obarray-make)
"An obarray containing symbols for all encountered names in
PHP. Used to optimize string comparison.")
(defvar phpinspect-names (make-hash-table :test #'equal :size 5000 :rehash-size 1.2)
"An hash-table containing cons cells representing encountered names in
PHP code. Used to optimize string comparison. See also `phpinspect-indern-name'")
(defun phpinspect-make-name-hash ()
(make-hash-table :test #'equal :size 5000 :rehash-size 1.2))
(define-inline phpinspect-name-string (name)
(inline-quote (cdr ,name)))
(defvar phpinspect-project-root-file-list
'("composer.json" "composer.lock" ".git" ".svn" ".hg")
@ -34,6 +40,11 @@ PHP. Used to optimize string comparison.")
(defvar phpinspect--debug nil
"Enable debug logs for phpinspect by setting this variable to true")
(defun phpinspect-message (&rest args)
(let ((format-string (car args))
(args (cdr args)))
(apply #'message `(,(concat "[phpinspect] " format-string) ,@args))))
(defun phpinspect-toggle-logging ()
(interactive)
(if (setq phpinspect--debug (not phpinspect--debug))
@ -112,8 +123,10 @@ level of START-FILE in stead of `default-directory`."
(string= parent-without-vendor "")))
(phpinspect--find-project-root parent-without-vendor))))))))
(defsubst phpinspect-intern-name (name)
(intern name phpinspect-name-obarray))
(defun phpinspect-intern-name (name)
(setq name (cons 'phpinspect-name name))
(or (gethash name phpinspect-names)
(puthash name name phpinspect-names)))
(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
(let ((new-plist)

@ -31,6 +31,9 @@
(require 'phpinspect-queue)
(require 'phpinspect-pipeline)
(eval-when-compile
(phpinspect--declare-log-group 'worker))
(defcustom phpinspect-worker-pause-time 1
"Number of seconds that `phpinspect-worker' should pause when
user input is detected. A higher value means better
@ -93,6 +96,7 @@ on the worker independent of dynamic variables during testing.")
(cl-defmethod phpinspect-worker-wakeup ((worker phpinspect-worker))
(when (eq main-thread (thread--blocker (phpinspect-worker-thread worker)))
(phpinspect--log "Attempting to wakeup worker thread")
(thread-signal (phpinspect-worker-thread worker)
'phpinspect-wakeup-thread nil)))
@ -120,6 +124,7 @@ on the worker independent of dynamic variables during testing.")
"Specialized enqueuement method for index tasks. Prevents
indexation tasks from being added when there are identical tasks
already present in the queue."
(phpinspect--log "Enqueuing task")
(phpinspect-queue-enqueue-noduplicate (phpinspect-worker-queue worker) task #'phpinspect-task=))
(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-dynamic-worker) task)
@ -135,15 +140,19 @@ already present in the queue."
;; queue.
(condition-case err
(ignore-error phpinspect-wakeup-thread
(phpinspect--log "Dequeueing next task")
(let* ((task (phpinspect-queue-dequeue (phpinspect-worker-queue worker)))
(mx (make-mutex))
(continue (make-condition-variable mx)))
(if task
;; 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))
(if (phpinspect-project-purged (phpinspect-task-project task))
(phpinspect--log "Projecthas been purged. Skipping task")
(phpinspect--log "Executing task")
(phpinspect-task-execute task worker))
;; else: join with the main thread until wakeup is signaled
(phpinspect--log "No tasks, joining main thread")
(thread-join main-thread))
;; Pause for a second after indexing something, to allow user input to
@ -225,5 +234,8 @@ already present in the queue."
(cl-defgeneric phpinspect-task-project (task)
"The project that this task belongs to.")
(cl-defmethod phpinspect-worker-enqueue ((_worker (eql 'nil-worker)) &rest _ignored))
(cl-defmethod phpinspect-worker-live-p ((_worker (eql 'nil-worker)) &rest _ignored) t)
(provide 'phpinspect-worker)
;;; phpinspect-worker.el ends here

@ -28,7 +28,6 @@
(require 'cl-lib)
(require 'json)
(require 'obarray)
;; internal dependencies
(require 'phpinspect-cache)
@ -47,10 +46,6 @@
(require 'phpinspect-suggest)
(require 'phpinspect-completion)
(defvar-local phpinspect--buffer-index nil
"The result of the last successfull parse + index action
executed by phpinspect for the current buffer")
(defvar phpinspect-insert-file-contents-function #'insert-file-contents-literally
"Function that phpinspect uses to insert file contents into a buffer.")
@ -82,12 +77,16 @@
(defun phpinspect--init-mode ()
"Initialize the phpinspect minor mode for the current buffer."
(phpinspect-ensure-worker)
(when (and phpinspect-load-stubs (not phpinspect-stub-cache))
(phpinspect-load-stub-index))
(setq phpinspect-current-buffer
(phpinspect-make-buffer
:buffer (current-buffer)
:project (phpinspect--cache-get-project-create
(phpinspect--get-or-create-global-cache)
(phpinspect-current-project-root))))
(phpinspect-make-buffer :buffer (current-buffer)))
(phpinspect-register-current-buffer
(lambda () (phpinspect-buffer-reset phpinspect-current-buffer)))
(add-hook 'kill-buffer-hook #'phpinspect-unregister-current-buffer)
(add-hook 'after-change-functions #'phpinspect-after-change-function)
(when (featurep 'company)
@ -121,7 +120,8 @@ Reparses the entire buffer without token reuse."
(kill-local-variable 'phpinspect--buffer-project)
(kill-local-variable 'company-backends)
(kill-local-variable 'eldoc-documentation-function)
(kill-local-variable 'eldoc-message-commands))
(kill-local-variable 'eldoc-message-commands)
(phpinspect-unregister-current-buffer))
(defun phpinspect--mode-function ()
(if (and (boundp 'phpinspect-mode) phpinspect-mode)
@ -305,7 +305,7 @@ dependencies, are returned."
(phpinspect-current-project-root)))
(autoloader (phpinspect-project-autoload project)))
(let ((fqns))
(maphash (lambda (type _) (push (symbol-name type) fqns))
(maphash (lambda (type _) (push (phpinspect-name-string type) fqns))
(if (eq 'own filter)
(phpinspect-autoloader-own-types autoloader)
(phpinspect-autoloader-types autoloader)))

@ -0,0 +1,13 @@
;;; install-deps.el --- Install dependencies -*- lexical-binding: t -*-
(require 'lisp-mnt)
(let* ((project-dir (file-name-parent-directory (file-name-directory (macroexp-file-name))))
(file (expand-file-name "phpinspect.el" project-dir))
dependencies)
(with-temp-buffer
(insert-file-contents file)
(setq dependencies (read (lm-header-multiline "package-requires")))
(dolist (dep dependencies)
(package-install (car dep)))))

@ -303,64 +303,6 @@ class FlufferUpper
(phpinspect--make-type-resolver-for-resolvecontext
context))))))
(ert-deftest phpinspect-eldoc-function-for-object-method ()
(let* ((php-code "
class Thing
{
function getThis(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
$this->getThis(new \\DateTime(), bla)")
(tokens (phpinspect-parse-string php-code))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100))
(phpinspect-purge-cache)
(phpinspect-cache-project-class
(phpinspect-current-project-root)
(cdar (alist-get 'classes (cdr index))))
(should (string= "getThis: ($moment DateTime, $thing Thing, $other): Thing"
(with-temp-buffer
(insert php-code)
(backward-char)
(setq-local phpinspect-current-buffer
(phpinspect-make-buffer :buffer (current-buffer)))
(phpinspect-buffer-parse phpinspect-current-buffer)
(phpinspect-eldoc-function))))))
(ert-deftest phpinspect-eldoc-function-for-static-method ()
(let* ((php-code "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
function doStuff()
{
self::doThing(")
(tokens (phpinspect-parse-string php-code))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100))
(phpinspect-purge-cache)
(phpinspect-cache-project-class
(phpinspect-current-project-root)
(cdar (alist-get 'classes (cdr index))))
(should (string= "doThing: ($moment DateTime, $thing Thing, $other): Thing"
(with-temp-buffer
(insert php-code)
(setq-local phpinspect-current-buffer
(phpinspect-make-buffer :buffer (current-buffer)))
(phpinspect-eldoc-function))))))
(ert-deftest phpinspect-resolve-type-from-context-static-method ()
(with-temp-buffer

@ -288,7 +288,7 @@ class YYY {
(ert-deftest phpinspect-buffer-index-classes ()
(let* ((buffer (phpinspect-make-buffer :project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(let* ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(namespaces (phpinspect-make-splayt))
(declarations (phpinspect-make-splayt))
(classes (phpinspect-make-splayt))
@ -356,7 +356,7 @@ class YYY {
(should (= 1 (hash-table-count (phpinspect-project-class-index (phpinspect-buffer-project buffer))))))))
(ert-deftest phpinspect-buffer-index-functions ()
(let ((buffer (phpinspect-make-buffer :project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(let ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(namespaces (phpinspect-make-splayt))
(declarations (phpinspect-make-splayt))
(classes (phpinspect-make-splayt))
@ -408,7 +408,7 @@ class YYY {
(phpinspect--make-type :name "\\NS\\TestClass"))))))))
(ert-deftest phpinspect-buffer-index-class-variables ()
(let ((buffer (phpinspect-make-buffer :project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(let ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
(namespaces (phpinspect-make-splayt))
(declarations (phpinspect-make-splayt))
(classes (phpinspect-make-splayt))

@ -5,6 +5,9 @@
(ert-deftest phpinspect-eld-method-call ()
(with-temp-buffer
(phpinspect-ensure-worker)
(phpinspect-purge-cache)
(let* ((php-code "
class Thing
{
@ -23,8 +26,6 @@ class Thing
(phpinspect-eldoc-word-width 100)
(buffer (phpinspect-make-buffer :buffer (current-buffer)))
second-arg-pos inside-nested-list-pos first-arg-pos)
(phpinspect-ensure-worker)
(phpinspect-purge-cache)
(phpinspect-cache-project-class
(phpinspect-current-project-root)
(cdar (alist-get 'classes (cdr index))))
@ -53,42 +54,60 @@ class Thing
(should (= 0 (phpinspect-function-doc-arg-pos result)))
(should (string= "getThis" (phpinspect--function-name (phpinspect-function-doc-fn result))))))))
;; (ert-deftest phpinspect-eld-attribute ()
;; (with-temp-buffer
;; (let* ((php-code "
;; class Thing
;; {
;; /** @var \\DateTime **/
;; public $banana;
(ert-deftest phpinspect-eldoc-function-for-object-method ()
(phpinspect-purge-cache)
(let* ((php-code "
class Thing
{
function getThis(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
;; function getThis(\\DateTime $moment, Thing $thing, $other): static
;; {
;; return $this;
;; }
function doStuff()
{
$this->getThis(new \\DateTime(), bla)")
(tokens (phpinspect-parse-string php-code))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100))
(phpinspect-cache-project-class
(phpinspect-current-project-root)
(cdar (alist-get 'classes (cdr index))))
;; function doStuff()
;; {
;; $this->banana;
;; $this->getThis(new \\DateTime(), bla)")
;; (tokens (phpinspect-parse-string php-code))
;; (index (phpinspect--index-tokens tokens))
;; (phpinspect-project-root-function (lambda () "phpinspect-test"))
;; (phpinspect-eldoc-word-width 100)
;; (buffer (phpinspect-make-buffer :buffer (current-buffer)))
;; getThis-pos banana-pos)
(should (string= "getThis: ($moment DateTime, $thing Thing, $other): Thing"
(with-temp-buffer
(insert php-code)
(backward-char)
(setq-local phpinspect-current-buffer
(phpinspect-make-buffer :buffer (current-buffer)))
(phpinspect-buffer-parse phpinspect-current-buffer)
(phpinspect-eldoc-function))))))
;; (insert php-code)
;; (backward-char 28)
;; (setq getThis-pos (point))
;; (backward-char 22)
;; (setq banana-pos (point))
(ert-deftest phpinspect-eldoc-function-for-static-method ()
(phpinspect-purge-cache)
(let* ((php-code "
class Thing
{
static function doThing(\\DateTime $moment, Thing $thing, $other): static
{
return $this;
}
;; (phpinspect-ensure-worker)
;; (phpinspect-purge-cache)
;; (phpinspect-cache-project-class
;; (phpinspect-current-project-root)
;; (cdar (alist-get 'classes (cdr index))))
function doStuff()
{
self::doThing(")
(tokens (phpinspect-parse-string php-code))
(index (phpinspect--index-tokens tokens))
(phpinspect-project-root-function (lambda () "phpinspect-test"))
(phpinspect-eldoc-word-width 100))
(phpinspect-cache-project-class
(phpinspect-current-project-root)
(cdar (alist-get 'classes (cdr index))))
;; (let ((result (phpinspect-eldoc-query-execute
;; (phpinspect-make-eldoc-query :point getThis-pos :buffer buffer))))
;; (message "Result: %s" result)))))
(should (string= "doThing: ($moment DateTime, $thing Thing, $other): Thing"
(with-temp-buffer
(insert php-code)
(setq-local phpinspect-current-buffer
(phpinspect-make-buffer :buffer (current-buffer)))
(phpinspect-eldoc-function))))))

@ -51,7 +51,7 @@
`(phpinspect--root-index
(imports)
(classes
(,(phpinspect--make-type :name"\\Potato" :fully-qualified t)
(,(phpinspect--make-type :name "\\Potato" :fully-qualified t)
phpinspect--indexed-class
(complete . t)
(class-name . ,(phpinspect--make-type :name "\\Potato" :fully-qualified t))
@ -103,7 +103,7 @@ return StaticThing::create(new ThingFactory())->makeThing((((new Potato())->anti
'("Cheese" "Bacon" "Ham" "Bagel" "Monkey" "ExtendedThing"
"StaticThing" "Thing" "ThingFactory" "Potato" "OtherThing"))
#'string<))
(sort used-types (lambda (s1 s2) (string< (symbol-name s1) (symbol-name s2))))))))
(sort used-types (lambda (s1 s2) (string< (phpinspect-name-string s1) (phpinspect-name-string s2))))))))
(ert-deftest phpinspect--find-used-types-in-tokens ()
(let ((blocks `(

Loading…
Cancel
Save