branch: externals/phpinspect commit 9a25959aad06f81752a3ee2e9974c347a0e75190 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Use metadata tree instead of hash table for token lookup --- phpinspect-bmap.el | 54 ++++++++++++++-------------------- phpinspect-completion.el | 2 +- phpinspect-eldoc.el | 59 ++++++++++++++++++++++++++----------- phpinspect-resolvecontext.el | 69 ++++++++++++++++++++++++-------------------- test/test-eldoc.el | 8 ++++- 5 files changed, 109 insertions(+), 83 deletions(-) diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el index 8f87c29217..a155e69e6a 100644 --- a/phpinspect-bmap.el +++ b/phpinspect-bmap.el @@ -25,6 +25,8 @@ (require 'phpinspect-splayt) (require 'phpinspect-meta) +(require 'phpinspect-util) +(require 'compat) (cl-defstruct (phpinspect-bmap (:constructor phpinspect-make-bmap)) (starts (make-hash-table :test #'eql @@ -40,8 +42,17 @@ :type list) (overlays (phpinspect-make-splayt) :type phpinspect-splayt) + (-root-meta nil + :type phpinspect-meta) (last-token-start nil - :type integer)) + :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)) @@ -84,9 +95,6 @@ (define-inline phpinspect-overlay-end (overlay) (inline-quote (caddr ,overlay))) -(define-inline phpinspect-overlay-token-meta (overlay) - (inline-quote (car (nthcdr 5 ,overlay)))) - (define-inline phpinspect-overlay-overlaps-point (overlay point) (inline-letevals (overlay point) (inline-quote @@ -134,7 +142,10 @@ (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 overlay)))) + (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 "")) @@ -149,11 +160,8 @@ (when (and last-token-start (<= start last-token-start)) (let ((child) - (stack (phpinspect-bmap-token-stack bmap)) - (right-siblings)) - - (while (and (car stack) (>= (phpinspect-meta-start (car stack)) - start)) + (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)) @@ -224,29 +232,9 @@ (and (listp token) (keywordp (car token)))) -(cl-defmethod phpinspect-bmap-last-token-before-point ((bmap phpinspect-bmap) point &optional limit) - "Search backward in BMAP for last token ending before POINT. - -LIMIT is the maximum number of positions to check backward before -giving up. If not provided, this is 100." - (unless limit (setq limit 100)) - (let* ((ending) - (point-limit (- point limit))) - (unless (hash-table-empty-p (phpinspect-bmap-ends bmap)) - (while (not (or (<= point 0) (<= point point-limit) - (setq ending (phpinspect-bmap-tokens-ending-at bmap point)))) - (setq point (- point 1))) - (car (last ending))))) - -(cl-defmethod phpinspect-bmap-last-token-starting-before-point ((bmap phpinspect-bmap) point &optional limit) - (unless limit (setq limit 100)) - (let* ((starting) - (point-limit (- point limit))) - (unless (hash-table-empty-p (phpinspect-bmap-starts bmap)) - (while (not (or (<= point 0) (<= point point-limit) - (setq starting (phpinspect-bmap-token-starting-at bmap point)))) - (setq point (- point 1))) - starting))) +(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)) diff --git a/phpinspect-completion.el b/phpinspect-completion.el index 338664a165..554f723201 100644 --- a/phpinspect-completion.el +++ b/phpinspect-completion.el @@ -124,7 +124,7 @@ and CONTEXT. All strategies must implement this method.") (phpinspect-completion-query-point q)) (phpinspect-variable-p (phpinspect-meta-token - (phpinspect-bmap-last-token-starting-before-point + (phpinspect-bmap-last-token-before-point (phpinspect-buffer-parse-map (phpinspect-completion-query-buffer q)) (phpinspect-completion-query-point q)))))) diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el index ffdc5e8459..b77f7c3d4a 100644 --- a/phpinspect-eldoc.el +++ b/phpinspect-eldoc.el @@ -22,6 +22,9 @@ ;;; Commentary: ;;; Code: +(require 'phpinspect-util) +(require 'phpinspect-meta) +(require 'phpinspect-parser) (defvar phpinspect-eldoc-word-width 14 "The maximum width of words in eldoc strings.") @@ -120,43 +123,65 @@ be implemented for return values of `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* ((token-map (phpinspect-buffer-parse-map (phpinspect-eldoc-query-buffer q))) - (enclosing-token (cadr (phpinspect--resolvecontext-enclosing-tokens + (enclosing-token (car (phpinspect--resolvecontext-enclosing-metadata rctx))) - (statement (phpinspect-find-statement-before-point - token-map (phpinspect-bmap-token-meta token-map enclosing-token) - (phpinspect-eldoc-query-point q))) + (left-sibling ) + (statement ) match-result static arg-list arg-pos) - (phpinspect--log "Eldoc statement is: %s" statement) - (phpinspect--log "Enclosing token was: %s" enclosing-token) + (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))) + (phpinspect-meta-overlaps-point left-sibling (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 (or (phpinspect-return-p (phpinspect-meta-token left-sibling)) + (phpinspect-end-of-statement-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-attrib-p - :f #'phpinspect-list-p)) + :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 (car match-result)) + static (phpinspect-static-attrib-p (phpinspect-meta-token (car match-result))) arg-pos (seq-reduce - (lambda (count token) - (if (and (phpinspect-comma-p token) - (>= (phpinspect-eldoc-query-point q) - (phpinspect-meta-end - (phpinspect-bmap-token-meta token-map token)))) + (lambda (count meta) + (if (phpinspect-comma-p (phpinspect-meta-token meta)) (+ count 1) count)) - arg-list 0)) + (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) (butlast statement 2)) + (setf (phpinspect--resolvecontext-subject rctx) + (mapcar #'phpinspect-meta-token (butlast statement 2))) (let* ((type-of-previous-statement (phpinspect-resolve-type-from-context rctx)) - (method-name-sym (phpinspect-intern-name (car (cdadar match-result)))) + (method-name-sym (phpinspect-intern-name (cadadr (phpinspect-meta-token (car match-result))))) (class (phpinspect-project-get-class-create (phpinspect--resolvecontext-project rctx) type-of-previous-statement)) diff --git a/phpinspect-resolvecontext.el b/phpinspect-resolvecontext.el index fd66f68091..03636864dd 100644 --- a/phpinspect-resolvecontext.el +++ b/phpinspect-resolvecontext.el @@ -27,6 +27,8 @@ (require 'phpinspect-project) (require 'phpinspect-parser) (require 'phpinspect-type) +(require 'phpinspect-meta) +(require 'phpinspect-util) (cl-defstruct (phpinspect--resolvecontext (:constructor phpinspect--make-resolvecontext)) @@ -38,6 +40,10 @@ :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 @@ -60,24 +66,21 @@ (string= "return" (cadr token)))) (defun phpinspect-find-statement-before-point (bmap meta point) - (let ((children (reverse (cdr (phpinspect-meta-token meta)))) - child-meta - previous-siblings) + (let ((children (reverse (phpinspect-meta-find-children-before meta point))) + token previous-siblings) (catch 'return (dolist (child children) - (when (phpinspect-probably-token-p child) - (setq child-meta (phpinspect-bmap-token-meta bmap child)) - (unless child-meta - (phpinspect--log "[ERROR] No metadata object found for token %s" child)) - (when (< (phpinspect-meta-start child-meta) point) - (if (and (not previous-siblings) (phpinspect-blocklike-p child)) - (progn - (throw 'return (phpinspect-find-statement-before-point bmap child-meta point))) - (when (or (phpinspect-return-p child) - (phpinspect-end-of-statement-p child)) - (throw 'return previous-siblings)) - (push child previous-siblings))))) - previous-siblings))) + (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 (or (phpinspect-return-p token) + (phpinspect-end-of-statement-p token)) + (throw 'return previous-siblings)) + (push child previous-siblings))))) + previous-siblings)) (defun phpinspect--get-last-statement-in-token (token) (setq token (cond ((phpinspect-function-p token) @@ -107,25 +110,23 @@ (siblings)) (phpinspect--log "Last token before point: %s, right siblings: %s, parent: %s" (phpinspect-meta-string subject) - (phpinspect-meta-right-siblings 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 - (setq next-sibling (phpinspect-bmap-token-meta bmap next-sibling)) (phpinspect-meta-overlaps-point next-sibling point)) (setq subject next-sibling))) ;; Dig down through tokens that can contain statements - (catch 'break - (while (and subject - (phpinspect-enclosing-token-p (phpinspect-meta-token subject)) - (cdr (phpinspect-meta-token subject)) 0) - (let ((new-subject - (phpinspect-bmap-token-meta - bmap (car (last (cdr (phpinspect-meta-token subject))))))) + (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))))) @@ -134,10 +135,9 @@ (phpinspect-meta-token subject)) (when subject (setq subject-token - (phpinspect-find-statement-before-point - bmap - (phpinspect-meta-parent subject) - point)) + (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 @@ -151,12 +151,13 @@ (or (not granny) (phpinspect-function-p (phpinspect-meta-token granny)) (phpinspect-class-p (phpinspect-meta-token granny)))) - (push (phpinspect-meta-token parent) enclosing-tokens)) + (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 enclosing-tokens) + :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) @@ -212,5 +213,11 @@ accompanied by all of its 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 diff --git a/test/test-eldoc.el b/test/test-eldoc.el index f384ffbda7..86a52a105f 100644 --- a/test/test-eldoc.el +++ b/test/test-eldoc.el @@ -22,7 +22,7 @@ class Thing (phpinspect-project-root-function (lambda () "phpinspect-test")) (phpinspect-eldoc-word-width 100) (buffer (phpinspect-make-buffer :buffer (current-buffer))) - second-arg-pos first-arg-pos) + second-arg-pos inside-nested-list-pos first-arg-pos) (phpinspect-ensure-worker) (phpinspect-purge-cache) (phpinspect-cache-project-class @@ -33,6 +33,8 @@ class Thing (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 @@ -41,6 +43,10 @@ class Thing (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))