Compare commits
104 Commits
WIP-increm
...
master
@ -1 +1,9 @@
|
||||
*.elc
|
||||
*.elc
|
||||
/benchmarks/profile.txt
|
||||
/.deps
|
||||
/data
|
||||
|
||||
|
||||
# ELPA-generated files
|
||||
/phpinspect-autoloads.el
|
||||
/phpinspect-pkg.el
|
||||
|
@ -0,0 +1,52 @@
|
||||
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
@ -0,0 +1,53 @@
|
||||
;;; 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)))
|
@ -0,0 +1,137 @@
|
||||
;;; 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)))
|
@ -0,0 +1,103 @@
|
||||
;;; 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)))
|
@ -0,0 +1,51 @@
|
||||
;;; 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)))
|
@ -0,0 +1,288 @@
|
||||
;;; 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
|
@ -0,0 +1,70 @@
|
||||
;;; 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
|
@ -0,0 +1,78 @@
|
||||
;;; 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)
|
@ -0,0 +1,319 @@
|
||||
;;; 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)
|
@ -0,0 +1,235 @@
|
||||
;;; 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
|
@ -0,0 +1,283 @@
|
||||
;;; 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)
|
@ -0,0 +1,242 @@
|
||||
;;; 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
|
@ -0,0 +1,138 @@
|
||||
;;; 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
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,394 @@
|
||||
;;; 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
|
@ -0,0 +1,95 @@
|
||||
;;; 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)
|
@ -0,0 +1,129 @@
|
||||
;;; 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
|
@ -0,0 +1,500 @@
|
||||
;;; 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)
|
@ -0,0 +1,231 @@
|
||||
;;; 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
|
@ -0,0 +1,499 @@
|
||||
;;; 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)
|
@ -0,0 +1,144 @@
|
||||
;;; 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)
|
@ -0,0 +1,83 @@
|
||||
;;; 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)
|
@ -0,0 +1,254 @@
|
||||
;;; 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)
|
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,101 @@
|
||||
<?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));
|
||||
}
|
@ -0,0 +1,35 @@
|
||||
;;; 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)))))
|
@ -0,0 +1,20 @@
|
||||
#!/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 (: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)))))))))
|
||||
(: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)))))))))
|
@ -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 ";")) (: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)))))))))
|
||||
(: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))))))))))
|
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 (: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 ";")))) (: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 (: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 ";"))))))))
|
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 (: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 ";"))))))))
|
||||
(: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 ";"))))))))
|
File diff suppressed because one or more lines are too long
@ -0,0 +1,36 @@
|
||||
;;; 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)
|
@ -0,0 +1,91 @@
|
||||
;; -*- 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))))
|
@ -0,0 +1,134 @@
|
||||
;;; 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)))))
|
@ -0,0 +1,113 @@
|
||||
;;; 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))))))
|
@ -0,0 +1,42 @@
|
||||
;; 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))))
|
@ -0,0 +1,53 @@
|
||||
;;; 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)))))
|
@ -0,0 +1,146 @@
|
||||
;; 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))))
|
@ -0,0 +1,66 @@
|
||||
;;; 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))))
|
@ -0,0 +1,112 @@
|
||||
;; -*- 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"))))))
|
@ -0,0 +1,186 @@
|
||||
;; 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)))))
|
@ -0,0 +1,57 @@
|
||||
;;; 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))))))
|
Loading…
Reference in New Issue