WIP: Split code up into separate files
parent
74bd0ad434
commit
e07e1ed9e6
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
@ -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
|
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue