branch: externals/phpinspect commit b68baaec8310eddad1f1e28244758a69032ca310 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Make parse context cancellable and restore state after interrupt --- phpinspect-bmap.el | 33 ++++---- phpinspect-meta.el | 78 +++++++++++++++++-- phpinspect-parse-context.el | 182 ++++++++++++++++++++++++++++++++++++++++++++ phpinspect-parser.el | 87 ++++----------------- phpinspect.el | 15 ---- test/phpinspect-test.el | 73 +----------------- test/test-parse-context.el | 52 +++++++++++++ test/test-parser.el | 125 ++++++++++++++++++++++++++++++ 8 files changed, 465 insertions(+), 180 deletions(-) diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el index 44494ca366..8f87c29217 100644 --- a/phpinspect-bmap.el +++ b/phpinspect-bmap.el @@ -255,22 +255,23 @@ giving up. If not provided, this is 100." (overlay) (last-overlay (phpinspect-splayt-node-value (phpinspect-splayt-root-node overlays)))) - (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))) + (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) diff --git a/phpinspect-meta.el b/phpinspect-meta.el index 03ce5767b7..f1c877a432 100644 --- a/phpinspect-meta.el +++ b/phpinspect-meta.el @@ -105,8 +105,10 @@ (inline-quote (when (phpinspect-meta-parent ,meta) ;; Update absolute start and end - (setf (phpinspect-meta-absolute-start ,meta) (phpinspect-meta-start ,meta)) (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) @@ -114,16 +116,76 @@ (setf (phpinspect-meta-absolute-end meta) (+ (phpinspect-meta-end meta) delta))) (defun phpinspect-meta-right-siblings (meta) - (mapcar #'phpinspect-meta-token - (sort - (phpinspect-splayt-find-all-after - (phpinspect-meta-children (phpinspect-meta-parent meta)) (phpinspect-meta-parent-offset meta)) - #'phpinspect-meta-sort-start))) + (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-overlapping-child ((meta (head meta)) (point integer)) + (let ((child (phpinspect-splayt-find-largest-before + (phpinspect-meta-children meta) (phpinspect-meta--point-offset meta point)))) + (when (and child (phpinspect-meta-overlaps-point 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-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)) (defun phpinspect-meta-string (meta) - (format "[start: %d, end: %d, token: %s]" - (phpinspect-meta-start meta) (phpinspect-meta-end meta) (phpinspect-meta-token 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) diff --git a/phpinspect-parse-context.el b/phpinspect-parse-context.el new file mode 100644 index 0000000000..ded203bb0d --- /dev/null +++ b/phpinspect-parse-context.el @@ -0,0 +1,182 @@ +;;; phpinspect-parse-context.el --- PHP parsing context module -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Free Software Foundation, Inc + +;; Author: Hugo Thunnissen <de...@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-bmap) + +(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) + (interrupt-threshold (time-convert '(2 . 1000)) + :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 (phpinspect-make-bmap) + :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-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))) + + ;; 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-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))))) + +(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)) + +(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-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-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)))))) + +(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 diff --git a/phpinspect-parser.el b/phpinspect-parser.el index 4d75d06a7e..79a61c2141 100644 --- a/phpinspect-parser.el +++ b/phpinspect-parser.el @@ -26,6 +26,7 @@ ;;(require 'phpinspect-tree) (require 'phpinspect-edtrack) (require 'phpinspect-bmap) +(require 'phpinspect-parse-context) (defvar phpinspect-parser-obarray (obarray-make) "An obarray containing symbols for all phpinspect (sub)parsers.") @@ -37,68 +38,6 @@ (define-inline phpinspect--word-end-regex () (inline-quote "\\([[:blank:]]\\|[^0-9a-zA-Z_]\\)"))) -(defvar phpinspect-parse-context nil - "An instance of `phpinspect-pctx' that is used when -parsing. Usually used in combination with -`phpinspect-with-parse-context'") - -(defmacro phpinspect-with-parse-context (ctx &rest body) - (declare (indent 1)) - (let ((old-ctx phpinspect-parse-context)) - `(unwind-protect - (progn - (setq phpinspect-parse-context ,ctx) - ,@body) - (setq phpinspect-parse-context ,old-ctx)))) - -(cl-defstruct (phpinspect-pctx (:constructor phpinspect-make-pctx)) - "Parser Context" - (incremental nil) - (interrupt-threshold (time-convert '(2 . 1000)) - :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.") - (edtrack nil - :type phpinspect-edtrack) - (bmap (phpinspect-make-bmap) - :type phpinspect-bmap) - (previous-bmap nil - :type phpinspect-bmap) - (whitespace-before "" - :type string)) - -(defsubst phpinspect-pctx-check-interrupt (pctx) - (unless (phpinspect-pctx--start-time pctx) - (setf (phpinspect-pctx--start-time pctx) (time-convert nil))) - - ;; 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))) - (throw 'phpinspect-parse-interrupted nil))) - - - -(defsubst phpinspect-pctx-register-token (pctx token start end) - (phpinspect-bmap-register - (phpinspect-pctx-bmap pctx) start end token (phpinspect-pctx-consume-whitespace pctx))) - -(defsubst phpinspect-pctx-register-whitespace (pctx whitespace) - (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-list-handlers () (let ((handlers)) @@ -458,15 +397,6 @@ have any effect." (setf (phpinspect-parser-incremental-func (symbol-value parser-symbol)) nil)) phpinspect-parser-obarray)) -(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))))) (defun phpinspect-make-parser-function (tree-type handler-list &optional delimiter-predicate) "Create a parser function using the handlers by names defined in HANDLER-LIST. @@ -1139,5 +1069,20 @@ the properties of the class" (current-buffer) point nil 'root)))) +(defun phpinspect-parse-current-buffer () + (phpinspect-parse-buffer-until-point + (current-buffer) + (point-max))) + +(defun phpinspect-parse-string (string) + (with-temp-buffer + (insert string) + (phpinspect-parse-current-buffer))) + +(defun phpinspect-parse-file (file) + (with-temp-buffer + (insert-file-contents file) + (phpinspect-parse-current-buffer))) + (provide 'phpinspect-parser) ;;; phpinspect-parser.el ends here diff --git a/phpinspect.el b/phpinspect.el index aefdeda345..fd465c8439 100644 --- a/phpinspect.el +++ b/phpinspect.el @@ -71,16 +71,6 @@ phpinspect") project-root) indexed-class))) -(defun phpinspect-parse-file (file) - (with-temp-buffer - (phpinspect-insert-file-contents file) - (phpinspect-parse-current-buffer))) - -(defun phpinspect-parse-current-buffer () - (phpinspect-parse-buffer-until-point - (current-buffer) - (point-max))) - (defun phpinspect-parse-string-to-bmap (string) (with-temp-buffer (insert string) @@ -91,11 +81,6 @@ phpinspect") (phpinspect-pctx-bmap context)))) -(defun phpinspect-parse-string (string) - (with-temp-buffer - (insert string) - (phpinspect-parse-current-buffer))) - (defun phpinspect-after-change-function (start end pre-change-length) (when phpinspect-current-buffer (phpinspect-buffer-register-edit phpinspect-current-buffer start end pre-change-length))) diff --git a/test/phpinspect-test.el b/test/phpinspect-test.el index 53fc8be8e8..86ba1cfa2f 100644 --- a/test/phpinspect-test.el +++ b/test/phpinspect-test.el @@ -246,76 +246,6 @@ (should (equal expected assignments)))) -(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)))) - (ert-deftest phpinspect-resolve-type-from-context () (let* ((pctx (phpinspect-make-pctx :incremental t)) @@ -610,7 +540,10 @@ class Thing (load-file (concat phpinspect-test-directory "/test-bmap.el")) (load-file (concat phpinspect-test-directory "/test-edtrack.el")) (load-file (concat phpinspect-test-directory "/test-resolvecontext.el")) +(load-file (concat phpinspect-test-directory "/test-parser.el")) +(load-file (concat phpinspect-test-directory "/test-parse-context.el")) (load-file (concat phpinspect-test-directory "/test-splayt.el")) + (provide 'phpinspect-test) ;;; phpinspect-test.el ends here diff --git a/test/test-parse-context.el b/test/test-parse-context.el new file mode 100644 index 0000000000..b79db41183 --- /dev/null +++ b/test/test-parse-context.el @@ -0,0 +1,52 @@ +;;; phpinspect-test.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Free Software Foundation, Inc. + +;; Author: Hugo Thunnissen <de...@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) + +(ert-deftest phpinspect-pctx-cancel () + (let ((meta (phpinspect-make-meta nil 10 20 " " 'token 'overlay nil)) + (pctx (phpinspect-make-pctx))) + (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))))) diff --git a/test/test-parser.el b/test/test-parser.el index cc891d2e8c..d2ecf947c3 100644 --- a/test/test-parser.el +++ b/test/test-parser.el @@ -1,6 +1,46 @@ +;; test-parser.el --- Unit tests for phpinspect.el -*- lexical-binding: t; -*- + +;; Copyright (C) 2021 Free Software Foundation, Inc. + +;; Author: Hugo Thunnissen <de...@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) +(defvar phpinspect-test-directory + (file-name-directory + (or load-file-name + buffer-file-name)) + "Directory that phpinspect tests reside in.") + + +(defvar phpinspect-test-php-file-directory + (concat + (file-name-directory + (or load-file-name + buffer-file-name)) + "/fixtures") + "Directory with syntax trees of example PHP files.") + + (ert-deftest phpinspect-parse-bmap () (let* ((ctx (phpinspect-make-pctx :incremental t)) (code " @@ -30,3 +70,88 @@ class TestClass { (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)) + (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))))