branch: externals/phpinspect commit 9b82c0d0f6d0cdf9af850554ed2dc8561c721037 Author: Hugo Thunnissen <de...@hugot.nl> Commit: Hugo Thunnissen <de...@hugot.nl>
Reimplement `phpinspect-fix-imports' using metadata objects --- phpinspect-imports.el | 108 ++++++++++++++++++++++++++------------------------ phpinspect-meta.el | 22 ++++++++++ phpinspect-splayt.el | 32 ++++++++++++++- test/test-splayt.el | 17 ++++++++ 4 files changed, 126 insertions(+), 53 deletions(-) diff --git a/phpinspect-imports.el b/phpinspect-imports.el index 0dc753bf7f..e971e75dd8 100644 --- a/phpinspect-imports.el +++ b/phpinspect-imports.el @@ -35,7 +35,14 @@ (goto-char point) (insert data))) -(defun phpinspect-add-use (fqn buffer &optional namespace-token) +(defun phpinspect-find-first-use (token-meta) + (if (and (phpinspect-namespace-p (phpinspect-meta-token token-meta)) + (phpinspect-namespace-is-blocked-p (phpinspect-meta-token token-meta))) + (phpinspect-find-first-use (phpinspect-meta-last-child token-meta)) + (phpinspect-meta-find-first-child-matching + token-meta (phpinspect-meta-wrap-token-pred #'phpinspect-use-p)))) + +(defun phpinspect-add-use (fqn buffer &optional namespace-meta) "Add use statement for FQN to BUFFER. If NAMESPACE-TOKEN is non-nil, it is assumed to be a token that @@ -44,46 +51,47 @@ buffer position to insert the use statement at." (when (string-match "^\\\\" fqn) (setq fqn (string-trim-left fqn "\\\\"))) - (if namespace-token - (let* ((meta (phpinspect-bmap-token-meta - (phpinspect-buffer-map buffer) namespace-token)) - (existing-use (seq-find #'phpinspect-use-p - (phpinspect-namespace-body namespace-token))) - (namespace-block (phpinspect-namespace-block namespace-token))) + (if namespace-meta + (let* ((namespace-block (and (phpinspect-namespace-is-blocked-p + (phpinspect-meta-token namespace-meta)) + (phpinspect-meta-last-child namespace-meta))) + (existing-use (phpinspect-find-first-use namespace-meta))) (if existing-use (phpinspect-insert-at-point - (phpinspect-meta-start - (phpinspect-buffer-token-meta buffer existing-use)) - (format "use %s;%c" fqn ?\n)) + (phpinspect-meta-start existing-use) (format "use %s;%c" fqn ?\n)) (if namespace-block (phpinspect-insert-at-point - (+ 1 (phpinspect-meta-start - (phpinspect-buffer-token-meta buffer namespace-block))) + (+ 1 (phpinspect-meta-start namespace-block)) (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)) (phpinspect-insert-at-point (phpinspect-meta-end - (phpinspect-buffer-token-meta - buffer (seq-find #'phpinspect-terminator-p namespace-token))) + (phpinspect-meta-find-first-child-matching + namespace-meta (phpinspect-meta-wrap-token-pred #'phpinspect-terminator-p))) (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n))))) ;; else - (let ((existing-use (seq-find #'phpinspect-use-p - (phpinspect-buffer-tree buffer)))) + (let ((existing-use (phpinspect-meta-find-first-child-matching + (phpinspect-buffer-root-meta buffer) + (phpinspect-meta-wrap-token-pred #'phpinspect-use-p)))) (if existing-use (phpinspect-insert-at-point - (phpinspect-meta-start - (phpinspect-buffer-token-meta buffer existing-use)) + (phpinspect-meta-start existing-use) (format "use %s;%c" fqn ?\n)) - (let ((first-token (cadr (phpinspect-buffer-tree buffer)))) - (if (and (phpinspect-word-p first-token) - (string= "declare" (cadr first-token))) + (let* ((first-token (phpinspect-meta-first-child (phpinspect-buffer-root-meta buffer))) + token-after) + (message "First token %s" (phpinspect-meta-string first-token)) + (when (and (phpinspect-word-p (phpinspect-meta-token first-token)) + (string= "declare" (cadr (phpinspect-meta-token first-token)))) + (progn + (setq token-after first-token) + (while (and token-after (not (phpinspect-terminator-p + (phpinspect-meta-token token-after)))) + (setq token-after (phpinspect-meta-find-right-sibling token-after)) + (message "Token after: %s" (phpinspect-meta-string token-after))))) + (if token-after (phpinspect-insert-at-point - (phpinspect-meta-end - (phpinspect-buffer-token-meta - buffer (seq-find #'phpinspect-terminator-p (phpinspect-buffer-tree buffer)))) - (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)) + (phpinspect-meta-end token-after) (format "%c%cuse %s;%c" ?\n ?\n fqn ?\n)) (phpinspect-insert-at-point - (phpinspect-meta-start - (phpinspect-buffer-token-meta buffer first-token)) + (phpinspect-meta-start first-token) (format "%c%cuse %s;%c%c" ?\n ?\n fqn ?\n ?\n)))))))) (defun phpinspect-add-use-interactive (typename buffer project &optional namespace-token) @@ -109,10 +117,10 @@ buffer position to insert the use statement at." that there are import (\"use\") statements for them." (interactive) (if phpinspect-current-buffer - (let* ((tree (phpinspect-buffer-parse phpinspect-current-buffer)) + (let* ((buffer phpinspect-current-buffer) + (tree (phpinspect-buffer-parse buffer)) (index (phpinspect--index-tokens - tree nil (phpinspect-buffer-location-resolver - phpinspect-current-buffer))) + tree nil (phpinspect-buffer-location-resolver buffer))) (classes (alist-get 'classes index)) (imports (alist-get 'imports index)) (project (phpinspect--cache-get-project-create @@ -122,20 +130,23 @@ that there are import (\"use\") statements for them." (let* ((class-imports (alist-get 'imports class)) (used-types (alist-get 'used-types class)) (class-name (alist-get 'class-name class)) - (region)) - (dolist (type used-types) - ;; Retrieve latest version of class location data changes with - ;; each added use statement + reindex. - (setq region - (alist-get 'location - (phpinspect-index-get-class - index class-name))) - - (let ((namespace - (seq-find (lambda (meta) (phpinspect-namespace-p (phpinspect-meta-token meta))) - (phpinspect-buffer-tokens-enclosing-point - phpinspect-current-buffer (phpinspect-region-start region))))) + (region (alist-get 'location class)) + token-meta namespace) + (message "Region: %s" region) + (message "index: %s" index) + (setq token-meta (phpinspect-meta-find-parent-matching-token + (phpinspect-bmap-last-token-before-point + (phpinspect-buffer-map buffer) + (+ (phpinspect-region-start region) 1)) + #'phpinspect-class-p)) + (unless token-meta + (error "Unable to find token for class %s" class-name)) + + + (dolist (type used-types) + (setq namespace (phpinspect-meta-find-parent-matching-token + token-meta #'phpinspect-namespace-p)) ;; Add use statements for types that aren't imported. (unless (or (or (alist-get type class-imports) (alist-get type imports)) @@ -147,14 +158,7 @@ that there are import (\"use\") statements for them." (phpinspect-autoloader-types (phpinspect-project-autoload project)))) (phpinspect-add-use-interactive - type phpinspect-current-buffer project (phpinspect-meta-token namespace)) - ;; Buffer has been modified by adding type, update buffer map - ;; and index for correct location data. - (setq index - (phpinspect--index-tokens - (phpinspect-buffer-parse phpinspect-current-buffer) - nil - (phpinspect-buffer-location-resolver - phpinspect-current-buffer))))))))))) + type buffer project namespace) + (phpinspect-buffer-parse buffer 'no-interrupt)))))))) (provide 'phpinspect-imports) diff --git a/phpinspect-meta.el b/phpinspect-meta.el index 2ba41bafdd..e898371c9e 100644 --- a/phpinspect-meta.el +++ b/phpinspect-meta.el @@ -133,6 +133,12 @@ (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) (phpinspect-meta--point-offset meta point)))) @@ -162,6 +168,10 @@ (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) @@ -180,6 +190,18 @@ (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 diff --git a/phpinspect-splayt.el b/phpinspect-splayt.el index c4874f6993..37f81fa08a 100644 --- a/phpinspect-splayt.el +++ b/phpinspect-splayt.el @@ -93,6 +93,9 @@ apeared to be a little more performant than using `let'." (define-inline phpinspect-splayt-root-node (splayt) (inline-quote (car ,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 @@ -216,7 +219,7 @@ apeared to be a little more performant than using `let'." (define-inline phpinspect-splayt-insert-node (splayt node) (inline-letevals (splayt node (parent (inline-quote (phpinspect-splayt-node-temp-store ,node)))) (inline-quote - (if (not (phpinspect-splayt-root-node ,splayt)) + (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))) @@ -292,6 +295,33 @@ near the top of the tee." (,(car place-and-splayt) (phpinspect-splayt-root-node ,(cadr 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 + ,@(if (symbolp place) (list place))) + (while (or ,stack ,current) + (if ,current + (progn + (push ,current ,stack) + (setq ,current (phpinspect-splayt-node-left ,current))) + (setq ,current (pop ,stack)) + (setf ,place (phpinspect-splayt-node-value ,current)) + ,@body + (setq ,current (phpinspect-splayt-node-right ,current))))))) + +(defmacro phpinspect-splayt-traverse-lr (place-and-splayt &rest body) + "Traverse splay tree in cadr of PLACE-AND-SPLAYT depth-first from left to right, executing BODY. + +The car of PLACE-AND-SPLAYT is assigned the value of each node." + (declare (indent 1)) + `(phpinspect-splayt-node-traverse-lr + (,(car place-and-splayt) (phpinspect-splayt-root-node ,(cadr place-and-splayt))) + ,@body)) + (define-inline phpinspect-splayt-node-key-less-p (node key) (inline-quote (> ,key (phpinspect-splayt-node-key ,node)))) diff --git a/test/test-splayt.el b/test/test-splayt.el index 525fe1aaae..7eacf7d5ce 100644 --- a/test/test-splayt.el +++ b/test/test-splayt.el @@ -101,6 +101,23 @@ (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")