[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/phpinspect e07e1ed9e6 033/126: WIP: Split code up into
From: |
ELPA Syncer |
Subject: |
[elpa] externals/phpinspect e07e1ed9e6 033/126: WIP: Split code up into separate files |
Date: |
Sat, 12 Aug 2023 00:58:38 -0400 (EDT) |
branch: externals/phpinspect
commit e07e1ed9e6659512695b6e175719f8c4211ca2c2
Author: Hugo Thunnissen <devel@hugot.nl>
Commit: Hugo Thunnissen <devel@hugot.nl>
WIP: Split code up into separate files
---
phpinspect-cache.el | 63 ++
phpinspect-class.el | 168 +++++
phpinspect-index.el | 395 ++++++++++++
phpinspect-parser.el | 828 +++++++++++++++++++++++++
phpinspect-project.el | 92 +++
phpinspect-type.el | 187 ++++++
phpinspect-util.el | 66 ++
phpinspect.el | 1641 +------------------------------------------------
8 files changed, 1806 insertions(+), 1634 deletions(-)
diff --git a/phpinspect-cache.el b/phpinspect-cache.el
new file mode 100644
index 0000000000..f546068b0a
--- /dev/null
+++ b/phpinspect-cache.el
@@ -0,0 +1,63 @@
+;;; phpinspect.el --- PHP parsing and completion package -*- lexical-binding:
t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'phpinspect-project)
+
+(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
+ (active-projects nil
+ :type alist
+ :documentation
+ "An `alist` that contains the root directory
+ paths of all currently active phpinspect
+ projects")
+ (projects (make-hash-table :test 'equal :size 10)
+ :type hash-table
+ :documentation
+ "A `hash-table` with the root directories of projects
+as keys and project caches as values."))
+
+(cl-defgeneric phpinspect--cache-getproject
+ ((cache phpinspect--cache) (project-name string))
+ "Get project by PROJECT-NAME that is located in CACHE.")
+
+(cl-defmethod phpinspect--cache-getproject
+ ((cache phpinspect--cache) (project-root string))
+ (gethash project-root (phpinspect--cache-projects cache)))
+
+(cl-defgeneric 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.")
+
+(cl-defmethod phpinspect--cache-get-project-create
+ ((cache phpinspect--cache) (project-root string))
+ (or (phpinspect--cache-getproject cache project-root)
+ (puthash project-root
+ (phpinspect--make-project-cache)
+ (phpinspect--cache-projects cache))))
+
+(provide 'phpinspect-cache)
+;;; phpinspect.el ends here
diff --git a/phpinspect-class.el b/phpinspect-class.el
new file mode 100644
index 0000000000..d03a21a336
--- /dev/null
+++ b/phpinspect-class.el
@@ -0,0 +1,168 @@
+;;; phpinspect-class.el --- PHP parsing module -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'phpinspect-type)
+
+(cl-defstruct (phpinspect--class (:constructor
phpinspect--make-class-generated))
+ (project nil
+ :type phpinspect--project
+ :documentaton
+ "The project that this class belongs to")
+ (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.")
+ (variables nil
+ :type list
+ :documentation
+ "Variables that belong to this class.")
+ (extended-classes (make-hash-table :test 'eq)
+ :type hash-table
+ :documentation
+ "All extended/implemented classes.")
+ (subscriptions nil
+ :type list
+ :documentation
+ "A list of subscription functions that should be
+ called whenever anything about this class is
+ updated"))
+
+(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
+ (dolist (sub (phpinspect--class-subscriptions class))
+ (funcall sub class)))
+
+(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
+ (index (head
phpinspect--indexed-class)))
+ (setf (phpinspect--class-index class) index)
+ (dolist (method (alist-get 'methods index))
+ (phpinspect--class-update-method class method))
+
+ (dolist (method (alist-get 'static-methods index))
+ (phpinspect--class-update-static-method class method))
+
+ (setf (phpinspect--class-variables class)
+ (alist-get 'variables index))
+
+ (setf (phpinspect--class-extended-classes class)
+ (seq-filter
+ #'phpinspect--class-p
+ (mapcar
+ (lambda (class-name)
+ (phpinspect--project-get-class-create (phpinspect--class-project
class)
+ class-name))
+ `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))))
+
+ (dolist (extended (phpinspect--class-extended-classes class))
+ (phpinspect--class-incorporate class extended)
+ (phpinspect--class-subscribe class extended))
+
+ (phpinspect--class-trigger-update class))
+
+(cl-defmethod phpinspect--class-get-method ((class phpinspect--class)
method-name)
+ (gethash method-name (phpinspect--class-methods class)))
+
+(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class)
method-name)
+ (gethash method-name (phpinspect--class-static-methods class)))
+
+(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))
+ (puthash (phpinspect--function-name-symbol method)
+ method
+ (phpinspect--class-methods class)))
+
+(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
+ (method
phpinspect--function))
+ (puthash (phpinspect--function-name-symbol method)
+ method
+ (phpinspect--class-static-methods class)))
+
+(cl-defmethod phpinspect--class-get-method-return-type
+ ((class phpinspect--class) (method-name symbol))
+ (let ((method (phpinspect--class-get-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)
+ (push method methods))
+ (phpinspect--class-methods class))
+ methods))
+
+(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
+ (method
phpinspect--function))
+ (let ((existing (gethash (phpinspect--function-name-symbol method)
+ (phpinspect--class-static-methods class))))
+ (if existing
+ (progn
+ (unless (eq (phpinspect--function-return-type method)
+ phpinspect--null-type)
+ (setf (phpinspect--function-return-type existing)
+ (phpinspect--function-return-type method))
+ (setf (phpinspect--function-arguments existing)
+ (phpinspect--function-arguments method))))
+ (phpinspect--class-set-static-method class method))))
+
+(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
+ (method phpinspect--function))
+ (let ((existing (gethash (phpinspect--function-name-symbol method)
+ (phpinspect--class-methods class))))
+ (if existing
+ (progn
+ (unless (eq (phpinspect--function-return-type method)
+ phpinspect--null-type)
+ (setf (phpinspect--function-return-type existing)
+ (phpinspect--function-return-type method))
+ (setf (phpinspect--function-arguments existing)
+ (phpinspect--function-arguments method))))
+ (phpinspect--class-set-method class method))))
+
+(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
+ (other-class phpinspect--class))
+ (maphash (lambda (k method)
+ (phpinspect--class-update-method class method))
+ (phpinspect--class-methods other-class)))
+
+(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
+ (subscription-class
phpinspect--class))
+ (let ((update-function
+ (lambda (new-class)
+ (phpinspect--class-incorporate class new-class)
+ (phpinspect--class-trigger-update class))))
+ (push update-function (phpinspect--class-subscriptions
subscription-class))))
+
+(provide 'phpinspect-class)
+;;; phpinspect-class.el ends here
diff --git a/phpinspect-index.el b/phpinspect-index.el
new file mode 100644
index 0000000000..9211ed9f47
--- /dev/null
+++ b/phpinspect-index.el
@@ -0,0 +1,395 @@
+;;; phpinspect-index.el --- PHP parsing and completion package -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'phpinspect-util)
+(require 'phpinspect-type)
+
+(defun phpinspect--function-from-scope (scope)
+ (cond ((and (phpinspect-static-p (cadr scope))
+ (phpinspect-function-p (caddr scope)))
+ (caddr scope))
+ ((phpinspect-function-p (cadr scope))
+ (cadr scope))
+ (t nil)))
+
+(defun phpinspect-var-annotation-p (token)
+ (phpinspect-token-type-p token :var-annotation))
+
+(defun phpinspect-return-annotation-p (token)
+ (phpinspect-token-type-p token :return-annotation))
+
+(defun phpinspect--index-function-arg-list (type-resolver arg-list)
+ (let ((arg-index)
+ (current-token)
+ (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))
+ nil)
+ arg-index))))
+ (nreverse arg-index)))
+
+(defun phpinspect--index-function-from-scope (type-resolver scope
comment-before)
+ (let* ((php-func (cadr scope))
+ (declaration (cadr php-func))
+ (type (if (phpinspect-word-p (car (last declaration)))
+ (phpinspect--make-type :name (cadar (last declaration))))))
+
+ ;; @return annotation. Has precedence over typehint when dealing with a
collection.
+ (let* ((is-collection
+ (when type
+ (member (phpinspect--type-name
+ (funcall type-resolver type))
phpinspect-collection-types)))
+ (return-annotation-type
+ (when (or (not type) is-collection)
+ (cadadr
+ (seq-find #'phpinspect-return-annotation-p
+ comment-before)))))
+ (phpinspect--log "found return annotation %s" return-annotation-type)
+
+ (when return-annotation-type
+ (cond ((not type)
+ (setq type (funcall type-resolver
+ (phpinspect--make-type :name
return-annotation-type))))
+ (is-collection
+ (phpinspect--log "Detected collection type in: %s" scope)
+ (setf (phpinspect--type-contains type)
+ (funcall type-resolver
+ (phpinspect--make-type :name
return-annotation-type)))
+ (setf (phpinspect--type-collection type) t)))))
+
+ (phpinspect--make-function
+ :scope `(,(car scope))
+ :name (cadadr (cdr declaration))
+ :return-type (if type (funcall type-resolver type)
+ phpinspect--null-type)
+ :arguments (phpinspect--index-function-arg-list
+ type-resolver
+ (phpinspect-function-argument-list php-func)))))
+
+(defun phpinspect--index-const-from-scope (scope)
+ (phpinspect--make-variable
+ :scope `(,(car scope))
+ :name (cadr (cadr (cadr scope)))))
+
+(defun phpinspect--var-annotations-from-token (token)
+ (seq-filter #'phpinspect-var-annotation-p token))
+
+(defun phpinspect--index-variable-from-scope (type-resolver scope
comment-before)
+ "Index the variable inside `scope`."
+ (let* ((var-annotations (phpinspect--var-annotations-from-token
comment-before))
+ (variable-name (cadr (cadr scope)))
+ (type (if var-annotations
+ ;; Find the right annotation by variable name
+ (or (cadr (cadr (seq-find (lambda (annotation)
+ (string= (cadr (caddr
annotation)) variable-name))
+ var-annotations)))
+ ;; Give up and just use the last one encountered
+ (cadr (cadr (car (last var-annotations))))))))
+ (phpinspect--log "calling resolver from index-variable-from-scope")
+ (phpinspect--make-variable
+ :name variable-name
+ :scope `(,(car scope))
+ :type (if type (funcall type-resolver (phpinspect--make-type :name
type))))))
+
+(defun phpinspect-doc-block-p (token)
+ (phpinspect-token-type-p token :doc-block))
+
+(defun phpinspect--get-class-name-from-token (class-token)
+ (let ((subtoken (seq-find (lambda (word)
+ (and (phpinspect-word-p word)
+ (not (string-match
+ (concat "^"
(phpinspect-handler-regexp 'class-keyword))
+ (concat (cadr word) " ")))))
+ (cadr class-token))))
+ (cadr subtoken)))
+
+(defun phpinspect--index-class (type-resolver class)
+ "Create an alist with relevant attributes of a parsed class."
+ (phpinspect--log "INDEXING CLASS")
+ (let ((methods)
+ (static-methods)
+ (static-variables)
+ (variables)
+ (constants)
+ (extends)
+ (implements)
+ (class-name (phpinspect--get-class-name-from-token class))
+ ;; Keep track of encountered comments to be able to use type
+ ;; annotations.
+ (comment-before))
+
+ ;; Find out what the class extends or implements
+ (let ((enc-extends nil)
+ (enc-implements nil))
+ (dolist (word (cadr class))
+ (if (phpinspect-word-p word)
+ (cond ((string= (cadr word) "extends")
+ (phpinspect--log "Class %s extends other classes"
class-name)
+ (setq enc-extends t))
+ ((string= (cadr word) "implements")
+ (setq enc-extends nil)
+ (phpinspect--log "Class %s implements in interface"
class-name)
+ (setq enc-implements t))
+ (t
+ (phpinspect--log "Calling Resolver from index-class on %s"
(cadr word))
+ (cond (enc-extends
+ (push (funcall type-resolver (phpinspect--make-type
+ :name (cadr word)))
+ extends))
+ (enc-implements
+ (push (funcall type-resolver (phpinspect--make-type
+ :name (cadr word)))
+ implements))))))))
+
+ (dolist (token (caddr class))
+ (cond ((phpinspect-scope-p token)
+ (cond ((phpinspect-const-p (cadr token))
+ (push (phpinspect--index-const-from-scope token)
constants))
+
+ ((phpinspect-variable-p (cadr token))
+ (push (phpinspect--index-variable-from-scope type-resolver
+ token
+
comment-before)
+ variables))
+
+ ((phpinspect-static-p (cadr token))
+ (cond ((phpinspect-function-p (cadadr token))
+ (push (phpinspect--index-function-from-scope
type-resolver
+ (list
(car token)
+
(cadadr token))
+
comment-before)
+ static-methods))
+
+ ((phpinspect-variable-p (cadadr token))
+ (push (phpinspect--index-variable-from-scope
type-resolver
+ (list
(car token)
+
(cadadr token))
+
comment-before)
+ static-variables))))
+ (t
+ (phpinspect--log "comment-before is: %s" comment-before)
+ (push (phpinspect--index-function-from-scope type-resolver
+ token
+
comment-before)
+ methods))))
+ ((phpinspect-static-p token)
+ (cond ((phpinspect-function-p (cadr token))
+ (push (phpinspect--index-function-from-scope type-resolver
+ `(:public
+ ,(cadr
token))
+
comment-before)
+ static-methods))
+
+ ((phpinspect-variable-p (cadr token))
+ (push (phpinspect--index-variable-from-scope type-resolver
+ `(:public
+ ,(cadr
token))
+
comment-before)
+ static-variables))))
+ ((phpinspect-const-p token)
+ ;; Bare constants are always public
+ (push (phpinspect--index-const-from-scope (list :public token))
+ constants))
+ ((phpinspect-function-p token)
+ ;; Bare functions are always public
+ (push (phpinspect--index-function-from-scope type-resolver
+ (list :public token)
+ comment-before)
+ methods))
+ ((phpinspect-doc-block-p token)
+ (phpinspect--log "setting comment-before %s" token)
+ (setq comment-before token))
+
+ ;; Prevent comments from sticking around too long
+ (t
+ (phpinspect--log "Unsetting comment-before")
+ (setq comment-before nil))))
+
+ ;; Dirty hack that assumes the constructor argument names to be the same
as the object
+ ;; attributes' names.
+ ;;;
+ ;; 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)))
+ (when constructor
+ (phpinspect--log "Constructor was found")
+ (dolist (variable variables)
+ (when (not (phpinspect--variable-type variable))
+ (phpinspect--log "Looking for variable type in constructor
arguments (%s)"
+ variable)
+ (let ((constructor-parameter-type
+ (car (alist-get (phpinspect--variable-name variable)
+ (phpinspect--function-arguments constructor)
+ nil nil #'string=))))
+ (if constructor-parameter-type
+ (setf (phpinspect--variable-type variable)
+ (funcall type-resolver
constructor-parameter-type))))))))
+
+ (let ((class-name (funcall type-resolver (phpinspect--make-type :name
class-name))))
+ `(,class-name .
+ (phpinspect--indexed-class
+ (methods . ,methods)
+ (class-name . ,class-name)
+ (static-methods . ,static-methods)
+ (static-variables . ,static-variables)
+ (variables . ,variables)
+ (constants . ,constants)
+ (extends . ,extends)
+ (implements . ,implements))))))
+
+(defsubst phpinspect-namespace-body (namespace)
+ "Return the nested tokens in NAMESPACE tokens' body.
+Accounts for namespaces that are defined with '{}' blocks."
+ (if (phpinspect-block-p (caddr namespace))
+ (cdaddr namespace)
+ (cdr namespace)))
+
+(defun phpinspect--index-classes (types classes &optional namespace indexed)
+ "Index the class tokens in `classes`, using the types in `types`
+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)
+ class)
+ indexed)
+ (phpinspect--index-classes types 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-name (if (and (phpinspect-word-p (caddr use))
+ (string= "as" (cadr (caddr use))))
+ (cadr (cadddr use))
+ (progn (string-match "[^\\]+$" fqn)
+ (match-string 0 fqn)))))
+ (cons (phpinspect-intern-name type-name) type)))
+
+(defun phpinspect--uses-to-types (uses)
+ (mapcar #'phpinspect--use-to-type uses))
+
+(defun phpinspect--index-namespace (namespace)
+ (phpinspect--index-classes
+ (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
+ (seq-filter #'phpinspect-class-p namespace)
+ (cadadr namespace)))
+
+(defun phpinspect--index-namespaces (namespaces &optional indexed)
+ (if namespaces
+ (progn
+ (push (phpinspect--index-namespace (pop namespaces)) indexed)
+ (phpinspect--index-namespaces namespaces indexed))
+ (apply #'append (nreverse indexed))))
+
+(defun phpinspect--index-functions (&rest _args)
+ "TODO: implement function indexation. This is a stub function.")
+
+(defun phpinspect--index-tokens (tokens)
+ "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
+ )
+
+;; (defun phpinspect--get-or-create-index-for-class-file (class-fqn)
+;; (phpinspect--log "Getting or creating index for %s" class-fqn)
+;; (phpinspect-get-or-create-cached-project-class
+;; (phpinspect-project-root)
+;; class-fqn))
+
+(defun phpinspect-index-file (file-name)
+ (phpinspect--index-tokens (phpinspect-parse-file file-name)))
+
+(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))))
+
+ ;; (let ((existing-index (phpinspect-get-cached-project-class
+ ;; project-root
+ ;; class-fqn)))
+ ;; (or
+ ;; existing-index
+ ;; (progn
+ ;; (let* ((class-file (phpinspect-class-filepath class-fqn))
+ ;; (visited-buffer (when class-file (find-buffer-visiting
class-file)))
+ ;; (new-index))
+
+ ;; (phpinspect--log "No existing index for FQN: %s" class-fqn)
+ ;; (phpinspect--log "filepath: %s" class-file)
+ ;; (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)))
+ ;; (dolist (class (alist-get 'classes new-index))
+ ;; (when class
+ ;; (phpinspect-cache-project-class
+ ;; project-root
+ ;; (cdr class))))
+ ;; (alist-get class-fqn (alist-get 'classes new-index)
+ ;; nil
+ ;; nil
+ ;; #'phpinspect--type=))))))))
+
+
+(defun phpinspect--index-current-buffer ()
+ (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
+
+(defun phpinspect-index-current-buffer ()
+ "Index a PHP file for classes and the methods they have"
+ (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
+
+;; (defun phpinspect--get-variables-for-class (buffer-classes class &optional
static)
+;; (let ((class-index (or (assoc-default class buffer-classes
#'phpinspect--type=)
+;; (phpinspect--get-or-create-index-for-class-file
class))))
+;; (when class-index
+;; (if static
+;; (append (alist-get 'static-variables class-index)
+;; (alist-get 'constants class-index))
+;; (alist-get 'variables class-index)))))
+
+
+(provide 'phpinspect-index)
+;;; phpinspect-index.el ends here
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
new file mode 100644
index 0000000000..9d2609bcbc
--- /dev/null
+++ b/phpinspect-parser.el
@@ -0,0 +1,828 @@
+;;; phpinspect-parser.el --- PHP parsing module -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defvar phpinspect-parser-obarray (obarray-make)
+ "An obarray containing symbols for all phpinspect (sub)parsers.")
+
+(defvar phpinspect-handler-obarray (obarray-make)
+ "An obarray containing symbols for all phpinspect parser handlers.")
+
+(eval-when-compile
+ (define-inline phpinspect--word-end-regex ()
+ (inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)")))
+
+(defun phpinspect-list-handlers ()
+ (let ((handlers))
+ (mapatoms (lambda (handler)
+ (push (symbol-name handler) handlers))
+ phpinspect-handler-obarray)
+ handlers))
+
+(defun phpinspect-describe-handler (handler-name)
+ "Display a buffer containing HANDLER-NAMEs docstring and attribute-plist."
+ (interactive (list (completing-read "Pick a handler:"
(phpinspect-list-handlers))))
+ (with-current-buffer (get-buffer-create "phpinspect-handler-description")
+ (insert (concat
+ (pp (symbol-value (intern handler-name
phpinspect-handler-obarray)))
+ "\n"
+ (documentation (intern handler-name phpinspect-handler-obarray))))
+ (pop-to-buffer (current-buffer))))
+
+(defsubst phpinspect--strip-last-char (string)
+ (substring string 0 (- (length string) 1)))
+
+(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
+ "Return a token of type TOKEN-KEYWORD with STRING as value.
+If STRING has text properties, they are stripped."
+ (let ((value (copy-sequence string))
+ (length (length string)))
+ (forward-char length)
+ (set-text-properties 0 length nil value)
+ (list token-keyword value)))
+
+
+(defsubst phpinspect-token-type-p (object type)
+ "Returns t if OBJECT is a token of type TYPE.
+Type can be any of the token types returned by
+`phpinspect-parse-buffer-until-point`"
+ (and (listp object) (eq (car object) type)))
+
+(defsubst phpinspect-object-attrib-p (token)
+ (phpinspect-token-type-p token :object-attrib))
+
+(defsubst phpinspect-static-attrib-p (token)
+ (phpinspect-token-type-p token :static-attrib))
+
+(defsubst phpinspect-attrib-p (token)
+ (or (phpinspect-object-attrib-p token)
+ (phpinspect-static-attrib-p token)))
+
+(defun phpinspect-html-p (token)
+ (phpinspect-token-type-p token :html))
+
+(defun phpinspect-comma-p (token)
+ (phpinspect-token-type-p token :comma))
+
+(defsubst phpinspect-terminator-p (token)
+ (phpinspect-token-type-p token :terminator))
+
+(defsubst phpinspect-end-of-token-p (token)
+ (or (phpinspect-terminator-p token)
+ (phpinspect-comma-p token)
+ (phpinspect-html-p token)))
+
+(defsubst phpinspect-end-of-statement-p (token)
+ (or (phpinspect-end-of-token-p token)
+ (phpinspect-block-p token)))
+
+(defsubst phpinspect-incomplete-block-p (token)
+ (phpinspect-token-type-p token :incomplete-block))
+
+(defsubst phpinspect-block-p (token)
+ (or (phpinspect-token-type-p token :block)
+ (phpinspect-incomplete-block-p token)))
+
+(defun phpinspect-end-of-use-p (token)
+ (or (phpinspect-block-p token)
+ (phpinspect-end-of-token-p token)))
+
+(defun phpinspect-static-p (token)
+ (phpinspect-token-type-p token :static))
+
+(defsubst phpinspect-incomplete-const-p (token)
+ (phpinspect-token-type-p token :incomplete-const))
+
+(defsubst phpinspect-const-p (token)
+ (or (phpinspect-token-type-p token :const)
+ (phpinspect-incomplete-const-p token)))
+
+(defsubst phpinspect-scope-p (token)
+ (or (phpinspect-token-type-p token :public)
+ (phpinspect-token-type-p token :private)
+ (phpinspect-token-type-p token :protected)))
+
+(defsubst phpinspect-namespace-p (object)
+ (phpinspect-token-type-p object :namespace))
+
+(defun phpinspect-incomplete-class-p (token)
+ (and (phpinspect-class-p token)
+ (phpinspect-incomplete-block-p (car (last token)))))
+
+(defun phpinspect-incomplete-namespace-p (token)
+ (and (phpinspect-namespace-p token)
+ (or (phpinspect-incomplete-block-p (car (last token)))
+ (phpinspect-incomplete-class-p (car (last token))))))
+
+(defun phpinspect-function-p (token)
+ (phpinspect-token-type-p token :function))
+
+
+(defun phpinspect-class-p (token)
+ (phpinspect-token-type-p token :class))
+
+(defun phpinspect-incomplete-method-p (token)
+ (or (phpinspect-incomplete-function-p token)
+ (and (phpinspect-scope-p token)
+ (phpinspect-incomplete-function-p (car (last token))))
+ (and (phpinspect-scope-p token)
+ (phpinspect-static-p (car (last token)))
+ (phpinspect-incomplete-function-p (car (last (car (last token))))))
+ (and (phpinspect-scope-p token)
+ (phpinspect-function-p (car (last token))))))
+
+(defun phpinspect-incomplete-function-p (token)
+ (and (phpinspect-function-p token)
+ (phpinspect-incomplete-block-p (car (last token)))))
+
+(defsubst phpinspect-incomplete-list-p (token)
+ (phpinspect-token-type-p token :incomplete-list))
+
+(defsubst phpinspect-list-p (token)
+ (or (phpinspect-token-type-p token :list)
+ (phpinspect-incomplete-list-p token)))
+
+(defun phpinspect-declaration-p (token)
+ (phpinspect-token-type-p token :declaration))
+
+(defsubst phpinspect-assignment-p (token)
+ (phpinspect-token-type-p token :assignment))
+
+(defun phpinspect-function-argument-list (php-func)
+ "Get the argument list of a function"
+ (seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func
nil) nil))
+
+(defsubst phpinspect-variable-p (token)
+ (phpinspect-token-type-p token :variable))
+
+(defsubst phpinspect-word-p (token)
+ (phpinspect-token-type-p token :word))
+
+(defsubst phpinspect-incomplete-array-p (token)
+ (phpinspect-token-type-p token :incomplete-array))
+
+(defsubst phpinspect-array-p (token)
+ (or (phpinspect-token-type-p token :array)
+ (phpinspect-incomplete-array-p token)))
+
+(defsubst phpinspect-incomplete-token-p (token)
+ (or (phpinspect-incomplete-class-p token)
+ (phpinspect-incomplete-block-p token)
+ (phpinspect-incomplete-list-p token)
+ (phpinspect-incomplete-array-p token)
+ (phpinspect-incomplete-const-p token)
+ (phpinspect-incomplete-function-p token)
+ (phpinspect-incomplete-method-p token)
+ (phpinspect-incomplete-namespace-p token)))
+
+(defun phpinspect--static-terminator-p (token)
+ (or (phpinspect-function-p token)
+ (phpinspect-end-of-token-p token)))
+
+(defun phpinspect--scope-terminator-p (token)
+ (or (phpinspect-function-p token)
+ (phpinspect-end-of-token-p token)
+ (phpinspect-const-p token)
+ (phpinspect-static-p token)))
+
+(defun phpinspect-namespace-keyword-p (token)
+ (and (phpinspect-word-p token) (string= (car (last token)) "namespace")))
+
+(defun phpinspect-use-keyword-p (token)
+ (and (phpinspect-word-p token) (string= (car (last token)) "use")))
+
+
+(defsubst phpinspect-root-p (object)
+ (phpinspect-token-type-p object :root))
+
+(defsubst phpinspect-namespace-or-root-p (object)
+ (or (phpinspect-namespace-p object)
+ (phpinspect-root-p object)))
+
+(defun phpinspect-use-p (object)
+ (phpinspect-token-type-p object :use))
+
+(defun phpinspect-comment-p (token)
+ (phpinspect-token-type-p token :comment))
+
+(defsubst phpinspect-class-block (class)
+ (caddr class))
+
+(defsubst phpinspect-namespace-block (namespace)
+ (when (and (= (length namespace) 3)
+ (phpinspect-block-p (caddr namespace)))
+ (caddr namespace)))
+
+(defsubst phpinspect-function-block (php-func)
+ (caddr php-func))
+
+(defsubst phpinspect-not-class-p (token)
+ "Apply inverse of `phpinspect-class-p' to TOKEN."
+ (not (phpinspect-class-p token)))
+
+(defmacro phpinspect-defhandler (name arguments docstring attribute-plist
&rest body)
+ "Define a parser handler that becomes available for use with
phpinspect-parse.
+
+A parser handler is a function that is able to identify and parse
+tokens from PHP code at `point` in the current buffer. It's
+return value must be the resulting token. Aside from parsing it
+has to manage the state of `point` in a way that it skips over
+the tokens it has parsed. That way the next handler can
+correctly pick up from where it has left off.
+
+Parser handlers are unrolled in a `cond` statement by
+`phpinspect-make-parser`. The resulting code is something akin
+to the following:
+
+(while ...
+ (cond (((looking-at \"{\")
+ (funcall block-handler (match-string 0) max-point)
+ ((looking-at \"\\$\")
+ (funcall variable-handler ...
+etc.
+
+NAME must be a symbol. It does not need to be prefixed with a
+\"namespace\" because parser handlers are stored in their own
+obarray (`phpinspect-handler-obarray`).
+
+ARGUMENTS must an argument list as accepted by `lambda`. A
+handler must be able to accept 2 arguments: START-TOKEN and
+MAX-POINT. START-TOKEN is the match string that resulted from
+the comparison of the handlers' `regexp` attribute with the text
+at `point`. MAX-POINT is the point in the current buffer up
+until which the parser is supposed to parse. For some tokens you
+may not want/need to respect MAX-POINT, in which case you can
+ignore it.
+
+DOCSTRING is mandatory. It should contain a description of the
+tokens the handler is able to process and (if present) any
+particularities of the handler.
+
+ATTRIBUTE-PLIST is a plist that must contain at least a `regexp` key.
+ Possible keys:
+ - regexp: The regular expression that marks the start of the token.
+
+BODY is a function body as accepted by `lambda` that parses the
+text at point and returns the resulting token.
+
+When altering/adding handlers during runtime, make sure to purge
+the parser cache to make sure that your new handler functions are used.
+You can purge the parser cache with \\[phpinspect-purge-parser-cache]."
+ (declare (indent defun))
+ (when (not (symbolp name))
+ (error "In definition of phpinspect handler %s: NAME bust be a symbol"
name))
+
+ (when (not (plist-member attribute-plist 'regexp))
+ (error "In definition of phpinspect handler %s ATTRIBUTE-PLIST must
contain key `regexp`"
+ name))
+
+ ;; Eval regexp. It might be a `concat` statement and we don't want to be
executing that
+ ;; every time the parser advances one character and has to check for the
regexp
+ ;; occurence.
+ (setq attribute-plist (plist-put attribute-plist 'regexp
+ (eval (plist-get attribute-plist 'regexp)
+ t)))
+ (let ((name (symbol-name name)))
+ `(progn
+ (set (intern ,name phpinspect-handler-obarray) (quote ,attribute-plist))
+ (defalias (intern ,name phpinspect-handler-obarray)
+ #'(lambda (,@arguments)
+ ,docstring
+ ,@body))
+ (unless (byte-code-function-p
+ (symbol-function
+ (intern ,name phpinspect-handler-obarray)))
+ (byte-compile (intern ,name phpinspect-handler-obarray))))))
+
+(defun phpinspect-get-parser-create (tree-type &rest parser-parameters)
+ "Retrieve a parser for TREE-TYPE from `phpinspect-parser-obarray'.
+
+TREE-TYPE must be a symbol or keyword representing the type of
+the token the parser is able to parse.
+
+If a parser by TREE-TYPE doesn't exist, it is created by callng
+`phpinspect-make-parser` with TREE-TYPE as first argument and
+PARSER-PARAMETERS as the rest of the arguments. The resulting
+parser function is then returned in byte-compiled form."
+ (let* ((parser-name (symbol-name tree-type))
+ (parser-symbol (intern-soft parser-name phpinspect-parser-obarray)))
+ (or (and parser-symbol (symbol-function parser-symbol))
+ (defalias (intern parser-name phpinspect-parser-obarray)
+ (byte-compile (apply #'phpinspect-make-parser
+ `(,tree-type ,@parser-parameters)))))))
+
+(defun phpinspect-purge-parser-cache ()
+ "Empty `phpinspect-parser-obarray`.
+
+This is useful when you need to change parser handlers or parsers
+during runtime. Parsers are implemented with macros, so changing
+handler functions without calling this function will often not
+have any effect."
+ (interactive)
+ (setq phpinspect-parser-obarray (obarray-make)))
+
+(defun phpinspect-make-parser (tree-type handler-list &optional
delimiter-predicate)
+ "Create a parser function using the handlers by names defined in
HANDLER-LIST.
+
+See also `phpinspect-defhandler`.
+
+TREE-TYPE must be a symbol or a keyword representing the token
+type.
+
+HANDLER-LIST must be a list of either symbol or string
+representation of handler symbols which can be found in
+`phpinspect-handler-obarray`.
+
+DELIMITER-PREDICATE must be a function. It is passed the last
+parsed token after every handler iteration. If it evaluates to
+something other than nil, parsing is deemed completed and the
+loop exits. An example use case of this is to determine the end
+of a statement. You can use `phpinspect-terminator-p` as
+delimiter predicate and have parsing stop when the last parsed
+token is \";\", which marks the end of a statement in PHP."
+ (let ((handlers (mapcar
+ (lambda (handler-name)
+ (let* ((handler-name (symbol-name handler-name))
+ (handler (intern-soft handler-name
phpinspect-handler-obarray)))
+ (if handler
+ handler
+ (error "No handler found by name \"%s\""
handler-name))))
+ handler-list))
+ (delimiter-predicate (if (symbolp delimiter-predicate)
+ `(quote ,delimiter-predicate)
+ delimiter-predicate)))
+ `(lambda (buffer max-point &optional continue-condition)
+ (with-current-buffer buffer
+ (let ((tokens)
+ (delimiter-predicate (when (functionp ,delimiter-predicate)
,delimiter-predicate)))
+ (while (and (< (point) max-point)
+ (if continue-condition (funcall continue-condition) t)
+ (not (if delimiter-predicate
+ (funcall delimiter-predicate (car (last
tokens)))
+ nil)))
+ (cond ,@(mapcar
+ (lambda (handler)
+ `((looking-at ,(plist-get (symbol-value handler)
'regexp))
+ (let ((token (funcall ,(symbol-function handler)
+ (match-string 0)
+ max-point)))
+ (when token
+ (if (null tokens)
+ (setq tokens (list token))
+ (nconc tokens (list token)))))))
+ handlers)
+ (t (forward-char))))
+ (push ,tree-type tokens))))))
+
+(phpinspect-defhandler comma (comma &rest _ignored)
+ "Handler for comma tokens"
+ (regexp ",")
+ (phpinspect-munch-token-without-attribs comma :comma))
+
+(phpinspect-defhandler word (word &rest _ignored)
+ "Handler for bareword tokens"
+ (regexp "[A-Za-z_\\][\\A-Za-z_0-9]*")
+ (let ((length (length word)))
+ (forward-char length)
+ (set-text-properties 0 length nil word)
+ (list :word word)))
+
+(defsubst phpinspect-handler (handler-name)
+ (intern-soft (symbol-name handler-name) phpinspect-handler-obarray))
+
+(defsubst phpinspect-handler-regexp (handler-name)
+ (plist-get (symbol-value (phpinspect-handler handler-name)) 'regexp))
+
+(defsubst phpinspect--parse-annotation-parameters (parameter-amount)
+ (let* ((words)
+ (word-handler (phpinspect-handler 'word))
+ (word-regexp (phpinspect-handler-regexp 'word))
+ (variable-handler (phpinspect-handler 'variable))
+ (variable-regexp (phpinspect-handler-regexp 'variable)))
+ (while (not (or (looking-at "\\*/") (= (length words) parameter-amount)))
+ (forward-char)
+ (cond ((looking-at word-regexp)
+ (push (funcall word-handler (match-string 0)) words))
+ ((looking-at variable-regexp)
+ (push (funcall variable-handler (match-string 0)) words))))
+ (nreverse words)))
+
+(phpinspect-defhandler annotation (start-token &rest _ignored)
+ "Handler for in-comment @annotations"
+ (regexp "@")
+ (forward-char (length start-token))
+ (if (looking-at (phpinspect-handler-regexp 'word))
+ (let ((annotation-name (match-string 0)))
+ (forward-char (length annotation-name))
+ (cond ((string= annotation-name "var")
+ ;; The @var annotation accepts 2 parameters:
+ ;; the type and the $variable name
+ (append (list :var-annotation)
+ (phpinspect--parse-annotation-parameters 2)))
+ ((string= annotation-name "return")
+ ;; The @return annotation only accepts 1 word as parameter:
+ ;; The return type
+ (append (list :return-annotation)
+ (phpinspect--parse-annotation-parameters 1)))
+ ((string= annotation-name "param")
+ ;; The @param annotation accepts 2 parameters:
+ ;; The type of the param, and the param's $name
+ (append (list :param-annotation)
+ (phpinspect--parse-annotation-parameters 2)))
+ (t
+ (list :annotation annotation-name))))
+ (list :annotation nil)))
+
+(phpinspect-defhandler tag (start-token max-point)
+ "Handler that discards any inline HTML it encounters"
+ (regexp "\\?>")
+ (forward-char (length start-token))
+ (or (re-search-forward "<\\?php\\|<\\?" nil t)
+ (goto-char max-point))
+ (list :html))
+
+(phpinspect-defhandler comment (start-token max-point)
+ "Handler for comments and doc blocks"
+ (regexp "#\\|//\\|/\\*")
+ (forward-char (length start-token))
+
+ (cond ((string-match "/\\*" start-token)
+ (let* ((continue-condition (lambda () (not (looking-at "\\*/"))))
+ (parser (phpinspect-get-parser-create
+ :doc-block
+ '(annotation whitespace)))
+ (doc-block (funcall parser (current-buffer) max-point
continue-condition)))
+ (forward-char 2)
+ doc-block))
+ (t
+ (let ((parser (phpinspect-get-parser-create :comment '(tag)
#'phpinspect-html-p))
+ (end-position (line-end-position)))
+ (funcall parser (current-buffer) end-position)))))
+
+(phpinspect-defhandler variable (start-token &rest _ignored)
+ "Handler for tokens indicating reference to a variable"
+ (regexp "\\$")
+ (forward-char (length start-token))
+ (if (looking-at (phpinspect-handler-regexp 'word))
+ (phpinspect-munch-token-without-attribs (match-string 0) :variable)
+ (list :variable nil)))
+
+(phpinspect-defhandler whitespace (whitespace &rest _ignored)
+ "Handler that discards whitespace"
+ (regexp "[[:blank:]]+")
+ (forward-char (length whitespace)))
+
+(phpinspect-defhandler equals (equals &rest _ignored)
+ "Handler for strict and unstrict equality comparison tokens."
+ (regexp "===?")
+ (phpinspect-munch-token-without-attribs equals :equals))
+
+(phpinspect-defhandler assignment-operator (operator &rest _ignored)
+ "Handler for tokens indicating that an assignment is taking place"
+ (regexp "[+-]?=")
+ (phpinspect-munch-token-without-attribs operator :assignment))
+
+(phpinspect-defhandler terminator (terminator &rest _ignored)
+ "Handler for statement terminators."
+ (regexp ";")
+ (phpinspect-munch-token-without-attribs terminator :terminator))
+
+(phpinspect-defhandler use-keyword (start-token max-point)
+ "Handler for the use keyword and tokens that might follow to give it meaning"
+ (regexp (concat "use" (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (forward-char (length start-token))
+
+ (let ((parser (phpinspect-get-parser-create
+ :use
+ '(word tag block-without-scopes terminator)
+ #'phpinspect-end-of-use-p)))
+ (funcall parser (current-buffer) max-point)))
+
+(phpinspect-defhandler attribute-reference (start-token &rest _ignored)
+ "Handler for references to object attributes, or static class attributes."
+ (regexp "->\\|::")
+ (forward-char (length start-token))
+ (looking-at (phpinspect-handler-regexp 'word))
+ (let ((name (if (looking-at (phpinspect-handler-regexp 'word))
+ (funcall (phpinspect-handler 'word) (match-string 0))
+ nil)))
+ (cond
+ ((string= start-token "::")
+ (list :static-attrib name))
+ ((string= start-token "->")
+ (list :object-attrib name)))))
+
+(phpinspect-defhandler namespace (start-token max-point)
+ "Handler for the namespace keyword. This is a special one
+ because it is not always delimited by a block like classes or
+ functions. This handler parses the namespace declaration, and
+ then continues to parse subsequent tokens, only stopping when
+ either a block has been parsed or another namespace keyword has
+ been encountered."
+ (regexp (concat "namespace" (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (forward-char (length start-token))
+ (phpinspect-parse-with-handler-list
+ (current-buffer)
+ :namespace
+ max-point
+ (lambda () (not (looking-at (phpinspect-handler-regexp 'namespace))))
+ #'phpinspect-block-p))
+
+(phpinspect-defhandler const-keyword (start-token max-point)
+ "Handler for the const keyword."
+ (regexp (concat "const" (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (forward-char (length start-token))
+ (let* ((parser (phpinspect-get-parser-create
+ :const
+ '(word comment assignment-operator string array
+ terminator)
+ #'phpinspect-end-of-token-p))
+ (token (funcall parser (current-buffer) max-point)))
+ (when (phpinspect-incomplete-token-p (car (last token)))
+ (setcar token :incomplete-const))
+ token))
+
+(phpinspect-defhandler string (start-token &rest _ignored)
+ "Handler for strings"
+ (regexp "\"\\|'")
+ (list :string (phpinspect--munch-string start-token)))
+
+(phpinspect-defhandler block-without-scopes (start-token max-point)
+ "Handler for code blocks that cannot contain scope, const or
+static keywords with the same meaning as in a class block."
+ (regexp "{")
+ (forward-char (length start-token))
+ (let* ((complete-block nil)
+ (parser (phpinspect-get-parser-create
+ :block
+ '(array tag equals list comma
+ attribute-reference variable
+ assignment-operator whitespace
+ function-keyword word terminator here-doc
+ string comment block-without-scopes)))
+ (continue-condition (lambda ()
+ (not (and (char-equal (char-after) ?})
+ (setq complete-block t)))))
+ (parsed (funcall parser (current-buffer) max-point
continue-condition)))
+ (if complete-block
+ (forward-char)
+ (setcar parsed :incomplete-block))
+ parsed))
+
+
+(phpinspect-defhandler class-block (start-token max-point)
+ "Handler for code blocks that cannot contain classes"
+ (regexp "{")
+ (forward-char (length start-token))
+ (let* ((complete-block nil)
+ (parser (phpinspect-get-parser-create
+ :block
+ '(array tag equals list comma
+ attribute-reference variable
+ assignment-operator whitespace scope-keyword
+ static-keyword const-keyword use-keyword
+ function-keyword word terminator here-doc
+ string comment block)))
+ (continue-condition (lambda ()
+ (not (and (char-equal (char-after) ?})
+ (setq complete-block t)))))
+ (parsed (funcall parser (current-buffer) max-point
continue-condition)))
+ (if complete-block
+ (forward-char)
+ (setcar parsed :incomplete-block))
+ parsed))
+
+(phpinspect-defhandler block (start-token max-point)
+ "Handler for code blocks"
+ (regexp "{")
+ (forward-char (length start-token))
+ (let* ((complete-block nil)
+ (continue-condition (lambda ()
+ ;; When we encounter a closing brace for this
+ ;; block, we can mark the block as complete.
+ (not (and (char-equal (char-after) ?})
+ (setq complete-block t)))))
+ (parsed (phpinspect-parse-with-handler-list
+ (current-buffer) :block max-point continue-condition)))
+ (if complete-block
+ ;; After meeting the char-after requirement above, we need to move
+ ;; one char forward to prevent parent-blocks from exiting because
+ ;; of the same char.
+ (forward-char)
+ (setcar parsed :incomplete-block))
+ parsed))
+
+(phpinspect-defhandler here-doc (start-token &rest _ignored)
+ "Handler for heredocs. Discards their contents."
+ (regexp "<<<")
+ (forward-char (length start-token))
+ (if (looking-at "[A-Za-z0-9'\"\\_]+")
+ (re-search-forward (concat "^" (regexp-quote (match-string 0))) nil t))
+ (list :here-doc))
+
+
+(defun phpinspect--munch-string (start-token)
+ "Consume text at point until a non-escaped `START-TOKEN` is found.
+
+Returns the consumed text string without face properties."
+ (forward-char (length start-token))
+ (let ((start-point (point)))
+ (cond ((looking-at start-token)
+ (forward-char)
+ "")
+ ((looking-at (concat "\\([\\][\\]\\)+" (regexp-quote start-token)))
+ (let ((match (match-string 0)))
+ (forward-char (length match))
+ (buffer-substring-no-properties start-point
+ (+ start-point (- (length match)
+ (length
start-token))))))
+ (t
+ (re-search-forward (format "\\([^\\]\\([\\][\\]\\)+\\|[^\\]\\)%s"
+ (regexp-quote start-token))
+ nil t)
+ (buffer-substring-no-properties start-point (- (point) 1))))))
+
+(phpinspect-defhandler list (start-token max-point)
+ "Handler for php syntactic lists (Note: this does not include
+datatypes like arrays, merely lists that are of a syntactic
+nature like argument lists"
+ (regexp "(")
+ (forward-char (length start-token))
+ (let* ((complete-list nil)
+ (php-list (funcall
+ (phpinspect-get-parser-create
+ :list
+ '(array tag equals list comma
+ attribute-reference variable
+ assignment-operator whitespace
+ function-keyword word terminator here-doc
+ string comment block-without-scopes))
+ (current-buffer)
+ max-point
+ (lambda () (not (and (char-equal (char-after) ?\)) (setq
complete-list t)))))))
+
+ (if complete-list
+ ;; Prevent parent-lists (if any) from exiting by skipping over the
+ ;; ")" character
+ (forward-char)
+ (setcar php-list :incomplete-list))
+ php-list))
+
+;; TODO: Look into using different names for function and class :declaration
tokens. They
+;; don't necessarily require the same handlers to parse.
+(defsubst phpinspect-get-or-create-declaration-parser ()
+ (phpinspect-get-parser-create :declaration
+ '(comment word list terminator tag)
+ #'phpinspect-end-of-token-p))
+
+
+(phpinspect-defhandler function-keyword (start-token max-point)
+ "Handler for the function keyword and tokens that follow to give it meaning"
+ (regexp (concat "function" (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (let* ((parser (phpinspect-get-or-create-declaration-parser))
+ (continue-condition (lambda () (not (char-equal (char-after) ?{))))
+ (declaration (funcall parser (current-buffer) max-point
continue-condition)))
+ (if (phpinspect-end-of-token-p (car (last declaration)))
+ (list :function declaration)
+ (list :function
+ declaration
+ (funcall (phpinspect-handler 'block-without-scopes)
+ (char-to-string (char-after)) max-point)))))
+
+(phpinspect-defhandler scope-keyword (start-token max-point)
+ "Handler for scope keywords"
+ (regexp (mapconcat (lambda (word)
+ (concat word (phpinspect--word-end-regex)))
+ (list "public" "private" "protected")
+ "\\|"))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (forward-char (length start-token))
+ (funcall (phpinspect-get-parser-create
+ (cond ((string= start-token "public") :public)
+ ((string= start-token "private") :private)
+ ((string= start-token "protected") :protected))
+ '(function-keyword static-keyword const-keyword
+ variable here-doc string terminator tag comment)
+ #'phpinspect--scope-terminator-p)
+ (current-buffer)
+ max-point))
+
+(phpinspect-defhandler static-keyword (start-token max-point)
+ "Handler for the static keyword"
+ (regexp (concat "static" (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (forward-char (length start-token))
+ (funcall (phpinspect-get-parser-create
+ :static
+ '(comment function-keyword variable array word
+ terminator tag)
+ #'phpinspect--static-terminator-p)
+ (current-buffer)
+ max-point))
+
+(phpinspect-defhandler fat-arrow (arrow &rest _ignored)
+ "Handler for the \"fat arrow\" in arrays and foreach expressions"
+ (regexp "=>")
+ (phpinspect-munch-token-without-attribs arrow :fat-arrow))
+
+(phpinspect-defhandler array (start-token max-point)
+ "Handler for arrays, in the bracketet as well as the list notation"
+ (regexp "\\[\\|array(")
+ (forward-char (length start-token))
+ (let* ((end-char (cond ((string= start-token "[") ?\])
+ ((string= start-token "array(") ?\))))
+ (end-char-reached nil)
+ (token (funcall (phpinspect-get-parser-create
+ :array
+ '(comment comma list here-doc string
+ array variable attribute-reference
+ word fat-arrow))
+ (current-buffer)
+ max-point
+ (lambda () (not (and (char-equal (char-after)
end-char)
+ (setq end-char-reached t)))))))
+
+ ;; Skip over the end char to prevent enclosing arrays or lists
+ ;; from terminating.
+ (if end-char-reached
+ (forward-char)
+ ;; Signal incompleteness when terminated because of max-point
+ (setcar token :incomplete-array))
+ token))
+
+(phpinspect-defhandler class-keyword (start-token max-point)
+ "Handler for the class keyword, and tokens that follow to define
+the properties of the class"
+ (regexp (concat "\\(abstract\\|final\\|class\\|interface\\|trait\\)"
+ (phpinspect--word-end-regex)))
+ (setq start-token (phpinspect--strip-last-char start-token))
+ (list :class (funcall (phpinspect-get-or-create-declaration-parser)
+ (current-buffer)
+ max-point
+ (lambda () (not (char-equal (char-after) ?{))))
+ (funcall (phpinspect-handler 'class-block)
+ (char-to-string (char-after)) max-point)))
+
+(defun phpinspect-parse-with-handler-list
+ (buffer tree-type max-point &optional continue-condition
delimiter-predicate)
+ "Parse BUFFER for TREE-TYPE tokens until MAX-POINT.
+
+Stop at CONTINUE-CONDITION or DELIMITER-PREDICATE.
+
+This just calls `phpinspect-get-parser-create` to make a parser
+that contains all handlers necessary to parse code."
+ (let ((parser (phpinspect-get-parser-create
+ tree-type
+ '(array tag equals list comma
+ attribute-reference variable
+ assignment-operator whitespace scope-keyword
+ static-keyword const-keyword use-keyword
+ class-keyword function-keyword word terminator
+ here-doc string comment block)
+ delimiter-predicate)))
+ (funcall parser buffer max-point continue-condition)))
+
+
+(defun phpinspect-parse-buffer-until-point (buffer point)
+ (with-current-buffer buffer
+ (save-excursion
+ (goto-char (point-min))
+ (re-search-forward "<\\?php\\|<\\?" nil t)
+ (funcall (phpinspect-get-parser-create
+ :root
+ '(namespace array equals list comma
+ attribute-reference variable assignment-operator
+ whitespace scope-keyword static-keyword
+ const-keyword use-keyword class-keyword
+ function-keyword word terminator here-doc string
+ comment tag block))
+ (current-buffer)
+ point))))
+
+(provide 'phpinspect-parser)
+;;; phpinspect-parser.el ends here
diff --git a/phpinspect-project.el b/phpinspect-project.el
new file mode 100644
index 0000000000..2dfa5efce1
--- /dev/null
+++ b/phpinspect-project.el
@@ -0,0 +1,92 @@
+;;; phpinspect-project.el --- PHP parsing and completion package -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'phpinspect-class)
+(require 'phpinspect-type)
+
+(cl-defstruct (phpinspect--project (:constructor
phpinspect--make-project-cache))
+ (class-index (make-hash-table :test 'eq :size 100 :rehash-size 40)
+ :type hash-table
+ :documentation
+ "A `hash-table` that contains all of the currently
+indexed classes in the project"))
+
+(cl-defgeneric phpinspect--project-add-class
+ ((project phpinspect--project) (class (head phpinspect--indexed-class)))
+ "Add an indexed CLASS to PROJECT.")
+
+(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
+ (phpinspect--class-set-index existing-class indexed-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))))))
+
+(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-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))
+
+ (let* ((class-file (phpinspect-class-filepath class-fqn))
+ (visited-buffer (when class-file (find-buffer-visiting
class-file)))
+ (new-index)
+ (class-index))
+
+ (phpinspect--log "No existing index for FQN: %s" class-fqn)
+ (phpinspect--log "filepath: %s" class-file)
+ (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)))
+ (setq class-index
+ (alist-get class-fqn (alist-get 'classes new-index)
+ nil
+ nil
+ #'phpinspect--type=))
+ (when class-index
+ (phpinspect--class-set-index class class-index)))))
+ class))
+
+(cl-defmethod phpinspect--project-get-class
+ ((project phpinspect--project) (class-fqn phpinspect--type))
+ (gethash (phpinspect--type-name-symbol class-fqn)
+ (phpinspect--project-class-index project)))
+
+(provide 'phpinspect-project)
+;;; phpinspect-project.el ends here
diff --git a/phpinspect-type.el b/phpinspect-type.el
new file mode 100644
index 0000000000..0816fa04a0
--- /dev/null
+++ b/phpinspect-type.el
@@ -0,0 +1,187 @@
+;;; phpinspect-type.el --- Data structures that represent phpinspect types
-*- lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(require 'phpinspect-util)
+
+(cl-defstruct (phpinspect--type
+ (:constructor phpinspect--make-type-generated))
+ "Represents an instance of a PHP type in the phpinspect syntax tree."
+ (name-symbol nil
+ :type symbol
+ :documentation
+ "Symbol representation of the type name.")
+ (collection nil
+ :type bool
+ :documentation
+ "Whether or not the type is a collection")
+ (contains nil
+ :type phpinspect--type
+ :documentation
+ "When the type is a collection, this attribute is set to the type
that the collection is expected to contain")
+ (fully-qualified nil
+ :type bool
+ :documentation
+ "Whether or not the type name is fully qualified"))
+
+(defmacro phpinspect--make-type (&rest property-list)
+ `(phpinspect--make-type-generated
+ ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
+
+(defconst phpinspect--null-type (phpinspect--make-type :name "\\null"))
+
+(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
+ (setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
+
+(cl-defmethod phpinspect--type-name ((type phpinspect--type))
+ (symbol-name (phpinspect--type-name-symbol type)))
+
+(defun phpinspect--get-bare-class-name-from-fqn (fqn)
+ (car (last (split-string fqn "\\\\"))))
+
+(cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
+ (phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
+
+(cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2
phpinspect--type))
+ (eq (phpinspect--type-name-symbol type1) (phpinspect--type-name-symbol
type2)))
+
+(defun phpinspect--resolve-type-name (types namespace type)
+ "Get the FQN for TYPE, using TYPES and NAMESPACE as context.
+
+TYPES must be an alist with class names as cars and FQNs as cdrs.
+NAMESPACE may be nil, or a string with a namespace FQN."
+ (phpinspect--log "Resolving %s from namespace %s" type namespace)
+ ;; Absolute FQN
+ (cond ((string-match "^\\\\" type)
+ type)
+
+ ;; Native type
+ ((member type phpinspect-native-types)
+ (concat "\\" type))
+
+ ;; Relative FQN
+ ((and namespace (string-match "\\\\" type))
+ (concat "\\" namespace "\\" type))
+
+ ;; 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)))))))
+
+(cl-defmethod phpinspect--type-resolve (types namespace (type
phpinspect--type))
+ (unless (phpinspect--type-fully-qualified type)
+ (phpinspect--type-set-name
+ type
+ (phpinspect--resolve-type-name types namespace (phpinspect--type-name
type)))
+ (setf (phpinspect--type-fully-qualified type) t))
+ type)
+
+(defun phpinspect--make-type-resolver (types &optional token-tree namespace)
+ "Little wrapper closure to pass around and resolve types with."
+
+ (let* ((inside-class
+ (if token-tree (or (phpinspect--find-innermost-incomplete-class
token-tree)
+ (phpinspect--find-class-token token-tree))))
+ (inside-class-name (if inside-class
(phpinspect--get-class-name-from-token
+ inside-class)))
+ (self-type (phpinspect--make-type :name "self"))
+ (static-type (phpinspect--make-type :name "static")))
+ (lambda (type)
+ (phpinspect--type-resolve
+ types
+ namespace
+ (if (and inside-class-name (or (phpinspect--type= type self-type)
+ (phpinspect--type= type static-type)))
+ (progn
+ (phpinspect--log "Returning inside class name for %s : %s"
+ type inside-class-name)
+ (phpinspect--make-type :name inside-class-name))
+ ;; else
+ type)))))
+
+(cl-defgeneric phpinspect--format-type-name (name)
+ (string-remove-prefix "\\" name))
+
+(cl-defmethod phpinspect--format-type-name ((type phpinspect--type))
+ (phpinspect--format-type-name (phpinspect--type-name type)))
+
+(cl-defstruct (phpinspect--function (:constructor
phpinspect--make-function-generated))
+ "A PHP function."
+ (name-symbol nil
+ :type symbol
+ :documentation
+ "A symbol associated with the name of the function")
+ (scope nil
+ :type phpinspect-scope
+ :documentation
+ "When the function is a method, this should contain the
+scope of the function as returned by `phpinspect-parse-scope`.")
+ (arguments nil
+ :type list
+ :documentation
+ "A simple list with function arguments and their
+types in tuples. Each list should have the name of the variable
+as first element and the type as second element.")
+ (return-type nil
+ :type phpinspect--type
+ :documentation
+ "A phpinspect--type object representing the the
+return type of the function."))
+
+(defmacro phpinspect--make-function (&rest property-list)
+ `(phpinspect--make-function-generated
+ ,@(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)))
+
+(cl-defgeneric phpinspect--function-name ((func phpinspect--function)))
+
+(cl-defmethod phpinspect--function-name ((func phpinspect--function))
+ (symbol-name (phpinspect--function-name-symbol func)))
+
+
+(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
+ "A PHP Variable."
+ (name nil
+ :type string
+ :documentation
+ "A string containing the name of the variable.")
+ (scope nil
+ :type phpinspect-scope
+ :documentation
+ "When the variable is an object attribute, this should
+contain the scope of the variable as returned by
+`phpinspect-parse-scope`")
+ (type nil
+ :type string
+ :documentation
+ "A string containing the FQN of the variable's type"))
+
+
+(provide 'phpinspect-type)
+;;; phpinspect-type.el ends here
diff --git a/phpinspect-util.el b/phpinspect-util.el
new file mode 100644
index 0000000000..56920f506b
--- /dev/null
+++ b/phpinspect-util.el
@@ -0,0 +1,66 @@
+;;; phpinspect-util.el --- PHP parsing and completion package -*-
lexical-binding: t; -*-
+
+;; Copyright (C) 2021 Free Software Foundation, Inc
+
+;; Author: Hugo Thunnissen <devel@hugot.nl>
+;; Keywords: php, languages, tools, convenience
+;; Version: 0
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program. If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;; Code:
+
+(defvar phpinspect-name-obarray (obarray-make)
+ "An obarray containing symbols for all encountered names in
+PHP. Used to optimize string comparison.")
+
+(defvar phpinspect--debug nil
+ "Enable debug logs for phpinspect by setting this variable to true")
+
+(defsubst phpinspect-intern-name (name)
+ (intern name phpinspect-name-obarray))
+
+(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
+ (let ((new-plist)
+ (wrap-value))
+ (dolist (item property-list)
+ (when wrap-value
+ (setq item `(phpinspect-intern-name ,item))
+ (setq wrap-value nil))
+ (when (eq item :name)
+ (setq item :name-symbol)
+ (setq wrap-value t))
+ (push item new-plist))
+ (nreverse new-plist)))
+
+(defun phpinspect-toggle-logging ()
+ (interactive)
+ (if (setq phpinspect--debug (not phpinspect--debug))
+ (message "Enabled phpinspect logging.")
+ (message "Disabled phpinspect logging.")))
+
+
+(defsubst phpinspect--log (&rest args)
+ (when phpinspect--debug
+ (with-current-buffer (get-buffer-create "**phpinspect-logs**")
+ (unless window-point-insertion-type
+ (set (make-local-variable 'window-point-insertion-type) t))
+ (goto-char (buffer-end 1))
+ (insert (concat "[" (format-time-string "%H:%M:%S") "]: "
+ (apply #'format args) "\n")))))
+
+(provide 'phpinspect-util)
+;;; phpinspect-util.el ends here
diff --git a/phpinspect.el b/phpinspect.el
index d69169656c..1ceb74e6ab 100644
--- a/phpinspect.el
+++ b/phpinspect.el
@@ -29,23 +29,18 @@
(require 'json)
(require 'obarray)
-(defvar phpinspect-parser-obarray (obarray-make)
- "An obarray containing symbols for all phpinspect (sub)parsers.")
-
-(defvar phpinspect-handler-obarray (obarray-make)
- "An obarray containing symbols for all phpinspect parser handlers.")
-
-(defvar phpinspect-name-obarray (obarray-make)
- "An obarray containing symbols for all encountered names in
-PHP. Used to optimize string comparison.")
+;; internal dependencies
+(require 'phpinspect-cache)
+(require 'phpinspect-parser)
+(require 'phpinspect-project)
+(require 'phpinspect-util)
+(require 'phpinspect-type)
+(require 'phpinspect-index)
(defvar-local phpinspect--buffer-index nil
"The result of the last successfull parse + index action
executed by phpinspect for the current buffer")
-(defvar phpinspect--debug nil
- "Enable debug logs for phpinspect by setting this variable to true")
-
(defvar phpinspect-cache ()
"In-memory nested key-value store used for caching by
phpinspect")
@@ -88,905 +83,6 @@ Should normally be set to \"phpinspect-index.bash\" in the
source
'("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed")
"FQNs of types that should be treated as collecitons when inferring types.")
-(defsubst phpinspect-intern-name (name)
- (intern name phpinspect-name-obarray))
-
-(eval-when-compile
- (define-inline phpinspect--word-end-regex ()
- (inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)")))
-
-(defun phpinspect-list-handlers ()
- (let ((handlers))
- (mapatoms (lambda (handler)
- (push (symbol-name handler) handlers))
- phpinspect-handler-obarray)
- handlers))
-
-(defun phpinspect-describe-handler (handler-name)
- "Display a buffer containing HANDLER-NAMEs docstring and attribute-plist."
- (interactive (list (completing-read "Pick a handler:"
(phpinspect-list-handlers))))
- (with-current-buffer (get-buffer-create "phpinspect-handler-description")
- (insert (concat
- (pp (symbol-value (intern handler-name
phpinspect-handler-obarray)))
- "\n"
- (documentation (intern handler-name phpinspect-handler-obarray))))
- (pop-to-buffer (current-buffer))))
-
-(defsubst phpinspect--strip-last-char (string)
- (substring string 0 (- (length string) 1)))
-
-(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
- "Return a token of type TOKEN-KEYWORD with STRING as value.
-If STRING has text properties, they are stripped."
- (let ((value (copy-sequence string))
- (length (length string)))
- (forward-char length)
- (set-text-properties 0 length nil value)
- (list token-keyword value)))
-
-
-(defsubst phpinspect-token-type-p (object type)
- "Returns t if OBJECT is a token of type TYPE.
-Type can be any of the token types returned by
-`phpinspect-parse-buffer-until-point`"
- (and (listp object) (eq (car object) type)))
-
-(defsubst phpinspect-object-attrib-p (token)
- (phpinspect-token-type-p token :object-attrib))
-
-(defsubst phpinspect-static-attrib-p (token)
- (phpinspect-token-type-p token :static-attrib))
-
-(defsubst phpinspect-attrib-p (token)
- (or (phpinspect-object-attrib-p token)
- (phpinspect-static-attrib-p token)))
-
-(defun phpinspect-html-p (token)
- (phpinspect-token-type-p token :html))
-
-(defun phpinspect-comma-p (token)
- (phpinspect-token-type-p token :comma))
-
-(defsubst phpinspect-terminator-p (token)
- (phpinspect-token-type-p token :terminator))
-
-(defsubst phpinspect-end-of-token-p (token)
- (or (phpinspect-terminator-p token)
- (phpinspect-comma-p token)
- (phpinspect-html-p token)))
-
-(defsubst phpinspect-end-of-statement-p (token)
- (or (phpinspect-end-of-token-p token)
- (phpinspect-block-p token)))
-
-(defsubst phpinspect-incomplete-block-p (token)
- (phpinspect-token-type-p token :incomplete-block))
-
-(defsubst phpinspect-block-p (token)
- (or (phpinspect-token-type-p token :block)
- (phpinspect-incomplete-block-p token)))
-
-(defun phpinspect-end-of-use-p (token)
- (or (phpinspect-block-p token)
- (phpinspect-end-of-token-p token)))
-
-(defun phpinspect-static-p (token)
- (phpinspect-token-type-p token :static))
-
-(defsubst phpinspect-incomplete-const-p (token)
- (phpinspect-token-type-p token :incomplete-const))
-
-(defsubst phpinspect-const-p (token)
- (or (phpinspect-token-type-p token :const)
- (phpinspect-incomplete-const-p token)))
-
-(defsubst phpinspect-scope-p (token)
- (or (phpinspect-token-type-p token :public)
- (phpinspect-token-type-p token :private)
- (phpinspect-token-type-p token :protected)))
-
-(defsubst phpinspect-namespace-p (object)
- (phpinspect-token-type-p object :namespace))
-
-(defun phpinspect-incomplete-class-p (token)
- (and (phpinspect-class-p token)
- (phpinspect-incomplete-block-p (car (last token)))))
-
-(defun phpinspect-incomplete-namespace-p (token)
- (and (phpinspect-namespace-p token)
- (or (phpinspect-incomplete-block-p (car (last token)))
- (phpinspect-incomplete-class-p (car (last token))))))
-
-(defun phpinspect-function-p (token)
- (phpinspect-token-type-p token :function))
-
-
-(defun phpinspect-class-p (token)
- (phpinspect-token-type-p token :class))
-
-(defun phpinspect-incomplete-method-p (token)
- (or (phpinspect-incomplete-function-p token)
- (and (phpinspect-scope-p token)
- (phpinspect-incomplete-function-p (car (last token))))
- (and (phpinspect-scope-p token)
- (phpinspect-static-p (car (last token)))
- (phpinspect-incomplete-function-p (car (last (car (last token))))))
- (and (phpinspect-scope-p token)
- (phpinspect-function-p (car (last token))))))
-
-(defun phpinspect-incomplete-function-p (token)
- (and (phpinspect-function-p token)
- (phpinspect-incomplete-block-p (car (last token)))))
-
-(defsubst phpinspect-incomplete-list-p (token)
- (phpinspect-token-type-p token :incomplete-list))
-
-(defsubst phpinspect-list-p (token)
- (or (phpinspect-token-type-p token :list)
- (phpinspect-incomplete-list-p token)))
-
-(defun phpinspect-declaration-p (token)
- (phpinspect-token-type-p token :declaration))
-
-(defsubst phpinspect-assignment-p (token)
- (phpinspect-token-type-p token :assignment))
-
-(defun phpinspect-function-argument-list (php-func)
- "Get the argument list of a function"
- (seq-find #'phpinspect-list-p (seq-find #'phpinspect-declaration-p php-func
nil) nil))
-
-(defsubst phpinspect-variable-p (token)
- (phpinspect-token-type-p token :variable))
-
-(defsubst phpinspect-word-p (token)
- (phpinspect-token-type-p token :word))
-
-(defsubst phpinspect-incomplete-array-p (token)
- (phpinspect-token-type-p token :incomplete-array))
-
-(defsubst phpinspect-array-p (token)
- (or (phpinspect-token-type-p token :array)
- (phpinspect-incomplete-array-p token)))
-
-(defsubst phpinspect-incomplete-token-p (token)
- (or (phpinspect-incomplete-class-p token)
- (phpinspect-incomplete-block-p token)
- (phpinspect-incomplete-list-p token)
- (phpinspect-incomplete-array-p token)
- (phpinspect-incomplete-const-p token)
- (phpinspect-incomplete-function-p token)
- (phpinspect-incomplete-method-p token)
- (phpinspect-incomplete-namespace-p token)))
-
-(defun phpinspect--static-terminator-p (token)
- (or (phpinspect-function-p token)
- (phpinspect-end-of-token-p token)))
-
-(defun phpinspect--scope-terminator-p (token)
- (or (phpinspect-function-p token)
- (phpinspect-end-of-token-p token)
- (phpinspect-const-p token)
- (phpinspect-static-p token)))
-
-(defun phpinspect-namespace-keyword-p (token)
- (and (phpinspect-word-p token) (string= (car (last token)) "namespace")))
-
-(defun phpinspect-use-keyword-p (token)
- (and (phpinspect-word-p token) (string= (car (last token)) "use")))
-
-
-(defsubst phpinspect-root-p (object)
- (phpinspect-token-type-p object :root))
-
-(defsubst phpinspect-namespace-or-root-p (object)
- (or (phpinspect-namespace-p object)
- (phpinspect-root-p object)))
-
-(defun phpinspect-use-p (object)
- (phpinspect-token-type-p object :use))
-
-(defun phpinspect-comment-p (token)
- (phpinspect-token-type-p token :comment))
-
-(defsubst phpinspect-class-block (class)
- (caddr class))
-
-(defsubst phpinspect-namespace-block (namespace)
- (when (and (= (length namespace) 3)
- (phpinspect-block-p (caddr namespace)))
- (caddr namespace)))
-
-(defsubst phpinspect-function-block (php-func)
- (caddr php-func))
-
-(defsubst phpinspect-not-class-p (token)
- "Apply inverse of `phpinspect-class-p' to TOKEN."
- (not (phpinspect-class-p token)))
-
-(defmacro phpinspect-defhandler (name arguments docstring attribute-plist
&rest body)
- "Define a parser handler that becomes available for use with
phpinspect-parse.
-
-A parser handler is a function that is able to identify and parse
-tokens from PHP code at `point` in the current buffer. It's
-return value must be the resulting token. Aside from parsing it
-has to manage the state of `point` in a way that it skips over
-the tokens it has parsed. That way the next handler can
-correctly pick up from where it has left off.
-
-Parser handlers are unrolled in a `cond` statement by
-`phpinspect-make-parser`. The resulting code is something akin
-to the following:
-
-(while ...
- (cond (((looking-at \"{\")
- (funcall block-handler (match-string 0) max-point)
- ((looking-at \"\\$\")
- (funcall variable-handler ...
-etc.
-
-NAME must be a symbol. It does not need to be prefixed with a
-\"namespace\" because parser handlers are stored in their own
-obarray (`phpinspect-handler-obarray`).
-
-ARGUMENTS must an argument list as accepted by `lambda`. A
-handler must be able to accept 2 arguments: START-TOKEN and
-MAX-POINT. START-TOKEN is the match string that resulted from
-the comparison of the handlers' `regexp` attribute with the text
-at `point`. MAX-POINT is the point in the current buffer up
-until which the parser is supposed to parse. For some tokens you
-may not want/need to respect MAX-POINT, in which case you can
-ignore it.
-
-DOCSTRING is mandatory. It should contain a description of the
-tokens the handler is able to process and (if present) any
-particularities of the handler.
-
-ATTRIBUTE-PLIST is a plist that must contain at least a `regexp` key.
- Possible keys:
- - regexp: The regular expression that marks the start of the token.
-
-BODY is a function body as accepted by `lambda` that parses the
-text at point and returns the resulting token.
-
-When altering/adding handlers during runtime, make sure to purge
-the parser cache to make sure that your new handler functions are used.
-You can purge the parser cache with \\[phpinspect-purge-parser-cache]."
- (declare (indent defun))
- (when (not (symbolp name))
- (error "In definition of phpinspect handler %s: NAME bust be a symbol"
name))
-
- (when (not (plist-member attribute-plist 'regexp))
- (error "In definition of phpinspect handler %s ATTRIBUTE-PLIST must
contain key `regexp`"
- name))
-
- ;; Eval regexp. It might be a `concat` statement and we don't want to be
executing that
- ;; every time the parser advances one character and has to check for the
regexp
- ;; occurence.
- (setq attribute-plist (plist-put attribute-plist 'regexp
- (eval (plist-get attribute-plist 'regexp)
- t)))
- (let ((name (symbol-name name)))
- `(progn
- (set (intern ,name phpinspect-handler-obarray) (quote ,attribute-plist))
- (defalias (intern ,name phpinspect-handler-obarray)
- #'(lambda (,@arguments)
- ,docstring
- ,@body))
- (unless (byte-code-function-p
- (symbol-function
- (intern ,name phpinspect-handler-obarray)))
- (byte-compile (intern ,name phpinspect-handler-obarray))))))
-
-(defun phpinspect-get-parser-create (tree-type &rest parser-parameters)
- "Retrieve a parser for TREE-TYPE from `phpinspect-parser-obarray'.
-
-TREE-TYPE must be a symbol or keyword representing the type of
-the token the parser is able to parse.
-
-If a parser by TREE-TYPE doesn't exist, it is created by callng
-`phpinspect-make-parser` with TREE-TYPE as first argument and
-PARSER-PARAMETERS as the rest of the arguments. The resulting
-parser function is then returned in byte-compiled form."
- (let* ((parser-name (symbol-name tree-type))
- (parser-symbol (intern-soft parser-name phpinspect-parser-obarray)))
- (or (and parser-symbol (symbol-function parser-symbol))
- (defalias (intern parser-name phpinspect-parser-obarray)
- (byte-compile (apply #'phpinspect-make-parser
- `(,tree-type ,@parser-parameters)))))))
-
-(defun phpinspect-purge-parser-cache ()
- "Empty `phpinspect-parser-obarray`.
-
-This is useful when you need to change parser handlers or parsers
-during runtime. Parsers are implemented with macros, so changing
-handler functions without calling this function will often not
-have any effect."
- (interactive)
- (setq phpinspect-parser-obarray (obarray-make)))
-
-(defun phpinspect-make-parser (tree-type handler-list &optional
delimiter-predicate)
- "Create a parser function using the handlers by names defined in
HANDLER-LIST.
-
-See also `phpinspect-defhandler`.
-
-TREE-TYPE must be a symbol or a keyword representing the token
-type.
-
-HANDLER-LIST must be a list of either symbol or string
-representation of handler symbols which can be found in
-`phpinspect-handler-obarray`.
-
-DELIMITER-PREDICATE must be a function. It is passed the last
-parsed token after every handler iteration. If it evaluates to
-something other than nil, parsing is deemed completed and the
-loop exits. An example use case of this is to determine the end
-of a statement. You can use `phpinspect-terminator-p` as
-delimiter predicate and have parsing stop when the last parsed
-token is \";\", which marks the end of a statement in PHP."
- (let ((handlers (mapcar
- (lambda (handler-name)
- (let* ((handler-name (symbol-name handler-name))
- (handler (intern-soft handler-name
phpinspect-handler-obarray)))
- (if handler
- handler
- (error "No handler found by name \"%s\""
handler-name))))
- handler-list))
- (delimiter-predicate (if (symbolp delimiter-predicate)
- `(quote ,delimiter-predicate)
- delimiter-predicate)))
- `(lambda (buffer max-point &optional continue-condition)
- (with-current-buffer buffer
- (let ((tokens)
- (delimiter-predicate (when (functionp ,delimiter-predicate)
,delimiter-predicate)))
- (while (and (< (point) max-point)
- (if continue-condition (funcall continue-condition) t)
- (not (if delimiter-predicate
- (funcall delimiter-predicate (car (last
tokens)))
- nil)))
- (cond ,@(mapcar
- (lambda (handler)
- `((looking-at ,(plist-get (symbol-value handler)
'regexp))
- (let ((token (funcall ,(symbol-function handler)
- (match-string 0)
- max-point)))
- (when token
- (if (null tokens)
- (setq tokens (list token))
- (nconc tokens (list token)))))))
- handlers)
- (t (forward-char))))
- (push ,tree-type tokens))))))
-
-(phpinspect-defhandler comma (comma &rest _ignored)
- "Handler for comma tokens"
- (regexp ",")
- (phpinspect-munch-token-without-attribs comma :comma))
-
-(phpinspect-defhandler word (word &rest _ignored)
- "Handler for bareword tokens"
- (regexp "[A-Za-z_\\][\\A-Za-z_0-9]*")
- (let ((length (length word)))
- (forward-char length)
- (set-text-properties 0 length nil word)
- (list :word word)))
-
-(defsubst phpinspect-handler (handler-name)
- (intern-soft (symbol-name handler-name) phpinspect-handler-obarray))
-
-(defsubst phpinspect-handler-regexp (handler-name)
- (plist-get (symbol-value (phpinspect-handler handler-name)) 'regexp))
-
-(defsubst phpinspect--parse-annotation-parameters (parameter-amount)
- (let* ((words)
- (word-handler (phpinspect-handler 'word))
- (word-regexp (phpinspect-handler-regexp 'word))
- (variable-handler (phpinspect-handler 'variable))
- (variable-regexp (phpinspect-handler-regexp 'variable)))
- (while (not (or (looking-at "\\*/") (= (length words) parameter-amount)))
- (forward-char)
- (cond ((looking-at word-regexp)
- (push (funcall word-handler (match-string 0)) words))
- ((looking-at variable-regexp)
- (push (funcall variable-handler (match-string 0)) words))))
- (nreverse words)))
-
-(phpinspect-defhandler annotation (start-token &rest _ignored)
- "Handler for in-comment @annotations"
- (regexp "@")
- (forward-char (length start-token))
- (if (looking-at (phpinspect-handler-regexp 'word))
- (let ((annotation-name (match-string 0)))
- (forward-char (length annotation-name))
- (cond ((string= annotation-name "var")
- ;; The @var annotation accepts 2 parameters:
- ;; the type and the $variable name
- (append (list :var-annotation)
- (phpinspect--parse-annotation-parameters 2)))
- ((string= annotation-name "return")
- ;; The @return annotation only accepts 1 word as parameter:
- ;; The return type
- (append (list :return-annotation)
- (phpinspect--parse-annotation-parameters 1)))
- ((string= annotation-name "param")
- ;; The @param annotation accepts 2 parameters:
- ;; The type of the param, and the param's $name
- (append (list :param-annotation)
- (phpinspect--parse-annotation-parameters 2)))
- (t
- (list :annotation annotation-name))))
- (list :annotation nil)))
-
-(phpinspect-defhandler tag (start-token max-point)
- "Handler that discards any inline HTML it encounters"
- (regexp "\\?>")
- (forward-char (length start-token))
- (or (re-search-forward "<\\?php\\|<\\?" nil t)
- (goto-char max-point))
- (list :html))
-
-(phpinspect-defhandler comment (start-token max-point)
- "Handler for comments and doc blocks"
- (regexp "#\\|//\\|/\\*")
- (forward-char (length start-token))
-
- (cond ((string-match "/\\*" start-token)
- (let* ((continue-condition (lambda () (not (looking-at "\\*/"))))
- (parser (phpinspect-get-parser-create
- :doc-block
- '(annotation whitespace)))
- (doc-block (funcall parser (current-buffer) max-point
continue-condition)))
- (forward-char 2)
- doc-block))
- (t
- (let ((parser (phpinspect-get-parser-create :comment '(tag)
#'phpinspect-html-p))
- (end-position (line-end-position)))
- (funcall parser (current-buffer) end-position)))))
-
-(phpinspect-defhandler variable (start-token &rest _ignored)
- "Handler for tokens indicating reference to a variable"
- (regexp "\\$")
- (forward-char (length start-token))
- (if (looking-at (phpinspect-handler-regexp 'word))
- (phpinspect-munch-token-without-attribs (match-string 0) :variable)
- (list :variable nil)))
-
-(phpinspect-defhandler whitespace (whitespace &rest _ignored)
- "Handler that discards whitespace"
- (regexp "[[:blank:]]+")
- (forward-char (length whitespace)))
-
-(phpinspect-defhandler equals (equals &rest _ignored)
- "Handler for strict and unstrict equality comparison tokens."
- (regexp "===?")
- (phpinspect-munch-token-without-attribs equals :equals))
-
-(phpinspect-defhandler assignment-operator (operator &rest _ignored)
- "Handler for tokens indicating that an assignment is taking place"
- (regexp "[+-]?=")
- (phpinspect-munch-token-without-attribs operator :assignment))
-
-(phpinspect-defhandler terminator (terminator &rest _ignored)
- "Handler for statement terminators."
- (regexp ";")
- (phpinspect-munch-token-without-attribs terminator :terminator))
-
-(phpinspect-defhandler use-keyword (start-token max-point)
- "Handler for the use keyword and tokens that might follow to give it meaning"
- (regexp (concat "use" (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (forward-char (length start-token))
-
- (let ((parser (phpinspect-get-parser-create
- :use
- '(word tag block-without-scopes terminator)
- #'phpinspect-end-of-use-p)))
- (funcall parser (current-buffer) max-point)))
-
-(phpinspect-defhandler attribute-reference (start-token &rest _ignored)
- "Handler for references to object attributes, or static class attributes."
- (regexp "->\\|::")
- (forward-char (length start-token))
- (looking-at (phpinspect-handler-regexp 'word))
- (let ((name (if (looking-at (phpinspect-handler-regexp 'word))
- (funcall (phpinspect-handler 'word) (match-string 0))
- nil)))
- (cond
- ((string= start-token "::")
- (list :static-attrib name))
- ((string= start-token "->")
- (list :object-attrib name)))))
-
-(phpinspect-defhandler namespace (start-token max-point)
- "Handler for the namespace keyword. This is a special one
- because it is not always delimited by a block like classes or
- functions. This handler parses the namespace declaration, and
- then continues to parse subsequent tokens, only stopping when
- either a block has been parsed or another namespace keyword has
- been encountered."
- (regexp (concat "namespace" (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (forward-char (length start-token))
- (phpinspect-parse-with-handler-list
- (current-buffer)
- :namespace
- max-point
- (lambda () (not (looking-at (phpinspect-handler-regexp 'namespace))))
- #'phpinspect-block-p))
-
-(phpinspect-defhandler const-keyword (start-token max-point)
- "Handler for the const keyword."
- (regexp (concat "const" (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (forward-char (length start-token))
- (let* ((parser (phpinspect-get-parser-create
- :const
- '(word comment assignment-operator string array
- terminator)
- #'phpinspect-end-of-token-p))
- (token (funcall parser (current-buffer) max-point)))
- (when (phpinspect-incomplete-token-p (car (last token)))
- (setcar token :incomplete-const))
- token))
-
-(phpinspect-defhandler string (start-token &rest _ignored)
- "Handler for strings"
- (regexp "\"\\|'")
- (list :string (phpinspect--munch-string start-token)))
-
-(phpinspect-defhandler block-without-scopes (start-token max-point)
- "Handler for code blocks that cannot contain scope, const or
-static keywords with the same meaning as in a class block."
- (regexp "{")
- (forward-char (length start-token))
- (let* ((complete-block nil)
- (parser (phpinspect-get-parser-create
- :block
- '(array tag equals list comma
- attribute-reference variable
- assignment-operator whitespace
- function-keyword word terminator here-doc
- string comment block-without-scopes)))
- (continue-condition (lambda ()
- (not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
- (parsed (funcall parser (current-buffer) max-point
continue-condition)))
- (if complete-block
- (forward-char)
- (setcar parsed :incomplete-block))
- parsed))
-
-
-(phpinspect-defhandler class-block (start-token max-point)
- "Handler for code blocks that cannot contain classes"
- (regexp "{")
- (forward-char (length start-token))
- (let* ((complete-block nil)
- (parser (phpinspect-get-parser-create
- :block
- '(array tag equals list comma
- attribute-reference variable
- assignment-operator whitespace scope-keyword
- static-keyword const-keyword use-keyword
- function-keyword word terminator here-doc
- string comment block)))
- (continue-condition (lambda ()
- (not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
- (parsed (funcall parser (current-buffer) max-point
continue-condition)))
- (if complete-block
- (forward-char)
- (setcar parsed :incomplete-block))
- parsed))
-
-(phpinspect-defhandler block (start-token max-point)
- "Handler for code blocks"
- (regexp "{")
- (forward-char (length start-token))
- (let* ((complete-block nil)
- (continue-condition (lambda ()
- ;; When we encounter a closing brace for this
- ;; block, we can mark the block as complete.
- (not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
- (parsed (phpinspect-parse-with-handler-list
- (current-buffer) :block max-point continue-condition)))
- (if complete-block
- ;; After meeting the char-after requirement above, we need to move
- ;; one char forward to prevent parent-blocks from exiting because
- ;; of the same char.
- (forward-char)
- (setcar parsed :incomplete-block))
- parsed))
-
-(phpinspect-defhandler here-doc (start-token &rest _ignored)
- "Handler for heredocs. Discards their contents."
- (regexp "<<<")
- (forward-char (length start-token))
- (if (looking-at "[A-Za-z0-9'\"\\_]+")
- (re-search-forward (concat "^" (regexp-quote (match-string 0))) nil t))
- (list :here-doc))
-
-
-(defun phpinspect--munch-string (start-token)
- "Consume text at point until a non-escaped `START-TOKEN` is found.
-
-Returns the consumed text string without face properties."
- (forward-char (length start-token))
- (let ((start-point (point)))
- (cond ((looking-at start-token)
- (forward-char)
- "")
- ((looking-at (concat "\\([\\][\\]\\)+" (regexp-quote start-token)))
- (let ((match (match-string 0)))
- (forward-char (length match))
- (buffer-substring-no-properties start-point
- (+ start-point (- (length match)
- (length
start-token))))))
- (t
- (re-search-forward (format "\\([^\\]\\([\\][\\]\\)+\\|[^\\]\\)%s"
- (regexp-quote start-token))
- nil t)
- (buffer-substring-no-properties start-point (- (point) 1))))))
-
-(phpinspect-defhandler list (start-token max-point)
- "Handler for php syntactic lists (Note: this does not include
-datatypes like arrays, merely lists that are of a syntactic
-nature like argument lists"
- (regexp "(")
- (forward-char (length start-token))
- (let* ((complete-list nil)
- (php-list (funcall
- (phpinspect-get-parser-create
- :list
- '(array tag equals list comma
- attribute-reference variable
- assignment-operator whitespace
- function-keyword word terminator here-doc
- string comment block-without-scopes))
- (current-buffer)
- max-point
- (lambda () (not (and (char-equal (char-after) ?\)) (setq
complete-list t)))))))
-
- (if complete-list
- ;; Prevent parent-lists (if any) from exiting by skipping over the
- ;; ")" character
- (forward-char)
- (setcar php-list :incomplete-list))
- php-list))
-
-;; TODO: Look into using different names for function and class :declaration
tokens. They
-;; don't necessarily require the same handlers to parse.
-(defsubst phpinspect-get-or-create-declaration-parser ()
- (phpinspect-get-parser-create :declaration
- '(comment word list terminator tag)
- #'phpinspect-end-of-token-p))
-
-
-(phpinspect-defhandler function-keyword (start-token max-point)
- "Handler for the function keyword and tokens that follow to give it meaning"
- (regexp (concat "function" (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (let* ((parser (phpinspect-get-or-create-declaration-parser))
- (continue-condition (lambda () (not (char-equal (char-after) ?{))))
- (declaration (funcall parser (current-buffer) max-point
continue-condition)))
- (if (phpinspect-end-of-token-p (car (last declaration)))
- (list :function declaration)
- (list :function
- declaration
- (funcall (phpinspect-handler 'block-without-scopes)
- (char-to-string (char-after)) max-point)))))
-
-(phpinspect-defhandler scope-keyword (start-token max-point)
- "Handler for scope keywords"
- (regexp (mapconcat (lambda (word)
- (concat word (phpinspect--word-end-regex)))
- (list "public" "private" "protected")
- "\\|"))
- (setq start-token (phpinspect--strip-last-char start-token))
- (forward-char (length start-token))
- (funcall (phpinspect-get-parser-create
- (cond ((string= start-token "public") :public)
- ((string= start-token "private") :private)
- ((string= start-token "protected") :protected))
- '(function-keyword static-keyword const-keyword
- variable here-doc string terminator tag comment)
- #'phpinspect--scope-terminator-p)
- (current-buffer)
- max-point))
-
-(phpinspect-defhandler static-keyword (start-token max-point)
- "Handler for the static keyword"
- (regexp (concat "static" (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (forward-char (length start-token))
- (funcall (phpinspect-get-parser-create
- :static
- '(comment function-keyword variable array word
- terminator tag)
- #'phpinspect--static-terminator-p)
- (current-buffer)
- max-point))
-
-(phpinspect-defhandler fat-arrow (arrow &rest _ignored)
- "Handler for the \"fat arrow\" in arrays and foreach expressions"
- (regexp "=>")
- (phpinspect-munch-token-without-attribs arrow :fat-arrow))
-
-(phpinspect-defhandler array (start-token max-point)
- "Handler for arrays, in the bracketet as well as the list notation"
- (regexp "\\[\\|array(")
- (forward-char (length start-token))
- (let* ((end-char (cond ((string= start-token "[") ?\])
- ((string= start-token "array(") ?\))))
- (end-char-reached nil)
- (token (funcall (phpinspect-get-parser-create
- :array
- '(comment comma list here-doc string
- array variable attribute-reference
- word fat-arrow))
- (current-buffer)
- max-point
- (lambda () (not (and (char-equal (char-after)
end-char)
- (setq end-char-reached t)))))))
-
- ;; Skip over the end char to prevent enclosing arrays or lists
- ;; from terminating.
- (if end-char-reached
- (forward-char)
- ;; Signal incompleteness when terminated because of max-point
- (setcar token :incomplete-array))
- token))
-
-(phpinspect-defhandler class-keyword (start-token max-point)
- "Handler for the class keyword, and tokens that follow to define
-the properties of the class"
- (regexp (concat "\\(abstract\\|final\\|class\\|interface\\|trait\\)"
- (phpinspect--word-end-regex)))
- (setq start-token (phpinspect--strip-last-char start-token))
- (list :class (funcall (phpinspect-get-or-create-declaration-parser)
- (current-buffer)
- max-point
- (lambda () (not (char-equal (char-after) ?{))))
- (funcall (phpinspect-handler 'class-block)
- (char-to-string (char-after)) max-point)))
-
-(defun phpinspect-parse-with-handler-list
- (buffer tree-type max-point &optional continue-condition
delimiter-predicate)
- "Parse BUFFER for TREE-TYPE tokens until MAX-POINT.
-
-Stop at CONTINUE-CONDITION or DELIMITER-PREDICATE.
-
-This just calls `phpinspect-get-parser-create` to make a parser
-that contains all handlers necessary to parse code."
- (let ((parser (phpinspect-get-parser-create
- tree-type
- '(array tag equals list comma
- attribute-reference variable
- assignment-operator whitespace scope-keyword
- static-keyword const-keyword use-keyword
- class-keyword function-keyword word terminator
- here-doc string comment block)
- delimiter-predicate)))
- (funcall parser buffer max-point continue-condition)))
-
-
-(defun phpinspect-parse-buffer-until-point (buffer point)
- (with-current-buffer buffer
- (save-excursion
- (goto-char (point-min))
- (re-search-forward "<\\?php\\|<\\?" nil t)
- (funcall (phpinspect-get-parser-create
- :root
- '(namespace array equals list comma
- attribute-reference variable assignment-operator
- whitespace scope-keyword static-keyword
- const-keyword use-keyword class-keyword
- function-keyword word terminator here-doc string
- comment tag block))
- (current-buffer)
- point))))
-
-(cl-defstruct (phpinspect--type
- (:constructor phpinspect--make-type-generated))
- "Represents an instance of a PHP type in the phpinspect syntax tree."
- (name-symbol nil
- :type symbol
- :documentation
- "Symbol representation of the type name.")
- (collection nil
- :type bool
- :documentation
- "Whether or not the type is a collection")
- (contains nil
- :type phpinspect--type
- :documentation
- "When the type is a collection, this attribute is set to the type
that the collection is expected to contain")
- (fully-qualified nil
- :type bool
- :documentation
- "Whether or not the type name is fully qualified"))
-
-(defsubst phpinspect--wrap-plist-name-in-symbol (property-list)
- (let ((new-plist)
- (wrap-value))
- (dolist (item property-list)
- (when wrap-value
- (setq item `(phpinspect-intern-name ,item))
- (setq wrap-value nil))
- (when (eq item :name)
- (setq item :name-symbol)
- (setq wrap-value t))
- (push item new-plist))
- (nreverse new-plist)))
-
-(defmacro phpinspect--make-type (&rest property-list)
- `(phpinspect--make-type-generated
- ,@(phpinspect--wrap-plist-name-in-symbol property-list)))
-
-(defconst phpinspect--null-type (phpinspect--make-type :name "\\null"))
-
-(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
- (setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
-
-(cl-defmethod phpinspect--type-name ((type phpinspect--type))
- (symbol-name (phpinspect--type-name-symbol type)))
-
-(cl-defmethod phpinspect--type= ((type1 phpinspect--type) (type2
phpinspect--type))
- (eq (phpinspect--type-name-symbol type1) (phpinspect--type-name-symbol
type2)))
-
-(cl-defstruct (phpinspect--function (:constructor
phpinspect--make-function-generated))
- "A PHP function."
- (name-symbol nil
- :type symbol
- :documentation
- "A symbol associated with the name of the function")
- (scope nil
- :type phpinspect-scope
- :documentation
- "When the function is a method, this should contain the
-scope of the function as returned by `phpinspect-parse-scope`.")
- (arguments nil
- :type list
- :documentation
- "A simple list with function arguments and their
-types in tuples. Each list should have the name of the variable
-as first element and the type as second element.")
- (return-type nil
- :type phpinspect--type
- :documentation
- "A phpinspect--type object representing the the
-return type of the function."))
-
-(defmacro phpinspect--make-function (&rest property-list)
- `(phpinspect--make-function-generated
- ,@(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)))
-
-(cl-defgeneric phpinspect--function-name ((func phpinspect--function)))
-
-(cl-defmethod phpinspect--function-name ((func phpinspect--function))
- (symbol-name (phpinspect--function-name-symbol func)))
-
-
-(cl-defstruct (phpinspect--variable (:constructor phpinspect--make-variable))
- "A PHP Variable."
- (name nil
- :type string
- :documentation
- "A string containing the name of the variable.")
- (scope nil
- :type phpinspect-scope
- :documentation
- "When the variable is an object attribute, this should
-contain the scope of the variable as returned by
-`phpinspect-parse-scope`")
- (type nil
- :type string
- :documentation
- "A string containing the FQN of the variable's type"))
-
(cl-defstruct (phpinspect--completion
(:constructor phpinspect--construct-completion))
"Contains a possible completion value with all it's attributes."
@@ -995,11 +91,6 @@ contain the scope of the variable as returned by
(annotation nil :type string)
(kind nil :type symbol))
-(cl-defgeneric phpinspect--format-type-name (name)
- (string-remove-prefix "\\" name))
-
-(cl-defmethod phpinspect--format-type-name ((type phpinspect--type))
- (phpinspect--format-type-name (phpinspect--type-name type)))
(cl-defgeneric phpinspect--make-completion (completion-candidate)
"Creates a `phpinspect--completion` for a possible completion
@@ -1078,21 +169,6 @@ accompanied by all of its enclosing tokens."
resolvecontext))
resolvecontext)))
-(defun phpinspect-toggle-logging ()
- (interactive)
- (if (setq phpinspect--debug (not phpinspect--debug))
- (message "Enabled phpinspect logging.")
- (message "Disabled phpinspect logging.")))
-
-
-(defsubst phpinspect--log (&rest args)
- (when phpinspect--debug
- (with-current-buffer (get-buffer-create "**phpinspect-logs**")
- (unless window-point-insertion-type
- (set (make-local-variable 'window-point-insertion-type) t))
- (goto-char (buffer-end 1))
- (insert (concat "[" (format-time-string "%H:%M:%S") "]: "
- (apply #'format args) "\n")))))
(defsubst phpinspect-cache-project-class (project-root indexed-class)
(when project-root
@@ -1126,33 +202,6 @@ accompanied by all of its enclosing tokens."
(seq-uniq classes #'eq)))
-
-;; (defun phpinspect-get-cached-project-class-methods
-;; (project-root class-fqn &optional static)
-;; (phpinspect--log "Getting cached project class methods for %s (%s)"
-;; project-root class-fqn)
-;; (when project-root
-;; (let ((index (phpinspect-get-or-create-cached-project-class
-;; project-root
-;; class-fqn)))
-;; (when index
-;; (phpinspect--log "Retrieved class index, starting method collection
%s (%s)"
-;; project-root class-fqn)
-
-;; (let ((methods-key (if static 'static-methods 'methods))
-;; (methods))
-
-;; (dolist (class (phpinspect-get-project-class-inherit-classes
-;; project-root
-;; index))
-;; (dolist (method (alist-get methods-key class))
-;; (push method methods)))
-
-;; (dolist (method (alist-get methods-key index))
-;; (push method methods))
-
-;; (nreverse methods))))))
-
(defun phpinspect-get-cached-project-class-methods (project-root class-fqn
&optional static)
(phpinspect--log "Getting cached project class methods for %s (%s)"
project-root class-fqn)
@@ -1175,19 +224,6 @@ accompanied by all of its enclosing tokens."
,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
-;; (phpinspect--log "Getting cached project class method type for %s
(%s::%s)"
-;; project-root class-fqn method-name)
-;; (let ((found-method
-;; (phpinspect-find-function-in-list
-;; method-name
-;; (phpinspect-get-cached-project-class-methods project-root
class-fqn))))
-;; (when found-method
-;; (phpinspect--log "Found method: %s" found-method)
-;; (phpinspect--function-return-type found-method)))))
-
(defsubst phpinspect-get-cached-project-class-method-type
(project-root class-fqn method-name)
(when project-root
@@ -1636,427 +672,6 @@ resolve types of function argument variables."
(phpinspect-function-argument-list enclosing-token))))))
type))
-(defun phpinspect--function-from-scope (scope)
- (cond ((and (phpinspect-static-p (cadr scope))
- (phpinspect-function-p (caddr scope)))
- (caddr scope))
- ((phpinspect-function-p (cadr scope))
- (cadr scope))
- (t nil)))
-
-(defun phpinspect--make-type-resolver (types &optional token-tree namespace)
- "Little wrapper closure to pass around and resolve types with."
-
- (let* ((inside-class
- (if token-tree (or (phpinspect--find-innermost-incomplete-class
token-tree)
- (phpinspect--find-class-token token-tree))))
- (inside-class-name (if inside-class
(phpinspect--get-class-name-from-token
- inside-class)))
- (self-type (phpinspect--make-type :name "self"))
- (static-type (phpinspect--make-type :name "static")))
- (lambda (type)
- (phpinspect--type-resolve
- types
- namespace
- (if (and inside-class-name (or (phpinspect--type= type self-type)
- (phpinspect--type= type static-type)))
- (progn
- (phpinspect--log "Returning inside class name for %s : %s"
- type inside-class-name)
- (phpinspect--make-type :name inside-class-name))
- ;; else
- type)))))
-
-
-(defun phpinspect--resolve-type-name (types namespace type)
- "Get the FQN for TYPE, using TYPES and NAMESPACE as context.
-
-TYPES must be an alist with class names as cars and FQNs as cdrs.
-NAMESPACE may be nil, or a string with a namespace FQN."
- (phpinspect--log "Resolving %s from namespace %s" type namespace)
- ;; Absolute FQN
- (cond ((string-match "^\\\\" type)
- type)
-
- ;; Native type
- ((member type phpinspect-native-types)
- (concat "\\" type))
-
- ;; Relative FQN
- ((and namespace (string-match "\\\\" type))
- (concat "\\" namespace "\\" type))
-
- ;; 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)))))))
-
-(cl-defmethod phpinspect--type-resolve (types namespace (type
phpinspect--type))
- (unless (phpinspect--type-fully-qualified type)
- (phpinspect--type-set-name
- type
- (phpinspect--resolve-type-name types namespace (phpinspect--type-name
type)))
- (setf (phpinspect--type-fully-qualified type) t))
- type)
-
-(defun phpinspect-var-annotation-p (token)
- (phpinspect-token-type-p token :var-annotation))
-
-(defun phpinspect-return-annotation-p (token)
- (phpinspect-token-type-p token :return-annotation))
-
-(defun phpinspect--index-function-arg-list (type-resolver arg-list)
- (let ((arg-index)
- (current-token)
- (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))
- nil)
- arg-index))))
- (nreverse arg-index)))
-
-(defun phpinspect--index-function-from-scope (type-resolver scope
comment-before)
- (let* ((php-func (cadr scope))
- (declaration (cadr php-func))
- (type (if (phpinspect-word-p (car (last declaration)))
- (phpinspect--make-type :name (cadar (last declaration))))))
-
- ;; @return annotation. Has precedence over typehint when dealing with a
collection.
- (let* ((is-collection
- (when type
- (member (phpinspect--type-name
- (funcall type-resolver type))
phpinspect-collection-types)))
- (return-annotation-type
- (when (or (not type) is-collection)
- (cadadr
- (seq-find #'phpinspect-return-annotation-p
- comment-before)))))
- (phpinspect--log "found return annotation %s" return-annotation-type)
-
- (when return-annotation-type
- (cond ((not type)
- (setq type (funcall type-resolver
- (phpinspect--make-type :name
return-annotation-type))))
- (is-collection
- (phpinspect--log "Detected collection type in: %s" scope)
- (setf (phpinspect--type-contains type)
- (funcall type-resolver
- (phpinspect--make-type :name
return-annotation-type)))
- (setf (phpinspect--type-collection type) t)))))
-
- (phpinspect--make-function
- :scope `(,(car scope))
- :name (cadadr (cdr declaration))
- :return-type (if type (funcall type-resolver type)
- phpinspect--null-type)
- :arguments (phpinspect--index-function-arg-list
- type-resolver
- (phpinspect-function-argument-list php-func)))))
-
-(defun phpinspect--index-const-from-scope (scope)
- (phpinspect--make-variable
- :scope `(,(car scope))
- :name (cadr (cadr (cadr scope)))))
-
-(defun phpinspect--var-annotations-from-token (token)
- (seq-filter #'phpinspect-var-annotation-p token))
-
-(defun phpinspect--index-variable-from-scope (type-resolver scope
comment-before)
- "Index the variable inside `scope`."
- (let* ((var-annotations (phpinspect--var-annotations-from-token
comment-before))
- (variable-name (cadr (cadr scope)))
- (type (if var-annotations
- ;; Find the right annotation by variable name
- (or (cadr (cadr (seq-find (lambda (annotation)
- (string= (cadr (caddr
annotation)) variable-name))
- var-annotations)))
- ;; Give up and just use the last one encountered
- (cadr (cadr (car (last var-annotations))))))))
- (phpinspect--log "calling resolver from index-variable-from-scope")
- (phpinspect--make-variable
- :name variable-name
- :scope `(,(car scope))
- :type (if type (funcall type-resolver (phpinspect--make-type :name
type))))))
-
-(defun phpinspect-doc-block-p (token)
- (phpinspect-token-type-p token :doc-block))
-
-(defun phpinspect--get-class-name-from-token (class-token)
- (let ((subtoken (seq-find (lambda (word)
- (and (phpinspect-word-p word)
- (not (string-match
- (concat "^"
(phpinspect-handler-regexp 'class-keyword))
- (concat (cadr word) " ")))))
- (cadr class-token))))
- (cadr subtoken)))
-
-(defun phpinspect--index-class (type-resolver class)
- "Create an alist with relevant attributes of a parsed class."
- (phpinspect--log "INDEXING CLASS")
- (let ((methods)
- (static-methods)
- (static-variables)
- (variables)
- (constants)
- (extends)
- (implements)
- (class-name (phpinspect--get-class-name-from-token class))
- ;; Keep track of encountered comments to be able to use type
- ;; annotations.
- (comment-before))
-
- ;; Find out what the class extends or implements
- (let ((enc-extends nil)
- (enc-implements nil))
- (dolist (word (cadr class))
- (if (phpinspect-word-p word)
- (cond ((string= (cadr word) "extends")
- (phpinspect--log "Class %s extends other classes"
class-name)
- (setq enc-extends t))
- ((string= (cadr word) "implements")
- (setq enc-extends nil)
- (phpinspect--log "Class %s implements in interface"
class-name)
- (setq enc-implements t))
- (t
- (phpinspect--log "Calling Resolver from index-class on %s"
(cadr word))
- (cond (enc-extends
- (push (funcall type-resolver (phpinspect--make-type
- :name (cadr word)))
- extends))
- (enc-implements
- (push (funcall type-resolver (phpinspect--make-type
- :name (cadr word)))
- implements))))))))
-
- (dolist (token (caddr class))
- (cond ((phpinspect-scope-p token)
- (cond ((phpinspect-const-p (cadr token))
- (push (phpinspect--index-const-from-scope token)
constants))
-
- ((phpinspect-variable-p (cadr token))
- (push (phpinspect--index-variable-from-scope type-resolver
- token
-
comment-before)
- variables))
-
- ((phpinspect-static-p (cadr token))
- (cond ((phpinspect-function-p (cadadr token))
- (push (phpinspect--index-function-from-scope
type-resolver
- (list
(car token)
-
(cadadr token))
-
comment-before)
- static-methods))
-
- ((phpinspect-variable-p (cadadr token))
- (push (phpinspect--index-variable-from-scope
type-resolver
- (list
(car token)
-
(cadadr token))
-
comment-before)
- static-variables))))
- (t
- (phpinspect--log "comment-before is: %s" comment-before)
- (push (phpinspect--index-function-from-scope type-resolver
- token
-
comment-before)
- methods))))
- ((phpinspect-static-p token)
- (cond ((phpinspect-function-p (cadr token))
- (push (phpinspect--index-function-from-scope type-resolver
- `(:public
- ,(cadr
token))
-
comment-before)
- static-methods))
-
- ((phpinspect-variable-p (cadr token))
- (push (phpinspect--index-variable-from-scope type-resolver
- `(:public
- ,(cadr
token))
-
comment-before)
- static-variables))))
- ((phpinspect-const-p token)
- ;; Bare constants are always public
- (push (phpinspect--index-const-from-scope (list :public token))
- constants))
- ((phpinspect-function-p token)
- ;; Bare functions are always public
- (push (phpinspect--index-function-from-scope type-resolver
- (list :public token)
- comment-before)
- methods))
- ((phpinspect-doc-block-p token)
- (phpinspect--log "setting comment-before %s" token)
- (setq comment-before token))
-
- ;; Prevent comments from sticking around too long
- (t
- (phpinspect--log "Unsetting comment-before")
- (setq comment-before nil))))
-
- ;; Dirty hack that assumes the constructor argument names to be the same
as the object
- ;; attributes' names.
- ;;;
- ;; 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)))
- (when constructor
- (phpinspect--log "Constructor was found")
- (dolist (variable variables)
- (when (not (phpinspect--variable-type variable))
- (phpinspect--log "Looking for variable type in constructor
arguments (%s)"
- variable)
- (let ((constructor-parameter-type
- (car (alist-get (phpinspect--variable-name variable)
- (phpinspect--function-arguments constructor)
- nil nil #'string=))))
- (if constructor-parameter-type
- (setf (phpinspect--variable-type variable)
- (funcall type-resolver
constructor-parameter-type))))))))
-
- (let ((class-name (funcall type-resolver (phpinspect--make-type :name
class-name))))
- `(,class-name .
- (phpinspect--indexed-class
- (methods . ,methods)
- (class-name . ,class-name)
- (static-methods . ,static-methods)
- (static-variables . ,static-variables)
- (variables . ,variables)
- (constants . ,constants)
- (extends . ,extends)
- (implements . ,implements))))))
-
-(defsubst phpinspect-namespace-body (namespace)
- "Return the nested tokens in NAMESPACE tokens' body.
-Accounts for namespaces that are defined with '{}' blocks."
- (if (phpinspect-block-p (caddr namespace))
- (cdaddr namespace)
- (cdr namespace)))
-
-(defun phpinspect--index-classes (types classes &optional namespace indexed)
- "Index the class tokens in `classes`, using the types in `types`
-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)
- class)
- indexed)
- (phpinspect--index-classes types 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-name (if (and (phpinspect-word-p (caddr use))
- (string= "as" (cadr (caddr use))))
- (cadr (cadddr use))
- (progn (string-match "[^\\]+$" fqn)
- (match-string 0 fqn)))))
- (cons (phpinspect-intern-name type-name) type)))
-
-(defun phpinspect--uses-to-types (uses)
- (mapcar #'phpinspect--use-to-type uses))
-
-(defun phpinspect--index-namespace (namespace)
- (phpinspect--index-classes
- (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
- (seq-filter #'phpinspect-class-p namespace)
- (cadadr namespace)))
-
-(defun phpinspect--index-namespaces (namespaces &optional indexed)
- (if namespaces
- (progn
- (push (phpinspect--index-namespace (pop namespaces)) indexed)
- (phpinspect--index-namespaces namespaces indexed))
- (apply #'append (nreverse indexed))))
-
-(defun phpinspect--index-functions (&rest _args)
- "TODO: implement function indexation. This is a stub function.")
-
-(defun phpinspect--index-tokens (tokens)
- "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
- )
-
-;; (defun phpinspect--get-or-create-index-for-class-file (class-fqn)
-;; (phpinspect--log "Getting or creating index for %s" class-fqn)
-;; (phpinspect-get-or-create-cached-project-class
-;; (phpinspect-project-root)
-;; class-fqn))
-
-(defun phpinspect-index-file (file-name)
- (phpinspect--index-tokens (phpinspect-parse-file file-name)))
-
-(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))))
-
- ;; (let ((existing-index (phpinspect-get-cached-project-class
- ;; project-root
- ;; class-fqn)))
- ;; (or
- ;; existing-index
- ;; (progn
- ;; (let* ((class-file (phpinspect-class-filepath class-fqn))
- ;; (visited-buffer (when class-file (find-buffer-visiting
class-file)))
- ;; (new-index))
-
- ;; (phpinspect--log "No existing index for FQN: %s" class-fqn)
- ;; (phpinspect--log "filepath: %s" class-file)
- ;; (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)))
- ;; (dolist (class (alist-get 'classes new-index))
- ;; (when class
- ;; (phpinspect-cache-project-class
- ;; project-root
- ;; (cdr class))))
- ;; (alist-get class-fqn (alist-get 'classes new-index)
- ;; nil
- ;; nil
- ;; #'phpinspect--type=))))))))
-
-
-(defun phpinspect--index-current-buffer ()
- (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
-
-(defun phpinspect-index-current-buffer ()
- "Index a PHP file for classes and the methods they have"
- (phpinspect--index-tokens (phpinspect-parse-current-buffer)))
-
-;; (defun phpinspect--get-variables-for-class (buffer-classes class &optional
static)
-;; (let ((class-index (or (assoc-default class buffer-classes
#'phpinspect--type=)
-;; (phpinspect--get-or-create-index-for-class-file
class))))
-;; (when class-index
-;; (if static
-;; (append (alist-get 'static-variables class-index)
-;; (alist-get 'constants class-index))
-;; (alist-get 'variables class-index)))))
(defun phpinspect--get-variables-for-class (buffer-classes class-name
&optional static)
(let ((class (phpinspect-get-or-create-cached-project-class
@@ -2261,12 +876,6 @@ level of a token. Nested variables are ignored."
(defsubst phpinspect-not-variable-p (token)
(not (phpinspect-variable-p token)))
-(defun phpinspect--get-bare-class-name-from-fqn (fqn)
- (car (last (split-string fqn "\\\\"))))
-
-(cl-defmethod phpinspect--type-bare-name ((type phpinspect--type))
- (phpinspect--get-bare-class-name-from-fqn (phpinspect--type-name type)))
-
(cl-defmethod phpinspect--make-completion
((completion-candidate phpinspect--variable))
(phpinspect--construct-completion
@@ -2488,242 +1097,6 @@ static variables and static methods."
(phpinspect--completion-meta
(phpinspect--completion-list-get-metadata
phpinspect--last-completion-list arg)))))
-
-(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
- (active-projects nil
- :type alist
- :documentation
- "An `alist` that contains the root directory
- paths of all currently active phpinspect
- projects")
- (projects (make-hash-table :test 'equal :size 10)
- :type hash-table
- :documentation
- "A `hash-table` with the root directories of projects
-as keys and project caches as values."))
-
-(cl-defstruct (phpinspect--project (:constructor
phpinspect--make-project-cache))
- (class-index (make-hash-table :test 'eq :size 100 :rehash-size 40)
- :type hash-table
- :documentation
- "A `hash-table` that contains all of the currently
-indexed classes in the project"))
-
-(cl-defstruct (phpinspect--class (:constructor
phpinspect--make-class-generated))
- (project nil
- :type phpinspect--project
- :documentaton
- "The project that this class belongs to")
- (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.")
- (variables nil
- :type list
- :documentation
- "Variables that belong to this class.")
- (extended-classes (make-hash-table :test 'eq)
- :type hash-table
- :documentation
- "All extended/implemented classes.")
- (subscriptions nil
- :type list
- :documentation
- "A list of subscription functions that should be
- called whenever anything about this class is
- updated"))
-
-(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
- (dolist (sub (phpinspect--class-subscriptions class))
- (funcall sub class)))
-
-(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
- (index (head
phpinspect--indexed-class)))
- (setf (phpinspect--class-index class) index)
- (dolist (method (alist-get 'methods index))
- (phpinspect--class-update-method class method))
-
- (dolist (method (alist-get 'static-methods index))
- (phpinspect--class-update-static-method class method))
-
- (setf (phpinspect--class-variables class)
- (alist-get 'variables index))
-
- (setf (phpinspect--class-extended-classes class)
- (seq-filter
- #'phpinspect--class-p
- (mapcar
- (lambda (class-name)
- (phpinspect--project-get-class-create (phpinspect--class-project
class)
- class-name))
- `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))))
-
- (dolist (extended (phpinspect--class-extended-classes class))
- (phpinspect--class-incorporate class extended)
- (phpinspect--class-subscribe class extended))
-
- (phpinspect--class-trigger-update class))
-
-(cl-defmethod phpinspect--class-get-method ((class phpinspect--class)
method-name)
- (gethash method-name (phpinspect--class-methods class)))
-
-(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class)
method-name)
- (gethash method-name (phpinspect--class-static-methods class)))
-
-(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))
- (puthash (phpinspect--function-name-symbol method)
- method
- (phpinspect--class-methods class)))
-
-(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
- (method
phpinspect--function))
- (puthash (phpinspect--function-name-symbol method)
- method
- (phpinspect--class-static-methods class)))
-
-(cl-defmethod phpinspect--class-get-method-return-type
- ((class phpinspect--class) (method-name symbol))
- (let ((method (phpinspect--class-get-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)
- (push method methods))
- (phpinspect--class-methods class))
- methods))
-
-(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
- (method
phpinspect--function))
- (let ((existing (gethash (phpinspect--function-name-symbol method)
- (phpinspect--class-static-methods class))))
- (if existing
- (progn
- (unless (eq (phpinspect--function-return-type method)
- phpinspect--null-type)
- (setf (phpinspect--function-return-type existing)
- (phpinspect--function-return-type method))
- (setf (phpinspect--function-arguments existing)
- (phpinspect--function-arguments method))))
- (phpinspect--class-set-static-method class method))))
-
-(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
- (method phpinspect--function))
- (let ((existing (gethash (phpinspect--function-name-symbol method)
- (phpinspect--class-methods class))))
- (if existing
- (progn
- (unless (eq (phpinspect--function-return-type method)
- phpinspect--null-type)
- (setf (phpinspect--function-return-type existing)
- (phpinspect--function-return-type method))
- (setf (phpinspect--function-arguments existing)
- (phpinspect--function-arguments method))))
- (phpinspect--class-set-method class method))))
-
-(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
- (other-class phpinspect--class))
- (maphash (lambda (k method)
- (phpinspect--class-update-method class method))
- (phpinspect--class-methods other-class)))
-
-(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
- (subscription-class
phpinspect--class))
- (let ((update-function
- (lambda (new-class)
- (phpinspect--class-incorporate class new-class)
- (phpinspect--class-trigger-update class))))
- (push update-function (phpinspect--class-subscriptions
subscription-class))))
-1
-(cl-defgeneric phpinspect--cache-getproject
- ((cache phpinspect--cache) (project-name string))
- "Get project by PROJECT-NAME that is located in CACHE.")
-
-(cl-defmethod phpinspect--cache-getproject
- ((cache phpinspect--cache) (project-root string))
- (gethash project-root (phpinspect--cache-projects cache)))
-
-(cl-defgeneric 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.")
-
-(cl-defmethod phpinspect--cache-get-project-create
- ((cache phpinspect--cache) (project-root string))
- (or (phpinspect--cache-getproject cache project-root)
- (puthash project-root
- (phpinspect--make-project-cache)
- (phpinspect--cache-projects cache))))
-
-(cl-defgeneric phpinspect--project-add-class
- ((project phpinspect--project) (class (head phpinspect--indexed-class)))
- "Add an indexed CLASS to PROJECT.")
-
-(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
- (phpinspect--class-set-index existing-class indexed-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))))))
-
-(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-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))
-
- (let* ((class-file (phpinspect-class-filepath class-fqn))
- (visited-buffer (when class-file (find-buffer-visiting
class-file)))
- (new-index)
- (class-index))
-
- (phpinspect--log "No existing index for FQN: %s" class-fqn)
- (phpinspect--log "filepath: %s" class-file)
- (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)))
- (setq class-index
- (alist-get class-fqn (alist-get 'classes new-index)
- nil
- nil
- #'phpinspect--type=))
- (when class-index
- (phpinspect--class-set-index class class-index)))))
- class))
-
-(cl-defmethod phpinspect--project-get-class
- ((project phpinspect--project) (class-fqn phpinspect--type))
- (gethash (phpinspect--type-name-symbol class-fqn)
- (phpinspect--project-class-index project)))
-
(defun phpinspect--get-or-create-global-cache ()
"Get `phpinspect-cache'.
If its value is nil, it is created and then returned."
- [elpa] branch externals/phpinspect created (now 3175d9a6ac), ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 681f13d4de 001/126: Initial commit: Move out of personal dotfile repository., ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect ad12203a91 003/126: Add more parts of phpns that were apparently still required, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect d6c63ef6f4 004/126: Truncate "uses_own" index file before filling the index, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect bbc37bea32 007/126: Update README code format and to use setq-local, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect f7c2a49b25 008/126: Remove dependency on php-project.el, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 098146bfc5 014/126: Add resolvecontext structure and optimze type resolving process, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect e07e1ed9e6 033/126: WIP: Split code up into separate files,
ELPA Syncer <=
- [elpa] externals/phpinspect 6b8db3a318 028/126: Test and fix eldoc function for static methods, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 4c2b3dd601 019/126: Only attempt to return the inside class name when it is non-nil, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect a2ce841555 038/126: WIP: Make index thread block/wakeup when queue is empty/populated, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 56eaa3b36d 046/126: Replace index-thread with more generic and encapsulated worker type, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 0c0c1ca381 059/126: Add test for phpinspect-get-pattern-type-in-block, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect ca8d0972ff 050/126: Implement psr-0 and psr-4 autoloaders, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 1f145665ef 083/126: Exclude "return" from resolvecontext subject + count comma at point for eldoc arg number, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 0ca527dbbd 070/126: Adapt `phpinspect-purge-parser-cache' to new parser caching approach, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect 6627f6f76f 073/126: Remove commented parser code, ELPA Syncer, 2023/08/12
- [elpa] externals/phpinspect e270729e14 088/126: Implement splay tree for overlay storage/lookup, ELPA Syncer, 2023/08/12