Compare commits
1 Commits
Author | SHA1 | Date |
---|---|---|
Hugo Thunnissen | f7f5776f98 | 3 years ago |
@ -1,9 +1 @@
|
||||
*.elc
|
||||
/benchmarks/profile.txt
|
||||
/.deps
|
||||
/data
|
||||
|
||||
|
||||
# ELPA-generated files
|
||||
/phpinspect-autoloads.el
|
||||
/phpinspect-pkg.el
|
||||
*.elc
|
@ -1,5 +0,0 @@
|
||||
pipeline:
|
||||
test:
|
||||
image: silex/emacs:28.1-ci
|
||||
commands:
|
||||
- emacs -L ./ -batch -l ert -l ./phpinspect.el -l ./test/phpinspect-test.el -f ert-run-tests-batch-and-exit
|
@ -1,52 +0,0 @@
|
||||
export EMACS ?= $(shell which emacs)
|
||||
|
||||
ELC_FILES = $(patsubst %.el, %.elc, $(shell ls -1 ./*.el ./test/*.el ./benchmarks/*.el))
|
||||
DEP_DIRECTORY = $(CURDIR)/.deps
|
||||
RUN_EMACS := emacs -batch -L $(CURDIR) --eval '(package-initialize)'
|
||||
|
||||
export HOME = ${DEP_DIRECTORY}
|
||||
|
||||
$(CURDIR): deps
|
||||
$(CURDIR):$(ELC_FILES)
|
||||
$(CURDIR): ./data/builtin-stubs-index.eld.gz
|
||||
|
||||
./.deps: ./phpinspect.el
|
||||
./.deps:
|
||||
emacs -batch -l ./scripts/install-deps.el
|
||||
|
||||
./stubs/builtins.php: ./scripts/generate-builtin-stubs.php
|
||||
mkdir -p ./stubs/
|
||||
php ./scripts/generate-builtin-stubs.php > ./stubs/builtins.php
|
||||
|
||||
./data/builtin-stubs-index.eld.gz: ./stubs/builtins.php | ./.deps
|
||||
mkdir -p ./data/
|
||||
$(RUN_EMACS) -l phpinspect-cache -f phpinspect-dump-stub-index
|
||||
|
||||
%.elc: %.el
|
||||
$(RUN_EMACS) --eval '(setq byte-compile-error-on-warn t)' -f batch-byte-compile $<
|
||||
|
||||
.PHONY: deps
|
||||
deps: ./.deps
|
||||
|
||||
.PHONY: stub-index
|
||||
stub-index: ./data/builtin-stubs-index.eld.gz
|
||||
|
||||
.PHONY: clean
|
||||
clean:
|
||||
rm -f $(ELC_FILES) ./data/builtin-stubs-index.eld.gz
|
||||
|
||||
.PHONY: clean-all
|
||||
clean-all: clean
|
||||
rm -f ./stubs/builtins.php
|
||||
|
||||
.PHONY: compile
|
||||
compile: ./.deps
|
||||
compile: $(ELC_FILES)
|
||||
|
||||
.PHONY: compile-native
|
||||
compile-native: ./.deps
|
||||
bash ./scripts/native-compile.bash
|
||||
|
||||
.PHONY: test
|
||||
test: deps
|
||||
$(RUN_EMACS) -L ./test -l ./test/phpinspect-test e -f ert-run-tests-batch
|
File diff suppressed because it is too large
Load Diff
@ -1,53 +0,0 @@
|
||||
;;; appendage.el --- Benchmarks of list appendage -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
;; Keywords: benchmark
|
||||
|
||||
;; 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:
|
||||
|
||||
|
||||
(message "20000 appendages using nconc")
|
||||
(garbage-collect)
|
||||
(benchmark
|
||||
1 '(let (list)
|
||||
(dotimes (i 20000)
|
||||
(setq list (nconc list (list i))))
|
||||
|
||||
list))
|
||||
|
||||
(message "20000 appendages using push + nreverse")
|
||||
(garbage-collect)
|
||||
(benchmark
|
||||
1 '(let (list)
|
||||
(dotimes (i 20000)
|
||||
(push i list))
|
||||
|
||||
(nreverse list)))
|
||||
|
||||
(message "20000 appendages using rear pointer")
|
||||
(garbage-collect)
|
||||
(benchmark
|
||||
1 '(let* ((list (cons nil nil))
|
||||
(rear list))
|
||||
|
||||
(dotimes (i 20000)
|
||||
(setq rear (setcdr rear (cons i nil))))
|
||||
|
||||
(cdr list)))
|
@ -1,137 +0,0 @@
|
||||
;;; parse-file.el --- Benchmarks of phpinspect parser in different configurations -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
;; Keywords: benchmark
|
||||
|
||||
;; 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-parser)
|
||||
|
||||
(defun phpinspect-parse-current-buffer ()
|
||||
(phpinspect-parse-buffer-until-point
|
||||
(current-buffer)
|
||||
(point-max)))
|
||||
|
||||
|
||||
|
||||
(let* ((here (file-name-directory (macroexp-file-name)))
|
||||
(benchmark-file (or (getenv "PHPINSPECT_BENCHMARK_FILE")
|
||||
(expand-file-name "Response.php" here)))
|
||||
result)
|
||||
|
||||
|
||||
|
||||
(with-temp-buffer
|
||||
(insert-file-contents benchmark-file)
|
||||
|
||||
(message "Incremental parse (warmup):")
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :incremental t :bmap (phpinspect-make-bmap))
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(let ((bmap (phpinspect-make-bmap))
|
||||
(bmap2 (phpinspect-make-bmap)))
|
||||
(message "Incremental parse:")
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :incremental t :bmap bmap)
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(garbage-collect)
|
||||
(message "Incremental parse (no edits):")
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
|
||||
:bmap bmap2
|
||||
:previous-bmap bmap
|
||||
:edtrack (phpinspect-make-edtrack))
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
(garbage-collect)
|
||||
|
||||
(message "Incremental parse repeat (no edits):")
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :incremental t
|
||||
:bmap (phpinspect-make-bmap)
|
||||
:previous-bmap bmap2
|
||||
:edtrack (phpinspect-make-edtrack))
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(garbage-collect)
|
||||
|
||||
(let ((edtrack (phpinspect-make-edtrack))
|
||||
(bmap (phpinspect-make-bmap))
|
||||
(bmap-after (phpinspect-make-bmap)))
|
||||
;; Fresh
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :incremental t :bmap bmap)
|
||||
(phpinspect-parse-current-buffer))
|
||||
|
||||
(message "Incremental parse after buffer edit:")
|
||||
;; Removes closing curly brace of __construct
|
||||
(goto-char 9062)
|
||||
(delete-char -1)
|
||||
|
||||
(garbage-collect)
|
||||
|
||||
(phpinspect-edtrack-register-edit edtrack 9061 9061 1)
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :bmap bmap-after :incremental t :previous-bmap bmap :edtrack edtrack)
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
(phpinspect-edtrack-clear edtrack)
|
||||
(insert "{")
|
||||
|
||||
(phpinspect-edtrack-register-edit edtrack 9061 9062 0)
|
||||
;; Mark region as edit without length deta
|
||||
(phpinspect-edtrack-register-edit edtrack 19552 19562 10)
|
||||
|
||||
(garbage-collect)
|
||||
|
||||
;;(profiler-start 'cpu)
|
||||
(message "Incremental parse after 2 more edits:")
|
||||
(phpinspect-with-parse-context (phpinspect-make-pctx :bmap (phpinspect-make-bmap)
|
||||
:incremental t
|
||||
:previous-bmap bmap-after
|
||||
:edtrack edtrack)
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
;; (save-current-buffer
|
||||
;; (profiler-stop)
|
||||
;; (profiler-report)
|
||||
;; (profiler-report-write-profile (expand-file-name "profile.txt" here)))
|
||||
)))
|
||||
|
||||
(with-temp-buffer
|
||||
(insert-file-contents benchmark-file)
|
||||
|
||||
(garbage-collect)
|
||||
(message "Bare (no token reuse) parse (warmup):")
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer)))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(garbage-collect)
|
||||
(message "Bare (no token reuse) parse:")
|
||||
(setq result (benchmark-run 1 (phpinspect-parse-current-buffer))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result)))
|
@ -1,103 +0,0 @@
|
||||
;;; splay-tree.el --- Benchmarks of phpinspect-splayt.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
;; Keywords: benchmark
|
||||
|
||||
;; 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-splayt)
|
||||
|
||||
(let ((tree (phpinspect-make-splayt))
|
||||
result)
|
||||
(message "Splay tree 10000 insertions:")
|
||||
(garbage-collect)
|
||||
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(dotimes (i 10000)
|
||||
(phpinspect-splayt-insert tree i 'value))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Splay tree 10000 lookups:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(dotimes (i 10000)
|
||||
(phpinspect-splayt-find tree i))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Splay tree 10000 items traversal:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(phpinspect-splayt-traverse (i tree)
|
||||
(ignore i))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result))
|
||||
|
||||
|
||||
(message "Splay tree 10000 items LR traversal:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(phpinspect-splayt-traverse-lr (i tree)
|
||||
(ignore i))))
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result)))
|
||||
|
||||
|
||||
|
||||
(let (map result)
|
||||
(message "Hashtable 10000 insertions:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(progn
|
||||
(setq map (make-hash-table :test #'eq :size 10000 :rehash-size 1.5))
|
||||
(dotimes (i 10000)
|
||||
(puthash i 'value map)))))
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Hashtable 10000 lookups:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(dotimes (i 10000)
|
||||
(ignore (gethash i map)))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result))
|
||||
|
||||
|
||||
(message "Hashtable 10000 iterations:")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1
|
||||
(ignore (maphash (lambda (k v) k v) map))))
|
||||
|
||||
(message "Elapsed time: %f (%f in %d GC's)"
|
||||
(car result) (caddr result) (cadr result)))
|
@ -1,51 +0,0 @@
|
||||
;;; stubs.el --- Benchmarks of phpinspect stub index dump and load times -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
;; Keywords: benchmark
|
||||
|
||||
;; 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-cache)
|
||||
|
||||
(let (result)
|
||||
|
||||
(message "Building and loading stub cache")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1 (phpinspect-build-stub-cache)))
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Building stub cache")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1 (phpinspect-build-stub-index)))
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Building and dumping stub cache")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1 (phpinspect-dump-stub-index)))
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result))
|
||||
|
||||
(message "Loading stub cache")
|
||||
(garbage-collect)
|
||||
(setq result
|
||||
(benchmark-run 1 (phpinspect-load-stub-index)))
|
||||
(message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) (cadr result)))
|
@ -1,288 +0,0 @@
|
||||
;;; phpinspect-autoload.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'cl-lib)
|
||||
(require 'phpinspect-fs)
|
||||
(require 'phpinspect-util)
|
||||
(require 'phpinspect-pipeline)
|
||||
(require 'json)
|
||||
|
||||
(cl-defstruct (phpinspect-psr0
|
||||
(:constructor phpinspect-make-psr0-generated))
|
||||
(prefix nil
|
||||
:type string
|
||||
:documentation "The namespace prefix for which the directories contain code.")
|
||||
(autoloader nil
|
||||
:type phpinspect-autoloader)
|
||||
(own nil
|
||||
:type boolean)
|
||||
(fs nil
|
||||
:type phpinspect-fs)
|
||||
(directories nil
|
||||
:type list
|
||||
:documentation
|
||||
"The directories that this autoloader finds code in."))
|
||||
|
||||
(cl-defstruct (phpinspect-psr4
|
||||
(:constructor phpinspect-make-psr4-generated))
|
||||
(prefix nil
|
||||
:type string
|
||||
:documentation "The namespace prefix for which the directories contain code.")
|
||||
(autoloader nil
|
||||
:type phpinspect-autoloader)
|
||||
(own nil
|
||||
:type boolean)
|
||||
(fs nil
|
||||
:type phpinspect-fs)
|
||||
(directories nil
|
||||
:type list
|
||||
:documentation
|
||||
"The directories that this autoloader finds code in."))
|
||||
|
||||
(cl-defstruct (phpinspect-files (:constructor phpinspect-make-files))
|
||||
(autoloader nil)
|
||||
(list nil
|
||||
:type list
|
||||
:documentation
|
||||
"List of files to be indexed"))
|
||||
|
||||
(cl-defstruct (phpinspect-autoloader
|
||||
(:constructor phpinspect-make-autoloader))
|
||||
(refresh-thread nil
|
||||
:type thread)
|
||||
(fs nil
|
||||
:type phpinspect-fs)
|
||||
(file-indexer nil
|
||||
:type function)
|
||||
(project-root-resolver nil
|
||||
:type function)
|
||||
(own-types (make-hash-table :test 'eq :size 10000 :rehash-size 10000)
|
||||
:type hash-table
|
||||
:documentation "The internal types that can be
|
||||
autoloaded through this autoloader")
|
||||
(types (make-hash-table :test 'eq :size 10000 :rehash-size 10000)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"The external types that can be autoloaded through this autoloader.")
|
||||
(type-name-fqn-bags (make-hash-table :test 'eq :size 3000 :rehash-size 3000)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"Hash table that contains lists of fully
|
||||
qualified names congruent with a bareword type name. Keyed by
|
||||
bareword typenames."))
|
||||
|
||||
(cl-defmethod phpinspect--read-json-file (fs file)
|
||||
(with-temp-buffer
|
||||
(phpinspect-fs-insert-file-contents fs file)
|
||||
(goto-char 0)
|
||||
(phpinspect-json-preset (json-read))))
|
||||
|
||||
(define-inline phpinspect-filename-to-typename (dir filename &optional prefix)
|
||||
(inline-quote
|
||||
(phpinspect-intern-name
|
||||
(replace-regexp-in-string
|
||||
"\\\\[\\]+"
|
||||
"\\\\"
|
||||
(concat "\\"
|
||||
(or ,prefix "")
|
||||
(replace-regexp-in-string
|
||||
"/" "\\\\"
|
||||
(string-remove-suffix
|
||||
".php"
|
||||
(string-remove-prefix ,dir ,filename))))))))
|
||||
|
||||
(defun phpinspect-find-composer-json-files (fs project-root)
|
||||
(let ((cj-path (concat project-root "/composer.json"))
|
||||
(vendor-dir (concat project-root "/vendor"))
|
||||
files)
|
||||
(when (phpinspect-fs-file-exists-p fs cj-path)
|
||||
(push `(local . ,cj-path) files))
|
||||
|
||||
(when (phpinspect-fs-file-directory-p fs vendor-dir)
|
||||
(dolist (author-dir (phpinspect-fs-directory-files fs vendor-dir))
|
||||
(when (phpinspect-fs-file-directory-p fs author-dir)
|
||||
(dolist (dependency-dir (phpinspect-fs-directory-files fs author-dir))
|
||||
(setq cj-path (concat dependency-dir "/composer.json"))
|
||||
(when (and (phpinspect-fs-file-directory-p fs dependency-dir)
|
||||
(phpinspect-fs-file-exists-p fs cj-path))
|
||||
(push `(vendor . ,cj-path) files))))))
|
||||
files))
|
||||
|
||||
(cl-defmethod phpinspect-al-strategy-execute ((strat phpinspect-psr4))
|
||||
(let* ((fs (phpinspect-psr4-fs strat))
|
||||
(al (phpinspect-psr4-autoloader strat))
|
||||
(own (phpinspect-psr4-own strat))
|
||||
(own-typehash (phpinspect-autoloader-own-types al))
|
||||
(typehash (phpinspect-autoloader-types al))
|
||||
(prefix (phpinspect-psr4-prefix strat))
|
||||
type-fqn)
|
||||
(dolist (dir (phpinspect-psr4-directories strat))
|
||||
(when (phpinspect-fs-file-directory-p fs dir)
|
||||
(dolist (file (phpinspect-fs-directory-files-recursively fs dir "\\.php$"))
|
||||
(setq type-fqn (phpinspect-filename-to-typename dir file prefix))
|
||||
(phpinspect-autoloader-put-type-bag al type-fqn)
|
||||
(puthash type-fqn file typehash)
|
||||
(when own
|
||||
(puthash type-fqn file own-typehash)))))))
|
||||
|
||||
(cl-defmethod phpinspect-al-strategy-execute ((strat phpinspect-psr0))
|
||||
(let* ((fs (phpinspect-psr0-fs strat))
|
||||
(al (phpinspect-psr0-autoloader strat))
|
||||
(own (phpinspect-psr0-own strat))
|
||||
(own-typehash (phpinspect-autoloader-own-types al))
|
||||
(typehash (phpinspect-autoloader-types al))
|
||||
type-fqn)
|
||||
(dolist (dir (phpinspect-psr0-directories strat))
|
||||
(dolist (file (phpinspect-fs-directory-files-recursively fs dir "\\.php$"))
|
||||
(setq type-fqn (phpinspect-filename-to-typename dir file))
|
||||
(phpinspect-autoloader-put-type-bag al type-fqn)
|
||||
(puthash type-fqn file typehash)
|
||||
(when own
|
||||
(puthash type-fqn file own-typehash))))))
|
||||
|
||||
(cl-defmethod phpinspect-al-strategy-execute ((strat phpinspect-files))
|
||||
(phpinspect--log "indexing files list: %s" (phpinspect-files-list strat))
|
||||
(let* ((indexer (phpinspect-autoloader-file-indexer (phpinspect-files-autoloader strat))))
|
||||
(phpinspect-pipeline (phpinspect-files-list strat)
|
||||
:into (funcall :with-context indexer))))
|
||||
|
||||
(cl-defmethod phpinspect-autoloader-put-type-bag ((al phpinspect-autoloader) (type-fqn (head phpinspect-name)))
|
||||
(let* ((type-name (phpinspect-intern-name
|
||||
(car (last (split-string (phpinspect-name-string type-fqn) "\\\\")))))
|
||||
(bag (gethash type-name (phpinspect-autoloader-type-name-fqn-bags al))))
|
||||
(if bag
|
||||
(setcdr bag (cons type-fqn (cdr bag)))
|
||||
(push type-fqn bag)
|
||||
(puthash type-name bag (phpinspect-autoloader-type-name-fqn-bags al)))))
|
||||
|
||||
(cl-defmethod phpinspect-autoloader-get-type-bag ((al phpinspect-autoloader) (type-name (head phpinspect-name)))
|
||||
(gethash type-name (phpinspect-autoloader-type-name-fqn-bags al)))
|
||||
|
||||
(cl-defmethod phpinspect-iterate-composer-jsons
|
||||
((al phpinspect-autoloader) file)
|
||||
(let* ((fs (phpinspect-autoloader-fs al))
|
||||
(project-root (file-name-directory (cdr file)))
|
||||
json autoload batch)
|
||||
|
||||
|
||||
(condition-case err
|
||||
(setq json (phpinspect--read-json-file fs (cdr file)))
|
||||
(t (phpinspect-message "Error parsing composer json at %s : %s " (cdr file) err)))
|
||||
|
||||
(when json
|
||||
(setq autoload (gethash "autoload" json))
|
||||
|
||||
(when (hash-table-p autoload)
|
||||
(maphash
|
||||
(lambda (type prefixes)
|
||||
(let ((strategy))
|
||||
(pcase type
|
||||
("psr-0"
|
||||
(maphash
|
||||
(lambda (prefix directory-paths)
|
||||
(when (stringp directory-paths)
|
||||
(setq directory-paths (list directory-paths)))
|
||||
(setq strategy (phpinspect-make-psr0-generated
|
||||
:autoloader al
|
||||
:fs fs
|
||||
:prefix prefix
|
||||
:own (eq 'local (car file))))
|
||||
(dolist (path directory-paths)
|
||||
(push (file-name-concat project-root path)
|
||||
(phpinspect-psr0-directories strategy)))
|
||||
(push strategy batch))
|
||||
prefixes))
|
||||
("psr-4"
|
||||
(maphash
|
||||
(lambda (prefix directory-paths)
|
||||
(when (stringp directory-paths)
|
||||
(setq directory-paths (list directory-paths)))
|
||||
(setq strategy (phpinspect-make-psr4-generated
|
||||
:fs fs
|
||||
:autoloader al
|
||||
:prefix prefix
|
||||
:own (eq 'local (car file))))
|
||||
(dolist (path directory-paths)
|
||||
(push (file-name-concat project-root path)
|
||||
(phpinspect-psr4-directories strategy)))
|
||||
(push strategy batch))
|
||||
prefixes))
|
||||
("files"
|
||||
(setq strategy
|
||||
(phpinspect-make-files
|
||||
:list (mapcar
|
||||
(lambda (file) (file-name-concat project-root file))
|
||||
prefixes)
|
||||
:autoloader al))
|
||||
(push strategy batch))
|
||||
(_ (phpinspect--log "Unsupported autoload strategy \"%s\" encountered" type)))))
|
||||
autoload)
|
||||
(phpinspect-pipeline-emit-all batch)))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect-autoloader-resolve ((autoloader phpinspect-autoloader)
|
||||
(typename (head phpinspect-name)))
|
||||
;; Wait for pending refresh if not running in main thread.
|
||||
(unless (eq main-thread (current-thread))
|
||||
(when (and (phpinspect-autoloader-refresh-thread autoloader)
|
||||
(thread-live-p (phpinspect-autoloader-refresh-thread autoloader)))
|
||||
(phpinspect--log
|
||||
"Pausing thread %s to await autoload refresh completion"
|
||||
(thread-name (current-thread)))
|
||||
(thread-join (phpinspect-autoloader-refresh-thread autoloader))
|
||||
(phpinspect--log "Autoload refresh completed, continuing waiting thread %s"
|
||||
(thread-name (current-thread)))))
|
||||
|
||||
(or (gethash typename (phpinspect-autoloader-own-types autoloader))
|
||||
(gethash typename (phpinspect-autoloader-types autoloader))))
|
||||
|
||||
(cl-defmethod phpinspect-autoloader-refresh ((autoloader phpinspect-autoloader) &optional async-callback)
|
||||
"Refresh autoload definitions by reading composer.json files
|
||||
from the project and vendor folders."
|
||||
(let* ((project-root (funcall (phpinspect-autoloader-project-root-resolver autoloader)))
|
||||
(fs (phpinspect-autoloader-fs autoloader)))
|
||||
(setf (phpinspect-autoloader-type-name-fqn-bags autoloader)
|
||||
(make-hash-table :test 'eq :size 3000 :rehash-size 3000))
|
||||
(setf (phpinspect-autoloader-own-types autoloader)
|
||||
(make-hash-table :test 'eq :size 10000 :rehash-size 10000))
|
||||
(setf (phpinspect-autoloader-types autoloader)
|
||||
(make-hash-table :test 'eq :size 10000 :rehash-size 10000))
|
||||
|
||||
(setf (phpinspect-autoloader-refresh-thread autoloader)
|
||||
(phpinspect-pipeline (phpinspect-find-composer-json-files fs project-root)
|
||||
:async (or async-callback
|
||||
(lambda (_result error)
|
||||
(if error
|
||||
(phpinspect-message "Error during autoloader refresh: %s" error)
|
||||
(phpinspect-message
|
||||
(concat "Refreshed project autoloader. Found %d types within project,"
|
||||
" %d types total.")
|
||||
(hash-table-count (phpinspect-autoloader-own-types autoloader))
|
||||
(hash-table-count (phpinspect-autoloader-types autoloader))))))
|
||||
:into (phpinspect-iterate-composer-jsons :with-context autoloader)
|
||||
:into phpinspect-al-strategy-execute))))
|
||||
|
||||
(provide 'phpinspect-autoload)
|
||||
;;; phpinspect-autoload.el ends here
|
@ -1,288 +0,0 @@
|
||||
;;; phpinspect-bmap.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-splayt)
|
||||
(require 'phpinspect-meta)
|
||||
(require 'phpinspect-changeset)
|
||||
(require 'phpinspect-parse-context)
|
||||
(require 'phpinspect-util)
|
||||
(require 'compat)
|
||||
(require 'phpinspect-token-predicates)
|
||||
|
||||
(eval-when-compile
|
||||
(defvar phpinspect-parse-context nil
|
||||
"dummy for compilation")
|
||||
|
||||
(declare-function phpinspect-pctx-register-changeset "phpinspect-parse-context" (pctx changeset))
|
||||
|
||||
(phpinspect--declare-log-group 'bmap))
|
||||
|
||||
(cl-defstruct (phpinspect-bmap (:constructor phpinspect-make-bmap))
|
||||
(starts (make-hash-table :test #'eql
|
||||
:size (floor (/ (point-max) 2))
|
||||
:rehash-size 1.5))
|
||||
(ends (make-hash-table :test #'eql
|
||||
:size (floor (/ (point-max) 2))
|
||||
:rehash-size 1.5))
|
||||
(meta (make-hash-table :test #'eq
|
||||
:size (floor (/ (point-max) 2))
|
||||
:rehash-size 1.5))
|
||||
(token-stack nil
|
||||
:type list)
|
||||
(overlays (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt)
|
||||
(declarations (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The declaration tokens encountered.")
|
||||
(imports (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The import statements encountered.")
|
||||
(functions (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The function definitions encountered.")
|
||||
(classes (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The classes encountered.")
|
||||
(class-variables (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The class attribute variables encountered")
|
||||
(namespaces (phpinspect-make-splayt)
|
||||
:type phpinspect-splayt
|
||||
:documentation "The namespaces encountered")
|
||||
(-root-meta nil
|
||||
:type phpinspect-meta)
|
||||
(last-token-start nil
|
||||
:type integer))
|
||||
|
||||
(define-inline phpinspect-bmap-root-meta (bmap)
|
||||
(inline-letevals (bmap)
|
||||
(inline-quote
|
||||
(with-memoization (phpinspect-bmap--root-meta ,bmap)
|
||||
(phpinspect-bmap-token-starting-at
|
||||
,bmap (phpinspect-bmap-last-token-start ,bmap))))))
|
||||
|
||||
(defsubst phpinspect-make-region (start end)
|
||||
(list start end))
|
||||
|
||||
(defalias 'phpinspect-region-start #'car)
|
||||
(defalias 'phpinspect-region-end #'cadr)
|
||||
|
||||
(defsubst phpinspect-region-size (region)
|
||||
(- (phpinspect-region-end region) (phpinspect-region-start region)))
|
||||
|
||||
(defsubst phpinspect-region> (reg1 reg2)
|
||||
(> (phpinspect-region-size reg1) (phpinspect-region-size reg2)))
|
||||
|
||||
(defsubst phpinspect-region< (reg1 reg2)
|
||||
(< (phpinspect-region-size reg1) (phpinspect-region-size reg2)))
|
||||
|
||||
(defsubst phpinspect-region-overlaps-point (reg point)
|
||||
(and (> (phpinspect-region-end reg) point)
|
||||
(<= (phpinspect-region-start reg) point)))
|
||||
|
||||
(defsubst phpinspect-region-overlaps (reg1 reg2)
|
||||
(or (phpinspect-region-overlaps-point reg1 (phpinspect-region-start reg2))
|
||||
(phpinspect-region-overlaps-point reg1 (- (phpinspect-region-end reg2) 1))
|
||||
(phpinspect-region-overlaps-point reg2 (phpinspect-region-start reg1))
|
||||
(phpinspect-region-overlaps-point reg2 (- (phpinspect-region-end reg1) 1))))
|
||||
|
||||
(defsubst phpinspect-region-encloses (reg1 reg2)
|
||||
(and (<= (phpinspect-region-start reg1) (phpinspect-region-start reg2))
|
||||
(>= (phpinspect-region-end reg1) (phpinspect-region-end reg2))))
|
||||
|
||||
(define-inline phpinspect-overlay-bmap (overlay)
|
||||
(inline-quote (car (nthcdr 4 ,overlay))))
|
||||
|
||||
(define-inline phpinspect-overlay-delta (overlay)
|
||||
(inline-quote (cadddr ,overlay)))
|
||||
|
||||
(define-inline phpinspect-overlay-start (overlay)
|
||||
(inline-quote (cadr ,overlay)))
|
||||
|
||||
(define-inline phpinspect-overlay-end (overlay)
|
||||
(inline-quote (caddr ,overlay)))
|
||||
|
||||
(define-inline phpinspect-overlay-overlaps-point (overlay point)
|
||||
(inline-letevals (overlay point)
|
||||
(inline-quote
|
||||
(and (> (phpinspect-overlay-end ,overlay) ,point)
|
||||
(<= (phpinspect-overlay-start ,overlay) ,point)))))
|
||||
|
||||
(defsubst phpinspect-bmap-register (bmap start end token &optional whitespace-before overlay)
|
||||
(let* ((starts (phpinspect-bmap-starts bmap))
|
||||
(ends (phpinspect-bmap-ends bmap))
|
||||
(meta (phpinspect-bmap-meta bmap))
|
||||
(last-token-start (phpinspect-bmap-last-token-start bmap))
|
||||
(existing-end (gethash end ends))
|
||||
(token-meta (or overlay (phpinspect-make-meta nil start end whitespace-before token))))
|
||||
(when (< end start)
|
||||
(error "Token %s ends before it starts. Start: %s, end: %s" token start end))
|
||||
|
||||
(unless whitespace-before
|
||||
(setq whitespace-before ""))
|
||||
|
||||
(puthash start token-meta starts)
|
||||
|
||||
(cond
|
||||
((phpinspect-use-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-imports bmap) (phpinspect-meta-start token-meta) token-meta))
|
||||
((phpinspect-class-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-classes bmap) (phpinspect-meta-start token-meta) token-meta))
|
||||
((phpinspect-declaration-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-declarations bmap) (phpinspect-meta-start token-meta) token-meta))
|
||||
((phpinspect-function-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-functions bmap) (phpinspect-meta-start token-meta) token-meta))
|
||||
((phpinspect-namespace-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-namespaces bmap) (phpinspect-meta-start token-meta) token-meta))
|
||||
((or (phpinspect-const-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-class-variable-p (phpinspect-meta-token token-meta)))
|
||||
(phpinspect-splayt-insert
|
||||
(phpinspect-bmap-class-variables bmap) (phpinspect-meta-start token-meta) token-meta)))
|
||||
|
||||
(if existing-end
|
||||
(push token existing-end)
|
||||
(puthash end (list token-meta) ends))
|
||||
|
||||
(puthash token token-meta meta)
|
||||
|
||||
(when (and last-token-start
|
||||
(<= start last-token-start))
|
||||
(let ((child)
|
||||
(stack (phpinspect-bmap-token-stack bmap)))
|
||||
(while (and (car stack) (>= (phpinspect-meta-start (car stack)) start))
|
||||
(setq child (pop stack))
|
||||
(phpinspect-meta-set-parent child token-meta))
|
||||
|
||||
(setf (phpinspect-bmap-token-stack bmap) stack)))
|
||||
|
||||
(setf (phpinspect-bmap-last-token-start bmap) start)
|
||||
(push token-meta (phpinspect-bmap-token-stack bmap))))
|
||||
|
||||
(define-inline phpinspect-pctx-register-token (pctx token start end)
|
||||
(inline-letevals (pctx)
|
||||
(inline-quote
|
||||
(phpinspect-bmap-register
|
||||
(phpinspect-pctx-bmap ,pctx) ,start ,end ,token (phpinspect-pctx-consume-whitespace ,pctx)))))
|
||||
|
||||
|
||||
(defsubst phpinspect-overlay-p (overlay)
|
||||
(and (listp overlay)
|
||||
(eq 'overlay (car overlay))))
|
||||
|
||||
(defsubst phpinspect-bmap-overlay-at-point (bmap point)
|
||||
(let ((overlay (phpinspect-splayt-find-largest-before (phpinspect-bmap-overlays bmap) point)))
|
||||
(when (and overlay (phpinspect-overlay-overlaps-point overlay point))
|
||||
overlay)))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-token-starting-at ((overlay (head overlay)) point)
|
||||
(phpinspect-bmap-token-starting-at
|
||||
(phpinspect-overlay-bmap overlay) (- point (phpinspect-overlay-delta overlay))))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-token-starting-at ((bmap phpinspect-bmap) point)
|
||||
(let ((overlay (phpinspect-bmap-overlay-at-point bmap point)))
|
||||
(if overlay
|
||||
(phpinspect-bmap-token-starting-at overlay point)
|
||||
(gethash point (phpinspect-bmap-starts bmap)))))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-tokens-ending-at ((overlay (head overlay)) point)
|
||||
(phpinspect-bmap-tokens-ending-at
|
||||
(phpinspect-overlay-bmap overlay) (- point (phpinspect-overlay-delta overlay))))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-tokens-ending-at ((bmap phpinspect-bmap) point)
|
||||
(let ((overlay (phpinspect-bmap-overlay-at-point bmap point)))
|
||||
(if overlay
|
||||
(phpinspect-bmap-tokens-ending-at overlay point)
|
||||
(gethash point (phpinspect-bmap-ends bmap)))))
|
||||
|
||||
(defsubst phpinspect-bmap-tokens-overlapping (bmap point)
|
||||
(sort
|
||||
(phpinspect-meta-find-overlapping-children (phpinspect-bmap-root-meta bmap) point)
|
||||
#'phpinspect-meta-sort-width))
|
||||
|
||||
(defsubst phpinspect-overlay-encloses-meta (overlay meta)
|
||||
(and (>= (phpinspect-meta-start meta) (phpinspect-overlay-start overlay))
|
||||
(<= (phpinspect-meta-end meta) (phpinspect-overlay-end overlay))))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-token-meta ((overlay (head overlay)) token)
|
||||
(phpinspect-bmap-token-meta (phpinspect-overlay-bmap overlay) token))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-token-meta ((bmap phpinspect-bmap) token)
|
||||
(unless (phpinspect-probably-token-p token)
|
||||
(error "Unexpected argument, expected `phpinspect-token-p'. Got invalid token %s" token))
|
||||
|
||||
(or (gethash token (phpinspect-bmap-meta bmap))
|
||||
(let ((found?))
|
||||
(catch 'found
|
||||
(phpinspect-splayt-traverse (overlay (phpinspect-bmap-overlays bmap))
|
||||
(when (setq found? (phpinspect-bmap-token-meta overlay token))
|
||||
;; Hit overlay's node to rebalance tree
|
||||
(phpinspect-splayt-find
|
||||
(phpinspect-bmap-overlays bmap) (phpinspect-overlay-end overlay))
|
||||
(throw 'found found?)))))))
|
||||
|
||||
(cl-defmethod phpinspect-bmap-last-token-before-point ((bmap phpinspect-bmap) point)
|
||||
"Search backward in BMAP for last token ending before POINT."
|
||||
(phpinspect-meta-find-child-before-recursively (phpinspect-bmap-root-meta bmap) point))
|
||||
|
||||
(defsubst phpinspect-bmap-overlay (bmap bmap-overlay token-meta pos-delta &optional whitespace-before)
|
||||
(let* ((overlays (phpinspect-bmap-overlays bmap))
|
||||
(start (+ (phpinspect-meta-start token-meta) pos-delta))
|
||||
(end (+ (phpinspect-meta-end token-meta) pos-delta))
|
||||
overlay
|
||||
(last-overlay (phpinspect-splayt-node-value (phpinspect-splayt-root-node overlays))))
|
||||
|
||||
(phpinspect-meta-with-changeset token-meta
|
||||
(phpinspect-meta-detach-parent token-meta)
|
||||
(phpinspect-meta-shift token-meta pos-delta)
|
||||
|
||||
(if (and last-overlay (= (- start (length whitespace-before)) (phpinspect-overlay-end last-overlay))
|
||||
(= pos-delta (phpinspect-overlay-delta last-overlay)))
|
||||
(progn
|
||||
(phpinspect--log "Expanding previous overlay from (%d,%d) to (%d,%d)"
|
||||
(phpinspect-overlay-start last-overlay) (phpinspect-overlay-end last-overlay)
|
||||
(phpinspect-overlay-start last-overlay) end)
|
||||
(setf (phpinspect-overlay-end last-overlay) end)
|
||||
(setf (phpinspect-meta-overlay token-meta) last-overlay))
|
||||
(phpinspect--log "Inserting new overlay at (%d,%d)" start end)
|
||||
(setq overlay `(overlay ,start ,end ,pos-delta ,bmap-overlay ,token-meta))
|
||||
(setf (phpinspect-meta-overlay token-meta) overlay)
|
||||
(phpinspect-splayt-insert (phpinspect-bmap-overlays bmap) (phpinspect-overlay-start overlay) overlay))
|
||||
(phpinspect-bmap-register bmap start end (phpinspect-meta-token token-meta) whitespace-before token-meta))))
|
||||
|
||||
(defun phpinspect-bmap-make-location-resolver (bmap)
|
||||
(lambda (token)
|
||||
(let ((meta (phpinspect-bmap-token-meta bmap token)))
|
||||
(if meta
|
||||
(phpinspect-make-region (phpinspect-meta-start meta)
|
||||
(phpinspect-meta-end meta))
|
||||
(phpinspect-make-region 0 0)))))
|
||||
|
||||
(provide 'phpinspect-bmap)
|
||||
;;; phpinspect-bmap.el ends here
|
@ -1,494 +0,0 @@
|
||||
;;; phpinspect-buffer.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-parser)
|
||||
(require 'phpinspect-bmap)
|
||||
(require 'phpinspect-edtrack)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-toc)
|
||||
(require 'phpinspect-resolvecontext)
|
||||
(require 'phpinspect-resolve)
|
||||
(require 'phpinspect-util)
|
||||
|
||||
(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'.")
|
||||
|
||||
(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 associated emacs buffer")
|
||||
(tree nil
|
||||
:documentation
|
||||
"Parsed token tree that resulted from last parse")
|
||||
(map nil
|
||||
:type phpinspect-bmap)
|
||||
(-last-indexed-bmap nil)
|
||||
(imports nil
|
||||
:type phpinspect-toc)
|
||||
(namespaces nil
|
||||
:type phpinspect-toc)
|
||||
(classes nil
|
||||
:type phpinspect-toc)
|
||||
(class-variables nil
|
||||
:type phpinspect-toc)
|
||||
(declarations nil
|
||||
:type phpinspect-toc)
|
||||
(functions nil
|
||||
:type phpinspect-toc)
|
||||
(token-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
|
||||
(-project nil
|
||||
:type phpinspect-project)
|
||||
(edit-tracker (phpinspect-make-edtrack)
|
||||
:type phpinspect-edtrack))
|
||||
|
||||
(defun phpinspect-buffer-project (buffer)
|
||||
(or (phpinspect-buffer--project buffer)
|
||||
(with-current-buffer (phpinspect-buffer-buffer buffer)
|
||||
(phpinspect--cache-get-project-create (phpinspect--get-or-create-global-cache)
|
||||
(phpinspect-current-project-root)))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-parse ((buffer phpinspect-buffer) &optional no-interrupt)
|
||||
"Parse the PHP code in the the emacs buffer that this object is
|
||||
linked with."
|
||||
(let ((tree))
|
||||
(if (or (not (phpinspect-buffer-tree buffer))
|
||||
(phpinspect-edtrack-taint-pool (phpinspect-buffer-edit-tracker buffer)))
|
||||
(with-current-buffer (phpinspect-buffer-buffer buffer)
|
||||
(let* ((map (phpinspect-make-bmap))
|
||||
(buffer-map (phpinspect-buffer-map buffer))
|
||||
(ctx (phpinspect-make-pctx
|
||||
:interrupt-predicate (unless no-interrupt #'phpinspect--input-pending-p)
|
||||
:bmap map
|
||||
:incremental t
|
||||
:previous-bmap buffer-map
|
||||
:edtrack (phpinspect-buffer-edit-tracker buffer))))
|
||||
(phpinspect-with-parse-context ctx
|
||||
(phpinspect--log "Parsing buffer")
|
||||
(let ((parsed (phpinspect-parse-current-buffer)))
|
||||
(setf (phpinspect-buffer-map buffer) map)
|
||||
(setf (phpinspect-buffer-tree buffer) parsed)
|
||||
(phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker buffer))
|
||||
|
||||
;; set return value
|
||||
(setq tree parsed)))))
|
||||
;; Else: Just return last parse result
|
||||
(setq tree (phpinspect-buffer-tree buffer)))
|
||||
tree))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-get-index-for-token ((buffer phpinspect-buffer) token)
|
||||
(gethash token (phpinspect-buffer-token-index buffer)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-set-index-reference-for-token ((buffer phpinspect-buffer) token index)
|
||||
(unless (phpinspect-probably-token-p token)
|
||||
(error "%s does not seem to be a token" token))
|
||||
(puthash token index (phpinspect-buffer-token-index buffer)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-update-index-reference-for-token ((buffer phpinspect-buffer) old new)
|
||||
(unless (and (phpinspect-probably-token-p old) (phpinspect-probably-token-p new))
|
||||
(when (and old new)
|
||||
(error "old and new parameters should be tokens")))
|
||||
|
||||
(when-let ((index (gethash old (phpinspect-buffer-token-index buffer))))
|
||||
(remhash old (phpinspect-buffer-token-index buffer))
|
||||
(puthash new index (phpinspect-buffer-token-index buffer))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-delete-index-for-token ((buffer phpinspect-buffer) token)
|
||||
(unless (phpinspect-probably-token-p token)
|
||||
(error "%s does not seem to be a token" token))
|
||||
|
||||
(cond ((phpinspect-class-p token)
|
||||
(when-let ((class (gethash token (phpinspect-buffer-token-index buffer))))
|
||||
(remhash token (phpinspect-buffer-token-index buffer))
|
||||
(phpinspect-project-delete-class (phpinspect-buffer-project buffer) class)))
|
||||
((or (phpinspect-const-p token) (phpinspect-variable-p token))
|
||||
(when-let ((var (gethash token (phpinspect-buffer-token-index buffer))))
|
||||
(remhash token (phpinspect-buffer-token-index buffer))
|
||||
(when-let ((class (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(car var))))
|
||||
(phpinspect--class-delete-variable class (cdr var)))))
|
||||
((phpinspect-function-p token)
|
||||
(when-let ((func (gethash token (phpinspect-buffer-token-index buffer))))
|
||||
(remhash token (phpinspect-buffer-token-index buffer))
|
||||
(cond ((phpinspect-project-p (car func))
|
||||
(phpinspect-project-delete-function (phpinspect-buffer-project buffer) (phpinspect--function-name-symbol (cdr func))))
|
||||
((phpinspect--type-p (car func))
|
||||
(when-let ((class (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(car func))))
|
||||
(phpinspect--class-delete-method class (cdr func))))
|
||||
(t (error "Invalid index location")))))
|
||||
(t (error "Cannot delete index for token %s" token))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-namespace-at-point ((buffer phpinspect-buffer) (point integer))
|
||||
(let ((namespace (phpinspect-splayt-find-largest-before
|
||||
(phpinspect-toc-tree (phpinspect-buffer-namespaces buffer))
|
||||
point)))
|
||||
(and namespace (phpinspect-meta-overlaps-point namespace point) namespace)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-imports ((buffer phpinspect-buffer) (imports (head phpinspect-splayt)))
|
||||
(let (to-be-indexed)
|
||||
(if (phpinspect-buffer-imports buffer)
|
||||
(pcase-let* ((`(,new) (phpinspect-toc-update
|
||||
(phpinspect-buffer-imports buffer) imports (phpinspect-buffer-root-meta buffer))))
|
||||
(setq to-be-indexed new))
|
||||
(setq to-be-indexed (phpinspect-splayt-to-list imports))
|
||||
(setf (phpinspect-buffer-imports buffer) (phpinspect-make-toc imports)))
|
||||
|
||||
(phpinspect-project-enqueue-imports
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--uses-to-types (mapcar #'phpinspect-meta-token to-be-indexed)))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-namespaces ((buffer phpinspect-buffer) (namespaces (head phpinspect-splayt)))
|
||||
(if (phpinspect-buffer-namespaces buffer)
|
||||
(phpinspect-toc-update (phpinspect-buffer-namespaces buffer) namespaces (phpinspect-buffer-root-meta buffer))
|
||||
(setf (phpinspect-buffer-namespaces buffer) (phpinspect-make-toc namespaces))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-declarations ((buffer phpinspect-buffer) (declarations (head phpinspect-splayt)))
|
||||
(if (phpinspect-buffer-declarations buffer)
|
||||
(phpinspect-toc-update (phpinspect-buffer-declarations buffer) declarations (phpinspect-buffer-root-meta buffer))
|
||||
(setf (phpinspect-buffer-declarations buffer) (phpinspect-make-toc declarations))))
|
||||
|
||||
(defun phpinspect-get-token-index-context (namespaces all-imports meta)
|
||||
(let ((namespace (phpinspect-toc-token-at-point namespaces (phpinspect-meta-start meta)))
|
||||
namespace-name imports)
|
||||
(if namespace
|
||||
(progn
|
||||
(setq namespace-name (phpinspect-namespace-name (phpinspect-meta-token namespace))
|
||||
imports (mapcar #'phpinspect-meta-token
|
||||
(phpinspect-toc-tokens-in-region
|
||||
all-imports (phpinspect-meta-start namespace) (phpinspect-meta-start meta)))))
|
||||
(setq namespace-name nil
|
||||
imports (mapcar #'phpinspect-meta-token
|
||||
(phpinspect-toc-tokens-in-region
|
||||
all-imports 0 (phpinspect-meta-start meta)))))
|
||||
|
||||
(list imports namespace-name)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-classes ((buffer phpinspect-buffer) (classes (head phpinspect-splayt)))
|
||||
(let ((declarations (phpinspect-buffer-declarations buffer))
|
||||
(namespaces (phpinspect-buffer-namespaces buffer))
|
||||
(buffer-imports (phpinspect-buffer-imports buffer))
|
||||
(project (phpinspect-buffer-project buffer)))
|
||||
(if (phpinspect-buffer-classes buffer)
|
||||
(pcase-let* ((`(,new-classes ,deleted-classes) (phpinspect-toc-update
|
||||
(phpinspect-buffer-classes buffer)
|
||||
classes (phpinspect-buffer-root-meta buffer)))
|
||||
(new-declarations) (declaration) (replaced) (indexed) (class))
|
||||
(dolist (class new-classes)
|
||||
(when (setq declaration (phpinspect-toc-token-at-or-after-point declarations (phpinspect-meta-start class)))
|
||||
(push (cons (phpinspect-meta-token declaration) class) new-declarations)))
|
||||
|
||||
(dolist (deleted deleted-classes)
|
||||
(if (and (setq class (phpinspect-buffer-get-index-for-token
|
||||
buffer (phpinspect-meta-token deleted)))
|
||||
(setq replaced (assoc (phpinspect--class-declaration class) new-declarations #'equal)))
|
||||
(pcase-let ((`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports (cdr replaced))))
|
||||
(phpinspect-buffer-update-index-reference-for-token
|
||||
buffer (phpinspect-meta-token deleted) (phpinspect-meta-token (cdr replaced)))
|
||||
(phpinspect--class-update-declaration class (car replaced) imports namespace-name)
|
||||
(push (cdr replaced) indexed))
|
||||
(phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deleted))))
|
||||
|
||||
(dolist (class new-declarations)
|
||||
(unless (memq (cdr class) indexed)
|
||||
(let (imports namespace-name class-name class-obj)
|
||||
(pcase-setq `(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports (cdr class))
|
||||
`(,class-name) (phpinspect--index-class-declaration
|
||||
(car class)
|
||||
(phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types imports)
|
||||
(phpinspect-class-block (phpinspect-meta-token (cdr class)))
|
||||
namespace-name)))
|
||||
(when class-name
|
||||
(setq class-obj (phpinspect-project-get-class-create project class-name 'no-enqueue))
|
||||
(phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token (cdr class)) class-obj)
|
||||
(phpinspect--class-update-declaration class-obj (car class) imports namespace-name))))))
|
||||
;; Else: Index all classes
|
||||
(setf (phpinspect-buffer-classes buffer) (phpinspect-make-toc classes))
|
||||
(phpinspect-splayt-traverse (class classes)
|
||||
(pcase-let* ((declaration (phpinspect-toc-token-at-or-after-point declarations (phpinspect-meta-start class)))
|
||||
(`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces buffer-imports class))
|
||||
(`(,class-name) (phpinspect--index-class-declaration
|
||||
(phpinspect-meta-token declaration)
|
||||
(phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types imports)
|
||||
(phpinspect-class-block (phpinspect-meta-token class))
|
||||
namespace-name)))
|
||||
(class-obj))
|
||||
(when class-name
|
||||
(setq class-obj (phpinspect-project-get-class-create project class-name 'no-enqueue))
|
||||
(phpinspect-buffer-set-index-reference-for-token buffer (phpinspect-meta-token class) class-obj)
|
||||
(phpinspect--class-update-declaration class-obj (phpinspect-meta-token declaration) imports namespace-name)))))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-functions ((buffer phpinspect-buffer) (functions (head phpinspect-splayt)))
|
||||
(let ((classes (phpinspect-buffer-classes buffer))
|
||||
(namespaces (phpinspect-buffer-namespaces buffer))
|
||||
(imports (phpinspect-buffer-imports buffer))
|
||||
to-be-indexed class-environments class indexed)
|
||||
(if (phpinspect-buffer-functions buffer)
|
||||
(pcase-let ((`(,new-funcs ,deleted-funcs)
|
||||
(phpinspect-toc-update (phpinspect-buffer-functions buffer) functions (phpinspect-buffer-root-meta buffer))))
|
||||
(setq to-be-indexed new-funcs)
|
||||
|
||||
(dolist (deleted deleted-funcs)
|
||||
(phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deleted))))
|
||||
(setq to-be-indexed (phpinspect-splayt-to-list functions))
|
||||
(setf (phpinspect-buffer-functions buffer) (phpinspect-make-toc functions)))
|
||||
|
||||
(dolist (func to-be-indexed)
|
||||
(if (setq class (phpinspect-toc-token-at-point classes (phpinspect-meta-start func)))
|
||||
(let (scope static indexed index-env comment-before)
|
||||
(if (phpinspect-static-p (phpinspect-meta-token (phpinspect-meta-parent func)))
|
||||
(progn
|
||||
(setq static (phpinspect-meta-parent func))
|
||||
(when (phpinspect-scope-p (phpinspect-meta-token (phpinspect-meta-parent static)))
|
||||
(setq scope `(,(car (phpinspect-meta-token (phpinspect-meta-parent static)))
|
||||
,(phpinspect-meta-token func))
|
||||
comment-before (phpinspect-meta-find-left-sibling (phpinspect-meta-parent static)))))
|
||||
(when (phpinspect-scope-p (phpinspect-meta-token (phpinspect-meta-parent func)))
|
||||
(setq scope (phpinspect-meta-token (phpinspect-meta-parent func))
|
||||
comment-before (phpinspect-meta-find-left-sibling (phpinspect-meta-parent func)))))
|
||||
|
||||
(unless scope (setq scope `(:public ,(phpinspect-meta-token func))))
|
||||
|
||||
(unless (setq index-env (alist-get class class-environments nil nil #'eq))
|
||||
(setq index-env (phpinspect-get-token-index-context namespaces imports class))
|
||||
(setcar index-env (phpinspect--uses-to-types (car index-env)))
|
||||
(push (phpinspect--make-type-resolver (car index-env) (phpinspect-meta-token class) (cadr index-env))
|
||||
index-env)
|
||||
(push (cons class index-env) class-environments))
|
||||
|
||||
(unless comment-before
|
||||
(setq comment-before (phpinspect-meta-find-left-sibling func)))
|
||||
|
||||
(setq comment-before (phpinspect-meta-token comment-before))
|
||||
|
||||
(pcase-let ((`(,type-resolver) index-env)
|
||||
(class-obj (phpinspect-buffer-get-index-for-token buffer (phpinspect-meta-token class))))
|
||||
(unless class-obj (error "Unable to find class obj for class %s" (phpinspect-meta-token class)))
|
||||
|
||||
(setq indexed (phpinspect--index-function-from-scope
|
||||
type-resolver
|
||||
scope
|
||||
(and (phpinspect-comment-p comment-before) comment-before)))
|
||||
(if static
|
||||
(phpinspect--class-set-static-method class-obj indexed)
|
||||
(phpinspect--class-set-method class-obj indexed))
|
||||
|
||||
(phpinspect-buffer-set-index-reference-for-token
|
||||
buffer (phpinspect-meta-token func)
|
||||
(cons (phpinspect--class-name class-obj) indexed))))
|
||||
;; Else: index function
|
||||
(pcase-let ((`(,imports ,namespace-name) (phpinspect-get-token-index-context namespaces imports func))
|
||||
(comment-before (phpinspect-meta-find-left-sibling func)))
|
||||
(setq indexed (phpinspect--index-function-from-scope
|
||||
(phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types imports) nil namespace-name)
|
||||
`(:public ,(phpinspect-meta-token func))
|
||||
(and (phpinspect-comment-p comment-before) comment-before)
|
||||
nil namespace-name))
|
||||
(phpinspect-project-set-function (phpinspect-buffer-project buffer) indexed)
|
||||
(phpinspect-buffer-set-index-reference-for-token
|
||||
buffer (phpinspect-meta-token func)
|
||||
(cons (phpinspect-buffer-project buffer) indexed)))))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect-buffer-index-class-variables ((buffer phpinspect-buffer) (class-variables (head phpinspect-splayt)))
|
||||
(let ((classes (phpinspect-buffer-classes buffer))
|
||||
(namespaces (phpinspect-buffer-namespaces buffer))
|
||||
(imports (phpinspect-buffer-imports buffer))
|
||||
to-be-indexed class-environments class class-obj)
|
||||
(if (phpinspect-buffer-class-variables buffer)
|
||||
(pcase-let ((`(,new-vars ,deleted-vars)
|
||||
(phpinspect-toc-update
|
||||
(phpinspect-buffer-class-variables buffer) class-variables (phpinspect-buffer-root-meta buffer))))
|
||||
(setq to-be-indexed new-vars)
|
||||
|
||||
(dolist (deleted deleted-vars)
|
||||
(phpinspect-buffer-delete-index-for-token buffer (phpinspect-meta-token deleted))))
|
||||
|
||||
(setq to-be-indexed (phpinspect-splayt-to-list class-variables))
|
||||
(setf (phpinspect-buffer-class-variables buffer) (phpinspect-make-toc class-variables)))
|
||||
|
||||
(dolist (var to-be-indexed)
|
||||
(when (and class (> (phpinspect-meta-start var) (phpinspect-meta-end class)))
|
||||
(setq class nil))
|
||||
|
||||
(unless class
|
||||
(setq class (phpinspect-toc-token-at-point classes (phpinspect-meta-start var))))
|
||||
|
||||
(setq class-obj (phpinspect-buffer-get-index-for-token buffer (phpinspect-meta-token class)))
|
||||
|
||||
(let (scope static indexed index-env comment-before)
|
||||
(if (phpinspect-static-p (phpinspect-meta-token (phpinspect-meta-parent var)))
|
||||
(progn
|
||||
(setq static (phpinspect-meta-parent var))
|
||||
(when (phpinspect-scope-p (phpinspect-meta-token (phpinspect-meta-parent static)))
|
||||
(setq scope `(,(car (phpinspect-meta-token (phpinspect-meta-parent static)))
|
||||
,(phpinspect-meta-token var))
|
||||
comment-before (phpinspect-meta-find-left-sibling (phpinspect-meta-parent static)))))
|
||||
(when (phpinspect-scope-p (phpinspect-meta-token (phpinspect-meta-parent var)))
|
||||
(setq scope (phpinspect-meta-token (phpinspect-meta-parent var))
|
||||
comment-before (phpinspect-meta-find-left-sibling (phpinspect-meta-parent var)))))
|
||||
|
||||
(unless scope (setq scope `(:public ,(phpinspect-meta-token var))))
|
||||
|
||||
(unless (setq index-env (alist-get class class-environments nil nil #'eq))
|
||||
(setq index-env (phpinspect-get-token-index-context namespaces imports class))
|
||||
(push (cons class index-env) class-environments))
|
||||
|
||||
(unless comment-before
|
||||
(setq comment-before (phpinspect-meta-find-left-sibling var)))
|
||||
|
||||
(setq comment-before (phpinspect-meta-token comment-before))
|
||||
|
||||
(pcase-let* ((`(,imports ,namespace-name) index-env)
|
||||
(type-resolver
|
||||
(phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types imports)
|
||||
(phpinspect-meta-token class)
|
||||
namespace-name)))
|
||||
|
||||
(setq indexed
|
||||
(if (phpinspect-const-p (phpinspect-meta-token var))
|
||||
(phpinspect--index-const-from-scope scope)
|
||||
(phpinspect--index-variable-from-scope
|
||||
type-resolver
|
||||
scope
|
||||
(and (phpinspect-comment-p comment-before) comment-before)
|
||||
static)))
|
||||
|
||||
(when (and (phpinspect-variable-p (phpinspect-meta-token var)) (not (phpinspect--variable-type indexed)))
|
||||
(when-let* ((constructor (phpinspect--class-get-method class-obj (phpinspect-intern-name "__construct")))
|
||||
(rctx (phpinspect--make-resolvecontext :enclosing-tokens (list (phpinspect-meta-token class))
|
||||
:enclosing-metadata (list class))))
|
||||
(setf (phpinspect--variable-type indexed)
|
||||
(phpinspect-get-pattern-type-in-block
|
||||
rctx (phpinspect--make-pattern :m `(:variable "this")
|
||||
:m `(:object-attrib (:word ,(cadr (phpinspect-meta-token var)))))
|
||||
(phpinspect-function-block (phpinspect--function-token constructor))
|
||||
type-resolver
|
||||
(phpinspect-function-argument-list (phpinspect--function-token constructor))))))
|
||||
|
||||
|
||||
(phpinspect--class-set-variable class-obj indexed)
|
||||
|
||||
(phpinspect-buffer-set-index-reference-for-token
|
||||
buffer (phpinspect-meta-token var)
|
||||
(cons (phpinspect--class-name class-obj) indexed)))))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-reset ((buffer phpinspect-buffer))
|
||||
(setf (phpinspect-buffer-tree buffer) nil)
|
||||
(setf (phpinspect-buffer-map buffer) (phpinspect-make-bmap))
|
||||
(setf (phpinspect-buffer-declarations buffer) nil)
|
||||
(setf (phpinspect-buffer-imports buffer) nil)
|
||||
(setf (phpinspect-buffer-token-index buffer)
|
||||
(make-hash-table :test 'eq :size 100 :rehash-size 1.5))
|
||||
(phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker buffer)))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-reparse ((buffer phpinspect-buffer))
|
||||
(phpinspect-buffer-reset buffer)
|
||||
(phpinspect-buffer-parse buffer 'no-interrupt))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-update-project-index ((buffer phpinspect-buffer))
|
||||
(when (phpinspect-buffer-project buffer)
|
||||
(let ((map (phpinspect-buffer-map buffer)))
|
||||
(unless (eq map (phpinspect-buffer--last-indexed-bmap buffer))
|
||||
(phpinspect-buffer-index-imports buffer (phpinspect-bmap-imports map))
|
||||
(phpinspect-buffer-index-declarations buffer (phpinspect-bmap-declarations map))
|
||||
(phpinspect-buffer-index-namespaces buffer (phpinspect-bmap-namespaces map))
|
||||
(phpinspect-buffer-index-classes buffer (phpinspect-bmap-classes map))
|
||||
(phpinspect-buffer-index-functions buffer (phpinspect-bmap-functions map))
|
||||
(phpinspect-buffer-index-class-variables buffer (phpinspect-bmap-class-variables map))
|
||||
(setf (phpinspect-buffer--last-indexed-bmap buffer) map)))))
|
||||
|
||||
(defsubst phpinspect-buffer-parse-map (buffer)
|
||||
(phpinspect-buffer-parse buffer)
|
||||
(phpinspect-buffer-map buffer))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-register-edit
|
||||
((buffer phpinspect-buffer) (start integer) (end integer) (pre-change-length integer))
|
||||
"Mark a region of the buffer as edited."
|
||||
|
||||
;; Take into account "atoms" (tokens without clear delimiters like words,
|
||||
;; variables and object attributes. The meaning of these tokens will change as
|
||||
;; they grow or shrink, so their full regions need to be marked for a reparse).
|
||||
(save-excursion
|
||||
(goto-char start)
|
||||
(when (looking-back "\\(\\$\\|->\\|::\\)?[^][)(}{[:blank:]\n;'\"]+" nil t)
|
||||
(setq start (- start (length (match-string 0))))
|
||||
(setq pre-change-length (+ pre-change-length (length (match-string 0))))))
|
||||
|
||||
(phpinspect-edtrack-register-edit
|
||||
(phpinspect-buffer-edit-tracker buffer) start end pre-change-length))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-tokens-enclosing-point ((buffer phpinspect-buffer) point)
|
||||
(phpinspect-bmap-tokens-overlapping (phpinspect-buffer-map buffer) point))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-token-meta ((buffer phpinspect-buffer) token)
|
||||
(phpinspect-bmap-token-meta (phpinspect-buffer-map buffer) token))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-location-resolver ((buffer phpinspect-buffer))
|
||||
"Derive location resolver from BUFFER's buffer map. Guarantees to
|
||||
retrieve the lastest available map of BUFFER upon first
|
||||
invocation, but subsequent invocations will not update the used
|
||||
map afterwards, so don't keep the resolver around for long term
|
||||
use."
|
||||
(let ((bmap-resolver))
|
||||
(lambda (token)
|
||||
(funcall (with-memoization bmap-resolver
|
||||
(phpinspect-bmap-make-location-resolver (phpinspect-buffer-map buffer)))
|
||||
token))))
|
||||
|
||||
(cl-defmethod phpinspect-buffer-root-meta ((buffer phpinspect-buffer))
|
||||
(phpinspect-bmap-root-meta (phpinspect-buffer-map buffer)))
|
||||
|
||||
(defun phpinspect-display-buffer-tree ()
|
||||
(interactive)
|
||||
(when phpinspect-current-buffer
|
||||
(let ((buffer phpinspect-current-buffer))
|
||||
(pop-to-buffer (generate-new-buffer "phpinspect-buffer-tree"))
|
||||
(insert (pp-to-string (phpinspect-buffer-parse buffer 'no-interrupt)))
|
||||
(read-only-mode))))
|
||||
|
||||
(defun phpinspect-display-buffer-index ()
|
||||
(interactive)
|
||||
(when phpinspect-current-buffer
|
||||
(let ((buffer phpinspect-current-buffer))
|
||||
(pop-to-buffer (generate-new-buffer "phpinspect-buffer-tree"))
|
||||
(insert (pp-to-string (phpinspect--index-tokens (phpinspect-buffer-parse buffer 'no-interrupt))))
|
||||
(read-only-mode))))
|
||||
|
||||
(defun phpinspect-after-change-function (start end pre-change-length)
|
||||
(when phpinspect-current-buffer
|
||||
(phpinspect-buffer-register-edit phpinspect-current-buffer start end pre-change-length)))
|
||||
|
||||
(provide 'phpinspect-buffer)
|
@ -1,290 +0,0 @@
|
||||
;;; phpinspect.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-autoload)
|
||||
(require 'phpinspect-worker)
|
||||
|
||||
(defcustom phpinspect-load-stubs t
|
||||
"If and when phpinspect should load code stubs."
|
||||
:type '(choice
|
||||
(const
|
||||
:tag
|
||||
"Load stubs on first mode init." t)
|
||||
(const
|
||||
:tag
|
||||
"Never load stubs." nil))
|
||||
:group 'phpinspect)
|
||||
|
||||
(defvar phpinspect-buffers (make-hash-table :test #'eq)
|
||||
"All buffers for which `phpinspect-mode' is currently active.
|
||||
|
||||
Hash table with buffer (native emacs buffer object, `bufferp') as
|
||||
key, and a reset-function as value. The reset-function is called
|
||||
without arguments when the cache is purged (see
|
||||
`phpinspect-purge-cache'.")
|
||||
|
||||
(defun phpinspect-register-current-buffer (reset-func)
|
||||
(puthash (current-buffer) reset-func phpinspect-buffers))
|
||||
|
||||
(defun phpinspect-unregister-current-buffer ()
|
||||
(remhash (current-buffer) phpinspect-buffers))
|
||||
|
||||
(defvar phpinspect-stub-cache nil
|
||||
"An instance of `phpinspect--cache' containing an index of PHP
|
||||
functions and classes which phpinspect preloads. This index is
|
||||
not supposed to be mutated after initial creation.")
|
||||
|
||||
(defmacro phpinspect--cache-edit (cache &rest body)
|
||||
(declare (indent 1))
|
||||
`(unless (phpinspect--cache-read-only-p ,cache)
|
||||
,@body))
|
||||
|
||||
(defvar phpinspect-cache nil
|
||||
"An object used to store and access metadata of PHP projects.")
|
||||
|
||||
(cl-defstruct (phpinspect--cache (:constructor phpinspect--make-cache))
|
||||
(read-only-p nil
|
||||
:type boolean
|
||||
:documentation
|
||||
"Whether this cache instance is read-only, meaning that it's data
|
||||
should never be changed.
|
||||
|
||||
When the value of this slot is non-nil:
|
||||
|
||||
- Actions that would normally mutate it's data should become
|
||||
no-ops.
|
||||
- All projects that are retrieved from it should be marked as read-only as well.")
|
||||
(extra-class-retriever nil
|
||||
:type lambda
|
||||
:documentation
|
||||
"A function that should accept a `phpinspect--type' and return
|
||||
matching `phpinspect--class' instances or nil. Used to discover
|
||||
classes that are defined outside of code that this cache knows about.")
|
||||
(extra-function-retriever nil
|
||||
:type lambda
|
||||
:documentation
|
||||
"A function that should accept a `phpinspect-name' (see
|
||||
`phpinspect-intern-name') and return matching
|
||||
`phpinspect--function' instances or nil. Used to discover
|
||||
functions that are defined outside of code that this cache knows
|
||||
about.")
|
||||
(projects (make-hash-table :test 'equal :size 10)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"A `hash-table` with the root directories of projects
|
||||
as keys and project caches as values."))
|
||||
|
||||
(defun phpinspect--get-stub-class (fqn)
|
||||
(when phpinspect-stub-cache
|
||||
(phpinspect--log "Getting stub class")
|
||||
(catch 'return
|
||||
(maphash (lambda (_name project)
|
||||
(when-let ((class (phpinspect-project-get-class project fqn)))
|
||||
(throw 'return class)))
|
||||
(phpinspect--cache-projects phpinspect-stub-cache)))))
|
||||
|
||||
(defun phpinspect--get-stub-function (name)
|
||||
(when phpinspect-stub-cache
|
||||
(if name
|
||||
(catch 'return
|
||||
(phpinspect--log "Getting stub function by name %s" name)
|
||||
(maphash (lambda (_name project)
|
||||
(when-let ((class (phpinspect-project-get-function project name)))
|
||||
(throw 'return class)))
|
||||
(phpinspect--cache-projects phpinspect-stub-cache)))
|
||||
(let* ((funcs (cons nil nil))
|
||||
(funcs-rear funcs))
|
||||
(phpinspect--log "Retrieving all stub functions for nil name")
|
||||
(maphash (lambda (_name project)
|
||||
(setq funcs-rear (last (nconc funcs-rear (phpinspect-project-get-functions project)))))
|
||||
(phpinspect--cache-projects phpinspect-stub-cache))
|
||||
(cdr funcs)))))
|
||||
|
||||
(defun phpinspect--get-or-create-global-cache ()
|
||||
"Get `phpinspect-cache'.
|
||||
If its value is nil, it is created and then returned."
|
||||
(or phpinspect-cache
|
||||
(setq phpinspect-cache
|
||||
(phpinspect--make-cache
|
||||
:extra-class-retriever #'phpinspect--get-stub-class
|
||||
:extra-function-retriever #'phpinspect--get-stub-function))))
|
||||
|
||||
(defun phpinspect-purge-cache ()
|
||||
"Assign a fresh, empty cache object to `phpinspect-cache'.
|
||||
This effectively purges any cached code information from all
|
||||
currently opened projects."
|
||||
(interactive)
|
||||
(when phpinspect-cache
|
||||
;; Allow currently known cached projects to cleanup after themselves
|
||||
(maphash (lambda (_ project)
|
||||
(phpinspect-project-purge project))
|
||||
(phpinspect--cache-projects phpinspect-cache)))
|
||||
|
||||
|
||||
(maphash (lambda (buffer reset-hook)
|
||||
(with-current-buffer buffer
|
||||
(funcall reset-hook)))
|
||||
phpinspect-buffers)
|
||||
|
||||
;; Assign a fresh cache object
|
||||
(setq phpinspect-cache (phpinspect--get-or-create-global-cache))
|
||||
(setq phpinspect-names (phpinspect-make-name-hash))
|
||||
(phpinspect-define-standard-types))
|
||||
|
||||
(cl-defmethod phpinspect--cache-get-project
|
||||
((cache phpinspect--cache) (project-root string))
|
||||
(let ((project (gethash project-root (phpinspect--cache-projects cache))))
|
||||
(when (and project (phpinspect--cache-read-only-p cache)
|
||||
(not (phpinspect-project-read-only-p project)))
|
||||
(setf (phpinspect-project-read-only-p project) t))
|
||||
|
||||
project))
|
||||
|
||||
(defun phpinspect-get-or-create-cached-project-class (project-root class-fqn)
|
||||
(when project-root
|
||||
(let ((project (phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
project-root)))
|
||||
(phpinspect-project-get-class-extra-or-create project class-fqn))))
|
||||
|
||||
(cl-defmethod phpinspect--cache-get-project-create
|
||||
((cache phpinspect--cache) (project-root string))
|
||||
"Get a project that is located in PROJECT-ROOT from CACHE.
|
||||
If no such project exists in the cache yet, it is created and
|
||||
then returned."
|
||||
(let ((project (phpinspect--cache-get-project cache project-root)))
|
||||
(unless project
|
||||
(phpinspect--cache-edit cache
|
||||
(setq project
|
||||
(puthash project-root
|
||||
(phpinspect--make-project
|
||||
:fs (phpinspect-make-fs)
|
||||
:root project-root
|
||||
:extra-class-retriever (phpinspect--cache-extra-class-retriever cache)
|
||||
:extra-function-retriever (phpinspect--cache-extra-function-retriever cache)
|
||||
:worker (phpinspect-make-dynamic-worker))
|
||||
(phpinspect--cache-projects cache)))
|
||||
(let ((autoloader (phpinspect-make-autoloader
|
||||
:fs (phpinspect-project-fs project)
|
||||
:file-indexer (phpinspect-project-make-file-indexer project)
|
||||
:project-root-resolver (phpinspect-project-make-root-resolver project))))
|
||||
(setf (phpinspect-project-autoload project) autoloader)
|
||||
(phpinspect-autoloader-refresh autoloader)
|
||||
(phpinspect-project-enqueue-include-dirs project))))
|
||||
project))
|
||||
|
||||
(defun phpinspect-project-enqueue-include-dirs (project)
|
||||
(interactive (list (phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
(phpinspect-current-project-root))))
|
||||
(phpinspect-project-edit project
|
||||
(let ((dirs (alist-get 'include-dirs
|
||||
(alist-get (phpinspect-project-root project)
|
||||
phpinspect-projects
|
||||
nil nil #'string=))))
|
||||
(dolist (dir dirs)
|
||||
(phpinspect-message "enqueueing dir %s" dir)
|
||||
(phpinspect-worker-enqueue
|
||||
(phpinspect-project-worker project)
|
||||
(phpinspect-make-index-dir-task :dir dir :project project))))))
|
||||
|
||||
(defun phpinspect-project-add-include-dir (dir)
|
||||
"Configure DIR as an include dir for the current project."
|
||||
(interactive (list (read-directory-name "Include Directory: ")))
|
||||
(custom-set-variables '(phpinspect-projects))
|
||||
(let ((existing
|
||||
(alist-get (phpinspect-current-project-root) phpinspect-projects nil #'string=)))
|
||||
(if existing
|
||||
(push dir (alist-get 'include-dirs existing))
|
||||
(push `(,(phpinspect-current-project-root) . ((include-dirs . (,dir)))) phpinspect-projects)))
|
||||
|
||||
(customize-save-variable 'phpinspect-projects phpinspect-projects)
|
||||
|
||||
(phpinspect-project-enqueue-include-dirs (phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
(phpinspect-current-project-root))))
|
||||
|
||||
(defconst phpinspect-stub-directory
|
||||
(expand-file-name "stubs" (file-name-directory (macroexp-file-name)))
|
||||
"Directory where PHP stub files are located.")
|
||||
|
||||
(defconst phpinspect-data-directory
|
||||
(expand-file-name "data" (file-name-directory (macroexp-file-name)))
|
||||
"Directory for data distributed with phpinspect.")
|
||||
|
||||
(defconst phpinspect-stub-cache-file
|
||||
(expand-file-name "builtin-stubs.eld" phpinspect-data-directory)
|
||||
"")
|
||||
|
||||
(defconst phpinspect-builtin-index-file
|
||||
(expand-file-name (concat "builtin-stubs-index.eld" (if (zlib-available-p) ".gz" ""))
|
||||
phpinspect-data-directory)
|
||||
"")
|
||||
|
||||
(defun phpinspect-build-stub-cache ()
|
||||
(let* ((cache (phpinspect--make-cache))
|
||||
(builtin-project (phpinspect--cache-get-project-create cache "builtins"))
|
||||
(phpinspect-worker 'nil-worker))
|
||||
(phpinspect-project-add-index builtin-project (phpinspect-build-stub-index))))
|
||||
|
||||
(defun phpinspect-build-stub-index ()
|
||||
(phpinspect--index-tokens (phpinspect-parse-file (expand-file-name "builtins.php" phpinspect-stub-directory))))
|
||||
|
||||
(defun phpinspect-dump-stub-index ()
|
||||
(interactive)
|
||||
(let* ((phpinspect-names (phpinspect-make-name-hash))
|
||||
(index (phpinspect-build-stub-index)))
|
||||
(with-temp-buffer
|
||||
(let ((print-length nil)
|
||||
(print-level nil)
|
||||
(print-circle t))
|
||||
|
||||
(prin1 (list (cons 'names phpinspect-names)
|
||||
(cons 'index index))
|
||||
(current-buffer))
|
||||
(write-file phpinspect-builtin-index-file)))))
|
||||
|
||||
(defun phpinspect-load-stub-index ()
|
||||
(interactive)
|
||||
(unless (file-exists-p phpinspect-builtin-index-file)
|
||||
(phpinspect-message "No stub index dump found, dumping stub index ...")
|
||||
(phpinspect-dump-stub-index))
|
||||
|
||||
(let* ((data (with-temp-buffer
|
||||
(insert-file-contents phpinspect-builtin-index-file)
|
||||
(goto-char (point-min))
|
||||
(read (current-buffer))))
|
||||
(project (phpinspect--make-project :worker 'nil-worker)))
|
||||
(phpinspect-purge-cache)
|
||||
(setq phpinspect-names (alist-get 'names data))
|
||||
(phpinspect-define-standard-types)
|
||||
(setq phpinspect-stub-cache (phpinspect--make-cache))
|
||||
(phpinspect-project-add-index project (alist-get 'index data))
|
||||
(puthash "builtins" project (phpinspect--cache-projects phpinspect-stub-cache))
|
||||
(setf (phpinspect--cache-read-only-p phpinspect-stub-cache) t)))
|
||||
|
||||
;;; phpinspect.el ends here
|
||||
(provide 'phpinspect-cache)
|
@ -1,70 +0,0 @@
|
||||
;;; phpinspect-changeset.el --- Metadata changeset module -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
(eval-when-compile
|
||||
(require 'phpinspect-meta))
|
||||
|
||||
(define-inline phpinspect-make-changeset (meta)
|
||||
(inline-letevals (meta)
|
||||
(inline-quote
|
||||
(list (phpinspect-meta-start ,meta) (phpinspect-meta-end ,meta)
|
||||
(phpinspect-meta-parent ,meta) (phpinspect-meta-overlay ,meta)
|
||||
(phpinspect-meta-parent-offset ,meta) ,meta))))
|
||||
|
||||
(define-inline phpinspect-changeset-start (set)
|
||||
(inline-quote (car ,set)))
|
||||
|
||||
(define-inline phpinspect-changeset-end (set)
|
||||
(inline-quote (cadr ,set)))
|
||||
|
||||
(define-inline phpinspect-changeset-parent (set)
|
||||
(inline-quote (caddr ,set)))
|
||||
|
||||
(define-inline phpinspect-changeset-overlay (set)
|
||||
(inline-quote (cadddr ,set)))
|
||||
|
||||
(define-inline phpinspect-changeset-parent-offset (set)
|
||||
(inline-quote (car (cddddr ,set))))
|
||||
|
||||
(define-inline phpinspect-changeset-meta (set)
|
||||
(inline-quote (car (nthcdr 5 ,set))))
|
||||
|
||||
(define-inline phpinspect-changeset-revert (changeset)
|
||||
(inline-letevals (changeset)
|
||||
(inline-quote
|
||||
(progn
|
||||
(setf (phpinspect-meta-parent (phpinspect-changeset-meta ,changeset))
|
||||
(phpinspect-changeset-parent ,changeset))
|
||||
(setf (phpinspect-meta-overlay (phpinspect-changeset-meta ,changeset))
|
||||
(phpinspect-changeset-overlay ,changeset))
|
||||
(setf (phpinspect-meta-absolute-start (phpinspect-changeset-meta ,changeset))
|
||||
(phpinspect-changeset-start ,changeset))
|
||||
(setf (phpinspect-meta-absolute-end (phpinspect-changeset-meta ,changeset))
|
||||
(phpinspect-changeset-end ,changeset))
|
||||
(setf (phpinspect-meta-parent-offset (phpinspect-changeset-meta ,changeset))
|
||||
(phpinspect-changeset-parent-offset ,changeset))))))
|
||||
|
||||
(provide 'phpinspect-changeset)
|
||||
;;; phpinspect-changeset.el ends here
|
@ -1,78 +0,0 @@
|
||||
;;; phpinspect-class-struct.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
|
||||
(cl-defstruct (phpinspect--class (:constructor phpinspect--make-class-generated))
|
||||
(class-retriever nil
|
||||
:type lambda
|
||||
:documentaton
|
||||
"A function that returns classes for types
|
||||
(should accept `phpinspect--type' as argument)")
|
||||
|
||||
(read-only-p nil
|
||||
:type boolean
|
||||
:documentation
|
||||
"Whether this class instance is read-only, meaning that its data
|
||||
should never be changed. Methods and functions that are meant to
|
||||
manipulate class data should become no-ops when this slot has a
|
||||
non-nil value.")
|
||||
(index nil
|
||||
:type phpinspect--indexed-class
|
||||
:documentation
|
||||
"The index that this class is derived from")
|
||||
(methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"All methods, including those from extended classes.")
|
||||
(static-methods (make-hash-table :test 'eq :size 20 :rehash-size 20)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"All static methods this class provides,
|
||||
including those from extended classes.")
|
||||
(name nil
|
||||
:type phpinspect--type)
|
||||
(variables nil
|
||||
:type list
|
||||
:documentation
|
||||
"Variables that belong to this class.")
|
||||
(extended-classes nil
|
||||
:type list
|
||||
:documentation
|
||||
"All extended/implemented classes.")
|
||||
(subscriptions (make-hash-table :test #'eq :size 10 :rehash-size 1.5)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"A list of subscription functions that should be
|
||||
called whenever anything about this class is
|
||||
updated")
|
||||
(declaration nil)
|
||||
(initial-index nil
|
||||
:type bool
|
||||
:documentation
|
||||
"A boolean indicating whether or not this class
|
||||
has been indexed yet."))
|
||||
|
||||
|
||||
(provide 'phpinspect-class-struct)
|
@ -1,263 +0,0 @@
|
||||
;;; phpinspect-class.el --- PHP parsing module -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-class-struct)
|
||||
|
||||
(defmacro phpinspect--class-edit (class &rest body)
|
||||
"Declare intent to edit CLASS in BODY.
|
||||
|
||||
Conditionally executes BODY depending on
|
||||
`phpinspect--class-read-only-p' value."
|
||||
(declare (indent 1))
|
||||
`(unless (phpinspect--class-read-only-p ,class)
|
||||
,@body))
|
||||
|
||||
(cl-defmethod phpinspect--class-trigger-update ((class phpinspect--class))
|
||||
(dolist (sub (hash-table-values (phpinspect--class-subscriptions class)))
|
||||
(funcall sub class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-update-extensions ((class phpinspect--class) extensions)
|
||||
(phpinspect--class-edit class
|
||||
(setf (phpinspect--class-extended-classes class)
|
||||
(seq-filter
|
||||
#'phpinspect--class-p
|
||||
(mapcar
|
||||
(lambda (class-name)
|
||||
(funcall (phpinspect--class-class-retriever class) class-name))
|
||||
extensions)))
|
||||
|
||||
(dolist (extended (phpinspect--class-extended-classes class))
|
||||
(phpinspect--class-incorporate class extended))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect--class-set-index ((class phpinspect--class)
|
||||
(index (head phpinspect--indexed-class)))
|
||||
(phpinspect--class-edit class
|
||||
(setf (phpinspect--class-declaration class) (alist-get 'declaration index))
|
||||
(setf (phpinspect--class-name class) (alist-get 'class-name index))
|
||||
|
||||
;; Override methods when class seems syntactically correct (has balanced braces)
|
||||
(when (alist-get 'complete index)
|
||||
(let ((methods (phpinspect--class-methods class))
|
||||
(static-methods (phpinspect--class-static-methods class)))
|
||||
|
||||
(dolist (method (hash-table-values methods))
|
||||
(unless (phpinspect--function--inherited method)
|
||||
(remhash (phpinspect--function-name-symbol method) methods)))
|
||||
(dolist (method (hash-table-values static-methods))
|
||||
(unless (phpinspect--function--inherited method)
|
||||
(remhash (phpinspect--function-name-symbol method) static-methods)))))
|
||||
|
||||
(setf (phpinspect--class-initial-index class) t)
|
||||
(setf (phpinspect--class-index class) index)
|
||||
|
||||
(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)
|
||||
(append (alist-get 'variables index)
|
||||
(alist-get 'constants index)
|
||||
(alist-get 'static-variables index)))
|
||||
|
||||
(phpinspect--class-update-extensions
|
||||
class `(,@(alist-get 'implements index) ,@(alist-get 'extends index)))
|
||||
|
||||
(phpinspect--class-trigger-update class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-update-declaration
|
||||
((class phpinspect--class) declaration imports namespace-name)
|
||||
(phpinspect--class-edit class
|
||||
(pcase-let ((`(,class-name ,extends ,implements ,_used-types)
|
||||
(phpinspect--index-class-declaration
|
||||
declaration (phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types imports) nil namespace-name))))
|
||||
(setf (phpinspect--class-name class) class-name)
|
||||
(setf (phpinspect--class-declaration class) declaration)
|
||||
(phpinspect--class-update-extensions class `(,@extends ,@implements)))))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-method ((class phpinspect--class) (method-name (head phpinspect-name)))
|
||||
(gethash method-name (phpinspect--class-methods class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-static-method ((class phpinspect--class) (method-name (head phpinspect-name)))
|
||||
(gethash method-name (phpinspect--class-static-methods class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-variable
|
||||
((class phpinspect--class) (variable-name string))
|
||||
(catch 'found
|
||||
(dolist (variable (phpinspect--class-variables class))
|
||||
(when (string= variable-name (phpinspect--variable-name variable))
|
||||
(throw 'found variable)))))
|
||||
|
||||
(cl-defmethod phpinspect--class-set-variable ((class phpinspect--class)
|
||||
(var phpinspect--variable))
|
||||
(phpinspect--class-edit class
|
||||
(push var (phpinspect--class-variables class))))
|
||||
|
||||
(cl-defmethod phpinspect--class-delete-variable ((class phpinspect--class)
|
||||
(var phpinspect--variable))
|
||||
(phpinspect--class-edit class
|
||||
(setf (phpinspect--class-variables class)
|
||||
(seq-filter (lambda (clvar) (not (eq var clvar)))
|
||||
(phpinspect--class-variables class)))))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-variables ((class phpinspect--class))
|
||||
(seq-filter #'phpinspect--variable-vanilla-p (phpinspect--class-variables class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-static-variables ((class phpinspect--class))
|
||||
(seq-filter #'phpinspect--variable-static-p (phpinspect--class-variables class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-constants ((class phpinspect--class))
|
||||
(seq-filter #'phpinspect--variable-const-p (phpinspect--class-variables class)))
|
||||
|
||||
(cl-defmethod phpinspect--add-method-copy-to-map
|
||||
((map hash-table)
|
||||
(class-name phpinspect--type)
|
||||
(method phpinspect--function))
|
||||
(setq method (phpinspect--copy-function method))
|
||||
|
||||
(setf (phpinspect--function-return-type method)
|
||||
(phpinspect--resolve-late-static-binding
|
||||
(phpinspect--function-return-type method)
|
||||
class-name))
|
||||
|
||||
(puthash (phpinspect--function-name-symbol method)
|
||||
method
|
||||
map))
|
||||
|
||||
(cl-defmethod phpinspect--class-set-method ((class phpinspect--class)
|
||||
(method phpinspect--function))
|
||||
(phpinspect--class-edit class
|
||||
(phpinspect--log "Adding method by name %s to class"
|
||||
(phpinspect--function-name method))
|
||||
(phpinspect--add-method-copy-to-map
|
||||
(phpinspect--class-methods class)
|
||||
(phpinspect--class-name class)
|
||||
method)))
|
||||
|
||||
(cl-defmethod phpinspect--class-set-static-method ((class phpinspect--class)
|
||||
(method phpinspect--function))
|
||||
(phpinspect--class-edit class
|
||||
(phpinspect--add-method-copy-to-map
|
||||
(phpinspect--class-static-methods class)
|
||||
(phpinspect--class-name class)
|
||||
method)))
|
||||
|
||||
(cl-defmethod phpinspect--class-delete-method ((class phpinspect--class) (method phpinspect--function))
|
||||
(phpinspect--class-edit class
|
||||
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-static-methods class))
|
||||
(remhash (phpinspect--function-name-symbol method) (phpinspect--class-methods class))))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-method-return-type
|
||||
((class phpinspect--class) (method-name (head phpinspect-name)))
|
||||
(let ((method (phpinspect--class-get-method class method-name)))
|
||||
(when method
|
||||
(phpinspect--function-return-type method))))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-static-method-return-type
|
||||
((class phpinspect--class) (method-name (head phpinspect-name)))
|
||||
(let ((method (phpinspect--class-get-static-method class method-name)))
|
||||
(when method
|
||||
(phpinspect--function-return-type method))))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-method-list ((class phpinspect--class))
|
||||
(hash-table-values (phpinspect--class-methods class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-get-static-method-list ((class phpinspect--class))
|
||||
(hash-table-values (phpinspect--class-static-methods class)))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect--merge-method ((class-name phpinspect--type)
|
||||
(existing phpinspect--function)
|
||||
(method phpinspect--function)
|
||||
&optional extended)
|
||||
(let ((new-return-type (phpinspect--resolve-late-static-binding
|
||||
(phpinspect--function-return-type method)
|
||||
class-name)))
|
||||
(unless (phpinspect--type= new-return-type phpinspect--null-type)
|
||||
(phpinspect--log "method return type %s" (phpinspect--function-return-type method))
|
||||
(setf (phpinspect--function-return-type existing)
|
||||
new-return-type))
|
||||
|
||||
(setf (phpinspect--function--inherited existing)
|
||||
extended)
|
||||
|
||||
(setf (phpinspect--function-arguments existing)
|
||||
(phpinspect--function-arguments method)))
|
||||
existing)
|
||||
|
||||
(cl-defmethod phpinspect--class-update-static-method ((class phpinspect--class)
|
||||
(method phpinspect--function)
|
||||
&optional extended)
|
||||
(phpinspect--class-edit class
|
||||
(let ((existing (gethash (phpinspect--function-name-symbol method)
|
||||
(phpinspect--class-static-methods class))))
|
||||
(if existing
|
||||
(phpinspect--merge-method (phpinspect--class-name class) existing method extended)
|
||||
(setf (phpinspect--function--inherited method) extended)
|
||||
(phpinspect--class-set-static-method class method)))))
|
||||
|
||||
(cl-defmethod phpinspect--class-update-method ((class phpinspect--class)
|
||||
(method phpinspect--function)
|
||||
&optional extended)
|
||||
(phpinspect--class-edit class
|
||||
(let* ((existing (gethash (phpinspect--function-name-symbol method)
|
||||
(phpinspect--class-methods class))))
|
||||
|
||||
(if existing
|
||||
(phpinspect--merge-method (phpinspect--class-name class) existing method extended)
|
||||
(setf (phpinspect--function--inherited method) extended)
|
||||
(phpinspect--class-set-method class method)))))
|
||||
|
||||
;; FIXME: Remove inherited methods when they no longer exist in parent classes
|
||||
;; (and/or the current class in the case of abstract methods).
|
||||
(cl-defmethod phpinspect--class-incorporate ((class phpinspect--class)
|
||||
(other-class phpinspect--class))
|
||||
(phpinspect--class-edit class
|
||||
(dolist (method (phpinspect--class-get-method-list other-class))
|
||||
(phpinspect--class-update-method class method 'extended))
|
||||
|
||||
(dolist (method (phpinspect--class-get-static-method-list other-class))
|
||||
(phpinspect--class-update-static-method class method 'extended))
|
||||
|
||||
(phpinspect--class-subscribe class other-class)))
|
||||
|
||||
(cl-defmethod phpinspect--class-subscribe ((class phpinspect--class)
|
||||
(subscription-class phpinspect--class))
|
||||
(phpinspect--class-edit class
|
||||
(unless (gethash subscription-class (phpinspect--class-subscriptions class))
|
||||
(let ((update-function
|
||||
(lambda (new-class)
|
||||
(phpinspect--class-edit class
|
||||
(phpinspect--class-incorporate class new-class)
|
||||
(phpinspect--class-trigger-update class)))))
|
||||
(puthash subscription-class update-function
|
||||
(phpinspect--class-subscriptions subscription-class))))))
|
||||
|
||||
(provide 'phpinspect-class)
|
||||
;;; phpinspect-class.el ends here
|
@ -1,319 +0,0 @@
|
||||
;;; phpinspect-type.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'obarray)
|
||||
|
||||
(require 'phpinspect-bmap)
|
||||
(require 'phpinspect-buffer)
|
||||
(require 'phpinspect-resolvecontext)
|
||||
(require 'phpinspect-suggest)
|
||||
|
||||
(defvar phpinspect--last-completion-list nil
|
||||
"Used internally to save metadata about completion options
|
||||
between company backend calls")
|
||||
|
||||
(cl-defstruct (phpinspect--completion
|
||||
(:constructor phpinspect--construct-completion))
|
||||
"Contains a possible completion value with all it's attributes."
|
||||
(target nil
|
||||
:documentation
|
||||
"The object that this completion is aimed at/completing towards")
|
||||
(value nil :type string)
|
||||
(meta nil :type string)
|
||||
(annotation nil :type string)
|
||||
(kind nil :type symbol))
|
||||
|
||||
(cl-defgeneric phpinspect--make-completion (completion-candidate)
|
||||
"Creates a `phpinspect--completion` for a possible completion
|
||||
candidate. Candidates can be indexed functions and variables.")
|
||||
|
||||
(cl-defstruct (phpinspect--completion-list
|
||||
(:constructor phpinspect--make-completion-list))
|
||||
"Contains all data for a completion at point"
|
||||
(completion-start nil
|
||||
:type integer)
|
||||
(completion-end nil
|
||||
:type integer)
|
||||
(completions (obarray-make)
|
||||
:type obarray
|
||||
:documentation
|
||||
"A list of completion strings")
|
||||
(has-candidates nil))
|
||||
|
||||
(cl-defgeneric phpinspect--completion-list-add
|
||||
(comp-list completion)
|
||||
"Add a completion to a completion-list.")
|
||||
|
||||
(cl-defmethod phpinspect--completion-list-add
|
||||
((comp-list phpinspect--completion-list) (completion phpinspect--completion))
|
||||
(setf (phpinspect--completion-list-has-candidates comp-list) t)
|
||||
|
||||
;; Ignore completions in an invalid state (nil values)
|
||||
(when (phpinspect--completion-value completion)
|
||||
(unless (intern-soft (phpinspect--completion-value completion)
|
||||
(phpinspect--completion-list-completions comp-list))
|
||||
(set (intern (phpinspect--completion-value completion)
|
||||
(phpinspect--completion-list-completions comp-list))
|
||||
completion))))
|
||||
|
||||
(cl-defmethod phpinspect--completion-list-get-metadata
|
||||
((comp-list phpinspect--completion-list) (completion-name string))
|
||||
(let ((comp-sym (intern-soft completion-name
|
||||
(phpinspect--completion-list-completions comp-list))))
|
||||
(when comp-sym
|
||||
(symbol-value comp-sym))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect--completion-list-strings
|
||||
((comp-list phpinspect--completion-list))
|
||||
(let ((strings))
|
||||
(obarray-map (lambda (sym) (push (symbol-name sym) strings))
|
||||
(phpinspect--completion-list-completions comp-list))
|
||||
strings))
|
||||
|
||||
(cl-defstruct (phpinspect-completion-query (:constructor phpinspect-make-completion-query))
|
||||
(completion-point 0
|
||||
:type integer
|
||||
:documentation
|
||||
"Position in the buffer from where the resolvecontext is determined.")
|
||||
(point 0
|
||||
:type integer
|
||||
:documentation "Position in buffer for which to provide completions")
|
||||
(buffer nil
|
||||
:type phpinspect-buffer))
|
||||
|
||||
|
||||
(cl-defgeneric phpinspect-comp-strategy-supports (strategy query context)
|
||||
"Should return non-nil if STRATEGY should be deployed for QUERY
|
||||
and CONTEXT. All strategies must implement this method.")
|
||||
|
||||
(cl-defgeneric phpinspect-comp-strategy-execute (strategy query context)
|
||||
"Should return a list of objects for which
|
||||
`phpinspect--make-completion' is implemented.")
|
||||
|
||||
(cl-defstruct (phpinspect-comp-sigil (:constructor phpinspect-make-comp-sigil))
|
||||
"Completion strategy for the sigil ($) character.")
|
||||
|
||||
(defun phpinspect-completion-subject-at-point (buffer point predicate)
|
||||
(let ((subject (phpinspect-bmap-last-token-before-point
|
||||
(phpinspect-buffer-parse-map buffer) point)))
|
||||
(and subject
|
||||
(funcall predicate (phpinspect-meta-token subject))
|
||||
(>= (phpinspect-meta-end subject) point)
|
||||
subject)))
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-supports
|
||||
((_strat phpinspect-comp-sigil) (q phpinspect-completion-query)
|
||||
(_rctx phpinspect--resolvecontext))
|
||||
(when-let ((subject (phpinspect-completion-subject-at-point
|
||||
(phpinspect-completion-query-buffer q)
|
||||
(phpinspect-completion-query-point q)
|
||||
#'phpinspect-variable-p)))
|
||||
(list (+ (phpinspect-meta-start subject) 1) (phpinspect-meta-end subject))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-execute
|
||||
((_strat phpinspect-comp-sigil) (_q phpinspect-completion-query)
|
||||
(rctx phpinspect--resolvecontext))
|
||||
(phpinspect-suggest-variables-at-point rctx))
|
||||
|
||||
(define-inline phpinspect-attrib-start (attrib-meta)
|
||||
"The start position of the name of the attribute that is being referenced.
|
||||
|
||||
ATTRIB-META must be an instance of phpinspect-meta (see `phpinspect-make-meta'),
|
||||
belonging to a token that conforms with `phpinspect-attrib-p'"
|
||||
(inline-letevals (attrib-meta)
|
||||
(inline-quote
|
||||
(- (phpinspect-meta-end ,attrib-meta)
|
||||
(length (cadadr (phpinspect-meta-token ,attrib-meta)))))))
|
||||
|
||||
(cl-defstruct (phpinspect-comp-attribute (:constructor phpinspect-make-comp-attribute))
|
||||
"Completion strategy for object attributes")
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-supports
|
||||
((_strat phpinspect-comp-attribute) (q phpinspect-completion-query)
|
||||
(_rctx phpinspect--resolvecontext))
|
||||
(when-let ((subject (phpinspect-completion-subject-at-point
|
||||
(phpinspect-completion-query-buffer q)
|
||||
(phpinspect-completion-query-point q)
|
||||
#'phpinspect-object-attrib-p)))
|
||||
(list (phpinspect-attrib-start subject) (phpinspect-meta-end subject))))
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-execute
|
||||
((_strat phpinspect-comp-attribute) (_q phpinspect-completion-query)
|
||||
(rctx phpinspect--resolvecontext))
|
||||
(phpinspect-suggest-attributes-at-point rctx))
|
||||
|
||||
(cl-defstruct (phpinspect-comp-static-attribute (:constructor phpinspect-make-comp-static-attribute))
|
||||
"Completion strategy for static attributes")
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-supports
|
||||
((_strat phpinspect-comp-static-attribute) (q phpinspect-completion-query)
|
||||
(_rctx phpinspect--resolvecontext))
|
||||
(when-let ((subject (phpinspect-completion-subject-at-point
|
||||
(phpinspect-completion-query-buffer q)
|
||||
(phpinspect-completion-query-point q)
|
||||
#'phpinspect-static-attrib-p)))
|
||||
(list (phpinspect-attrib-start subject) (phpinspect-meta-end subject))))
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-execute
|
||||
((_strat phpinspect-comp-static-attribute) (_q phpinspect-completion-query)
|
||||
(rctx phpinspect--resolvecontext))
|
||||
(phpinspect-suggest-attributes-at-point rctx 'static))
|
||||
|
||||
(cl-defstruct (phpinspect-comp-word (:constructor phpinspect-make-comp-word))
|
||||
"Comletion strategy for bare words")
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-supports
|
||||
((_strat phpinspect-comp-word) (q phpinspect-completion-query)
|
||||
(_rctx phpinspect--resolvecontext))
|
||||
(when-let ((subject (phpinspect-completion-subject-at-point
|
||||
(phpinspect-completion-query-buffer q)
|
||||
(phpinspect-completion-query-point q)
|
||||
#'phpinspect-word-p)))
|
||||
(list (phpinspect-meta-start subject) (phpinspect-meta-end subject))))
|
||||
|
||||
(cl-defmethod phpinspect-comp-strategy-execute
|
||||
((_strat phpinspect-comp-word) (_q phpinspect-completion-query)
|
||||
(rctx phpinspect--resolvecontext))
|
||||
(phpinspect-suggest-functions rctx))
|
||||
|
||||
(defvar phpinspect-completion-strategies (list (phpinspect-make-comp-attribute)
|
||||
(phpinspect-make-comp-sigil)
|
||||
(phpinspect-make-comp-word)
|
||||
(phpinspect-make-comp-static-attribute))
|
||||
"List of completion strategies that phpinspect can use.")
|
||||
|
||||
(defun phpinspect--get-completion-query ()
|
||||
(phpinspect-make-completion-query
|
||||
:buffer phpinspect-current-buffer
|
||||
:completion-point (phpinspect--determine-completion-point)
|
||||
:point (point)))
|
||||
|
||||
(cl-defmethod phpinspect-completion-query-execute ((query phpinspect-completion-query))
|
||||
"Execute QUERY.
|
||||
|
||||
Returns list of `phpinspect--completion'."
|
||||
(let* ((buffer (phpinspect-completion-query-buffer query))
|
||||
(point (phpinspect-completion-query-point query))
|
||||
(buffer-map (phpinspect-buffer-parse-map buffer))
|
||||
(rctx (phpinspect-get-resolvecontext buffer-map point))
|
||||
(completion-list (phpinspect--make-completion-list)))
|
||||
(phpinspect-buffer-update-project-index buffer)
|
||||
|
||||
(dolist (strategy phpinspect-completion-strategies)
|
||||
(when-let (region (phpinspect-comp-strategy-supports strategy query rctx))
|
||||
(setf (phpinspect--completion-list-completion-start completion-list)
|
||||
(car region)
|
||||
(phpinspect--completion-list-completion-end completion-list)
|
||||
(cadr region))
|
||||
|
||||
(phpinspect--log "Found matching completion strategy. Executing...")
|
||||
(dolist (candidate (phpinspect-comp-strategy-execute strategy query rctx))
|
||||
(phpinspect--completion-list-add
|
||||
completion-list (phpinspect--make-completion candidate)))))
|
||||
(setq phpinspect--last-completion-list completion-list)))
|
||||
|
||||
(cl-defmethod phpinspect--make-completion
|
||||
((completion-candidate phpinspect--function))
|
||||
"Create a `phpinspect--completion` for COMPLETION-CANDIDATE."
|
||||
(phpinspect--construct-completion
|
||||
:value (phpinspect--function-name completion-candidate)
|
||||
:meta (concat "(" (mapconcat (lambda (arg)
|
||||
(concat "$" (if (> (length (car arg)) 8)
|
||||
(truncate-string-to-width (car arg) 8 nil)
|
||||
(car arg))))
|
||||
(phpinspect--function-arguments completion-candidate)
|
||||
", ")
|
||||
") "
|
||||
(phpinspect--format-type-name (phpinspect--function-return-type completion-candidate)))
|
||||
:annotation (concat " "
|
||||
(phpinspect--type-bare-name
|
||||
(phpinspect--function-return-type completion-candidate)))
|
||||
:target completion-candidate
|
||||
:kind 'function))
|
||||
|
||||
(cl-defmethod phpinspect--make-completion
|
||||
((completion-candidate phpinspect--variable))
|
||||
(phpinspect--construct-completion
|
||||
:value (phpinspect--variable-name completion-candidate)
|
||||
:meta (phpinspect--format-type-name
|
||||
(or (phpinspect--variable-type completion-candidate)
|
||||
phpinspect--null-type))
|
||||
:target completion-candidate
|
||||
:annotation (concat " "
|
||||
(phpinspect--type-bare-name
|
||||
(or (phpinspect--variable-type completion-candidate)
|
||||
phpinspect--null-type)))
|
||||
:kind 'variable))
|
||||
|
||||
(define-inline phpinspect--prefix-for-completion (completion)
|
||||
(inline-letevals (completion)
|
||||
(inline-quote
|
||||
(pcase (phpinspect--completion-kind ,completion)
|
||||
('function "<f> ")
|
||||
('variable "<va> ")))))
|
||||
|
||||
|
||||
(defun phpinspect-complete-at-point ()
|
||||
(catch 'phpinspect-parse-interrupted
|
||||
(let ((comp-list (phpinspect-completion-query-execute (phpinspect--get-completion-query)))
|
||||
strings)
|
||||
(obarray-map (lambda (sym) (push (symbol-name sym) strings)) (phpinspect--completion-list-completions comp-list))
|
||||
(and (phpinspect--completion-list-has-candidates comp-list)
|
||||
(list (phpinspect--completion-list-completion-start comp-list)
|
||||
(phpinspect--completion-list-completion-end comp-list)
|
||||
strings
|
||||
:affixation-function
|
||||
(lambda (completions)
|
||||
(let (affixated completion)
|
||||
(dolist (comp completions)
|
||||
(setq completion (phpinspect--completion-list-get-metadata comp-list comp))
|
||||
(push (list comp (phpinspect--prefix-for-completion completion)
|
||||
(phpinspect--completion-meta completion))
|
||||
affixated))
|
||||
(nreverse affixated)))
|
||||
:exit-function
|
||||
(lambda (comp-name state)
|
||||
(let ((comp (phpinspect--completion-list-get-metadata
|
||||
phpinspect--last-completion-list
|
||||
comp-name)))
|
||||
(when (and (eq 'finished state)
|
||||
(eq 'function (phpinspect--completion-kind comp)))
|
||||
(insert "(")
|
||||
(when (= 0 (length (phpinspect--function-arguments
|
||||
(phpinspect--completion-target comp))))
|
||||
(insert ")")))))
|
||||
:company-kind (lambda (comp-name)
|
||||
(let ((comp
|
||||
(phpinspect--completion-list-get-metadata
|
||||
phpinspect--last-completion-list
|
||||
comp-name)))
|
||||
(if comp
|
||||
(phpinspect--completion-kind comp)
|
||||
(phpinspect--log "Unable to find matching completion for name %s" comp-name)
|
||||
nil))))))))
|
||||
|
||||
(provide 'phpinspect-completion)
|
@ -1,235 +0,0 @@
|
||||
;;; phpinspect-edtrack.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'phpinspect-meta)
|
||||
(phpinspect--declare-log-group 'edtrack))
|
||||
|
||||
(cl-defstruct (phpinspect-edtrack (:constructor phpinspect-make-edtrack))
|
||||
(edits nil
|
||||
:type list)
|
||||
(taint-pool nil
|
||||
:type list)
|
||||
(last-edit nil
|
||||
:type cons
|
||||
:documentation "Last registered edit")
|
||||
(last-edit-start -1
|
||||
:type integer
|
||||
:documentation "Last registered edit start position"))
|
||||
|
||||
(defsubst phpinspect-edit-original-end (edit)
|
||||
(or (caar edit) 0))
|
||||
|
||||
(defsubst phpinspect-edit-delta (edit)
|
||||
(let ((delta (or (cdar edit) 0))
|
||||
(previous-edit edit))
|
||||
(while (setq previous-edit (cdr previous-edit))
|
||||
(setq delta (+ delta (cdar previous-edit))))
|
||||
delta))
|
||||
|
||||
(defsubst phpinspect-edit-end (edit)
|
||||
(let ((end (or (caar edit) 0))
|
||||
(previous-edit (cdr edit)))
|
||||
(+ end (phpinspect-edit-delta previous-edit))))
|
||||
|
||||
(defsubst phpinspect-edtrack-original-position-at-point (track point)
|
||||
(let ((edit (phpinspect-edtrack-edits track))
|
||||
(encroached)
|
||||
(pos))
|
||||
(while (and edit (<= point (phpinspect-edit-end edit)))
|
||||
(setq edit (cdr edit)))
|
||||
|
||||
(setq pos (- point (phpinspect-edit-delta edit)))
|
||||
|
||||
;; When point is within the edit delta's range, correct the delta by the
|
||||
;; amount of encroachment.
|
||||
(if (< 0 (setq encroached (- (+ (phpinspect-edit-end edit) (or (cdar edit) 0)) point)))
|
||||
(+ pos encroached)
|
||||
pos)))
|
||||
|
||||
(defsubst phpinspect-edtrack-current-position-at-point (track point)
|
||||
(let ((edit (phpinspect-edtrack-edits track))
|
||||
(encroached)
|
||||
(pos))
|
||||
(while (and edit (<= point (phpinspect-edit-original-end edit)))
|
||||
(setq edit (cdr edit)))
|
||||
|
||||
(setq pos (+ point (phpinspect-edit-delta edit)))
|
||||
|
||||
(if (< 0 (setq encroached (- (+ (phpinspect-edit-original-end edit) (or (cdar edit) 0)) point)))
|
||||
(- pos encroached)
|
||||
pos)))
|
||||
|
||||
(define-inline phpinspect-taint-start (taint)
|
||||
(inline-quote (car ,taint)))
|
||||
|
||||
(define-inline phpinspect-taint-end (taint)
|
||||
(inline-quote (cdr ,taint)))
|
||||
|
||||
(define-inline phpinspect-make-taint (start end)
|
||||
(inline-quote (cons ,start ,end)))
|
||||
|
||||
(defsubst phpinspect-taint-overlaps-point (taint point)
|
||||
(and (> (phpinspect-taint-end taint) point)
|
||||
(<= (phpinspect-taint-start taint) point)))
|
||||
|
||||
(defsubst phpinspect-taint-overlaps-region (taint start end)
|
||||
(or (phpinspect-taint-overlaps-point taint start)
|
||||
(phpinspect-taint-overlaps-point taint end)
|
||||
(and (> end (phpinspect-taint-start taint))
|
||||
(<= start (phpinspect-taint-start taint)))
|
||||
(and (> end (phpinspect-taint-end taint))
|
||||
(<= start (phpinspect-taint-end taint)))))
|
||||
|
||||
(defsubst phpinspect-taint-overlaps (taint1 taint2)
|
||||
(or (phpinspect-taint-overlaps-point taint1 (phpinspect-taint-start taint2))
|
||||
(phpinspect-taint-overlaps-point taint1 (phpinspect-taint-end taint2))
|
||||
(phpinspect-taint-overlaps-point taint2 (phpinspect-taint-start taint1))
|
||||
(phpinspect-taint-overlaps-point taint2 (phpinspect-taint-end taint1))))
|
||||
|
||||
(defsubst phpinspect-taint-overlaps-meta (taint meta)
|
||||
(or (phpinspect-taint-overlaps-point taint (phpinspect-meta-start meta))
|
||||
(phpinspect-taint-overlaps-point taint (phpinspect-meta-end meta))
|
||||
(phpinspect-meta-overlaps-point meta (phpinspect-taint-start taint))
|
||||
(phpinspect-meta-overlaps-point meta (phpinspect-taint-end taint))))
|
||||
|
||||
(defsubst phpinspect-edtrack-register-taint (track start end)
|
||||
(let ((pool (phpinspect-edtrack-taint-pool track))
|
||||
(idx 0)
|
||||
(overlap-start)
|
||||
(overlap-end)
|
||||
(left-neighbour)
|
||||
(taint (phpinspect-make-taint start end)))
|
||||
(catch 'break
|
||||
(while pool
|
||||
(if (phpinspect-taint-overlaps taint (car pool))
|
||||
(progn
|
||||
(when (< (phpinspect-taint-start (car pool)) start)
|
||||
(setcar taint (phpinspect-taint-start (car pool))))
|
||||
(when (> (phpinspect-taint-end (car pool)) end)
|
||||
(setcdr taint (phpinspect-taint-end (car pool))))
|
||||
|
||||
(when (not overlap-start)
|
||||
(setq overlap-start idx))
|
||||
(setq overlap-end idx))
|
||||
|
||||
;; Else
|
||||
(when overlap-start
|
||||
(throw 'break nil))
|
||||
|
||||
(when (> start (phpinspect-taint-end (car pool)))
|
||||
(setq left-neighbour pool)
|
||||
(throw 'break nil)))
|
||||
|
||||
(setq pool (cdr pool)
|
||||
idx (+ idx 1))))
|
||||
|
||||
(cond (overlap-start
|
||||
(setq pool (phpinspect-edtrack-taint-pool track))
|
||||
(setcar (nthcdr overlap-start pool) taint)
|
||||
(setcdr (nthcdr overlap-start pool) (nthcdr (+ 1 overlap-end) pool)))
|
||||
(left-neighbour
|
||||
(setcdr left-neighbour (cons taint (cdr left-neighbour))))
|
||||
(t
|
||||
(push taint (phpinspect-edtrack-taint-pool track))))))
|
||||
|
||||
(defsubst phpinspect-edtrack-register-edit (track start end pre-change-length)
|
||||
(phpinspect--log
|
||||
"Edtrack registered change: [start: %d, end: %d, pre-change-length: %d]"
|
||||
start end pre-change-length)
|
||||
|
||||
(let ((original-start (phpinspect-edtrack-original-position-at-point track start)))
|
||||
(phpinspect-edtrack-register-taint
|
||||
track original-start (+ original-start pre-change-length)))
|
||||
|
||||
(let ((edit-before (phpinspect-edtrack-edits track)))
|
||||
(while (and edit-before (< end (phpinspect-edit-end edit-before)))
|
||||
(setq edit-before (cdr edit-before)))
|
||||
|
||||
(let ((delta ;; The delta of this edit.
|
||||
(- (- end start) pre-change-length))
|
||||
new-edit)
|
||||
(setq new-edit (cons
|
||||
;; The end location of the edited region, before being
|
||||
;; edited, with the delta edits that happened at preceding
|
||||
;; points in the buffer subtratted. This corresponds with
|
||||
;; the original position of the region end before the
|
||||
;; buffer was ever edited.
|
||||
(phpinspect-edtrack-original-position-at-point
|
||||
track (+ start pre-change-length))
|
||||
delta))
|
||||
(if edit-before
|
||||
(progn
|
||||
(setcdr edit-before (cons (car edit-before) (cdr edit-before)))
|
||||
(setcar edit-before new-edit))
|
||||
(if (phpinspect-edtrack-edits track)
|
||||
(push new-edit (cdr (last (phpinspect-edtrack-edits track))))
|
||||
(push new-edit (phpinspect-edtrack-edits track)))))))
|
||||
|
||||
|
||||
(defsubst phpinspect-edtrack-clear-taint-pool (track)
|
||||
(setf (phpinspect-edtrack-taint-pool track) nil))
|
||||
|
||||
(defsubst phpinspect-edtrack-clear (track)
|
||||
(setf (phpinspect-edtrack-edits track) nil)
|
||||
(setf (phpinspect-edtrack-last-edit track) nil)
|
||||
(setf (phpinspect-edtrack-last-edit-start track) -1)
|
||||
(phpinspect-edtrack-clear-taint-pool track))
|
||||
|
||||
|
||||
(defsubst phpinspect-edtrack-make-taint-iterator (track)
|
||||
(cons (car (phpinspect-edtrack-taint-pool track))
|
||||
(cl-copy-list (cdr (phpinspect-edtrack-taint-pool track)))))
|
||||
|
||||
(define-inline phpinspect-taint-iterator-current (iter)
|
||||
(inline-quote (car ,iter)))
|
||||
|
||||
(define-inline phpinspect-taint-iterator-follow (iter pos)
|
||||
(inline-letevals (iter pos)
|
||||
(inline-quote
|
||||
(or (while (and (phpinspect-taint-iterator-current ,iter)
|
||||
(> ,pos (phpinspect-taint-end
|
||||
(phpinspect-taint-iterator-current ,iter))))
|
||||
(setf (phpinspect-taint-iterator-current ,iter) (pop (cdr ,iter))))
|
||||
(phpinspect-taint-iterator-current ,iter)))))
|
||||
|
||||
(define-inline phpinspect-taint-iterator-token-is-tainted-p (iter meta)
|
||||
(inline-letevals (iter meta)
|
||||
(inline-quote
|
||||
(and (phpinspect-taint-iterator-follow ,iter (phpinspect-meta-start ,meta))
|
||||
(phpinspect-taint-overlaps-meta
|
||||
(phpinspect-taint-iterator-current ,iter) ,meta)))))
|
||||
|
||||
(define-inline phpinspect-taint-iterator-region-is-tainted-p (iter start end)
|
||||
(inline-letevals (iter start end)
|
||||
(inline-quote
|
||||
(and (phpinspect-taint-iterator-follow ,iter ,start)
|
||||
(phpinspect-taint-overlaps-region
|
||||
(phpinspect-taint-iterator-current ,iter) ,start ,end)))))
|
||||
|
||||
(provide 'phpinspect-edtrack)
|
||||
;;; phpinspect-edtrack.el ends here
|
@ -1,283 +0,0 @@
|
||||
;;; phpinspect-eldoc.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-meta)
|
||||
(require 'phpinspect-token-predicates)
|
||||
(require 'phpinspect-resolve)
|
||||
(require 'phpinspect-buffer)
|
||||
|
||||
(eval-when-compile
|
||||
(phpinspect--declare-log-group 'eldoc))
|
||||
|
||||
(defvar phpinspect-eldoc-word-width 14
|
||||
"The maximum width of words in eldoc strings.")
|
||||
|
||||
(cl-defstruct (phpinspect-eldoc-query (:constructor phpinspect-make-eldoc-query))
|
||||
(point 0
|
||||
:type integer
|
||||
:documentation "Position in buffer for which to provide hints")
|
||||
(buffer nil
|
||||
:type phpinspect-buffer))
|
||||
|
||||
|
||||
(cl-defgeneric phpinspect-eld-strategy-supports (strategy query context)
|
||||
"Should return non-nil if STRATEGY should be deployed for QUERY
|
||||
and CONTEXT. All strategies must implement this method.")
|
||||
|
||||
(cl-defgeneric phpinspect-eld-strategy-execute (strategy query context)
|
||||
"Should return an object for which `phpinspect-eldoc-string' is implemented.")
|
||||
|
||||
(cl-defgeneric phpinspect-eldoc-string (response)
|
||||
"Should return a string to be displayed by eldoc. This needs to
|
||||
be implemented for return values of `phpinspect-eld-strategy-execute'")
|
||||
|
||||
(cl-defstruct (phpinspect-eld-attribute (:constructor phpinspect-make-eld-attribute))
|
||||
"Eldoc strategy for object attributes.")
|
||||
|
||||
(cl-defmethod phpinspect-eld-strategy-supports
|
||||
((_strat phpinspect-eld-attribute) (_q phpinspect-eldoc-query) (rctx phpinspect--resolvecontext))
|
||||
(phpinspect-attrib-p (car (last (phpinspect--resolvecontext-subject rctx)))))
|
||||
|
||||
(cl-defmethod phpinspect-eld-strategy-execute
|
||||
((_strat phpinspect-eld-attribute) (_q phpinspect-eldoc-query) (rctx phpinspect--resolvecontext))
|
||||
(let ((attrib (car (last (phpinspect--resolvecontext-subject rctx))))
|
||||
type-before)
|
||||
(setf (phpinspect--resolvecontext-subject rctx) (butlast (phpinspect--resolvecontext-subject rctx)))
|
||||
(setq type-before (phpinspect-resolve-type-from-context rctx))
|
||||
|
||||
(when type-before
|
||||
(let ((class (phpinspect-project-get-class-extra-or-create
|
||||
(phpinspect--resolvecontext-project rctx)
|
||||
type-before))
|
||||
(attribute-name (cadadr attrib))
|
||||
variable method result)
|
||||
(when attribute-name
|
||||
(cond ((phpinspect-static-attrib-p attrib)
|
||||
(setq variable (phpinspect--class-get-variable class attribute-name))
|
||||
|
||||
(if (and variable
|
||||
(or (phpinspect--variable-static-p variable)
|
||||
(phpinspect--variable-const-p variable)))
|
||||
(setq result variable)
|
||||
(setq method (phpinspect--class-get-static-method
|
||||
class (phpinspect-intern-name attribute-name)))
|
||||
(when method
|
||||
(setq result (phpinspect-make-function-doc :fn method)))))
|
||||
((phpinspect-object-attrib-p attrib)
|
||||
(setq variable (phpinspect--class-get-variable class attribute-name))
|
||||
|
||||
(if (and variable
|
||||
(phpinspect--variable-vanilla-p variable))
|
||||
(setq result variable)
|
||||
(setq method (phpinspect--class-get-method
|
||||
class (phpinspect-intern-name attribute-name)))
|
||||
(when method
|
||||
(setq result (phpinspect-make-function-doc :fn method))))))
|
||||
result)))))
|
||||
|
||||
(cl-defstruct (phpinspect-eld-function-args (:constructor phpinspect-make-eld-function-args))
|
||||
"Eldoc strategy for function arguments.")
|
||||
|
||||
(cl-defmethod phpinspect-eld-strategy-supports
|
||||
((_strat phpinspect-eld-function-args) (_q phpinspect-eldoc-query) (rctx phpinspect--resolvecontext))
|
||||
(let ((parent-token (car (phpinspect--resolvecontext-enclosing-tokens rctx))))
|
||||
;; When our subject is inside a list, it is probably an argument of a
|
||||
;; function/method call, which is what this strategy provides information for.
|
||||
(or (phpinspect-list-p parent-token)
|
||||
;; When the last token in our subject is a list, we're either at the end
|
||||
;; of a buffer in an incomplete argument list (no closing paren), or in
|
||||
;; an empty argument list of a function call.
|
||||
(phpinspect-list-p
|
||||
(car (last (phpinspect--resolvecontext-subject rctx)))))))
|
||||
|
||||
(cl-defmethod phpinspect-eld-strategy-execute
|
||||
((_strat phpinspect-eld-function-args) (q phpinspect-eldoc-query) (rctx phpinspect--resolvecontext))
|
||||
(phpinspect--log "Executing `phpinspect-eld-function-args' strategy")
|
||||
(let* ((enclosing-token (car (phpinspect--resolvecontext-enclosing-metadata
|
||||
rctx)))
|
||||
(left-sibling )
|
||||
(statement )
|
||||
match-result static arg-list arg-pos)
|
||||
|
||||
(cond
|
||||
;; Subject is a statement
|
||||
((and (phpinspect-list-p (car (last (phpinspect--resolvecontext-subject rctx))))
|
||||
enclosing-token)
|
||||
|
||||
(setq left-sibling (phpinspect-meta-find-child-before-recursively
|
||||
enclosing-token (phpinspect-eldoc-query-point q))))
|
||||
;; Subject is inside an argument list
|
||||
((and enclosing-token
|
||||
(phpinspect-list-p (phpinspect-meta-token enclosing-token)))
|
||||
(setq left-sibling (phpinspect-meta-find-left-sibling enclosing-token)
|
||||
statement (list enclosing-token))))
|
||||
|
||||
(phpinspect--log "Left sibling: %s" (phpinspect-meta-string left-sibling))
|
||||
(phpinspect--log "Enclosing parent: %s" (phpinspect-meta-string (phpinspect-meta-parent enclosing-token)))
|
||||
|
||||
|
||||
(while (and left-sibling
|
||||
(not (phpinspect-statement-introduction-p (phpinspect-meta-token left-sibling))))
|
||||
(unless (phpinspect-comment-p (phpinspect-meta-token left-sibling))
|
||||
(push left-sibling statement))
|
||||
(setq left-sibling (phpinspect-meta-find-left-sibling left-sibling)))
|
||||
|
||||
(phpinspect--log "Eldoc statement is: %s" (mapcar #'phpinspect-meta-token statement))
|
||||
(phpinspect--log "Enclosing token was: %s" (phpinspect-meta-token enclosing-token))
|
||||
|
||||
(when enclosing-token
|
||||
(cond
|
||||
;; Method call
|
||||
((setq match-result (phpinspect--match-sequence (last statement 2)
|
||||
:f (phpinspect-meta-wrap-token-pred #'phpinspect-attrib-p)
|
||||
:f (phpinspect-meta-wrap-token-pred #'phpinspect-list-p)))
|
||||
(phpinspect--log "Eldoc context is a method call")
|
||||
|
||||
(setq arg-list (car (last match-result))
|
||||
static (phpinspect-static-attrib-p (phpinspect-meta-token (car match-result)))
|
||||
arg-pos (seq-reduce
|
||||
(lambda (count meta)
|
||||
(if (phpinspect-comma-p (phpinspect-meta-token meta))
|
||||
(+ count 1)
|
||||
count))
|
||||
(phpinspect-meta-find-children-before arg-list (phpinspect-eldoc-query-point q)) 0))
|
||||
|
||||
;; Set resolvecontext subject to the statement minus the method
|
||||
;; name. Point is likely to be at a location inside a method call like
|
||||
;; "$a->b->doSomething(". The resulting subject should be "$a->b".
|
||||
(setf (phpinspect--resolvecontext-subject rctx)
|
||||
(mapcar #'phpinspect-meta-token (butlast statement 2)))
|
||||
|
||||
|
||||
|
||||
(when-let* ((type-of-previous-statement
|
||||
(phpinspect-resolve-type-from-context rctx))
|
||||
(method-name-sym (phpinspect-intern-name (cadadr (phpinspect-meta-token (car match-result)))))
|
||||
(class (phpinspect-project-get-class-extra-or-create
|
||||
(phpinspect--resolvecontext-project rctx)
|
||||
type-of-previous-statement))
|
||||
(method (if static
|
||||
(phpinspect--class-get-static-method class method-name-sym)
|
||||
(phpinspect--class-get-method class method-name-sym))))
|
||||
|
||||
(when method
|
||||
(phpinspect-make-function-doc :fn method :arg-pos arg-pos))))
|
||||
((setq match-result (phpinspect--match-sequence (last statement 2)
|
||||
:f (phpinspect-meta-wrap-token-pred #'phpinspect-word-p)
|
||||
:f (phpinspect-meta-wrap-token-pred #'phpinspect-list-p)))
|
||||
(phpinspect--log "Eldoc context is a function call")
|
||||
|
||||
(setq arg-list (car (last match-result))
|
||||
arg-pos (seq-reduce
|
||||
(lambda (count meta)
|
||||
(if (phpinspect-comma-p (phpinspect-meta-token meta))
|
||||
(+ count 1)
|
||||
count))
|
||||
(phpinspect-meta-find-children-before arg-list (phpinspect-eldoc-query-point q)) 0))
|
||||
|
||||
(let ((func (phpinspect-project-get-function-or-extra
|
||||
(phpinspect--resolvecontext-project rctx)
|
||||
(phpinspect-intern-name (cadr (phpinspect-meta-token (car match-result)))))))
|
||||
(phpinspect--log "Got past that")
|
||||
(when func
|
||||
(phpinspect-make-function-doc :fn func :arg-pos arg-pos))))))))
|
||||
|
||||
(cl-defmethod phpinspect-eldoc-string ((var phpinspect--variable))
|
||||
(concat (truncate-string-to-width
|
||||
(propertize (concat (if (phpinspect--variable-vanilla-p var) "$" "")
|
||||
(phpinspect--variable-name var))
|
||||
'face 'font-lock-variable-name-face)
|
||||
phpinspect-eldoc-word-width)
|
||||
": "
|
||||
(propertize (phpinspect--format-type-name (phpinspect--variable-type var))
|
||||
'face 'font-lock-type-face)))
|
||||
|
||||
(cl-defstruct (phpinspect-function-doc (:constructor phpinspect-make-function-doc))
|
||||
(fn nil
|
||||
:type phpinspect--function)
|
||||
(arg-pos nil))
|
||||
|
||||
(cl-defmethod phpinspect-eldoc-string ((doc phpinspect-function-doc))
|
||||
(let ((fn (phpinspect-function-doc-fn doc))
|
||||
(arg-pos (phpinspect-function-doc-arg-pos doc))
|
||||
(arg-count 0))
|
||||
(concat (truncate-string-to-width
|
||||
(phpinspect--function-name fn) phpinspect-eldoc-word-width) ": ("
|
||||
(mapconcat
|
||||
(lambda (arg)
|
||||
(let ((doc-string
|
||||
(concat "$" (truncate-string-to-width
|
||||
(car arg) phpinspect-eldoc-word-width)
|
||||
(if (cadr arg) " " "")
|
||||
(phpinspect--format-type-name (or (cadr arg) "")))))
|
||||
(when (and arg-pos (= arg-count arg-pos))
|
||||
(setq doc-string
|
||||
(propertize
|
||||
doc-string 'face 'eldoc-highlight-function-argument)))
|
||||
(setq arg-count (+ arg-count 1))
|
||||
doc-string))
|
||||
(phpinspect--function-arguments fn)
|
||||
", ")
|
||||
"): "
|
||||
(propertize
|
||||
(phpinspect--format-type-name (phpinspect--function-return-type fn))
|
||||
'face 'font-lock-type-face))))
|
||||
|
||||
(defvar phpinspect-eldoc-strategies (list (phpinspect-make-eld-attribute)
|
||||
(phpinspect-make-eld-function-args))
|
||||
"The eldoc strategies that phpinspect is currently allowed to
|
||||
employ. Strategies are queried in the order of this list. See
|
||||
also `phpinspect-eldoc-query-execute'.")
|
||||
|
||||
(cl-defmethod phpinspect-eldoc-query-execute ((query phpinspect-eldoc-query))
|
||||
(let* ((buffer (phpinspect-eldoc-query-buffer query))
|
||||
(point (phpinspect-eldoc-query-point query))
|
||||
(buffer-map (phpinspect-buffer-parse-map buffer))
|
||||
(rctx (phpinspect-get-resolvecontext buffer-map point)))
|
||||
(phpinspect-buffer-update-project-index buffer)
|
||||
(catch 'matched
|
||||
(dolist (strategy phpinspect-eldoc-strategies)
|
||||
(when (phpinspect-eld-strategy-supports strategy query rctx)
|
||||
(phpinspect--log "Found matching eldoc strategy. Executing...")
|
||||
(throw 'matched (phpinspect-eld-strategy-execute strategy query rctx)))))))
|
||||
|
||||
(defun phpinspect-eldoc-function ()
|
||||
"An `eldoc-documentation-function` implementation for PHP files.
|
||||
|
||||
Ignores `eldoc-argument-case` and `eldoc-echo-area-use-multiline-p`.
|
||||
|
||||
TODO:
|
||||
- Respect `eldoc-echo-area-use-multiline-p`
|
||||
"
|
||||
(catch 'phpinspect-parse-interrupted
|
||||
(let ((resp (phpinspect-eldoc-query-execute
|
||||
(phpinspect-make-eldoc-query
|
||||
:buffer phpinspect-current-buffer
|
||||
:point (phpinspect--determine-completion-point)))))
|
||||
(when resp
|
||||
(phpinspect-eldoc-string resp)))))
|
||||
|
||||
|
||||
(provide 'phpinspect-eldoc)
|
@ -1,176 +0,0 @@
|
||||
;;; phpinspect-fs.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
(defconst phpinspect--cat-executable (executable-find "cat")
|
||||
"The executable used to read files asynchronously from the filesystem.")
|
||||
|
||||
(cl-defstruct (phpinspect-fs (:constructor phpinspect-make-fs)))
|
||||
|
||||
(cl-defstruct (phpinspect-virtual-fs (:constructor phpinspect-make-virtual-fs))
|
||||
"A rough in-memory filesystem. Useful for testing."
|
||||
(files (make-hash-table :test 'equal)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"The files in the virtual filesystem"))
|
||||
|
||||
(defsubst phpinspect-make-virtual-file (contents)
|
||||
(list contents (current-time)))
|
||||
|
||||
(defalias 'phpinspect-virtual-file-modification-time #'cadr)
|
||||
(defalias 'phpinspect-virtual-file-contents #'car)
|
||||
|
||||
(defun phpinspect-virtual-fs-set-file (fs path contents)
|
||||
(declare (indent defun))
|
||||
(puthash path (phpinspect-make-virtual-file contents)
|
||||
(phpinspect-virtual-fs-files fs)))
|
||||
|
||||
(cl-defgeneric phpinspect-fs-file-exists-p (fs file))
|
||||
(cl-defgeneric phpinspect-fs-file-directory-p (fs file))
|
||||
(cl-defgeneric phpinspect-fs-file-modification-time (fs file))
|
||||
(cl-defgeneric phpinspect-fs-insert-file-contents (fs file &optional prefer-async)
|
||||
"Insert file contents of FILE.
|
||||
|
||||
When PREFER-ASYNC is set and FS supports it, effort is made to
|
||||
execute the insertion asynchronously in scenario's where this can
|
||||
prevent the main thread (or other running threads) from stalling
|
||||
while the current thread executes. When running in the main
|
||||
thread, PREFER-ASYNC has no effect.")
|
||||
(cl-defgeneric phpinspect-fs-directory-files (fs directory match))
|
||||
(cl-defgeneric phpinspect-fs-directory-files-recursively (fs directory match))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-exists-p ((_fs phpinspect-fs) file)
|
||||
(file-exists-p file))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-exists-p ((fs phpinspect-virtual-fs) file)
|
||||
(and (gethash file (phpinspect-virtual-fs-files fs)) t))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-directory-p ((_fs phpinspect-fs) file)
|
||||
(file-directory-p file))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-directory-p ((fs phpinspect-virtual-fs) file)
|
||||
(setq file (concat (string-remove-suffix "/" file) "/"))
|
||||
(let ((is-directory? nil))
|
||||
(maphash
|
||||
(lambda (existing-file _ignored)
|
||||
(when (string-prefix-p (file-name-directory existing-file)
|
||||
file)
|
||||
(setq is-directory? t)))
|
||||
(phpinspect-virtual-fs-files fs))
|
||||
is-directory?))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-modification-time ((fs phpinspect-virtual-fs) file)
|
||||
(let ((file (gethash file (phpinspect-virtual-fs-files fs))))
|
||||
(when file
|
||||
(phpinspect-virtual-file-modification-time file))))
|
||||
|
||||
(cl-defmethod phpinspect-fs-file-modification-time ((_fs phpinspect-fs) file)
|
||||
(let ((attributes (file-attributes file)))
|
||||
(when attributes
|
||||
(file-attribute-modification-time attributes))))
|
||||
|
||||
|
||||
(defsubst phpinspect--insert-file-contents-asynchronously (file)
|
||||
"Inserts FILE contents into the current buffer asynchronously,
|
||||
while blocking the current thread.
|
||||
|
||||
Errors when executed in main thread, as it should be used to make
|
||||
background operations less invasive. Usage in the main thread can
|
||||
only be the result of a logic error."
|
||||
(let* ((thread (current-thread))
|
||||
(mx (make-mutex))
|
||||
(condition (make-condition-variable mx))
|
||||
(err)
|
||||
(sentinel
|
||||
(lambda (process event)
|
||||
(with-mutex mx
|
||||
(if (string-match-p "^\\(deleted\\|exited\\|failed\\|connection\\)" event)
|
||||
(progn
|
||||
(setq err (format "cat process %s failed with event: %s" process event))
|
||||
(condition-notify condition))
|
||||
(when (string-match-p "^finished" event)
|
||||
(condition-notify condition)))))))
|
||||
(when (not phpinspect--cat-executable)
|
||||
(error
|
||||
"ERROR: phpinspect--insert-file-contents-asynchronously called when cat-executable is not set"))
|
||||
|
||||
(when (eq thread main-thread)
|
||||
(error "ERROR: phpinspect--insert-file-contents-asynchronously called from main-thread"))
|
||||
|
||||
(with-mutex mx
|
||||
(make-process :name "phpinspect--insert-file-contents-asynchronously"
|
||||
:command `(,phpinspect--cat-executable ,file)
|
||||
:buffer (current-buffer)
|
||||
:sentinel sentinel)
|
||||
|
||||
(condition-wait condition)
|
||||
(when err (error err)))))
|
||||
|
||||
(cl-defmethod phpinspect-fs-insert-file-contents ((_fs phpinspect-fs) file &optional prefer-async)
|
||||
"Insert file contents from FILE. "
|
||||
(if (and prefer-async (not (eq (current-thread) main-thread))
|
||||
phpinspect--cat-executable)
|
||||
(phpinspect--insert-file-contents-asynchronously file)
|
||||
(insert-file-contents-literally file)))
|
||||
|
||||
(cl-defmethod phpinspect-fs-insert-file-contents ((fs phpinspect-virtual-fs) file &optional _ignored)
|
||||
(let ((file-obj (gethash file (phpinspect-virtual-fs-files fs))))
|
||||
(when file (insert (or (phpinspect-virtual-file-contents file-obj) "")))))
|
||||
|
||||
(cl-defmethod phpinspect-fs-directory-files ((_fs phpinspect-fs) directory &optional match)
|
||||
(directory-files directory t match t))
|
||||
|
||||
(cl-defmethod phpinspect-fs-directory-files ((fs phpinspect-virtual-fs) directory &optional match)
|
||||
(setq directory (replace-regexp-in-string "[/]+" "/" (concat directory "/")))
|
||||
(let ((files))
|
||||
(maphash
|
||||
(lambda (file _ignored)
|
||||
(let ((basename (replace-regexp-in-string "/.*$" "" (string-remove-prefix directory file))))
|
||||
(when (and (string-prefix-p directory file)
|
||||
(string-match-p "^[^/]*$" basename)
|
||||
(if match (string-match-p match basename) t))
|
||||
(cl-pushnew (concat directory basename) files :test #'string=))))
|
||||
(phpinspect-virtual-fs-files fs))
|
||||
files))
|
||||
|
||||
(cl-defmethod phpinspect-fs-directory-files-recursively ((_fs phpinspect-fs) directory &optional match)
|
||||
(directory-files-recursively directory
|
||||
match
|
||||
t ;; Ignore directories that cannot be read
|
||||
t ;; follow symlinks
|
||||
))
|
||||
|
||||
(cl-defmethod phpinspect-fs-directory-files-recursively ((fs phpinspect-virtual-fs) directory &optional match)
|
||||
(setq directory (replace-regexp-in-string "[/]+" "/" (concat directory "/")))
|
||||
(let ((files))
|
||||
(maphash
|
||||
(lambda (file _ignored)
|
||||
(when (and (string-prefix-p directory file)
|
||||
(if match (string-match-p match file) t))
|
||||
(push file files)))
|
||||
(phpinspect-virtual-fs-files fs))
|
||||
files))
|
||||
|
||||
(provide 'phpinspect-fs)
|
||||
;;; phpinspect-fs.el ends here
|
@ -1,165 +0,0 @@
|
||||
; phpinspect-imports.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-token-predicates)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-autoload)
|
||||
(require 'phpinspect-buffer)
|
||||
(require 'phpinspect-cache)
|
||||
(require 'phpinspect-util)
|
||||
|
||||
(defun phpinspect-insert-at-point (point data)
|
||||
(save-excursion
|
||||
(goto-char point)
|
||||
(insert data)))
|
||||
|
||||
(defun phpinspect-find-first-use (token-meta)
|
||||
(if (and (phpinspect-namespace-p (phpinspect-meta-token token-meta))
|
||||
(phpinspect-namespace-is-blocked-p (phpinspect-meta-token token-meta)))
|
||||
(phpinspect-find-first-use (phpinspect-meta-last-child token-meta))
|
||||
(phpinspect-meta-find-first-child-matching
|
||||
token-meta (phpinspect-meta-wrap-token-pred #'phpinspect-use-p))))
|
||||
|
||||
(defun phpinspect-add-use (fqn buffer &optional namespace-meta)
|
||||
"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-meta
|
||||
(let* ((namespace-block (and (phpinspect-namespace-is-blocked-p
|
||||
(phpinspect-meta-token namespace-meta))
|
||||
(phpinspect-meta-last-child namespace-meta)))
|
||||
(existing-use (phpinspect-find-first-use namespace-meta)))
|
||||
(if existing-use
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-meta-start existing-use) (format "use %s;%c" fqn ?\n))
|
||||
(if namespace-block
|
||||
(phpinspect-insert-at-point
|
||||
(+ 1 (phpinspect-meta-start namespace-block))
|
||||
(format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-meta-end
|
||||
(phpinspect-meta-find-first-child-matching
|
||||
namespace-meta (phpinspect-meta-wrap-token-pred #'phpinspect-terminator-p)))
|
||||
(format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)))))
|
||||
;; else
|
||||
(let ((existing-use (phpinspect-meta-find-first-child-matching
|
||||
(phpinspect-buffer-root-meta buffer)
|
||||
(phpinspect-meta-wrap-token-pred #'phpinspect-use-p))))
|
||||
(if existing-use
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-meta-start existing-use)
|
||||
(format "use %s;%c" fqn ?\n))
|
||||
(let* ((first-token (phpinspect-meta-first-child (phpinspect-buffer-root-meta buffer)))
|
||||
token-after)
|
||||
(when (and (phpinspect-word-p (phpinspect-meta-token first-token))
|
||||
(string= "declare" (cadr (phpinspect-meta-token first-token))))
|
||||
(progn
|
||||
(setq token-after first-token)
|
||||
(while (and token-after (not (phpinspect-terminator-p
|
||||
(phpinspect-meta-token token-after))))
|
||||
(setq token-after (phpinspect-meta-find-right-sibling token-after)))))
|
||||
(if token-after
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-meta-end token-after) (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))
|
||||
(phpinspect-insert-at-point
|
||||
(phpinspect-meta-start 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))
|
||||
(fqns (phpinspect-autoloader-get-type-bag autoloader typename)))
|
||||
(cond ((= 1 (length fqns))
|
||||
(phpinspect-add-use (phpinspect-name-string (car fqns)) buffer namespace-token))
|
||||
((> (length fqns) 1)
|
||||
(phpinspect-add-use (completing-read "Class: " (phpinspect-names-to-alist fqns))
|
||||
buffer namespace-token))
|
||||
(t (phpinspect-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-add-use-statements-for-missing-types (types buffer imports project parent-token)
|
||||
(let (namespace namespace-name)
|
||||
(dolist (type types)
|
||||
(setq namespace (phpinspect-meta-find-parent-matching-token
|
||||
parent-token #'phpinspect-namespace-p)
|
||||
namespace-name (phpinspect-namespace-name namespace))
|
||||
;; Add use statements for types that aren't imported or already referenced
|
||||
;; with a fully qualified name.
|
||||
(unless (or (or (alist-get type imports))
|
||||
(gethash (phpinspect-intern-name
|
||||
(concat namespace-name "\\" (phpinspect-name-string type)))
|
||||
(phpinspect-autoloader-types
|
||||
(phpinspect-project-autoload project))))
|
||||
(phpinspect-add-use-interactive type buffer project namespace)
|
||||
(phpinspect-buffer-parse buffer 'no-interrupt)))))
|
||||
|
||||
(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* ((buffer phpinspect-current-buffer)
|
||||
(tree (phpinspect-buffer-parse buffer))
|
||||
(index (phpinspect--index-tokens
|
||||
tree nil (phpinspect-buffer-location-resolver buffer)))
|
||||
(classes (alist-get 'classes index))
|
||||
(imports (alist-get 'imports index))
|
||||
(project (phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
(phpinspect-current-project-root)))
|
||||
(used-types (alist-get 'used-types index)))
|
||||
|
||||
(phpinspect-add-use-statements-for-missing-types
|
||||
used-types buffer imports project (phpinspect-buffer-root-meta buffer))
|
||||
|
||||
(dolist (class classes)
|
||||
(let* ((class-imports (alist-get 'imports class))
|
||||
(used-types (alist-get 'used-types class))
|
||||
(class-name (alist-get 'class-name class))
|
||||
(region (alist-get 'location class))
|
||||
token-meta)
|
||||
(setq token-meta (phpinspect-meta-find-parent-matching-token
|
||||
(phpinspect-bmap-last-token-before-point
|
||||
(phpinspect-buffer-map buffer)
|
||||
(+ (phpinspect-region-start region) 1))
|
||||
#'phpinspect-class-p))
|
||||
(unless token-meta
|
||||
(error "Unable to find token for class %s" class-name))
|
||||
|
||||
(phpinspect-add-use-statements-for-missing-types
|
||||
used-types buffer (append imports class-imports) project token-meta))))))
|
||||
|
||||
(provide 'phpinspect-imports)
|
@ -0,0 +1,853 @@
|
||||
#!/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 "$@"
|
@ -1,519 +0,0 @@
|
||||
;;; phpinspect-index.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'cl-lib)
|
||||
(require 'phpinspect-util)
|
||||
(require 'phpinspect-type)
|
||||
(require 'phpinspect-token-predicates)
|
||||
(require 'phpinspect-parser)
|
||||
|
||||
(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--index-function-arg-list (type-resolver arg-list &optional add-used-types)
|
||||
(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)
|
||||
(when add-used-types (funcall add-used-types (list (cadr current-token)))))
|
||||
((phpinspect-variable-p (car arg-list))
|
||||
(push `(,(cadr (pop arg-list))
|
||||
nil)
|
||||
arg-index))))
|
||||
(nreverse arg-index)))
|
||||
|
||||
(defsubst phpinspect--should-prefer-return-annotation (type)
|
||||
"Returns non-nil if return annotation should supersede typehint
|
||||
of TYPE, if available."
|
||||
(or (not type)
|
||||
(phpinspect--type= type phpinspect--object-type)))
|
||||
|
||||
(defun phpinspect--index-function-declaration (declaration type-resolver add-used-types)
|
||||
(let (current name function-args return-type)
|
||||
(catch 'break
|
||||
(while (setq current (pop declaration))
|
||||
(cond ((and (phpinspect-word-p current)
|
||||
(phpinspect-word-p (car declaration))
|
||||
(string= "function" (cadr current)))
|
||||
(setq name (cadr (pop declaration))))
|
||||
((phpinspect-list-p current)
|
||||
(setq function-args
|
||||
(phpinspect--index-function-arg-list
|
||||
type-resolver current add-used-types))
|
||||
|
||||
(when (setq return-type (seq-find #'phpinspect-word-p declaration))
|
||||
(setq return-type (funcall type-resolver
|
||||
(phpinspect--make-type :name (cadr return-type)))))
|
||||
|
||||
(throw 'break nil)))))
|
||||
|
||||
(list name function-args return-type)))
|
||||
|
||||
(defun phpinspect--index-function-from-scope (type-resolver scope comment-before &optional add-used-types namespace)
|
||||
"Index a function inside SCOPE token using phpdoc metadata in COMMENT-BEFORE.
|
||||
|
||||
If ADD-USED-TYPES is set, it must be a function and will be
|
||||
called with a list of the types that are used within the
|
||||
function (think \"new\" statements, return types etc.)."
|
||||
(phpinspect--log "Indexing function")
|
||||
(let* ((php-func (cadr scope))
|
||||
(declaration (cadr php-func))
|
||||
name type arguments)
|
||||
|
||||
(pcase-setq `(,name ,arguments ,type)
|
||||
(phpinspect--index-function-declaration
|
||||
declaration type-resolver add-used-types))
|
||||
|
||||
;; FIXME: Anonymous functions should not be indexed! (or if they are, they
|
||||
;; should at least not be visible from various UIs unless assigned to a
|
||||
;; variable as a closure).
|
||||
(unless name (setq name "anonymous"))
|
||||
|
||||
(phpinspect--log "Checking function return annotations")
|
||||
|
||||
;; @return annotation. When dealing with a collection, we want to store the
|
||||
;; type of its members.
|
||||
(let* ((return-annotation-type
|
||||
(cadadr (seq-find #'phpinspect-return-annotation-p comment-before)))
|
||||
(is-collection
|
||||
(and type
|
||||
(phpinspect--type-is-collection type))))
|
||||
(phpinspect--log "found return annotation %s in %s when type is %s"
|
||||
return-annotation-type comment-before type)
|
||||
|
||||
(unless (stringp return-annotation-type)
|
||||
(phpinspect--log "Discarding invalid return annotation type %s" return-annotation-type)
|
||||
(setq return-annotation-type nil))
|
||||
|
||||
(when return-annotation-type
|
||||
(when (string-suffix-p "[]" return-annotation-type)
|
||||
(setq is-collection t)
|
||||
(setq return-annotation-type (string-trim-right return-annotation-type "\\[\\]")))
|
||||
|
||||
(cond ((phpinspect--should-prefer-return-annotation 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)))))
|
||||
|
||||
(when add-used-types
|
||||
(let ((used-types (phpinspect--find-used-types-in-tokens
|
||||
`(,(seq-find #'phpinspect-block-p php-func)))))
|
||||
(when type (push (phpinspect--type-bare-name type) used-types))
|
||||
(funcall add-used-types used-types)))
|
||||
|
||||
(phpinspect--log "Creating function object")
|
||||
(phpinspect--make-function
|
||||
:scope `(,(car scope))
|
||||
:token php-func
|
||||
:name (concat (if namespace (concat namespace "\\") "") name)
|
||||
:return-type (or type phpinspect--null-type)
|
||||
:arguments arguments)))
|
||||
|
||||
(define-inline phpinspect--safe-cadr (list)
|
||||
(inline-letevals (list)
|
||||
(inline-quote
|
||||
(when (listp ,list) (cadr ,list)))))
|
||||
|
||||
(defun phpinspect--index-const-from-scope (scope)
|
||||
(phpinspect--make-variable
|
||||
:scope `(,(car scope))
|
||||
:mutability `(,(caadr scope))
|
||||
:name (phpinspect--safe-cadr (phpinspect--safe-cadr (phpinspect--safe-cadr scope)))))
|
||||
|
||||
(defun phpinspect--var-annotations-from-token (token)
|
||||
(seq-filter #'phpinspect-var-annotation-p token))
|
||||
|
||||
(defun phpinspect--variable-type-string-from-comment (comment variable-name)
|
||||
(let* ((var-annotations (phpinspect--var-annotations-from-token comment))
|
||||
(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))))))))
|
||||
;; If type is not a string, the annotation is probably invalid and we should
|
||||
;; return nil.
|
||||
(when (stringp type) type)))
|
||||
|
||||
(defun phpinspect--index-variable-from-scope (type-resolver scope comment-before &optional static)
|
||||
"Index the variable inside `scope`."
|
||||
(let* ((variable-name (cadr (cadr scope)))
|
||||
(type
|
||||
(phpinspect--variable-type-string-from-comment comment-before variable-name)))
|
||||
(phpinspect--log "calling resolver from index-variable-from-scope")
|
||||
(phpinspect--make-variable
|
||||
;; Static class variables are always prefixed with dollar signs when
|
||||
;; referenced.
|
||||
:name (if static (concat "$" variable-name) variable-name)
|
||||
:scope `(,(car scope))
|
||||
:lifetime (when static '(:static))
|
||||
:type (if type (funcall type-resolver (phpinspect--make-type :name type))))))
|
||||
|
||||
(defun phpinspect-doc-block-p (token)
|
||||
(phpinspect-token-type-p token :doc-block))
|
||||
|
||||
|
||||
(defsubst phpinspect--index-method-annotations (type-resolver comment)
|
||||
(let ((annotations (seq-filter #'phpinspect-method-annotation-p comment))
|
||||
(methods))
|
||||
(dolist (annotation annotations)
|
||||
(let ((return-type) (name) (arg-list))
|
||||
(when (> (length annotation) 2)
|
||||
(cond ((and (phpinspect-word-p (nth 1 annotation))
|
||||
(phpinspect-word-p (nth 2 annotation))
|
||||
(phpinspect-list-p (nth 3 annotation)))
|
||||
(setq return-type (cadr (nth 1 annotation)))
|
||||
(setq name (cadr (nth 2 annotation)))
|
||||
(setq arg-list (nth 3 annotation)))
|
||||
((and (phpinspect-word-p (nth 1 annotation))
|
||||
(phpinspect-list-p (nth 2 annotation)))
|
||||
(setq return-type "void")
|
||||
(setq name (cadr (nth 1 annotation)))
|
||||
(setq arg-list (nth 2 annotation))))
|
||||
|
||||
(when name
|
||||
(push (phpinspect--make-function
|
||||
:scope '(:public)
|
||||
:name name
|
||||
:return-type (funcall type-resolver (phpinspect--make-type :name return-type))
|
||||
:arguments (phpinspect--index-function-arg-list type-resolver arg-list))
|
||||
methods)))))
|
||||
methods))
|
||||
|
||||
(defun phpinspect--index-class (imports type-resolver location-resolver class &optional doc-block)
|
||||
"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)
|
||||
;; Keep track of encountered comments to be able to use type
|
||||
;; annotations.
|
||||
(comment-before)
|
||||
;; The types that are used within the code of this class' methods.
|
||||
(used-types)
|
||||
(add-used-types))
|
||||
(setq add-used-types
|
||||
(lambda (additional-used-types)
|
||||
(if used-types
|
||||
(nconc used-types additional-used-types)
|
||||
(setq used-types additional-used-types))))
|
||||
|
||||
(pcase-setq `(,class-name ,extends ,implements ,used-types)
|
||||
(phpinspect--index-class-declaration (cadr class) type-resolver))
|
||||
|
||||
|
||||
(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
|
||||
add-used-types)
|
||||
static-methods))
|
||||
|
||||
((phpinspect-variable-p (cadadr token))
|
||||
(push (phpinspect--index-variable-from-scope type-resolver
|
||||
(list (car token)
|
||||
(cadadr token))
|
||||
comment-before
|
||||
'static)
|
||||
static-variables))))
|
||||
(t
|
||||
(phpinspect--log "comment-before is: %s" comment-before)
|
||||
(push (phpinspect--index-function-from-scope type-resolver
|
||||
token
|
||||
comment-before
|
||||
add-used-types)
|
||||
methods))))
|
||||
((phpinspect-static-p token)
|
||||
(cond ((phpinspect-function-p (cadr token))
|
||||
(push (phpinspect--index-function-from-scope type-resolver
|
||||
`(:public
|
||||
,(cadr token))
|
||||
comment-before
|
||||
add-used-types)
|
||||
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
|
||||
add-used-types)
|
||||
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))))))))
|
||||
|
||||
;; Add method annotations to methods
|
||||
(when doc-block
|
||||
(setq methods
|
||||
(nconc methods (phpinspect--index-method-annotations type-resolver doc-block))))
|
||||
|
||||
`(,class-name .
|
||||
(phpinspect--indexed-class
|
||||
(complete . ,(not (phpinspect-incomplete-class-p class)))
|
||||
(class-name . ,class-name)
|
||||
(declaration . ,(seq-find #'phpinspect-declaration-p class))
|
||||
(location . ,(funcall location-resolver class))
|
||||
(imports . ,imports)
|
||||
(methods . ,methods)
|
||||
(static-methods . ,static-methods)
|
||||
(static-variables . ,static-variables)
|
||||
(variables . ,variables)
|
||||
(constants . ,constants)
|
||||
(extends . ,extends)
|
||||
(implements . ,implements)
|
||||
(used-types . ,(mapcar #'phpinspect-intern-name
|
||||
(seq-uniq used-types #'string=)))))))
|
||||
|
||||
(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-in-tokens
|
||||
(imports tokens type-resolver-factory location-resolver &optional namespace)
|
||||
"Index the class tokens among TOKENS.
|
||||
|
||||
NAMESPACE will be assumed the root namespace if not provided"
|
||||
(let ((comment-before)
|
||||
(indexed))
|
||||
(dolist (token tokens)
|
||||
(cond ((phpinspect-doc-block-p token)
|
||||
(setq comment-before token))
|
||||
((phpinspect-class-p token)
|
||||
(push (phpinspect--index-class
|
||||
imports (funcall type-resolver-factory imports token namespace)
|
||||
location-resolver token comment-before)
|
||||
indexed)
|
||||
(setq comment-before nil))))
|
||||
indexed))
|
||||
|
||||
(defun phpinspect--index-namespace (namespace type-resolver-factory location-resolver)
|
||||
(let* (used-types
|
||||
(index
|
||||
`((classes . ,(phpinspect--index-classes-in-tokens
|
||||
(phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
|
||||
namespace
|
||||
type-resolver-factory location-resolver (cadadr namespace)))
|
||||
(functions . ,(phpinspect--index-functions-in-tokens
|
||||
namespace
|
||||
type-resolver-factory
|
||||
(phpinspect--uses-to-types (seq-filter #'phpinspect-use-p namespace))
|
||||
(cadadr namespace)
|
||||
(lambda (types) (setq used-types (nconc used-types types))))))))
|
||||
|
||||
(push `(used-types . ,used-types) index)))
|
||||
|
||||
(defun phpinspect--index-namespaces
|
||||
(namespaces type-resolver-factory location-resolver &optional indexed)
|
||||
(if namespaces
|
||||
(let ((namespace-index
|
||||
(phpinspect--index-namespace
|
||||
(pop namespaces) type-resolver-factory location-resolver)))
|
||||
|
||||
(if indexed
|
||||
(progn
|
||||
(nconc (alist-get 'used-types indexed)
|
||||
(alist-get 'used-types namespace-index))
|
||||
(nconc (alist-get 'classes indexed)
|
||||
(alist-get 'classes namespace-index))
|
||||
(nconc (alist-get 'functions indexed)
|
||||
(alist-get 'functions namespace-index)))
|
||||
(setq indexed namespace-index))
|
||||
(phpinspect--index-namespaces
|
||||
namespaces type-resolver-factory location-resolver indexed))
|
||||
indexed))
|
||||
|
||||
(defun phpinspect--index-functions-in-tokens (tokens type-resolver-factory &optional imports namespace add-used-types)
|
||||
"TODO: implement function indexation. This is a stub function."
|
||||
(let ((type-resolver (funcall type-resolver-factory imports nil namespace))
|
||||
comment-before functions)
|
||||
(dolist (token tokens)
|
||||
(cond ((phpinspect-comment-p token)
|
||||
(setq comment-before token))
|
||||
((phpinspect-function-p token)
|
||||
(push (phpinspect--index-function-from-scope
|
||||
type-resolver `(:public ,token) comment-before add-used-types
|
||||
namespace)
|
||||
functions))))
|
||||
functions))
|
||||
|
||||
(defun phpinspect--find-used-types-in-tokens (tokens)
|
||||
"Find usage of the \"new\" keyword in TOKENS.
|
||||
|
||||
Return value is a list of the types that are \"newed\"."
|
||||
(let* ((previous-tokens)
|
||||
(used-types (cons nil nil))
|
||||
(used-types-rear used-types))
|
||||
(while tokens
|
||||
(let ((token (pop tokens))
|
||||
(previous-token (car previous-tokens)))
|
||||
(cond ((and (phpinspect-word-p previous-token)
|
||||
(string= "new" (cadr previous-token))
|
||||
(phpinspect-word-p token))
|
||||
(let ((type (cadr token)))
|
||||
(when (not (string-match-p "\\\\" type))
|
||||
(setq used-types-rear (setcdr used-types-rear (cons type nil))))))
|
||||
((and (phpinspect-static-attrib-p token)
|
||||
(phpinspect-word-p previous-token))
|
||||
(let ((type (cadr previous-token)))
|
||||
(when (not (string-match-p "\\\\" type))
|
||||
(setq used-types-rear (setcdr used-types-rear (cons type nil))))))
|
||||
((phpinspect-object-attrib-p token)
|
||||
(let ((lists (seq-filter #'phpinspect-list-p token)))
|
||||
(dolist (list lists)
|
||||
(setq used-types-rear
|
||||
(nconc used-types-rear
|
||||
(phpinspect--find-used-types-in-tokens (cdr list)))
|
||||
used-types-rear (last used-types-rear)))))
|
||||
((or (phpinspect-list-p token) (phpinspect-block-p token))
|
||||
(setq used-types-rear
|
||||
(nconc used-types-rear (phpinspect--find-used-types-in-tokens (cdr token)))
|
||||
used-types-rear (last used-types-rear))))
|
||||
(push token previous-tokens)))
|
||||
(cdr used-types)))
|
||||
|
||||
(defun phpinspect--index-tokens (tokens &optional type-resolver-factory location-resolver)
|
||||
"Index TOKENS as returned by `phpinspect--parse-current-buffer`."
|
||||
(or
|
||||
(condition-case-unless-debug err
|
||||
(progn
|
||||
(unless type-resolver-factory
|
||||
(setq type-resolver-factory #'phpinspect--make-type-resolver))
|
||||
|
||||
(unless location-resolver
|
||||
(setq location-resolver (lambda (_) (list 0 0))))
|
||||
|
||||
(let* ((imports (phpinspect--uses-to-types (seq-filter #'phpinspect-use-p tokens)))
|
||||
(namespace-index
|
||||
(phpinspect--index-namespaces (seq-filter #'phpinspect-namespace-p tokens)
|
||||
type-resolver-factory
|
||||
location-resolver)))
|
||||
`(phpinspect--root-index
|
||||
(imports . ,imports)
|
||||
(classes ,@(append
|
||||
(alist-get 'classes namespace-index)
|
||||
(phpinspect--index-classes-in-tokens
|
||||
imports tokens type-resolver-factory location-resolver)))
|
||||
(used-types ,@(mapcar #'phpinspect-intern-name
|
||||
(seq-uniq
|
||||
(append
|
||||
(alist-get 'used-types namespace-index)
|
||||
(phpinspect--find-used-types-in-tokens tokens))
|
||||
#'string=)))
|
||||
(functions . ,(append
|
||||
(alist-get 'functions namespace-index)
|
||||
(phpinspect--index-functions-in-tokens
|
||||
tokens type-resolver-factory imports))))))
|
||||
(t
|
||||
(phpinspect--log "phpinspect--index-tokens failed: %s. Enable debug-on-error for backtrace." err)
|
||||
nil))
|
||||
'(phpinspect--root-index)))
|
||||
|
||||
(cl-defmethod phpinspect-index-get-class
|
||||
((index (head phpinspect--root-index)) (class-name phpinspect--type))
|
||||
(alist-get class-name (alist-get 'classes index)
|
||||
nil nil #'phpinspect--type=))
|
||||
|
||||
(defun phpinspect-index-current-buffer ()
|
||||
"Index a PHP file for classes and the methods they have"
|
||||
(phpinspect--index-tokens (phpinspect-parse-current-buffer)))
|
||||
|
||||
(provide 'phpinspect-index)
|
||||
;;; phpinspect-index.el ends here
|
@ -1,242 +0,0 @@
|
||||
;;; phpinspect-meta.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-splayt)
|
||||
|
||||
(define-inline phpinspect-make-meta
|
||||
(parent start end whitespace-before token &optional overlay children parent-offset)
|
||||
(inline-quote (list 'meta ,parent ,start ,end ,whitespace-before ,token ,overlay
|
||||
;;,children ,parent-offset)))
|
||||
(or ,children (phpinspect-make-splayt)) ,parent-offset)))
|
||||
|
||||
(define-inline phpinspect-meta-parent (meta)
|
||||
(inline-quote (cadr ,meta)))
|
||||
|
||||
(define-inline phpinspect-meta-children (meta)
|
||||
(inline-quote (car (nthcdr 7 ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-parent-offset (meta)
|
||||
(inline-quote (car (nthcdr 8 ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-overlay (meta)
|
||||
(inline-quote (car (nthcdr 6 ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-token (meta)
|
||||
(inline-quote (car (nthcdr 5 ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-absolute-end (meta)
|
||||
(inline-quote (cadddr ,meta)))
|
||||
|
||||
(define-inline phpinspect-meta-whitespace-before (meta)
|
||||
(inline-quote (car (cddddr ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-parent-start (meta)
|
||||
"Calculate parent start position iteratively based on parent offsets."
|
||||
(inline-letevals (meta)
|
||||
(inline-quote
|
||||
(let ((start (or (phpinspect-meta-parent-offset ,meta) 0))
|
||||
(current ,meta))
|
||||
(while (phpinspect-meta-parent current)
|
||||
(setq current (phpinspect-meta-parent current)
|
||||
start (+ start (or (phpinspect-meta-parent-offset current) 0))))
|
||||
|
||||
(+ (phpinspect-meta-absolute-start current) start)))))
|
||||
|
||||
(define-inline phpinspect-meta-start (meta)
|
||||
"Calculate the start position of META."
|
||||
(inline-quote
|
||||
(if (phpinspect-meta-parent ,meta)
|
||||
(+ (phpinspect-meta-parent-start (phpinspect-meta-parent ,meta))
|
||||
(phpinspect-meta-parent-offset ,meta))
|
||||
(phpinspect-meta-absolute-start ,meta))))
|
||||
|
||||
(define-inline phpinspect-meta-width (meta)
|
||||
(inline-letevals (meta)
|
||||
(inline-quote
|
||||
(- (phpinspect-meta-absolute-end ,meta) (phpinspect-meta-absolute-start ,meta)))))
|
||||
|
||||
(define-inline phpinspect-meta-end (meta)
|
||||
(inline-letevals (meta)
|
||||
(inline-quote
|
||||
(+ (phpinspect-meta-start ,meta) (phpinspect-meta-width ,meta)))))
|
||||
|
||||
(defsubst phpinspect-meta-find-root (meta)
|
||||
(while (phpinspect-meta-parent meta)
|
||||
(setq meta (phpinspect-meta-parent meta)))
|
||||
meta)
|
||||
|
||||
(defun phpinspect-meta-sort-width (meta1 meta2)
|
||||
(< (phpinspect-meta-width meta1) (phpinspect-meta-width meta2)))
|
||||
|
||||
(defun phpinspect-meta-sort-start (meta1 meta2)
|
||||
(< (phpinspect-meta-start meta1) (phpinspect-meta-start meta2)))
|
||||
|
||||
(define-inline phpinspect-meta-absolute-start (meta)
|
||||
(inline-quote (caddr ,meta)))
|
||||
|
||||
(define-inline phpinspect-meta-overlaps-point (meta point)
|
||||
"Check if META's region overlaps POINT."
|
||||
(inline-letevals (point meta)
|
||||
(inline-quote
|
||||
(and (> (phpinspect-meta-end ,meta) ,point)
|
||||
(<= (phpinspect-meta-start ,meta) ,point)))))
|
||||
|
||||
(defun phpinspect-meta-find-parent-matching-token (meta predicate)
|
||||
(if (funcall predicate (phpinspect-meta-token meta))
|
||||
meta
|
||||
(catch 'found
|
||||
(while (phpinspect-meta-parent meta)
|
||||
(setq meta (phpinspect-meta-parent meta))
|
||||
(when (funcall predicate (phpinspect-meta-token meta))
|
||||
(throw 'found meta))))))
|
||||
|
||||
(define-inline phpinspect-meta-set-parent (meta parent)
|
||||
(inline-letevals (meta parent)
|
||||
(inline-quote
|
||||
(progn
|
||||
(when ,parent
|
||||
(setf (phpinspect-meta-parent-offset ,meta)
|
||||
(- (phpinspect-meta-start ,meta) (phpinspect-meta-start ,parent)))
|
||||
(phpinspect-meta-add-child ,parent ,meta))
|
||||
(setcar (cdr ,meta) ,parent)
|
||||
|
||||
,meta))))
|
||||
|
||||
;; Note: using defsubst here causes a byte code overflow
|
||||
(defun phpinspect-meta-add-child (meta child)
|
||||
(phpinspect-splayt-insert (phpinspect-meta-children meta) (phpinspect-meta-parent-offset child) child))
|
||||
|
||||
(define-inline phpinspect-meta-detach-parent (meta)
|
||||
(inline-letevals (meta)
|
||||
(inline-quote
|
||||
(when (phpinspect-meta-parent ,meta)
|
||||
;; Update absolute start and end
|
||||
(setf (phpinspect-meta-absolute-end ,meta) (phpinspect-meta-end ,meta))
|
||||
;; Note: start should always be updated after end, as end is derived from
|
||||
;; it.
|
||||
(setf (phpinspect-meta-absolute-start ,meta) (phpinspect-meta-start ,meta))
|
||||
(setf (phpinspect-meta-parent ,meta) nil)))))
|
||||
|
||||
(defun phpinspect-meta-shift (meta delta)
|
||||
(setf (phpinspect-meta-absolute-start meta) (+ (phpinspect-meta-start meta) delta))
|
||||
(setf (phpinspect-meta-absolute-end meta) (+ (phpinspect-meta-end meta) delta)))
|
||||
|
||||
(defun phpinspect-meta-right-siblings (meta)
|
||||
(sort
|
||||
(phpinspect-splayt-find-all-after
|
||||
(phpinspect-meta-children (phpinspect-meta-parent meta)) (phpinspect-meta-parent-offset meta))
|
||||
#'phpinspect-meta-sort-start))
|
||||
|
||||
(defun phpinspect-meta-wrap-token-pred (predicate)
|
||||
(lambda (meta) (funcall predicate (phpinspect-meta-token meta))))
|
||||
|
||||
(define-inline phpinspect-meta--point-offset (meta point)
|
||||
(inline-quote
|
||||
(- ,point (phpinspect-meta-start ,meta))))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-left-sibling ((meta (head meta)))
|
||||
(when (phpinspect-meta-parent meta)
|
||||
(phpinspect-splayt-find-largest-before (phpinspect-meta-children (phpinspect-meta-parent meta))
|
||||
(phpinspect-meta-parent-offset meta))))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-right-sibling ((meta (head meta)))
|
||||
(when (phpinspect-meta-parent meta)
|
||||
(phpinspect-splayt-find-smallest-after (phpinspect-meta-children (phpinspect-meta-parent meta))
|
||||
(phpinspect-meta-parent-offset meta))))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-overlapping-child ((meta (head meta)) (point integer))
|
||||
(let ((child (phpinspect-splayt-find-largest-before
|
||||
(phpinspect-meta-children meta)
|
||||
;; Use point +1 as a child starting at point still overlaps
|
||||
(+ (phpinspect-meta--point-offset meta point) 1))))
|
||||
(when (and child (phpinspect-meta-overlaps-point child point))
|
||||
child)))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-overlapping-children ((meta (head meta)) (point integer))
|
||||
(let ((child meta)
|
||||
children)
|
||||
(while (and (setq child (phpinspect-meta-find-overlapping-child child point))
|
||||
(phpinspect-meta-overlaps-point child point))
|
||||
(push child children))
|
||||
children))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-child-starting-at ((meta (head meta)) (point integer))
|
||||
(phpinspect-splayt-find (phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point)))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-child-starting-at-recursively ((meta (head meta)) (point integer))
|
||||
(let ((child (phpinspect-meta-find-child-starting-at meta point)))
|
||||
(if child
|
||||
child
|
||||
(setq child (phpinspect-meta-find-overlapping-child meta point))
|
||||
(when child
|
||||
(phpinspect-meta-find-child-starting-at-recursively child point)))))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-child-before ((meta (head meta)) (point integer))
|
||||
(phpinspect-splayt-find-largest-before
|
||||
(phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point)))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-child-after ((meta (head meta)) (point integer))
|
||||
(phpinspect-splayt-find-smallest-after
|
||||
(phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point)))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-child-before-recursively ((meta (head meta)) (point integer))
|
||||
(let ((child meta)
|
||||
last)
|
||||
(while (setq child (phpinspect-meta-find-child-before child point))
|
||||
(setq last child))
|
||||
last))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-children-after ((meta (head meta)) (point integer))
|
||||
(sort
|
||||
(phpinspect-splayt-find-all-after
|
||||
(phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point))
|
||||
#'phpinspect-meta-sort-start))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-children-before ((meta (head meta)) (point integer))
|
||||
(sort (phpinspect-splayt-find-all-before
|
||||
(phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point))
|
||||
#'phpinspect-meta-sort-start))
|
||||
|
||||
(cl-defmethod phpinspect-meta-find-first-child-matching ((meta (head meta)) predicate)
|
||||
(catch 'return
|
||||
(phpinspect-splayt-traverse-lr (child (phpinspect-meta-children meta))
|
||||
(when (funcall predicate child)
|
||||
(throw 'return child)))))
|
||||
|
||||
(cl-defmethod phpinspect-meta-last-child ((meta (head meta)))
|
||||
(phpinspect-meta-find-child-before meta (phpinspect-meta-end meta)))
|
||||
|
||||
(cl-defmethod phpinspect-meta-first-child ((meta (head meta)))
|
||||
(phpinspect-meta-find-child-after meta (- (phpinspect-meta-start meta) 1)))
|
||||
|
||||
|
||||
(defun phpinspect-meta-string (meta)
|
||||
(if meta
|
||||
(format "[start: %d, end: %d, token: %s]"
|
||||
(phpinspect-meta-start meta) (phpinspect-meta-end meta) (phpinspect-meta-token meta))
|
||||
"[nil]"))
|
||||
|
||||
(provide 'phpinspect-meta)
|
||||
;;; phpinspect-meta.el ends here
|
@ -1,138 +0,0 @@
|
||||
;;; phpinspect-parse-context.el --- PHP parsing context module -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-meta)
|
||||
(require 'phpinspect-changeset)
|
||||
|
||||
(defvar phpinspect-parse-context nil
|
||||
"An instance of `phpinspect-pctx' that is used when
|
||||
parsing. Usually used in combination with
|
||||
`phpinspect-with-parse-context'")
|
||||
|
||||
(cl-defstruct (phpinspect-pctx (:constructor phpinspect-make-pctx))
|
||||
"Parser Context"
|
||||
(incremental nil)
|
||||
(meta-iterator nil)
|
||||
(interrupt-threshold (time-convert '(0 0 2000 0) t)
|
||||
:documentation
|
||||
"After how much time `interrupt-predicate'
|
||||
should be polled. This is 2ms by default.")
|
||||
(-start-time nil
|
||||
:documentation "The time at which the parse started.
|
||||
This variable is for private use and not always set.")
|
||||
(interrupt-predicate nil
|
||||
:documentation
|
||||
"A function that is called in intervals during parsing when
|
||||
set. If this function returns a non-nil value, the parse process
|
||||
is interrupted and the symbol `phpinspect-parse-interrupted' is
|
||||
thrown.")
|
||||
(changesets nil
|
||||
:type list
|
||||
:documentation "Metadata change sets executed during this parse")
|
||||
(edtrack nil
|
||||
:type phpinspect-edtrack)
|
||||
(bmap nil
|
||||
:type phpinspect-bmap)
|
||||
(previous-bmap nil
|
||||
:type phpinspect-bmap)
|
||||
(whitespace-before ""
|
||||
:type string))
|
||||
|
||||
(defmacro phpinspect-with-parse-context (ctx &rest body)
|
||||
(declare (indent 1))
|
||||
(let ((old-ctx (gensym))
|
||||
(completed (gensym))
|
||||
(result (gensym)))
|
||||
`(let ((,old-ctx phpinspect-parse-context)
|
||||
(,result)
|
||||
(,completed))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(setq phpinspect-parse-context ,ctx
|
||||
,result (progn ,@body)
|
||||
,completed t)
|
||||
,result)
|
||||
(progn
|
||||
(unless ,completed (phpinspect-pctx-cancel ,ctx))
|
||||
(setq phpinspect-parse-context ,old-ctx))))))
|
||||
(defmacro phpinspect-pctx-save-whitespace (pctx &rest body)
|
||||
(declare (indent 1))
|
||||
(let ((save-sym (gensym)))
|
||||
`(let ((,save-sym (phpinspect-pctx-whitespace-before ,pctx)))
|
||||
(unwind-protect
|
||||
(progn
|
||||
(setf (phpinspect-pctx-whitespace-before ,pctx) nil)
|
||||
,@body)
|
||||
(setf (phpinspect-pctx-whitespace-before ,pctx) ,save-sym)))))
|
||||
|
||||
(define-inline phpinspect-pctx-register-changeset (pctx changeset)
|
||||
(inline-quote
|
||||
(progn
|
||||
(push ,changeset (phpinspect-pctx-changesets ,pctx)))))
|
||||
|
||||
(define-inline phpinspect-meta-with-changeset (meta &rest body)
|
||||
(declare (indent 1))
|
||||
(inline-letevals (meta)
|
||||
(push 'progn body)
|
||||
(inline-quote
|
||||
(progn
|
||||
(when phpinspect-parse-context
|
||||
(phpinspect-pctx-register-changeset
|
||||
phpinspect-parse-context (phpinspect-make-changeset ,meta)))
|
||||
,body))))
|
||||
|
||||
|
||||
(define-inline phpinspect-pctx-check-interrupt (pctx)
|
||||
(inline-letevals (pctx)
|
||||
(inline-quote
|
||||
(progn
|
||||
(unless (phpinspect-pctx--start-time ,pctx)
|
||||
(setf (phpinspect-pctx--start-time ,pctx) (time-convert nil t)))
|
||||
|
||||
;; Interrupt when blocking too long while input is pending.
|
||||
(when (and (time-less-p (phpinspect-pctx-interrupt-threshold ,pctx)
|
||||
(time-since (phpinspect-pctx--start-time ,pctx)))
|
||||
(funcall (phpinspect-pctx-interrupt-predicate ,pctx)))
|
||||
(phpinspect-pctx-cancel ,pctx)
|
||||
(throw 'phpinspect-parse-interrupted nil))))))
|
||||
|
||||
(define-inline phpinspect-pctx-register-whitespace (pctx whitespace)
|
||||
(inline-quote
|
||||
(setf (phpinspect-pctx-whitespace-before ,pctx) ,whitespace)))
|
||||
|
||||
(defsubst phpinspect-pctx-consume-whitespace (pctx)
|
||||
(let ((whitespace (phpinspect-pctx-whitespace-before pctx)))
|
||||
(setf (phpinspect-pctx-whitespace-before pctx) "")
|
||||
whitespace))
|
||||
|
||||
(defun phpinspect-pctx-cancel (pctx)
|
||||
(phpinspect--log "Cancelling parse context")
|
||||
(dolist (changeset (phpinspect-pctx-changesets pctx))
|
||||
(phpinspect-changeset-revert changeset))
|
||||
(setf (phpinspect-pctx-changesets pctx) nil))
|
||||
|
||||
(provide 'phpinspect-parse-context)
|
||||
;;; phpinspect-parse-context.el ends here
|
@ -1,883 +0,0 @@
|
||||
;;; phpinspect-parser.el --- PHP parsing module -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'cl-lib)
|
||||
(require 'phpinspect-edtrack)
|
||||
(require 'phpinspect-bmap)
|
||||
(require 'phpinspect-meta)
|
||||
(require 'phpinspect-parse-context)
|
||||
(require 'phpinspect-token-predicates)
|
||||
|
||||
(eval-when-compile
|
||||
(define-inline phpinspect--word-end-regex ()
|
||||
(inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)")))
|
||||
|
||||
(define-inline phpinspect--strip-word-end-space (string)
|
||||
(inline-letevals (string)
|
||||
(inline-quote
|
||||
(progn
|
||||
(when phpinspect-parse-context
|
||||
(phpinspect-pctx-register-whitespace
|
||||
phpinspect-parse-context
|
||||
(substring ,string (- (length ,string) 1) (length ,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)))
|
||||
|
||||
(eval-and-compile
|
||||
(defun phpinspect-handler-func-name (handler-name)
|
||||
(intern (concat "phpinspect--" (symbol-name handler-name) "-handler")))
|
||||
|
||||
(defun phpinspect-handler-regexp-func-name (handler-name)
|
||||
(intern (concat "phpinspect--" (symbol-name handler-name) "-handler-regexp")))
|
||||
|
||||
(defun phpinspect-parser-func-name (name &optional suffix)
|
||||
(intern (concat "phpinspect--parse-" (symbol-name name) (if suffix (concat "-" suffix) "")))))
|
||||
|
||||
(defmacro phpinspect-defhandler (name arguments docstring attribute-alist &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-function` and equivalents. 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 automatically prefixed.
|
||||
|
||||
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-ALIST is an alist 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 (alist-get 'regexp attribute-alist))
|
||||
(error "In definition of phpinspect handler %s ATTRIBUTE-PLIST must contain key `regexp`"
|
||||
name))
|
||||
|
||||
(let ((regexp-inline-name (phpinspect-handler-regexp-func-name name))
|
||||
(inline-name (phpinspect-handler-func-name name)))
|
||||
`(progn
|
||||
(define-inline ,regexp-inline-name ()
|
||||
(inline-letevals ((regexp ,(alist-get 'regexp attribute-alist)))
|
||||
(inline-quote ,(quote ,regexp))))
|
||||
|
||||
(defsubst ,inline-name (,@arguments)
|
||||
,docstring
|
||||
,@body)
|
||||
|
||||
(put (quote ,inline-name) 'phpinspect--handler t))))
|
||||
|
||||
(eval-when-compile
|
||||
(defun phpinspect-make-parser-function (name tree-type handlers &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.
|
||||
|
||||
HANDLERS must be a list of symbols referring to existing
|
||||
parser handlers defined using `phpinspect-defhandler'.
|
||||
|
||||
DELIMITER-PREDICATE must be a function. It is passed the last
|
||||
parsed token after every handler iteration. If it returns
|
||||
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."
|
||||
(cl-assert (symbolp delimiter-predicate))
|
||||
`(defun ,(phpinspect-parser-func-name name "simple") (buffer max-point &optional skip-over continue-condition &rest _ignored)
|
||||
(with-current-buffer buffer
|
||||
(let* ((tokens (cons ,tree-type nil))
|
||||
(tokens-rear tokens)
|
||||
token)
|
||||
(when skip-over (forward-char skip-over))
|
||||
(while (and (< (point) max-point)
|
||||
(if continue-condition (funcall continue-condition) t)
|
||||
(not ,(if delimiter-predicate
|
||||
`(,delimiter-predicate (car (last tokens)))
|
||||
nil)))
|
||||
(cond ,@(mapcar
|
||||
(lambda (handler)
|
||||
`((looking-at (,(phpinspect-handler-regexp-func-name handler)))
|
||||
(setq token (,(phpinspect-handler-func-name handler) (match-string 0) max-point))
|
||||
(when token
|
||||
(setq tokens-rear (setcdr tokens-rear (cons token nil))))))
|
||||
handlers)
|
||||
(t (forward-char))))
|
||||
|
||||
;; Return
|
||||
tokens))))
|
||||
|
||||
|
||||
(defun phpinspect-make-incremental-parser-function (name tree-type handlers &optional delimiter-predicate)
|
||||
"Like `phpinspect-make-parser-function', but returned function
|
||||
is able to reuse an already parsed tree."
|
||||
(cl-assert (symbolp delimiter-predicate))
|
||||
`(defun ,(phpinspect-parser-func-name name "incremental") (context buffer max-point &optional skip-over continue-condition root)
|
||||
(with-current-buffer buffer
|
||||
(let* ((tokens (cons ,tree-type nil))
|
||||
(tokens-rear tokens)
|
||||
(root-start (point))
|
||||
(bmap (phpinspect-pctx-bmap context))
|
||||
(previous-bmap (phpinspect-pctx-previous-bmap context))
|
||||
(edtrack (phpinspect-pctx-edtrack context))
|
||||
(taint-iterator (when edtrack (phpinspect-edtrack-make-taint-iterator edtrack)))
|
||||
(check-interrupt (phpinspect-pctx-interrupt-predicate context))
|
||||
|
||||
;; Loop variables
|
||||
(start-position)
|
||||
(original-position)
|
||||
(current-end-position)
|
||||
(existing-meta)
|
||||
(delta)
|
||||
(token))
|
||||
(when skip-over (forward-char skip-over))
|
||||
(phpinspect-pctx-save-whitespace context
|
||||
(while (and (< (point) max-point)
|
||||
(if continue-condition (funcall continue-condition) t)
|
||||
(not ,(if delimiter-predicate
|
||||
`(,delimiter-predicate (car (last tokens)))
|
||||
nil)))
|
||||
(when check-interrupt
|
||||
(phpinspect-pctx-check-interrupt context))
|
||||
|
||||
(setq start-position (point))
|
||||
(cond ((and previous-bmap edtrack
|
||||
(setq existing-meta
|
||||
(phpinspect-bmap-token-starting-at
|
||||
previous-bmap
|
||||
(setq original-position
|
||||
(phpinspect-edtrack-original-position-at-point edtrack start-position))))
|
||||
(not (or (phpinspect-root-p (phpinspect-meta-token existing-meta))
|
||||
(phpinspect-taint-iterator-token-is-tainted-p taint-iterator existing-meta))))
|
||||
(setq delta (- start-position original-position)
|
||||
current-end-position (+ (phpinspect-meta-end existing-meta) delta)
|
||||
token (phpinspect-meta-token existing-meta))
|
||||
|
||||
;;(message "Reusing token %s at point %s" (phpinspect-meta-string existing-meta) (point))
|
||||
;; Re-register existing token
|
||||
(phpinspect-bmap-overlay
|
||||
bmap previous-bmap existing-meta delta
|
||||
(phpinspect-pctx-consume-whitespace context))
|
||||
|
||||
(goto-char current-end-position)
|
||||
|
||||
;; Skip over whitespace after so that we don't do a full
|
||||
;; run down all of the handlers during the next iteration
|
||||
(when (looking-at (phpinspect-handler-regexp whitespace))
|
||||
(,(phpinspect-handler-func-name 'whitespace) (match-string 0))))
|
||||
,@(mapcar
|
||||
(lambda (handler)
|
||||
`((looking-at (,(phpinspect-handler-regexp-func-name handler)))
|
||||
(setq token (,(phpinspect-handler-func-name handler) (match-string 0) max-point))
|
||||
(when token
|
||||
(phpinspect-pctx-register-token context token start-position (point)))))
|
||||
handlers)
|
||||
(t (forward-char)))
|
||||
(when token
|
||||
(setq tokens-rear (setcdr tokens-rear (cons token nil)))
|
||||
(setq token nil))))
|
||||
(when root
|
||||
(phpinspect-pctx-register-token context tokens root-start (point)))
|
||||
|
||||
;; Return
|
||||
tokens))))
|
||||
|
||||
(cl-defstruct (phpinspect-parser (:constructor phpinspect-make-parser))
|
||||
(name 'root
|
||||
:type symbol
|
||||
:read-only t)
|
||||
(tree-keyword "root"
|
||||
:type string
|
||||
:read-only t
|
||||
:documentation "Name of the keyword that is used as car of the
|
||||
root token, in string form without \":\" prefix.")
|
||||
(handlers '(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)
|
||||
:type list
|
||||
:read-only t
|
||||
:documentation "A list of symbols referring to the
|
||||
handlers that this parser uses.")
|
||||
(delimiter-predicate nil
|
||||
:type function
|
||||
:read-only t
|
||||
:documentation "A predicate function that is passed each
|
||||
parsed token. When the predicate returns a non-nil value, the parser stops
|
||||
executing.")
|
||||
(func nil
|
||||
:type function
|
||||
:documentation "The parser function.")
|
||||
(incremental-func nil
|
||||
:type function
|
||||
:documentation "Incemental parser function"))
|
||||
|
||||
(cl-defmethod phpinspect-parser-compile ((parser phpinspect-parser))
|
||||
"Create/return parser function."
|
||||
(or (phpinspect-parser-func parser)
|
||||
(setf (phpinspect-parser-func parser)
|
||||
(phpinspect-make-parser-function
|
||||
(phpinspect-parser-name parser)
|
||||
(intern (concat ":" (phpinspect-parser-tree-keyword parser)))
|
||||
(phpinspect-parser-handlers parser)
|
||||
(phpinspect-parser-delimiter-predicate parser)))))
|
||||
|
||||
(cl-defmethod phpinspect-parser-compile-incremental ((parser phpinspect-parser))
|
||||
"Like `phpinspect-parser-compile', but for an incremental
|
||||
version of the parser function."
|
||||
(or (phpinspect-parser-incremental-func parser)
|
||||
(setf (phpinspect-parser-incremental-func parser)
|
||||
(phpinspect-make-incremental-parser-function
|
||||
(phpinspect-parser-name parser)
|
||||
(intern (concat ":" (phpinspect-parser-tree-keyword parser)))
|
||||
(phpinspect-parser-handlers parser)
|
||||
(phpinspect-parser-delimiter-predicate parser)))))
|
||||
|
||||
(cl-defmethod phpinspect-parser-compile-entry ((parser phpinspect-parser))
|
||||
(let ((func-name (phpinspect-parser-func-name (phpinspect-parser-name parser)))
|
||||
(incremental-name (phpinspect-parser-func-name (phpinspect-parser-name parser) "incremental"))
|
||||
(simple-name (phpinspect-parser-func-name (phpinspect-parser-name parser) "simple")))
|
||||
`(defun ,func-name (buffer max-point &optional skip-over continue-condition root)
|
||||
"Parse BUFFER, starting at point and ending at MAX-POINT.
|
||||
|
||||
If SKIP-OVER is non-nil, it must be a number of characters that
|
||||
to skip over before starting to parse.
|
||||
|
||||
If CONTINUE-CONDITION is non-nil, it must be a function. It will
|
||||
be called after each parsed child token with the token as
|
||||
argument. If the return value is nil, parsing is stopped.
|
||||
|
||||
If ROOT is non-nil, this signals that there is no parent parser
|
||||
that will take care of registering metadata for the parser's
|
||||
returned token tree. So the parser should register the metadata
|
||||
of the root of its returned tree itself, before
|
||||
returning. Currently, token metadata is only registered when
|
||||
parsing incrementally."
|
||||
(if (and phpinspect-parse-context
|
||||
(phpinspect-pctx-incremental phpinspect-parse-context))
|
||||
(,incremental-name phpinspect-parse-context buffer max-point skip-over continue-condition root)
|
||||
(,simple-name buffer max-point skip-over continue-condition root)))))
|
||||
|
||||
) ;; End of eval-when-compile
|
||||
|
||||
(defmacro phpinspect-defparser (name &rest parameters)
|
||||
(declare (indent 1))
|
||||
(unless (symbolp name)
|
||||
(error "Name must be a symbol"))
|
||||
|
||||
(setq parameters (nconc parameters (list :name `(quote ,name))))
|
||||
|
||||
(let* ((func-name (phpinspect-parser-func-name name))
|
||||
(simple-name (phpinspect-parser-func-name name "simple"))
|
||||
(incremental-name (phpinspect-parser-func-name name "incremental")))
|
||||
|
||||
`(eval-when-compile
|
||||
(let ((parser (phpinspect-make-parser ,@parameters)))
|
||||
(defconst ,simple-name parser)
|
||||
(defconst ,incremental-name parser)
|
||||
|
||||
(put (quote ,simple-name) 'phpinspect--parser t)
|
||||
(put (quote ,incremental-name) 'phpinspect--incremental-parser t)
|
||||
|
||||
;; Stub function to please the byte compiler (real function will be
|
||||
;; defined by `phpinspect-define-parser-functions'.
|
||||
(defun ,func-name (_buffer _max-point &optional _skip-over _continue-condition _root))))))
|
||||
|
||||
(define-inline phpinspect-parser-func-bound-p (symbol)
|
||||
(inline-quote (get ,symbol 'phpinspect--parser)))
|
||||
|
||||
(define-inline phpinspect-incremental-parser-func-bound-p (symbol)
|
||||
(inline-quote (get ,symbol 'phpinspect--incremental-parser)))
|
||||
|
||||
(defun phpinspect-handler-bound-p (symbol)
|
||||
(get symbol 'phpinspect--handler))
|
||||
|
||||
(defmacro phpinspect-define-parser-functions ()
|
||||
(let (names incremental-names function-definitions)
|
||||
(obarray-map (lambda (sym)
|
||||
(cond ((phpinspect-parser-func-bound-p sym)
|
||||
(push sym names))
|
||||
((phpinspect-incremental-parser-func-bound-p sym)
|
||||
(push sym incremental-names))))
|
||||
obarray)
|
||||
|
||||
(dolist (name names)
|
||||
(push (phpinspect-parser-compile-entry (symbol-value name))
|
||||
function-definitions))
|
||||
|
||||
(dolist (name names)
|
||||
(push (phpinspect-parser-compile (symbol-value name))
|
||||
function-definitions))
|
||||
|
||||
(dolist (name incremental-names)
|
||||
(push (phpinspect-parser-compile-incremental (symbol-value name))
|
||||
function-definitions))
|
||||
|
||||
(push 'progn function-definitions)
|
||||
|
||||
function-definitions))
|
||||
|
||||
(phpinspect-defhandler comma (comma &rest _ignored)
|
||||
"Handler for comma tokens"
|
||||
((regexp . ","))
|
||||
(phpinspect-munch-token-without-attribs comma :comma))
|
||||
|
||||
(phpinspect-defhandler word (word &rest --length)
|
||||
"Handler for bareword tokens"
|
||||
((regexp . "[A-Za-z_\\][\\A-Za-z_0-9]*"))
|
||||
(setq --length (length word))
|
||||
(forward-char --length)
|
||||
(set-text-properties 0 --length nil word)
|
||||
(list :word word))
|
||||
|
||||
(defmacro phpinspect-handler-regexp (handler-name)
|
||||
(unless (symbolp handler-name)
|
||||
(error "handler-name must be a known value and a symbol at compile time, name"))
|
||||
|
||||
(let ((name (phpinspect-handler-regexp-func-name handler-name)))
|
||||
`(,name)))
|
||||
|
||||
(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-defparser list
|
||||
:tree-keyword "list"
|
||||
:handlers '(array tag equals list comma
|
||||
attribute-reference variable assignment-operator
|
||||
whitespace function-keyword word terminator here-doc
|
||||
string comment block-without-scopes))
|
||||
|
||||
(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 . "("))
|
||||
(let* ((complete-list nil)
|
||||
(php-list (phpinspect--parse-list
|
||||
(current-buffer)
|
||||
max-point
|
||||
(length start-token)
|
||||
(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))
|
||||
|
||||
(defsubst phpinspect--parse-annotation-parameters (parameter-amount)
|
||||
(let* ((words)
|
||||
(list-regexp (phpinspect-handler-regexp list))
|
||||
;; Return annotations may end with "[]" for collections.
|
||||
(word-regexp (concat (phpinspect-handler-regexp word) "\\(\\[\\]\\)?"))
|
||||
(variable-regexp (phpinspect-handler-regexp variable))
|
||||
(annotation-regexp (phpinspect-handler-regexp annotation)))
|
||||
(while (not (or (looking-at annotation-regexp)
|
||||
(= (point) (point-max))
|
||||
(= (length words) parameter-amount)))
|
||||
(cond ((looking-at list-regexp)
|
||||
(push (phpinspect--list-handler (match-string 0) (point-max)) words))
|
||||
((looking-at word-regexp)
|
||||
(push (phpinspect--word-handler (match-string 0)) words))
|
||||
((looking-at variable-regexp)
|
||||
(push (phpinspect--variable-handler (match-string 0)) words))
|
||||
(t (forward-char))))
|
||||
(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
|
||||
(cons :var-annotation
|
||||
(phpinspect--parse-annotation-parameters 2)))
|
||||
((string= annotation-name "return")
|
||||
;; The @return annotation only accepts 1 word as parameter:
|
||||
;; The return type
|
||||
(cons :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
|
||||
(cons :param-annotation
|
||||
(phpinspect--parse-annotation-parameters 2)))
|
||||
((string= annotation-name "method")
|
||||
(cons :method-annotation
|
||||
(phpinspect--parse-annotation-parameters 3)))
|
||||
(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-defparser doc-block
|
||||
:tree-keyword "doc-block"
|
||||
:handlers '(annotation whitespace))
|
||||
|
||||
(phpinspect-defparser comment
|
||||
:tree-keyword "comment"
|
||||
:handlers '(tag)
|
||||
:delimiter-predicate #'phpinspect-html-p)
|
||||
|
||||
(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* ((region-start (point))
|
||||
;; Move to the end of the comment region
|
||||
(region-end
|
||||
(progn
|
||||
(while (not (or (= max-point (point)) (looking-at "\\*/")))
|
||||
(forward-char))
|
||||
(point)))
|
||||
(doc-block (save-restriction
|
||||
(goto-char region-start)
|
||||
(narrow-to-region region-start region-end)
|
||||
(phpinspect--parse-doc-block (current-buffer) (point-max)))))
|
||||
(forward-char 2)
|
||||
doc-block))
|
||||
(t
|
||||
(let ((end-position (line-end-position)))
|
||||
(phpinspect--parse-comment (current-buffer) end-position)))))
|
||||
|
||||
|
||||
(phpinspect-defhandler class-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) :class-variable)
|
||||
(list :class-variable nil)))
|
||||
|
||||
|
||||
(phpinspect-defhandler whitespace (whitespace &rest _ignored)
|
||||
"Handler that discards whitespace"
|
||||
((regexp . "[[:blank:]\n]+"))
|
||||
(when phpinspect-parse-context
|
||||
(phpinspect-pctx-register-whitespace phpinspect-parse-context whitespace))
|
||||
(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-defparser use
|
||||
:tree-keyword "use"
|
||||
:handlers '(word tag block-without-scopes terminator)
|
||||
:delimiter-predicate #'phpinspect-end-of-use-p)
|
||||
|
||||
(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-word-end-space start-token))
|
||||
(forward-char (length start-token))
|
||||
(phpinspect--parse-use (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))
|
||||
(phpinspect--word-handler (match-string 0))
|
||||
nil)))
|
||||
(cond
|
||||
((string= start-token "::")
|
||||
(list :static-attrib name))
|
||||
((string= start-token "->")
|
||||
(list :object-attrib name)))))
|
||||
|
||||
(phpinspect-defparser namespace
|
||||
:tree-keyword "namespace"
|
||||
:delimiter-predicate #'phpinspect-block-p)
|
||||
|
||||
(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-word-end-space start-token))
|
||||
(forward-char (length start-token))
|
||||
(phpinspect--parse-namespace
|
||||
(current-buffer)
|
||||
max-point
|
||||
nil
|
||||
(lambda () (not (looking-at (phpinspect-handler-regexp namespace))))))
|
||||
|
||||
(phpinspect-defparser const
|
||||
:tree-keyword "const"
|
||||
:handlers '(word comment assignment-operator string array terminator)
|
||||
:delimiter-predicate #'phpinspect-end-of-token-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-word-end-space start-token))
|
||||
(forward-char (length start-token))
|
||||
|
||||
(setq start-token (phpinspect--parse-const (current-buffer) max-point))
|
||||
(when (phpinspect-incomplete-token-p (car (last start-token)))
|
||||
(setcar start-token :incomplete-const))
|
||||
start-token)
|
||||
|
||||
(phpinspect-defhandler string (start-token &rest _ignored)
|
||||
"Handler for strings"
|
||||
((regexp . "\\(\"\\|'\\)"))
|
||||
(list :string (phpinspect--munch-string start-token)))
|
||||
|
||||
(phpinspect-defparser block-without-scopes
|
||||
:tree-keyword "block"
|
||||
:handlers '(array tag equals list comma attribute-reference variable
|
||||
assignment-operator whitespace function-keyword word
|
||||
terminator here-doc string comment block-without-scopes))
|
||||
|
||||
(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 . "{"))
|
||||
(let* ((complete-block nil)
|
||||
(continue-condition (lambda ()
|
||||
(not (and (char-equal (char-after) ?})
|
||||
(setq complete-block t)))))
|
||||
(parsed (phpinspect--parse-block-without-scopes
|
||||
(current-buffer) max-point (length start-token) continue-condition 'root)))
|
||||
(if complete-block
|
||||
(forward-char)
|
||||
(setcar parsed :incomplete-block))
|
||||
parsed))
|
||||
|
||||
(phpinspect-defparser class-block
|
||||
:tree-keyword "block"
|
||||
:handlers '(array tag equals list comma attribute-reference class-variable
|
||||
assignment-operator whitespace scope-keyword static-keyword
|
||||
const-keyword use-keyword function-keyword word terminator
|
||||
here-doc string comment block))
|
||||
|
||||
(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)
|
||||
(continue-condition (lambda ()
|
||||
(not (and (char-equal (char-after) ?})
|
||||
(setq complete-block t)))))
|
||||
(parsed (phpinspect--parse-class-block
|
||||
(current-buffer) max-point (length start-token) continue-condition 'root)))
|
||||
(if complete-block
|
||||
(forward-char)
|
||||
(setcar parsed :incomplete-block))
|
||||
parsed))
|
||||
|
||||
(phpinspect-defparser block
|
||||
:tree-keyword "block")
|
||||
|
||||
(phpinspect-defhandler block (start-token max-point)
|
||||
"Handler for code blocks"
|
||||
((regexp . "{"))
|
||||
(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-block
|
||||
(current-buffer) max-point (length start-token) 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))
|
||||
|
||||
|
||||
(define-inline 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."
|
||||
(inline-letevals (start-token)
|
||||
(inline-quote
|
||||
(progn
|
||||
(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-defparser declaration
|
||||
:tree-keyword "declaration"
|
||||
:handlers '(comment word list terminator tag)
|
||||
:delimiter-predicate #'phpinspect-end-of-token-p)
|
||||
|
||||
;; TODO: Look into using different names for function and class :declaration tokens. They
|
||||
;; don't necessarily require the same handlers to parse.
|
||||
(define-inline phpinspect-parse-declaration (buffer max-point &optional continue-condition root)
|
||||
(inline-quote
|
||||
(let ((result (phpinspect--parse-declaration ,buffer ,max-point nil ,continue-condition ,root)))
|
||||
(if (phpinspect-terminator-p (car (last result)))
|
||||
(butlast result)
|
||||
result))))
|
||||
|
||||
(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-word-end-space start-token))
|
||||
(let* ((continue-condition (lambda () (not (or (char-equal (char-after) ?{)
|
||||
(char-equal (char-after) ?})))))
|
||||
(declaration (phpinspect-parse-declaration (current-buffer) max-point continue-condition 'root)))
|
||||
|
||||
(if (or (phpinspect-end-of-token-p (car (last declaration)))
|
||||
(not (looking-at (phpinspect-handler-regexp block))))
|
||||
(list :function declaration)
|
||||
(list :function
|
||||
declaration
|
||||
(phpinspect--block-without-scopes-handler
|
||||
(char-to-string (char-after)) max-point)))))
|
||||
|
||||
(phpinspect-defparser scope-public
|
||||
:tree-keyword "public"
|
||||
:handlers '(function-keyword static-keyword const-keyword class-variable here-doc
|
||||
string terminator tag comment)
|
||||
:delimiter-predicate #'phpinspect--scope-terminator-p)
|
||||
|
||||
(phpinspect-defparser scope-private
|
||||
:tree-keyword "private"
|
||||
:handlers '(function-keyword static-keyword const-keyword class-variable here-doc
|
||||
string terminator tag comment)
|
||||
:delimiter-predicate #'phpinspect--scope-terminator-p)
|
||||
|
||||
(phpinspect-defparser scope-protected
|
||||
:tree-keyword "protected"
|
||||
:handlers '(function-keyword static-keyword const-keyword class-variable here-doc
|
||||
string terminator tag comment)
|
||||
:delimiter-predicate #'phpinspect--scope-terminator-p)
|
||||
|
||||
(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-word-end-space start-token))
|
||||
(forward-char (length start-token))
|
||||
(cond ((string= start-token "public") (phpinspect--parse-scope-public (current-buffer) max-point))
|
||||
((string= start-token "private") (phpinspect--parse-scope-private (current-buffer) max-point))
|
||||
((string= start-token "protected") (phpinspect--parse-scope-protected (current-buffer) max-point))))
|
||||
|
||||
(phpinspect-defparser static
|
||||
:tree-keyword "static"
|
||||
:handlers '(comment function-keyword class-variable array word terminator tag)
|
||||
:delimiter-predicate #'phpinspect--static-terminator-p)
|
||||
|
||||
(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-word-end-space start-token))
|
||||
(forward-char (length start-token))
|
||||
(phpinspect--parse-static (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-defparser array
|
||||
:tree-keyword "array"
|
||||
:handlers '(comment comma list here-doc string array variable
|
||||
attribute-reference word fat-arrow))
|
||||
|
||||
(phpinspect-defhandler array (start-token max-point)
|
||||
"Handler for arrays, in the bracketet as well as the list notation"
|
||||
((regexp . "\\[\\|array("))
|
||||
(let* ((end-char (cond ((string= start-token "[") ?\])
|
||||
((string= start-token "array(") ?\))))
|
||||
(end-char-reached nil)
|
||||
(token (phpinspect--parse-array
|
||||
(current-buffer)
|
||||
max-point
|
||||
(length start-token)
|
||||
(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-word-end-space start-token))
|
||||
`(:class ,(phpinspect-parse-declaration
|
||||
(current-buffer)
|
||||
max-point
|
||||
(lambda () (not (char-equal (char-after) ?{)))
|
||||
'root)
|
||||
,@(when (looking-at (phpinspect--class-block-handler-regexp))
|
||||
(list (phpinspect--class-block-handler
|
||||
(char-to-string (char-after)) max-point)))))
|
||||
|
||||
(phpinspect-defparser root
|
||||
:tree-keyword "root"
|
||||
:handlers '(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))
|
||||
|
||||
(defun phpinspect-parse-current-buffer ()
|
||||
(phpinspect-parse-buffer-until-point
|
||||
(current-buffer)
|
||||
(point-max)))
|
||||
|
||||
(defun phpinspect-parse-string (string)
|
||||
(with-temp-buffer
|
||||
(insert string)
|
||||
(phpinspect-parse-current-buffer)))
|
||||
|
||||
(defun phpinspect-parse-file (file)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(phpinspect-parse-current-buffer)))
|
||||
|
||||
;; Define all registered parser functions
|
||||
(eval-and-compile
|
||||
(phpinspect-define-parser-functions))
|
||||
|
||||
(defun phpinspect-parse-buffer-until-point (buffer point)
|
||||
(with-current-buffer buffer
|
||||
(save-excursion
|
||||
(goto-char (point-min))
|
||||
(re-search-forward "<\\?php\\|<\\?" nil t)
|
||||
(phpinspect--parse-root (current-buffer) point nil nil 'root))))
|
||||
|
||||
(provide 'phpinspect-parser)
|
||||
;;; phpinspect-parser.el ends here
|
@ -1,394 +0,0 @@
|
||||
;;; phpinspect-pipeline.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-queue)
|
||||
(require 'phpinspect-util)
|
||||
|
||||
(define-error 'phpinspect-pipeline-incoming "Signal for incoming pipeline data")
|
||||
(define-error 'phpinspect-pipeline-error "Signal for pipeline errors")
|
||||
|
||||
(defcustom phpinspect-pipeline-pause-time 0.5
|
||||
"Number of seconds to pause a pipeline thread when emacs receives
|
||||
user input. This is similar to `phpinspect-worker-pause-time',
|
||||
but pipelines are meant to run in bursts. For that reason, the
|
||||
default pause time for pipelines is lower to be a little more
|
||||
aggressive in hogging cpu time.
|
||||
|
||||
Set this variable to a higher value if you experience a lot of
|
||||
jitter when editing during pipeline operations. At the time of
|
||||
writing, pipelines are used to refresh the project
|
||||
index/autoloader and for the indexation of \"include\"
|
||||
directories."
|
||||
:type 'number
|
||||
:group 'phpinspect)
|
||||
|
||||
(cl-defstruct (phpinspect-pipeline-end (:constructor phpinspect-make-pipeline-end))
|
||||
(value nil
|
||||
:type any)
|
||||
(error nil)
|
||||
(thread nil
|
||||
:type thread))
|
||||
|
||||
(cl-defstruct (phpinspect-pipeline-emission (:constructor phpinspect-make-pipeline-emission))
|
||||
(collection nil
|
||||
:type list))
|
||||
|
||||
(cl-defstruct (phpinspect-pipeline-thread (:constructor phpinspect-make-pipeline-thread))
|
||||
(in-queue nil
|
||||
:type phpinspect-queue)
|
||||
(end nil
|
||||
:type boolean))
|
||||
|
||||
(cl-defstruct (phpinspect-pipeline-ctx (:constructor phpinspect-make-pipeline-ctx))
|
||||
(threads nil
|
||||
:type alist))
|
||||
|
||||
(cl-defmethod phpinspect-pipeline-ctx-register-thread ((ctx phpinspect-pipeline-ctx) thread in-queue)
|
||||
(push (cons thread (phpinspect-make-pipeline-thread :in-queue in-queue))
|
||||
(phpinspect-pipeline-ctx-threads ctx)))
|
||||
|
||||
(cl-defmethod phpinspect-pipeline-ctx-get-thread ((ctx phpinspect-pipeline-ctx) thread)
|
||||
(alist-get thread (phpinspect-pipeline-ctx-threads ctx)
|
||||
nil nil #'eq))
|
||||
|
||||
(cl-defmethod phpinspect-pipeline-ctx-register-end ((ctx phpinspect-pipeline-ctx) (end phpinspect-pipeline-end))
|
||||
(let ((thread (phpinspect-pipeline-ctx-get-thread ctx (phpinspect-pipeline-end-thread end))))
|
||||
(setf (phpinspect-pipeline-thread-end thread) end)))
|
||||
|
||||
(cl-defmethod phpinspect-pipeline-ctx-close ((ctx phpinspect-pipeline-ctx))
|
||||
(let (errors err end thread-live)
|
||||
(dolist (thread (phpinspect-pipeline-ctx-threads ctx))
|
||||
(setq end (phpinspect-pipeline-thread-end (cdr thread))
|
||||
err (or (thread-last-error (car thread))
|
||||
(and end (phpinspect-pipeline-end-error end)))
|
||||
thread-live (thread-live-p (car thread)))
|
||||
|
||||
(when thread-live
|
||||
(if end
|
||||
(setq errors (nconc errors (list (format "Thread %s ended pipeline, but is still running"
|
||||
(thread-name (car thread))))))
|
||||
(setq errors (nconc errors (list (format "Thread %s is still running when pipeline is closing"
|
||||
(thread-name (car thread))))))))
|
||||
|
||||
(when err
|
||||
(setq errors (nconc errors (list (format "Thread %s signaled error: %s"
|
||||
(thread-name (car thread))
|
||||
err)))))
|
||||
(unless end
|
||||
(setq errors (nconc errors (list (format "Thread %s never ended"
|
||||
(thread-name (car thread)))))))
|
||||
|
||||
(when (thread-live-p (car thread))
|
||||
(thread-signal (car thread) 'quit nil)))
|
||||
|
||||
(when errors
|
||||
(signal 'phpinspect-pipeline-error errors))))
|
||||
|
||||
(define-inline phpinspect-pipeline-emit (data)
|
||||
(inline-letevals (data)
|
||||
(inline-quote
|
||||
(throw 'phpinspect-pipeline-emit ,data))))
|
||||
|
||||
(define-inline phpinspect-pipeline-emit-all (collection)
|
||||
(inline-letevals (collection)
|
||||
(inline-quote
|
||||
(throw 'phpinspect-pipeline-emit
|
||||
(if ,collection
|
||||
(phpinspect-make-pipeline-emission
|
||||
:collection ,collection)
|
||||
,collection)))))
|
||||
|
||||
(defmacro phpinspect-pipeline-end (&optional value)
|
||||
(if value
|
||||
`(throw 'phpinspect-pipeline-emit
|
||||
(phpinspect-make-pipeline-end :value ,value :thread (current-thread)))
|
||||
`(throw 'phpinspect-pipeline-emit
|
||||
(phpinspect-make-pipeline-end :thread (current-thread)))))
|
||||
|
||||
(define-inline phpinspect-pipeline-pause ()
|
||||
"Pause the current pipeline thread"
|
||||
(inline-quote
|
||||
(if (phpinspect--input-pending-p)
|
||||
(let ((mx (make-mutex)))
|
||||
(phpinspect-thread-pause
|
||||
phpinspect-pipeline-pause-time mx (make-condition-variable mx "phpinspect-pipeline-pause")))
|
||||
(thread-yield))))
|
||||
|
||||
(define-inline phpinspect--read-pipeline-emission (&rest body)
|
||||
(push 'progn body)
|
||||
(inline-quote
|
||||
(catch 'phpinspect-pipeline-emit
|
||||
,body
|
||||
nil)))
|
||||
|
||||
(defmacro phpinspect--run-as-pipeline-step (func-name queue consumer-queue pipeline-ctx &optional local-ctx)
|
||||
(unless (symbolp func-name)
|
||||
(error "Function name must be a symbol, got: %s" func-name))
|
||||
|
||||
|
||||
(let* ((thread-name (concat "phpinspect-pipeline-" (symbol-name func-name)))
|
||||
(statement (list func-name))
|
||||
(statement-rear statement)
|
||||
(incoming (gensym "incoming"))
|
||||
(outgoing (gensym "outgoing"))
|
||||
(inc-queue (gensym "queue"))
|
||||
(out-queue (gensym "queue"))
|
||||
(context-sym (gensym "context"))
|
||||
(continue-running (gensym "continue-running"))
|
||||
(pctx-sym (gensym "pipeline-ctx"))
|
||||
(incoming-end (gensym "incoming-end"))
|
||||
(end (gensym "end")))
|
||||
|
||||
(when local-ctx
|
||||
(setq statement-rear (setcdr statement-rear (cons context-sym nil))))
|
||||
|
||||
(setq statement-rear (setcdr statement-rear (cons incoming nil)))
|
||||
|
||||
`(let ((,inc-queue ,queue)
|
||||
(,out-queue ,consumer-queue)
|
||||
(,context-sym ,local-ctx)
|
||||
(,pctx-sym ,pipeline-ctx))
|
||||
(make-thread
|
||||
(lambda ()
|
||||
(let ((,continue-running t)
|
||||
,incoming ,outgoing ,end ,incoming-end)
|
||||
|
||||
(phpinspect-pipeline--register-wakeup-function ,inc-queue)
|
||||
(while ,continue-running
|
||||
(condition-case-unless-debug err
|
||||
(progn
|
||||
(phpinspect-pipeline-pause)
|
||||
;; Prevent quitting during step execution, as this could
|
||||
;; break data integrity.
|
||||
(let ((inhibit-quit t))
|
||||
(setq ,incoming (phpinspect-pipeline-receive ,inc-queue))
|
||||
|
||||
(if (phpinspect-pipeline-end-p ,incoming)
|
||||
(progn
|
||||
(setq ,incoming-end ,incoming)
|
||||
(when (phpinspect-pipeline-end-value ,incoming)
|
||||
(progn
|
||||
(setq ,incoming (phpinspect-pipeline-end-value ,incoming)
|
||||
,outgoing (phpinspect--read-pipeline-emission ,statement))
|
||||
(phpinspect-pipeline--enqueue ,out-queue ,outgoing 'no-notify)))
|
||||
|
||||
(setq ,end (phpinspect-make-pipeline-end :thread (current-thread)))
|
||||
(phpinspect-pipeline-ctx-register-end ,pctx-sym ,end)
|
||||
(setq ,continue-running nil)
|
||||
(phpinspect-pipeline--enqueue ,out-queue ,end))
|
||||
|
||||
;; Else
|
||||
(setq ,outgoing (phpinspect--read-pipeline-emission ,statement))
|
||||
(when (phpinspect-pipeline-end-p ,outgoing)
|
||||
(setq ,end (phpinspect-make-pipeline-end :thread (current-thread)))
|
||||
(phpinspect-pipeline-ctx-register-end ,pctx-sym ,end)
|
||||
(setq ,continue-running nil))
|
||||
(phpinspect-pipeline--enqueue ,out-queue ,outgoing))))
|
||||
(quit (ignore-error phpinspect-pipeline-incoming
|
||||
(phpinspect-pipeline-pause)))
|
||||
(phpinspect-pipeline-incoming)
|
||||
(t (phpinspect--log "Pipeline thread errored: %s" err)
|
||||
(setq ,end (phpinspect-make-pipeline-end :thread (current-thread) :error err))
|
||||
(setq ,continue-running nil)
|
||||
(phpinspect-pipeline-ctx-register-end ,pctx-sym ,end)
|
||||
(phpinspect-pipeline--enqueue ,out-queue ,end))))))
|
||||
,thread-name))))
|
||||
|
||||
|
||||
(defun phpinspect--chain-pipeline-steps (steps start-queue end-queue ctx)
|
||||
(let ((result (gensym "result"))
|
||||
(incoming (gensym "incoming"))
|
||||
(outgoing (gensym "outgoing"))
|
||||
(ctx-sym (gensym "ctx"))
|
||||
body name step statement)
|
||||
(while (setq step (pop steps))
|
||||
(setq name (phpinspect--pipeline-step-name step))
|
||||
|
||||
(setq statement
|
||||
(if (phpinspect--pipeline-step--context-var-name step)
|
||||
`(phpinspect--run-as-pipeline-step
|
||||
,name ,incoming ,outgoing ,ctx-sym ,(phpinspect--pipeline-step--context-var-name step))
|
||||
`(phpinspect--run-as-pipeline-step ,name ,incoming ,outgoing ,ctx-sym)))
|
||||
(setq body (nconc body `(,(if steps
|
||||
`(setq ,outgoing (phpinspect-make-queue))
|
||||
`(setq ,outgoing ,end-queue))
|
||||
(phpinspect-pipeline-ctx-register-thread ,ctx-sym ,statement ,incoming)
|
||||
(setq ,incoming ,outgoing)))))
|
||||
|
||||
`(let ((,incoming ,start-queue) (,ctx-sym ,ctx) ,result ,outgoing)
|
||||
,@body)))
|
||||
|
||||
(cl-defstruct (phpinspect--pipeline-step (:constructor phpinspect--make-pipeline-step))
|
||||
(context nil
|
||||
:type any
|
||||
:documentation
|
||||
"An object that is passed as first argument to all step executions")
|
||||
(-context-var-name nil
|
||||
:type symbol
|
||||
:documentation
|
||||
"Variable name used to store context in")
|
||||
(name nil
|
||||
:type symbol
|
||||
:documentation
|
||||
"The name of this step"))
|
||||
|
||||
(defmacro phpinspect--pipeline (seed-form &rest parameters)
|
||||
(let (key value steps let-vars)
|
||||
|
||||
(while parameters
|
||||
(setq key (pop parameters)
|
||||
value (pop parameters))
|
||||
|
||||
(pcase key
|
||||
(:into
|
||||
(let* ((construct-params (cons nil nil))
|
||||
(cons-params-rear construct-params)
|
||||
parameters name)
|
||||
|
||||
(if (listp value)
|
||||
(progn
|
||||
(setq name (car value)
|
||||
parameters (cdr value)))
|
||||
(setq name value))
|
||||
|
||||
(unless (symbolp name)
|
||||
(error "Step name should be a symbol"))
|
||||
|
||||
(let (key value)
|
||||
(while parameters
|
||||
(setq key (pop parameters)
|
||||
value (pop parameters))
|
||||
(setq key (intern (string-replace ":with-" ":" (symbol-name key))))
|
||||
(setq cons-params-rear
|
||||
(setcdr cons-params-rear (cons key (cons value nil))))))
|
||||
(push (apply #'phpinspect--make-pipeline-step `(,@(cdr construct-params) :name ,name))
|
||||
steps)))
|
||||
(_ (error "unexpected key %s" key))))
|
||||
|
||||
(setq steps (nreverse steps))
|
||||
|
||||
(dolist (step steps)
|
||||
(when (phpinspect--pipeline-step-context step)
|
||||
(setf (phpinspect--pipeline-step--context-var-name step) (gensym "ctx"))
|
||||
(push `(,(phpinspect--pipeline-step--context-var-name step)
|
||||
,(phpinspect--pipeline-step-context step))
|
||||
let-vars)))
|
||||
|
||||
(let ((queue-sym (gensym "queue"))
|
||||
(end-queue-sym (gensym "end-queue"))
|
||||
(ctx-sym (gensym "ctx"))
|
||||
(recv-sym (gensym))
|
||||
(result-sym (gensym))
|
||||
(seed-sym (gensym))
|
||||
(collecting-sym (gensym)))
|
||||
`(progn
|
||||
(when (eq main-thread (current-thread))
|
||||
(error "Pipelines should not run in the main thread"))
|
||||
|
||||
(let* (,@let-vars
|
||||
(,ctx-sym (phpinspect-make-pipeline-ctx))
|
||||
(,queue-sym (phpinspect-make-queue))
|
||||
(,end-queue-sym (phpinspect-make-queue))
|
||||
(,collecting-sym t)
|
||||
,recv-sym ,result-sym ,seed-sym)
|
||||
|
||||
,(phpinspect--chain-pipeline-steps steps queue-sym end-queue-sym ctx-sym)
|
||||
|
||||
(setq ,seed-sym ,seed-form)
|
||||
(when ,seed-sym
|
||||
(phpinspect-pipeline--enqueue
|
||||
,queue-sym
|
||||
(phpinspect-make-pipeline-emission :collection ,seed-form) 'no-notify))
|
||||
|
||||
(phpinspect-pipeline--enqueue
|
||||
,queue-sym (phpinspect-make-pipeline-end :thread (current-thread)))
|
||||
|
||||
(while ,collecting-sym
|
||||
(ignore-error phpinspect-pipeline-incoming
|
||||
(progn
|
||||
(phpinspect-pipeline--register-wakeup-function ,end-queue-sym)
|
||||
(while (not (phpinspect-pipeline-end-p
|
||||
(setq ,recv-sym (phpinspect-pipeline-receive ,end-queue-sym))))
|
||||
(setq ,result-sym (nconc ,result-sym (list ,recv-sym))))
|
||||
(setq ,collecting-sym nil))))
|
||||
|
||||
(phpinspect-pipeline-ctx-close ,ctx-sym)
|
||||
,result-sym)))))
|
||||
|
||||
(defmacro phpinspect-pipeline (seed-form &rest parameters)
|
||||
(declare (indent defun))
|
||||
(let ((result (gensym))
|
||||
(async-sym (gensym))
|
||||
key value async macro-params)
|
||||
(while parameters
|
||||
(setq key (pop parameters)
|
||||
value (pop parameters))
|
||||
|
||||
(pcase key
|
||||
(:async (setq async value))
|
||||
(_ (setq macro-params (nconc macro-params (list key value))))))
|
||||
|
||||
`(if-let ((,async-sym ,async))
|
||||
(make-thread
|
||||
(lambda ()
|
||||
(condition-case err
|
||||
(let ((,result (phpinspect--pipeline ,seed-form ,@macro-params)))
|
||||
(funcall ,async-sym (or ,result 'phpinspect-pipeline-nil-result) nil))
|
||||
(t (funcall ,async-sym nil err))))
|
||||
"phpinspect-pipeline-async")
|
||||
(phpinspect--pipeline ,seed-form ,@macro-params))))
|
||||
|
||||
(define-inline phpinspect-pipeline-receive (queue)
|
||||
(inline-letevals (queue)
|
||||
(inline-quote
|
||||
(let ((val))
|
||||
(while (not (setq val (phpinspect-queue-dequeue ,queue)))
|
||||
(thread-yield))
|
||||
val))))
|
||||
|
||||
(defun phpinspect-pipeline-step-name (name &optional suffix)
|
||||
(intern (concat (symbol-name name) (if suffix (concat "-" suffix) ""))))
|
||||
|
||||
(define-inline phpinspect-pipeline--register-wakeup-function (queue)
|
||||
(inline-quote
|
||||
(let ((thread (current-thread)))
|
||||
(setf (phpinspect-queue-subscription ,queue)
|
||||
(lambda () (thread-signal thread 'phpinspect-pipeline-incoming nil))))))
|
||||
|
||||
(define-inline phpinspect-pipeline--enqueue (queue emission &optional no-notify)
|
||||
(inline-letevals (queue emission no-notify)
|
||||
(inline-quote
|
||||
(when ,emission
|
||||
(if (phpinspect-pipeline-emission-p ,emission)
|
||||
(when (phpinspect-pipeline-emission-collection ,emission)
|
||||
(while (cdr (phpinspect-pipeline-emission-collection ,emission))
|
||||
(phpinspect-queue-enqueue
|
||||
,queue (pop (phpinspect-pipeline-emission-collection ,emission))
|
||||
,no-notify))
|
||||
(phpinspect-queue-enqueue
|
||||
,queue (pop (phpinspect-pipeline-emission-collection ,emission)) ,no-notify))
|
||||
(phpinspect-queue-enqueue ,queue ,emission ,no-notify))))))
|
||||
|
||||
(provide 'phpinspect-pipeline)
|
||||
;;; phpinspect-pipeline.el ends here
|
@ -1,95 +0,0 @@
|
||||
;;; phpinspect-project-struct.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
(eval-when-compile
|
||||
(declare-function phpinspect-make-dynamic-worker "phpinspect-worker.el"))
|
||||
|
||||
(cl-defstruct (phpinspect-project (:constructor phpinspect--make-project))
|
||||
(read-only-p nil
|
||||
:type boolean
|
||||
:documentation
|
||||
"Whether this project instance is read-only, meaning that its data
|
||||
should never be changed.
|
||||
|
||||
When this slot has a non-nil value:
|
||||
|
||||
- Methods and functions that are meant to manipulate class data
|
||||
should become no-ops.
|
||||
- All classes retrieved from it should be marked as read-only as well.")
|
||||
(extra-class-retriever nil
|
||||
:type lambda
|
||||
:documentation
|
||||
"A function that should accept a `phpinspect--type' and return
|
||||
matching `phpinspect--class' instances or nil. Used to discover
|
||||
classes that are defined outside of project code.")
|
||||
(extra-function-retriever nil
|
||||
:type lambda
|
||||
:documentation
|
||||
"A function that should accept a `phpinspect-name' (see
|
||||
`phpinspect-intern-name') and return matching `phpinspect--function'
|
||||
instances or nil. Used to discover functions that are defined
|
||||
outside of project code.")
|
||||
(class-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"A `hash-table` that contains all of the currently
|
||||
indexed classes in the project")
|
||||
(function-index (make-hash-table :test 'eq :size 100 :rehash-size 2.0)
|
||||
:type hash-table
|
||||
:documentation
|
||||
"A hash able that contains all of the currently indexed functions
|
||||
in the project")
|
||||
(function-token-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
|
||||
(fs nil
|
||||
:type phpinspect-fs
|
||||
:documentation
|
||||
"The filesystem object through which this project's files
|
||||
can be accessed.")
|
||||
(autoload nil
|
||||
:type phpinspect-autoload
|
||||
:documentation
|
||||
"The autoload object through which this project's type
|
||||
definitions can be retrieved")
|
||||
(worker (progn
|
||||
(unless (featurep 'phpinspect-worker)
|
||||
(require 'phpinspect-worker))
|
||||
(phpinspect-make-dynamic-worker))
|
||||
:type phpinspect-worker
|
||||
:documentation
|
||||
"The worker that this project may queue tasks for")
|
||||
(root nil
|
||||
:type string
|
||||
:documentation
|
||||
"The root directory of this project")
|
||||
(purged nil
|
||||
:type boolean
|
||||
:documentation "Whether or not the project has been purged or not.
|
||||
Projects get purged when they are removed from the global cache.")
|
||||
(file-watchers (make-hash-table :test #'equal :size 10000 :rehash-size 10000)
|
||||
:type hash-table
|
||||
:documentation "All active file watchers in this project,
|
||||
indexed by the absolute paths of the files they're watching."))
|
||||
|
||||
(provide 'phpinspect-project-struct)
|
@ -1,404 +0,0 @@
|
||||
;;; phpinspect-project.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-struct)
|
||||
(require 'phpinspect-autoload)
|
||||
(require 'phpinspect-worker)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-class)
|
||||
(require 'phpinspect-type)
|
||||
(require 'phpinspect-fs)
|
||||
(require 'filenotify)
|
||||
|
||||
(defvar phpinspect-auto-reindex nil
|
||||
"Whether or not phpinspect should automatically search for new
|
||||
files. The current implementation is clumsy and can result in
|
||||
serious performance hits. Enable at your own risk (:")
|
||||
|
||||
(defvar phpinspect-project-root-function #'phpinspect--find-project-root
|
||||
"Function that phpinspect uses to find the root directory of a project.")
|
||||
|
||||
(defvar-local phpinspect--buffer-project nil
|
||||
"The root directory of the PHP project that this buffer belongs to")
|
||||
|
||||
(defmacro phpinspect-project-edit (project &rest body)
|
||||
(declare (indent 1))
|
||||
`(unless (phpinspect-project-read-only-p ,project)
|
||||
,@body))
|
||||
|
||||
(defsubst phpinspect-current-project-root ()
|
||||
"Call `phpinspect-project-root-function' with ARGS as arguments."
|
||||
(unless (and (boundp 'phpinspect--buffer-project) phpinspect--buffer-project)
|
||||
(set (make-local-variable 'phpinspect--buffer-project) (funcall phpinspect-project-root-function)))
|
||||
phpinspect--buffer-project)
|
||||
|
||||
(cl-defmethod phpinspect-project-purge ((project phpinspect-project))
|
||||
"Disable all background processes for project and put it in a `purged` state."
|
||||
(maphash (lambda (_ watcher) (file-notify-rm-watch watcher))
|
||||
(phpinspect-project-file-watchers project))
|
||||
|
||||
(setf (phpinspect-project-file-watchers project)
|
||||
(make-hash-table :test #'equal :size 10000 :rehash-size 10000))
|
||||
(setf (phpinspect-project-purged project) t))
|
||||
|
||||
(cl-defmethod phpinspect-project-watch-file ((project phpinspect-project)
|
||||
filepath
|
||||
callback)
|
||||
(phpinspect-project-edit project
|
||||
(let ((watcher (file-notify-add-watch filepath '(change) callback)))
|
||||
(puthash filepath watcher (phpinspect-project-file-watchers project)))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-return-types-to-index-queueue
|
||||
((project phpinspect-project) methods)
|
||||
(phpinspect-project-edit project
|
||||
(dolist (method methods)
|
||||
(when (phpinspect--function-return-type method)
|
||||
(phpinspect-project-enqueue-if-not-present
|
||||
project
|
||||
(phpinspect--function-return-type method))))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-variable-types-to-index-queue
|
||||
((project phpinspect-project) variables)
|
||||
(phpinspect-project-edit project
|
||||
(dolist (var variables)
|
||||
(when (phpinspect--variable-type var)
|
||||
(phpinspect-project-enqueue-if-not-present project (phpinspect--variable-type var))))))
|
||||
|
||||
(cl-defmethod phpinspect-project-enqueue-if-not-present
|
||||
((project phpinspect-project) (type phpinspect--type))
|
||||
(phpinspect-project-edit project
|
||||
(unless (phpinspect--type-is-native type)
|
||||
(let ((class (phpinspect-project-get-class project type)))
|
||||
(when (or (not class)
|
||||
(not (or (phpinspect--class-initial-index class))))
|
||||
(when (not class)
|
||||
(setq class (phpinspect-project-create-class project type)))
|
||||
(unless (or (phpinspect--type= phpinspect--null-type type)
|
||||
(phpinspect--type-is-native type))
|
||||
(phpinspect--log "Adding unpresent class %s to index queue" type)
|
||||
(phpinspect-worker-enqueue (phpinspect-project-worker project)
|
||||
(phpinspect-make-index-task project type))))))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-class-attribute-types-to-index-queue
|
||||
((project phpinspect-project) (class phpinspect--class))
|
||||
(phpinspect-project-edit project
|
||||
(phpinspect-project-add-return-types-to-index-queueue
|
||||
project
|
||||
(phpinspect--class-get-method-list class))
|
||||
(phpinspect-project-add-return-types-to-index-queueue
|
||||
project
|
||||
(phpinspect--class-get-static-method-list class))
|
||||
(phpinspect-project-add-variable-types-to-index-queue
|
||||
project
|
||||
(phpinspect--class-variables class))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-index
|
||||
((project phpinspect-project) (index (head phpinspect--root-index)) &optional index-imports)
|
||||
(phpinspect-project-edit project
|
||||
(when index-imports
|
||||
(phpinspect-project-enqueue-imports project (alist-get 'imports (cdr index))))
|
||||
|
||||
(dolist (indexed-class (alist-get 'classes (cdr index)))
|
||||
(phpinspect-project-add-class project (cdr indexed-class) index-imports))
|
||||
|
||||
(dolist (func (alist-get 'functions (cdr index)))
|
||||
(phpinspect-project-set-function project func))))
|
||||
|
||||
(cl-defmethod phpinspect-project-set-function
|
||||
((project phpinspect-project) (func phpinspect--function))
|
||||
(phpinspect-project-edit project
|
||||
(puthash (phpinspect--function-name-symbol func) func
|
||||
(phpinspect-project-function-index project))))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-function
|
||||
((project phpinspect-project) (name (head phpinspect-name)))
|
||||
(gethash name (phpinspect-project-function-index project)))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-function-or-extra
|
||||
((project phpinspect-project) (name (head phpinspect-name)))
|
||||
(or (phpinspect-project-get-function project name)
|
||||
(and (phpinspect-project-extra-function-retriever project)
|
||||
(funcall (phpinspect-project-extra-function-retriever project)
|
||||
name))))
|
||||
|
||||
(cl-defmethod phpinspect-project-delete-function
|
||||
((project phpinspect-project) (name (head phpinspect-name)))
|
||||
(phpinspect-project-edit project
|
||||
(remhash name (phpinspect-project-function-index project))))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-functions ((project phpinspect-project))
|
||||
(let ((funcs))
|
||||
(maphash
|
||||
(lambda (_name func) (push func funcs))
|
||||
(phpinspect-project-function-index project))
|
||||
|
||||
funcs))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-functions-with-extra ((project phpinspect-project))
|
||||
(let ((funcs))
|
||||
(maphash
|
||||
(lambda (_name func) (push func funcs))
|
||||
(phpinspect-project-function-index project))
|
||||
|
||||
(if (phpinspect-project-extra-function-retriever project)
|
||||
(nconc funcs (funcall (phpinspect-project-extra-function-retriever project) nil))
|
||||
funcs)))
|
||||
|
||||
(cl-defmethod phpinspect-project-enqueue-imports
|
||||
((project phpinspect-project) imports)
|
||||
(phpinspect-project-edit project
|
||||
(dolist (import imports)
|
||||
(when import
|
||||
(phpinspect--log "Adding import to index queue: %s" import)
|
||||
(phpinspect-project-enqueue-if-not-present project (cdr import))))))
|
||||
|
||||
(cl-defmethod phpinspect-project-delete-class ((project phpinspect-project) (class phpinspect--class))
|
||||
(phpinspect-project-delete-class project (phpinspect--class-name class)))
|
||||
|
||||
(cl-defmethod phpinspect-project-delete-class ((project phpinspect-project) (class-name phpinspect--type))
|
||||
(phpinspect-project-edit project
|
||||
(remhash (phpinspect--type-name-symbol class-name) (phpinspect-project-class-index project))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-class
|
||||
((project phpinspect-project) (indexed-class (head phpinspect--indexed-class)) &optional index-imports)
|
||||
(phpinspect-project-edit project
|
||||
(if (not (alist-get 'class-name (cdr indexed-class)))
|
||||
(phpinspect--log "Error: Class with declaration %s does not have a name" (alist-get 'declaration indexed-class))
|
||||
;; Else
|
||||
(let* ((class-name (phpinspect--type-name-symbol
|
||||
(alist-get 'class-name (cdr indexed-class))))
|
||||
(class (gethash class-name
|
||||
(phpinspect-project-class-index project))))
|
||||
(unless class
|
||||
(setq class (phpinspect--make-class-generated
|
||||
:class-retriever (phpinspect-project-make-class-retriever project))))
|
||||
|
||||
(when index-imports
|
||||
(phpinspect-project-enqueue-imports
|
||||
project (alist-get 'imports (cdr indexed-class))))
|
||||
|
||||
(phpinspect--class-set-index class indexed-class)
|
||||
(puthash class-name class (phpinspect-project-class-index project))
|
||||
(phpinspect-project-add-class-attribute-types-to-index-queue project class)))))
|
||||
|
||||
(cl-defmethod phpinspect-project-set-class
|
||||
((project phpinspect-project) (class-fqn phpinspect--type) (class phpinspect--class))
|
||||
(phpinspect-project-edit project
|
||||
(puthash (phpinspect--type-name-symbol class-fqn)
|
||||
class
|
||||
(phpinspect-project-class-index project))))
|
||||
|
||||
(cl-defmethod phpinspect-project-create-class
|
||||
((project phpinspect-project) (class-fqn phpinspect--type))
|
||||
(phpinspect-project-edit project
|
||||
(let ((class (phpinspect--make-class-generated
|
||||
:class-retriever (phpinspect-project-make-class-retriever project))))
|
||||
(phpinspect-project-set-class project class-fqn class)
|
||||
class)))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-class-create
|
||||
((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue)
|
||||
(let ((class (phpinspect-project-get-class project class-fqn)))
|
||||
(unless class
|
||||
(phpinspect-project-edit project
|
||||
(setq class (phpinspect-project-create-class project class-fqn))
|
||||
(unless no-enqueue
|
||||
(phpinspect-project-enqueue-if-not-present project class-fqn))))
|
||||
class))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-class-extra-or-create
|
||||
((project phpinspect-project) (class-fqn phpinspect--type) &optional no-enqueue)
|
||||
(or (phpinspect-project-get-class-or-extra project class-fqn)
|
||||
(phpinspect-project-get-class-create project class-fqn no-enqueue)))
|
||||
|
||||
(defalias 'phpinspect-project-add-class-if-missing #'phpinspect-project-get-class-create)
|
||||
|
||||
(cl-defmethod phpinspect-project-get-class
|
||||
((project phpinspect-project) (class-fqn phpinspect--type))
|
||||
"Get indexed class by name of CLASS-FQN stored in PROJECT."
|
||||
(let ((class (gethash (phpinspect--type-name-symbol class-fqn)
|
||||
(phpinspect-project-class-index project))))
|
||||
(when (and class (phpinspect-project-read-only-p project)
|
||||
(not (phpinspect--class-read-only-p class)))
|
||||
(setf (phpinspect--class-read-only-p class) t))
|
||||
|
||||
class))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-class-or-extra
|
||||
((project phpinspect-project) (class-fqn phpinspect--type))
|
||||
(or (phpinspect-project-get-class project class-fqn)
|
||||
(and (phpinspect-project-extra-class-retriever project)
|
||||
(funcall (phpinspect-project-extra-class-retriever project)
|
||||
class-fqn))))
|
||||
|
||||
(cl-defmethod phpinspect-project-get-type-filepath
|
||||
((project phpinspect-project) (type phpinspect--type) &optional index-new)
|
||||
"Retrieve filepath to TYPE definition file.
|
||||
|
||||
when INDEX-NEW is non-nil, new files are added to the index
|
||||
before the search is executed."
|
||||
(let* ((autoloader (phpinspect-project-autoload project)))
|
||||
(when (eq index-new 'index-new)
|
||||
(phpinspect-project-edit project
|
||||
(phpinspect-autoloader-refresh autoloader)))
|
||||
(let* ((result (phpinspect-autoloader-resolve
|
||||
autoloader (phpinspect--type-name-symbol type))))
|
||||
(if (not result)
|
||||
;; Index new files and try again if not done already.
|
||||
(if (eq index-new 'index-new)
|
||||
nil
|
||||
(when phpinspect-auto-reindex
|
||||
(phpinspect--log "Failed finding filepath for type %s. Retrying with reindex."
|
||||
(phpinspect--type-name type))
|
||||
(phpinspect-project-get-type-filepath project type 'index-new)))
|
||||
result))))
|
||||
|
||||
(cl-defmethod phpinspect-project-index-type-file
|
||||
((project phpinspect-project) (type phpinspect--type))
|
||||
"Index the file that TYPE is expected to be defined in."
|
||||
|
||||
(condition-case error
|
||||
(let* ((file (phpinspect-project-get-type-filepath project type))
|
||||
(visited-buffer (when file (find-buffer-visiting file))))
|
||||
(when file
|
||||
(if visited-buffer
|
||||
(with-current-buffer visited-buffer (phpinspect-index-current-buffer))
|
||||
(with-temp-buffer (phpinspect-project-index-file project file)))))
|
||||
(file-missing
|
||||
(phpinspect--log "Failed to find file for type %s: %s" type error)
|
||||
nil)))
|
||||
|
||||
(cl-defmethod phpinspect-project-index-file
|
||||
((project phpinspect-project) (filename string))
|
||||
"Index "
|
||||
(let ((fs (phpinspect-project-fs project)))
|
||||
(with-temp-buffer
|
||||
(phpinspect-fs-insert-file-contents fs filename 'prefer-async)
|
||||
(phpinspect-index-current-buffer))))
|
||||
|
||||
(cl-defmethod phpinspect-project-add-file-index ((project phpinspect-project) (filename string))
|
||||
(phpinspect-project-add-index project (phpinspect-project-index-file project filename)))
|
||||
|
||||
(defcustom phpinspect-projects nil
|
||||
"PHPInspect Projects."
|
||||
:type '(alist :key-type string
|
||||
:value-type (alist :key-type symbol
|
||||
:options ((include-dirs (repeat string)))))
|
||||
:group 'phpinspect)
|
||||
|
||||
(defun phpinspect-project-make-file-indexer (project)
|
||||
(lambda (filename)
|
||||
(phpinspect-project-add-file-index project filename)))
|
||||
|
||||
(defun phpinspect-project-make-root-resolver (project)
|
||||
(lambda () (phpinspect-project-root project)))
|
||||
|
||||
(defun phpinspect-project-make-class-retriever (project)
|
||||
(lambda (type)
|
||||
(or (phpinspect-project-get-class-or-extra project type)
|
||||
(phpinspect-project-get-class-create project type))))
|
||||
|
||||
;;; INDEX TASK
|
||||
(cl-defstruct (phpinspect-index-task
|
||||
(:constructor phpinspect-make-index-task-generated))
|
||||
"Represents an index task that can be executed by a `phpinspect-worker`."
|
||||
(project nil
|
||||
:type phpinspect-project
|
||||
:documentation
|
||||
"The project that the task should be executed for.")
|
||||
(type nil
|
||||
:type phpinspect--type
|
||||
:documentation
|
||||
"The type whose file should be indexed."))
|
||||
|
||||
(cl-defmethod phpinspect-make-index-task ((project phpinspect-project)
|
||||
(type phpinspect--type))
|
||||
(phpinspect-make-index-task-generated
|
||||
:project project
|
||||
:type type))
|
||||
|
||||
(cl-defmethod phpinspect-task-project ((task phpinspect-index-task))
|
||||
(phpinspect-index-task-project task))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect-task= ((task1 phpinspect-index-task) (task2 phpinspect-index-task))
|
||||
(and (eq (phpinspect-index-task-project task1)
|
||||
(phpinspect-index-task-project task2))
|
||||
(phpinspect--type= (phpinspect-index-task-type task1) (phpinspect-index-task-type task2))))
|
||||
|
||||
(cl-defmethod phpinspect-task-execute ((task phpinspect-index-task)
|
||||
(worker phpinspect-worker))
|
||||
"Execute index TASK for WORKER."
|
||||
(let ((project (phpinspect-index-task-project task))
|
||||
(is-native-type (phpinspect--type-is-native
|
||||
(phpinspect-index-task-type task))))
|
||||
(phpinspect--log "Indexing class %s for project in %s as task."
|
||||
(phpinspect-index-task-type task)
|
||||
(phpinspect-project-root project))
|
||||
|
||||
(cond (is-native-type
|
||||
(phpinspect--log "Skipping indexation of native type %s as task"
|
||||
(phpinspect-index-task-type task))
|
||||
|
||||
;; We can skip pausing when a native type is encountered
|
||||
;; and skipped, as we haven't done any intensive work that
|
||||
;; may cause hangups.
|
||||
(setf (phpinspect-worker-skip-next-pause worker) t))
|
||||
(t
|
||||
(let* ((type (phpinspect-index-task-type task))
|
||||
(root-index (phpinspect-project-index-type-file project type)))
|
||||
(when root-index
|
||||
(phpinspect-project-add-index project root-index)))))))
|
||||
|
||||
;;; INDEX FILE TASK
|
||||
(cl-defstruct (phpinspect-index-dir-task (:constructor phpinspect-make-index-dir-task))
|
||||
"A task for the indexation of files"
|
||||
(project nil
|
||||
:type phpinspect-project)
|
||||
(dir nil
|
||||
:type string))
|
||||
|
||||
(cl-defmethod phpinspect-task=
|
||||
((task1 phpinspect-index-dir-task) (task2 phpinspect-index-dir-task))
|
||||
(and (eq (phpinspect-index-dir-task-project task1)
|
||||
(phpinspect-index-dir-task-project task2))
|
||||
(string= (phpinspect-index-dir-task-dir task1)
|
||||
(phpinspect-index-dir-task-dir task2))))
|
||||
|
||||
(cl-defmethod phpinspect-task-project ((task phpinspect-index-dir-task))
|
||||
(phpinspect-index-dir-task-project task))
|
||||
|
||||
(cl-defmethod phpinspect-task-execute ((task phpinspect-index-dir-task)
|
||||
(_worker phpinspect-worker))
|
||||
(phpinspect--log "Entering..")
|
||||
(let* ((project (phpinspect-index-dir-task-project task))
|
||||
(fs (phpinspect-project-fs project))
|
||||
(dir (phpinspect-index-dir-task-dir task)))
|
||||
(phpinspect--log "Indexing directory %s" dir)
|
||||
(phpinspect-pipeline (phpinspect-fs-directory-files-recursively fs dir "\\.php$")
|
||||
:into (phpinspect-project-add-file-index :with-context project))))
|
||||
|
||||
(provide 'phpinspect-project)
|
||||
;;; phpinspect-project.el ends here
|
@ -1,129 +0,0 @@
|
||||
;;; phpinspect-queue.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
|
||||
(cl-defstruct (phpinspect-queue
|
||||
(:constructor phpinspect-make-queue-generated))
|
||||
(-first nil
|
||||
:type phpinspect-queue-item
|
||||
:documentation
|
||||
"The first item in the queue")
|
||||
(-last nil
|
||||
:type phpinspect-queue-item
|
||||
:documentation
|
||||
"The last item in the queue")
|
||||
(subscription nil
|
||||
:type function
|
||||
:documentation
|
||||
"A function that should be called when items are
|
||||
enqueued."))
|
||||
|
||||
(cl-defstruct (phpinspect-queue-item
|
||||
(:constructor phpinspect-make-queue-item))
|
||||
(next nil
|
||||
:type phpinspect-queue-item
|
||||
:documentation
|
||||
"The next item in the queue")
|
||||
(value nil
|
||||
:type any
|
||||
:documentation
|
||||
"The value stored in the queue")
|
||||
(previous nil
|
||||
:type phpinspect-queue-item
|
||||
:documentation
|
||||
"The previous item in the queue"))
|
||||
|
||||
(define-inline phpinspect-make-queue (&optional subscription)
|
||||
(inline-quote
|
||||
(progn
|
||||
(phpinspect-make-queue-generated :subscription ,subscription))))
|
||||
|
||||
(cl-defmethod phpinspect-queue-first ((queue phpinspect-queue))
|
||||
(phpinspect-queue--first queue))
|
||||
|
||||
(cl-defmethod phpinspect-queue-last ((queue phpinspect-queue))
|
||||
(or (phpinspect-queue--last queue) (phpinspect-queue--first queue)))
|
||||
|
||||
(cl-defmethod phpinspect-queue-enqueue ((queue phpinspect-queue) value &optional no-notify)
|
||||
"Add VALUE to the end of the queue that ITEM is part of."
|
||||
(let ((last (phpinspect-queue-last queue))
|
||||
(new-item (phpinspect-make-queue-item :value value)))
|
||||
(if (not last)
|
||||
(setf (phpinspect-queue--first queue) new-item)
|
||||
(setf (phpinspect-queue-item-next last) new-item)
|
||||
(setf (phpinspect-queue-item-previous new-item) last))
|
||||
(setf (phpinspect-queue--last queue) new-item))
|
||||
|
||||
(when (and (not no-notify) (phpinspect-queue-subscription queue))
|
||||
(funcall (phpinspect-queue-subscription queue))))
|
||||
|
||||
(cl-defmethod phpinspect-queue-dequeue ((queue phpinspect-queue))
|
||||
"Remove the value at the front of the queue that ITEM is part of and return it."
|
||||
(let* ((first (phpinspect-queue-first queue))
|
||||
next value)
|
||||
(when first
|
||||
(setq next (phpinspect-queue-item-next first))
|
||||
(setq value (phpinspect-queue-item-value first)))
|
||||
(if next
|
||||
(setf (phpinspect-queue-item-previous next) nil)
|
||||
(setf (phpinspect-queue--last queue) nil))
|
||||
(setf (phpinspect-queue--first queue) next)
|
||||
value))
|
||||
|
||||
(defmacro phpinspect-doqueue (place-and-queue &rest body)
|
||||
"Loop over queue defined in PLACE-AND-QUEUE executing BODY.
|
||||
|
||||
PLACE-AND-QUEUE is a two-member list. The first item should be
|
||||
the place that the current value in the queue should be assigned
|
||||
to upon each iteration. The second item should be a queue-item
|
||||
belonging to the queue that must be iterated over.
|
||||
|
||||
BODY can be any form."
|
||||
(declare (indent defun))
|
||||
(let ((item-sym (gensym))
|
||||
(place (car place-and-queue))
|
||||
(queue (cadr place-and-queue)))
|
||||
`(let* ((,item-sym (phpinspect-queue-first ,queue)))
|
||||
(while ,item-sym
|
||||
(let ((,place (phpinspect-queue-item-value ,item-sym)))
|
||||
,@body
|
||||
(setq ,item-sym (phpinspect-queue-item-next ,item-sym)))))))
|
||||
|
||||
(cl-defmethod phpinspect-queue-find
|
||||
((queue phpinspect-queue) value comparison-func)
|
||||
"Find VALUE in the queue that ITEM is part of using COMPARISON-FUNC."
|
||||
(catch 'found
|
||||
(phpinspect-doqueue (current-value queue)
|
||||
(when (funcall comparison-func current-value value)
|
||||
(throw 'found current-value)))))
|
||||
|
||||
(cl-defmethod phpinspect-queue-enqueue-noduplicate
|
||||
((queue phpinspect-queue) value comparison-func)
|
||||
|
||||
(when (not (phpinspect-queue-find queue value comparison-func))
|
||||
(phpinspect-queue-enqueue queue value)))
|
||||
|
||||
(provide 'phpinspect-queue)
|
||||
;;; phpinspect-queue.el ends here
|
@ -1,500 +0,0 @@
|
||||
;;; phpinspect-resolve.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-resolvecontext)
|
||||
(require 'phpinspect-cache)
|
||||
(require 'phpinspect-type)
|
||||
(require 'phpinspect-token-predicates)
|
||||
|
||||
(cl-defstruct (phpinspect--assignment
|
||||
(:constructor phpinspect--make-assignment))
|
||||
(to nil
|
||||
:type phpinspect-variable
|
||||
:documentation "The variable that is assigned to")
|
||||
(from nil
|
||||
:type phpinspect-token
|
||||
:documentation "The token that is assigned from"))
|
||||
|
||||
(defsubst phpinspect-block-or-list-p (token)
|
||||
(or (phpinspect-block-p token)
|
||||
(phpinspect-list-p token)))
|
||||
|
||||
(defsubst phpinspect-maybe-assignment-p (token)
|
||||
"Like `phpinspect-assignment-p', but includes \"as\" barewords as possible tokens."
|
||||
(or (phpinspect-assignment-p token)
|
||||
(equal '(:word "as") token)))
|
||||
|
||||
(cl-defgeneric phpinspect--find-assignments-in-token (token)
|
||||
"Find any assignments that are in TOKEN, at top level or nested in blocks"
|
||||
(when (keywordp (car token))
|
||||
(setq token (cdr token)))
|
||||
|
||||
(let ((assignments)
|
||||
(blocks-or-lists)
|
||||
(statements (phpinspect--split-statements token)))
|
||||
(dolist (statement statements)
|
||||
(when (seq-find #'phpinspect-maybe-assignment-p statement)
|
||||
(phpinspect--log "Found assignment statement")
|
||||
(push statement assignments))
|
||||
|
||||
(when (setq blocks-or-lists (seq-filter #'phpinspect-block-or-list-p statement))
|
||||
(dolist (block-or-list blocks-or-lists)
|
||||
(phpinspect--log "Found block or list %s" block-or-list)
|
||||
(let ((local-assignments (phpinspect--find-assignments-in-token block-or-list)))
|
||||
(dolist (local-assignment (nreverse local-assignments))
|
||||
(push local-assignment assignments))))))
|
||||
|
||||
;; return
|
||||
(phpinspect--log "Found assignments in token: %s" assignments)
|
||||
(phpinspect--log "Found statements in token: %s" statements)
|
||||
assignments))
|
||||
|
||||
(defsubst phpinspect-not-assignment-p (token)
|
||||
"Inverse of applying `phpinspect-assignment-p to TOKEN."
|
||||
(not (phpinspect-maybe-assignment-p token)))
|
||||
|
||||
(defsubst phpinspect-not-comment-p (token)
|
||||
(not (phpinspect-comment-p token)))
|
||||
|
||||
(defun phpinspect--find-assignments-by-predicate (token predicate)
|
||||
(let ((variable-assignments)
|
||||
(all-assignments (phpinspect--find-assignments-in-token token)))
|
||||
(dolist (assignment all-assignments)
|
||||
(let* ((is-loop-assignment nil)
|
||||
(left-of-assignment
|
||||
(seq-filter #'phpinspect-not-comment-p
|
||||
(seq-take-while #'phpinspect-not-assignment-p assignment)))
|
||||
(right-of-assignment
|
||||
(seq-filter
|
||||
#'phpinspect-not-comment-p
|
||||
(cdr (seq-drop-while
|
||||
(lambda (elt)
|
||||
(if (phpinspect-maybe-assignment-p elt)
|
||||
(progn
|
||||
(when (equal '(:word "as") elt)
|
||||
(phpinspect--log "It's a loop assignment %s" elt)
|
||||
(setq is-loop-assignment t))
|
||||
nil)
|
||||
t))
|
||||
assignment)))))
|
||||
|
||||
(if is-loop-assignment
|
||||
(when (funcall predicate right-of-assignment)
|
||||
;; Masquerade as an array access assignment
|
||||
(setq left-of-assignment (append left-of-assignment '((:array))))
|
||||
(push (phpinspect--make-assignment :to right-of-assignment
|
||||
:from left-of-assignment)
|
||||
variable-assignments))
|
||||
(when (funcall predicate left-of-assignment)
|
||||
(push (phpinspect--make-assignment :from right-of-assignment
|
||||
:to left-of-assignment)
|
||||
variable-assignments)))))
|
||||
(phpinspect--log "Returning the thing %s" variable-assignments)
|
||||
(nreverse variable-assignments)))
|
||||
|
||||
(defsubst phpinspect-drop-preceding-barewords (statement)
|
||||
(while (and statement (phpinspect-word-p (cadr statement)))
|
||||
(pop statement))
|
||||
statement)
|
||||
|
||||
;; TODO: the use of this function and similar ones should be replaced with code
|
||||
;; that uses locally injected project objects in stead of retrieving the project
|
||||
;; object through global variables.
|
||||
(defsubst phpinspect-get-cached-project-class (project-root class-fqn)
|
||||
(when project-root
|
||||
(phpinspect-project-get-class-or-extra
|
||||
(phpinspect--cache-get-project-create (phpinspect--get-or-create-global-cache)
|
||||
project-root)
|
||||
class-fqn)))
|
||||
|
||||
(defun phpinspect-get-cached-project-class-methods (project-root class-fqn &optional static)
|
||||
(phpinspect--log "Getting cached project class methods for %s (%s)"
|
||||
project-root class-fqn)
|
||||
(when project-root
|
||||
(let ((class (phpinspect-get-or-create-cached-project-class
|
||||
project-root
|
||||
class-fqn)))
|
||||
(when class
|
||||
(phpinspect--log "Retrieved class index, starting method collection %s (%s)"
|
||||
project-root class-fqn)
|
||||
(if static
|
||||
(phpinspect--class-get-static-method-list class)
|
||||
(phpinspect--class-get-method-list class))))))
|
||||
|
||||
(defsubst phpinspect-get-cached-project-class-method-type
|
||||
(project-root class-fqn method-name)
|
||||
(when project-root
|
||||
(let* ((class (phpinspect-get-or-create-cached-project-class project-root class-fqn))
|
||||
(method))
|
||||
(when class
|
||||
(setq method
|
||||
(phpinspect--class-get-method class (phpinspect-intern-name method-name)))
|
||||
(when method
|
||||
(phpinspect--function-return-type method))))))
|
||||
|
||||
(defsubst phpinspect-get-cached-project-class-variable-type
|
||||
(project-root class-fqn variable-name)
|
||||
(phpinspect--log "Getting cached project class variable type for %s (%s::%s)"
|
||||
project-root class-fqn variable-name)
|
||||
(when project-root
|
||||
(let ((found-variable
|
||||
(phpinspect--class-get-variable
|
||||
(phpinspect-get-or-create-cached-project-class project-root class-fqn)
|
||||
variable-name)))
|
||||
(when found-variable
|
||||
(phpinspect--variable-type found-variable)))))
|
||||
|
||||
(defsubst phpinspect-get-cached-project-class-static-method-type
|
||||
(project-root class-fqn method-name)
|
||||
(when project-root
|
||||
(let* ((class (phpinspect-get-or-create-cached-project-class project-root class-fqn))
|
||||
(method))
|
||||
(when class
|
||||
(setq method
|
||||
(phpinspect--class-get-static-method
|
||||
class
|
||||
(phpinspect-intern-name method-name)))
|
||||
(when method
|
||||
(phpinspect--function-return-type method))))))
|
||||
|
||||
(defun phpinspect-get-derived-statement-type-in-block
|
||||
(resolvecontext statement php-block type-resolver &optional function-arg-list)
|
||||
"Get type of RESOLVECONTEXT subject in PHP-BLOCK.
|
||||
|
||||
Use TYPE-RESOLVER and FUNCTION-ARG-LIST in the process.
|
||||
|
||||
An example of a derived statement would be the following php code:
|
||||
$variable->attribute->method();
|
||||
$variable->attribute;
|
||||
$variable->method();
|
||||
self::method();
|
||||
ClassName::method();
|
||||
$variable = ClassName::method();
|
||||
$variable = $variable->method();"
|
||||
;; A derived statement can be an assignment itself.
|
||||
(when (seq-find #'phpinspect-assignment-p statement)
|
||||
(phpinspect--log "Derived statement is an assignment: %s" statement)
|
||||
(setq statement (cdr (seq-drop-while #'phpinspect-not-assignment-p statement))))
|
||||
(phpinspect--log "Get derived statement type in block: %s" statement)
|
||||
(let* ((first-token (pop statement))
|
||||
(current-token)
|
||||
(previous-attribute-type))
|
||||
;; No first token means we were passed an empty list.
|
||||
(when (and first-token
|
||||
(setq previous-attribute-type
|
||||
(or
|
||||
;; Statements starting with a bare word can indicate a static
|
||||
;; method call. These could be statements with "return" or
|
||||
;; another bare-word at the start though, so we drop preceding
|
||||
;; barewords when they are present.
|
||||
(when (phpinspect-word-p first-token)
|
||||
(when (phpinspect-word-p (car statement))
|
||||
(setq statement (phpinspect-drop-preceding-barewords
|
||||
statement))
|
||||
(setq first-token (pop statement)))
|
||||
(funcall type-resolver (phpinspect--make-type
|
||||
:name (cadr first-token))))
|
||||
|
||||
;; No bare word, assume we're dealing with a variable.
|
||||
(when (phpinspect-variable-p first-token)
|
||||
(phpinspect-get-variable-type-in-block
|
||||
resolvecontext
|
||||
(cadr first-token)
|
||||
php-block
|
||||
type-resolver
|
||||
function-arg-list)))))
|
||||
|
||||
(phpinspect--log "Statement: %s" statement)
|
||||
(phpinspect--log "Starting attribute type: %s" previous-attribute-type)
|
||||
(while (setq current-token (pop statement))
|
||||
(phpinspect--log "Current derived statement token: %s" current-token)
|
||||
(cond ((phpinspect-object-attrib-p current-token)
|
||||
(let ((attribute-word (cadr current-token)))
|
||||
(when (phpinspect-word-p attribute-word)
|
||||
(if (phpinspect-list-p (car statement))
|
||||
(progn
|
||||
(pop statement)
|
||||
(setq previous-attribute-type
|
||||
(or
|
||||
(phpinspect-get-cached-project-class-method-type
|
||||
(phpinspect--resolvecontext-project-root
|
||||
resolvecontext)
|
||||
(funcall type-resolver previous-attribute-type)
|
||||
(cadr attribute-word))
|
||||
previous-attribute-type)))
|
||||
(setq previous-attribute-type
|
||||
(or
|
||||
(phpinspect-get-cached-project-class-variable-type
|
||||
(phpinspect--resolvecontext-project-root
|
||||
resolvecontext)
|
||||
(funcall type-resolver previous-attribute-type)
|
||||
(cadr attribute-word))
|
||||
previous-attribute-type))))))
|
||||
((phpinspect-static-attrib-p current-token)
|
||||
(let ((attribute-word (cadr current-token)))
|
||||
(phpinspect--log "Found attribute word: %s" attribute-word)
|
||||
(phpinspect--log "checking if next token is a list. Token: %s"
|
||||
(car statement))
|
||||
(when (phpinspect-word-p attribute-word)
|
||||
(if (phpinspect-list-p (car statement))
|
||||
(progn
|
||||
(pop statement)
|
||||
(setq previous-attribute-type
|
||||
(or
|
||||
(phpinspect-get-cached-project-class-static-method-type
|
||||
(phpinspect--resolvecontext-project-root
|
||||
resolvecontext)
|
||||
(funcall type-resolver previous-attribute-type)
|
||||
(cadr attribute-word))
|
||||
previous-attribute-type)))))))
|
||||
((and previous-attribute-type (phpinspect-array-p current-token))
|
||||
(setq previous-attribute-type
|
||||
(or (phpinspect--type-contains previous-attribute-type)
|
||||
previous-attribute-type)))))
|
||||
(phpinspect--log "Found derived type: %s" previous-attribute-type)
|
||||
;; Make sure to always return a FQN
|
||||
(funcall type-resolver previous-attribute-type))))
|
||||
|
||||
;;;;
|
||||
;; TODO: since we're passing type-resolver to all of the get-variable-type functions now,
|
||||
;; we may as well always return FQNs in stead of relative type names.
|
||||
;;;;
|
||||
(defun phpinspect-get-variable-type-in-block
|
||||
(resolvecontext variable-name php-block type-resolver &optional function-arg-list)
|
||||
"Find the type of VARIABLE-NAME in PHP-BLOCK using TYPE-RESOLVER.
|
||||
|
||||
Returns either a FQN or a relative type name, depending on
|
||||
whether or not the root variable of the assignment value (right
|
||||
side of assignment) can be found in FUNCTION-ARG-LIST.
|
||||
|
||||
When PHP-BLOCK belongs to a function, supply FUNCTION-ARG-LIST to
|
||||
resolve types of function argument variables."
|
||||
(phpinspect--log "Looking for assignments of variable %s in php block" variable-name)
|
||||
(if (string= variable-name "this")
|
||||
(funcall type-resolver (phpinspect--make-type :name "self"))
|
||||
(phpinspect-get-pattern-type-in-block
|
||||
resolvecontext (phpinspect--make-pattern :m `(:variable ,variable-name))
|
||||
php-block type-resolver function-arg-list)))
|
||||
|
||||
(defun phpinspect-get-pattern-type-in-block
|
||||
(resolvecontext pattern php-block type-resolver &optional function-arg-list)
|
||||
"Find the type of PATTERN in PHP-BLOCK using TYPE-RESOLVER.
|
||||
|
||||
PATTERN must be an object of the type `phpinspect--pattern'.
|
||||
|
||||
Returns either a FQN or a relative type name, depending on
|
||||
whether or not the root variable of the assignment value (right
|
||||
side of assignment) needs to be extracted from FUNCTION-ARG-LIST.
|
||||
|
||||
When PHP-BLOCK belongs to a function, supply FUNCTION-ARG-LIST to
|
||||
resolve types of function argument variables."
|
||||
(let* ((assignments
|
||||
(phpinspect--find-assignments-by-predicate
|
||||
php-block (phpinspect--pattern-matcher pattern)))
|
||||
(last-assignment (when assignments (car (last assignments))))
|
||||
(last-assignment-value (when last-assignment
|
||||
(phpinspect--assignment-from last-assignment)))
|
||||
(pattern-code (phpinspect--pattern-code pattern))
|
||||
(result))
|
||||
(phpinspect--log "Looking for assignments of pattern %s in php block" pattern-code)
|
||||
|
||||
(if (not assignments)
|
||||
(when (and (= (length pattern-code) 2) (phpinspect-variable-p (cadr pattern-code)))
|
||||
(let ((variable-name (cadadr pattern-code)))
|
||||
(progn
|
||||
(phpinspect--log "No assignments found for variable %s, checking function arguments: %s"
|
||||
variable-name function-arg-list)
|
||||
(setq result (phpinspect-get-variable-type-in-function-arg-list
|
||||
variable-name type-resolver function-arg-list)))))
|
||||
(setq result
|
||||
(phpinspect--interpret-expression-type-in-context
|
||||
resolvecontext php-block type-resolver
|
||||
last-assignment-value function-arg-list)))
|
||||
|
||||
(phpinspect--log "Type interpreted from last assignment expression of pattern %s: %s"
|
||||
pattern-code result)
|
||||
|
||||
(when (and result (phpinspect--type-collection result) (not (phpinspect--type-contains result)))
|
||||
(phpinspect--log (concat
|
||||
"Interpreted type %s is a collection type, but 'contains'"
|
||||
"attribute is not set. Attempting to infer type from context")
|
||||
result)
|
||||
(setq result (phpinspect--copy-type result))
|
||||
(let ((concat-pattern
|
||||
(phpinspect--pattern-concat
|
||||
pattern (phpinspect--make-pattern :f #'phpinspect-array-p))))
|
||||
(phpinspect--log "Inferring type of concatenated pattern %s"
|
||||
(phpinspect--pattern-code concat-pattern))
|
||||
(setf (phpinspect--type-contains result)
|
||||
(phpinspect-get-pattern-type-in-block
|
||||
resolvecontext concat-pattern php-block
|
||||
type-resolver function-arg-list))))
|
||||
|
||||
; return
|
||||
result))
|
||||
|
||||
(defun phpinspect--split-statements (tokens &optional predicate)
|
||||
"Split TOKENS into separate statements.
|
||||
|
||||
If PREDICATE is provided, it is used as additional predicate to
|
||||
determine whether a token delimits a statement."
|
||||
(let ((sublists)
|
||||
(current-sublist))
|
||||
(dolist (thing tokens)
|
||||
(if (or (phpinspect-statement-introduction-p thing)
|
||||
(when predicate (funcall predicate thing)))
|
||||
(when current-sublist
|
||||
(when (phpinspect-block-p thing)
|
||||
(push thing current-sublist))
|
||||
(push (nreverse current-sublist) sublists)
|
||||
(setq current-sublist nil))
|
||||
(push thing current-sublist)))
|
||||
(when current-sublist
|
||||
(push (nreverse current-sublist) sublists))
|
||||
(nreverse sublists)))
|
||||
|
||||
(defun phpinspect-get-variable-type-in-function-arg-list (variable-name type-resolver arg-list)
|
||||
"Infer VARIABLE-NAME's type from typehints in
|
||||
ARG-LIST. ARG-LIST should be a list token as returned by
|
||||
`phpinspect--list-handler` (see also `phpinspect-list-p`)"
|
||||
(let ((arg-no (seq-position arg-list
|
||||
variable-name
|
||||
(lambda (token variable-name)
|
||||
(and (phpinspect-variable-p token)
|
||||
(string= (car (last token)) variable-name))))))
|
||||
(if (and arg-no
|
||||
(> arg-no 0))
|
||||
(let ((arg (elt arg-list (- arg-no 1))))
|
||||
(if (phpinspect-word-p arg)
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name (car (last arg))))
|
||||
nil)))))
|
||||
|
||||
(defun phpinspect--interpret-expression-type-in-context
|
||||
(resolvecontext php-block type-resolver expression &optional function-arg-list)
|
||||
"Infer EXPRESSION's type from provided context.
|
||||
|
||||
Use RESOLVECONTEXT, PHP-BLOCK, TYPE-RESOLVER and
|
||||
FUNCTION-ARG-LIST as contextual information to infer type of
|
||||
EXPRESSION."
|
||||
|
||||
;; When the right of an assignment is more than $variable; or "string";(so
|
||||
;; (:variable "variable") (:terminator ";") or (:string "string") (:terminator ";")
|
||||
;; in tokens), we're likely working with a derived assignment like $object->method()
|
||||
;; or $object->attributen
|
||||
(cond ((phpinspect-array-p (car expression))
|
||||
(let ((collection-contains)
|
||||
(collection-items (phpinspect--split-statements (cdr (car expression))))
|
||||
(count 0))
|
||||
(phpinspect--log "Checking collection items: %s" collection-items)
|
||||
(while (and (< count (length collection-items))
|
||||
(not collection-contains))
|
||||
(setq collection-contains
|
||||
(phpinspect--interpret-expression-type-in-context
|
||||
resolvecontext php-block type-resolver
|
||||
(elt collection-items count) function-arg-list)
|
||||
count (+ count 1)))
|
||||
|
||||
(phpinspect--log "Collection contained: %s" collection-contains)
|
||||
|
||||
(phpinspect--make-type :name "\\array"
|
||||
:fully-qualified t
|
||||
:collection t
|
||||
:contains collection-contains)))
|
||||
((and (phpinspect-word-p (car expression))
|
||||
(string= (cadar expression) "new"))
|
||||
(funcall
|
||||
type-resolver (phpinspect--make-type :name (cadadr expression))))
|
||||
((and (> (length expression) 1)
|
||||
(seq-find (lambda (part) (or (phpinspect-attrib-p part)
|
||||
(phpinspect-array-p part)))
|
||||
expression))
|
||||
(phpinspect--log "Variable was assigned with a derived statement")
|
||||
(phpinspect-get-derived-statement-type-in-block
|
||||
resolvecontext expression php-block
|
||||
type-resolver function-arg-list))
|
||||
|
||||
;; If the right of an assignment is just $variable;, we can check if it is a
|
||||
;; function argument and otherwise recurse to find the type of that variable.
|
||||
((phpinspect-variable-p (car expression))
|
||||
(phpinspect--log "Variable was assigned with the value of another variable: %s"
|
||||
expression)
|
||||
(or (when function-arg-list
|
||||
(phpinspect-get-variable-type-in-function-arg-list
|
||||
(cadar expression)
|
||||
type-resolver function-arg-list))
|
||||
(phpinspect-get-variable-type-in-block resolvecontext
|
||||
(cadar expression)
|
||||
php-block
|
||||
type-resolver
|
||||
function-arg-list)))))
|
||||
|
||||
|
||||
(defun phpinspect-resolve-type-from-context (resolvecontext &optional type-resolver)
|
||||
(unless type-resolver
|
||||
(setq type-resolver
|
||||
(phpinspect--make-type-resolver-for-resolvecontext resolvecontext)))
|
||||
(phpinspect--log "Looking for type of statement: %s in nested token"
|
||||
(phpinspect--resolvecontext-subject resolvecontext))
|
||||
;; Find all enclosing tokens that aren't classes. Classes do not contain variable
|
||||
;; assignments which have effect in the current scope, which is what we're trying
|
||||
;; to find here to infer the statement type.
|
||||
(let ((enclosing-tokens (seq-filter #'phpinspect-not-class-p
|
||||
(phpinspect--resolvecontext-enclosing-tokens
|
||||
resolvecontext)))
|
||||
(enclosing-token)
|
||||
(type))
|
||||
(while (and enclosing-tokens (not type))
|
||||
;;(phpinspect--log "Trying to find type in %s" enclosing-token)
|
||||
(setq enclosing-token (pop enclosing-tokens))
|
||||
|
||||
(setq type
|
||||
(cond ((phpinspect-namespace-p enclosing-token)
|
||||
(phpinspect-get-derived-statement-type-in-block
|
||||
resolvecontext
|
||||
(phpinspect--resolvecontext-subject
|
||||
resolvecontext)
|
||||
(or (phpinspect-namespace-block enclosing-token)
|
||||
enclosing-token)
|
||||
type-resolver))
|
||||
((or (phpinspect-block-p enclosing-token)
|
||||
(phpinspect-root-p enclosing-token))
|
||||
(phpinspect-get-derived-statement-type-in-block
|
||||
resolvecontext
|
||||
(phpinspect--resolvecontext-subject
|
||||
resolvecontext)
|
||||
enclosing-token
|
||||
type-resolver))
|
||||
((phpinspect-function-p enclosing-token)
|
||||
(phpinspect-get-derived-statement-type-in-block
|
||||
resolvecontext
|
||||
(phpinspect--resolvecontext-subject
|
||||
resolvecontext)
|
||||
(phpinspect-function-block enclosing-token)
|
||||
type-resolver
|
||||
(phpinspect-function-argument-list enclosing-token))))))
|
||||
type))
|
||||
|
||||
(provide 'phpinspect-resolve)
|
@ -1,231 +0,0 @@
|
||||
;;; phpinspect-resolvecontext.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-bmap)
|
||||
(require 'phpinspect-cache)
|
||||
(require 'phpinspect-project)
|
||||
(require 'phpinspect-token-predicates)
|
||||
(require 'phpinspect-type)
|
||||
(require 'phpinspect-meta)
|
||||
(require 'phpinspect-util)
|
||||
|
||||
|
||||
(defsubst phpinspect-blocklike-p (token)
|
||||
(or (phpinspect-block-p token)
|
||||
(phpinspect-function-p token)
|
||||
(phpinspect-class-p token)
|
||||
(phpinspect-namespace-p token)))
|
||||
|
||||
(defsubst phpinspect-return-p (token)
|
||||
(and (phpinspect-word-p token)
|
||||
(string= "return" (cadr token))))
|
||||
|
||||
(define-inline phpinspect-statement-introduction-p (token)
|
||||
(inline-letevals (token)
|
||||
(inline-quote
|
||||
(or (phpinspect-return-p ,token)
|
||||
(phpinspect-end-of-statement-p ,token)
|
||||
(phpinspect-function-p ,token)))))
|
||||
|
||||
(cl-defstruct (phpinspect--resolvecontext
|
||||
(:constructor phpinspect--make-resolvecontext))
|
||||
(subject nil
|
||||
:type phpinspect--token
|
||||
:documentation
|
||||
"The statement we're trying to resolve the type of.")
|
||||
(project-root nil
|
||||
:type string
|
||||
:documentation
|
||||
"The root directory of the project we're resolving types for.")
|
||||
(enclosing-metadata nil
|
||||
:type list
|
||||
:documentation
|
||||
"Metadata of tokens that enclose the subject.")
|
||||
(enclosing-tokens nil
|
||||
:type list
|
||||
:documentation
|
||||
"Tokens that enclose the subject."))
|
||||
|
||||
(cl-defmethod phpinspect--resolvecontext-push-enclosing-token
|
||||
((resolvecontext phpinspect--resolvecontext) enclosing-token)
|
||||
"Add ENCLOSING-TOKEN to RESOLVECONTEXTs enclosing token stack."
|
||||
(push enclosing-token (phpinspect--resolvecontext-enclosing-tokens
|
||||
resolvecontext)))
|
||||
|
||||
(defun phpinspect-find-statement-before-point (bmap meta point)
|
||||
(let ((children (reverse (phpinspect-meta-find-children-before meta point)))
|
||||
token previous-siblings)
|
||||
(catch 'return
|
||||
(dolist (child children)
|
||||
(setq token (phpinspect-meta-token child))
|
||||
|
||||
(when (< (phpinspect-meta-start child) point)
|
||||
(if (and (not previous-siblings) (phpinspect-blocklike-p token))
|
||||
(progn
|
||||
(throw 'return (phpinspect-find-statement-before-point bmap child point)))
|
||||
(when (phpinspect-statement-introduction-p token)
|
||||
(throw 'return previous-siblings))
|
||||
(unless (phpinspect-comment-p token)
|
||||
(push child previous-siblings))))))
|
||||
previous-siblings))
|
||||
|
||||
(defun phpinspect--get-last-statement-in-token (token)
|
||||
(setq token (cond ((phpinspect-function-p token)
|
||||
(phpinspect-function-block token))
|
||||
((phpinspect-namespace-p token)
|
||||
(phpinspect-namespace-block token))
|
||||
(t token)))
|
||||
(nreverse
|
||||
(seq-take-while
|
||||
(let ((keep-taking t) (last-test nil))
|
||||
(lambda (elt)
|
||||
(when last-test
|
||||
(setq keep-taking nil))
|
||||
(setq last-test (phpinspect-variable-p elt))
|
||||
(and keep-taking
|
||||
(not (phpinspect-end-of-statement-p elt))
|
||||
(listp elt))))
|
||||
(reverse token))))
|
||||
|
||||
(cl-defmethod phpinspect-get-resolvecontext
|
||||
((bmap phpinspect-bmap) (point integer))
|
||||
(let* ((enclosing-tokens)
|
||||
;; When there are no enclosing tokens, point is probably at the absolute
|
||||
;; end of the buffer, so we find the last child before point.
|
||||
(subject (phpinspect-bmap-last-token-before-point bmap point))
|
||||
(subject-token))
|
||||
(phpinspect--log "Last token before point: %s, right siblings: %s, parent: %s"
|
||||
(phpinspect-meta-string subject)
|
||||
(mapcar #'phpinspect-meta-token (phpinspect-meta-right-siblings subject))
|
||||
(phpinspect-meta-string (phpinspect-meta-parent subject)))
|
||||
|
||||
(let ((next-sibling (car (phpinspect-meta-right-siblings subject))))
|
||||
;; When the right sibling of the last ending token overlaps point, this is
|
||||
;; our actual subject.
|
||||
(when (and next-sibling
|
||||
(phpinspect-meta-overlaps-point next-sibling point))
|
||||
(setq subject next-sibling)))
|
||||
|
||||
;; Dig down through tokens that can contain statements
|
||||
(let (new-subject)
|
||||
(catch 'break
|
||||
(while (and subject
|
||||
(phpinspect-enclosing-token-p (phpinspect-meta-token subject))
|
||||
(cdr (phpinspect-meta-token subject)))
|
||||
(setq new-subject (phpinspect-meta-find-child-before subject point))
|
||||
(if new-subject
|
||||
(setq subject new-subject)
|
||||
(throw 'break nil)))))
|
||||
|
||||
(phpinspect--log "Initial resolvecontext subject token: %s"
|
||||
(phpinspect-meta-token subject))
|
||||
(when subject
|
||||
(setq subject-token
|
||||
(mapcar #'phpinspect-meta-token
|
||||
(phpinspect-find-statement-before-point
|
||||
bmap (phpinspect-meta-parent subject) point)))
|
||||
|
||||
(phpinspect--log "Ultimate resolvecontext subject token: %s. Parent: %s"
|
||||
subject-token (phpinspect-meta-token
|
||||
(phpinspect-meta-parent subject)))
|
||||
|
||||
;; Iterate through subject parents to build stack of enclosing tokens
|
||||
(let ((parent (phpinspect-meta-parent subject)))
|
||||
(while parent
|
||||
(let ((granny (phpinspect-meta-parent parent)))
|
||||
(unless (and (phpinspect-block-p (phpinspect-meta-token parent))
|
||||
(or (not granny)
|
||||
(phpinspect-function-p (phpinspect-meta-token granny))
|
||||
(phpinspect-class-p (phpinspect-meta-token granny))))
|
||||
(push parent enclosing-tokens))
|
||||
(setq parent (phpinspect-meta-parent parent))))))
|
||||
|
||||
(phpinspect--make-resolvecontext
|
||||
:subject (phpinspect--get-last-statement-in-token subject-token)
|
||||
:enclosing-tokens (nreverse (mapcar #'phpinspect-meta-token enclosing-tokens))
|
||||
:enclosing-metadata (nreverse enclosing-tokens)
|
||||
:project-root (phpinspect-current-project-root))))
|
||||
|
||||
(defun phpinspect--resolvecontext-project (rctx)
|
||||
(phpinspect--cache-get-project-create
|
||||
(phpinspect--get-or-create-global-cache)
|
||||
(phpinspect--resolvecontext-project-root rctx)))
|
||||
|
||||
(defun phpinspect--get-resolvecontext (token &optional resolvecontext)
|
||||
"Find the deepest nested incomplete token in TOKEN.
|
||||
If RESOLVECONTEXT is nil, it is created. Returns RESOLVECONTEXT
|
||||
of type `phpinspect--resolvecontext' containing the last
|
||||
statement of the innermost incomplete token as subject
|
||||
accompanied by all of its enclosing tokens."
|
||||
(unless resolvecontext
|
||||
(setq resolvecontext (phpinspect--make-resolvecontext
|
||||
:project-root (phpinspect-current-project-root))))
|
||||
|
||||
(let ((last-token (car (last token)))
|
||||
(last-encountered-token (car
|
||||
(phpinspect--resolvecontext-enclosing-tokens
|
||||
resolvecontext))))
|
||||
(unless (and (or (phpinspect-function-p last-encountered-token)
|
||||
(phpinspect-class-p last-encountered-token))
|
||||
(phpinspect-block-p token))
|
||||
;; When a class or function has been inserted already, its block
|
||||
;; doesn't need to be added on top.
|
||||
(phpinspect--resolvecontext-push-enclosing-token resolvecontext token))
|
||||
|
||||
(if (phpinspect-incomplete-token-p last-token)
|
||||
(phpinspect--get-resolvecontext last-token resolvecontext)
|
||||
;; else
|
||||
(setf (phpinspect--resolvecontext-subject resolvecontext)
|
||||
(phpinspect--get-last-statement-in-token token))
|
||||
|
||||
resolvecontext)))
|
||||
|
||||
(defun phpinspect--make-type-resolver-for-resolvecontext
|
||||
(resolvecontext)
|
||||
(let ((namespace-or-root
|
||||
(seq-find #'phpinspect-namespace-or-root-p
|
||||
(phpinspect--resolvecontext-enclosing-tokens
|
||||
resolvecontext)))
|
||||
(namespace-name))
|
||||
(when (phpinspect-namespace-p namespace-or-root)
|
||||
(setq namespace-name (cadadr namespace-or-root))
|
||||
(setq namespace-or-root (phpinspect-namespace-body namespace-or-root)))
|
||||
|
||||
(phpinspect--make-type-resolver
|
||||
(phpinspect--uses-to-types
|
||||
(seq-filter #'phpinspect-use-p namespace-or-root))
|
||||
(seq-find #'phpinspect-class-p
|
||||
(phpinspect--resolvecontext-enclosing-tokens
|
||||
resolvecontext))
|
||||
namespace-name)))
|
||||
|
||||
(cl-defmethod phpinspect--resolvecontext-pop-meta ((rctx phpinspect--resolvecontext))
|
||||
"Remove the first element of enclosing token metadata and
|
||||
return it. Pops enclosing tokens to keep both in sync."
|
||||
(pop (phpinspect--resolvecontext-enclosing-tokens rctx))
|
||||
(pop (phpinspect--resolvecontext-enclosing-metadata rctx)))
|
||||
|
||||
(provide 'phpinspect-resolvecontext)
|
||||
;;; phpinspect-resolvecontext.el ends here
|
@ -1,114 +0,0 @@
|
||||
;;; phpinspect-index.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-class)
|
||||
|
||||
(cl-defgeneric phpinspect--serialize-type (_type)
|
||||
nil)
|
||||
|
||||
(cl-defmethod phpinspect--serialize-type ((type phpinspect--type))
|
||||
`(phpinspect--make-type
|
||||
:name ,(phpinspect--type-name type)
|
||||
:collection ,(phpinspect--type-collection type)
|
||||
:contains ,(when (phpinspect--type-contains type)
|
||||
(phpinspect--serialize-type (phpinspect--type-contains type)))
|
||||
:fully-qualified ,(phpinspect--type-fully-qualified type)))
|
||||
|
||||
;; (cl-defmethod phpinspect--serialize-function (_func)
|
||||
;; nil)
|
||||
|
||||
(cl-defmethod phpinspect--serialize-function ((func phpinspect--function))
|
||||
`(phpinspect--make-function
|
||||
:name ,(phpinspect--function-name func)
|
||||
:token (quote ,(phpinspect--function-token func))
|
||||
:scope (quote ,(phpinspect--function-scope func))
|
||||
:arguments ,(append '(list)
|
||||
(mapcar (lambda (arg)
|
||||
`(list ,(car arg) ,(phpinspect--serialize-type (cadr arg))))
|
||||
(phpinspect--function-arguments func)))
|
||||
:return-type ,(when (phpinspect--function-return-type func)
|
||||
(phpinspect--serialize-type
|
||||
(phpinspect--function-return-type func)))))
|
||||
|
||||
(cl-defmethod phpinspect--serialize-variable ((var phpinspect--variable))
|
||||
`(phpinspect--make-variable :name ,(phpinspect--variable-name var)
|
||||
:type ,(when (phpinspect--variable-type var)
|
||||
(phpinspect--serialize-type
|
||||
(phpinspect--variable-type var)))
|
||||
:scope (quote ,(phpinspect--variable-scope var))))
|
||||
|
||||
(cl-defmethod phpinspect--serialize-indexed-class ((class (head phpinspect--indexed-class)))
|
||||
``(phpinspect--indexed-class
|
||||
(complete . ,,(alist-get 'complete class))
|
||||
(class-name . ,,(phpinspect--serialize-type (alist-get 'class-name class)))
|
||||
(declaration . ,(quote ,(alist-get 'declaration class)))
|
||||
(imports . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-import
|
||||
(alist-get 'imports class))))
|
||||
(methods . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-function
|
||||
(alist-get 'methods class))))
|
||||
(static-methods . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-function
|
||||
(alist-get 'static-methods class))))
|
||||
(static-variables . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-variable
|
||||
(alist-get 'static-variables class))))
|
||||
(variables . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-variable
|
||||
(alist-get 'variables class))))
|
||||
(constants . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-variable
|
||||
(alist-get 'constants class))))
|
||||
(extends . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-type
|
||||
(alist-get 'extends class))))
|
||||
(implements . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-type
|
||||
(alist-get 'implements class))))))
|
||||
|
||||
(cl-defmethod phpinspect--serialize-root-index ((index (head phpinspect--root-index)))
|
||||
``(phpinspect--root-index
|
||||
(imports . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-import
|
||||
(alist-get 'imports index))))
|
||||
(classes . ,(list
|
||||
,@(mapcar (lambda (cons-class)
|
||||
`(cons ,(phpinspect--serialize-type (car cons-class))
|
||||
,(phpinspect--serialize-indexed-class (cdr cons-class))))
|
||||
(alist-get 'classes index))))
|
||||
(functions . ,,(append '(list)
|
||||
(mapcar #'phpinspect--serialize-function
|
||||
(alist-get 'functions index))))))
|
||||
|
||||
|
||||
(defun phpinspect--serialize-import (import)
|
||||
`(cons
|
||||
(phpinspect-intern-name ,(symbol-name (car import)))
|
||||
,(phpinspect--serialize-type (cdr import))))
|
||||
|
||||
(provide 'phpinspect-serialize)
|
||||
;;; phpinspect-serialize.el ends here
|
@ -1,499 +0,0 @@
|
||||
;;; phpinspect-splayt.el --- A Splay Tree Implementation -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
;;
|
||||
;; A splay tree implementation exclusively using `cons' and `list' data
|
||||
;; structures with the aim to have as low a memory footprint as possible.
|
||||
;;
|
||||
;; Important functions:
|
||||
;; - `phpinspect-splayt-insert'
|
||||
;; - `phpinspect-splayt-find'
|
||||
;; - `phpinspect-splayt-find-smallest-after'
|
||||
;; - `phpinspect-splayt-find-all-after'
|
||||
;; - `phpinspect-splayt-traverse'
|
||||
;;;
|
||||
;;
|
||||
;; DEVELOPING
|
||||
;;
|
||||
;; The main aim for this tree implementation is to be reasonably fast and
|
||||
;; comfortable to use for most of phpinspect's common operations. That means:
|
||||
;;
|
||||
;; - Fast insertion of sequential keys (for example when parsing a buffer from left to right)
|
||||
;; - Consing as few bytes as possible by keeping the data structure simple, to avoid GC pauses as much as possible
|
||||
;; - Fast repeated acces of "hot" regions (for example the edited region of a buffer)
|
||||
;; - A straight forward public API to retrieve sets of nodes
|
||||
;;
|
||||
;; ** Inline Functions **
|
||||
;; There is a lot of use of `define-inline' in this file. Most of these inlines
|
||||
;; improve performance significantly. This is especially true for smaller
|
||||
;; inlined functions. It might be possible to change one or two of the larger
|
||||
;; functions to normal defuns without reintroducing a lot of ovehead. If you
|
||||
;; want to do this to make debugging the code a little easier, and you can
|
||||
;; backup that it doesn't impact performance all that much with some benchmarks
|
||||
;; (especially parse-file.el in the benchmarks folder), your PR will be welcomed.
|
||||
;;
|
||||
|
||||
;;; Code:
|
||||
|
||||
(define-inline phpinspect-make-splayt-node (key value &optional left right parent temp-store)
|
||||
(inline-quote (cons (cons ,key ,value) (list ,left ,right ,parent ,temp-store))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-left (node)
|
||||
(inline-quote (cadr ,node)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-right (node)
|
||||
(inline-quote (caddr ,node)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key (node)
|
||||
(inline-quote (caar ,node)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-value (node)
|
||||
(inline-quote (cdar ,node)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-parent (node)
|
||||
(inline-quote (cadddr ,node)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-temp-store (node)
|
||||
"Dedicated place to store data when necessary. Mostly used for
|
||||
rotations without instantiating a lexical environment with
|
||||
`let'. When only used once or twice in a function call, this
|
||||
apeared to be a little more performant than using `let'."
|
||||
(inline-quote (car (cddddr ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-update-parent (node parent new-val)
|
||||
(inline-letevals (node parent new-val)
|
||||
(inline-quote
|
||||
(if (eq ,node (phpinspect-splayt-node-left ,parent))
|
||||
(setf (phpinspect-splayt-node-left ,parent) ,new-val)
|
||||
(setf (phpinspect-splayt-node-right ,parent) ,new-val)))))
|
||||
|
||||
(define-inline phpinspect-make-splayt (&optional root-node)
|
||||
(inline-quote
|
||||
(cons 'phpinspect-splayt ,root-node)))
|
||||
|
||||
(define-inline phpinspect-splayt-root-node (splayt)
|
||||
(inline-quote (cdr ,splayt)))
|
||||
|
||||
(define-inline phpinspect-splayt-empty-p (splayt)
|
||||
(inline-quote (not (phpinspect-splayt-root-node ,splayt))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-rotate-right (node &optional splayt)
|
||||
(inline-letevals (node splayt)
|
||||
(inline-quote
|
||||
(progn
|
||||
;; Save right node of left child
|
||||
(setf (phpinspect-splayt-node-temp-store ,node)
|
||||
(phpinspect-splayt-node-right (phpinspect-splayt-node-left ,node)))
|
||||
|
||||
;; Update node parent to reference left as child
|
||||
(when (phpinspect-splayt-node-parent ,node)
|
||||
(phpinspect-splayt-node-update-parent
|
||||
,node (phpinspect-splayt-node-parent ,node) (phpinspect-splayt-node-left ,node)))
|
||||
|
||||
;; Set left node new parent
|
||||
(setf (phpinspect-splayt-node-parent (phpinspect-splayt-node-left ,node))
|
||||
(phpinspect-splayt-node-parent ,node))
|
||||
|
||||
;; Set left node as node's new parent
|
||||
(setf (phpinspect-splayt-node-parent ,node)
|
||||
(phpinspect-splayt-node-left ,node))
|
||||
|
||||
;; Set node as left's right child
|
||||
(setf (phpinspect-splayt-node-right (phpinspect-splayt-node-left ,node)) ,node)
|
||||
|
||||
;; Set left's right child as node's left child
|
||||
(setf (phpinspect-splayt-node-left ,node) (phpinspect-splayt-node-temp-store ,node))
|
||||
|
||||
;; Update new left child's parent
|
||||
(when (phpinspect-splayt-node-left ,node)
|
||||
(setf (phpinspect-splayt-node-parent (phpinspect-splayt-node-left ,node)) ,node))
|
||||
|
||||
;; Update root node of tree when necessary
|
||||
(when (and ,splayt (eq ,node (phpinspect-splayt-root-node ,splayt)))
|
||||
(setf (phpinspect-splayt-root-node ,splayt) (phpinspect-splayt-node-parent ,node)))))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-rotate-left (node &optional splayt)
|
||||
(inline-letevals (node splayt)
|
||||
(inline-quote
|
||||
(progn
|
||||
;; Save left node of right child
|
||||
(setf (phpinspect-splayt-node-temp-store ,node)
|
||||
(phpinspect-splayt-node-left (phpinspect-splayt-node-right ,node)))
|
||||
|
||||
;; Update node parent to reference right as child
|
||||
(when (phpinspect-splayt-node-parent ,node)
|
||||
(phpinspect-splayt-node-update-parent
|
||||
,node (phpinspect-splayt-node-parent ,node) (phpinspect-splayt-node-right ,node)))
|
||||
|
||||
;; Set right node new parent
|
||||
(setf (phpinspect-splayt-node-parent (phpinspect-splayt-node-right ,node))
|
||||
(phpinspect-splayt-node-parent ,node))
|
||||
|
||||
;; Set right node as node's new parent
|
||||
(setf (phpinspect-splayt-node-parent ,node)
|
||||
(phpinspect-splayt-node-right ,node))
|
||||
|
||||
;; Set node as right's left child
|
||||
(setf (phpinspect-splayt-node-left (phpinspect-splayt-node-right ,node)) ,node)
|
||||
|
||||
;; Set right's left child as node's right child
|
||||
(setf (phpinspect-splayt-node-right ,node) (phpinspect-splayt-node-temp-store ,node))
|
||||
|
||||
;; Update new right child's parent
|
||||
(when (phpinspect-splayt-node-right ,node)
|
||||
(setf (phpinspect-splayt-node-parent (phpinspect-splayt-node-right ,node)) ,node))
|
||||
|
||||
;; Update root node of tree when necessary
|
||||
(when (and ,splayt (eq ,node (phpinspect-splayt-root-node ,splayt)))
|
||||
(setf (phpinspect-splayt-root-node ,splayt) (phpinspect-splayt-node-parent ,node)))))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-grandparent (node)
|
||||
(inline-quote (phpinspect-splayt-node-parent (phpinspect-splayt-node-parent ,node))))
|
||||
|
||||
(define-inline phpinspect-splay (splayt node)
|
||||
(inline-letevals (splayt node)
|
||||
(let ((parent (inline-quote (phpinspect-splayt-node-parent ,node)))
|
||||
(grandparent (inline-quote (phpinspect-splayt-node-grandparent ,node))))
|
||||
(inline-quote
|
||||
(progn
|
||||
(while ,parent
|
||||
(if (phpinspect-splayt-node-grandparent ,node)
|
||||
(cond
|
||||
;; Zig-Zig rotation
|
||||
((and (eq ,parent (phpinspect-splayt-node-left ,grandparent))
|
||||
(eq ,node (phpinspect-splayt-node-left ,parent)))
|
||||
(phpinspect-splayt-node-rotate-right ,grandparent ,splayt)
|
||||
(phpinspect-splayt-node-rotate-right ,parent ,splayt))
|
||||
|
||||
;; Zag-Zag rotation
|
||||
((and (eq (phpinspect-splayt-node-parent ,node)
|
||||
(phpinspect-splayt-node-right (phpinspect-splayt-node-grandparent ,node)))
|
||||
(eq ,node (phpinspect-splayt-node-right (phpinspect-splayt-node-parent ,node))))
|
||||
(phpinspect-splayt-node-rotate-left ,grandparent ,splayt)
|
||||
(phpinspect-splayt-node-rotate-left ,parent ,splayt))
|
||||
|
||||
;; Zig-Zag rotation
|
||||
((and (eq ,parent (phpinspect-splayt-node-right ,grandparent))
|
||||
(eq ,node (phpinspect-splayt-node-left ,parent)))
|
||||
(phpinspect-splayt-node-rotate-right ,parent ,splayt)
|
||||
(phpinspect-splayt-node-rotate-left ,parent ,splayt))
|
||||
|
||||
;; Zag-Zig rotation
|
||||
((and (eq ,parent (phpinspect-splayt-node-left ,grandparent))
|
||||
(eq ,node (phpinspect-splayt-node-right ,parent)))
|
||||
(phpinspect-splayt-node-rotate-left ,parent ,splayt)
|
||||
(phpinspect-splayt-node-rotate-right ,parent ,splayt))
|
||||
(t
|
||||
(error "Failed in determining rotation strategy.")))
|
||||
|
||||
;; Else
|
||||
(if (eq ,node (phpinspect-splayt-node-left ,parent))
|
||||
(phpinspect-splayt-node-rotate-right ,parent ,splayt)
|
||||
(phpinspect-splayt-node-rotate-left ,parent ,splayt))))
|
||||
|
||||
,node)))))
|
||||
|
||||
(define-inline phpinspect-splayt-insert-node (splayt node)
|
||||
(inline-letevals (splayt node (parent (inline-quote (phpinspect-splayt-node-temp-store ,node))))
|
||||
(inline-quote
|
||||
(if (phpinspect-splayt-empty-p ,splayt)
|
||||
(setf (phpinspect-splayt-root-node ,splayt) ,node)
|
||||
(progn
|
||||
(setf ,parent (phpinspect-splayt-find-insertion-node ,splayt (phpinspect-splayt-node-key ,node)))
|
||||
(unless ,parent
|
||||
(error "Error: failed to find parent node for %s" ,node))
|
||||
|
||||
(setf (phpinspect-splayt-node-parent ,node) ,parent)
|
||||
(if (< (phpinspect-splayt-node-key ,parent) (phpinspect-splayt-node-key ,node))
|
||||
(setf (phpinspect-splayt-node-right ,parent) ,node)
|
||||
(setf (phpinspect-splayt-node-left ,parent) ,node))
|
||||
|
||||
(phpinspect-splay ,splayt ,node))))))
|
||||
|
||||
(defmacro phpinspect-splayt-node-traverse (place-and-node &rest body)
|
||||
(declare (indent 1))
|
||||
(let ((place (car place-and-node))
|
||||
(current-sym (gensym))
|
||||
(stack-sym (gensym))
|
||||
(queue-sym (gensym))
|
||||
(reverse-sym (gensym))
|
||||
(node-sym (gensym))
|
||||
(size-sym (gensym)))
|
||||
`(let* ((,node-sym ,(cadr place-and-node))
|
||||
;; Make place locally scoped variable if a symbol
|
||||
(,queue-sym (when ,node-sym
|
||||
(list ,node-sym)))
|
||||
(,reverse-sym t)
|
||||
,current-sym
|
||||
,size-sym
|
||||
,stack-sym)
|
||||
|
||||
(while ,queue-sym
|
||||
(setq ,size-sym (length ,queue-sym))
|
||||
|
||||
(while (> ,size-sym 0)
|
||||
(setq ,current-sym (car (last ,queue-sym))
|
||||
,queue-sym (butlast ,queue-sym))
|
||||
|
||||
(if ,reverse-sym
|
||||
(push ,current-sym ,stack-sym)
|
||||
(let ((,place ,current-sym))
|
||||
,@body))
|
||||
|
||||
(when (phpinspect-splayt-node-right ,current-sym)
|
||||
(push (phpinspect-splayt-node-right ,current-sym) ,queue-sym))
|
||||
|
||||
(when (phpinspect-splayt-node-left ,current-sym)
|
||||
(push (phpinspect-splayt-node-left ,current-sym) ,queue-sym))
|
||||
|
||||
(setq ,size-sym (- ,size-sym 1)))
|
||||
|
||||
(when ,reverse-sym
|
||||
(while ,stack-sym
|
||||
(setq ,current-sym (pop ,stack-sym))
|
||||
(let ((,place ,current-sym))
|
||||
,@body)))
|
||||
|
||||
(setq ,reverse-sym (not ,reverse-sym)))
|
||||
|
||||
nil)))
|
||||
|
||||
(defmacro phpinspect-splayt-traverse (place-and-splayt &rest body)
|
||||
"Traverse SPLAYT, executing BODY.
|
||||
|
||||
The PLACE is assigned the value of each node.
|
||||
|
||||
Traversal is breadth-first to take advantage of the splay trees'
|
||||
main benefit: the most accessed interval of keys is likely to be
|
||||
near the top of the tee.
|
||||
|
||||
(fn (PLACE SPLAYT) BODY...)"
|
||||
(declare (indent 1))
|
||||
`(phpinspect-splayt-node-traverse
|
||||
(,(car place-and-splayt) (phpinspect-splayt-root-node ,(cadr place-and-splayt)))
|
||||
(setf ,(car place-and-splayt) (phpinspect-splayt-node-value ,(car place-and-splayt)))
|
||||
,@body))
|
||||
|
||||
(defmacro phpinspect-splayt-node-traverse-lr (place-and-node &rest body)
|
||||
(declare (indent 1))
|
||||
(let ((place (car place-and-node))
|
||||
(current (gensym))
|
||||
(stack (gensym)))
|
||||
`(let* ((,current ,(cadr place-and-node))
|
||||
,stack)
|
||||
(while (or ,stack ,current)
|
||||
(if ,current
|
||||
(progn
|
||||
(push ,current ,stack)
|
||||
(setq ,current (phpinspect-splayt-node-left ,current)))
|
||||
(setq ,current (pop ,stack))
|
||||
(let ((,place ,current))
|
||||
,@body)
|
||||
(setq ,current (phpinspect-splayt-node-right ,current)))))))
|
||||
|
||||
(defmacro phpinspect-splayt-traverse-lr (place-and-splayt &rest body)
|
||||
"Traverse SPLAYT depth-first from left to right,executing BODY.
|
||||
|
||||
The PLACE is assigned the value of each node.
|
||||
|
||||
(fn (PLACE SPLAYT) BODY...)"
|
||||
(declare (indent 1))
|
||||
`(phpinspect-splayt-node-traverse-lr
|
||||
(,(car place-and-splayt) (phpinspect-splayt-root-node ,(cadr place-and-splayt)))
|
||||
(let ((,(car place-and-splayt) (phpinspect-splayt-node-value ,(car place-and-splayt))))
|
||||
,@body)))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key-less-p (node key)
|
||||
(inline-quote (> ,key (phpinspect-splayt-node-key ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key-le-p (node key)
|
||||
(inline-quote (>= ,key (phpinspect-splayt-node-key ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key-equal-p (node key)
|
||||
(inline-quote (= ,key (phpinspect-splayt-node-key ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key-greater-p (node key)
|
||||
(inline-quote (< ,key (phpinspect-splayt-node-key ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-node-key-ge-p (node key)
|
||||
(inline-quote (<= ,key (phpinspect-splayt-node-key ,node))))
|
||||
|
||||
(define-inline phpinspect-splayt-find-node (splayt key)
|
||||
(inline-letevals (splayt key)
|
||||
(inline-quote
|
||||
(let ((current (phpinspect-splayt-root-node ,splayt)))
|
||||
(catch 'return
|
||||
(while current
|
||||
(if (= ,key (phpinspect-splayt-node-key current))
|
||||
(progn
|
||||
(phpinspect-splay ,splayt current)
|
||||
(throw 'return current))
|
||||
(if (phpinspect-splayt-node-key-greater-p current ,key)
|
||||
(setq current (phpinspect-splayt-node-left current))
|
||||
(setq current (phpinspect-splayt-node-right current))))))))))
|
||||
|
||||
(define-inline phpinspect-splayt-find-insertion-node (splayt key)
|
||||
(inline-letevals (splayt key)
|
||||
(inline-quote
|
||||
(let ((current (phpinspect-splayt-root-node ,splayt)))
|
||||
(catch 'return
|
||||
(while current
|
||||
(if (or (and (phpinspect-splayt-node-key-greater-p current ,key)
|
||||
(not (phpinspect-splayt-node-left current)))
|
||||
(and (phpinspect-splayt-node-key-le-p current ,key)
|
||||
(not (phpinspect-splayt-node-right current))))
|
||||
(throw 'return current)
|
||||
(if (< ,key (phpinspect-splayt-node-key current))
|
||||
(setq current (phpinspect-splayt-node-left current))
|
||||
(setq current (phpinspect-splayt-node-right current))))))))))
|
||||
|
||||
(define-inline phpinspect-splayt-find-smallest-node-after (splayt key)
|
||||
(inline-letevals (splayt key)
|
||||
(inline-quote
|
||||
(let ((current (phpinspect-splayt-root-node ,splayt))
|
||||
smallest)
|
||||
|
||||
(catch 'break
|
||||
(while current
|
||||
(cond
|
||||
((phpinspect-splayt-node-key-greater-p current ,key)
|
||||
(when (and smallest
|
||||
(phpinspect-splayt-node-key-greater-p
|
||||
current (phpinspect-splayt-node-key smallest)))
|
||||
(throw 'break nil))
|
||||
|
||||
(setf smallest current
|
||||
current (phpinspect-splayt-node-left current)))
|
||||
((phpinspect-splayt-node-right current)
|
||||
(setf current (phpinspect-splayt-node-right current)))
|
||||
(t (throw 'break nil)))))
|
||||
|
||||
smallest))))
|
||||
|
||||
(define-inline phpinspect-splayt-find-largest-node-before (splayt key)
|
||||
(inline-letevals (splayt key)
|
||||
(inline-quote
|
||||
(let ((current (phpinspect-splayt-root-node ,splayt))
|
||||
largest)
|
||||
|
||||
(catch 'break
|
||||
(while current
|
||||
(cond
|
||||
((and (phpinspect-splayt-node-key-less-p current ,key))
|
||||
(when (and largest
|
||||
(phpinspect-splayt-node-key-less-p
|
||||
current (phpinspect-splayt-node-key largest)))
|
||||
(throw 'break nil))
|
||||
(setf largest current
|
||||
current (phpinspect-splayt-node-right current)))
|
||||
((phpinspect-splayt-node-left current)
|
||||
(setf current (phpinspect-splayt-node-left current)))
|
||||
(t (throw 'break nil)))))
|
||||
|
||||
largest))))
|
||||
|
||||
(defun phpinspect-splayt-find-all-after (splayt key)
|
||||
"Find all values in SPLAYT with a key higher than KEY."
|
||||
(let* ((first (phpinspect-splayt-find-smallest-node-after splayt key))
|
||||
(all (cons nil nil))
|
||||
(all-rear all))
|
||||
(while first
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value first) nil)))
|
||||
|
||||
(phpinspect-splayt-node-traverse (sibling (phpinspect-splayt-node-right first))
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value sibling) nil))))
|
||||
|
||||
(if (and (phpinspect-splayt-node-parent first)
|
||||
(eq first (phpinspect-splayt-node-left (phpinspect-splayt-node-parent first))))
|
||||
(setq first (phpinspect-splayt-node-parent first))
|
||||
(setq first nil)))
|
||||
(cdr all)))
|
||||
|
||||
(defun phpinspect-splayt-find-all-between (splayt key-min key-max)
|
||||
(let* ((first (phpinspect-splayt-find-smallest-node-after splayt key-min))
|
||||
(all (cons nil nil))
|
||||
(all-rear all))
|
||||
(catch 'return
|
||||
(while first
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value first) nil)))
|
||||
|
||||
(phpinspect-splayt-node-traverse-lr (sibling (phpinspect-splayt-node-right first))
|
||||
(when (>= (phpinspect-splayt-node-key sibling) key-max)
|
||||
(throw 'return (cdr all)))
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value sibling) nil))))
|
||||
|
||||
(if (and (phpinspect-splayt-node-parent first)
|
||||
(eq first (phpinspect-splayt-node-left (phpinspect-splayt-node-parent first))))
|
||||
(setq first (phpinspect-splayt-node-parent first))
|
||||
(setq first nil)))
|
||||
(cdr all))))
|
||||
|
||||
(defun phpinspect-splayt-find-all-before (splayt key)
|
||||
"Find all values in SPLAYT with a key higher than KEY."
|
||||
(let* ((first (phpinspect-splayt-find-largest-node-before splayt key))
|
||||
(all (cons nil nil))
|
||||
(all-rear all))
|
||||
(while first
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value first) nil)))
|
||||
|
||||
(phpinspect-splayt-node-traverse (sibling (phpinspect-splayt-node-left first))
|
||||
(setq all-rear (setcdr all-rear (cons (phpinspect-splayt-node-value sibling) nil))))
|
||||
|
||||
(if (and (phpinspect-splayt-node-parent first)
|
||||
(eq first (phpinspect-splayt-node-right (phpinspect-splayt-node-parent first))))
|
||||
(setq first (phpinspect-splayt-node-parent first))
|
||||
(setq first nil)))
|
||||
(cdr all)))
|
||||
|
||||
(defun phpinspect-splayt-to-list (tree)
|
||||
"Convert TREE to an ordered list."
|
||||
(let* ((list (cons nil nil))
|
||||
(rear list))
|
||||
(phpinspect-splayt-traverse-lr (val tree)
|
||||
(setq rear (setcdr rear (cons val nil))))
|
||||
|
||||
(cdr list)))
|
||||
|
||||
(cl-defmethod seq-do (func (tree (head phpinspect-splayt)))
|
||||
(phpinspect-splayt-traverse-lr (val tree)
|
||||
(funcall func val)))
|
||||
|
||||
(defun phpinspect-splayt-find-smallest-after (splayt key)
|
||||
"Find value of node with smallest key that is higher than KEY in SPLAYT."
|
||||
(phpinspect-splayt-node-value
|
||||
(phpinspect-splay
|
||||
splayt (phpinspect-splayt-find-smallest-node-after splayt key))))
|
||||
|
||||
(defun phpinspect-splayt-find-largest-before (splayt key)
|
||||
"Find value of node with smallest key that is higher than KEY in SPLAYT."
|
||||
(phpinspect-splayt-node-value
|
||||
(phpinspect-splay
|
||||
splayt (phpinspect-splayt-find-largest-node-before splayt key))))
|
||||
|
||||
(defun phpinspect-splayt-find (splayt key)
|
||||
(phpinspect-splayt-node-value (phpinspect-splayt-find-node splayt key)))
|
||||
|
||||
(defun phpinspect-splayt-insert (tree key value)
|
||||
"Insert KEY as VALUE into TREE."
|
||||
(phpinspect-splayt-insert-node tree (phpinspect-make-splayt-node key value)))
|
||||
|
||||
(provide 'phpinspect-splayt)
|
@ -1,144 +0,0 @@
|
||||
;;; phpinspect-suggest.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-resolvecontext)
|
||||
(require 'phpinspect-resolve)
|
||||
(require 'phpinspect-token-predicates)
|
||||
(require 'phpinspect-type)
|
||||
(require 'phpinspect-project)
|
||||
(require 'phpinspect-class)
|
||||
|
||||
(defun phpinspect-suggest-functions (rctx)
|
||||
(let* ((project (phpinspect--resolvecontext-project rctx)))
|
||||
(phpinspect-project-get-functions-with-extra project)))
|
||||
|
||||
(defun phpinspect-suggest-variables-at-point (resolvecontext)
|
||||
(phpinspect--log "Suggesting variables at point")
|
||||
(let ((variables))
|
||||
(dolist (token (phpinspect--resolvecontext-enclosing-tokens resolvecontext))
|
||||
(when (phpinspect-not-class-p token)
|
||||
(let ((token-list token)
|
||||
(potential-variable))
|
||||
(while token-list
|
||||
(setq potential-variable (pop token-list))
|
||||
(cond ((phpinspect-variable-p potential-variable)
|
||||
(phpinspect--log "Pushing variable %s" potential-variable)
|
||||
(push (phpinspect--make-variable
|
||||
:name (cadr potential-variable)
|
||||
:type phpinspect--null-type)
|
||||
variables))
|
||||
((phpinspect-function-p potential-variable)
|
||||
(push (phpinspect-function-block potential-variable) token-list)
|
||||
(dolist (argument (phpinspect-function-argument-list potential-variable))
|
||||
(when (phpinspect-variable-p argument)
|
||||
(push (phpinspect--make-variable
|
||||
:name (cadr argument)
|
||||
:type phpinspect--null-type)
|
||||
variables))))
|
||||
((phpinspect-block-p potential-variable)
|
||||
(dolist (nested-token (cdr potential-variable))
|
||||
(push nested-token token-list))))))))
|
||||
|
||||
;; Only return variables that have a name. Unnamed variables are just dollar
|
||||
;; signs (:
|
||||
(seq-filter #'phpinspect--variable-name variables)))
|
||||
|
||||
(defun phpinspect-get-cached-project-class-methods (project-root class-fqn &optional static)
|
||||
(phpinspect--log "Getting cached project class methods for %s (%s)"
|
||||
project-root class-fqn)
|
||||
(when project-root
|
||||
(let ((class (phpinspect-get-or-create-cached-project-class
|
||||
project-root
|
||||
class-fqn)))
|
||||
(phpinspect--log (if class
|
||||
"Retrieved class index, starting method collection %s (%s)"
|
||||
"No class index found in %s for %s")
|
||||
project-root class-fqn)
|
||||
|
||||
(when class
|
||||
(if static
|
||||
(phpinspect--class-get-static-method-list class)
|
||||
(phpinspect--class-get-method-list class))))))
|
||||
|
||||
(defun phpinspect--get-methods-for-class
|
||||
(resolvecontext class &optional static)
|
||||
"Find all known cached methods for CLASS."
|
||||
(or (phpinspect-get-cached-project-class-methods
|
||||
(phpinspect--resolvecontext-project-root resolvecontext)
|
||||
class static)
|
||||
(progn (phpinspect--log "Failed to find methods for class %s :(" class) nil)))
|
||||
|
||||
(defun phpinspect--get-variables-for-class (class-name &optional static)
|
||||
(let ((class (phpinspect-get-or-create-cached-project-class
|
||||
(phpinspect-current-project-root)
|
||||
class-name)))
|
||||
(when class
|
||||
(if static
|
||||
(append (phpinspect--class-get-static-variables class) (phpinspect--class-get-constants class))
|
||||
(phpinspect--class-get-variables class)))))
|
||||
|
||||
(defun phpinspect--make-method-lister (resolvecontext &optional static)
|
||||
(lambda (fqn)
|
||||
(phpinspect--get-methods-for-class resolvecontext fqn static)))
|
||||
|
||||
(defun phpinspect-suggest-attributes-at-point
|
||||
(resolvecontext &optional static)
|
||||
"Suggest object or class attributes at point.
|
||||
|
||||
RESOLVECONTEXT must be a structure of the type
|
||||
`phpinspect--resolvecontext'. The PHP type of its subject is
|
||||
resolved to provide completion candidates.
|
||||
|
||||
If STATIC is non-nil, candidates are provided for constants,
|
||||
static variables and static methods."
|
||||
;; Strip away the existing (incomplete) attribute token. Otherwise, resolving
|
||||
;; a type from this context while the user has already typed part of an
|
||||
;; attribute name could return the type of an existing attribute that matches
|
||||
;; the incomplete name. (this could for example result in methods of the type
|
||||
;; of $this->entity to be suggested when we really want more suggestions for
|
||||
;; attributes of the type $this like $this->entityRepository). Essentially, we
|
||||
;; convert the subject $this->entity into $this so that only the type of $this
|
||||
;; (or whatever comes before the attribute accessor token (-> or ::)) is
|
||||
;; actually resolved.
|
||||
(when (phpinspect-attrib-p (car (last (phpinspect--resolvecontext-subject resolvecontext))))
|
||||
(setf (phpinspect--resolvecontext-subject resolvecontext)
|
||||
(butlast (phpinspect--resolvecontext-subject resolvecontext))))
|
||||
|
||||
(let* ((type-resolver (phpinspect--make-type-resolver-for-resolvecontext
|
||||
resolvecontext))
|
||||
(method-lister (phpinspect--make-method-lister
|
||||
resolvecontext
|
||||
static)))
|
||||
(let ((statement-type (phpinspect-resolve-type-from-context
|
||||
resolvecontext
|
||||
type-resolver)))
|
||||
(when statement-type
|
||||
(let ((type (funcall type-resolver statement-type)))
|
||||
(append (phpinspect--get-variables-for-class
|
||||
type
|
||||
static)
|
||||
(funcall method-lister type)))))))
|
||||
|
||||
(provide 'phpinspect-suggest)
|
@ -1,83 +0,0 @@
|
||||
;;; phpinspect-toc.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-splayt)
|
||||
(eval-when-compile
|
||||
(require 'phpinspect-meta))
|
||||
|
||||
(defun phpinspect-make-toc (&optional tree)
|
||||
(let ((table (make-hash-table :test #'eq :size 20 :rehash-size 2.0)))
|
||||
(if tree
|
||||
(phpinspect-splayt-traverse-lr (meta tree)
|
||||
(puthash (phpinspect-meta-token meta) meta table))
|
||||
(setq tree (phpinspect-make-splayt)))
|
||||
|
||||
(list tree table)))
|
||||
|
||||
(define-inline phpinspect-toc-register (toc meta)
|
||||
(inline-letevals (toc meta)
|
||||
(inline-quote
|
||||
(progn
|
||||
(phpinspect-splayt-insert (phpinspect-toc-tree ,toc) (phpinspect-meta-start ,meta) ,meta)
|
||||
(puthash (phpinspect-meta-token ,meta) ,meta (phpinspect-toc-table ,toc))))))
|
||||
|
||||
(define-inline phpinspect-toc-tree (toc)
|
||||
(inline-quote (car ,toc)))
|
||||
|
||||
(define-inline phpinspect-toc-table (toc)
|
||||
(inline-quote (cadr ,toc)))
|
||||
|
||||
(defun phpinspect-toc-update (toc new-tree current-root)
|
||||
(let ((current-tree (phpinspect-toc-tree toc))
|
||||
(new-table (make-hash-table :test #'eq :size 20 :rehash-size 2.0))
|
||||
new deleted)
|
||||
|
||||
(phpinspect-splayt-traverse-lr (meta new-tree)
|
||||
(puthash (phpinspect-meta-token meta) meta new-table)
|
||||
(push meta new))
|
||||
|
||||
(phpinspect-splayt-traverse-lr (meta current-tree)
|
||||
(if (eq (phpinspect-meta-find-root meta) current-root)
|
||||
(progn
|
||||
(phpinspect-splayt-insert new-tree (phpinspect-meta-start meta) meta)
|
||||
(puthash (phpinspect-meta-token meta) meta new-table))
|
||||
(push meta deleted)))
|
||||
|
||||
(setf (phpinspect-toc-tree toc) new-tree)
|
||||
(setf (phpinspect-toc-table toc) new-table)
|
||||
|
||||
(list new deleted)))
|
||||
|
||||
(defun phpinspect-toc-token-at-point (toc point)
|
||||
(let ((result (phpinspect-splayt-find-largest-before (phpinspect-toc-tree toc) (+ point 1))))
|
||||
(and result (phpinspect-meta-overlaps-point result point) result)))
|
||||
|
||||
(defun phpinspect-toc-token-at-or-after-point (toc point)
|
||||
(phpinspect-splayt-find-smallest-after (phpinspect-toc-tree toc) (- point 1)))
|
||||
|
||||
(defun phpinspect-toc-tokens-in-region (toc start end)
|
||||
(phpinspect-splayt-find-all-between (phpinspect-toc-tree toc) start end))
|
||||
|
||||
(provide 'phpinspect-toc)
|
@ -1,254 +0,0 @@
|
||||
;;; phpinspect-token-predicates.el --- Predicates for phpinspect-parser tokens types -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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:
|
||||
|
||||
|
||||
(define-inline 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`"
|
||||
(inline-letevals (object)
|
||||
(inline-quote
|
||||
(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-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)))
|
||||
|
||||
(defsubst phpinspect-end-of-statement-p (token)
|
||||
(or (phpinspect-end-of-token-p token)
|
||||
(phpinspect-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)))
|
||||
|
||||
(define-inline phpinspect-scope-p (token)
|
||||
(inline-letevals (token)
|
||||
(inline-quote
|
||||
(or (phpinspect-token-type-p ,token :public)
|
||||
(phpinspect-token-type-p ,token :private)
|
||||
(phpinspect-token-type-p ,token :protected)))))
|
||||
|
||||
(define-inline phpinspect-namespace-p (object)
|
||||
(inline-quote
|
||||
(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))))))
|
||||
|
||||
(define-inline phpinspect-function-p (token)
|
||||
(inline-quote (phpinspect-token-type-p ,token :function)))
|
||||
|
||||
(define-inline phpinspect-class-p (token)
|
||||
(inline-quote (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)))
|
||||
|
||||
(define-inline phpinspect-declaration-p (token)
|
||||
(inline-quote
|
||||
(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))
|
||||
|
||||
(defun phpinspect-annotation-p (token)
|
||||
(phpinspect-token-type-p token :annotation))
|
||||
|
||||
(defun phpinspect-method-annotation-p (token)
|
||||
(phpinspect-token-type-p token :method-annotation))
|
||||
|
||||
(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))
|
||||
|
||||
(define-inline phpinspect-class-variable-p (token)
|
||||
(inline-quote (phpinspect-token-type-p ,token :class-variable)))
|
||||
|
||||
(define-inline phpinspect-variable-p (token)
|
||||
(inline-letevals (token)
|
||||
(inline-quote
|
||||
(or (phpinspect-token-type-p ,token :variable)
|
||||
(phpinspect-token-type-p ,token :class-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-root-p (object)
|
||||
(phpinspect-token-type-p object :root))
|
||||
|
||||
(defun phpinspect-incomplete-token-p (token)
|
||||
(or (phpinspect-incomplete-root-p token)
|
||||
(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-incomplete-root-p (token)
|
||||
(and (phpinspect-root-p token)
|
||||
(seq-find #'phpinspect-incomplete-token-p (cdr 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)))
|
||||
|
||||
(defsubst phpinspect-enclosing-token-p (token)
|
||||
"Returns t when a token can enclose other tokens"
|
||||
(or
|
||||
(phpinspect-list-p token)
|
||||
(phpinspect-block-p token)
|
||||
(phpinspect-class-p token)
|
||||
(phpinspect-function-p token)
|
||||
(phpinspect-array-p token)
|
||||
(phpinspect-scope-p token)
|
||||
(phpinspect-static-p token)
|
||||
(phpinspect-const-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-namespace-or-root-p (object)
|
||||
(or (phpinspect-namespace-p object)
|
||||
(phpinspect-root-p object)))
|
||||
|
||||
(define-inline phpinspect-use-p (object)
|
||||
(inline-quote (phpinspect-token-type-p ,object :use)))
|
||||
|
||||
(defun phpinspect-comment-p (token)
|
||||
(or (phpinspect-token-type-p token :comment)
|
||||
(phpinspect-token-type-p token :doc-block)))
|
||||
|
||||
(defsubst phpinspect-class-block (class)
|
||||
(caddr class))
|
||||
|
||||
(define-inline phpinspect-namespace-is-blocked-p (namespace)
|
||||
(inline-letevals (namespace)
|
||||
(inline-quote
|
||||
(and (= (length ,namespace) 3) (phpinspect-block-p (caddr ,namespace))))))
|
||||
|
||||
(defsubst phpinspect-namespace-block (namespace)
|
||||
(when (phpinspect-namespace-is-blocked-p 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)))
|
||||
|
||||
(defsubst phpinspect-probably-token-p (token)
|
||||
(and (listp token)
|
||||
(keywordp (car token))))
|
||||
|
||||
(provide 'phpinspect-token-predicates)
|
@ -1,364 +0,0 @@
|
||||
;;; phpinspect-type.el --- Data structures that represent phpinspect types -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-token-predicates)
|
||||
|
||||
(eval-when-compile
|
||||
(require 'phpinspect-parser))
|
||||
|
||||
(cl-defstruct (phpinspect--type
|
||||
(:constructor phpinspect--make-type-generated)
|
||||
(:copier phpinspect--copy-type))
|
||||
"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)))
|
||||
|
||||
(defun phpinspect--make-types (type-names)
|
||||
(mapcar (lambda (name) (phpinspect--make-type :name name))
|
||||
type-names))
|
||||
|
||||
(defconst phpinspect-native-typenames
|
||||
;; self, parent and resource are not valid type name.
|
||||
;; see https://www.php.net/manual/ja/language.types.declarations.php
|
||||
;;;
|
||||
;; However, this list does not need to be valid, it just needs to contain the
|
||||
;; list of type names that we should not attempst to resolve relatively.
|
||||
'("array" "bool" "callable" "float" "int" "iterable" "mixed" "object" "string" "void" "self" "static" "this"))
|
||||
|
||||
(defvar phpinspect-native-types
|
||||
(phpinspect--make-types (mapcar (lambda (name) (concat "\\" name))
|
||||
phpinspect-native-typenames)))
|
||||
|
||||
(defvar phpinspect-collection-types
|
||||
(phpinspect--make-types '("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed"))
|
||||
"FQNs of types that should be treated as collecitons when inferring types.")
|
||||
|
||||
(defvar phpinspect--object-type (phpinspect--make-type :name "\\object" :fully-qualified t))
|
||||
(defvar phpinspect--static-type (phpinspect--make-type :name "\\static" :fully-qualified t))
|
||||
(defvar phpinspect--self-type (phpinspect--make-type :name "\\self" :fully-qualified t))
|
||||
(defvar phpinspect--this-type (phpinspect--make-type :name "\\this" :fully-qualified t))
|
||||
(defvar phpinspect--null-type (phpinspect--make-type :name "\\null" :fully-qualified t))
|
||||
|
||||
(defun phpinspect-define-standard-types ()
|
||||
(setq phpinspect-native-types
|
||||
(phpinspect--make-types (mapcar (lambda (name) (concat "\\" name))
|
||||
phpinspect-native-typenames))
|
||||
phpinspect-collection-types (phpinspect--make-types
|
||||
'("\\array" "\\iterable" "\\SplObjectCollection" "\\mixed"))
|
||||
phpinspect--object-type (phpinspect--make-type :name "\\object" :fully-qualified t)
|
||||
phpinspect--static-type (phpinspect--make-type :name "\\static" :fully-qualified t)
|
||||
phpinspect--self-type (phpinspect--make-type :name "\\self" :fully-qualified t)
|
||||
phpinspect--this-type (phpinspect--make-type :name "\\this" :fully-qualified t)
|
||||
phpinspect--null-type (phpinspect--make-type :name "\\null" :fully-qualified t)))
|
||||
|
||||
(cl-defmethod phpinspect--type-set-name ((type phpinspect--type) (name string))
|
||||
(setf (phpinspect--type-name-symbol type) (phpinspect-intern-name name)))
|
||||
|
||||
(cl-defmethod phpinspect--type-does-late-static-binding ((type phpinspect--type))
|
||||
"Whether or not TYPE is used for late static binding.
|
||||
See https://wiki.php.net/rfc/static_return_type ."
|
||||
(or (phpinspect--type= type phpinspect--static-type)
|
||||
(phpinspect--type= type phpinspect--this-type)))
|
||||
|
||||
(cl-defmethod phpinspect--resolve-late-static-binding
|
||||
((type phpinspect--type)
|
||||
(class-type phpinspect--type))
|
||||
(if (phpinspect--type-does-late-static-binding type)
|
||||
class-type
|
||||
type))
|
||||
|
||||
(defsubst phpinspect--type-is-native (type)
|
||||
(catch 'found
|
||||
(dolist (native phpinspect-native-types)
|
||||
(when (phpinspect--type= type native)
|
||||
(throw 'found t)))))
|
||||
|
||||
(defsubst phpinspect--type-is-collection (type)
|
||||
(catch 'found
|
||||
(dolist (collection phpinspect-collection-types)
|
||||
(when (phpinspect--type= type collection)
|
||||
(throw 'found t)))))
|
||||
|
||||
|
||||
(cl-defmethod phpinspect--type-name ((type phpinspect--type))
|
||||
(phpinspect-name-string (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))
|
||||
"Return just the name, without namespace part, of 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-typenames)
|
||||
(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)))
|
||||
(cond (from-types
|
||||
(phpinspect--type-name from-types))
|
||||
(namespace
|
||||
(concat "\\" namespace "\\" type))
|
||||
(t (concat "\\" type)))))))
|
||||
|
||||
(cl-defmethod phpinspect--type-resolve (types namespace (type phpinspect--type))
|
||||
(unless (phpinspect--type-fully-qualified type)
|
||||
(phpinspect--type-set-name
|
||||
type
|
||||
(phpinspect--resolve-type-name types namespace (phpinspect--type-name type)))
|
||||
(setf (phpinspect--type-fully-qualified type) t))
|
||||
(when (phpinspect--type-is-collection type)
|
||||
(setf (phpinspect--type-collection type) t))
|
||||
type)
|
||||
|
||||
(defun phpinspect--find-innermost-incomplete-class (token)
|
||||
(let ((last-token (car (last token))))
|
||||
(cond ((phpinspect-incomplete-class-p token) token)
|
||||
((phpinspect-incomplete-token-p last-token)
|
||||
(phpinspect--find-innermost-incomplete-class last-token)))))
|
||||
|
||||
(defun phpinspect--find-class-token (token)
|
||||
"Recurse into token tree until a class is found."
|
||||
(when (and (listp token) (> (length token) 1))
|
||||
(let ((last-token (car (last token))))
|
||||
(cond ((phpinspect-class-p token) token)
|
||||
(last-token
|
||||
(phpinspect--find-class-token last-token))))))
|
||||
|
||||
(defun phpinspect--make-type-resolver (types &optional token-tree namespace)
|
||||
"Little wrapper closure to pass around and resolve types with."
|
||||
(let* ((inside-class
|
||||
(and token-tree (or (phpinspect--find-innermost-incomplete-class token-tree)
|
||||
(phpinspect--find-class-token token-tree))))
|
||||
(inside-class-name
|
||||
(and inside-class (phpinspect--get-class-name-from-token inside-class))))
|
||||
(lambda (type)
|
||||
(phpinspect--type-resolve
|
||||
types
|
||||
namespace
|
||||
(if (and inside-class-name (phpinspect--type= type phpinspect--self-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)
|
||||
(if name
|
||||
(error "Unexpected value: %s" name)
|
||||
"unknown-type"))
|
||||
|
||||
(cl-defmethod phpinspect--format-type-name ((name string))
|
||||
(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)
|
||||
(:copier phpinspect--copy-function))
|
||||
"A PHP function."
|
||||
(name-symbol nil
|
||||
:type symbol
|
||||
:documentation
|
||||
"A symbol associated with the name of the function")
|
||||
(token nil
|
||||
:type phpinspect-function-p
|
||||
:documentation
|
||||
"The tokens with which this function was declared.")
|
||||
(-inherited nil
|
||||
:type boolean
|
||||
:documentation
|
||||
"Whether this function has been incorporated into a class as
|
||||
method of an extended class.")
|
||||
(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-names)))
|
||||
|
||||
(define-inline phpinspect--function-name (func)
|
||||
(inline-quote (phpinspect-name-string (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
|
||||
:documentation
|
||||
"When the variable is an object attribute, this should
|
||||
contain the scope of the variable as returned by
|
||||
`phpinspect-parse-scope'")
|
||||
(lifetime nil
|
||||
:documentation
|
||||
"The lifetime of the variable (e.g. whether it is static or not). Will
|
||||
contain the parsed keyword token indicating the lifetime of the variable")
|
||||
(mutability nil
|
||||
:documentation
|
||||
"The mutability of the variable (e.g. whether it is constant or
|
||||
not). Will contain the parsed keyword token indicating the
|
||||
mutability of the variable")
|
||||
(type nil
|
||||
:type string
|
||||
:documentation
|
||||
"A string containing the FQN of the variable's type"))
|
||||
|
||||
(defun phpinspect--variable-static-p (variable)
|
||||
(phpinspect-static-p (phpinspect--variable-lifetime variable)))
|
||||
|
||||
(defun phpinspect--variable-const-p (variable)
|
||||
(phpinspect-const-p (phpinspect--variable-mutability variable)))
|
||||
|
||||
(defun phpinspect--variable-vanilla-p (variable)
|
||||
(not (or (phpinspect--variable-static-p variable)
|
||||
(phpinspect--variable-const-p variable))))
|
||||
|
||||
(defun phpinspect--use-to-type (use)
|
||||
(let* ((fqn (cadr (cadr use)))
|
||||
(type (phpinspect--make-type :name (if (string-match "^\\\\" fqn)
|
||||
fqn
|
||||
(concat "\\" fqn))
|
||||
:fully-qualified t))
|
||||
(type-name (if (and (phpinspect-word-p (caddr use))
|
||||
(string= "as" (cadr (caddr use))))
|
||||
(cadr (cadddr use))
|
||||
(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--get-class-name-from-token (class-token)
|
||||
(let ((subtoken (seq-find (lambda (word)
|
||||
(and (phpinspect-word-p word)
|
||||
(not (string-match
|
||||
(concat "^" (phpinspect--class-keyword-handler-regexp))
|
||||
(concat (cadr word) " ")))))
|
||||
(cadr class-token))))
|
||||
(cadr subtoken)))
|
||||
|
||||
(defun phpinspect--index-class-declaration (decl type-resolver)
|
||||
;; Find out what the class extends or implements
|
||||
(let (encountered-extends encountered-implements encountered-class
|
||||
class-name extends implements used-types)
|
||||
(dolist (word decl)
|
||||
(if (phpinspect-word-p word)
|
||||
(cond ((string= (cadr word) "extends")
|
||||
(phpinspect--log "Class %s extends other classes" class-name)
|
||||
(setq encountered-extends t))
|
||||
((string= (cadr word) "implements")
|
||||
(setq encountered-extends nil)
|
||||
(phpinspect--log "Class %s implements in interface" class-name)
|
||||
(setq encountered-implements t))
|
||||
((string-match-p
|
||||
(eval-when-compile
|
||||
(concat "^" (phpinspect--class-keyword-handler-regexp) "?$"))
|
||||
(cadr word))
|
||||
(setq encountered-class t))
|
||||
(t
|
||||
(phpinspect--log "Calling Resolver from index-class on %s" (cadr word))
|
||||
(cond (encountered-extends
|
||||
(push (funcall type-resolver (phpinspect--make-type
|
||||
:name (cadr word)))
|
||||
extends)
|
||||
(push (cadr word) used-types))
|
||||
(encountered-implements
|
||||
(push (funcall type-resolver (phpinspect--make-type
|
||||
:name (cadr word)))
|
||||
implements)
|
||||
(push (cadr word) used-types))
|
||||
(encountered-class
|
||||
(setq class-name (funcall type-resolver (phpinspect--make-type :name (cadr word)))
|
||||
encountered-class nil)))))))
|
||||
|
||||
(list class-name extends implements used-types)))
|
||||
|
||||
(defun phpinspect-namespace-name (namespace)
|
||||
(or (and (phpinspect-namespace-p namespace)
|
||||
(phpinspect-word-p (cadr namespace))
|
||||
(cadadr namespace))
|
||||
""))
|
||||
|
||||
(provide 'phpinspect-type)
|
||||
;;; phpinspect-type.el ends here
|
@ -1,306 +0,0 @@
|
||||
;;; phpinspect-util.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-names (make-hash-table :test #'equal :size 5000 :rehash-size 1.2)
|
||||
"An hash-table containing cons cells representing encountered names in
|
||||
PHP code. Used to optimize string comparison. See also `phpinspect-intern-name'")
|
||||
|
||||
(defun phpinspect-make-name-hash ()
|
||||
(make-hash-table :test #'equal :size 5000 :rehash-size 1.2))
|
||||
|
||||
(define-inline phpinspect-name-string (name)
|
||||
(inline-quote (cdr ,name)))
|
||||
|
||||
(defvar phpinspect-project-root-file-list
|
||||
'("composer.json" "composer.lock" ".git" ".svn" ".hg")
|
||||
"List of files that could indicate a project root directory.")
|
||||
|
||||
(defvar phpinspect--debug nil
|
||||
"Enable debug logs for phpinspect by setting this variable to true")
|
||||
|
||||
(defun phpinspect-message (&rest args)
|
||||
(let ((format-string (car args))
|
||||
(args (cdr args)))
|
||||
(apply #'message `(,(concat "[phpinspect] " format-string) ,@args))))
|
||||
|
||||
(defun phpinspect-toggle-logging ()
|
||||
(interactive)
|
||||
(if (setq phpinspect--debug (not phpinspect--debug))
|
||||
(phpinspect-message "Enabled phpinspect logging.")
|
||||
(phpinspect-message "Disabled phpinspect logging.")))
|
||||
|
||||
(eval-and-compile
|
||||
(defvar phpinspect-log-groups nil)
|
||||
(defvar phpinspect-enabled-log-groups nil)
|
||||
(defvar-local phpinspect--current-log-group nil))
|
||||
|
||||
(define-inline phpinspect--declare-log-group (group)
|
||||
(unless (and (inline-const-p group) (symbolp (inline-const-val group)))
|
||||
(inline-error "Log group name should be a symbol"))
|
||||
|
||||
(inline-quote
|
||||
(progn
|
||||
(add-to-list 'phpinspect-log-groups
|
||||
(cons (macroexp-file-name) ,group)))))
|
||||
|
||||
(defun phpinspect-log-group-enabled-p (group)
|
||||
(seq-find (lambda (cons)
|
||||
(eq group (cdr cons)))
|
||||
phpinspect-enabled-log-groups))
|
||||
|
||||
(defmacro phpinspect--log (&rest args)
|
||||
(let ((log-group (alist-get (macroexp-file-name)
|
||||
phpinspect-log-groups nil nil #'string=)))
|
||||
`(when (and phpinspect--debug
|
||||
(or (not phpinspect-enabled-log-groups)
|
||||
,(when log-group
|
||||
`(member (quote ,log-group) phpinspect-enabled-log-groups))))
|
||||
(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") "]: "
|
||||
,(if log-group (concat "(" (symbol-name log-group) ") ") "")
|
||||
(format ,@args) "\n"))))))
|
||||
|
||||
(defun phpinspect-filter-logs (group-name)
|
||||
(interactive (list (completing-read "Log group: "
|
||||
(mapcar (lambda (g) (symbol-name (cdr g)))
|
||||
phpinspect-log-groups)
|
||||
nil t)))
|
||||
(add-to-list 'phpinspect-enabled-log-groups (intern group-name)))
|
||||
|
||||
(defun phpinspect-unfilter-logs ()
|
||||
(interactive)
|
||||
(setq phpinspect-enabled-log-groups nil))
|
||||
|
||||
(defun phpinspect--find-project-root (&optional start-file)
|
||||
"(Attempt to) Find the root directory of the visited PHP project.
|
||||
If a found project root has a parent directory called \"vendor\",
|
||||
the search continues upwards. See also
|
||||
`phpinspect--locate-dominating-project-file'.
|
||||
|
||||
If START-FILE is provided, searching starts at the directory
|
||||
level of START-FILE in stead of `default-directory`."
|
||||
(let ((project-file (phpinspect--locate-dominating-project-file
|
||||
(or start-file default-directory))))
|
||||
(phpinspect--log "Checking for project root at %s" project-file)
|
||||
(when project-file
|
||||
(let* ((directory (file-name-directory project-file))
|
||||
(directory-slugs (split-string (expand-file-name directory) "/")))
|
||||
(if (not (member "vendor" directory-slugs))
|
||||
(expand-file-name directory)
|
||||
;; else. Only continue if the parent directory is not "/"
|
||||
(let ((parent-without-vendor
|
||||
(string-join (seq-take-while (lambda (s) (not (string= s "vendor" )))
|
||||
directory-slugs)
|
||||
"/")))
|
||||
(when (not (or (string= parent-without-vendor "/")
|
||||
(string= parent-without-vendor "")))
|
||||
(phpinspect--find-project-root parent-without-vendor))))))))
|
||||
|
||||
(defun phpinspect-intern-name (name)
|
||||
(or (gethash name phpinspect-names)
|
||||
(puthash name (cons 'phpinspect-name name) phpinspect-names)))
|
||||
|
||||
(defun phpinspect-names-to-alist (names)
|
||||
(let ((alist))
|
||||
(dolist (name names)
|
||||
(push (cons (phpinspect-name-string name) name) alist))
|
||||
alist))
|
||||
|
||||
(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)))
|
||||
|
||||
(cl-defstruct (phpinspect--pattern
|
||||
(:constructor phpinspect--make-pattern-generated))
|
||||
"An object that can be used to match lists to a given
|
||||
pattern. See `phpinspect--match-sequence'."
|
||||
(matcher nil
|
||||
:type lambda
|
||||
:documentation "The function used to match sequences")
|
||||
(code nil
|
||||
:type list
|
||||
:documentation "The original code list used to create this pattern"))
|
||||
|
||||
(defmacro phpinspect--make-pattern (&rest pattern)
|
||||
`(phpinspect--make-pattern-generated
|
||||
:matcher (phpinspect--match-sequence-lambda ,@pattern)
|
||||
:code (list ,@(mapcar (lambda (part) (if (eq '* part) `(quote ,part) part))
|
||||
pattern))))
|
||||
|
||||
(defmacro phpinspect--match-sequence-lambda (&rest pattern)
|
||||
(let ((sequence-sym (gensym)))
|
||||
`(lambda (,sequence-sym)
|
||||
(phpinspect--match-sequence ,sequence-sym ,@pattern))))
|
||||
|
||||
(cl-defmethod phpinspect--pattern-match ((pattern phpinspect--pattern) sequence)
|
||||
"Match SEQUENCE to PATTERN."
|
||||
(funcall (phpinspect--pattern-matcher pattern) sequence))
|
||||
|
||||
(defmacro phpinspect--match-sequence (sequence &rest pattern)
|
||||
"Match SEQUENCE to positional matchers defined in PATTERN.
|
||||
|
||||
PATTERN is a plist with the allowed keys being :m and :f. Each
|
||||
key-value pair in the plist defines a match operation that is
|
||||
applied to the corresponding index of SEQUENCE (so for ex.: key 0
|
||||
is applied to pos. 0 of SEQUENCE, key 1 to pos. 1, and so on).
|
||||
|
||||
Possible match operations:
|
||||
|
||||
:m - This key can be used to match a list element to the literal
|
||||
value supplied for it, using the `equal' comparison function. For
|
||||
example, providing `(\"foobar\") as value will result in the
|
||||
comparison (equal (elt SEQUENCE pos) `(\"foobar\")). There is one
|
||||
exception to this rule: using the symbol * as value for the :m
|
||||
key will match anything, essentially skipping comparison for the
|
||||
element at this position in SEQUENCE.
|
||||
|
||||
:f - This key can be used to match a list element by executing
|
||||
the function provided as value. The function is executed with the
|
||||
list element as argument, and will be considered as matching if
|
||||
it evaluates to a non-nil value."
|
||||
(declare (indent 1))
|
||||
(let* ((pattern-length (length pattern))
|
||||
(sequence-length (/ pattern-length 2))
|
||||
(sequence-pos 0)
|
||||
(sequence-sym (gensym))
|
||||
(match-sym (gensym))
|
||||
(match-rear-sym (gensym))
|
||||
(checkers (cons nil nil))
|
||||
(checkers-rear checkers)
|
||||
key value)
|
||||
|
||||
(while (setq key (pop pattern))
|
||||
(unless (keywordp key)
|
||||
(error "Invalid pattern argument, expected keyword, got: %s" key))
|
||||
|
||||
(unless (setq value (pop pattern))
|
||||
(error "No value for key %s" key))
|
||||
|
||||
(cond ((eq key :m)
|
||||
(unless (eq value '*)
|
||||
(setq checkers-rear
|
||||
(setcdr checkers-rear
|
||||
(cons `(equal ,value (elt ,sequence-sym ,sequence-pos)) nil)))))
|
||||
((eq key :f)
|
||||
(setq checkers-rear
|
||||
(setcdr
|
||||
checkers-rear
|
||||
(cons
|
||||
(if (symbolp value)
|
||||
`(,value (elt ,sequence-sym ,sequence-pos))
|
||||
`(funcall ,value (elt ,sequence-sym ,sequence-pos)))
|
||||
nil))))
|
||||
(t (error "Invalid keyword: %s" key)))
|
||||
|
||||
(setq checkers-rear
|
||||
(setcdr checkers-rear
|
||||
(cons `(setq ,match-rear-sym
|
||||
(setcdr ,match-rear-sym
|
||||
(cons (elt ,sequence-sym ,sequence-pos) nil)))
|
||||
nil)))
|
||||
|
||||
(setq sequence-pos (+ sequence-pos 1)))
|
||||
|
||||
(setq checkers (cdr checkers))
|
||||
|
||||
`(let* ((,sequence-sym ,sequence)
|
||||
(,match-sym (cons nil nil))
|
||||
(,match-rear-sym ,match-sym))
|
||||
(and (= ,sequence-length (length ,sequence))
|
||||
,@checkers
|
||||
(cdr ,match-sym)))))
|
||||
|
||||
(defun phpinspect--pattern-concat (pattern1 pattern2)
|
||||
(let* ((pattern1-sequence-length (/ (length (phpinspect--pattern-code pattern1)) 2)))
|
||||
(phpinspect--make-pattern-generated
|
||||
:matcher (lambda (sequence)
|
||||
(unless (< (length sequence) pattern1-sequence-length)
|
||||
(and (phpinspect--pattern-match
|
||||
pattern1
|
||||
(butlast sequence (- (length sequence) pattern1-sequence-length)))
|
||||
(phpinspect--pattern-match
|
||||
pattern2
|
||||
(last sequence (- (length sequence) pattern1-sequence-length))))))
|
||||
:code (append (phpinspect--pattern-code pattern1)
|
||||
(phpinspect--pattern-code pattern2)))))
|
||||
|
||||
(defun phpinspect--locate-dominating-project-file (start-file)
|
||||
"Locate the first dominating file in `phpinspect-project-root-file-list`.
|
||||
Starts looking at START-FILE and then recurses up the directory
|
||||
hierarchy as long as no matching files are found. See also
|
||||
`locate-dominating-file'."
|
||||
(let ((dominating-file))
|
||||
(seq-find (lambda (file)
|
||||
(setq dominating-file (locate-dominating-file start-file file)))
|
||||
phpinspect-project-root-file-list)
|
||||
dominating-file))
|
||||
|
||||
(defun phpinspect--determine-completion-point ()
|
||||
"Find first point backwards that could contain any kind of
|
||||
context for completion."
|
||||
(save-excursion
|
||||
(re-search-backward "[^[:blank:]\n]" nil t)
|
||||
(forward-char)
|
||||
(point)))
|
||||
|
||||
(defmacro phpinspect-json-preset (&rest body)
|
||||
"Default options to wrap around `json-read' and similar BODY."
|
||||
`(let ((json-object-type 'hash-table)
|
||||
(json-array-type 'list)
|
||||
(json-key-type 'string))
|
||||
,@body))
|
||||
|
||||
(defun phpinspect--input-pending-p (&optional check-timers)
|
||||
(unless noninteractive
|
||||
(input-pending-p check-timers)))
|
||||
|
||||
(defun phpinspect-thread-pause (pause-time mx continue)
|
||||
"Pause current thread using MX and CONTINUE for PAUSE-TIME idle seconds.
|
||||
|
||||
PAUSE-TIME must be the idle time that the thread should pause for.
|
||||
MX must be a mutex
|
||||
CONTINUE must be a condition-variable"
|
||||
(phpinspect--log "Thread '%s' is paused for %d seconds" (thread-name (current-thread)) pause-time)
|
||||
(run-with-idle-timer
|
||||
pause-time
|
||||
nil
|
||||
(lambda () (with-mutex mx (condition-notify continue))))
|
||||
(with-mutex mx (condition-wait continue))
|
||||
(phpinspect--log "Thread '%s' continuing execution" (thread-name (current-thread))))
|
||||
|
||||
(provide 'phpinspect-util)
|
||||
;;; phpinspect-util.el ends here
|
@ -1,250 +0,0 @@
|
||||
;; phpinspect-worker.el --- PHP parsing and completion package -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'cl-lib)
|
||||
(require 'phpinspect-util)
|
||||
(require 'phpinspect-project-struct)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-class)
|
||||
(require 'phpinspect-queue)
|
||||
(require 'phpinspect-pipeline)
|
||||
|
||||
(eval-when-compile
|
||||
(phpinspect--declare-log-group 'worker))
|
||||
|
||||
(defcustom phpinspect-worker-pause-time 1
|
||||
"Number of seconds that `phpinspect-worker' should pause when
|
||||
user input is detected. A higher value means better
|
||||
responsiveness, at the cost of slower code indexation. On modern
|
||||
hardware this probably doesn't need to be tweaked."
|
||||
:type 'number
|
||||
:group 'phpinspect)
|
||||
|
||||
(defvar phpinspect-worker nil
|
||||
"Contains the phpinspect worker that is used by all projects.")
|
||||
|
||||
(cl-defstruct (phpinspect-worker
|
||||
(:constructor phpinspect-make-worker-generated))
|
||||
(queue nil
|
||||
:type phpinspect-queue-item
|
||||
:documentation
|
||||
"The queue of tasks that are pending")
|
||||
(thread nil
|
||||
:type thread
|
||||
:documentation
|
||||
"The thread of this worker")
|
||||
(continue-running nil
|
||||
:type bool
|
||||
:documentation
|
||||
"Whether or not the thread should continue
|
||||
running. If this is nil, the thread is stopped.")
|
||||
(skip-next-pause nil
|
||||
:type bool
|
||||
:documentation
|
||||
"Whether or not the thread should skip its next scheduled pause."))
|
||||
|
||||
(cl-defstruct (phpinspect-dynamic-worker
|
||||
(:constructor phpinspect-make-dynamic-worker-generated))
|
||||
"A dynamic worker is nothing other than an object that is
|
||||
supported by all of the same methods as a `phpinspect-worker`,
|
||||
but relies on an underlying, global worker to actually do the
|
||||
work. The reason for its implementation is to allow users to
|
||||
manage phpinspect's worker thread centrally in a dynamic
|
||||
variable, while also making the behaviour of objects that depend
|
||||
on the worker independent of dynamic variables during testing.")
|
||||
|
||||
(cl-defmethod phpinspect-resolve-dynamic-worker ((_worker phpinspect-dynamic-worker))
|
||||
phpinspect-worker)
|
||||
|
||||
(defun phpinspect-make-dynamic-worker ()
|
||||
(phpinspect-make-dynamic-worker-generated))
|
||||
|
||||
(defsubst phpinspect-make-worker ()
|
||||
"Create a new worker object."
|
||||
(let ((worker (phpinspect-make-worker-generated)))
|
||||
(setf (phpinspect-worker-queue worker)
|
||||
(phpinspect-make-queue (phpinspect-worker-make-wakeup-function worker)))
|
||||
worker))
|
||||
|
||||
(define-error 'phpinspect-wakeup-thread
|
||||
"This error is used to wakeup the index thread")
|
||||
|
||||
(cl-defgeneric phpinspect-worker-make-wakeup-function (worker)
|
||||
"Create a function that can be used to wake up WORKER's thread.")
|
||||
|
||||
(cl-defmethod phpinspect-worker-wakeup ((worker phpinspect-worker))
|
||||
(when (eq main-thread (thread--blocker (phpinspect-worker-thread worker)))
|
||||
(phpinspect--log "Attempting to wakeup worker thread")
|
||||
(thread-signal (phpinspect-worker-thread worker)
|
||||
'phpinspect-wakeup-thread nil)))
|
||||
|
||||
(cl-defmethod phpinspect-worker-make-wakeup-function ((worker phpinspect-worker))
|
||||
(lambda ()
|
||||
(phpinspect-worker-wakeup worker)))
|
||||
|
||||
(cl-defmethod phpinspect-worker-make-wakeup-function ((worker phpinspect-dynamic-worker))
|
||||
(phpinspect-worker-make-wakeup-function (phpinspect-resolve-dynamic-worker worker)))
|
||||
|
||||
(cl-defgeneric phpinspect-worker-live-p (worker)
|
||||
"Just a shorthand to check whether or not the WORKER's thread is running.")
|
||||
|
||||
(cl-defmethod phpinspect-worker-live-p ((worker phpinspect-worker))
|
||||
(when (phpinspect-worker-thread worker)
|
||||
(thread-live-p (phpinspect-worker-thread worker))))
|
||||
|
||||
(cl-defmethod phpinspect-worker-live-p ((worker phpinspect-dynamic-worker))
|
||||
(phpinspect-worker-live-p (phpinspect-resolve-dynamic-worker worker)))
|
||||
|
||||
(cl-defgeneric phpinspect-worker-enqueue (worker task)
|
||||
"Enqueue a TASK to be executed by WORKER.")
|
||||
|
||||
(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-worker) task)
|
||||
"Specialized enqueuement method for index tasks. Prevents
|
||||
indexation tasks from being added when there are identical tasks
|
||||
already present in the queue."
|
||||
(phpinspect--log "Enqueuing task")
|
||||
(phpinspect-queue-enqueue-noduplicate (phpinspect-worker-queue worker) task #'phpinspect-task=))
|
||||
|
||||
(cl-defmethod phpinspect-worker-enqueue ((worker phpinspect-dynamic-worker) task)
|
||||
(phpinspect-worker-enqueue (phpinspect-resolve-dynamic-worker worker)
|
||||
task))
|
||||
(cl-defgeneric phpinspect-worker-make-thread-function (worker)
|
||||
"Create a function that can be used to start WORKER's thread.")
|
||||
|
||||
(defun phpinspect--worker-pause ()
|
||||
(let* ((mx (make-mutex))
|
||||
(continue (make-condition-variable mx)))
|
||||
(phpinspect-thread-pause phpinspect-worker-pause-time mx continue)))
|
||||
|
||||
(cl-defmethod phpinspect-worker-make-thread-function ((worker phpinspect-worker))
|
||||
(lambda ()
|
||||
(while (phpinspect-worker-continue-running worker)
|
||||
;; This error is used to wake up the thread when new tasks are added to the
|
||||
;; queue.
|
||||
(condition-case err
|
||||
(progn
|
||||
(phpinspect--log "Dequeueing next task")
|
||||
(ignore-error phpinspect-wakeup-thread
|
||||
;; Prevent quitting during tasks, as this can break data integrity
|
||||
(let* ((inhibit-quit t)
|
||||
(task (phpinspect-queue-dequeue (phpinspect-worker-queue worker))))
|
||||
(if task
|
||||
;; Execute task if it belongs to a project that has not been
|
||||
;; purged (meaning that it is still actively used).
|
||||
(if (phpinspect-project-purged (phpinspect-task-project task))
|
||||
(phpinspect--log "Projecthas been purged. Skipping task")
|
||||
(phpinspect--log "Executing task")
|
||||
(phpinspect-task-execute task worker))
|
||||
;; else: join with the main thread until wakeup is signaled
|
||||
(phpinspect--log "No tasks, joining main thread")
|
||||
(thread-join main-thread))))
|
||||
|
||||
;; Pause for a second after indexing something, to allow user input to
|
||||
;; interrupt the thread.
|
||||
(unless (or (not (phpinspect--input-pending-p))
|
||||
(phpinspect-worker-skip-next-pause worker))
|
||||
(phpinspect--worker-pause))
|
||||
(setf (phpinspect-worker-skip-next-pause worker) nil))
|
||||
(quit (ignore-error phpinspect-wakeup-thread
|
||||
(phpinspect--worker-pause)))
|
||||
(phpinspect-wakeup-thread)
|
||||
((debug error) (thread-signal main-thread 'phpinspect-worker-error err))
|
||||
(t (phpinspect--log "Phpinspect worker thread errored :%s" err))))
|
||||
(phpinspect--log "Worker thread exiting")
|
||||
(phpinspect-message "phpinspect worker thread exited")))
|
||||
|
||||
(cl-defmethod phpinspect-worker-make-thread-function ((worker phpinspect-dynamic-worker))
|
||||
(phpinspect-worker-make-thread-function
|
||||
(phpinspect-resolve-dynamic-worker worker)))
|
||||
|
||||
(cl-defgeneric phpinspect-worker-start (worker)
|
||||
"Start WORKER's thread.")
|
||||
|
||||
(cl-defmethod phpinspect-worker-start ((worker phpinspect-worker))
|
||||
(if (phpinspect-worker-live-p worker)
|
||||
(error "Attempt to start a worker that is already running")
|
||||
(progn
|
||||
(setf (phpinspect-worker-continue-running worker) t)
|
||||
(setf (phpinspect-worker-thread worker)
|
||||
;; Use with-temp-buffer so as to not associate thread with the
|
||||
;; current buffer. Otherwise, the buffer associated with this thread
|
||||
;; will be unkillable while the thread is running.
|
||||
(with-temp-buffer
|
||||
(make-thread (phpinspect-worker-make-thread-function worker) "phpinspect-worker"))))))
|
||||
|
||||
(cl-defmethod phpinspect-worker-start ((worker phpinspect-dynamic-worker))
|
||||
(phpinspect-worker-start (phpinspect-resolve-dynamic-worker worker)))
|
||||
|
||||
(cl-defgeneric phpinspect-worker-stop (worker)
|
||||
"Stop the worker")
|
||||
|
||||
(cl-defmethod phpinspect-worker-stop ((worker phpinspect-worker))
|
||||
(setf (phpinspect-worker-continue-running worker) nil)
|
||||
(phpinspect-worker-wakeup worker))
|
||||
|
||||
(cl-defmethod phpinspect-worker-stop ((worker phpinspect-dynamic-worker))
|
||||
(phpinspect-worker-stop (phpinspect-resolve-dynamic-worker worker)))
|
||||
|
||||
(defun phpinspect-ensure-worker ()
|
||||
(interactive)
|
||||
(when (not phpinspect-worker)
|
||||
(setq phpinspect-worker (phpinspect-make-worker)))
|
||||
|
||||
(when (not (phpinspect-worker-live-p phpinspect-worker))
|
||||
(phpinspect-worker-start phpinspect-worker)))
|
||||
|
||||
(defun phpinspect-stop-worker ()
|
||||
(interactive)
|
||||
(phpinspect-worker-stop phpinspect-worker))
|
||||
|
||||
;;; TASKS
|
||||
;; The rest of this file contains task definitions. Tasks represent actions that
|
||||
;; can be executed by `phpinspect-worker'. Some methods are required to be
|
||||
;; implemented for all tasks, while others aren't.
|
||||
|
||||
;; REQUIRED METHODS:
|
||||
;; - phpinspect-task-execute
|
||||
;; - phpinspect-task-project
|
||||
|
||||
;; OPTIONAL METHODS:
|
||||
;; - phpinspect-task=
|
||||
|
||||
;;; Code:
|
||||
|
||||
(cl-defgeneric phpinspect-task-execute (task worker)
|
||||
"Execute TASK for WORKER.")
|
||||
|
||||
(cl-defmethod phpinspect-task= (_task1 _task2)
|
||||
"Whether or not TASK1 and TASK2 are set to execute the exact same action."
|
||||
nil)
|
||||
|
||||
(cl-defgeneric phpinspect-task-project (task)
|
||||
"The project that this task belongs to.")
|
||||
|
||||
(cl-defmethod phpinspect-worker-enqueue ((_worker (eql 'nil-worker)) &rest _ignored))
|
||||
(cl-defmethod phpinspect-worker-live-p ((_worker (eql 'nil-worker)) &rest _ignored) t)
|
||||
|
||||
(provide 'phpinspect-worker)
|
||||
;;; phpinspect-worker.el ends here
|
File diff suppressed because it is too large
Load Diff
@ -1,101 +0,0 @@
|
||||
<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
function generateClassStub(\ReflectionClass $class): string
|
||||
{
|
||||
$stub = '';
|
||||
|
||||
$hasNamespace = '' !== $class->getNamespaceName();
|
||||
if ($hasNamespace) {
|
||||
$stub = 'namespace ' . $class->getNamespaceName() . ' {' . PHP_EOL;
|
||||
}
|
||||
|
||||
if ($class->isFinal()) {
|
||||
$stub .= 'final ';
|
||||
}
|
||||
|
||||
if ($class->isAbstract() && !$class->isInterface()) {
|
||||
$stub .= 'abstract ';
|
||||
}
|
||||
|
||||
if ($class->isInterface()) {
|
||||
$stub .= 'interface ';
|
||||
} else if ($class->isTrait()) {
|
||||
$stub .= 'trait ';
|
||||
} else {
|
||||
$stub .= 'class ';
|
||||
}
|
||||
|
||||
$stub .= $class->getShortName() . ' {' . PHP_EOL;
|
||||
|
||||
foreach ($class->getMethods() as $method) {
|
||||
if ($method->isFinal()) {
|
||||
$stub .= 'final ';
|
||||
}
|
||||
|
||||
if ($method->isPublic()) {
|
||||
$stub .= 'public ';
|
||||
} else if ($method->isPrivate()) {
|
||||
$stub .= 'private ';
|
||||
} else if ($method->isProtected()) {
|
||||
$stub .= 'protected ';
|
||||
}
|
||||
|
||||
if ($method->isStatic()) {
|
||||
$stub .= 'static ';
|
||||
}
|
||||
|
||||
$stub .= generateFunctionStub($method);
|
||||
}
|
||||
|
||||
$stub .= '}' . PHP_EOL;
|
||||
|
||||
if ($hasNamespace) {
|
||||
$stub .= '}' . PHP_EOL; // Close namespace block
|
||||
}
|
||||
|
||||
return $stub;
|
||||
}
|
||||
|
||||
function generateFunctionStub(\ReflectionFunctionAbstract $function): string
|
||||
{
|
||||
$stub = 'function ' . $function->getName() . '(';
|
||||
|
||||
$parameters = [];
|
||||
foreach ($function->getParameters() as $ref_parameter) {
|
||||
$parameter = '';
|
||||
|
||||
if ($ref_parameter->hasType()) {
|
||||
$parameter .= $ref_parameter->getType()->__toString() . ' ';
|
||||
}
|
||||
|
||||
$parameter .= '$' . $ref_parameter->getName();
|
||||
|
||||
if ($ref_parameter->isDefaultValueAvailable()) {
|
||||
$parameter .= ' = ' . var_export($ref_parameter->getDefaultValue(), true);
|
||||
}
|
||||
|
||||
$parameters[] = $parameter;
|
||||
}
|
||||
$stub .= implode(', ', $parameters);
|
||||
|
||||
$stub .= ')';
|
||||
|
||||
if ($function->hasReturnType()) {
|
||||
$stub .= ': ' . $function->getReturnType()->__toString();
|
||||
} else if ($function->hasTentativeReturnType()) {
|
||||
$stub .= ': ?' . $function->getTentativeReturnType();
|
||||
}
|
||||
|
||||
$stub .= ' {}' . PHP_EOL;
|
||||
|
||||
return $stub;
|
||||
}
|
||||
|
||||
foreach (get_defined_functions(false)['internal'] as $function_name) {
|
||||
echo generateFunctionStub(new ReflectionFunction($function_name));
|
||||
}
|
||||
|
||||
foreach ([...get_declared_classes(), ...get_declared_interfaces(), ...get_declared_traits()] as $class_name) {
|
||||
echo generateClassStub(new ReflectionClass($class_name));
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
;;; install-deps.el --- Install package dependencies -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2023 Free Software Foundation, Inc
|
||||
|
||||
;; Author: Hugo Thunnissen <devel@hugot.nl>
|
||||
;; Keywords: script
|
||||
|
||||
;; 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 'lisp-mnt)
|
||||
|
||||
(let* ((project-dir (file-name-parent-directory (file-name-directory (macroexp-file-name))))
|
||||
(file (expand-file-name "phpinspect.el" project-dir))
|
||||
dependencies)
|
||||
|
||||
(with-temp-buffer
|
||||
(insert-file-contents file)
|
||||
(setq dependencies (read (lm-header-multiline "package-requires")))
|
||||
(dolist (dep dependencies)
|
||||
(package-install (car dep)))))
|
@ -1,20 +0,0 @@
|
||||
#!/bin/bash
|
||||
|
||||
rm ./**/*.elc
|
||||
rm *.elc
|
||||
|
||||
for file in ./*.el; do
|
||||
echo 'Compiling '"$file"' ...'
|
||||
cask emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
|
||||
done
|
||||
|
||||
for file in ./**/*.el; do
|
||||
echo 'Compiling '"$file"' ...'
|
||||
cask emacs -batch -L . --eval '(progn '"(require 'comp)"' (setq byte-compile-error-on-warn t native-compile-target-directory (car native-comp-eln-load-path)) (nreverse native-comp-eln-load-path))' -f batch-byte+native-compile "$file" || break
|
||||
done
|
||||
|
||||
|
||||
if [[ -z $NO_REMOVE_ELC ]]; then
|
||||
rm ./**/*.elc
|
||||
rm *.elc
|
||||
fi
|
File diff suppressed because it is too large
Load Diff
@ -1 +1 @@
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Controller") (:terminator ";") (:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) (:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use (:word "App\\Repository\\UserRepository") (:terminator ";")) (:use (:word "Doctrine\\ORM\\EntityManagerInterface") (:terminator ";")) (:use (:word "Twig\\Environment") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\Request") (:terminator ";")) (:use (:word "Symfony\\Component\\Routing\\Annotation\\Route") (:terminator ";")) (:class (:declaration (:word "class") (:word "AddressController")) (:incomplete-block (:const (:word "A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE") (:assignment "=") (:string "a value") (:terminator ";")) (:public (:const (:word "ARRAY_CONSTANT") (:assignment "=") (:array (:string "key") (:fat-arrow "=>") (:string "value") (:comma ",") (:string "key") (:fat-arrow "=>")) (:terminator ";"))) (:private (:class-variable "repo") (:terminator ";")) (:private (:class-variable "user_repo") (:terminator ";")) (:private (:class-variable "twig") (:terminator ";")) (:private (:class-variable "em") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "AddressRepository") (:variable "repo") (:comma ",") (:word "UserRepository") (:variable "user_repo") (:comma ",") (:word "Environment") (:variable "twig") (:comma ",") (:word "EntityManagerInterface") (:variable "em"))) (:block (:variable "this") (:object-attrib (:word "repo")) (:assignment "=") (:variable "repo") (:terminator ";") (:variable "this") (:object-attrib (:word "user_repo")) (:assignment "=") (:variable "user_repo") (:terminator ";") (:variable "this") (:object-attrib (:word "twig")) (:assignment "=") (:variable "twig") (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:assignment "=") (:variable "em") (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressPage") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:variable "address_string") (:assignment "=") (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address")) (:terminator ";") (:variable "address") (:assignment "=") (:word "new") (:word "Address") (:list (:variable "user") (:comma ",") (:variable "address_string")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "persist")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "user") (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "deleteAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:incomplete-block (:variable "address") (:assignment "=") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address"))) (:terminator ";") (:comment) (:comment) (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "remove")) (:incomplete-list (:variable "this") (:object-attrib (:word "em")) (:object-attrib nil)))))))))
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Controller") (:terminator ";") (:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) (:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use (:word "App\\Repository\\UserRepository") (:terminator ";")) (:use (:word "Doctrine\\ORM\\EntityManagerInterface") (:terminator ";")) (:use (:word "Twig\\Environment") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\Request") (:terminator ";")) (:use (:word "Symfony\\Component\\Routing\\Annotation\\Route") (:terminator ";")) (:class (:declaration (:word "class") (:word "AddressController")) (:incomplete-block (:const (:word "A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE") (:assignment "=") (:string "a value") (:terminator ";")) (:public (:const (:word "ARRAY_CONSTANT") (:assignment "=") (:array (:string "key") (:fat-arrow "=>") (:string "value") (:comma ",") (:string "key") (:fat-arrow "=>")) (:terminator ";"))) (:private (:variable "repo") (:terminator ";")) (:private (:variable "user_repo") (:terminator ";")) (:private (:variable "twig") (:terminator ";")) (:private (:variable "em") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "AddressRepository") (:variable "repo") (:comma ",") (:word "UserRepository") (:variable "user_repo") (:comma ",") (:word "Environment") (:variable "twig") (:comma ",") (:word "EntityManagerInterface") (:variable "em"))) (:block (:variable "this") (:object-attrib (:word "repo")) (:assignment "=") (:variable "repo") (:terminator ";") (:variable "this") (:object-attrib (:word "user_repo")) (:assignment "=") (:variable "user_repo") (:terminator ";") (:variable "this") (:object-attrib (:word "twig")) (:assignment "=") (:variable "twig") (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:assignment "=") (:variable "em") (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressPage") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:variable "address_string") (:assignment "=") (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address")) (:terminator ";") (:variable "address") (:assignment "=") (:word "new") (:word "Address") (:list (:variable "user") (:comma ",") (:variable "address_string")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "persist")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "user") (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "deleteAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:incomplete-block (:variable "address") (:assignment "=") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address"))) (:terminator ";") (:comment) (:comment) (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "remove")) (:incomplete-list (:variable "this") (:object-attrib (:word "em")) (:object-attrib nil)))))))))
|
@ -1 +1 @@
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Controller") (:incomplete-block (:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) (:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use (:word "App\\Repository\\UserRepository") (:terminator ";")) (:use (:word "Doctrine\\ORM\\EntityManagerInterface") (:terminator ";")) (:use (:word "Twig\\Environment") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\Request") (:terminator ";")) (:use (:word "Symfony\\Component\\Routing\\Annotation\\Route") (:terminator ";")) (:class (:declaration (:word "class") (:word "AddressController")) (:incomplete-block (:const (:word "A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE") (:assignment "=") (:string "a value") (:terminator ";")) (:public (:const (:word "ARRAY_CONSTANT") (:assignment "=") (:array (:string "key") (:fat-arrow "=>") (:string "value") (:comma ",") (:string "key") (:fat-arrow "=>")) (:terminator ";"))) (:private (:class-variable "repo") (:terminator ";")) (:private (:class-variable "user_repo") (:terminator ";")) (:private (:class-variable "twig") (:terminator ";")) (:private (:class-variable "em") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "AddressRepository") (:variable "repo") (:comma ",") (:word "UserRepository") (:variable "user_repo") (:comma ",") (:word "Environment") (:variable "twig") (:comma ",") (:word "EntityManagerInterface") (:variable "em"))) (:block (:variable "this") (:object-attrib (:word "repo")) (:assignment "=") (:variable "repo") (:terminator ";") (:variable "this") (:object-attrib (:word "user_repo")) (:assignment "=") (:variable "user_repo") (:terminator ";") (:variable "this") (:object-attrib (:word "twig")) (:assignment "=") (:variable "twig") (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:assignment "=") (:variable "em") (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressPage") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:variable "address_string") (:assignment "=") (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address")) (:terminator ";") (:variable "address") (:assignment "=") (:word "new") (:word "Address") (:list (:variable "user") (:comma ",") (:variable "address_string")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "persist")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "user") (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "deleteAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:incomplete-block (:variable "address") (:assignment "=") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address"))) (:terminator ";") (:comment) (:comment) (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "remove")) (:incomplete-list (:variable "this") (:object-attrib (:word "em")) (:object-attrib nil))))))))))
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Controller") (:incomplete-block (:use (:word "Symfony\\Component\\HttpFoundation\\Response") (:terminator ";")) (:use (:word "App\\Entity\\Address") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\RedirectResponse") (:terminator ";")) (:use (:word "App\\Repository\\AddressRepository") (:terminator ";")) (:use (:word "App\\Repository\\UserRepository") (:terminator ";")) (:use (:word "Doctrine\\ORM\\EntityManagerInterface") (:terminator ";")) (:use (:word "Twig\\Environment") (:terminator ";")) (:use (:word "Symfony\\Component\\HttpFoundation\\Request") (:terminator ";")) (:use (:word "Symfony\\Component\\Routing\\Annotation\\Route") (:terminator ";")) (:word "class") (:word "AddressController") (:incomplete-block (:const (:word "A_CONSTANT_FOR_THE_SAKE_OF_HAVING_ONE") (:assignment "=") (:string "a value") (:terminator ";")) (:public (:const (:word "ARRAY_CONSTANT") (:assignment "=") (:array (:string "key") (:fat-arrow "=>") (:string "value") (:comma ",") (:string "key") (:fat-arrow "=>")) (:terminator ";"))) (:private (:variable "repo") (:terminator ";")) (:private (:variable "user_repo") (:terminator ";")) (:private (:variable "twig") (:terminator ";")) (:private (:variable "em") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "AddressRepository") (:variable "repo") (:comma ",") (:word "UserRepository") (:variable "user_repo") (:comma ",") (:word "Environment") (:variable "twig") (:comma ",") (:word "EntityManagerInterface") (:variable "em"))) (:block (:variable "this") (:object-attrib (:word "repo")) (:assignment "=") (:variable "repo") (:terminator ";") (:variable "this") (:object-attrib (:word "user_repo")) (:assignment "=") (:variable "user_repo") (:terminator ";") (:variable "this") (:object-attrib (:word "twig")) (:assignment "=") (:variable "twig") (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:assignment "=") (:variable "em") (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressPage") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:word "return") (:word "new") (:word "Response") (:list (:variable "this") (:object-attrib (:word "twig")) (:object-attrib (:word "render")) (:list (:string "address/create.html.twig") (:comma ",") (:array (:string "user") (:fat-arrow "=>") (:variable "user") (:comma ",")))) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "addAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:block (:variable "user") (:assignment "=") (:variable "this") (:object-attrib (:word "user_repo")) (:object-attrib (:word "findOne")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "user"))) (:terminator ";") (:variable "address_string") (:assignment "=") (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address")) (:terminator ";") (:variable "address") (:assignment "=") (:word "new") (:word "Address") (:list (:variable "user") (:comma ",") (:variable "address_string")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "persist")) (:list (:variable "address")) (:terminator ";") (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "flush")) (:list) (:terminator ";") (:word "return") (:word "new") (:word "RedirectResponse") (:list (:string "/user/") (:variable "user") (:object-attrib (:word "getLoginName")) (:list) (:string "/manage")) (:terminator ";")))) (:doc-block (:annotation "Route")) (:public (:function (:declaration (:word "function") (:word "deleteAddressAction") (:list (:word "Request") (:variable "req")) (:word "Response")) (:incomplete-block (:variable "address") (:assignment "=") (:variable "this") (:object-attrib (:word "repo")) (:object-attrib (:word "find")) (:list (:variable "req") (:object-attrib (:word "request")) (:object-attrib (:word "get")) (:list (:string "address"))) (:terminator ";") (:comment) (:comment) (:variable "this") (:object-attrib (:word "em")) (:object-attrib (:word "remove")) (:incomplete-list (:variable "this") (:object-attrib (:word "em")) (:object-attrib nil)))))))))
|
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "string")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";")))) (:doc-block (:return-annotation (:word "DateTime[]"))) (:public (:function (:declaration (:word "function") (:word "arrayReturn") (:list) (:word "array")) (:block (:word "return") (:array (:word "new") (:word "\\DateTime") (:list)) (:terminator ";"))))))))
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:variable "token") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:variable "valid") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "string")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";"))))))))
|
File diff suppressed because one or more lines are too long
@ -1 +1 @@
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:class-variable "token") (:terminator ";")) (:private (:class-variable "extra") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:class-variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:class-variable "valid") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:class-variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "anAddedFunction") (:list)) (:block (:word "return") (:variable "this") (:object-attrib (:word "extra")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";"))))))))
|
||||
(:root (:word "declare") (:list (:word "strict_types") (:assignment "=")) (:terminator ";") (:namespace (:word "App\\Entity") (:terminator ";") (:use (:word "Doctrine\\ORM\\Mapping") (:word "as") (:word "ORM") (:terminator ";")) (:doc-block (:annotation "ORM\\Entity")) (:class (:declaration (:word "class") (:word "AuthToken")) (:block (:private (:variable "token") (:terminator ";")) (:private (:variable "extra") (:terminator ";")) (:doc-block (:var-annotation (:word "App\\\\Entity\\\\User"))) (:private (:variable "user") (:terminator ";")) (:doc-block (:var-annotation (:word "bool"))) (:private (:variable "valid") (:terminator ";")) (:doc-block (:var-annotation (:word "\\DateTime"))) (:private (:variable "creation_time") (:terminator ";")) (:public (:function (:declaration (:word "function") (:word "__construct") (:list (:word "string") (:variable "token") (:comma ",") (:word "User") (:variable "user") (:comma ",") (:word "bool") (:variable "valid") (:assignment "=") (:word "false") (:comma ",") (:word "\\DateTime") (:variable "creation_time") (:assignment "=") (:word "null"))) (:block (:variable "this") (:object-attrib (:word "token")) (:assignment "=") (:variable "token") (:terminator ";") (:variable "this") (:object-attrib (:word "user")) (:assignment "=") (:variable "user") (:terminator ";") (:variable "this") (:object-attrib (:word "valid")) (:assignment "=") (:variable "valid") (:terminator ";") (:variable "this") (:object-attrib (:word "creation_time")) (:assignment "=") (:variable "creation_time") (:word "new") (:word "\\DateTime") (:list) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getToken") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "token")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getUser") (:list) (:word "User")) (:block (:word "return") (:variable "this") (:object-attrib (:word "user")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "hasStudentRole") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "role_student")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "isValid") (:list) (:word "bool")) (:block (:word "return") (:variable "this") (:object-attrib (:word "valid")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "anAddedFunction") (:list)) (:block (:word "return") (:variable "this") (:object-attrib (:word "extra")) (:terminator ";")))) (:public (:function (:declaration (:word "function") (:word "getCreationTime") (:list) (:word "\\DateTime")) (:block (:word "return") (:variable "this") (:object-attrib (:word "creation_time")) (:terminator ";"))))))))
|
File diff suppressed because one or more lines are too long
@ -1,36 +0,0 @@
|
||||
;;; phpinspect-test-env.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'phpinspect-worker)
|
||||
(require 'phpinspect-cache)
|
||||
(require 'phpinspect-parser)
|
||||
|
||||
;; Make sure that the worker is running. TODO: fully encapsulate the worker the
|
||||
;; data types that are used in tests so that we don't depend on some global
|
||||
;; worker object for tests.
|
||||
(phpinspect-ensure-worker)
|
||||
(phpinspect-purge-cache)
|
||||
|
||||
(defvar phpinspect-test-directory
|
||||
(file-name-directory (macroexp-file-name))
|
||||
"Directory that phpinspect tests reside in.")
|
||||
|
||||
|
||||
(defvar phpinspect-test-php-file-directory
|
||||
(expand-file-name "fixtures" phpinspect-test-directory)
|
||||
"Directory with syntax trees of example PHP files.")
|
||||
|
||||
(defun phpinspect-test-read-fixture-data (name)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally (concat phpinspect-test-php-file-directory "/" name ".eld"))
|
||||
(read (current-buffer))))
|
||||
|
||||
(defun phpinspect-test-read-fixture-serialization (name)
|
||||
(with-temp-buffer
|
||||
(insert-file-contents-literally (concat phpinspect-test-php-file-directory "/" name ".eld"))
|
||||
(eval (read (current-buffer)) t)))
|
||||
|
||||
(defun phpinspect-test-parse-fixture-code (name)
|
||||
(phpinspect-parse-file
|
||||
(concat phpinspect-test-php-file-directory "/" name ".php")))
|
||||
|
||||
(provide 'phpinspect-test-env)
|
@ -1,183 +0,0 @@
|
||||
; test-autoload.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-fs)
|
||||
(require 'phpinspect-autoload)
|
||||
(require 'phpinspect-resolvecontext)
|
||||
|
||||
(ert-deftest phpinspect-filename-to-typename ()
|
||||
(should (eq (phpinspect-intern-name "\\Foo\\Bar") (phpinspect-filename-to-typename "src/" "src/Foo////////Bar.php")))
|
||||
|
||||
(should (eq (phpinspect-intern-name "\\Foo\\Bar") (phpinspect-filename-to-typename "src/somewhere/else/" "src/somewhere/else/Foo/Bar.php"))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-find-composer-json-files ()
|
||||
(let* ((fs (phpinspect-make-virtual-fs)))
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/composer.json"
|
||||
"{ \"autoload\": { \"psr-4\": {\"WoW\\\\Dwarves\\\\\": \"src/\"}}}")
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/vendor/runescape/client/composer.json"
|
||||
"{\"autoload\": { \"psr-0\": {\"Runescape\\\\Banana\\\\\": [\"src/\", \"lib\"]}}}")
|
||||
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/vendor/apples/pears/composer.json"
|
||||
"{\"autoload\": { \"psr-0\": {\"Runescape\\\\Banana\\\\\": [\"src/\", \"lib\"]}}}")
|
||||
|
||||
(let ((sorter (lambda (file1 file2) (string-lessp (cdr file1) (cdr file2)))))
|
||||
|
||||
(should (equal (sort (copy-sequence
|
||||
'((vendor . "/root/vendor/apples/pears/composer.json")
|
||||
(vendor . "/root/vendor/runescape/client/composer.json")
|
||||
(local . "/root/composer.json")))
|
||||
sorter)
|
||||
(sort (phpinspect-find-composer-json-files fs "/root")
|
||||
sorter))))))
|
||||
|
||||
(ert-deftest phpinspect-autoload-composer-json-iterator ()
|
||||
(let* ((fs (phpinspect-make-virtual-fs))
|
||||
(autoloader (phpinspect-make-autoloader
|
||||
:fs fs
|
||||
:project-root-resolver (lambda () "/root")
|
||||
:file-indexer
|
||||
(phpinspect-project-make-file-indexer
|
||||
(phpinspect--make-project :root "/root" :fs fs))))
|
||||
result error)
|
||||
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/composer.json"
|
||||
"{ \"autoload\": { \"psr-4\": {\"WoW\\\\Dwarves\\\\\": \"src/\"}}}")
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/vendor/runescape/client/composer.json"
|
||||
"{\"autoload\": { \"psr-0\": {\"Runescape\\\\Banana\\\\\": [\"src/\", \"lib\"]}}}")
|
||||
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/root/vendor/apples/pears/composer.json"
|
||||
"{\"autoload\": { \"psr-0\": {\"Runescape\\\\Banana\\\\\": [\"src/\", \"lib\"]},
|
||||
\"psr-4\": {\"Another\\\\Namespace\\\\\": [\"separate/\"]}}}")
|
||||
|
||||
(phpinspect-pipeline (phpinspect-find-composer-json-files fs "/root")
|
||||
:async (lambda (res err)
|
||||
(setq result res
|
||||
error err))
|
||||
:into (phpinspect-iterate-composer-jsons :with-context autoloader))
|
||||
|
||||
(while (not (or result error))
|
||||
(thread-yield))
|
||||
|
||||
(should-not error)
|
||||
|
||||
(should (= 4 (length result)))
|
||||
(should (= 2 (length (seq-filter #'phpinspect-psr0-p result))))
|
||||
(should (= 2 (length (seq-filter #'phpinspect-psr4-p result))))))
|
||||
|
||||
(ert-deftest phpinspect-al-put-type-bag ()
|
||||
(let ((al (phpinspect-make-autoloader)))
|
||||
(phpinspect-autoloader-put-type-bag al (phpinspect-intern-name "\\App\\Place\\Mountain"))
|
||||
(phpinspect-autoloader-put-type-bag al (phpinspect-intern-name "\\App\\Earth\\Mountain"))
|
||||
|
||||
(should (equal `(,(phpinspect-intern-name "\\App\\Place\\Mountain")
|
||||
,(phpinspect-intern-name "\\App\\Earth\\Mountain"))
|
||||
(phpinspect-autoloader-get-type-bag al (phpinspect-intern-name "Mountain"))))))
|
||||
|
||||
(ert-deftest phpinspect-al-strategy-execute ()
|
||||
(let* ((fs (phpinspect-make-virtual-fs))
|
||||
(project (phpinspect--make-project :root "/project/root" :fs fs))
|
||||
(autoloader (phpinspect-make-autoloader
|
||||
:fs fs
|
||||
:project-root-resolver (lambda () "/project/root")
|
||||
:file-indexer (phpinspect-project-make-file-indexer project)))
|
||||
result error)
|
||||
|
||||
(setf (phpinspect-project-autoload project) autoloader)
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs
|
||||
"/project/root/composer.json"
|
||||
"{ \"autoload\": { \"psr-4\": {\"App\\\\Banana\\\\\": [\"src/\", \"lib\"]}}}")
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs "/project/root/src/TestClass.php" "")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs
|
||||
"/project/root/vendor/runescape/client/composer.json"
|
||||
"{\"autoload\": { \"psr-0\": {\"Runescape\\\\Banana\\\\\": [\"src/\", \"lib\"]}}}")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs "/project/root/vendor/runescape/client/src/TestClass.php" "")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs
|
||||
"/project/root/vendor/runescape/client/src/Runescape/Banana/App.php"
|
||||
"")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs "/project/root/vendor/runescape/client/src/LibClass.php" "")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs
|
||||
"/project/root/vendor/not-runescape/wow/composer.json"
|
||||
"{ \"autoload\": { \"psr-4\": {\"WoW\\\\Dwarves\\\\\": \"src/\"},
|
||||
\"files\": [ \"include/FilesList.php\"]}}")
|
||||
|
||||
(phpinspect-virtual-fs-set-file fs
|
||||
"/project/root/vendor/not-runescape/wow/include/FilesList.php"
|
||||
"<?php class FilesList { function list() {} }")
|
||||
|
||||
(phpinspect-virtual-fs-set-file
|
||||
fs "/project/root/vendor/not-runescape/wow/src/TestClass.php" "")
|
||||
|
||||
(phpinspect-pipeline (phpinspect-find-composer-json-files fs "/project/root")
|
||||
:async (lambda (res err)
|
||||
(setq result res
|
||||
error err))
|
||||
:into (phpinspect-iterate-composer-jsons :with-context autoloader)
|
||||
:into phpinspect-al-strategy-execute)
|
||||
|
||||
(while (not (or result error))
|
||||
(thread-yield))
|
||||
|
||||
(should-not error)
|
||||
|
||||
(should-not (hash-table-empty-p (phpinspect-autoloader-own-types autoloader)))
|
||||
(should-not (hash-table-empty-p (phpinspect-autoloader-types autoloader)))
|
||||
|
||||
(should (phpinspect-project-get-class project (phpinspect--make-type :name "\\FilesList")))
|
||||
|
||||
(should (string= "/project/root/vendor/runescape/client/src/Runescape/Banana/App.php"
|
||||
(phpinspect-autoloader-resolve
|
||||
autoloader
|
||||
(phpinspect-intern-name "\\Runescape\\Banana\\App"))))
|
||||
(should (string= "/project/root/vendor/not-runescape/wow/src/TestClass.php"
|
||||
(phpinspect-autoloader-resolve
|
||||
autoloader
|
||||
(phpinspect-intern-name "\\WoW\\Dwarves\\TestClass"))))))
|
@ -1,91 +0,0 @@
|
||||
;; -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'phpinspect-bmap)
|
||||
|
||||
(ert-deftest phpinspect-bmap-overlay ()
|
||||
(let ((bmap (phpinspect-make-bmap))
|
||||
(bmap2 (phpinspect-make-bmap))
|
||||
(bmap3 (phpinspect-make-bmap))
|
||||
(token '(:token))
|
||||
(token2 '(:token2))
|
||||
(token3 '(:token3)))
|
||||
|
||||
(phpinspect-bmap-register bmap 10 20 token)
|
||||
(phpinspect-bmap-register bmap2 20 24 token2)
|
||||
(phpinspect-bmap-register bmap3 40 50 token3)
|
||||
|
||||
(should (phpinspect-bmap-token-starting-at bmap 10))
|
||||
|
||||
(phpinspect-bmap-overlay
|
||||
bmap bmap3 (phpinspect-bmap-token-starting-at bmap3 40) 10)
|
||||
|
||||
(should (phpinspect-bmap-token-starting-at bmap 50))
|
||||
|
||||
(phpinspect-bmap-overlay
|
||||
bmap2 bmap (phpinspect-bmap-token-starting-at bmap 10) -3)
|
||||
|
||||
(phpinspect-bmap-overlay
|
||||
bmap2 bmap (phpinspect-bmap-token-starting-at bmap 50) 5)
|
||||
|
||||
|
||||
(should (eq token2 (phpinspect-meta-token
|
||||
(phpinspect-bmap-token-starting-at bmap2 20))))
|
||||
(should (eq token (phpinspect-meta-token
|
||||
(phpinspect-bmap-token-starting-at bmap2 7))))
|
||||
|
||||
;; Nesting for token-starting-at
|
||||
(should (eq token3 (phpinspect-meta-token
|
||||
(phpinspect-bmap-token-starting-at bmap 50))))
|
||||
|
||||
(should (eq token3 (phpinspect-meta-token
|
||||
(phpinspect-bmap-token-starting-at bmap2 55))))
|
||||
|
||||
|
||||
(should (phpinspect-bmap-token-meta bmap token))
|
||||
(should (phpinspect-bmap-token-meta bmap2 token2))
|
||||
(should (phpinspect-bmap-token-meta bmap2 token))
|
||||
(should (phpinspect-bmap-token-meta bmap2 token3))))
|
||||
|
||||
(ert-deftest phpinspect-bmap-nest-parent ()
|
||||
(let ((bmap (phpinspect-make-bmap))
|
||||
(child '(:child))
|
||||
(parent '(:parent))
|
||||
(granny '(:granny)))
|
||||
(phpinspect-bmap-register bmap 10 20 child)
|
||||
(phpinspect-bmap-register bmap 5 25 parent)
|
||||
(phpinspect-bmap-register bmap 2 30 granny)
|
||||
|
||||
(let ((child-meta (phpinspect-bmap-token-meta bmap child))
|
||||
(parent-meta (phpinspect-bmap-token-meta bmap parent)))
|
||||
(should (eq parent (phpinspect-meta-token
|
||||
(phpinspect-meta-parent child-meta))))
|
||||
(should (eq granny (phpinspect-meta-token (phpinspect-meta-parent parent-meta)))))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-bmap-tokens-overlapping ()
|
||||
(let ((bmap (phpinspect-make-bmap)))
|
||||
(phpinspect-bmap-register bmap 9 20 '(:node3))
|
||||
(phpinspect-bmap-register bmap 21 44 '(:node4))
|
||||
(phpinspect-bmap-register bmap 20 200 '(:node2))
|
||||
(phpinspect-bmap-register bmap 9 200 '(:node1))
|
||||
(phpinspect-bmap-register bmap 1 300 '(:root))
|
||||
|
||||
(let ((result (phpinspect-bmap-tokens-overlapping bmap 22)))
|
||||
(should (equal '((:node4) (:node2) (:node1))
|
||||
(mapcar #'phpinspect-meta-token result))))))
|
||||
|
||||
(ert-deftest phpinspect-bmap-register ()
|
||||
(let* ((bmap (phpinspect-make-bmap))
|
||||
(token1 `(:word "foo"))
|
||||
(token2 `(:word "bar"))
|
||||
(token3 `(:block ,token1 ,token2))
|
||||
(token4 `(:list ,token3)))
|
||||
(phpinspect-bmap-register bmap 10 20 token1)
|
||||
(phpinspect-bmap-register bmap 20 30 token2)
|
||||
(phpinspect-bmap-register bmap 9 31 token3)
|
||||
(phpinspect-bmap-register bmap 8 32 token4)
|
||||
|
||||
(should (phpinspect-bmap-token-meta bmap token1))
|
||||
(should (phpinspect-bmap-token-meta bmap token2))
|
||||
(should (phpinspect-bmap-token-meta bmap token3))
|
||||
(should (phpinspect-bmap-token-meta bmap token4))))
|
@ -1,493 +0,0 @@
|
||||
;; test-buffer.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-test-env
|
||||
(expand-file-name "phpinspect-test-env.el"
|
||||
(file-name-directory (macroexp-file-name))))
|
||||
|
||||
(ert-deftest phpinspect-buffer-region-lookups ()
|
||||
(let* (parsed)
|
||||
(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))
|
||||
|
||||
(let* ((class (seq-find #'phpinspect-class-p
|
||||
(seq-find #'phpinspect-namespace-p parsed)))
|
||||
(classname (car (cddadr class))))
|
||||
|
||||
(let ((tokens (phpinspect-buffer-tokens-enclosing-point
|
||||
phpinspect-current-buffer 617)))
|
||||
(should (eq classname
|
||||
(phpinspect-meta-token (car tokens))))
|
||||
(should (phpinspect-declaration-p (phpinspect-meta-token (cadr tokens))))
|
||||
(should (eq class (phpinspect-meta-token (caddr tokens)))))))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-parse-buffer-no-current ()
|
||||
"Confirm that the parser is still functional with
|
||||
`phpinspect-current-buffer' unset."
|
||||
(let* ((parsed
|
||||
(with-temp-buffer
|
||||
(should-not phpinspect-current-buffer)
|
||||
(insert-file-contents (expand-file-name "NamespacedClass.php" phpinspect-test-php-file-directory))
|
||||
(phpinspect-parse-current-buffer))))
|
||||
|
||||
(should (cdr parsed))))
|
||||
|
||||
(cl-defstruct (phpinspect-document (:constructor phpinspect-make-document))
|
||||
(buffer (get-buffer-create
|
||||
(generate-new-buffer-name "**phpinspect-document** shadow buffer") t)
|
||||
:type buffer
|
||||
:documentation
|
||||
"A hidden buffer with a reference version of the document."))
|
||||
|
||||
(cl-defmethod phpinspect-document-apply-edit
|
||||
((document phpinspect-document) start end delta contents)
|
||||
(with-current-buffer (phpinspect-document-buffer document)
|
||||
(goto-char start)
|
||||
(delete-region (point) (- end delta))
|
||||
(insert contents)))
|
||||
|
||||
(cl-defmethod phpinspect-document-set-contents
|
||||
((document phpinspect-document) (contents string))
|
||||
(with-current-buffer (phpinspect-document-buffer document)
|
||||
(erase-buffer)
|
||||
(insert contents)))
|
||||
|
||||
(defmacro phpinspect-document-setq-local (document &rest assignments)
|
||||
(declare (indent 1))
|
||||
`(with-current-buffer (phpinspect-document-buffer ,document)
|
||||
(setq-local ,@assignments)))
|
||||
|
||||
(defmacro phpinspect-with-document-buffer (document &rest body)
|
||||
(declare (indent 1))
|
||||
`(with-current-buffer (phpinspect-document-buffer ,document)
|
||||
,@body))
|
||||
|
||||
(cl-defmethod phpinspect-document-contents ((document phpinspect-document))
|
||||
(with-current-buffer (phpinspect-document-buffer document)
|
||||
(buffer-string)))
|
||||
|
||||
(ert-deftest phpinspect-buffer-parse-incrementally ()
|
||||
(let* ((document (phpinspect-make-document))
|
||||
(buffer (phpinspect-make-buffer
|
||||
:buffer (phpinspect-document-buffer document)))
|
||||
(parsed))
|
||||
;; TODO: write tests for more complicated cases (multiple edits, etc.)
|
||||
(phpinspect-document-set-contents document "<?php function Bello() { echo 'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }")
|
||||
|
||||
(setq parsed (phpinspect-buffer-parse buffer))
|
||||
(should parsed)
|
||||
|
||||
(let* ((enclosing-bello (phpinspect-buffer-tokens-enclosing-point buffer 18))
|
||||
(bello (car enclosing-bello))
|
||||
(enclosing-bello1)
|
||||
(bello1)
|
||||
(bello2))
|
||||
(should (equal '(:word "Bello") (phpinspect-meta-token bello)))
|
||||
(should parsed)
|
||||
|
||||
;; Delete function block opening brace
|
||||
(phpinspect-document-apply-edit document 24 24 -1 "")
|
||||
(should (string= "<?php function Bello() echo 'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }"
|
||||
(phpinspect-document-contents document)))
|
||||
(phpinspect-buffer-register-edit buffer 24 24 1)
|
||||
(setq parsed (phpinspect-buffer-parse buffer))
|
||||
(should parsed)
|
||||
(setq enclosing-bello1 (phpinspect-buffer-tokens-enclosing-point buffer 18))
|
||||
(setq bello1 (car enclosing-bello1))
|
||||
(should (eq (phpinspect-meta-token bello) (phpinspect-meta-token bello1)))
|
||||
|
||||
(should (phpinspect-declaration-p (phpinspect-meta-token (phpinspect-meta-parent bello))))
|
||||
(should (phpinspect-declaration-p (phpinspect-meta-token (phpinspect-meta-parent bello1))))
|
||||
|
||||
(should (phpinspect-function-p (phpinspect-meta-token (phpinspect-meta-parent (phpinspect-meta-parent bello)))))
|
||||
(should (phpinspect-function-p (phpinspect-meta-token (phpinspect-meta-parent (phpinspect-meta-parent bello1)))))
|
||||
|
||||
(let ((function (phpinspect-meta-token (phpinspect-meta-parent (phpinspect-meta-parent bello1)))))
|
||||
(should (= 2 (length function)))
|
||||
(should (phpinspect-declaration-p (cadr function)))
|
||||
(should (member '(:word "Bello") (cadr function)))
|
||||
(should (member '(:word "echo") (cadr function))))
|
||||
|
||||
(phpinspect-document-apply-edit document 24 25 1 "{")
|
||||
(should (string= "<?php function Bello() { echo 'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }"
|
||||
(phpinspect-document-contents document)))
|
||||
(phpinspect-buffer-register-edit buffer 24 25 0)
|
||||
(setq parsed (phpinspect-buffer-parse buffer))
|
||||
(should parsed)
|
||||
(setq bello2 (car (phpinspect-buffer-tokens-enclosing-point buffer 18)))
|
||||
(should (eq (phpinspect-meta-token bello) (phpinspect-meta-token bello2))))))
|
||||
|
||||
(ert-deftest phpinspect-buffer-parse-incrementally-position-change ()
|
||||
(with-temp-buffer
|
||||
(let ((buffer (phpinspect-make-buffer :buffer (current-buffer))))
|
||||
(insert "<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\\Controller\\Api\\V1;
|
||||
|
||||
class AccountStatisticsController {
|
||||
|
||||
function __construct(){}
|
||||
}")
|
||||
|
||||
(setq-local phpinspect-test-buffer t)
|
||||
(add-to-list 'after-change-functions
|
||||
(lambda (start end pre-change-length)
|
||||
(when (boundp 'phpinspect-test-buffer)
|
||||
(phpinspect-buffer-register-edit buffer start end pre-change-length))))
|
||||
|
||||
(let* ((bmap (phpinspect-buffer-parse-map buffer))
|
||||
(class-location 67)
|
||||
(class (phpinspect-bmap-token-starting-at bmap class-location)))
|
||||
|
||||
(should class)
|
||||
(should (phpinspect-class-p (phpinspect-meta-token class)))
|
||||
(should (= class-location (phpinspect-meta-start class)))
|
||||
|
||||
(goto-char 65)
|
||||
(let ((edit-string "use Symfony\\Component\\HttpFoundation\\JsonResponse;\n")
|
||||
bmap class tokens-enclosing use-statement)
|
||||
(insert edit-string)
|
||||
|
||||
(setq bmap (phpinspect-buffer-parse-map buffer)
|
||||
class (phpinspect-bmap-token-starting-at bmap (+ 67 (length edit-string))))
|
||||
|
||||
(setq class-location (+ class-location (length edit-string)))
|
||||
(should class)
|
||||
(should (phpinspect-class-p (phpinspect-meta-token class)))
|
||||
(should (= class-location (phpinspect-meta-start class)))
|
||||
|
||||
(setq tokens-enclosing (phpinspect-bmap-tokens-overlapping bmap class-location))
|
||||
(setq class (seq-find (lambda (meta) (phpinspect-class-p (phpinspect-meta-token meta)))
|
||||
tokens-enclosing))
|
||||
(should class)
|
||||
(should (= class-location (phpinspect-meta-start class)))
|
||||
(should (phpinspect-class-p (phpinspect-meta-token class)))
|
||||
|
||||
(setq use-statement (phpinspect-bmap-token-starting-at bmap 65))
|
||||
(should use-statement)
|
||||
(should (phpinspect-use-p (phpinspect-meta-token use-statement)))
|
||||
(should (seq-find #'phpinspect-use-p (seq-find #'phpinspect-namespace-p (phpinspect-buffer-tree buffer))))
|
||||
|
||||
(let ((second-use))
|
||||
(goto-char 65)
|
||||
(setq edit-string "use Another\\Use\\Statement;\n")
|
||||
(insert edit-string)
|
||||
|
||||
(setq class-location (+ class-location (length edit-string)))
|
||||
(setq bmap (phpinspect-buffer-parse-map buffer)
|
||||
class (phpinspect-bmap-token-starting-at bmap class-location))
|
||||
|
||||
(should class)
|
||||
|
||||
(setq second-use (phpinspect-bmap-token-starting-at bmap 65))
|
||||
(should second-use)
|
||||
|
||||
(setq class (phpinspect-bmap-token-starting-at bmap class-location))
|
||||
(should class)
|
||||
(should (= class-location (phpinspect-meta-start class)))
|
||||
(should (phpinspect-class-p (phpinspect-meta-token class)))))))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-buffer-parse-incrementally-multiedit ()
|
||||
(let* ((document (phpinspect-make-document))
|
||||
(buffer (phpinspect-make-buffer
|
||||
:buffer (phpinspect-document-buffer document)))
|
||||
parsed parsed-after current-tree)
|
||||
|
||||
(phpinspect-document-set-contents
|
||||
document
|
||||
"<?php
|
||||
|
||||
|
||||
namespace XXX;
|
||||
|
||||
use ZZZ\\zzz;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
class YYY {
|
||||
|
||||
function Foo() {
|
||||
if(bar()) {
|
||||
return $baz->bip->bop(bar($bim), $bom)
|
||||
}
|
||||
}
|
||||
}")
|
||||
|
||||
(phpinspect-document-setq-local document
|
||||
phpinspect-current-buffer buffer)
|
||||
(phpinspect-with-document-buffer document
|
||||
(setq buffer-undo-list nil)
|
||||
(add-hook 'after-change-functions #'phpinspect-after-change-function))
|
||||
|
||||
(setq parsed (phpinspect-buffer-parse buffer 'no-interrupt))
|
||||
|
||||
;; Delete lines before class
|
||||
(phpinspect-with-document-buffer document
|
||||
(goto-char 40)
|
||||
(kill-line)
|
||||
(kill-line)
|
||||
(kill-line))
|
||||
|
||||
(setq parsed-after (phpinspect-buffer-parse buffer 'no-interrupt))
|
||||
|
||||
(should (equal parsed parsed-after))
|
||||
|
||||
;; Delete namespace declaration
|
||||
(phpinspect-with-document-buffer document
|
||||
(goto-char 9)
|
||||
(kill-line))
|
||||
|
||||
(setq parsed-after (phpinspect-buffer-parse buffer 'no-interrupt))
|
||||
(setq current-tree (phpinspect-with-document-buffer document
|
||||
(goto-char (point-min))
|
||||
(phpinspect-parse-buffer-until-point (current-buffer) (point-max))))
|
||||
|
||||
(should (equal current-tree parsed-after))
|
||||
|
||||
;;Bring back the namespace declaration
|
||||
(phpinspect-with-document-buffer document
|
||||
(undo-start)
|
||||
(undo-more 1))
|
||||
|
||||
(setq parsed-after (phpinspect-buffer-parse buffer 'no-interrupt))
|
||||
|
||||
(should (equal parsed parsed-after))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-buffer-index-classes ()
|
||||
(let* ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
|
||||
(namespaces (phpinspect-make-splayt))
|
||||
(declarations (phpinspect-make-splayt))
|
||||
(classes (phpinspect-make-splayt))
|
||||
(root (phpinspect-make-meta nil 1 200 "" 'root)))
|
||||
(phpinspect-splayt-insert
|
||||
namespaces 1 (phpinspect-meta-set-parent
|
||||
(phpinspect-make-meta nil 1 100 "" '(:namespace (:word "TestNamespace") (:terminator ";")))
|
||||
root))
|
||||
(phpinspect-splayt-insert
|
||||
declarations 20
|
||||
(phpinspect-make-meta nil 20 40 "" '(:declaration (:word "class") (:word "TestClass") (:word "extends") (:word "OtherTestClass"))))
|
||||
|
||||
|
||||
(phpinspect-splayt-insert classes 20 (phpinspect-make-meta nil 20 80 "" '(:class (:comment "bla") '(:declaration (:word "class") (:word "TestClass") (:word "extends") (:word "OtherTestClass")))))
|
||||
|
||||
(phpinspect-buffer-index-declarations buffer declarations)
|
||||
(phpinspect-buffer-index-namespaces buffer namespaces)
|
||||
(phpinspect-buffer-index-classes buffer classes)
|
||||
|
||||
(should (phpinspect-project-get-class (phpinspect-buffer-project buffer) (phpinspect--make-type :name "\\TestNamespace\\TestClass")))
|
||||
|
||||
(should (= 2 (hash-table-count (phpinspect-project-class-index (phpinspect-buffer-project buffer)))))
|
||||
(should (= 1 (length (phpinspect--class-extended-classes
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestNamespace\\TestClass"))))))
|
||||
|
||||
(let ((new-declarations (phpinspect-make-splayt))
|
||||
(new-classes (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert
|
||||
new-declarations
|
||||
20
|
||||
(phpinspect-meta-set-parent
|
||||
(phpinspect-make-meta nil 20 40 "" '(:declaration (:word "class") (:word "TestClass")))
|
||||
root))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
new-classes 20
|
||||
(phpinspect-meta-set-parent
|
||||
(phpinspect-make-meta nil 20 80 "" '(:class (:comment "bla") '(:declaration (:word "class") (:word "TestClass"))))
|
||||
root))
|
||||
|
||||
(setf (phpinspect-buffer-map buffer) (phpinspect-make-bmap :-root-meta root))
|
||||
|
||||
(phpinspect-buffer-index-declarations buffer new-declarations)
|
||||
(phpinspect-buffer-index-classes buffer new-classes)
|
||||
(should (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestNamespace\\TestClass")))
|
||||
|
||||
(should (= 0 (length (phpinspect--class-extended-classes
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestNamespace\\TestClass")))))))
|
||||
|
||||
(let ((new-classes (phpinspect-make-splayt))
|
||||
(new-root (phpinspect-make-meta nil 1 400 "" 'new-root)))
|
||||
(setf (phpinspect-bmap--root-meta (phpinspect-buffer-map buffer)) new-root)
|
||||
(phpinspect-buffer-index-classes buffer new-classes)
|
||||
|
||||
(should-not (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestNamespace\\TestClass")))
|
||||
|
||||
(should (= 1 (hash-table-count (phpinspect-project-class-index (phpinspect-buffer-project buffer))))))))
|
||||
|
||||
(ert-deftest phpinspect-buffer-index-functions ()
|
||||
(let ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
|
||||
(namespaces (phpinspect-make-splayt))
|
||||
(declarations (phpinspect-make-splayt))
|
||||
(classes (phpinspect-make-splayt))
|
||||
(functions (phpinspect-make-splayt)))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
namespaces 10
|
||||
(phpinspect-make-meta nil 10 200 "" '(:namespace (:word "NS") (:terminator ";"))))
|
||||
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
declarations 20
|
||||
(phpinspect-make-meta nil 20 30 "" '(:declaration (:word "class") (:word "TestClass"))))
|
||||
(phpinspect-splayt-insert
|
||||
classes 20
|
||||
(phpinspect-make-meta nil 20 70 "" '(:class (:declaration (:word "class") (:word "TestClass")))))
|
||||
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
declarations 40
|
||||
(phpinspect-make-meta nil 40 45 "" '(:declaration (:word "testMethod") (:list) (:word "RelativeType"))))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
functions 40
|
||||
(phpinspect-make-meta nil 40 50 "" '(:function (:declaration (:word "testMethod") (:list) (:word "RelativeType")))))
|
||||
|
||||
(phpinspect-buffer-index-declarations buffer declarations)
|
||||
(phpinspect-buffer-index-namespaces buffer namespaces)
|
||||
(phpinspect-buffer-index-classes buffer classes)
|
||||
|
||||
(phpinspect-buffer-index-functions buffer functions)
|
||||
|
||||
(should (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\NS\\TestClass")))
|
||||
|
||||
(should (= 1 (hash-table-count (phpinspect--class-methods
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\NS\\TestClass"))))))
|
||||
|
||||
(setf (phpinspect-buffer-map buffer) (phpinspect-make-bmap :-root-meta (phpinspect-make-meta nil 1 400 "" 'root)))
|
||||
|
||||
(phpinspect-buffer-index-functions buffer (phpinspect-make-splayt))
|
||||
|
||||
(should (= 0 (hash-table-count (phpinspect--class-methods
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\NS\\TestClass"))))))))
|
||||
|
||||
(ert-deftest phpinspect-buffer-index-class-variables ()
|
||||
(let ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project :autoload (phpinspect-make-autoloader))))
|
||||
(namespaces (phpinspect-make-splayt))
|
||||
(declarations (phpinspect-make-splayt))
|
||||
(classes (phpinspect-make-splayt))
|
||||
(functions (phpinspect-make-splayt))
|
||||
(variables (phpinspect-make-splayt)))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
functions 60
|
||||
(phpinspect-make-meta
|
||||
nil 60 65 ""
|
||||
(cadr (phpinspect-parse-string
|
||||
"<?php function __construct(array $thing) { $this->banana = $thing; }"))))
|
||||
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
declarations 20
|
||||
(phpinspect-make-meta nil 20 30 "" '(:declaration (:word "class") (:word "TestClass"))))
|
||||
(phpinspect-splayt-insert
|
||||
classes 20
|
||||
(phpinspect-make-meta nil 20 70 "" '(:class (:declaration (:word "class") (:word "TestClass")))))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
variables 33
|
||||
(phpinspect-make-meta nil 33 50 "" '(:class-variable "banana")))
|
||||
|
||||
(phpinspect-splayt-insert
|
||||
variables 54
|
||||
(phpinspect-make-meta nil 54 60 "" '(:const (:word "CONSTANT"))))
|
||||
|
||||
(phpinspect-buffer-index-declarations buffer declarations)
|
||||
(phpinspect-buffer-index-namespaces buffer namespaces)
|
||||
(phpinspect-buffer-index-classes buffer classes)
|
||||
(phpinspect-buffer-index-functions buffer functions)
|
||||
|
||||
(phpinspect-buffer-index-class-variables buffer variables)
|
||||
|
||||
(should (phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestClass")))
|
||||
|
||||
(should (= 2 (length (phpinspect--class-variables
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestClass"))))))
|
||||
|
||||
|
||||
(should (= 1 (length (phpinspect--class-get-constants
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestClass"))))))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(phpinspect--variable-type
|
||||
(phpinspect--class-get-variable
|
||||
(phpinspect-project-get-class
|
||||
(phpinspect-buffer-project buffer)
|
||||
(phpinspect--make-type :name "\\TestClass"))
|
||||
"banana"))))))
|
||||
|
||||
(ert-deftest phpinspect-buffer-map-imports ()
|
||||
(with-temp-buffer
|
||||
(let ((buffer (phpinspect-make-buffer :buffer (current-buffer))))
|
||||
(insert "<?php
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace App\\Controller\\Api\\V1;
|
||||
|
||||
use Illuminate\\Database\\Eloquent\\Model;
|
||||
use Illuminate\\Database\\Eloquent\\Relations\\Relation;
|
||||
use Illuminate\\Support\\Facades\\Auth;
|
||||
|
||||
class AccountStatisticsController {
|
||||
|
||||
function __construct(){}
|
||||
}")
|
||||
(let ((bmap (phpinspect-buffer-parse-map buffer)))
|
||||
(should (equal
|
||||
`((:use (:word "Illuminate\\Database\\Eloquent\\Model") (:terminator ";"))
|
||||
(:use (:word "Illuminate\\Database\\Eloquent\\Relations\\Relation") (:terminator ";"))
|
||||
(:use (:word "Illuminate\\Support\\Facades\\Auth") (:terminator ";")))
|
||||
(mapcar #'phpinspect-meta-token
|
||||
(phpinspect-splayt-to-list (phpinspect-bmap-imports bmap)))))))))
|
@ -1,113 +0,0 @@
|
||||
;; test-class.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-class)
|
||||
(require 'phpinspect-project)
|
||||
(require 'subr-x)
|
||||
(require 'phpinspect-worker)
|
||||
|
||||
(phpinspect-ensure-worker)
|
||||
|
||||
(ert-deftest phpinspect--merge-method-return-type ()
|
||||
(let* ((class-name (phpinspect--make-type :name "\\Something"))
|
||||
(method1 (phpinspect--make-function
|
||||
:name "fun"
|
||||
:return-type (phpinspect--make-type :name "\\array")))
|
||||
(method2 (phpinspect--make-function
|
||||
:name "fun"
|
||||
:return-type (phpinspect--make-type :name "\\bool")))
|
||||
(result (phpinspect--merge-method class-name method1 method2)))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\bool")
|
||||
(phpinspect--function-return-type result)))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\bool")
|
||||
(phpinspect--function-return-type method1)))))
|
||||
|
||||
(ert-deftest phpinspect-class-incorporate ()
|
||||
(let ((class (phpinspect--make-class-generated))
|
||||
(other-class (phpinspect--make-class-generated)))
|
||||
(phpinspect--class-set-index class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "Class"))))
|
||||
(phpinspect--class-set-index other-class `(phpinspect--indexed-class (class-name . ,(phpinspect--make-type :name "OtherClass"))))
|
||||
(phpinspect--class-update-method
|
||||
class (phpinspect--make-function :name "test" :return-type phpinspect--null-type))
|
||||
|
||||
(phpinspect--class-update-method
|
||||
other-class (phpinspect--make-function :name "other-test" :return-type phpinspect--null-type))
|
||||
|
||||
(phpinspect--class-incorporate class other-class)
|
||||
|
||||
(should (= 2 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
(should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class)))))
|
||||
|
||||
(phpinspect--class-set-index
|
||||
class
|
||||
`(phpinspect--indexed-class
|
||||
(complete . t)
|
||||
(class-name . ,(phpinspect--make-type :name "Class"))
|
||||
(methods . ,(list (phpinspect--make-function :name "test" :return-type phpinspect--null-type)
|
||||
(phpinspect--make-function :name "foobar" :return-type phpinspect--null-type)))))
|
||||
|
||||
(should (= 3 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
|
||||
(phpinspect--class-incorporate class other-class)
|
||||
(should (= 3 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
|
||||
(phpinspect--class-set-index
|
||||
class
|
||||
`(phpinspect--indexed-class
|
||||
(complete . t)
|
||||
(class-name . ,(phpinspect--make-type :name "Class"))
|
||||
(methods . ,(list (phpinspect--make-function :name "foobar" :return-type phpinspect--null-type)))))
|
||||
|
||||
(should (= 2 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
(should (phpinspect--class-get-method class (phpinspect-intern-name "other-test")))
|
||||
(should (phpinspect--class-get-method class (phpinspect-intern-name "foobar")))
|
||||
|
||||
(phpinspect--class-set-index
|
||||
class
|
||||
`(phpinspect--indexed-class
|
||||
(complete . t)
|
||||
(class-name . ,(phpinspect--make-type :name "Class"))
|
||||
(methods)))
|
||||
|
||||
(should (= 1 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
(should (phpinspect--class-get-method class (phpinspect-intern-name "other-test")))
|
||||
|
||||
(phpinspect--class-incorporate class other-class)
|
||||
(should (= 1 (length (hash-table-values (phpinspect--class-methods class)))))
|
||||
(should (= 1 (length (hash-table-values (phpinspect--class-subscriptions other-class)))))))
|
||||
|
||||
(ert-deftest phpinspect--class-update-declaration ()
|
||||
(let ((class (phpinspect--make-class-generated
|
||||
:class-retriever (phpinspect-project-make-class-retriever
|
||||
(phpinspect--make-project)))))
|
||||
(phpinspect--class-update-declaration class '(:declaration (:word "class") (:word "TestClass")
|
||||
(:word "extends") (:word "OtherClass")
|
||||
(:word "implements") (:word "ImplClass"))
|
||||
nil "NS")
|
||||
(should (= 2 (length (phpinspect--class-extended-classes class))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\NS\\TestClass" :fully-qualified t)
|
||||
(phpinspect--class-name class)))))
|
@ -1,134 +0,0 @@
|
||||
;;; test-edtrack.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'ert)
|
||||
(require 'phpinspect-edtrack)
|
||||
(require 'phpinspect-meta)
|
||||
|
||||
(ert-deftest phpinspect-edit-end ()
|
||||
(let ((edit (list (cons 10 3) (cons 6 5) (cons 4 -2))))
|
||||
(should (= 13 (phpinspect-edit-end edit)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-register-edit ()
|
||||
(let* ((edtrack (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit edtrack 5 10 10)
|
||||
(phpinspect-edtrack-register-edit edtrack 100 200 150)
|
||||
(phpinspect-edtrack-register-edit edtrack 15 22 7)
|
||||
|
||||
(should (equal `((255 . -50) (27 . 0) (15 . -5)) (phpinspect-edtrack-edits edtrack)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-register-encroaching-edit ()
|
||||
(let* ((edtrack (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit edtrack 5 10 0)
|
||||
(phpinspect-edtrack-register-edit edtrack 100 150 25)
|
||||
|
||||
;; Encroaches on delta of edit before by 15 points ((125 + 25) - 135 = 15),
|
||||
;; so the original end position should be calculated as 135 - (25 - 15) - 5 = 120
|
||||
;; (see also `phpinspect-edtrack-original-position-at-point')
|
||||
(phpinspect-edtrack-register-edit edtrack 135 170 0)
|
||||
|
||||
(should (equal `((120 . 35) (120 . 25) (5 . 5)) (phpinspect-edtrack-edits edtrack)))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-edtrack-orginal-position-at-point ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 20 0)
|
||||
(should (= 10 (phpinspect-edtrack-original-position-at-point track 20)))
|
||||
(should (= 10 (phpinspect-edtrack-original-position-at-point track 15)))
|
||||
(phpinspect-edtrack-register-edit track 30 40 5)
|
||||
(should (= 35 (phpinspect-edtrack-original-position-at-point track 50)))
|
||||
(should (= 25 (phpinspect-edtrack-original-position-at-point track 39)))))
|
||||
|
||||
(ert-deftest phpinsepct-edtrack-register-multi-edits ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 20 5)
|
||||
(phpinspect-edtrack-register-edit track 25 30 0)
|
||||
(phpinspect-edtrack-register-edit track 13 20 0)
|
||||
|
||||
(should (= 42 (phpinspect-edtrack-current-position-at-point track 25)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-register-multi-edits-deletions ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 20 5)
|
||||
(phpinspect-edtrack-register-edit track 25 30 20)
|
||||
(phpinspect-edtrack-register-edit track 13 20 0)
|
||||
|
||||
(should (= 42 (phpinspect-edtrack-current-position-at-point track 45)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-register-taint ()
|
||||
(let* ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-taint track 0 5)
|
||||
(phpinspect-edtrack-register-taint track 10 20)
|
||||
(should (equal (list (cons 0 5) (cons 10 20)) (phpinspect-edtrack-taint-pool track)))
|
||||
|
||||
(phpinspect-edtrack-register-taint track 3 20)
|
||||
|
||||
(should (equal (list (cons 0 20)) (phpinspect-edtrack-taint-pool track)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-taint-iterator ()
|
||||
(let ((track (phpinspect-make-edtrack))
|
||||
(iterator))
|
||||
(phpinspect-edtrack-register-taint track 120 150)
|
||||
(phpinspect-edtrack-register-taint track 5 30)
|
||||
(phpinspect-edtrack-register-taint track 25 50)
|
||||
(phpinspect-edtrack-register-taint track 70 100)
|
||||
|
||||
(setq iterator (phpinspect-edtrack-make-taint-iterator track))
|
||||
|
||||
(should-not (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 1 4 nil nil)))
|
||||
|
||||
(should (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 4 7 nil nil)))
|
||||
|
||||
(should (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 20 30 nil nil)))
|
||||
|
||||
(should-not (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 51 55 nil nil)))
|
||||
|
||||
(should (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 65 73 nil nil)))
|
||||
|
||||
(should (phpinspect-taint-iterator-token-is-tainted-p
|
||||
iterator (phpinspect-make-meta nil 100 130 nil nil)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-edit-derived-taint-iterator ()
|
||||
(let ((track (phpinspect-make-edtrack))
|
||||
iterator)
|
||||
(phpinspect-edtrack-register-edit track 10 20 5)
|
||||
(phpinspect-edtrack-register-edit track 15 30 0)
|
||||
(phpinspect-edtrack-register-edit track 20 25 10)
|
||||
|
||||
(setq iterator (phpinspect-edtrack-make-taint-iterator track))
|
||||
|
||||
(should (phpinspect-taint-iterator-region-is-tainted-p iterator 15 20))
|
||||
(should (phpinspect-taint-iterator-region-is-tainted-p iterator 25 30))
|
||||
(should-not (phpinspect-taint-iterator-region-is-tainted-p iterator 30 35))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-taint-overlapping-edits ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 20 5)
|
||||
|
||||
(should (equal (list (cons 10 15)) (phpinspect-edtrack-taint-pool track)))
|
||||
|
||||
(phpinspect-edtrack-register-edit track 15 0 1)
|
||||
(should (equal (list (cons 10 16)) (phpinspect-edtrack-taint-pool track)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-register-multi-edits-same-start ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 11 0)
|
||||
(phpinspect-edtrack-register-edit track 10 10 1)
|
||||
|
||||
(should (equal (list (cons 10 -1) (cons 10 1)) (phpinspect-edtrack-edits track)))))
|
||||
|
||||
(ert-deftest phpinspect-edtrack-undo ()
|
||||
(let ((track (phpinspect-make-edtrack)))
|
||||
(phpinspect-edtrack-register-edit track 10 10 10)
|
||||
(phpinspect-edtrack-register-edit track 10 10 10)
|
||||
(phpinspect-edtrack-register-edit track 10 30 0)
|
||||
|
||||
(should (= 30 (phpinspect-edtrack-original-position-at-point track 30)))
|
||||
(should (= 20 (phpinspect-edtrack-original-position-at-point track 20)))
|
||||
(should (= 15 (phpinspect-edtrack-original-position-at-point track 15)))
|
||||
(should (= 35 (phpinspect-edtrack-original-position-at-point track 35)))
|
||||
(should (= 10 (phpinspect-edtrack-original-position-at-point track 10)))))
|
@ -1,113 +0,0 @@
|
||||
;;; test-eldoc.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'phpinspect)
|
||||
(require 'phpinspect-eldoc)
|
||||
|
||||
(ert-deftest phpinspect-eld-method-call ()
|
||||
(with-temp-buffer
|
||||
(phpinspect-ensure-worker)
|
||||
(phpinspect-purge-cache)
|
||||
|
||||
(let* ((php-code "
|
||||
class Thing
|
||||
{
|
||||
|
||||
function getThis(\\DateTime $moment, Thing $thing, $other): static
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
function doStuff()
|
||||
{
|
||||
$this->getThis(new \\DateTime(), bla)")
|
||||
(tokens (phpinspect-parse-string php-code))
|
||||
(index (phpinspect--index-tokens tokens))
|
||||
(phpinspect-project-root-function (lambda () "phpinspect-test"))
|
||||
(phpinspect-eldoc-word-width 100)
|
||||
(buffer (phpinspect-make-buffer :buffer (current-buffer)))
|
||||
second-arg-pos inside-nested-list-pos first-arg-pos)
|
||||
(phpinspect-cache-project-class
|
||||
(phpinspect-current-project-root)
|
||||
(cdar (alist-get 'classes (cdr index))))
|
||||
|
||||
(insert php-code)
|
||||
(backward-char)
|
||||
(setq second-arg-pos (point))
|
||||
(backward-char 6)
|
||||
(setq inside-nested-list-pos (point))
|
||||
(backward-char 8)
|
||||
(setq first-arg-pos (point))
|
||||
|
||||
(let ((result (phpinspect-eldoc-query-execute
|
||||
(phpinspect-make-eldoc-query :point second-arg-pos :buffer buffer))))
|
||||
(should (phpinspect-function-doc-p result))
|
||||
(should (= 1 (phpinspect-function-doc-arg-pos result)))
|
||||
(should (string= "getThis" (phpinspect--function-name (phpinspect-function-doc-fn result))))
|
||||
|
||||
(setq result (phpinspect-eldoc-query-execute
|
||||
(phpinspect-make-eldoc-query :point inside-nested-list-pos :buffer buffer)))
|
||||
(should-not result)
|
||||
|
||||
(setq result (phpinspect-eldoc-query-execute
|
||||
(phpinspect-make-eldoc-query :point first-arg-pos :buffer buffer)))
|
||||
(should (phpinspect-function-doc-p result))
|
||||
(should (= 0 (phpinspect-function-doc-arg-pos result)))
|
||||
(should (string= "getThis" (phpinspect--function-name (phpinspect-function-doc-fn result))))))))
|
||||
|
||||
(ert-deftest phpinspect-eldoc-function-for-object-method ()
|
||||
(phpinspect-purge-cache)
|
||||
(let* ((php-code "
|
||||
class Thing
|
||||
{
|
||||
function getThis(\\DateTime $moment, Thing $thing, $other): static
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
function doStuff()
|
||||
{
|
||||
$this->getThis(new \\DateTime(), bla)")
|
||||
(tokens (phpinspect-parse-string php-code))
|
||||
(index (phpinspect--index-tokens tokens))
|
||||
(phpinspect-project-root-function (lambda () "phpinspect-test"))
|
||||
(phpinspect-eldoc-word-width 100))
|
||||
(phpinspect-cache-project-class
|
||||
(phpinspect-current-project-root)
|
||||
(cdar (alist-get 'classes (cdr index))))
|
||||
|
||||
(should (string= "getThis: ($moment DateTime, $thing Thing, $other): Thing"
|
||||
(with-temp-buffer
|
||||
(insert php-code)
|
||||
(backward-char)
|
||||
(setq-local phpinspect-current-buffer
|
||||
(phpinspect-make-buffer :buffer (current-buffer)))
|
||||
(phpinspect-buffer-parse phpinspect-current-buffer)
|
||||
(phpinspect-eldoc-function))))))
|
||||
|
||||
(ert-deftest phpinspect-eldoc-function-for-static-method ()
|
||||
(phpinspect-purge-cache)
|
||||
(let* ((php-code "
|
||||
class Thing
|
||||
{
|
||||
static function doThing(\\DateTime $moment, Thing $thing, $other): static
|
||||
{
|
||||
return $this;
|
||||
}
|
||||
|
||||
function doStuff()
|
||||
{
|
||||
self::doThing(")
|
||||
(tokens (phpinspect-parse-string php-code))
|
||||
(index (phpinspect--index-tokens tokens))
|
||||
(phpinspect-project-root-function (lambda () "phpinspect-test"))
|
||||
(phpinspect-eldoc-word-width 100))
|
||||
(phpinspect-cache-project-class
|
||||
(phpinspect-current-project-root)
|
||||
(cdar (alist-get 'classes (cdr index))))
|
||||
|
||||
(should (string= "doThing: ($moment DateTime, $thing Thing, $other): Thing"
|
||||
(with-temp-buffer
|
||||
(insert php-code)
|
||||
(setq-local phpinspect-current-buffer
|
||||
(phpinspect-make-buffer :buffer (current-buffer)))
|
||||
(phpinspect-eldoc-function))))))
|
@ -1,87 +0,0 @@
|
||||
;;; test-autoload.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-fs)
|
||||
|
||||
(ert-deftest phpinspect-virtual-fs-file-exists-p ()
|
||||
(let ((fs (phpinspect-make-virtual-fs)))
|
||||
(phpinspect-virtual-fs-set-file fs "/test/test.txt" "contents")
|
||||
|
||||
(should (phpinspect-fs-file-exists-p fs "/test/test.txt"))))
|
||||
|
||||
(ert-deftest phpinspect-virtual-fs-insert-file-contents ()
|
||||
(let ((fs (phpinspect-make-virtual-fs)))
|
||||
(phpinspect-virtual-fs-set-file fs "/test/test.txt" "contents")
|
||||
|
||||
(with-temp-buffer
|
||||
(phpinspect-fs-insert-file-contents fs "/test/test.txt")
|
||||
(should (string= "contents" (buffer-string))))
|
||||
|
||||
(with-temp-buffer
|
||||
(phpinspect-fs-insert-file-contents fs "/test/nonexistant")
|
||||
(should (string= "" (buffer-string))))))
|
||||
|
||||
(ert-deftest phpinspect-virtual-fs-directory-files-and-recursively ()
|
||||
(let ((fs (phpinspect-make-virtual-fs)))
|
||||
(puthash "/test/test.txt" "contents" (phpinspect-virtual-fs-files fs))
|
||||
(puthash "/a/b/c/dee.match" "contents" (phpinspect-virtual-fs-files fs))
|
||||
(puthash "/a/b/c/cee.match" "contents" (phpinspect-virtual-fs-files fs))
|
||||
(puthash "/a/b/c/aaa.match" "contents" (phpinspect-virtual-fs-files fs))
|
||||
(puthash "/a/b/c/nope.nomatch" "contents" (phpinspect-virtual-fs-files fs))
|
||||
(puthash "/a/b/d/jee.match" "contents" (phpinspect-virtual-fs-files fs))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files fs "/test/")))
|
||||
(should (equal '("/test/test.txt") files)))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files fs "/a/b/c")))
|
||||
(should (member "/a/b/c/dee.match" files))
|
||||
(should (member "/a/b/c/cee.match" files))
|
||||
(should (member "/a/b/c/aaa.match" files))
|
||||
(should (member "/a/b/c/nope.nomatch" files)))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files fs "/a/b/c" "\\.match$")))
|
||||
(should (member "/a/b/c/dee.match" files))
|
||||
(should (member "/a/b/c/cee.match" files))
|
||||
(should (member "/a/b/c/aaa.match" files))
|
||||
(should (not (member "/a/b/c/nope.nomatch" files))))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files fs "/a/b")))
|
||||
(should (member "/a/b/c" files))
|
||||
(should (member "/a/b/d" files))
|
||||
(should (= 2 (length files))))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files-recursively fs "/a/b")))
|
||||
(should (member "/a/b/c/dee.match" files))
|
||||
(should (member "/a/b/c/cee.match" files))
|
||||
(should (member "/a/b/c/aaa.match" files))
|
||||
(should (member "/a/b/c/nope.nomatch" files))
|
||||
(should (member "/a/b/d/jee.match" files)))
|
||||
|
||||
(let ((files (phpinspect-fs-directory-files-recursively fs "/a/b" "\\.match$")))
|
||||
(should (member "/a/b/c/dee.match" files))
|
||||
(should (member "/a/b/c/cee.match" files))
|
||||
(should (member "/a/b/c/aaa.match" files))
|
||||
(should (not (member "/a/b/c/nope.nomatch" files)))
|
||||
(should (member "/a/b/d/jee.match" files)))))
|
@ -1,250 +0,0 @@
|
||||
;;; test-index.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-parse-context)
|
||||
(require 'phpinspect-bmap)
|
||||
(require 'phpinspect-parser)
|
||||
(require 'phpinspect-test-env
|
||||
(expand-file-name "phpinspect-test-env.el"
|
||||
(file-name-directory (macroexp-file-name))))
|
||||
|
||||
|
||||
(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
|
||||
(complete . t)
|
||||
(class-name . ,(phpinspect--make-type :name "\\Potato" :fully-qualified t))
|
||||
(declaration . (:declaration (:word "class") (:word "Potato")))
|
||||
(location . (0 0))
|
||||
(imports)
|
||||
(methods)
|
||||
(static-methods . (,(phpinspect--make-function
|
||||
:name "staticMethod"
|
||||
:scope '(:public)
|
||||
:token '(:function (:declaration (:word "function")
|
||||
(:word "staticMethod")
|
||||
(:list (:variable "untyped")
|
||||
(:comma)
|
||||
(:word "array")
|
||||
(:variable "things")))
|
||||
(:block))
|
||||
:arguments `(("untyped" nil)
|
||||
("things" ,(phpinspect--make-type :name "\\array"
|
||||
:collection t
|
||||
:fully-qualified t)))
|
||||
:return-type phpinspect--null-type)))
|
||||
(static-variables)
|
||||
(variables)
|
||||
(constants)
|
||||
(extends)
|
||||
(implements)
|
||||
(used-types . (,(phpinspect-intern-name "array")))))
|
||||
(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 extends Cheese, Bacon implements Ham, Bagel {
|
||||
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
|
||||
(copy-sequence
|
||||
'("Cheese" "Bacon" "Ham" "Bagel" "Monkey" "ExtendedThing"
|
||||
"StaticThing" "Thing" "ThingFactory" "Potato" "OtherThing"))
|
||||
#'string<))
|
||||
(sort used-types (lambda (s1 s2) (string< (phpinspect-name-string s1) (phpinspect-name-string s2))))))))
|
||||
|
||||
(ert-deftest phpinspect--find-used-types-in-tokens ()
|
||||
(let ((blocks `(
|
||||
((: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 (sort (copy-sequence (cadr set)) #'string-lessp) (sort result #'string-lessp)))))))
|
||||
|
||||
(ert-deftest phpinspect-index-method-annotations ()
|
||||
(let* ((result (phpinspect--index-tokens
|
||||
(phpinspect-parse-string
|
||||
"<?php
|
||||
|
||||
/* @method int peel(bool $fast, array $loose)
|
||||
* @method Banana duplicate()
|
||||
@method hold() **/
|
||||
class Banana {}")))
|
||||
(class (car (alist-get 'classes result)))
|
||||
(methods (alist-get 'methods class)))
|
||||
(should (= 3 (length methods)))
|
||||
(dolist (method methods)
|
||||
(should (member (phpinspect--function-name method)
|
||||
'("duplicate" "hold" "peel")))
|
||||
|
||||
(cond ((string= (phpinspect--function-name method)
|
||||
"duplicate")
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\Banana" :fully-qualified t)
|
||||
(phpinspect--function-return-type method))))
|
||||
((string= (phpinspect--function-name method)
|
||||
"peel")
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\int" :fully-qualified t)
|
||||
(phpinspect--function-return-type method)))
|
||||
|
||||
(should (= 2 (length (phpinspect--function-arguments method))))
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\array" :fully-qualified t)
|
||||
(car (alist-get
|
||||
"loose" (phpinspect--function-arguments method) nil nil #'string=))))
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\bool" :fully-qualified t)
|
||||
(car (alist-get
|
||||
"fast" (phpinspect--function-arguments method) nil nil #'string=)))))
|
||||
((string= (phpinspect--function-name method)
|
||||
"hold")
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\void" :fully-qualified t)
|
||||
(phpinspect--function-return-type method))))))))
|
||||
|
||||
(ert-deftest phpinspect-index-tokens-class ()
|
||||
(let* ((index1
|
||||
(phpinspect--index-tokens
|
||||
(phpinspect-test-read-fixture-data "IndexClass1")))
|
||||
(index2
|
||||
(phpinspect-test-read-fixture-serialization "IndexClass1-indexed"))
|
||||
(index1-class (car (alist-get 'classes index1)))
|
||||
(index2-class (car (alist-get 'classes index2))))
|
||||
|
||||
(dolist (key '(class-name imports methods static-methods static-variables variables constants extends implements))
|
||||
(should (equal (alist-get key index1-class)
|
||||
(alist-get key index2-class))))))
|
||||
|
||||
(ert-deftest phpinspect-index-bmap-class ()
|
||||
(let* ((pctx (phpinspect-make-pctx :incremental t :bmap (phpinspect-make-bmap)))
|
||||
(tree))
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (expand-file-name "IndexClass1.php" phpinspect-test-php-file-directory))
|
||||
(setf (phpinspect-pctx-bmap pctx) (phpinspect-make-bmap))
|
||||
(phpinspect-with-parse-context pctx (setq tree (phpinspect-parse-current-buffer))))
|
||||
(let* ((index1 (phpinspect--index-tokens tree
|
||||
nil
|
||||
(phpinspect-bmap-make-location-resolver
|
||||
(phpinspect-pctx-bmap pctx))))
|
||||
(index2
|
||||
(phpinspect-test-read-fixture-serialization "IndexClass1-indexed"))
|
||||
(index1-class (car (alist-get 'classes index1)))
|
||||
(index2-class (car (alist-get 'classes index2))))
|
||||
|
||||
(dolist (key '(imports methods static-methods static-variables variables constants extends implements))
|
||||
(should (equal (alist-get key index1-class)
|
||||
(alist-get key index2-class))))
|
||||
|
||||
(should (alist-get 'location index1-class))
|
||||
(should (alist-get 'location index1-class)))))
|
||||
|
||||
(ert-deftest phpinspect-index-functions ()
|
||||
(let* ((code "<?php
|
||||
use Example\\Thing;
|
||||
|
||||
function test_func(): array {}
|
||||
|
||||
function example(): Thing {}")
|
||||
(tokens (phpinspect-parse-string code))
|
||||
(index (phpinspect--index-tokens tokens))
|
||||
functions)
|
||||
|
||||
(should (setq functions (alist-get 'functions index)))
|
||||
(should (= 2 (length functions)))
|
||||
(should (string= "test_func" (phpinspect--function-name (cadr functions))))
|
||||
(should (string= "example" (phpinspect--function-name (car functions))))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(phpinspect--function-return-type (cadr functions))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\Example\\Thing")
|
||||
(phpinspect--function-return-type (car functions))))))
|
||||
|
||||
(ert-deftest phpinspect-index-functions-in-namespace ()
|
||||
(let* ((code "<?php
|
||||
namespace Local;
|
||||
|
||||
use Example\\Thing;
|
||||
|
||||
function test_func(): array {}
|
||||
|
||||
function example(Firewall $wall): Thing {}")
|
||||
(tokens (phpinspect-parse-string code))
|
||||
(index (phpinspect--index-tokens tokens))
|
||||
functions)
|
||||
|
||||
(should (setq functions (alist-get 'functions index)))
|
||||
(should (= 2 (length functions)))
|
||||
(should (string= "Local\\test_func" (phpinspect--function-name (cadr functions))))
|
||||
(should (string= "Local\\example" (phpinspect--function-name (car functions))))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(phpinspect--function-return-type (cadr functions))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\Example\\Thing")
|
||||
(phpinspect--function-return-type (car functions))))
|
||||
(should (= 3 (length (alist-get 'used-types index))))
|
||||
(should (member (phpinspect-intern-name "Firewall") (alist-get 'used-types index)))
|
||||
(should (member (phpinspect-intern-name "array") (alist-get 'used-types index)))
|
||||
(should (member (phpinspect-intern-name "Thing") (alist-get 'used-types index)))
|
||||
|
||||
(should (alist-get 'used-types index))))
|
@ -1,42 +0,0 @@
|
||||
;; test-meta.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-meta)
|
||||
|
||||
(ert-deftest phpinspect-meta-start-relative-to-parent ()
|
||||
(let ((meta (phpinspect-make-meta nil 10 20 "" 'token))
|
||||
(parent1 (phpinspect-make-meta nil 9 22 "" 'token))
|
||||
(parent2 (phpinspect-make-meta nil 0 100 "" 'token)))
|
||||
(phpinspect-meta-set-parent meta parent1)
|
||||
(phpinspect-meta-set-parent parent1 parent2)
|
||||
|
||||
(should (= 10 (phpinspect-meta-start meta)))
|
||||
|
||||
(phpinspect-meta-shift parent2 20)
|
||||
(should (= 30 (phpinspect-meta-start meta)))
|
||||
|
||||
(should (phpinspect-meta-overlaps-point meta 30))))
|
@ -1,53 +0,0 @@
|
||||
;;; phpinspect-test.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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-parse-context)
|
||||
(require 'phpinspect-meta)
|
||||
(require 'phpinspect-bmap)
|
||||
|
||||
(ert-deftest phpinspect-pctx-cancel ()
|
||||
(let ((meta (phpinspect-make-meta nil 10 20 " " 'token 'overlay nil))
|
||||
(pctx (phpinspect-make-pctx :bmap (phpinspect-make-bmap))))
|
||||
(phpinspect-with-parse-context pctx
|
||||
(phpinspect-meta-with-changeset meta
|
||||
(setf (phpinspect-meta-absolute-start meta) 222)
|
||||
(setf (phpinspect-meta-absolute-end meta) 1234)
|
||||
(phpinspect-meta-set-parent meta (phpinspect-make-meta nil 1 2000 "" 'parent-token))
|
||||
(setf (phpinspect-meta-overlay meta) 'not-overlay)))
|
||||
|
||||
(should (= 222 (phpinspect-meta-start meta)))
|
||||
(should (= 1234 (phpinspect-meta-end meta)))
|
||||
(should (phpinspect-meta-parent meta))
|
||||
(should (eq 'not-overlay (phpinspect-meta-overlay meta)))
|
||||
(should (= 221 (phpinspect-meta-parent-offset meta)))
|
||||
|
||||
(phpinspect-pctx-cancel pctx)
|
||||
|
||||
(should (= 10 (phpinspect-meta-start meta)))
|
||||
(should (= 20 (phpinspect-meta-end meta)))
|
||||
(should-not (phpinspect-meta-parent meta))
|
||||
(should-not (phpinspect-meta-parent-offset meta))
|
||||
(should (eq 'overlay (phpinspect-meta-overlay meta)))))
|
@ -1,146 +0,0 @@
|
||||
;; test-parser.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-parser)
|
||||
(require 'phpinspect-index)
|
||||
(require 'phpinspect-test-env
|
||||
(expand-file-name "phpinspect-test-env.el"
|
||||
(file-name-directory (macroexp-file-name))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-parse-bmap ()
|
||||
(let* ((ctx (phpinspect-make-pctx :incremental t :bmap (phpinspect-make-bmap)))
|
||||
(code "
|
||||
class TestClass {
|
||||
public function getCurrentStatisticAction(): JsonResponse
|
||||
{
|
||||
$statistic = $this->repository->getCurrentStatistic();
|
||||
if (!$this->authorization->isGranted(EntityAction::VIEW, $statistic)) {
|
||||
return $this->responder->respondUnauthorized();
|
||||
}
|
||||
|
||||
return $this->responder->respond($statistic);
|
||||
}
|
||||
}")
|
||||
(bmap))
|
||||
(phpinspect-with-parse-context ctx
|
||||
(phpinspect-parse-string code))
|
||||
(setq bmap (phpinspect-pctx-bmap ctx))
|
||||
|
||||
(let ((enclosing (phpinspect-bmap-tokens-overlapping bmap 350))
|
||||
(parent))
|
||||
(should enclosing)
|
||||
(should (phpinspect-variable-p (phpinspect-meta-token (car enclosing))))
|
||||
(should (string= "statistic" (cadr (phpinspect-meta-token (car enclosing)))))
|
||||
(should (phpinspect-meta-parent (car enclosing)))
|
||||
|
||||
(setq parent (phpinspect-meta-parent (car enclosing)))
|
||||
(should (phpinspect-list-p (phpinspect-meta-token parent)))
|
||||
(should (phpinspect-block-p (phpinspect-meta-token (phpinspect-meta-parent parent)))))))
|
||||
|
||||
(ert-deftest phpinspect-parse-comma ()
|
||||
(let* ((code "(,)")
|
||||
(ctx (phpinspect-make-pctx :incremental t :bmap (phpinspect-make-bmap)))
|
||||
(parsed (phpinspect-with-parse-context ctx
|
||||
(phpinspect-parse-string code)))
|
||||
(comma (cadadr parsed))
|
||||
(map (phpinspect-pctx-bmap ctx))
|
||||
(comma-meta))
|
||||
(should (equal '(:root (:list (:comma ","))) parsed))
|
||||
(should (equal '(:comma ",") comma))
|
||||
|
||||
(should (setq comma-meta (phpinspect-bmap-token-meta map comma)))
|
||||
(should (= 1 (phpinspect-meta-width comma-meta)))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-parse-namespaced-class ()
|
||||
"Test phpinspect-parse on a namespaced class"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "NamespacedClass")
|
||||
(phpinspect-test-parse-fixture-code "NamespacedClass"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-block ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "Block")
|
||||
(phpinspect-test-parse-fixture-code "Block"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-functions ()
|
||||
"Test phpinspect-parse for php functions"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "Functions")
|
||||
(phpinspect-test-parse-fixture-code "Functions"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-namespaced-functions ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "NamespacedFunctions")
|
||||
(phpinspect-test-parse-fixture-code "NamespacedFunctions"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-variable ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "Variable")
|
||||
(phpinspect-test-parse-fixture-code "Variable"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-word ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "Word")
|
||||
(phpinspect-test-parse-fixture-code "Word"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-array ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "Array")
|
||||
(phpinspect-test-parse-fixture-code "Array"))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-parse-short-function ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "ShortFunction")
|
||||
(phpinspect-test-parse-fixture-code "ShortFunction"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-two-short-functions ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "TwoShortFunctions")
|
||||
(phpinspect-test-parse-fixture-code "TwoShortFunctions"))))
|
||||
|
||||
(ert-deftest phpinspect-parse-small-namespaced-class ()
|
||||
"Test phpinspect-parse for php blocks"
|
||||
(should
|
||||
(equal (phpinspect-test-read-fixture-data "SmallNamespacedClass")
|
||||
(phpinspect-test-parse-fixture-code "SmallNamespacedClass"))))
|
||||
|
||||
;; If this test fails, the syntax tree has a breaking change in it. Regenerate the
|
||||
;; fixtures and fix anything that is broken.
|
||||
(ert-deftest phpinspect-syntax-tree-change ()
|
||||
(let ((index (phpinspect--index-tokens
|
||||
(phpinspect-test-parse-fixture-code "IndexClass1")))
|
||||
(expected-result (phpinspect--index-tokens
|
||||
(phpinspect-test-read-fixture-data "IndexClass1"))))
|
||||
(should (equal index expected-result))))
|
@ -1,66 +0,0 @@
|
||||
;;; test-pipeline.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-pipeline)
|
||||
|
||||
(defun phpinspect--correct-the-record (input)
|
||||
(phpinspect-pipeline-emit
|
||||
(format "It's not %s, but GNU/%s" input input)))
|
||||
|
||||
(ert-deftest phpinspect-pipeline ()
|
||||
(let (result error)
|
||||
|
||||
(phpinspect-pipeline (list "Linux" "Emacs")
|
||||
:into phpinspect--correct-the-record
|
||||
:async (lambda (res err)
|
||||
(setq result res
|
||||
error err)))
|
||||
|
||||
(while (not (or result error))
|
||||
(thread-yield))
|
||||
|
||||
(should (equal '("It's not Linux, but GNU/Linux" "It's not Emacs, but GNU/Emacs")
|
||||
result))
|
||||
(should-not error)))
|
||||
|
||||
(defun phpinspect--aah-it-broke (input)
|
||||
(signal 'it-brokey input))
|
||||
|
||||
(ert-deftest phpinspect-pipeline-error ()
|
||||
|
||||
(let (result error)
|
||||
(phpinspect-pipeline (list "Holy smokey")
|
||||
:into phpinspect--aah-it-broke
|
||||
:async (lambda (res err)
|
||||
(setq result res
|
||||
error err)))
|
||||
|
||||
(while (not (or result error))
|
||||
(thread-yield))
|
||||
|
||||
(should error)
|
||||
(should (equal '(phpinspect-pipeline-error
|
||||
"Thread phpinspect-pipeline-phpinspect--aah-it-broke signaled error: (it-brokey . Holy smokey)")
|
||||
error))))
|
@ -1,44 +0,0 @@
|
||||
;; test-project.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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))
|
||||
(watch-file (concat root "/watch1"))
|
||||
(project (phpinspect--make-project :fs fs :root root)))
|
||||
(phpinspect-project-watch-file project watch-file #'ignore)
|
||||
|
||||
(phpinspect-project-purge project)
|
||||
|
||||
(should (= 0 (length (hash-table-values (phpinspect-project-file-watchers project)))))))
|
@ -1,112 +0,0 @@
|
||||
;; -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'phpinspect-resolvecontext)
|
||||
(require 'phpinspect)
|
||||
(require 'phpinspect-test-env
|
||||
(expand-file-name "phpinspect-test-env.el"
|
||||
(file-name-directory (macroexp-file-name))))
|
||||
|
||||
|
||||
(ert-deftest phinspect-get-resolvecontext ()
|
||||
(let* ((ctx (phpinspect-make-pctx :incremental t :bmap (phpinspect-make-bmap)))
|
||||
(code "
|
||||
class TestClass {
|
||||
public function getCurrentStatisticAction(): JsonResponse
|
||||
{
|
||||
$statistic = $this->repository->getCurrentStatistic();
|
||||
if (!$this->authorization->isGranted(EntityAction::VIEW, $statistic)) {
|
||||
return $this->responder->respondUnauthorized();
|
||||
}
|
||||
|
||||
$this->
|
||||
|
||||
return $this->responder->respond($statistic);
|
||||
}
|
||||
}")
|
||||
(bmap))
|
||||
(phpinspect-with-parse-context ctx
|
||||
(phpinspect-parse-string code))
|
||||
(setq bmap (phpinspect-pctx-bmap ctx))
|
||||
|
||||
(let ((rctx (phpinspect-get-resolvecontext bmap 317)))
|
||||
(should (phpinspect--resolvecontext-subject rctx))
|
||||
(should (phpinspect--resolvecontext-enclosing-tokens rctx)))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-type-resolver-for-resolvecontext ()
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (expand-file-name "IncompleteClass.php" phpinspect-test-php-file-directory))
|
||||
(let* ((bmap (phpinspect-parse-string-to-bmap (buffer-string)))
|
||||
(resolvecontext (phpinspect-get-resolvecontext bmap (point-max)))
|
||||
(type-resolver (phpinspect--make-type-resolver-for-resolvecontext
|
||||
resolvecontext)))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "\\array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type
|
||||
:name "\\Symfony\\Component\\HttpFoundation\\Response")
|
||||
(funcall type-resolver (phpinspect--make-type :name "Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\Response")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "\\Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\App\\Controller\\GastonLagaffe")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "GastonLagaffe"))))
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--make-type :name "\\App\\Controller\\Dupuis\\GastonLagaffe")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "Dupuis\\GastonLagaffe")))))))
|
||||
|
||||
(ert-deftest phpinspect-type-resolver-for-resolvecontext-namespace-block ()
|
||||
(with-temp-buffer
|
||||
(insert-file-contents (concat phpinspect-test-php-file-directory "/IncompleteClassBlockedNamespace.php"))
|
||||
(let* ((bmap (phpinspect-parse-string-to-bmap (buffer-string)))
|
||||
(resolvecontext (phpinspect-get-resolvecontext bmap (point-max)))
|
||||
(type-resolver (phpinspect--make-type-resolver-for-resolvecontext
|
||||
resolvecontext)))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver (phpinspect--make-type :name "array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver (phpinspect--make-type :name "\\array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type
|
||||
:name "\\Symfony\\Component\\HttpFoundation\\Response")
|
||||
(funcall type-resolver (phpinspect--make-type :name "Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\Response")
|
||||
(funcall type-resolver (phpinspect--make-type :name "\\Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\App\\Controller\\GastonLagaffe")
|
||||
(funcall type-resolver (phpinspect--make-type
|
||||
:name "GastonLagaffe"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type
|
||||
:name "\\App\\Controller\\Dupuis\\GastonLagaffe")
|
||||
(funcall type-resolver (phpinspect--make-type :name "Dupuis\\GastonLagaffe")))))))
|
||||
|
||||
(ert-deftest phpinspect-type-resolver-for-resolvecontext-multiple-namespace-blocks ()
|
||||
(let* ((resolvecontext (phpinspect--get-resolvecontext
|
||||
(phpinspect-test-read-fixture-data
|
||||
"IncompleteClassMultipleNamespaces")))
|
||||
(type-resolver (phpinspect--make-type-resolver-for-resolvecontext
|
||||
resolvecontext)))
|
||||
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\array")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "\\array"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type
|
||||
:name "\\Symfony\\Component\\HttpFoundation\\Response")
|
||||
(funcall type-resolver (phpinspect--make-type :name "Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\Response")
|
||||
(funcall type-resolver
|
||||
(phpinspect--make-type :name "\\Response"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type :name "\\App\\Controller\\GastonLagaffe")
|
||||
(funcall type-resolver (phpinspect--make-type :name "GastonLagaffe"))))
|
||||
(should (phpinspect--type= (phpinspect--make-type
|
||||
:name "\\App\\Controller\\Dupuis\\GastonLagaffe")
|
||||
(funcall type-resolver (phpinspect--make-type
|
||||
:name "Dupuis\\GastonLagaffe"))))))
|
@ -1,186 +0,0 @@
|
||||
;; test-splayt.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-splayt)
|
||||
|
||||
(ert-deftest phpinspect-splayt-node-rotate ()
|
||||
(let* ((one (phpinspect-make-splayt-node 1 "one"))
|
||||
(four (phpinspect-make-splayt-node 4 "four"))
|
||||
(three (phpinspect-make-splayt-node
|
||||
3 "three"
|
||||
one
|
||||
four)))
|
||||
(setf (phpinspect-splayt-node-parent four) three)
|
||||
(setf (phpinspect-splayt-node-parent one) three)
|
||||
(phpinspect-splayt-node-rotate-right three)
|
||||
|
||||
(should (eq one (phpinspect-splayt-node-parent three)))
|
||||
(should (eq three (phpinspect-splayt-node-parent four)))
|
||||
(should (eq three (phpinspect-splayt-node-right one)))
|
||||
(should (eq four (phpinspect-splayt-node-right three)))
|
||||
(should-not (phpinspect-splayt-node-left one))
|
||||
(should-not (phpinspect-splayt-node-left four))
|
||||
(should-not (phpinspect-splayt-node-left three))
|
||||
|
||||
(phpinspect-splayt-node-rotate-left one)
|
||||
|
||||
(should (eq one (phpinspect-splayt-node-left three)))
|
||||
(should (eq three (phpinspect-splayt-node-parent four)))
|
||||
(should (eq three (phpinspect-splayt-node-parent one)))
|
||||
(should (eq four (phpinspect-splayt-node-right three)))
|
||||
(should (eq one (phpinspect-splayt-node-left three)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
|
||||
(should (string= "eight" (phpinspect-splayt-find tree 8)))
|
||||
(should (string= "one" (phpinspect-splayt-find tree 1)))
|
||||
(should (string= "three" (phpinspect-splayt-find tree 3)))
|
||||
(should (string= "nine" (phpinspect-splayt-find tree 9)))
|
||||
(should (string= "four" (phpinspect-splayt-find tree 4)))
|
||||
(should (string= "twelve" (phpinspect-splayt-find tree 12)))
|
||||
(should (string= "eleven" (phpinspect-splayt-find tree 11)))
|
||||
|
||||
(let ((expected (sort (copy-sequence '("nine" "three" "eleven" "eight" "twelve" "four" "one")) #'string-lessp))
|
||||
(result))
|
||||
|
||||
(phpinspect-splayt-traverse (item tree)
|
||||
(push item result))
|
||||
|
||||
(setq result (sort result #'string-lessp))
|
||||
|
||||
(should (equal expected result)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt-traverse ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
(let ((expected (sort (copy-sequence '("nine" "three" "eleven" "eight" "twelve" "four" "one")) #'string-lessp))
|
||||
(result))
|
||||
|
||||
(phpinspect-splayt-traverse (item tree)
|
||||
(push item result))
|
||||
|
||||
(setq result (sort result #'string-lessp))
|
||||
|
||||
(should (equal expected result)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt-traverse-lr ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
(let ((expected '("one" "three" "four" "eight" "nine" "eleven" "twelve"))
|
||||
result)
|
||||
(phpinspect-splayt-traverse-lr (item tree)
|
||||
(setq result (nconc result (list item))))
|
||||
|
||||
(should (equal expected result)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt-find-smallest-after ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
|
||||
(should (string= "nine" (phpinspect-splayt-find-smallest-after tree 8)))
|
||||
(should (string= "three" (phpinspect-splayt-find-smallest-after tree 1)))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-splayt-find-largest-before ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
|
||||
(should (string= "four" (phpinspect-splayt-find-largest-before tree 8)))
|
||||
(should (string= "eleven" (phpinspect-splayt-find-largest-before tree 12)))
|
||||
(should (string= "one" (phpinspect-splayt-find-largest-before tree 2)))
|
||||
(should (string= "twelve" (phpinspect-splayt-find-largest-before tree 13)))))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-splayt-find-all-after ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
|
||||
(should (equal (sort (copy-sequence '("eight" "nine" "eleven" "twelve")) #'string-lessp)
|
||||
(sort (phpinspect-splayt-find-all-after tree 7) #'string-lessp)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt-to-list ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
(phpinspect-splayt-insert tree 2 "two")
|
||||
|
||||
|
||||
(should (equal '("one" "two" "three") (phpinspect-splayt-to-list tree)))))
|
||||
|
||||
(ert-deftest phpinspect-splayt-find-all-between ()
|
||||
(let ((tree (phpinspect-make-splayt)))
|
||||
(phpinspect-splayt-insert tree 9 "nine")
|
||||
(phpinspect-splayt-insert tree 3 "three")
|
||||
(phpinspect-splayt-insert tree 11 "eleven")
|
||||
(phpinspect-splayt-insert tree 8 "eight")
|
||||
(phpinspect-splayt-insert tree 12 "twelve")
|
||||
(phpinspect-splayt-insert tree 4 "four")
|
||||
(phpinspect-splayt-insert tree 1 "one")
|
||||
|
||||
(should (equal '("three" "four") (phpinspect-splayt-find-all-between tree 1 5)))))
|
@ -1,57 +0,0 @@
|
||||
;;; test-edtrack.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
(require 'phpinspect-toc)
|
||||
(require 'phpinspect-splayt)
|
||||
(require 'phpinspect-meta)
|
||||
|
||||
(ert-deftest phpinspect-make-toc ()
|
||||
(let ((tokens (phpinspect-make-splayt))
|
||||
toc)
|
||||
(phpinspect-splayt-insert tokens 1 (phpinspect-make-meta nil 1 20 "" 'token1))
|
||||
(phpinspect-splayt-insert tokens 40 (phpinspect-make-meta nil 40 45 "" 'token2))
|
||||
(phpinspect-splayt-insert tokens 55 (phpinspect-make-meta nil 55 70 "" 'token3))
|
||||
|
||||
(setq toc (phpinspect-make-toc tokens))
|
||||
|
||||
(should (= 3 (hash-table-count (phpinspect-toc-table toc))))
|
||||
(should (= 3 (length (phpinspect-splayt-to-list (phpinspect-toc-tree toc)))))))
|
||||
|
||||
(ert-deftest phpinspect-update-toc ()
|
||||
(let ((tokens (phpinspect-make-splayt))
|
||||
(root (phpinspect-make-meta nil 1 200 "" 'root))
|
||||
(new-root (phpinspect-make-meta nil 1 400 "" 'root))
|
||||
(tok1 (phpinspect-make-meta nil 1 20 "" 'token1))
|
||||
(tok2 (phpinspect-make-meta nil 40 45 "" 'token2))
|
||||
(tok3 (phpinspect-make-meta nil 55 70 "" 'token3))
|
||||
(tok4 (phpinspect-make-meta nil 71 91 "" 'token4))
|
||||
new-tokens toc)
|
||||
|
||||
(phpinspect-meta-set-parent tok1 root)
|
||||
(phpinspect-meta-set-parent tok2 root)
|
||||
(phpinspect-meta-set-parent tok3 root)
|
||||
|
||||
(phpinspect-splayt-insert tokens 1 tok1)
|
||||
(phpinspect-splayt-insert tokens 40 tok2)
|
||||
(phpinspect-splayt-insert tokens 55 tok3)
|
||||
|
||||
(setq toc (phpinspect-make-toc tokens))
|
||||
|
||||
(phpinspect-meta-set-parent tok2 new-root)
|
||||
(phpinspect-meta-set-parent tok3 new-root)
|
||||
(phpinspect-meta-set-parent tok4 new-root)
|
||||
|
||||
(setq new-tokens (phpinspect-make-splayt))
|
||||
(phpinspect-splayt-insert new-tokens 71 tok4)
|
||||
|
||||
(pcase-let ((`(,result-new ,result-deleted) (phpinspect-toc-update toc new-tokens new-root)))
|
||||
(should (= 1 (length result-new)))
|
||||
(should (= 1 (length result-deleted)))
|
||||
|
||||
(should (eq tok1 (car result-deleted)))
|
||||
(should (eq tok4 (car result-new))))
|
||||
|
||||
(should (equal '(token2 token3)
|
||||
(mapcar #'phpinspect-meta-token (phpinspect-toc-tokens-in-region toc 0 70))))
|
||||
|
||||
(should (equal '(token2 token3 token4)
|
||||
(mapcar #'phpinspect-meta-token (phpinspect-toc-tokens-in-region toc 0 91))))))
|
@ -1,38 +0,0 @@
|
||||
;; test-type.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-type)
|
||||
|
||||
(ert-deftest phpinspect--resolve-late-static-binding ()
|
||||
(let* ((sets '(("\\bool" . "\\bool")
|
||||
("\\static" . "\\AType")
|
||||
("\\this" . "\\AType"))))
|
||||
(dolist (set sets)
|
||||
(should (phpinspect--type=
|
||||
(phpinspect--resolve-late-static-binding
|
||||
(phpinspect--make-type :name (car set))
|
||||
(phpinspect--make-type :name "\\AType"))
|
||||
|
||||
(phpinspect--make-type :name (cdr set)))))))
|
@ -1,57 +0,0 @@
|
||||
;;; test-util.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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 'phpinspect-util)
|
||||
|
||||
(ert-deftest phpinspect--pattern ()
|
||||
(let* ((a "a")
|
||||
(pattern1 (phpinspect--make-pattern :m `(,a) :m * :m "b"))
|
||||
(pattern2 (phpinspect--make-pattern :f #'listp :m * :m "b")))
|
||||
|
||||
(should (equal '(:m ("a") :m * :m "b") (phpinspect--pattern-code pattern1)))
|
||||
(should (equal '(:f listp :m * :m "b") (phpinspect--pattern-code pattern2)))
|
||||
|
||||
(dolist (pattern `(,pattern1 ,pattern2))
|
||||
(should (phpinspect--pattern-match pattern '(("a") "c" "b")))
|
||||
(should (equal '(("a") "c" "b") (phpinspect--pattern-match pattern '(("a") "c" "b"))))
|
||||
(should (phpinspect--pattern-match pattern '(("a") nil "b")))
|
||||
(should-not (phpinspect--pattern-match pattern '(1 nil "b")))
|
||||
(should-not (phpinspect--pattern-match pattern '(("a") nil "b" "c"))))))
|
||||
|
||||
(ert-deftest phpinspect--pattern-concat ()
|
||||
(let* ((pattern1 (phpinspect--make-pattern :m "a" :m * :m "b"))
|
||||
(pattern2 (phpinspect--make-pattern :f #'stringp :m * :m "D"))
|
||||
(result (phpinspect--pattern-concat pattern1 pattern2)))
|
||||
|
||||
(should (equal '(:m "a" :m * :m "b" :f stringp :m * :m "D") (phpinspect--pattern-code result)))
|
||||
|
||||
(should (phpinspect--pattern-match result '("a" "anything" "b" "astring" nil "D")))))
|
||||
|
||||
(ert-deftest phpinspect--pattern-match-partially ()
|
||||
(let ((result (phpinspect--match-sequence '((:variable "this") (:object-attrib (:word "em")))
|
||||
:m '(:variable "this")
|
||||
:m '(:object-attrib (:word "not-a-match")))))
|
||||
|
||||
(should-not result)))
|
@ -1,93 +0,0 @@
|
||||
;;; test-worker.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*-
|
||||
|
||||
;; Copyright (C) 2021-2023 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)
|
||||
(require 'phpinspect-worker)
|
||||
|
||||
(ert-deftest phpinspect-queue-enqueue ()
|
||||
(let ((queue (phpinspect-make-queue)))
|
||||
(phpinspect-queue-enqueue queue "one")
|
||||
(phpinspect-queue-enqueue queue "two")
|
||||
(phpinspect-queue-enqueue queue "three")
|
||||
|
||||
(should (string= "one" (phpinspect-queue-dequeue queue)))
|
||||
(should (string= "two" (phpinspect-queue-dequeue queue)))
|
||||
(should (string= "three" (phpinspect-queue-dequeue queue)))
|
||||
(should-not (phpinspect-queue-dequeue queue))))
|
||||
|
||||
(ert-deftest phpinspect-queue-subscribe ()
|
||||
(let ((be-called nil))
|
||||
(let ((queue (phpinspect-make-queue (lambda () (setq be-called t)))))
|
||||
(phpinspect-queue-enqueue queue "one"))
|
||||
(should be-called)))
|
||||
|
||||
(ert-deftest phpinspect-queue-find ()
|
||||
(let ((queue (phpinspect-make-queue)))
|
||||
(phpinspect-queue-enqueue queue "one")
|
||||
(phpinspect-queue-enqueue queue "two")
|
||||
(phpinspect-queue-enqueue queue "three")
|
||||
|
||||
(should (string= "one" (phpinspect-queue-find queue "one" 'string=)))
|
||||
(should (string= "two" (phpinspect-queue-find queue "two" 'string=)))
|
||||
(should (string= "three" (phpinspect-queue-find queue "three" 'string=)))))
|
||||
|
||||
(ert-deftest phpinspect-doqueue ()
|
||||
;; Iterate over a populated queue
|
||||
(let ((queue (phpinspect-make-queue)))
|
||||
(phpinspect-queue-enqueue queue "one")
|
||||
(phpinspect-queue-enqueue queue "two")
|
||||
(phpinspect-queue-enqueue queue "three")
|
||||
(phpinspect-queue-enqueue queue "four")
|
||||
|
||||
(let ((expected-things '("one" "two" "three" "four"))
|
||||
(things))
|
||||
(phpinspect-doqueue (thing queue)
|
||||
(push thing things))
|
||||
|
||||
(should (equal expected-things (nreverse things)))))
|
||||
|
||||
;; attempt to iterate over an empty queue
|
||||
(let ((have-iterated nil))
|
||||
(phpinspect-doqueue (_thing (phpinspect-make-queue))
|
||||
(setq have-iterated t))
|
||||
|
||||
(should-not have-iterated)))
|
||||
|
||||
|
||||
(ert-deftest phpinspect-queue-enqueue-noduplicate ()
|
||||
(let ((queue (phpinspect-make-queue))
|
||||
(expected-things '("one" "two"))
|
||||
(things))
|
||||
|
||||
(phpinspect-queue-enqueue-noduplicate queue "one" 'string=)
|
||||
(phpinspect-queue-enqueue-noduplicate queue "two" 'string=)
|
||||
(phpinspect-queue-enqueue-noduplicate queue "two" 'string=)
|
||||
(phpinspect-queue-enqueue-noduplicate queue "one" 'string=)
|
||||
|
||||
(phpinspect-doqueue (thing queue)
|
||||
(push thing things))
|
||||
|
||||
(should (equal expected-things (nreverse things)))))
|
Loading…
Reference in New Issue