[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[elpa] externals/phpinspect d7704c9d7c 1/2: Add test for incremental par
From: |
ELPA Syncer |
Subject: |
[elpa] externals/phpinspect d7704c9d7c 1/2: Add test for incremental parser accuracy and fix some gnarly parser bugs |
Date: |
Mon, 19 Aug 2024 18:58:54 -0400 (EDT) |
branch: externals/phpinspect
commit d7704c9d7cb94d2361df27fa625efe22ac94d7d7
Author: Hugo Thunnissen <devel@hugot.nl>
Commit: Hugo Thunnissen <devel@hugot.nl>
Add test for incremental parser accuracy and fix some gnarly parser bugs
---
phpinspect-bmap.el | 24 +++++++--------
phpinspect-index.el | 13 +++++---
phpinspect-parse-context.el | 11 +++++--
phpinspect-parser.el | 71 ++++++++++++++++++++++++++++----------------
phpinspect-resolvecontext.el | 3 +-
test/test-buffer.el | 64 ++++++++++++++++++++++++++++++++++++++-
6 files changed, 140 insertions(+), 46 deletions(-)
diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el
index fbe7408ae6..0b1997304b 100644
--- a/phpinspect-bmap.el
+++ b/phpinspect-bmap.el
@@ -170,20 +170,20 @@
(push token existing-end)
(puthash end (list token-meta) ends))
- (puthash token token-meta meta)
+ (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))
+ (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-token-stack bmap) stack)))
- (setf (phpinspect-bmap-last-token-start bmap) start)
- (push token-meta (phpinspect-bmap-token-stack bmap))))
+ (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)
@@ -208,7 +208,7 @@
(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)
+ (phpinspect-bmap-token-starting-at overlay point)
(gethash point (phpinspect-bmap-starts bmap)))))
(cl-defmethod phpinspect-bmap-tokens-ending-at ((overlay (head overlay)) point)
diff --git a/phpinspect-index.el b/phpinspect-index.el
index 220df3d6fc..a866281d9f 100644
--- a/phpinspect-index.el
+++ b/phpinspect-index.el
@@ -197,18 +197,23 @@ function (think \"new\" statements, return types etc.)."
(seq-filter #'phpinspect-var-annotation-p token))
(define-inline phpinspect-var-annotation-variable (annotation)
- (inline-quote (cadr (caddr ,annotation))))
+ "Return ANNOTATION's variable name if and only if its structure is correct."
+ (inline-letevals ((variable-name (inline-quote (cadr (caddr ,annotation)))))
+ (inline-quote (and (stringp ,variable-name)
+ ,variable-name))))
(define-inline phpinspect-var-annotation-type (annotation)
- (inline-quote (cadadr ,annotation)))
+ "Returns ANNOTATION's variable type if and only if its structure is correct."
+ (inline-letevals ((variable-type (inline-quote (cadadr ,annotation))))
+ (inline-quote (and (stringp ,variable-type) ,variable-type))))
(defun phpinspect--find-var-annotation-for-variable (annotation-list variable
&optional predicate)
(catch 'return
(dolist (annotation annotation-list)
(when (and (or (phpinspect-var-annotation-p annotation) (and predicate
(funcall predicate annotation)))
(phpinspect-var-annotation-variable annotation)
- (string= (phpinspect-var-annotation-variable annotation)
- variable))
+ (equal (phpinspect-var-annotation-variable annotation)
+ variable))
(throw 'return annotation)))
nil))
diff --git a/phpinspect-parse-context.el b/phpinspect-parse-context.el
index e56e5d1687..0da38a6a27 100644
--- a/phpinspect-parse-context.el
+++ b/phpinspect-parse-context.el
@@ -61,6 +61,9 @@ thrown.")
(whitespace-before ""
:type string))
+(define-inline phpinspect-pctx-whitespace-before-length (ctx)
+ (inline-quote (length (phpinspect-pctx-whitespace-before ,ctx))))
+
(defmacro phpinspect-with-parse-context (ctx &rest body)
(declare (indent 1))
(let ((old-ctx (gensym))
@@ -78,13 +81,14 @@ thrown.")
(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)
+ (setf (phpinspect-pctx-whitespace-before ,pctx) "")
,@body)
(setf (phpinspect-pctx-whitespace-before ,pctx) ,save-sym)))))
@@ -120,8 +124,9 @@ thrown.")
(throw 'phpinspect-parse-interrupted nil))))))
(define-inline phpinspect-pctx-register-whitespace (pctx whitespace)
- (inline-quote
- (setf (phpinspect-pctx-whitespace-before ,pctx) ,whitespace)))
+ (inline-letevals (pctx)
+ (inline-quote
+ (setf (phpinspect-pctx-whitespace-before ,pctx) ,whitespace))))
(defsubst phpinspect-pctx-consume-whitespace (pctx)
(let ((whitespace (phpinspect-pctx-whitespace-before pctx)))
diff --git a/phpinspect-parser.el b/phpinspect-parser.el
index 5164420883..bb8328f4bd 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -38,10 +38,6 @@
(inline-letevals (string)
(inline-quote
(progn
- (when phpinspect-parse-context
- (phpinspect-pctx-register-whitespace
- phpinspect-parse-context
- (substring ,string (- (length ,string) 1) (length ,string))))
(substring ,string 0 (- (length ,string) 1))))))
(defsubst phpinspect-munch-token-without-attribs (string token-keyword)
@@ -178,11 +174,13 @@ token is \";\", which marks the end of a statement in
PHP."
tokens))))
- (defun phpinspect-make-incremental-parser-function (name tree-type handlers
&optional delimiter-predicate delimiter-condition)
+ (defun phpinspect-make-incremental-parser-function
+ (name tree-type handlers &optional delimiter-predicate
delimiter-condition)
"Like `phpinspect-make-parser-function', but returned function
is able to reuse an already parsed tree."
(cl-assert (symbolp delimiter-predicate))
- `(defun ,(phpinspect-parser-func-name name "incremental") (context buffer
max-point &optional skip-over continue-condition root)
+ `(defun ,(phpinspect-parser-func-name name "incremental")
+ (context buffer max-point &optional skip-over continue-condition root)
(with-current-buffer buffer
(let* ((tokens (cons ,tree-type nil))
(tokens-rear tokens)
@@ -201,7 +199,11 @@ is able to reuse an already parsed tree."
(delta)
(token))
(when skip-over (forward-char skip-over))
+
(phpinspect-pctx-save-whitespace context
+ (when (looking-at (phpinspect-handler-regexp whitespace))
+ (,(phpinspect-handler-func-name 'whitespace) (match-string 0)))
+
(while (and (< (point) max-point)
(if continue-condition (funcall continue-condition) t)
(not ,(if delimiter-predicate
@@ -245,10 +247,22 @@ is able to reuse an already parsed tree."
(when token
(phpinspect-pctx-register-token context token
start-position (point)))))
handlers)
- (t (forward-char)))
+ ;; When no handlers match, whitespace can be discarded (if
+ ;; we call forward-char, it probably won't be accurate
+ ;; anymore anyways. One reason that no handlers matched
+ ;; could be that this parser does not have the whitespace
+ ;; handler and as such does not contain relevant
+ ;; whitespace.
+ (t (phpinspect-pctx-consume-whitespace context)
+ (forward-char)))
(when token
(setq tokens-rear (setcdr tokens-rear (cons token nil)))
- (setq token nil))))
+ (setq token nil)))
+
+ ;; When there is unconsumed whitespace, move back. It should not
be
+ ;; included in the current parent token's length.
+ (backward-char (length (phpinspect-pctx-consume-whitespace
context))))
+
(when root
(phpinspect-pctx-register-token context tokens root-start
(point)))
@@ -446,12 +460,12 @@ nature like argument lists"
(current-buffer)
max-point
(length start-token)
- (lambda () (not (and (char-equal (char-after) ?\)) (setq
complete-list t)))))))
+ (lambda () (not (and (char-equal (char-after) ?\)) (setq
complete-list (point))))))))
(if complete-list
;; Prevent parent-lists (if any) from exiting by skipping over the
;; ")" character
- (forward-char)
+ (goto-char (+ complete-list 1))
(setcar php-list :incomplete-list))
php-list))
@@ -663,11 +677,11 @@ static keywords with the same meaning as in a class
block."
(let* ((complete-block nil)
(continue-condition (lambda ()
(not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
+ (setq complete-block (point))))))
(parsed (phpinspect--parse-block-without-scopes
- (current-buffer) max-point (length start-token)
continue-condition 'root)))
+ (current-buffer) max-point (length start-token)
continue-condition)))
(if complete-block
- (forward-char)
+ (goto-char (+ complete-block 1))
(setcar parsed :incomplete-block))
parsed))
@@ -685,11 +699,11 @@ static keywords with the same meaning as in a class
block."
(let* ((complete-block nil)
(continue-condition (lambda ()
(not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
+ (setq complete-block (point))))))
(parsed (phpinspect--parse-class-block
- (current-buffer) max-point (length start-token)
continue-condition 'root)))
+ (current-buffer) max-point (length start-token)
continue-condition)))
(if complete-block
- (forward-char)
+ (goto-char (+ complete-block 1))
(setcar parsed :incomplete-block))
parsed))
@@ -704,14 +718,14 @@ static keywords with the same meaning as in a class
block."
;; When we encounter a closing brace for this
;; block, we can mark the block as complete.
(not (and (char-equal (char-after) ?})
- (setq complete-block t)))))
+ (setq complete-block (point))))))
(parsed (phpinspect--parse-block
(current-buffer) max-point (length start-token)
continue-condition)))
(if complete-block
;; After meeting the char-after requirement above, we need to move
;; one char forward to prevent parent-blocks from exiting because
;; of the same char.
- (forward-char)
+ (goto-char (+ complete-block 1))
(setcar parsed :incomplete-block))
parsed))
@@ -775,10 +789,11 @@ Returns the consumed text string without face properties."
(if (or (phpinspect-end-of-token-p (car (last declaration)))
(not (looking-at (phpinspect-handler-regexp block))))
(list :function declaration)
- (list :function
- declaration
- (phpinspect--block-without-scopes-handler
- (char-to-string (char-after)) max-point)))))
+ `(:function
+ ,declaration
+ ,@(cdr (phpinspect--parse-function-body (current-buffer) max-point))))))
+;; (phpinspect--block-without-scopes-handler
+;; (char-to-string (char-after)) max-point)))))
(phpinspect-defparser scope-public
:tree-keyword "public"
@@ -869,9 +884,15 @@ the properties of the class"
max-point
(lambda () (not (char-equal (char-after) ?{)))
'root)
- ,@(when (looking-at (phpinspect--class-block-handler-regexp))
- (list (phpinspect--class-block-handler
- (char-to-string (char-after)) max-point)))))
+ ,@(cdr (phpinspect--parse-class-body (current-buffer) max-point
nil))))
+
+(phpinspect-defparser class-body
+ :handlers '(whitespace comment class-block)
+ :delimiter-predicate #'phpinspect-block-p)
+
+(phpinspect-defparser function-body
+ :handlers '(whitespace comment block-without-scopes)
+ :delimiter-predicate #'phpinspect-block-p)
(phpinspect-defparser root
:tree-keyword "root"
diff --git a/phpinspect-resolvecontext.el b/phpinspect-resolvecontext.el
index 5c0cee8043..3410fdcbb2 100644
--- a/phpinspect-resolvecontext.el
+++ b/phpinspect-resolvecontext.el
@@ -158,7 +158,7 @@ it would no longer be valid for the new enclosing tokens."
(throw 'break nil)))))
(phpinspect--log "Initial resolvecontext subject token: %s"
- (phpinspect-meta-token subject))
+ (phpinspect-meta-token subject))
(when subject
(setq subject-token
(mapcar #'phpinspect-meta-token
@@ -169,6 +169,7 @@ it would no longer be valid for the new enclosing tokens."
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
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 087406e8ad..7048456630 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -26,6 +26,7 @@
(require 'ert)
(require 'phpinspect-parser)
(require 'phpinspect-buffer)
+(require 'phpinspect-imports)
(require 'phpinspect-test-env
(expand-file-name "phpinspect-test-env.el"
(file-name-directory (macroexp-file-name))))
@@ -262,7 +263,6 @@ class YYY {
(kill-line))
(setq parsed-after (phpinspect-buffer-parse buffer 'no-interrupt))
-
(should (equal parsed parsed-after))
;; Delete namespace declaration
@@ -286,6 +286,68 @@ class YYY {
(should (equal parsed parsed-after))))
+(ert-deftest phpinspect-buffer-parse-incrementally-use ()
+ (with-temp-buffer
+ (let* ((buffer (phpinspect-make-buffer
+ :buffer (current-buffer))))
+
+
+ (setq-local phpinspect-current-buffer buffer)
+
+ (insert
+ "<?php
+
+namespace XXX;
+
+use ZZZ\\zzz;
+use AAA\\BBB; // comment
+
+use CCC;
+
+")
+
+ (add-hook 'after-change-functions #'phpinspect-after-change-function)
+ (phpinspect-buffer-parse buffer 'no-interrupt)
+ (let ((switch nil)
+ (delta 0))
+
+ (dotimes (i 100)
+ (if switch
+ (progn
+ (setq delta 0)
+ (goto-char 44)
+ (insert "hh")
+ (should (phpinspect-edtrack-edits
(phpinspect-buffer-edit-tracker buffer)))
+ (should (= 51 (phpinspect-edtrack-current-position-at-point
(phpinspect-buffer-edit-tracker buffer) 49))))
+ (progn
+ (setq delta (- 2))
+ (goto-char 44)
+ (delete-char 2)
+ (should (phpinspect-edtrack-edits (phpinspect-buffer-edit-tracker
buffer)))
+ (should (= 47 (phpinspect-edtrack-current-position-at-point
(phpinspect-buffer-edit-tracker buffer) 49)))))
+
+ (setq switch (not switch))
+
+ (phpinspect-buffer-parse buffer 'no-interrupt)
+
+ (let ((use (phpinspect-find-first-use
(phpinspect-meta-find-first-child-matching-token
+ (phpinspect-buffer-root-meta buffer)
+ #'phpinspect-namespace-p))))
+
+ (should use)
+ (should (= 2 (length (phpinspect-meta-whitespace-before use))))
+ (should (= 24 (phpinspect-meta-start use)))
+ (should (= 36 (phpinspect-meta-end use)))
+
+ (let ((sibling (phpinspect-meta-find-right-sibling use)))
+ (should sibling)
+ (should (= 37 (phpinspect-meta-start sibling)))
+ (should (= (+ delta 49) (phpinspect-meta-end sibling)))
+
+ (let ((2nd-sibling (phpinspect-meta-find-right-sibling
(phpinspect-meta-find-right-sibling sibling))))
+ (should 2nd-sibling)
+ (should (= (+ delta 63) (phpinspect-meta-start 2nd-sibling)))
+ (should (= (+ delta 71) (phpinspect-meta-end 2nd-sibling)))))))))))
(ert-deftest phpinspect-buffer-index-classes ()
(let* ((buffer (phpinspect-make-buffer :-project (phpinspect--make-project
:autoload (phpinspect-make-autoloader))))