Transition from index script to autoloader
continuous-integration/drone/push Build is failing
Details
continuous-integration/drone/push Build is failing
Details
parent
ca8d0972ff
commit
dbf0ec0390
@ -0,0 +1,81 @@
|
||||
;;; phpinspect-buffer.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-local phpinspect-current-buffer nil
|
||||
"An instance of `phpinspect-buffer' local to the active
|
||||
buffer. This variable is only set for buffers where
|
||||
`phpinspect-mode' is active. Also see `phpinspect-buffer'.")
|
||||
|
||||
(defsubst phpinspect-make-region (start end)
|
||||
(list start end))
|
||||
|
||||
(defalias 'phpinspect-region-start #'car)
|
||||
(defalias 'phpinspect-region-end #'cadr)
|
||||
|
||||
(cl-defstruct (phpinspect-buffer (:constructor phpinspect-make-buffer))
|
||||
"An object containing phpinspect related metadata linked to an
|
||||
emacs buffer."
|
||||
(buffer nil
|
||||
:type buffer
|
||||
:documentation "The underlying emacs buffer")
|
||||
(location-map (make-hash-table :test 'eq :size 400 :rehash-size 400)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"A map that lets us look up the character
|
||||
positions of a token within this buffer.")
|
||||
(tree nil
|
||||
:type list
|
||||
:documentation
|
||||
"An instance of a token tree as returned by
|
||||
`phpinspect--index-tokens'. Meant to be eventually consistent
|
||||
with the contents of the buffer."))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-parse ((buffer phpinspect-buffer))
|
||||
"Parse the PHP code in the the emacs buffer that this object is
|
||||
linked with."
|
||||
(with-current-buffer (phpinspect-buffer-buffer buffer)
|
||||
(setf (phpinspect-buffer-location-map buffer)
|
||||
(make-hash-table :test 'eq
|
||||
:size 400
|
||||
:rehash-size 400))
|
||||
|
||||
(let ((tree (phpinspect-parse-current-buffer)))
|
||||
(setf (phpinspect-buffer-tree buffer) tree)
|
||||
tree)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-token-location ((buffer phpinspect-buffer) token)
|
||||
(gethash token (phpinspect-buffer-location-map buffer)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-tokens-enclosing-point ((buffer phpinspect-buffer) point)
|
||||
(let ((tokens))
|
||||
(maphash
|
||||
(lambda (token region)
|
||||
(when (and (<= (phpinspect-region-start region) point)
|
||||
(>= (phpinspect-region-end region) point))
|
||||
(push token tokens)))
|
||||
(phpinspect-buffer-location-map buffer))
|
||||
tokens))
|
||||
|
||||
(provide 'phpinspect-buffer)
|
@ -0,0 +1,155 @@
|
||||
; phpinspect-imports.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:
|
||||
|
||||
;; See docstrings for documentation, starting with `phpinspect-mode'.
|
||||
|
||||
;;; Code:
|
||||
|
||||
(require 'phpinspect-parser)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-autoload)
|
||||
(require 'phpinspect-buffer)
|
||||
|
||||
(defun phpinspect-insert-at-point (point data)
|
||||
(save-excursion
|
||||
(goto-char point)
|
||||
(insert data)))
|
||||
|
||||
(defun phpinspect-add-use (fqn buffer &optional namespace-token)
|
||||
"Add use statement for FQN to BUFFER.
|
||||
|
||||
If NAMESPACE-TOKEN is non-nil, it is assumed to be a token that
|
||||
was parsed from BUFFER and its location will be used to find a
|
||||
buffer position to insert the use statement at."
|
||||
(when (string-match "^\\\\" fqn)
|
||||
(setq fqn (string-trim-left fqn "\\\\")))
|
||||
|
||||
(if namespace-token
|
||||
(let* ((region (gethash
|
||||
namespace-token (phpinspect-buffer-location-map buffer)))
|
||||
(existing-use (seq-find #'phpinspect-use-p
|
||||
(phpinspect-namespace-body namespace-token)))
|
||||
(namespace-block (phpinspect-namespace-block namespace-token)))
|
||||
(if existing-use
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-region-start
|
||||
(phpinspect-buffer-token-location buffer existing-use))
|
||||
(format "use %s;%c" fqn ?\n))
|
||||
(if namespace-block
|
||||
(phpinspect-insert-at-point
|
||||
(+ 1 (phpinspect-region-start
|
||||
(phpinspect-buffer-token-location buffer namespace-block)))
|
||||
(format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-region-end
|
||||
(phpinspect-buffer-token-location
|
||||
buffer (seq-find #'phpinspect-terminator-p namespace-token)))
|
||||
(format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)))))
|
||||
;; else
|
||||
(let ((existing-use (seq-find #'phpinspect-use-p
|
||||
(phpinspect-buffer-tree buffer))))
|
||||
(if existing-use
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-region-start
|
||||
(phpinspect-buffer-token-location buffer existing-use))
|
||||
(format "use %s;%c" fqn ?\n))
|
||||
(let ((first-token (cadr (phpinspect-buffer-tree buffer))))
|
||||
(if (and (phpinspect-word-p first-token)
|
||||
(string= "declare" (cadr first-token)))
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-region-end
|
||||
(phpinspect-buffer-token-location
|
||||
buffer (seq-find #'phpinspect-terminator-p (phpinspect-buffer-tree buffer))))
|
||||
(format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-region-start
|
||||
(phpinspect-buffer-token-location buffer first-token))
|
||||
(format "%c%cuse %s;%c%c" ?\n ?\n fqn ?\n ?\n))))))))
|
||||
|
||||
(defun phpinspect-add-use-interactive (typename buffer project &optional namespace-token)
|
||||
(let* ((autoloader (phpinspect--project-autoload project))
|
||||
(fqn-bags (phpinspect-autoloader-type-name-fqn-bags autoloader)))
|
||||
|
||||
(let ((fqns (gethash typename fqn-bags)))
|
||||
(cond ((= 1 (length fqns))
|
||||
(phpinspect-add-use (symbol-name (car fqns)) buffer namespace-token))
|
||||
((> (length fqns) 1)
|
||||
(phpinspect-add-use (symbol-name (completing-read "Class: " fqns))
|
||||
buffer namespace-token))
|
||||
(t (message "No import found for type %s" typename))))))
|
||||
|
||||
(defun phpinspect-namespace-part-of-typename (typename)
|
||||
(string-trim-right typename "\\\\?[^\\\\]+"))
|
||||
|
||||
(defalias 'phpinspect-fix-uses-interactive #'phpinspect-fix-imports
|
||||
"Alias for backwards compatibility")
|
||||
|
||||
(defun phpinspect-fix-imports ()
|
||||
"Find types that are used in the current buffer and make sure
|
||||
that there are import (\"use\") statements for them."
|
||||
(interactive)
|
||||
(if phpinspect-current-buffer
|
||||
(let* ((tree (phpinspect-buffer-parse phpinspect-current-buffer))
|
||||
(location-map (phpinspect-buffer-location-map phpinspect-current-buffer))
|
||||
(index (phpinspect--index-tokens
|
||||
tree nil (lambda (token) (gethash token location-map))))
|
||||
(classes (alist-get 'classes index))
|
||||
(imports (alist-get 'imports index))
|
||||
(project (phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
(phpinspect-project-root))))
|
||||
(dolist (class classes)
|
||||
(let* ((class-imports (alist-get 'imports class))
|
||||
(used-types (alist-get 'used-types class))
|
||||
(region (alist-get 'location class)))
|
||||
(dolist (type used-types)
|
||||
(let ((namespace
|
||||
(seq-find #'phpinspect-namespace-p
|
||||
(phpinspect-buffer-tokens-enclosing-point
|
||||
phpinspect-current-buffer (phpinspect-region-start region)))))
|
||||
;; Add use statements for types that aren't imported.
|
||||
|
||||
(unless (or (or (alist-get type class-imports)
|
||||
(alist-get type imports))
|
||||
(gethash (phpinspect-intern-name
|
||||
(concat (phpinspect-namespace-part-of-typename
|
||||
(phpinspect--type-name (alist-get 'class-name class)))
|
||||
"\\"
|
||||
(symbol-name type)))
|
||||
(phpinspect-autoloader-types
|
||||
(phpinspect--project-autoload project))))
|
||||
(phpinspect-add-use-interactive
|
||||
type phpinspect-current-buffer project namespace)
|
||||
;; Buffer has been modified by adding type, update tree +
|
||||
;; location map. This is not optimal but will have to do until
|
||||
;; partial parsing is implemented.
|
||||
;;
|
||||
;; Note: this basically implements a bug where the locations
|
||||
;; of classes are no longer congruent with their location in
|
||||
;; the buffer's code. In files that contain multiple namespace
|
||||
;; blocks this could cause problems as a namespace may grow by
|
||||
;; added import statements and start envelopping the classes
|
||||
;; below it.
|
||||
(phpinspect-buffer-parse phpinspect-current-buffer)))))))))
|
||||
|
||||
(provide 'phpinspect-imports)
|
@ -1,853 +0,0 @@
|
||||
#!/bin/bash
|
||||
##
|
||||
# phpinspect-index.bash - Resolve namespaces and fix missing use statements in your PHP
|
||||
# scripts.
|
||||
###
|
||||
# This script is derived from phpns, an earlier project that had a much wider scope than
|
||||
# just index files for phpinspect.el. Much of the code and command line argument options
|
||||
# can be removed.
|
||||
# TODO: remove whatever functionality is not required for phpinspect.el
|
||||
|
||||
# shellcheck disable=SC2155
|
||||
declare CACHE_DIR=./.cache/phpinspect
|
||||
declare INFO=1
|
||||
|
||||
# Cache locations
|
||||
declare CLASSES="$CACHE_DIR/classes"
|
||||
declare NAMESPACES="$CACHE_DIR/namespaces"
|
||||
declare USES="$CACHE_DIR/uses"
|
||||
declare USES_OWN="$CACHE_DIR/uses_own"
|
||||
declare USES_LOOKUP="$CACHE_DIR/uses_lookup"
|
||||
declare USES_LOOKUP_OWN="$CACHE_DIR/uses_lookup_own"
|
||||
declare FILE_PATHS="$CACHE_DIR/file_paths"
|
||||
declare NAMESPACE_FILE_PATHS="$CACHE_DIR/namespace_file_paths"
|
||||
declare INDEXED="$CACHE_DIR/indexed"
|
||||
|
||||
[[ $DEBUG -eq 2 ]] && set -x
|
||||
shopt -s extglob
|
||||
shopt -so pipefail
|
||||
|
||||
read -rd '' USAGE <<'EOF'
|
||||
phpns - Resolve namespaces and fix missing use statements in your PHP scripts.
|
||||
|
||||
USAGE:
|
||||
phpns COMMAND [ ARGUMENTS ] [ OPTIONS ]
|
||||
|
||||
COMMANDS:
|
||||
i, index Index the PHP project in the current directory
|
||||
fu, find-use CLASS_NAME Echo the FQN of a class
|
||||
fxu, fix-uses FILE Add needed use statements to FILE
|
||||
cns, classes-in-namespace NAMESPACE Echo the classes that reside in NAMESPACE
|
||||
fp, filepath FQN Echo the filepath of the class by the name of FQN.
|
||||
|
||||
TO BE IMPLEMENTED:
|
||||
rmuu, remove-unneeded-uses FILE: Remove all use statements for classes that are not being used.
|
||||
|
||||
OPTIONS FOR ALL COMMANDS:
|
||||
-s --silent Don't print info.
|
||||
|
||||
UNIQUE OPTIONS PER COMMAND:
|
||||
index:
|
||||
-d, --diff Show differences between the files in the index and the files in the project directory.
|
||||
-N, --new Only index new files
|
||||
find-use:
|
||||
-j, --json Provide possible use FQN's as a json array.
|
||||
-p, --prefer-own If there are matches inside the "src" dir, only use those.
|
||||
-a, --auto-pick Use first encountered match, don't provide a choice.
|
||||
-b. --bare Print FQN's without any additives.
|
||||
fix-uses:
|
||||
-j, --json Provide possible use FQN's per class as a json object with the class names as keys.
|
||||
-p, --prefer-own If there are matches inside the "src" dir, only use those.
|
||||
-a, --auto-pick Use first encountered match, for every class, don't provide a choice.
|
||||
-o, --stdout Print to stdout in stead of printing to the selected file.
|
||||
filepath: -
|
||||
|
||||
EOF
|
||||
|
||||
execute() {
|
||||
declare command="$1" INFO="$INFO"
|
||||
declare -a CONFIG=()
|
||||
shift
|
||||
|
||||
if [[ $command == @(-h|--help|help) ]]; then
|
||||
echo "$USAGE" >&2
|
||||
exit 0
|
||||
fi
|
||||
|
||||
if ! [[ -f ./composer.json ]] && ! [[ -d ./.git ]]; then
|
||||
echo "No composer.json or .git file found, not in root of poject, exiting." >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
case "$command" in
|
||||
i | index)
|
||||
handleArguments index "$@" || return $?
|
||||
|
||||
# The arguments to grep need to be dynamic here, because the diff option
|
||||
# requires different arguments to be passed to grep.
|
||||
declare -a grep_args=(
|
||||
-H
|
||||
'^\(class\|abstract[[:blank:]]\+class\|\(final[[:blank:]]\+\|/\*[[:blank:]]*final[[:blank:]]*\*/[[:blank:]]*\)class\|namespace\|interface\|trait\)[[:blank:]]\+[A-Za-z]\+'
|
||||
--exclude-dir={.cache,var,bin}
|
||||
--binary-files=without-match
|
||||
)
|
||||
|
||||
# Only index new files
|
||||
if [[ ${CONFIG[$INDEX_NEW]} == '--new' ]]; then
|
||||
declare -a new_files=() deleted_files=()
|
||||
|
||||
# Extract new files from diff.
|
||||
while IFS=':' read -ra diff_file; do
|
||||
if [[ ${diff_file[0]} == '-' ]]; then
|
||||
deleted_files=("${diff_file[1]}" "${deleted_files[@]}")
|
||||
elif [[ ${diff_file[0]} == '+' ]]; then
|
||||
new_files=("${diff_file[1]}" "${new_files[@]}")
|
||||
fi
|
||||
done < <(diffIndex)
|
||||
|
||||
# Inform the user if non-existent files were found. Right now the only
|
||||
# way to fix this is to reindex entirely.
|
||||
if [[ ${#deleted_files[@]} -gt 0 ]]; then
|
||||
info "There are ${#deleted_files[@]} non-existent files in your index. Consider reindexing to prevent incorrect results."
|
||||
info 'Some of these none existent files are:'
|
||||
for i in {0..19}; do
|
||||
[[ $i -ge ${#deleted_files[@]} ]] && break
|
||||
infof ' - "%s"\n' "${deleted_files[$i]}"
|
||||
done
|
||||
fi
|
||||
|
||||
if [[ ${#new_files[@]} -eq 0 ]]; then
|
||||
info 'No new files were found.'
|
||||
return 0
|
||||
else
|
||||
info "${#new_files[@]} new files found to index."
|
||||
fi
|
||||
|
||||
# To exclusively index new files, add the filenames to the arguments array
|
||||
grep_args=("${grep_args[@]}" "${new_files[@]}")
|
||||
elif [[ ${CONFIG[$INDEX_DIFF]} == '--diff' ]]; then
|
||||
diffIndex
|
||||
return $?
|
||||
else
|
||||
grep_args=("${grep_args[@]}" '-r' '--include=*.php')
|
||||
fi
|
||||
|
||||
# Index matching files
|
||||
grep -m 2 "${grep_args[@]}" | grep -v '^vendor/bin' | fillIndex
|
||||
|
||||
# Add non-matching files to the file with indexed files.
|
||||
# This is necessary to be able to diff the index.
|
||||
grep -L "${grep_args[@]}" | grep -v '^vendor/bin' >> "$INDEXED"
|
||||
;;
|
||||
fu | find-use)
|
||||
checkCache
|
||||
handleArguments find-use "$@" || return $?
|
||||
declare use_path='' class_name="${CONFIG[$CLASS_NAME]}"
|
||||
if [[ "$class_name" == @(array|string|float|int|void|mixed) ]]; then
|
||||
infof 'Type "%s" is not a class, but a primitive type.\n' "$class_name"
|
||||
return 1
|
||||
fi
|
||||
|
||||
findUsePathForClass "$class_name"
|
||||
;;
|
||||
fxu | fix-uses)
|
||||
checkCache
|
||||
handleArguments fix-uses "$@" || return $?
|
||||
|
||||
declare file="${CONFIG[$FILE]}"
|
||||
|
||||
if ! [[ -f $file ]]; then
|
||||
infof 'File "%s" does not exist or is not a regular file.\n' "$file"
|
||||
|
||||
return 1
|
||||
elif [[ ${CONFIG[$STDOUT]} == '--stdout' ]]; then
|
||||
fixMissingUseStatements "$file"
|
||||
else
|
||||
# shellcheck disable=SC2005
|
||||
echo "$(fixMissingUseStatements "$file")" > "$file"
|
||||
fi
|
||||
;;
|
||||
ns | namespace)
|
||||
checkCache
|
||||
declare file="$1"
|
||||
|
||||
# Try the index, if that doesn't work, attempt to extract the namespace from the file itself.
|
||||
if ! grep "(?<=$file:).*" "$NAMESPACE_FILE_PATHS"; then
|
||||
grep -Po '(?<=^namespace[[:blank:]])[A-Za-z_\\]+' "$file"
|
||||
fi
|
||||
;;
|
||||
cns | classes-in-namespace)
|
||||
handleArguments classes-in-namespace "$@" || return $?
|
||||
checkCache
|
||||
|
||||
declare namespace="${CONFIG[$NAMESPACE]}\\"
|
||||
debug "Checking for namespace $namespace"
|
||||
|
||||
awk -F ':' "/:${namespace//\\/\\\\}"'[^\\]+$/{ print $1; }' "$USES_LOOKUP"
|
||||
;;
|
||||
fp | filepath)
|
||||
handleArguments filepath "$@" || return $?
|
||||
checkCache
|
||||
|
||||
grep -Po "^.*(?=:${CONFIG[$CLASS_PATH]//\\/\\\\}$)" "$FILE_PATHS"
|
||||
;;
|
||||
*)
|
||||
printf 'Command "%s" is not a valid subcommand.\n' "$command" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
fixMissingUseStatements() {
|
||||
declare check_uses='false' check_needs='false' file="$1" namespace="$2"
|
||||
declare -A uses=() needs=() namespace=()
|
||||
declare -a classes=()
|
||||
|
||||
classes=($(execute cns "$(execute ns "$file")"))
|
||||
for class in "${classes[@]}"; do
|
||||
namespace["$class"]='in_namespace'
|
||||
done
|
||||
|
||||
findUsesAndNeeds < "$file"
|
||||
addUseStatements "${!needs[@]}" < "$file"
|
||||
}
|
||||
|
||||
findUsePathForClass() {
|
||||
declare class="$1"
|
||||
if [[ ${CONFIG[$PREFER_OWN]} == '--prefer-own' ]]; then
|
||||
declare -a possibilities=($(grep -Po "(?<=^${CONFIG[$CLASS_NAME]}:).*" "$USES_LOOKUP_OWN"))
|
||||
else
|
||||
declare -a possibilities=($(grep -Po "(?<=^${CONFIG[$CLASS_NAME]}:).*" "$USES_LOOKUP"))
|
||||
fi
|
||||
|
||||
if [[ ${#possibilities[@]} -eq 1 ]]; then
|
||||
use_path="${possibilities[0]}"
|
||||
debugf 'Single use path "%s" found' "${possibilities[0]}"
|
||||
|
||||
# Provide an escaped string for json output if requested.
|
||||
[[ ${CONFIG[$JSON]} == '--json' ]] && printf -v use_path '"%s"' "${use_path//\\/\\\\}"
|
||||
elif [[ ${#possibilities[@]} -eq 0 ]]; then
|
||||
_handle_no_use
|
||||
return $?
|
||||
else
|
||||
_handle_multiple_uses
|
||||
fi
|
||||
|
||||
infof 'Found use statement for "%s"\n' "$use_path" >&2
|
||||
if [[ ${CONFIG[$JSON]} == '--json' ]]; then
|
||||
echo '['
|
||||
echo "$use_path"
|
||||
printf ']'
|
||||
elif [[ ${CONFIG[$BARE]} ]]; then
|
||||
echo "$use_path"
|
||||
else
|
||||
echo "use $use_path;"
|
||||
fi
|
||||
}
|
||||
|
||||
_handle_no_use() {
|
||||
declare tried_index_new="$1"
|
||||
|
||||
if [[ $tried_index_new != true ]]; then
|
||||
execute index --silent --new
|
||||
execute fu "${CONFIG[@]}"
|
||||
return $?
|
||||
elif [[ ${CONFIG[$PREFER_OWN]} == '--prefer-own' ]]; then
|
||||
CONFIG[$PREFER_OWN]=
|
||||
execute fu "${CONFIG[@]}"
|
||||
return $?
|
||||
else
|
||||
infof 'No match found for class "%s"\n' "$class_name" >&2
|
||||
[[ ${CONFIG[$JSON]} == '--json' ]] && printf '[]'
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
_handle_multiple_uses() {
|
||||
if [[ ${CONFIG[$AUTO_PICK]} == '--auto-pick' ]]; then
|
||||
use_path="${possibilities[0]}"
|
||||
|
||||
return 0
|
||||
elif [[ ${CONFIG[$BARE]} == '--bare' ]]; then
|
||||
use_path="$(printf '%s\n' "${possibilities[@]}")"
|
||||
|
||||
return 0
|
||||
elif [[ ${CONFIG[$JSON]} == '--json' ]]; then
|
||||
use_path="$(
|
||||
for i in "${!possibilities[@]}"; do
|
||||
printf '"%s"' "${possibilities[$i]//\\/\\\\}"
|
||||
[[ $i -lt $((${#possibilities[@]}-1)) ]] && printf ','
|
||||
echo
|
||||
done
|
||||
)"
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
infof 'Multiple matches for class "%s", please pick one.\n' "$class_name" >&2
|
||||
select match in "${possibilities[@]}"; do
|
||||
use_path="$match"
|
||||
break
|
||||
done < /dev/tty
|
||||
}
|
||||
|
||||
addUseStatements() {
|
||||
declare -a needs=("$@")
|
||||
declare use_statements=''
|
||||
if [[ ${CONFIG[$JSON]} == '--json' ]]; then
|
||||
declare -i length="$((${#needs[@]}-1))" current=0
|
||||
echo '{'
|
||||
for needed in "${needs[@]}"; do
|
||||
printf '"%s": ' "$needed"
|
||||
execute fu --json "$needed" "${CONFIG[$PREFER_OWN]}" "${CONFIG[$AUTO_PICK]}"
|
||||
[[ $((current++)) -lt $length ]] && printf ','
|
||||
echo
|
||||
done
|
||||
echo '}'
|
||||
|
||||
return 0
|
||||
fi
|
||||
|
||||
while IFS='' read -r line; do
|
||||
echo "$line"
|
||||
|
||||
if [[ $line == namespace* ]]; then
|
||||
IFS='' read -r line && echo "$line"
|
||||
|
||||
use_statements="$(
|
||||
for needed in "${needs[@]}"; do
|
||||
execute fu "$needed" "${CONFIG[$PREFER_OWN]}" "${CONFIG[$AUTO_PICK]}"
|
||||
done | sort
|
||||
)"
|
||||
|
||||
[[ -n $use_statements ]] && echo "$use_statements"
|
||||
fi
|
||||
done
|
||||
|
||||
declare -i added_uses=0
|
||||
added_uses="$(echo -n "$use_statements" | wc -l)"
|
||||
[[ -n $use_statements ]] && ((added_uses++))
|
||||
info "$added_uses use statements added out of ${#needs[@]} needed types. Types that were needed:" >&2
|
||||
infof ' - "%s"\n' "${needs[@]}" >&2
|
||||
}
|
||||
|
||||
debug() {
|
||||
if [[ $DEBUG -ge 1 ]]; then
|
||||
echo "[DEBUG] => $1" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
debugf() {
|
||||
if [[ $DEBUG -ge 1 ]]; then
|
||||
declare format_string="$1"
|
||||
shift
|
||||
printf "[DEBUG] => $format_string" "$@" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
info() {
|
||||
if [[ $INFO -eq 1 ]]; then
|
||||
echo "[INFO] => $1" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2059
|
||||
infof() {
|
||||
if [[ $INFO -eq 1 ]]; then
|
||||
declare format_string="$1"
|
||||
shift
|
||||
printf "[INFO] => $format_string" "$@" >&2
|
||||
fi
|
||||
}
|
||||
|
||||
##
|
||||
# Functions for parameter parsing
|
||||
|
||||
# Enum for config
|
||||
declare -gri CLASS_NAME=0
|
||||
declare -gri PREFER_OWN=1
|
||||
declare -gri AUTO_PICK=2
|
||||
declare -gri STDOUT=3
|
||||
declare -gri JSON=4
|
||||
declare -gri BARE=5
|
||||
declare -gri WORD=6
|
||||
declare -gri EXPAND_CLASSES=7
|
||||
declare -gri NO_CLASSES=8
|
||||
declare -gri NAMESPACE=9
|
||||
declare -gri CLASS_PATH=10
|
||||
declare -gri INDEX_DIFF=11
|
||||
declare -gri NO_VENDOR=12 # Keep this around as it might be used later on
|
||||
declare -gri INDEX_NEW=13
|
||||
declare -gri FILE=14
|
||||
|
||||
handleArguments() {
|
||||
declare -p CONFIG &>>/dev/null || return 1
|
||||
declare command="$1"
|
||||
shift
|
||||
case "$command" in
|
||||
find-use)
|
||||
_handle_find_use_arguments "$@" || return $?
|
||||
;;
|
||||
fix-uses)
|
||||
_handle_fix_uses_arguments "$@" || return $?
|
||||
;;
|
||||
index)
|
||||
_handle_index_arguments "$@" || return $?
|
||||
;;
|
||||
classes-in-namespace)
|
||||
_handle_classes_in_namespace_arguments "$@" || return $?
|
||||
;;
|
||||
filepath)
|
||||
_handle_filepath_arguments "$@" || return $?
|
||||
;;
|
||||
*)
|
||||
printf 'handleArguments (line %s): Unknown command "%s" passed.\n' "$(caller)" "$command">&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
}
|
||||
|
||||
_handle_filepath_arguments() {
|
||||
declare arg="$1"
|
||||
while shift; do
|
||||
case "$arg" in
|
||||
-s | --silent)
|
||||
INFO=0
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
|
||||
declare -i i=1
|
||||
while [[ $i -lt ${#arg} ]]; do
|
||||
_handle_filepath_arguments "-${arg:$i:1}"
|
||||
((i++))
|
||||
done
|
||||
else
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
'')
|
||||
:
|
||||
;;
|
||||
*)
|
||||
if [[ -n ${CONFIG[$CLASS_PATH]} ]]; then
|
||||
printf 'Unexpected argument: "%s"\n' "$arg" >&2
|
||||
return 1
|
||||
fi
|
||||
CONFIG[$CLASS_PATH]="$arg"
|
||||
esac
|
||||
arg="$1"
|
||||
done
|
||||
}
|
||||
|
||||
_handle_classes_in_namespace_arguments() {
|
||||
declare arg="$1"
|
||||
while shift; do
|
||||
case "$arg" in
|
||||
-s | --silent)
|
||||
INFO=0
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
declare -i i=1
|
||||
while [[ $i -lt ${#arg} ]]; do
|
||||
_handle_classes_in_namespace_arguments "-${arg:$i:1}"
|
||||
((i++))
|
||||
done
|
||||
else
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
'')
|
||||
:
|
||||
;;
|
||||
*)
|
||||
if [[ -n ${CONFIG[$NAMESPACE]} ]]; then
|
||||
printf 'Unexpected argument: "%s"\n' "$arg" >&2
|
||||
return 1
|
||||
fi
|
||||
CONFIG[$NAMESPACE]="$arg"
|
||||
esac
|
||||
arg="$1"
|
||||
done
|
||||
}
|
||||
|
||||
_handle_index_arguments() {
|
||||
declare arg="$1"
|
||||
while shift; do
|
||||
case "$arg" in
|
||||
-s | --silent)
|
||||
INFO=0
|
||||
;;
|
||||
-d | --diff)
|
||||
CONFIG[$INDEX_DIFF]='--diff'
|
||||
;;
|
||||
-N | --new)
|
||||
CONFIG[$INDEX_NEW]='--new'
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
declare -i i=1
|
||||
while [[ $i -lt ${#arg} ]]; do
|
||||
_handle_index_arguments "-${arg:$i:1}"
|
||||
((i++))
|
||||
done
|
||||
else
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
*)
|
||||
printf 'Unexpected argument: "%s"\n' "$arg" >&2
|
||||
return 1
|
||||
;;
|
||||
esac
|
||||
arg="$1"
|
||||
done
|
||||
}
|
||||
|
||||
_handle_fix_uses_arguments() {
|
||||
declare arg="$1"
|
||||
while shift; do
|
||||
case "$arg" in
|
||||
-s | --silent)
|
||||
INFO=0
|
||||
;;
|
||||
-p | --prefer-own)
|
||||
CONFIG[$PREFER_OWN]='--prefer-own'
|
||||
;;
|
||||
-a | --auto-pick)
|
||||
CONFIG[$AUTO_PICK]='--auto-pick'
|
||||
;;
|
||||
-o | --stdout)
|
||||
CONFIG[$STDOUT]='--stdout'
|
||||
INFO=0
|
||||
;;
|
||||
-j | --json)
|
||||
CONFIG[$STDOUT]='--stdout'
|
||||
CONFIG[$JSON]='--json'
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
declare -i i=1
|
||||
while [[ $i -lt ${#arg} ]]; do
|
||||
_handle_fix_uses_arguments "-${arg:$i:1}"
|
||||
((i++))
|
||||
done
|
||||
else
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
'')
|
||||
:
|
||||
;;
|
||||
*)
|
||||
if [[ -n ${CONFIG[$FILE]} ]]; then
|
||||
printf 'Unexpected argument: "%s"\n' "$arg" >&2
|
||||
return 1
|
||||
fi
|
||||
CONFIG[$FILE]="$arg"
|
||||
esac
|
||||
arg="$1"
|
||||
done
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2034
|
||||
_handle_find_use_arguments() {
|
||||
declare arg="$1"
|
||||
while shift; do
|
||||
case "$arg" in
|
||||
-s | --silent)
|
||||
INFO=0
|
||||
;;
|
||||
-b | --bare)
|
||||
CONFIG[$BARE]='--bare'
|
||||
;;
|
||||
-p | --prefer-own)
|
||||
CONFIG[$PREFER_OWN]='--prefer-own'
|
||||
;;
|
||||
-a | --auto-pick)
|
||||
CONFIG[$AUTO_PICK]='--auto-pick'
|
||||
;;
|
||||
-j | --json) CONFIG[$STDOUT]='--stdout'
|
||||
CONFIG[$JSON]='--json'
|
||||
INFO=0
|
||||
;;
|
||||
--*)
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
;;
|
||||
-*)
|
||||
if [[ ${#arg} -gt 2 ]]; then
|
||||
declare -i i=1
|
||||
while [[ $i -lt ${#arg} ]]; do
|
||||
_handle_find_use_arguments "-${arg:$i:1}"
|
||||
((i++))
|
||||
done
|
||||
else
|
||||
printf 'Unknown option: "%s"\n' "${arg}" >&2
|
||||
return 1
|
||||
fi
|
||||
;;
|
||||
'')
|
||||
:
|
||||
;;
|
||||
*)
|
||||
if [[ -n ${CONFIG[$CLASS_NAME]} ]]; then
|
||||
printf 'Unexpected argument: "%s"\n' "$arg" >&2
|
||||
return 1
|
||||
fi
|
||||
CONFIG[$CLASS_NAME]="$arg"
|
||||
esac
|
||||
arg="$1"
|
||||
done
|
||||
}
|
||||
|
||||
##
|
||||
# This function outputs the difference between the files that are present in the
|
||||
# index and the files that are present in the project directory. The output format is:
|
||||
# +:NEW_FILE (**Not in index but exists on disk**)
|
||||
# -:DELETED_FILE (**In index but does not exist on disk**)
|
||||
##
|
||||
diffIndex() {
|
||||
diff --unchanged-line-format='' --new-line-format='+:%L' --old-line-format='-:%L' \
|
||||
<(sort -u < "$INDEXED" | sed '/^[[:blank:]]*$/d') \
|
||||
<(find ./ -name '*.php' -type f | sed 's!^\./\|^./\(var\|.cache\|vendor/bin\)/.\+$!!g; /^[[:blank:]]*$/d' | sort)
|
||||
}
|
||||
|
||||
##
|
||||
# This function reads the output of a grep command with the option -H or
|
||||
# --with-filename enabled. The lines containing class and namespace declarations
|
||||
# will be parsed and added to the index.
|
||||
#
|
||||
# shellcheck disable=SC2153
|
||||
##
|
||||
fillIndex() {
|
||||
[[ -n $CACHE_DIR ]] || return 1
|
||||
[[ -n $CLASSES ]] || return 1
|
||||
[[ -n $NAMESPACES ]] || return 1
|
||||
[[ -n $USES ]] || return 1
|
||||
[[ -n $USES_LOOKUP ]] || return 1
|
||||
[[ -n $USES_LOOKUP_OWN ]] || return 1
|
||||
[[ -n $FILE_PATHS ]] || return 1
|
||||
[[ -n $NAMESPACE_FILE_PATHS ]] || return 1
|
||||
[[ -n $INDEXED ]] || return 1
|
||||
|
||||
[[ -d $CACHE_DIR ]] || mkdir -p "$CACHE_DIR"
|
||||
|
||||
# Clean up index files if not diffing.
|
||||
if [[ ${CONFIG[$INDEX_NEW]} != '--new' ]]; then
|
||||
echo > "$NAMESPACES"
|
||||
echo > "$CLASSES"
|
||||
echo > "$USES"
|
||||
echo > "$USES_LOOKUP"
|
||||
echo > "$FILE_PATHS"
|
||||
echo > "$USES_OWN"
|
||||
echo > "$USES_LOOKUP_OWN"
|
||||
echo > "$NAMESPACE_FILE_PATHS"
|
||||
echo > "$INDEXED"
|
||||
fi
|
||||
|
||||
declare -A namespaces=() classes=()
|
||||
while IFS=':' read -ra line; do
|
||||
declare file="${line[0]}"
|
||||
|
||||
# Save the namespace or class to add to the FQN cache later on.
|
||||
if [[ "${line[1]}" =~ (class|trait|interface)[[:blank:]]+([A-Za-z_]+) ]]; then
|
||||
classes[$file]="${BASH_REMATCH[2]}"
|
||||
elif [[ "${line[1]}" =~ namespace[[:blank:]]+([A-Za-z_\\]+) ]]; then
|
||||
namespaces[$file]="${BASH_REMATCH[1]}"
|
||||
else
|
||||
debugf 'No class or namespace found in line "%s"' "${line[0]}"
|
||||
fi
|
||||
|
||||
# Add filename to file with indexed filenames. This is required
|
||||
# for diffing the index.
|
||||
echo "$file" >> "$INDEXED"
|
||||
|
||||
if [[ $((++lines%500)) -eq 0 ]]; then
|
||||
info "indexed $lines lines."
|
||||
fi
|
||||
done
|
||||
|
||||
# Fill up the index
|
||||
declare -i uses=0
|
||||
for file in "${!classes[@]}"; do
|
||||
declare namespace="${namespaces[$file]}"
|
||||
declare class="${classes[$file]}"
|
||||
|
||||
if [[ -z $class ]]; then
|
||||
debugf 'Class is missing for file "%s"\n' "$file"
|
||||
debugf 'Namespace: "%s"\n' "$namespace"
|
||||
continue
|
||||
fi
|
||||
|
||||
((uses++))
|
||||
[[ $((uses%500)) -eq 0 ]] && info "Found FQN's for $uses classes."
|
||||
|
||||
echo "$namespace" >> "$NAMESPACES"
|
||||
echo "$class" >> "$CLASSES"
|
||||
echo "$namespace\\$class" >> "$USES"
|
||||
echo "$class:$namespace\\$class" >> "$USES_LOOKUP"
|
||||
echo "$file:$namespace\\$class" >> "$FILE_PATHS"
|
||||
echo "$file:$namespace" >> "$NAMESPACE_FILE_PATHS"
|
||||
|
||||
if [[ $file != 'vendor/'* ]]; then
|
||||
echo "$namespace\\$class" >> "$USES_OWN"
|
||||
echo "$class:$namespace\\$class" >> "$USES_LOOKUP_OWN"
|
||||
fi
|
||||
|
||||
done
|
||||
|
||||
# This keeps the index of class names unique, so that completing class names takes as little
|
||||
# time as possible.
|
||||
# Use echo and a subshell here to prevent changing the file before the command is done.
|
||||
# shellcheck disable=SC2005
|
||||
echo "$(sort -u < "$CLASSES")" > "$CLASSES"
|
||||
|
||||
# Ditto for the namespaces index
|
||||
# shellcheck disable=SC2005
|
||||
echo "$(sort -u < "$NAMESPACES")" > "$NAMESPACES"
|
||||
|
||||
info "Finished indexing. Indexed ${lines} lines and found FQN's for $uses classes." >&2
|
||||
}
|
||||
|
||||
checkCache() {
|
||||
if ! [[ -d "$CACHE_DIR" ]]; then
|
||||
info "No cache dir found, indexing." >&2
|
||||
execute index
|
||||
fi
|
||||
}
|
||||
|
||||
##
|
||||
# Find use statements and needed classes in a file.
|
||||
|
||||
findUsesAndNeeds() {
|
||||
declare -p needs &>>/dev/null || return 1
|
||||
declare -p uses &>>/dev/null || return 1
|
||||
# shellcheck disable=SC2154
|
||||
declare -p namespace &>>/dev/null || return 1
|
||||
|
||||
while read -r line; do
|
||||
[[ $line == namespace* ]] && check_uses='true'
|
||||
if [[ $line == ?(@(abstract|final) )@(class|interface|trait)* ]]; then
|
||||
check_uses='false'
|
||||
check_needs='true'
|
||||
|
||||
read -ra line_array <<<"$line"
|
||||
set -- "${line_array[@]}"
|
||||
while shift && [[ "$1" != @(extends|implements) ]]; do :; done;
|
||||
while shift && [[ -n $1 ]]; do
|
||||
[[ $1 == 'implements' ]] && shift
|
||||
[[ $1 == \\* ]] || _set_needed_if_not_used "$1"
|
||||
done
|
||||
fi
|
||||
|
||||
if $check_uses; then
|
||||
if [[ $line == use* ]]; then
|
||||
declare class_name="${line##*\\}"
|
||||
[[ $class_name == *as* ]] && class_name="${class_name##*as }"
|
||||
debug "Class name: $class_name"
|
||||
class_name="${class_name%%[^a-zA-Z]*}"
|
||||
uses["$class_name"]='used'
|
||||
fi
|
||||
fi
|
||||
|
||||
if $check_needs; then
|
||||
if [[ $line == *function*([[:space:]])*([[:alnum:]_])\(* ]]; then
|
||||
_check_function_needs "$line"
|
||||
continue
|
||||
fi
|
||||
_check_needs "$line"
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
_check_function_needs() {
|
||||
# Strip everything up until function name and argument declaration.
|
||||
declare line="${1#*function}" function_declaration="${1#*function}"
|
||||
|
||||
# Collect the entire argument declaration
|
||||
while [[ $line != *'{'* ]] && read -r line; do
|
||||
function_declaration="$function_declaration $line"
|
||||
done
|
||||
|
||||
declare -a words=()
|
||||
read -ra words <<<"$function_declaration"
|
||||
for i in "${!words[@]}"; do
|
||||
if [[ "${words[$i]}" =~ ^'$'[a-zA-Z_]+ ]]; then
|
||||
declare prev_word="${words[$((i-1))]}"
|
||||
if [[ $prev_word =~ ^([^\(]*\()?([A-Za-z]+)$ ]]; then
|
||||
declare class_name="${BASH_REMATCH[2]}"
|
||||
debugf 'Found parameter type "%s" for function "%s"\n' "$class_name" "$function_declaration"
|
||||
_set_needed_if_not_used "$class_name"
|
||||
fi
|
||||
fi
|
||||
done
|
||||
if [[ "$function_declaration" =~ \):[[:space:]]+([a-zA-Z]+) ]]; then
|
||||
declare class_name="${BASH_REMATCH[1]}"
|
||||
debugf 'Found return type "%s" for function "%s"\n' "$class_name" "$function_declaration"
|
||||
_set_needed_if_not_used "$class_name"
|
||||
fi
|
||||
}
|
||||
|
||||
_check_needs() {
|
||||
declare line="$1" match=''
|
||||
if _line_matches "$line"; then
|
||||
declare class_name="${match//[^a-zA-Z]/}"
|
||||
|
||||
debugf 'Extracted type "%s" from line "%s". Entire match: "%s"\n' "$class_name" "$line" "${BASH_REMATCH[0]}"
|
||||
_set_needed_if_not_used "$class_name"
|
||||
|
||||
line="${line/"${BASH_REMATCH[0]/}"}"
|
||||
_check_needs "$line"
|
||||
fi
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2049
|
||||
_line_matches() {
|
||||
if [[ $line =~ 'new'[[:space:]]+([^\\][A-Za-z]+)\( ]] \
|
||||
|| [[ $line =~ 'instanceof'[[:space:]]+([A-Za-z]+) ]] \
|
||||
|| [[ $line =~ catch[[:space:]]*\(([A-Za-z]+) ]] \
|
||||
|| [[ $line =~ \*[[:blank:]]*@([A-Z][a-zA-Z]*) ]]; then
|
||||
match="${BASH_REMATCH[1]}"
|
||||
return $?
|
||||
elif [[ $line =~ @(var|param|return|throws)[[:space:]]+([A-Za-z]+) ]] \
|
||||
|| [[ $line =~ (^|[\(\[\{[:blank:]])([A-Za-z]+)'::' ]]; then
|
||||
match="${BASH_REMATCH[2]}"
|
||||
return $?
|
||||
fi
|
||||
return 1
|
||||
}
|
||||
|
||||
_set_needed_if_not_used() {
|
||||
declare class_name="$1"
|
||||
if [[ -z ${uses["$class_name"]} ]] \
|
||||
&& [[ -z ${namespace["$class_name"]} ]] \
|
||||
&& [[ "$class_name" != @(static|self|string|int|float|array|object|bool|mixed|parent|void) ]]; then
|
||||
needs["$class_name"]='needed'
|
||||
fi
|
||||
}
|
||||
|
||||
execute "$@"
|
@ -0,0 +1,68 @@
|
||||
;;; test-buffer.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
|
||||
;; 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 'ert)
|
||||
(require 'phpinspect-parser)
|
||||
(require 'phpinspect-buffer)
|
||||
|
||||
(ert-deftest phpinspect-parse-buffer-location-map ()
|
||||
"Confirm that the location map of `phpinspect-current-buffer' is
|
||||
populated when the variable is set and the data in it is accurate."
|
||||
(let* ((location-map)
|
||||
(parsed)
|
||||
(class))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (concat phpinspect-test-php-file-directory "/NamespacedClass.php"))
|
||||
(setq phpinspect-current-buffer
|
||||
(phpinspect-make-buffer :buffer (current-buffer)))
|
||||
(setq parsed (phpinspect-buffer-parse phpinspect-current-buffer))
|
||||
(setq location-map
|
||||
(phpinspect-buffer-location-map phpinspect-current-buffer)))
|
||||
|
||||
(let* ((class (seq-find #'phpinspect-class-p
|
||||
(seq-find #'phpinspect-namespace-p parsed)))
|
||||
(class-region (gethash class location-map))
|
||||
(classname-region (gethash (car (cddadr class)) location-map)))
|
||||
(should class)
|
||||
(should class-region)
|
||||
(should classname-region)
|
||||
;; Character position of the start of the class token.
|
||||
(should (= 417 (phpinspect-region-start class-region)))
|
||||
(should (= 2173 (phpinspect-region-end class-region)))
|
||||
|
||||
(should (= 423 (phpinspect-region-start classname-region)))
|
||||
(should (= 440 (phpinspect-region-end classname-region))))))
|
||||
|
||||
(ert-deftest phpinspect-parse-buffer-no-current ()
|
||||
"Confirm that the parser is still functional with
|
||||
`phpinspect-current-buffer' unset."
|
||||
(let*((buffer)
|
||||
(parsed))
|
||||
(with-temp-buffer
|
||||
(should-not phpinspect-current-buffer)
|
||||
(insert-file-contents (concat phpinspect-test-php-file-directory "/NamespacedClass.php"))
|
||||
(setq parsed (phpinspect-parse-current-buffer)))
|
||||
|
||||
(should (cdr parsed))))
|
@ -0,0 +1,103 @@
|
||||
;;; test-index.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
|
||||
;; 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 'ert)
|
||||
(require 'phpinspect-index)
|
||||
|
||||
(ert-deftest phpinspect-index-static-methods ()
|
||||
(let* ((class-tokens
|
||||
`(:root
|
||||
(:class
|
||||
(:declaration (:word "class") (:word "Potato"))
|
||||
(:block
|
||||
(:static
|
||||
(:function (:declaration (:word "function")
|
||||
(:word "staticMethod")
|
||||
(:list (:variable "untyped")
|
||||
(:comma)
|
||||
(:word "array")
|
||||
(:variable "things")))
|
||||
(:block)))))))
|
||||
(index (phpinspect--index-tokens class-tokens))
|
||||
(expected-index
|
||||
`(phpinspect--root-index
|
||||
(imports)
|
||||
(classes
|
||||
(,(phpinspect--make-type :name"\\Potato" :fully-qualified t)
|
||||
phpinspect--indexed-class
|
||||
(class-name . ,(phpinspect--make-type :name "\\Potato" :fully-qualified t))
|
||||
(location . (0 0))
|
||||
(imports)
|
||||
(methods)
|
||||
(static-methods . (,(phpinspect--make-function
|
||||
:name "staticMethod"
|
||||
:scope '(:public)
|
||||
:arguments `(("untyped" nil)
|
||||
("things" ,(phpinspect--make-type :name "\\array"
|
||||
:fully-qualified t)))
|
||||
:return-type phpinspect--null-type)))
|
||||
(static-variables)
|
||||
(variables)
|
||||
(constants)
|
||||
(extends)
|
||||
(implements)
|
||||
(used-types)))
|
||||
(used-types)
|
||||
(functions))))
|
||||
(should (equal expected-index index))))
|
||||
|
||||
(ert-deftest phpinspect-index-used-types-in-class ()
|
||||
(let* ((result (phpinspect--index-tokens
|
||||
(phpinspect-parse-string
|
||||
"<?php namespace Field; class Potato {
|
||||
public function makeThing(): Thing
|
||||
{
|
||||
if ((new Monkey())->tree() === true) {
|
||||
return new ExtendedThing();
|
||||
}
|
||||
return StaticThing::create(new ThingFactory())->makeThing((((new Potato())->antiPotato(new OtherThing()))));
|
||||
}")))
|
||||
(used-types (alist-get 'used-types (car (alist-get 'classes result)))))
|
||||
(should (equal
|
||||
(mapcar #'phpinspect-intern-name
|
||||
(sort
|
||||
'("Monkey" "ExtendedThing" "StaticThing" "Thing" "ThingFactory" "Potato" "OtherThing")
|
||||
#'string<))
|
||||
(sort used-types (lambda (s1 s2) (string< (symbol-name s1) (symbol-name s2))))))))
|
||||
|
||||
(ert-deftest phpinspect--find-used-types-in-tokens ()
|
||||
(let ((blocks `(
|
||||
((:block (:word "return")
|
||||
(:word "new")
|
||||
(:word "Response")
|
||||
(:list))
|
||||
("Response"))
|
||||
((:block (:list (:word "new") (:word "Response"))
|
||||
(:object-attrib (:word "someMethod")
|
||||
(:list (:word "new")
|
||||
(:word "Request"))))
|
||||
("Request" "Response")))))
|
||||
(dolist (set blocks)
|
||||
(let ((result (phpinspect--find-used-types-in-tokens (car set))))
|
||||
(should (equal (cadr set) result))))))
|
@ -0,0 +1,46 @@
|
||||
;; test-project.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021 Free Software Foundation, Inc.
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
|
||||
;; 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 'ert)
|
||||
(require 'phpinspect-project)
|
||||
|
||||
(ert-deftest phpinspect-project-purge ()
|
||||
(let ((project (phpinspect--make-project)))
|
||||
(phpinspect--project-purge project)
|
||||
|
||||
(should (eq t (phpinspect--project-purged project)))))
|
||||
|
||||
(ert-deftest phpinspect-project-watch-file-and-purge ()
|
||||
(let* ((root (make-temp-file "phpinspect-test" 'dir))
|
||||
(fs (phpinspect-make-fs))
|
||||
(worker (phpinspect-make-worker))
|
||||
(watch-file (concat root "/watch1"))
|
||||
(project (phpinspect--make-project :fs fs :root root)))
|
||||
(phpinspect--project-watch-file project watch-file
|
||||
(lambda (&rest ignored)))
|
||||
|
||||
(phpinspect--project-purge project)
|
||||
|
||||
(should (= 0 (length (hash-table-values (phpinspect--project-file-watchers project)))))))
|
Loading…
Reference in New Issue