branch: externals/phpinspect
commit 459e03855993b76651d427ef527d165e3de5a16d
Author: Hugo Thunnissen <de...@hugot.nl>
Commit: Hugo Thunnissen <de...@hugot.nl>

    WIP shadow buffer
---
 benchmarks/parse-file.el       | 182 +++++++++++++-------------
 phpinspect-bmap.el             |  20 +--
 phpinspect-buffer.el           | 284 ++++++++++++++++++++++++++++++++++++-----
 phpinspect-change.el           | 178 ++++++++++++++++++++++++++
 phpinspect-completion.el       |  16 +--
 phpinspect-diagnose.el         |  87 +++++++++++++
 phpinspect-eldoc.el            |   2 +-
 phpinspect-parse-context.el    | 108 +++++++---------
 phpinspect-parser.el           |  57 +++++----
 phpinspect-resolvecontext.el   |   3 +-
 phpinspect-shadow.el           | 212 ++++++++++++++++++++++++++++++
 phpinspect-token-predicates.el |   3 +
 phpinspect.el                  |   7 +-
 test/test-buffer.el            |  70 ++++------
 test/test-changeset.el         |  64 +++++-----
 test/test-eldoc.el             |   6 +-
 test/test-imports.el           |  81 ++++--------
 test/test-parse-context.el     |  40 +++---
 test/test-parser.el            | 144 ++++++++++-----------
 test/test-shadow.el            |  66 ++++++++++
 test/test-suggest.el           |  64 +++++-----
 21 files changed, 1198 insertions(+), 496 deletions(-)

diff --git a/benchmarks/parse-file.el b/benchmarks/parse-file.el
index d9685a16bb..27573e618f 100644
--- a/benchmarks/parse-file.el
+++ b/benchmarks/parse-file.el
@@ -24,128 +24,128 @@
 
 
 ;;(require 'profiler)
-(require 'phpinspect-parser)
-(require 'phpinspect-buffer)
+;; (require 'phpinspect-parser)
+;; (require 'phpinspect-buffer)
 
-(defun phpinspect-parse-current-buffer ()
-  (phpinspect-parse-buffer-until-point
-   (current-buffer)
-   (point-max)))
+;; (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)))
-       buffer
-       result)
+;; (let* ((here (file-name-directory (macroexp-file-name)))
+;;        (benchmark-file (or (getenv "PHPINSPECT_BENCHMARK_FILE")
+;;                            (expand-file-name "Response.php" here)))
+;;        buffer
+;;        result)
 
 
 
-  (with-temp-buffer
-    (insert-file-contents benchmark-file)
+;;   (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 "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))
+;;     (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))))
+;;     (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))
+;;       (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))))
+;;       (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 "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))
+;;       (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)
+;;       (garbage-collect)
 
-      (setq buffer (phpinspect-claim-buffer (current-buffer)))
-      (let ((edtrack (phpinspect-buffer-edit-tracker buffer))
-            (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))
+;;       (setq buffer (phpinspect-claim-buffer (current-buffer)))
+;;       (let ((edtrack (phpinspect-buffer-edit-tracker buffer))
+;;             (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))
 
-        ;; (setq token-hits nil
-        ;;       token-misses nil)
-        (message "Incremental parse after buffer edit:")
-        ;; Removes closing curly brace of __construct
-        (goto-char 9062)
-        (delete-char -1)
+;;         ;; (setq token-hits nil
+;;         ;;       token-misses nil)
+;;         (message "Incremental parse after buffer edit:")
+;;         ;; Removes closing curly brace of __construct
+;;         (goto-char 9062)
+;;         (delete-char -1)
 
-        (garbage-collect)
-        ;;        (profiler-start 'cpu)
+;;         (garbage-collect)
+;;         ;;        (profiler-start 'cpu)
 
-        ;;(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))))
+;;         ;;(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 "{")
+;;         (message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr 
result) (cadr result))
+;;         (phpinspect-edtrack-clear edtrack)
+;;         (insert "{")
 
-        ;;(message "Hits: %s, Misses: %s" token-hits token-misses)
+;;         ;;(message "Hits: %s, Misses: %s" token-hits token-misses)
 
-        ;;(phpinspect-edtrack-register-edit edtrack 9061 9062 0)
-        ;; Mark region as edit without length deta
-        ;;(phpinspect-edtrack-register-edit edtrack 19552 19562 10)
+;;         ;;(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)
+;;         (garbage-collect)
 
-        ;; (setq token-hits nil
-        ;;       token-misses nil)
+;;         ;; (setq token-hits nil
+;;         ;;       token-misses nil)
 
 
-        (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 "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))
-        ;;(message "Hits: %s, Misses: %s" token-hits token-misses)
+;;         (message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr 
result) (cadr result))
+;;         ;;(message "Hits: %s, Misses: %s" token-hits token-misses)
 
-        ;; (save-current-buffer
-        ;;   (profiler-stop)
-        ;;   (profiler-report)
-        ;;   (profiler-report-write-profile (expand-file-name "profile.txt" 
here)))
+;;         ;; (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)
+;;   (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)))
+;;     (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))
+;;     (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))))
+;;     (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)))
+;;   (message "Elapsed time: %f (%f in %d GC's)" (car result) (caddr result) 
(cadr result)))
diff --git a/phpinspect-bmap.el b/phpinspect-bmap.el
index de8f28a716..d29ef1058e 100644
--- a/phpinspect-bmap.el
+++ b/phpinspect-bmap.el
@@ -77,7 +77,6 @@ map parsed tokens to metadata about them and vice versa."
   (last-meta nil :type phpinspect-meta)
   (last-token-start nil
                     :type integer)
-  (mask 0 :type integer)
   (recycled-p nil
               :type boolean
               :documentation "Whether bmap contains recycled tokens")
@@ -146,10 +145,10 @@ object and re-used instead of instantiating a new object."
 (define-inline phpinspect-bmap-token-starting-after (bmap point)
   (inline-letevals (bmap point)
     (inline-quote
-     (when-let ((root-meta (phpinspect-bmap-root-meta ,bmap)))
+     (let ((root-meta (phpinspect-bmap-root-meta ,bmap)))
        (phpinspect-meta-find-child-after-recursively root-meta ,point)))))
 
-(defsubst phpinspect-bmap-tokens-overlapping (bmap point)
+(defun phpinspect-bmap-tokens-overlapping (bmap point)
   (sort
    (phpinspect-meta-find-overlapping-children (phpinspect-bmap-root-meta bmap) 
point)
    #'phpinspect-meta-sort-width))
@@ -171,7 +170,8 @@ compatibility with tests and for easy refactoring later on."
 
 (defun phpinspect-bmap-last-token-before-point (bmap point)
   "Search backward in BMAP for last token ending before POINT."
-  (phpinspect-meta-find-child-before-recursively (phpinspect-bmap-root-meta 
bmap) point))
+  (let ((root-meta (phpinspect-bmap-root-meta bmap)))
+    (phpinspect-meta-find-child-before-recursively root-meta point)))
 
 (define-inline phpinspect-bmap-recycle (bmap token-meta pos-delta &optional 
whitespace-before)
   "Re-use TOKEN-META as a token in BMAP, applying POS-DELTA.
@@ -192,13 +192,13 @@ via `phpinspect-parse-context'."
 
          (setf (phpinspect-bmap-recycled-p ,bmap) t)
 
-         (phpinspect-meta-with-changeset ,token-meta
-           (phpinspect-meta-detach-parent ,token-meta)
-           (phpinspect-meta-shift ,token-meta ,pos-delta)
 
-           (dlet ((phpinspect-meta--point-offset-base nil))
-             (phpinspect-bmap-register
-              ,bmap start end (phpinspect-meta-token ,token-meta) 
,whitespace-before ,token-meta)))))))
+         (phpinspect-meta-detach-parent ,token-meta)
+         (phpinspect-meta-shift ,token-meta ,pos-delta)
+
+         (dlet ((phpinspect-meta--point-offset-base nil))
+           (phpinspect-bmap-register
+            ,bmap start end (phpinspect-meta-token ,token-meta) 
,whitespace-before ,token-meta))))))
 
 (defun phpinspect-make-region (start end)
   (list start end))
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index 14515285e7..f7a9d1e6b0 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -32,6 +32,7 @@
 (require 'phpinspect-util)
 (require 'phpinspect-typedef)
 (require 'phpinspect-token-predicates)
+(require 'phpinspect-change)
 
 (phpinspect--declare-log-group 'buffer)
 
@@ -40,12 +41,17 @@
 buffer. This variable is only set for buffers where
 `phpinspect-mode' is active. Also see `phpinspect-buffer'.")
 
+
 (cl-defstruct (phpinspect-buffer (:constructor phpinspect-make-buffer))
   "An object containing phpinspect related metadata linked to an
 emacs buffer."
   (buffer nil
           :type buffer
           :documentation "The associated emacs buffer")
+  (shadow nil
+          :type buffer)
+  (-changes nil
+            :type list)
   (tree nil
         :documentation
         "Parsed token tree that resulted from last parse")
@@ -56,11 +62,12 @@ emacs buffer."
   (-deletions nil)
   (-additions nil)
   (-tokens nil)
+  (last-change nil :type phpinspect-change)
   (token-index (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
   (-project nil
-            :type phpinspect-project)
-  (edit-tracker (phpinspect-make-edtrack)
-                :type phpinspect-edtrack))
+            :type phpinspect-project))
+  ;; (edit-tracker (phpinspect-make-edtrack)
+  ;;               :type phpinspect-edtrack))
 
 (defmacro phpinspect-buffer--query-with-cache (buffer label &rest body)
   (declare (indent 2))
@@ -79,7 +86,7 @@ emacs buffer."
 
 (defun phpinspect-buffer-tainted-p (buffer)
   "Whether or not BUFFER's current tree needs updating to incorporate edits."
-  (and (phpinspect-edtrack-taint-pool (phpinspect-buffer-edit-tracker buffer)) 
t))
+  (not (phpi-shadow-synced-p (phpinspect-buffer-shadow buffer))))
 
 (defun phpinspect-buffer-needs-parse-p (buffer)
   "Whether or not BUFFER needs to be parsed for an updated tree to be present."
@@ -150,32 +157,39 @@ edits does not count as fresh (because incremental 
parsing has its flaws)."
 (cl-defmethod phpinspect-buffer-parse ((buffer phpinspect-buffer) &optional 
no-interrupt)
   "Parse the PHP code in the the emacs buffer that this object is
 linked with."
-  (let (tree)
-    (if (phpinspect-buffer-needs-parse-p buffer)
-        (with-current-buffer (phpinspect-buffer-buffer buffer)
-          (let* ((map (phpinspect-make-bmap))
-                 (buffer-map (phpinspect-buffer-map buffer))
-                 (ctx (phpinspect-make-pctx
-                       :interrupt-predicate (unless no-interrupt 
#'phpinspect--input-pending-p)
-                       :bmap map
-                       :incremental t
-                       :previous-bmap buffer-map
-                       :edtrack (phpinspect-buffer-edit-tracker buffer))))
-            (phpinspect-with-parse-context ctx
-              (phpinspect--log "Parsing buffer")
-              (let ((parsed (phpinspect-parse-current-buffer)))
-                ;; Inhibit quitting to guarantee data integrity
-                (let ((inhibit-quit t))
-                  (setf (phpinspect-buffer-tree buffer) parsed)
-                  (phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker 
buffer))
-                  (phpinspect-buffer--set-map buffer map buffer-map)
-
-                  ;; set return value
-                  (setq tree parsed))))))
-
-      ;; Else: Just return last parse result
-      (setq tree (phpinspect-buffer-tree buffer))
-      tree)))
+  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer) (not 
no-interrupt))
+  (unless (phpinspect-buffer-map buffer)
+    (phpi-shadow-enqueue-task (phpinspect-buffer-shadow buffer) 'parse-fresh)
+    (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer) (not 
no-interrupt)))
+
+  (phpinspect-buffer-tree buffer))
+
+;; (let (tree)
+  ;;   (if (phpinspect-buffer-needs-parse-p buffer)
+  ;;       (with-current-buffer (phpinspect-buffer-buffer buffer)
+  ;;         (let* ((map (phpinspect-make-bmap))
+  ;;                (buffer-map (phpinspect-buffer-map buffer))
+  ;;                (ctx (phpinspect-make-pctx
+  ;;                      :interrupt-predicate (unless no-interrupt 
#'phpinspect--input-pending-p)
+  ;;                      :bmap map
+  ;;                      :incremental t
+  ;;                      :previous-bmap buffer-map
+  ;;                      :edtrack (phpinspect-buffer-edit-tracker buffer))))
+  ;;           (phpinspect-with-parse-context ctx
+  ;;             (phpinspect--log "Parsing buffer")
+  ;;             (let ((parsed (phpinspect-parse-current-buffer)))
+  ;;               ;; Inhibit quitting to guarantee data integrity
+  ;;               (let ((inhibit-quit t))
+  ;;                 (setf (phpinspect-buffer-tree buffer) parsed)
+  ;;                 (phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker 
buffer))
+  ;;                 (phpinspect-buffer--set-map buffer map buffer-map)
+
+  ;;                 ;; set return value
+  ;;                 (setq tree parsed))))))
+
+  ;;     ;; Else: Just return last parse result
+  ;;     (setq tree (phpinspect-buffer-tree buffer))
+  ;;     tree)))
 
 (cl-defmethod phpinspect-buffer-get-index-for-token ((buffer 
phpinspect-buffer) token)
   (gethash token (phpinspect-buffer-token-index buffer)))
@@ -265,12 +279,26 @@ tokens that have been deleted from a buffer."
         (phpinspect-buffer-token-index buffer)
         (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
 
-  (phpinspect-edtrack-clear (phpinspect-buffer-edit-tracker buffer)))
+  (phpi-shadow-enqueue-task (phpinspect-buffer-shadow buffer) 'parse-fresh))
+
+(defun phpinspect-buffer-state (buffer)
+  (interactive (list (or phpinspect-current-buffer
+                         (error "Not a phpinspect buffer"))))
+
+  (let ((shadow (phpinspect-buffer-shadow buffer)))
+    (pop-to-buffer (generate-new-buffer "phpinspect-buffer-state"))
+    (insert (format (concat "Buffer name: %s\nLast Shadow Error: %s\n"
+                            "Shadow Thread Live: %s")
+                    (phpinspect-with-current-buffer buffer (buffer-name))
+                    (thread-last-error (phpi-shadow-thread shadow))
+                    (phpi-shadow-thread-live-p shadow)))
+    (read-only-mode)))
 
 (defun phpinspect-buffer-reparse (buffer)
   "Discard BUFFER's current token tree and re-parse fully."
   (interactive (list (or phpinspect-current-buffer (error "Not a phpinspect 
buffer"))))
   (phpinspect-buffer-reset buffer)
+  (phpi-shadow-enqueue-task (phpinspect-buffer-shadow buffer) 'parse-fresh)
   (phpinspect-buffer-parse buffer 'no-interrupt))
 
 (defun phpinspect-buffer-reindex (buffer)
@@ -602,7 +630,7 @@ continuing execution."
 
           (setf (phpinspect-buffer--last-indexed-bmap buffer) map)))))
 
-(defsubst phpinspect-buffer-parse-map (buffer)
+(defun phpinspect-buffer-parse-map (buffer)
   (phpinspect-buffer-parse buffer)
   (phpinspect-buffer-map buffer))
 
@@ -623,8 +651,14 @@ continuing execution."
       (setq start (- start (length (match-string 0))))
       (setq pre-change-length (+ pre-change-length (length (match-string 
0))))))
 
-  (phpinspect-edtrack-register-edit
-   (phpinspect-buffer-edit-tracker buffer) start end pre-change-length))
+  ;;(message "Registering edit: %d, %d, %d" start end pre-change-length)
+  (phpi-shadow-enqueue-task
+   (phpinspect-buffer-shadow buffer)
+   (phpi-change-create (phpinspect-buffer-buffer buffer)
+                       start end pre-change-length)))
+
+  ;; (phpinspect-edtrack-register-edit
+  ;;  (phpinspect-buffer-edit-tracker buffer) start end pre-change-length))
 
 (defun phpinspect-buffer-tokens-enclosing-point (buffer point)
   "Return token metadata objects for tokens enclosing POINT in BUFFER."
@@ -650,6 +684,7 @@ use."
                               (phpinspect-meta-end meta)))))
 
 (defun phpinspect-buffer-root-meta (buffer)
+  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer))
   (phpinspect-bmap-root-meta (phpinspect-buffer-map buffer)))
 
 (defun phpinspect-display-buffer-tree ()
@@ -684,6 +719,12 @@ use."
   (phpinspect-get-resolvecontext
    (phpinspect-buffer-project buffer) (phpinspect-buffer-parse-map buffer) 
point))
 
+(defun phpinspect-buffer-kill ()
+  (when phpinspect-current-buffer
+    (kill-buffer
+     (phpinspect-buffer-shadow
+      (phpinspect-buffer-shadow
+       phpinspect-current-buffer)))))
 
 (defun phpinspect-claim-buffer (buffer &optional project)
   "Setup an instance of `phpinspect-buffer' for BUFFER.
@@ -697,9 +738,182 @@ BUFFER must be a normal emacs buffer.
 If provided, PROJECT must be an instance of `phpinspect-project'."
   (with-current-buffer buffer
     (setq-local phpinspect-current-buffer
-               (phpinspect-make-buffer :buffer buffer :-project project))
+                       (phpinspect-make-buffer :buffer buffer :-project 
project))
+    (setf (phpinspect-buffer-shadow phpinspect-current-buffer)
+          (phpinspect-make-shadow phpinspect-current-buffer))
+
     (add-hook 'after-change-functions #'phpinspect-after-change-function nil t)
+    (add-hook 'kill-buffer-hook #'phpinspect-buffer-kill)
 
     phpinspect-current-buffer))
 
+
+;;;;;;;;;; SHADOWING ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
+
+(defvar phpinspect--shadow-counter 0)
+
+(defvar phpinspect-shadow-pause-time 0.05)
+
+(define-error 'phpinspect-wakeup-shadow
+              "This error is used to wakeup the shadow thread.")
+
+(cl-defstruct (phpinspect-shadow (:constructor 
phpinspect-make-shadow-generated)
+                                 (:conc-name phpi-shadow-))
+  (synced-p t :type boolean)
+  (origin nil :type phpinspect-buffer)
+  (buffer nil :type buffer)
+  (queue nil :type phpinspect--queue)
+  (thread nil :type thread)
+  (id nil :type integer))
+
+(defun phpi-shadow-wakeup-thread (shadow)
+  (thread-signal (phpi-shadow-thread shadow) 'phpinspect-wakeup-shadow nil))
+
+(defun phpi-shadow-thread-check-pause ()
+  (ignore-error phpinspect-wakeup-shadow
+    (if (or (phpinspect--input-pending-p)
+            quit-flag)
+        (let* ((mx (make-mutex))
+               (continue (make-condition-variable mx)))
+          (phpinspect-thread-pause phpinspect-shadow-pause-time mx continue))
+      (thread-yield))))
+
+(defun phpi-shadow-make-queue-subscription (shadow)
+  (lambda ()
+    (setf (phpi-shadow-synced-p shadow) nil)
+    (phpi-shadow-wakeup-thread shadow)))
+
+(defun phpi-shadow--thread-make-parser-interrupt-predicate ()
+  (lambda () (phpi-shadow-thread-check-pause) nil))
+
+(defun phpi-shadow-process-change (shadow change)
+  (with-current-buffer (phpi-shadow-buffer shadow)
+    (phpi-change-apply change (current-buffer))
+
+    ;;(message "parsing")
+
+    (let* ((buffer (phpi-shadow-origin shadow))
+           (pctx (phpinspect-make-pctx
+                  :incremental t
+                  :previous-bmap (phpinspect-buffer-map buffer)
+                  :bmap (phpinspect-make-bmap)
+                  :change change
+                  :interrupt-predicate 
(phpi-shadow--thread-make-parser-interrupt-predicate))))
+
+      (let (result)
+        ;; Parse new content
+        (with-current-buffer (phpi-shadow-buffer shadow)
+          (phpinspect-with-parse-context pctx
+            (setq result (phpinspect-parse-current-buffer))))
+
+        (setf (phpinspect-buffer-tree buffer) result))
+
+        ;;(message "setting map")
+        (phpinspect-buffer--set-map
+         buffer (phpinspect-pctx-bmap pctx) (phpinspect-pctx-previous-bmap 
pctx)))))
+
+(defun phpi-shadow-parse-fresh (shadow)
+  (with-current-buffer (phpi-shadow-buffer shadow)
+    ;;(message "parsing fresh")
+    (let* ((buffer (phpi-shadow-origin shadow))
+           (pctx (phpinspect-make-pctx
+                  :incremental t
+                  :bmap (phpinspect-make-bmap)
+                  :interrupt-predicate 
(phpi-shadow--thread-make-parser-interrupt-predicate))))
+
+      (let (result)
+        ;; Parse new content
+        (with-current-buffer (phpi-shadow-buffer shadow)
+          (phpinspect-with-parse-context pctx
+            (setq result (phpinspect-parse-current-buffer))))
+
+        (setf (phpinspect-buffer-tree buffer) result)
+
+        ;;(message "Result: %s" result)
+
+        (phpinspect-buffer--set-map
+         buffer (phpinspect-pctx-bmap pctx) nil)))))
+
+(defun phpinspect-visit-shadow-buffer (buffer)
+  (interactive (list (or phpinspect-current-buffer
+                         (error "Not a phpinspect buffer"))))
+  (pop-to-buffer (phpi-shadow-buffer (phpinspect-buffer-shadow buffer))))
+
+(defun phpi-shadow-make-thread-function (shadow)
+  ;;(message "making thread function")
+  (lambda ()
+    (let ((inhibit-quit t))
+      ;;(message "Func starting")
+      (while t
+        ;;(message "shadow thread working")
+        (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue shadow))))
+            (progn
+              (pcase task
+                ((pred phpinspect-change-p)
+                 (phpi-shadow-process-change shadow task))
+                ('parse-fresh
+                 (phpi-shadow-parse-fresh shadow))
+                (_
+                 (phpinspect-message
+                  "Shadow thread received unsupported task type: %s"
+                  (type-of task))))
+
+              ;; Rest after task completion
+              (phpi-shadow-thread-check-pause))
+
+          ;; No work to do, join main thread
+          (setf (phpi-shadow-synced-p shadow) t)
+          (ignore-error 'phpinspect-wakeup-shadow
+            (thread-join main-thread)))))))
+
+(defun phpi-shadow-thread-live-p (shadow)
+  (thread-live-p (phpi-shadow-thread shadow)))
+
+(defun phpi-shadow-await-synced (shadow &optional allow-interrupt)
+  (cl-assert (phpi-shadow-thread-live-p shadow))
+
+  (while (not (phpi-shadow-synced-p shadow))
+    (when (and allow-interrupt (phpinspect--input-pending-p))
+      (throw 'phpinspect-interrupted nil))
+
+    (unless (phpi-shadow-thread-live-p shadow)
+      (error "Shadow thread exited: %s"
+             (thread-last-error (phpi-shadow-thread shadow))))
+
+    (sleep-for 0.005)))
+
+(defun phpi-shadow-make-thread (shadow)
+  (make-thread
+   (phpi-shadow-make-thread-function shadow)
+   (format " **phpinspect-shadow-thread**<%d>" (phpi-shadow-id shadow))))
+
+(defun phpinspect-make-shadow (origin)
+  (cl-assert (phpinspect-buffer-p origin))
+
+  (let* ((id (cl-incf phpinspect--shadow-counter))
+         (shadow (phpinspect-make-shadow-generated
+                  :origin origin
+                  :buffer (generate-new-buffer
+                           (format " **phpinspect-shadow**<%d>" id))
+                  :id id)))
+
+    ;; Copy buffer contents
+    (with-current-buffer (phpi-shadow-buffer shadow)
+      (insert (phpinspect-with-current-buffer origin (buffer-string))))
+
+
+
+    (setf (phpi-shadow-queue shadow)
+          (phpinspect-make-queue (phpi-shadow-make-queue-subscription shadow))
+
+          (phpi-shadow-thread shadow)
+          (phpi-shadow-make-thread shadow))
+
+    ;(phpi-shadow-enqueue-task shadow 'parse-fresh)
+
+    shadow))
+
+(defun phpi-shadow-enqueue-task (shadow task)
+  (phpinspect-queue-enqueue (phpi-shadow-queue shadow) task))
+
 (provide 'phpinspect-buffer)
diff --git a/phpinspect-change.el b/phpinspect-change.el
new file mode 100644
index 0000000000..499d0fa35d
--- /dev/null
+++ b/phpinspect-change.el
@@ -0,0 +1,178 @@
+;;; phpinspect-change.el --- A structure that represents buffer changes  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Hugo Thunnissen
+
+;; Author: Hugo Thunnissen <de...@hugot.nl>
+;; Keywords: languages
+
+;; 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)
+
+(cl-defstruct (phpinspect-change (:constructor phpinspect-make-change)
+                                 (:conc-name phpi-change-))
+  (synced-p t :type boolean)
+  (start nil :type integer)
+  (end nil :type integer)
+  (prev-length nil :type integer)
+  (content nil :type string))
+
+(defun phpi-change-apply (change buffer)
+  (let ((start (phpi-change-start change))
+        (pre-change-length (phpi-change-prev-length change))
+        (content (phpi-change-content change)))
+
+    ;;(message "Applying change: %s" content)
+
+    (with-current-buffer buffer
+      (delete-region start (+ start pre-change-length))
+      (goto-char start)
+      (when content
+        (insert content)))))
+
+(defun phpi-change-create (buffer start end pre-change-length)
+  (with-current-buffer buffer
+    (let (content)
+      (when (not (= start end))
+        (setq content (buffer-substring-no-properties start end)))
+
+      (phpinspect-make-change
+       :start start
+       :end end
+       :prev-length pre-change-length
+       :content content))))
+
+(defun phpi-change-prev-end (change)
+  (+ (phpi-change-start change) (phpi-change-prev-length change)))
+
+(defun phpi-change-cur-length (change)
+  (- (phpi-change-end change) (phpi-change-start change)))
+
+(defun phpi-change-delta (change)
+  (- (phpi-change-cur-length change) (phpi-change-prev-length change)))
+
+(defun phpi-calculate-point (delta prev-end point)
+  ;;(message "delta %s, prev-end %s, point %s" delta prev-end point)
+  (let ((delta-region (if (> 0 delta)
+                          (phpinspect-make-region (+ prev-end delta) prev-end)
+                        (phpinspect-make-region prev-end (+ prev-end delta)))))
+    ;;(message "Delta region: %s " delta-region)
+    (if (> 0 delta)
+        (if (> prev-end point)
+            (if (phpinspect-region-overlaps-point delta-region point)
+                (phpinspect-region-start delta-region)
+              point)
+          (if (< prev-end point)
+              (+ point delta)
+            point))
+      (if (< 0 delta)
+          (if (< prev-end point)
+              (if (phpinspect-region-overlaps-point delta-region point)
+                  (progn
+                    (message "WTF OVERLAP")
+                    (+ (phpinspect-region-end delta-region)
+                       (- (phpinspect-region-end delta-region) point)))
+                (message "+ pooint delta")
+                (+ point delta))
+            (message "NOPERS")
+            point)
+        (message "WAT")
+        point))))
+
+(defun phpi-change-post-position (change point)
+  (phpi-calculate-point
+   (phpi-change-delta change)
+   (phpi-change-prev-end change)
+   point))
+  ;; (if (> point (phpi-change-end change))
+  ;;     (if (> (- point (phpi-change-prev-end change))
+  ;;            (phpi-change-delta change))
+  ;;         (- point (phpi-change-delta change))
+  ;;       (phpi-change-end change))
+;;   point))
+
+;; (defun phpi-calculate-pre-point (delta end point)
+;;   (message "delta %s, prev-end %s, point %s" delta end point)
+;;   (let ((delta-region (if (< 0 delta)
+;;                           (phpinspect-make-region (- end delta) end)
+;;                         (phpinspect-make-region end (- end delta)))))
+;;     (message "delta region: %s" delta-region)
+    ;; (if (< ( delta-region) point)
+    ;;     (- point delta)
+
+
+    ;; (if (< (phpinspect-region-end delta-region) point)
+    ;;     (- point delta)
+
+
+    ;;         (if (< 0 delta)
+    ;;             (phpinspect-region-start delta-region)
+    ;;           point))
+    ;;     point))))
+
+    ;; (if (> 0 delta)
+    ;;     (if (< (phpinspect-region-end delta-region) point)
+    ;;         (- point delta)
+    ;;       (if (phpinspect-region-overlaps-point delta-region point)
+    ;;           end
+    ;;         point))
+    ;;   (if (< 0 delta)
+    ;;       (if (< (phpinspect-region-end delta-region) point)
+    ;;           (- point delta)
+    ;;         (if (< end point)
+    ;;             end
+    ;;           point))
+    ;;     point))))
+
+(defun phpi-change-pre-position (change point)
+;;  (message "Start %d" point)
+  (if (<= (phpi-change-end change) point)
+      (- point (phpi-change-delta change))
+    (if (and (< 0 (phpi-change-delta change))
+             (> (phpi-change-end change) point)
+             (<= (phpi-change-prev-end change) point))
+        (progn ;;(message "JAAAA: %s voor %s" (phpi-change-prev-end change) 
point)
+               (phpi-change-prev-end change))
+      point)))
+
+(defun phpi-change-overlaps-point (change point)
+  (and (> (phpi-change-end change) point)
+       (<= (phpi-change-start change) point)))
+
+(defun phpi-change-overlaps-pre-point (change point)
+  (and (> (phpi-change-prev-end change) point)
+       (<= (phpi-change-start change) point)))
+
+(defun phpi-change-tainted-token-p (change meta)
+  (or (phpi-change-overlaps-pre-point change (phpinspect-meta-start meta))
+      (phpi-change-overlaps-pre-point change (phpinspect-meta-end meta))
+      (phpinspect-meta-overlaps-point meta (phpi-change-start change))
+      (phpinspect-meta-overlaps-point meta (phpi-change-prev-end change))))
+
+(defun phpi-change-tainted-region-p (change start end)
+  (or (phpi-change-overlaps-pre-point change start)
+      (phpi-change-overlaps-pre-point change end)
+      (and (> end (phpi-change-start change))
+           (<= start (phpi-change-start change)))
+      (and (> end (phpi-change-prev-end change))
+           (<= start (phpi-change-prev-end change)))))
+
+(provide 'phpinspect-change)
+;;; phpinspect-change.el ends here
diff --git a/phpinspect-completion.el b/phpinspect-completion.el
index 22e5f5c5ca..08f633edf8 100644
--- a/phpinspect-completion.el
+++ b/phpinspect-completion.el
@@ -285,20 +285,18 @@ Completing words in a comment for example, is usually not 
useful."
   (and last-query
        (eq (phpinspect-completion-query-buffer last-query)
            (phpinspect-completion-query-buffer query))
-       (let ((taints (phpinspect-edtrack-taint-pool
-                      (phpinspect-buffer-edit-tracker
-                       (phpinspect-completion-query-buffer query))))
+       (let ((change (phpinspect-buffer-last-change
+                       (phpinspect-completion-query-buffer query)))
              (atoms-start
               (phpinspect-with-current-buffer 
(phpinspect-completion-query-buffer query)
                 (phpinspect--find-atoms-start 
(phpinspect-completion-query-point query)))))
-         (or (length= taints 0)
-             (and (length= taints 1)
-                  (<= atoms-start
+         (or (not change)
+             (and (<= atoms-start
                       (phpinspect-completion-query-point last-query))
-                  (>= (phpinspect-taint-end (car taints))
+                  (>= (phpi-change-end change)
                       (phpinspect-completion-query-point last-query))
                   (>= 1 (abs (- (phpinspect-completion-query-point query)
-                                (phpinspect-taint-end (car taints))))))))))
+                                (phpi-change-end change)))))))))
 
 (cl-defstruct (phpinspect--completion-parameters
                (:constructor phpinspect--make-completion-parameters))
@@ -474,7 +472,7 @@ Returns list of `phpinspect--completion'."
 
 
 (defun phpinspect-complete-at-point ()
-  (catch 'phpinspect-parse-interrupted
+  (catch 'phpinspect-interrupted
     (let ((comp-list (phpinspect-completion-query-execute 
(phpinspect--get-completion-query)))
           strings)
       (phpinspect--log "Completion list: %s" comp-list)
diff --git a/phpinspect-diagnose.el b/phpinspect-diagnose.el
new file mode 100644
index 0000000000..b49f80eb9a
--- /dev/null
+++ b/phpinspect-diagnose.el
@@ -0,0 +1,87 @@
+;;; phpinspect-diagnose.el --- Diagnose problems in phpinspect's internal 
state  -*- lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Hugo Thunnissen
+
+;; Author: Hugo Thunnissen <h...@yournextconcepts.com>
+;; Keywords: languages
+
+;; 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-buffer)
+(require 'phpinspect-meta)
+
+;;;###autoload
+(defun phpinspect-buffer-diagnose-tree (buffer)
+  "Diagnose problems in BUFFER's token (metadata) tree."
+  (interactive (list phpinspect-current-buffer))
+  (cl-assert (phpinspect-buffer-p buffer))
+
+  (phpinspect-meta-diagnose-parent-child-relations
+   (phpinspect-buffer-root-meta buffer))
+
+  (phpinspect-message "Finished diagnosis, no problems found"))
+
+(defun phpinspect-meta-children-as-string-safe (meta)
+  (let (result)
+    (phpinspect-splayt-traverse-lr (child (phpinspect-meta-children meta))
+      (if (eq child meta)
+          (push "[self]" result )
+        (push (phpinspect-meta-string-safe meta) result)))
+    (string-join result ", ")))
+
+(defun phpinspect-meta-string-safe (meta &optional start)
+  (setq start (or start 0))
+
+  (dlet ((phpinspect-meta--point-offset-base start))
+    (if meta
+        (format "[start: %d, end: %d, token (possibly incomplete): %s, 
children: %s]"
+                (phpinspect-meta-start meta)
+                (phpinspect-meta-end meta)
+                (if (phpinspect-atom-p (phpinspect-meta-token meta))
+                    (seq-subseq (phpinspect-meta-token meta) 0 2)
+                  (car (phpinspect-meta-token meta)))
+                (phpinspect-meta-children-as-string-safe meta))
+      "[nil]")))
+
+(defun phpinspect-meta-diagnose-parent-child-relations (meta)
+  "Find problems in parent-child relations of META and descendants."
+  (when meta
+    (let ((stack (list meta)))
+      (while-let ((current (pop stack)))
+        (message "checking")
+        ;; A token cannot be a parent of itself.
+        (when (eq (phpinspect-meta-parent current) current)
+          (let ((message
+                 (format "Cyclic parent-child relation detected at (current): 
%s"
+                         (phpinspect-meta-string-safe current))))
+            (error message)))
+
+        (phpinspect-splayt-traverse-lr (child (phpinspect-meta-children 
current))
+          (when (eq child current)
+            (let ((message
+                   (format "Cyclic parent-child relation detected at (child): 
%s"
+                           (phpinspect-meta-string-safe child))))
+              (error message)))
+
+
+          (push child stack))))))
+
+(provide 'phpinspect-diagnose)
+;;; phpinspect-diagnose.el ends here
diff --git a/phpinspect-eldoc.el b/phpinspect-eldoc.el
index ee81d60434..7be4f4809b 100644
--- a/phpinspect-eldoc.el
+++ b/phpinspect-eldoc.el
@@ -291,7 +291,7 @@ Ignores `eldoc-argument-case` and 
`eldoc-echo-area-use-multiline-p`.
 TODO:
  - Respect `eldoc-echo-area-use-multiline-p`
 "
-  (catch 'phpinspect-parse-interrupted
+  (catch 'phpinspect-interrupted
     (let ((resp (phpinspect-eldoc-query-execute
                  (phpinspect-make-eldoc-query
                   :buffer phpinspect-current-buffer
diff --git a/phpinspect-parse-context.el b/phpinspect-parse-context.el
index 2683211d45..ec27eab706 100644
--- a/phpinspect-parse-context.el
+++ b/phpinspect-parse-context.el
@@ -55,6 +55,7 @@ This is 2ms by default.")
    nil
    :documentation "The time at which the currently active parse cycle started.
 This slot is for private use and does not always have a value.")
+  (change nil :type phpinspect-change)
   (interrupt-predicate
    nil
    :documentation
@@ -64,20 +65,20 @@ called after each parsed token to make the final decision of
 interrupting the parser. If this function returns a non-nil
 value, the parse process is interrupted and the symbol
 `phpinspect-parse-interrupted' is signaled.")
-  (changesets
-   nil
-   :type list
-   :documentation
-   "Restore points for metadata changes executed during this
-parse. Usually populated through `phpinspect-meta-with-changeset'.")
-  (edtrack
-   nil
-   :type phpinspect-edtrack
-   :documentation
-   "When parsing incrementally, the edit tracker is used to determine
-whether a token from a previous parse (in the buffer map that is
-in the `previous-bmap' slot) can be recycled or is tainted/edited
-and should not be recycled.")
+;;   (changesets
+;;    nil
+;;    :type list
+;;    :documentation
+;;    "Restore points for metadata changes executed during this
+;; parse. Usually populated through `phpinspect-meta-with-changeset'.")
+;;   (edtrack
+;;    nil
+;;    :type phpinspect-edtrack
+;;    :documentation
+;;    "When parsing incrementally, the edit tracker is used to determine
+;; whether a token from a previous parse (in the buffer map that is
+;; in the `previous-bmap' slot) can be recycled or is tainted/edited
+;; and should not be recycled.")
   (bmap
    nil
    :type phpinspect-bmap
@@ -114,19 +115,8 @@ parsing incrementally.
 The error signal is not intercepted and will still need to be
 handled by the code using this macro."
   (declare (indent 1))
-  (let ((completed (gensym))
-        (result (gensym)))
-    `(dlet ((phpinspect-parse-context ,ctx))
-       (let ((,result)
-             (,completed))
-         (unwind-protect
-             (progn
-               (setq phpinspect-parse-context ,ctx
-                     ,result (progn ,@body)
-                     ,completed t)
-               ,result)
-           (progn
-             (unless ,completed (phpinspect-pctx-cancel ,ctx))))))))
+  `(dlet ((phpinspect-parse-context ,ctx))
+     (progn ,@body)))
 
 (defmacro phpinspect-pctx-save-whitespace (pctx &rest body)
   (declare (indent 1))
@@ -138,27 +128,27 @@ handled by the code using this macro."
              ,@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)
-  "Perform mutations on META in BODY, saving changes.
-
-Before BODY is executed, important slots of META are stored in a
-changeset object and appended to the changesets slot of the
-currently active parse context. The original state of META can be
-restored by calling `phpinspect-pctx-cancel'."
-  (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-register-changeset (pctx changeset)
+;;   (inline-quote
+;;    (progn
+;;      (push ,changeset (phpinspect-pctx-changesets ,pctx)))))
+
+;; (define-inline phpinspect-meta-with-changeset (meta &rest body)
+;;   "Perform mutations on META in BODY, saving changes.
+
+;; Before BODY is executed, important slots of META are stored in a
+;; changeset object and appended to the changesets slot of the
+;; currently active parse context. The original state of META can be
+;; restored by calling `phpinspect-pctx-cancel'."
+;;   (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)
   "Signal `phpinspect-parse-interrupted' when conditions are met.
@@ -180,7 +170,6 @@ metadata will be reverted in a call to 
`pphinspect-pctx-cancel'."
        (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)
@@ -193,17 +182,16 @@ metadata will be reverted in a call to 
`pphinspect-pctx-cancel'."
     (setf (phpinspect-pctx-whitespace-before pctx) "")
     whitespace))
 
-(defun phpinspect-pctx-cancel (pctx)
-  "Cancel PCTX, revert all changes made during its lifetime.
-
-Revert all changes made to the metadata tree while parsing
-incrementally. This function is usually called by
-`phpinspect-pctx-check-interrupt' when interrupt conditions are
-met."
-  (phpinspect--log "Cancelling parse context")
-  (dolist (changeset (phpinspect-pctx-changesets pctx))
-    (phpinspect-changeset-revert changeset))
-  (setf (phpinspect-pctx-changesets pctx) nil))
+;; (defun phpinspect-pctx-cancel (pctx)
+;;   "Cancel PCTX, revert all changes made during its lifetime.
+
+;; Revert all changes made to the metadata tree while parsing
+;; incrementally. This function is usually called by
+;; `phpinspect-pctx-check-interrupt' when interrupt conditions are
+;; met."
+;;   (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 59e35c697a..cfdcdae59b 100644
--- a/phpinspect-parser.el
+++ b/phpinspect-parser.el
@@ -24,7 +24,7 @@
 ;;; Code:
 
 (require 'cl-lib)
-(require 'phpinspect-edtrack)
+(require 'phpinspect-change)
 (require 'phpinspect-bmap)
 (require 'phpinspect-meta)
 (require 'phpinspect-parse-context)
@@ -136,7 +136,7 @@ text at point and returns the resulting token."
        (put (quote ,inline-name) 'definition-name (quote ,name))
        (put (quote ,regexp-inline-name) 'definition-name (quote ,name)))))
 
-(defun phpinspect--recycle-token (context taint-iterator point original-point 
token-meta tokens-rear &optional delimiter-predicate)
+(defun phpinspect--recycle-token (context change point original-point 
token-meta tokens-rear &optional delimiter-predicate continue-condition)
   "Attempt to re-use TOKEN-META and any of its eligible righthand siblings."
   (declare (speed 3))
   ;;(message "Recycle token called at %d, %s" (point) (phpinspect-meta-token 
token-meta))
@@ -154,7 +154,9 @@ text at point and returns the resulting token."
           ;;   (setq token-meta (phpinspect-meta-find-child-starting-at 
token-meta original-point)))
 
           (if (or (not token-meta)
-                  (phpinspect-taint-iterator-token-is-tainted-p taint-iterator 
token-meta))
+                  (phpinspect-root-p (phpinspect-meta-token token-meta))
+                  (phpinspect-incomplete-token-p (phpinspect-meta-token 
token-meta))
+                  (phpi-change-tainted-token-p change token-meta))
               (progn
                 ;; If the first passed token is tainted. Return tainted symbol 
to
                 ;; signal failure to parser loop. Otherwise return re-used 
tokens.
@@ -183,20 +185,25 @@ text at point and returns the resulting token."
               ;; right-sibling
               (dlet ((phpinspect-meta--point-offset-base nil))
                 (if-let (((not (and delimiter-predicate (funcall 
delimiter-predicate token))))
+
                          (right-sibling right-sibling)
                          ((setq phpinspect-meta--point-offset-base
                                 (+ original-point (- 
(phpinspect-meta-parent-offset right-sibling)
                                                      parent-offset))))
-                         ((not (phpinspect-taint-iterator-region-is-tainted-p
-                                taint-iterator current-end-position (+ delta 
(phpinspect-meta-start right-sibling))))))
-                    (progn
+                         ((not (phpi-change-tainted-region-p
+                                change current-end-position (+ delta 
(phpinspect-meta-start right-sibling)))))
+                         ((progn
+                            (goto-char (+ delta (phpinspect-meta-start 
right-sibling)))
+                            (phpinspect-pctx-register-whitespace context 
(phpinspect-meta-whitespace-before right-sibling))
+
+                            (if continue-condition (funcall 
continue-condition) t))))
                       ;;(message "using sibling %s" (phpinspect-meta-string 
right-sibling))
                       ;; There was a right sibling and it is eligible for
                       ;; re-use. Set token-meta and "recurse".
                       (setq first-iteration nil
                             token-meta right-sibling
                             point (+ delta (phpinspect-meta-start 
right-sibling))
-                            original-point (phpinspect-meta-start 
right-sibling)))
+                            original-point (phpinspect-meta-start 
right-sibling))
 
                   ;; No eligible right sibling, break.
                   (throw 'phpinspect--return tokens-rear))))))))))
@@ -261,8 +268,7 @@ is able to reuse an already parsed tree."
                 (tokens-rear tokens)
                 (root-start (point))
                 (previous-bmap (phpinspect-pctx-previous-bmap context))
-                (edtrack (phpinspect-pctx-edtrack context))
-                (taint-iterator (when edtrack 
(phpinspect-edtrack-make-taint-iterator edtrack)))
+                (change (phpinspect-pctx-change context))
                 (check-interrupt (phpinspect-pctx-interrupt-predicate context))
 
                 ;; Loop variables
@@ -291,34 +297,37 @@ is able to reuse an already parsed tree."
                ;;(message "Start: %d" start-position)
 
                (cond ((and-let*
-                          ((edtrack)
+                          ((change)
                            (previous-bmap)
                            (result
                             ;; Look for an already parsted token at POINT to
                             ;; adopt into new tree.
                             (or (phpinspect--recycle-token
                                  context
-                                 taint-iterator
+                                 change
                                  start-position
                                  (setq original-position
-                                       
(phpinspect-edtrack-original-position-at-point edtrack start-position))
+                                       (phpi-change-pre-position change 
start-position))
                                  (phpinspect-bmap-token-starting-at 
previous-bmap original-position)
                                  tokens-rear
-                                 ,(if delimiter-predicate `(quote 
,delimiter-predicate) 'nil))
+                                 ,(if delimiter-predicate `(quote 
,delimiter-predicate) 'nil)
+                                 continue-condition)
                                 ;; There is no token at POINT exactly. Attempt
                                 ;; to find any other adoptable token after
                                 ;; POINT.
-                                (when-let
-                                    ((token-after 
(phpinspect-bmap-token-starting-after previous-bmap original-position))
-                                     (start (phpinspect-meta-start 
token-after))
-                                     ((not 
(phpinspect-taint-iterator-region-is-tainted-p
-                                            taint-iterator original-position 
(+ start (phpinspect-meta-width token-after)))))
-                                     ;;(current-start (+ start-position (- 
start original-position))))
-                                     (current-start 
(phpinspect-edtrack-current-position-at-point edtrack start)))
-                                  ;; (message "YAAS: (%d,%d) '%s'" start 
current-start (buffer-substring current-start (point-max)))
-                                  (phpinspect--recycle-token
-                                   context taint-iterator current-start start 
token-after tokens-rear
-                                   ,(if delimiter-predicate `(quote 
,delimiter-predicate) 'nil)))))
+                                ;; (when-let
+                                ;;     ((token-after 
(phpinspect-bmap-token-starting-after previous-bmap original-position))
+                                ;;      (start (phpinspect-meta-start 
token-after))
+                                ;;      ((not (phpi-change-tainted-region-p
+                                ;;             change original-position (+ 
start (phpinspect-meta-width token-after)))))
+                                ;;      ;;(current-start (+ start-position (- 
start original-position))))
+                                ;;      (current-start (+ start (- 
original-position start-position))))
+                                ;;   ;; (message "YAAS: (%d,%d) '%s'" start 
current-start (buffer-substring current-start (point-max)))
+                                ;;   (phpinspect--recycle-token
+                                ;;    context change current-start start 
token-after tokens-rear
+                                ;;    ,(if delimiter-predicate `(quote 
,delimiter-predicate) 'nil)))
+
+                                ))
 
                            ;; `phpinspect--recycle-token' will return the 
symbol
                            ;; 'tainted' when the token that it tried to reuse
diff --git a/phpinspect-resolvecontext.el b/phpinspect-resolvecontext.el
index 22db4ac63c..fc3ade61df 100644
--- a/phpinspect-resolvecontext.el
+++ b/phpinspect-resolvecontext.el
@@ -46,8 +46,9 @@
     (inline-quote
      (or (phpinspect-return-p ,token)
          (phpinspect-end-of-statement-p ,token)
+         (phpinspect-fat-arrow-p ,token)
          (phpinspect-string-concatenator-p ,token)
-        (phpinspect-use-p ,token)
+            (phpinspect-use-p ,token)
          (phpinspect-function-p ,token)))))
 
 (cl-defstruct (phpinspect--resolvecontext
diff --git a/phpinspect-shadow.el b/phpinspect-shadow.el
new file mode 100644
index 0000000000..dd33e1f5b5
--- /dev/null
+++ b/phpinspect-shadow.el
@@ -0,0 +1,212 @@
+;;; phpinspect-shadow.el --- Shadow buffers for phpinspect buffers  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Hugo Thunnissen
+
+;; Author: Hugo Thunnissen <de...@hugot.nl>
+;; Keywords: languages
+
+;; 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 'cl-macs)
+
+;; (defvar phpinspect--shadow-counter 0)
+
+;; (defvar phpinspect-shadow-pause-time 0.05)
+
+;; (define-error 'phpinspect-wakeup-shadow
+;;   "This error is used to wakeup the shadow thread.")
+
+
+;; (cl-defstruct (phpinspect-shadow (:constructor 
phpinspect-make-shadow-generated)
+;;                                  (:conc-name phpi-shadow-))
+;;   (origin nil :type phpinspect-buffer)
+;;   (buffer nil :type buffer)
+;;   (queue nil :type phpinspect--queue)
+;;   (thread nil :type thread)
+;;   (id nil :type integer))
+
+;; (defun phpi-shadow-wakeup-thread (shadow)
+;;   (thread-signal (phpi-shadow-thread shadow) 'phpi-wakeup-shadow nil))
+
+;; (defun phpi-shadow-thread-check-pause ()
+;;   (ignore-error phpinspect-wakeup-shadow
+;;     (if (or (phpinspect--input-pending-p)
+;;             quit-flag)
+;;         (let* ((mx (make-mutex))
+;;                (continue (make-condition-variable mx)))
+;;           (phpinspect-thread-pause phpinspect-shadow-pause-time mx 
continue))
+;;       (thread-yield))))
+
+;; (defun phpi-shadow-make-queue-subscription (shadow)
+;;   (lambda ()
+;;     (setf (phpi-shadow-synced-p shadow) nil)
+;;     (phpi-shadow-wakeup-thread shadow)))
+
+;; (defun phpi-shadow--thread-make-parser-interrupt-predicate ()
+;;   (lambda () (phpi-shadow-thread-check-pause) nil))
+
+;; (defun phpi-shadow-process-change (shadow change)
+;;   (with-current-buffer (phpi-shadow-buffer shadow)
+;;     (phpi-change-apply change)
+
+;;     (let ((buffer (phpi-shadow-origin shadow))
+;;           (pctx (phpinspect-make-pctx
+;;                  :incremental t
+;;                  :previous-bmap (phpinspect-buffer-map buffer)
+;;                  :bmap (phpinspect-make-bmap)
+;;                  :change change
+;;                  :interrupt-predicate 
(phpi-shadow--thread-make-parser-interrupt-predicate))))
+
+;;       ;; Parse new content
+;;       (with-current-buffer (phpi-shadow-buffer shadow)
+;;         (phpinspect-with-parse-context pctx
+;;           (setf (phpinspect-buffer-tree buffer) 
(phpinspect-parse-current-buffer))))
+
+;;       (phpinspect-buffer--set-map buffer (phpinspect-pctx-bmap pctx)))))
+
+;; (defun phpi-shadow-make-thread-function (shadow)
+;;   (lambda ()
+;;     (let ((inhibit-quit t))
+;;       (while t
+;;         (if-let ((task (phpinspect-queue-dequeue (phpi-shadow-queue 
shadow))))
+;;             (progn
+;;               (pcase task
+;;                 ((pred phpinspect-change-p)
+;;                  (phpi-shadow-process-change shadow task))
+;;                 (_
+;;                  (phpinspect-message
+;;                   "Shadow thread received unsupported task type: %s"
+;;                   (type-of task))))
+
+;;               ;; Rest after task completion
+;;               (phpi-shadow-thread-check-pause))
+
+;;           ;; No work to do, join main thread
+;;           (setf (phpi-shadow-synced-p shadow) t)
+;;           (ignore-error 'phpinspect-wakeup-shadow
+;;             (thread-join main-thread)))))))
+
+;; (defun phpi-shadow-thead-live-p (shadow)
+;;   (thread-live-p (phpi-shadow-thread shadow)))
+
+;; (defun phpi-shadow-await-synced (shadow &optional allow-interrupt)
+;;   (cl-assert (phpi-shadow-thread-live-p shadow))
+
+;;   (while (not (phpi-shadow-synced-p shadow))
+;;     (when (and allow-interupt (phpinspect--input-pending-p))
+;;       (throw 'phpinspect-interrupted nil))
+
+;;     (sleep-for 0.005)))
+
+;; (defun phpi-shadow-make-thread (shadow)
+;;   (make-thread
+;;    (phpi-shadow-make-thread-function shadow)
+;;    (format " **phpinspect-shadow-thread**<%d>" (phpi-shadow-id shadow))))
+
+;; (defun phpinspect-make-shadow (origin)
+;;   (let* ((id (cl-incf phpinspect--shadow-counter))
+;;          (shadow (phpinspect-make-shadow-generated
+;;                   :origin origin
+;;                   :buffer (generate-new-buffer
+;;                            (format " **phpinspect-shadow**<%d>" id))
+;;                   :id id)))
+
+;;     ;; Copy buffer contents
+;;     (with-current-buffer (phpi-shadow-buffer shadow)
+;;       (insert (phpinspect-with-current-buffer origin (buffer-string))))
+
+;;     (setf (phpi-shadow-queue shadow)
+;;           (phpinspect-make-queue (phpi-shadow-make-queue-subscription 
shadow))
+
+;;           (phpi-shadow-thread shadow)
+;;           (phpi-shadow-make-thread shadow))))
+
+;; (defun phpi-shadow-register-change (shadow change)
+;;   (phpinspect-queue-enqueue (phpi-shadow-queue  shadow) change))
+
+;; (cl-defstruct (phpinspect-change (:constructor phpinspect-make-change)
+;;                                  (:conc-name phpi-change-))
+;;   (synced-p t :type boolean)
+;;   (start nil :type integer)
+;;   (end nil :type integer)
+;;   (prev-length nil :type integer)
+;;   (content nil :type string))
+
+;; (defun phpi-change-apply (change buffer)
+;;   (let ((start (phpi-change-start change))
+;;         (end (phpi-change-end change))
+;;         (pre-change-length (phpi-change-prev-length change))
+;;         (content (phpi-change-content change)))
+
+;;     (with-current-buffer buffer
+;;       (delete-region start (+ start pre-change-length))
+;;       (goto-char start)
+;;       (when content
+;;         (insert content)))))
+
+;; (defun phpi-change-create (buffer start end pre-change-length)
+;;   (with-current-buffer buffer
+;;     (let (content)
+;;       (when (not (= start end))
+;;         (setq content (buffer-substring-no-properties start end)))
+
+;;       (phpinspect-make-change
+;;        :start start
+;;        :end end
+;;        :prev-length pre-change-length
+;;        :content content))))
+
+;; (defun phpi-change-prev-end (change)
+;;   (+ (phpi-change-start change) (phpi-change-prev-length change)))
+
+;; (defun phpi-change-cur-length (change)
+;;   (- (phpi-change-end change) (phpi-change-start change)))
+
+;; (defun phpi-change-delta (change)
+;;   (- (phpi-change-prev-length change) (phpi-change-cur-length change)))
+
+;; (defun phpi-change-post-position (change point)
+;;   (if (> point (phpi-change-end change))
+;;         (if (> (phpi-change-prev-end change) point)
+;;             (phpi-change-end change)
+;;           (- point (phpi-change-delta change)))
+;;     point))
+
+;; (defun phpi-change-pre-position (change point)
+;;   (if (> point (phpi-change-end change))
+;;       (+ (phpi-change-delta change) point)
+;;     point))
+
+;; (defun phpi-change-overlaps-point (change point)
+;;   (and (> (phpi-change-end change) point)
+;;        (<= (phpi-change-start change) point)))
+
+;; (defun phpi-change-overlaps-pre-point (change point)
+;;   (and (> (phpi-change-prev-end change) point)
+;;        (<= (phpi-change-start change) point)))
+
+;; (defun phpi-change-tainted-token-p (change meta)
+;;   (or (phpi-change-overlaps-pre-point change (phpinspect-meta-start meta))
+;;       (phpi-change-overlaps-pre-point change (phpinspect-meta-end meta))
+;;       (phpinspect-meta-overlaps-point meta (phpi-change-start change))
+;;       (phpinspect-meta-overlaps-point meta (phpi-change-prev-end change))))
+
+(provide 'phpinspect-shadow)
+;;; phpinspect-shadow.el ends here
diff --git a/phpinspect-token-predicates.el b/phpinspect-token-predicates.el
index f512327e9f..b21cbfeeef 100644
--- a/phpinspect-token-predicates.el
+++ b/phpinspect-token-predicates.el
@@ -39,6 +39,9 @@ Type can be any of the token types returned by
 (defun phpinspect-string-concatenator-p (token)
   (phpinspect-token-type-p token :string-concatenator))
 
+(defun phpinspect-string-p (token)
+  (phpinspect-token-type-p token :string))
+
 (define-inline phpinspect-static-attrib-p (token)
   (inline-quote
    (phpinspect-token-type-p ,token :static-attrib)))
diff --git a/phpinspect.el b/phpinspect.el
index 31980118c1..8068b413ee 100644
--- a/phpinspect.el
+++ b/phpinspect.el
@@ -131,15 +131,12 @@
   (when (and phpinspect-load-stubs (not phpinspect-stub-cache))
     (phpinspect-load-stub-index))
 
-  (setq phpinspect-current-buffer
-        (phpinspect-make-buffer :buffer (current-buffer)))
+  (phpinspect-claim-buffer (current-buffer))
 
   (phpinspect-register-current-buffer
    (lambda () (phpinspect-buffer-reset phpinspect-current-buffer)))
   (add-hook 'kill-buffer-hook #'phpinspect-unregister-current-buffer)
 
-  (add-hook 'after-change-functions #'phpinspect-after-change-function)
-
   (when (featurep 'company)
     (make-local-variable 'company-backends)
     (add-to-list 'company-backends #'phpinspect-company-backend))
@@ -353,7 +350,7 @@ Example configuration for `company-mode':
                           arg)))
       (insert "(")))
    ((eq command 'candidates)
-    (catch 'phpinspect-parse-interrupted
+    (catch 'phpinspect-interrupted
       (let ((completion-list (phpinspect--suggest-at-point))
             (candidates))
 
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 28551780a4..7de760b468 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -36,8 +36,8 @@
     (with-temp-buffer
       (insert-file-contents (concat phpinspect-test-php-file-directory 
"/NamespacedClass.php"))
       (setq phpinspect-current-buffer
-            (phpinspect-make-buffer :buffer (current-buffer)))
-      (setq parsed (phpinspect-buffer-parse phpinspect-current-buffer))
+            (phpinspect-claim-buffer (current-buffer)))
+      (setq parsed (phpinspect-buffer-parse phpinspect-current-buffer 
'no-interrupt))
 
       (let* ((class (seq-find #'phpinspect-class-p
                               (seq-find #'phpinspect-namespace-p parsed)))
@@ -98,8 +98,8 @@
 
 (ert-deftest phpinspect-buffer-parse-incrementally ()
   (let* ((document (phpinspect-make-document))
-         (buffer (phpinspect-make-buffer
-                  :buffer (phpinspect-document-buffer document)))
+         (buffer (phpinspect-claim-buffer
+                  (phpinspect-document-buffer document)))
          (parsed))
     ;; TODO: write tests for more complicated cases (multiple edits, etc.)
     (phpinspect-document-set-contents document "<?php function Bello() { echo 
'Hello World!'; if ($name) { echo 'Hello ' . $name . '!';} }")
@@ -155,7 +155,7 @@
 
 (ert-deftest phpinspect-buffer-parse-incrementally-position-change ()
   (with-temp-buffer
-    (let ((buffer (phpinspect-make-buffer :buffer (current-buffer))))
+    (let ((buffer (phpinspect-claim-buffer (current-buffer))))
       (insert "<?php
 declare(strict_types=1);
 
@@ -166,12 +166,6 @@ class AccountStatisticsController {
     function __construct(){}
 }")
 
-      (setq-local phpinspect-test-buffer t)
-      (add-to-list 'after-change-functions
-                   (lambda (start end pre-change-length)
-                     (when (boundp 'phpinspect-test-buffer)
-                       (phpinspect-buffer-register-edit buffer start end 
pre-change-length))))
-
       (let* ((bmap (phpinspect-buffer-parse-map buffer))
              (class-location 67)
              (class (phpinspect-bmap-token-starting-at bmap class-location)))
@@ -196,6 +190,7 @@ class AccountStatisticsController {
           (setq tokens-enclosing (phpinspect-bmap-tokens-overlapping bmap 
class-location))
           (setq class (seq-find (lambda (meta) (phpinspect-class-p 
(phpinspect-meta-token meta)))
                                 tokens-enclosing))
+
           (should  class)
           (should (= class-location (phpinspect-meta-start class)))
           (should (phpinspect-class-p (phpinspect-meta-token class)))
@@ -227,8 +222,8 @@ class AccountStatisticsController {
 
 (ert-deftest phpinspect-buffer-parse-incrementally-multiedit ()
   (let* ((document (phpinspect-make-document))
-         (buffer (phpinspect-make-buffer
-                  :buffer (phpinspect-document-buffer document)))
+         (buffer (phpinspect-claim-buffer
+                  (phpinspect-document-buffer document)))
          parsed parsed-after current-tree)
 
     (phpinspect-document-set-contents
@@ -256,8 +251,7 @@ class YYY {
     (phpinspect-document-setq-local document
       phpinspect-current-buffer buffer)
     (phpinspect-with-document-buffer document
-      (setq buffer-undo-list nil)
-      (add-hook 'after-change-functions #'phpinspect-after-change-function))
+      (setq buffer-undo-list nil))
 
     (setq parsed (phpinspect-buffer-parse buffer 'no-interrupt))
 
@@ -294,8 +288,7 @@ class YYY {
 
 (ert-deftest phpinspect-buffer-parse-incrementally-class-with-method ()
   (with-temp-buffer
-  (let* ((buffer (phpinspect-make-buffer
-                  :buffer (current-buffer))))
+  (let* ((buffer (phpinspect-claim-buffer (current-buffer))))
 
 
     (setq-local phpinspect-current-buffer buffer)
@@ -314,7 +307,6 @@ class AAA {
 
 ")
 
-    (add-hook 'after-change-functions #'phpinspect-after-change-function)
     (phpinspect-buffer-parse buffer 'no-interrupt)
 
     (let ((switch t)
@@ -336,9 +328,6 @@ class AAA {
 
         (let ((token (phpinspect-bmap-last-token-before-point
                       (phpinspect-buffer-map buffer) (+ 68 delta))))
-          ;; (message "Map:")
-          ;; (dolist (meta (mapcar #'phpinspect-meta-string (sort 
(phpinspect-meta-flatten (phpinspect-buffer-root-meta buffer)) 
#'phpinspect-meta-sort-start)))
-          ;;   (message " - %s" meta))
           (should token)
           (should (phpinspect-variable-p (phpinspect-meta-token token)))
           (should (string= "banana" (cadr (phpinspect-meta-token token))))
@@ -349,8 +338,7 @@ class AAA {
 
 (ert-deftest phpinspect-buffer-parse-incrementally-use ()
   (with-temp-buffer
-  (let* ((buffer (phpinspect-make-buffer
-                  :buffer (current-buffer))))
+  (let* ((buffer (phpinspect-claim-buffer (current-buffer))))
 
 
     (setq-local phpinspect-current-buffer buffer)
@@ -367,7 +355,6 @@ use CCC;
 
 ")
 
-    (add-hook 'after-change-functions #'phpinspect-after-change-function)
     (phpinspect-buffer-parse buffer 'no-interrupt)
     (let ((switch nil)
           (delta 0))
@@ -377,15 +364,11 @@ use CCC;
             (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))))
+              (insert "hh"))
           (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)))))
+            (delete-char 2)))
 
         (setq switch (not switch))
 
@@ -532,9 +515,9 @@ public $banana; const CONSTANT = 0;
 
 (ert-deftest phpinspect-buffer-index-typehinted-class-variables ()
   (with-temp-buffer
-    (let ((buffer (phpinspect-make-buffer
-                   :buffer (current-buffer)
-                   :-project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader)))))
+    (let ((buffer (phpinspect-claim-buffer
+                   (current-buffer)
+                   (phpinspect--make-project :autoload 
(phpinspect-make-autoloader)))))
       (insert "<?php
 declare(strict_types=1);
 
@@ -600,9 +583,9 @@ class AccountStatisticsController {
 
 (ert-deftest phpinspect-buffer-parse-incrementally-unfinished-variable-scope ()
   (with-temp-buffer
-  (let* ((buffer (phpinspect-make-buffer
-                  :buffer (current-buffer)
-                  :-project (phpinspect--make-dummy-project))))
+  (let* ((buffer (phpinspect-claim-buffer
+                  (current-buffer)
+                  (phpinspect--make-dummy-project))))
 
 
     (setq-local phpinspect-current-buffer buffer)
@@ -620,7 +603,6 @@ class AAA {
 
 ")
 
-    (add-hook 'after-change-functions #'phpinspect-after-change-function)
     (phpinspect-buffer-parse buffer 'no-interrupt)
 
     ;; move to after class brace
@@ -635,7 +617,7 @@ class AAA {
 (ert-deftest phpinspect-buffer-parse-incrementally-trait-config ()
   (with-temp-buffer
     (let* ((project (phpinspect--make-dummy-composer-project-with-code))
-           (buffer (phpinspect-make-buffer :-project project :buffer 
(current-buffer))))
+           (buffer (phpinspect-claim-buffer (current-buffer) project)))
 
       (insert "<?php
 
@@ -651,7 +633,6 @@ class Bar {
                project (phpinspect--make-type :name "\\App\\Foo") 'no-enqueue))
 
       (setq-local phpinspect-current-buffer buffer)
-      (add-hook 'after-change-functions #'phpinspect-after-change-function)
       (phpinspect-buffer-parse buffer 'no-interrupt)
       (phpinspect-buffer-update-project-index buffer)
 
@@ -682,12 +663,11 @@ class Bar {
 (ert-deftest phpinspect-buffer-parse-incrementally-class-block-scope ()
   (with-temp-buffer
     (let* ((project (phpinspect--make-dummy-composer-project-with-code))
-           (buffer (phpinspect-make-buffer :-project project :buffer 
(current-buffer))))
+           (buffer (phpinspect-claim-buffer (current-buffer) project)))
 
       (insert "<?php class A { public function A() {} }")
 
       (setq-local phpinspect-current-buffer buffer)
-      (add-hook 'after-change-functions #'phpinspect-after-change-function)
       (let ((expected `(:root
                        (:class
                         (:class-declaration (:word "A"))
@@ -715,13 +695,14 @@ class Bar {
            (backward-char)
            (insert " ")
            (setq result (phpinspect-buffer-parse buffer 'no-interrupt))
+
            (should result)
            (should (equal expected result))))))
 
 (ert-deftest phpinspect-buffer-index-update-method-name ()
     (with-temp-buffer
       (let* ((project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader)))
-            (buffer (phpinspect-make-buffer :buffer (current-buffer) :-project 
project))
+            (buffer (phpinspect-claim-buffer (current-buffer) project))
             (class-type (phpinspect--make-type :name "\\NS\\TestClass" 
:fully-qualified t)))
       (insert "<?php
 namespace NS;
@@ -731,7 +712,6 @@ class TestClass
     function testMe(): RelativeType {}
 }")
       (setq-local phpinspect-current-buffer buffer)
-      (add-hook 'after-change-functions #'phpinspect-after-change-function)
       (phpinspect-buffer-update-project-index buffer)
 
       (goto-char 59)
@@ -751,10 +731,9 @@ class TestClass
 (ert-deftest phpinspect-buffer-parse-incrementally-comment ()
     (with-temp-buffer
       (let* ((project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader)))
-            (buffer (phpinspect-make-buffer :buffer (current-buffer) :-project 
project)))
+            (buffer (phpinspect-claim-buffer (current-buffer) project)))
        (insert "<?php\n\n// \n")
        (setq-local phpinspect-current-buffer buffer)
-       (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
        (should (equal '(:root (:comment))
                           (phpinspect-buffer-parse buffer)))
@@ -774,6 +753,7 @@ class TestClass
 
       (should (equal '(:root (:word "aaa") (:word "bbb") (:word "ccc")) 
(phpinspect-buffer-parse buffer)))
       (should-not (phpinspect-buffer--deletions buffer))
+
       (should (length= (phpinspect-buffer--additions buffer) 4))
 
       (goto-char (- (point-max) 5))
diff --git a/test/test-changeset.el b/test/test-changeset.el
index 8d21bc0c9d..aff48206b0 100644
--- a/test/test-changeset.el
+++ b/test/test-changeset.el
@@ -31,38 +31,38 @@
 (require 'phpinspect-meta)
 (require 'phpinspect-parse-context)
 
-(ert-deftest phpinspect-meta-with-changeset-revert-parent-relation ()
-  (let ((parent (phpinspect-make-meta nil 1 20 "" 'parent))
-        (child (phpinspect-make-meta nil 4 8 "" 'child))
-        (pctx (phpinspect-make-pctx))
-        (other-parent (phpinspect-make-meta nil 3 20 "" 'other-parent))
-        parent-offset)
-
-    (phpinspect-meta-set-parent child parent)
-    (setq parent-offset (phpinspect-meta-parent-offset child))
-
-
-    (phpinspect-with-parse-context pctx
-      (phpinspect-meta-with-changeset child
-        (phpinspect-meta-detach-parent child)
-        (phpinspect-meta-set-parent child other-parent)))
-
-    (phpinspect-changeset-revert (car (phpinspect-pctx-changesets pctx)))
-
-    (should (eq parent (phpinspect-meta-parent child)))
-    (let ((children (phpinspect-splayt-to-list
-                     (phpinspect-meta-children parent))))
-      (should (length= children 1))
-      (should (eq 'child (phpinspect-meta-token (car children))))
-
-      (should (= parent-offset (phpinspect-meta-parent-offset child)))
-      (should (= 4 (phpinspect-meta-start child)))
-      (should (= 8 (phpinspect-meta-end child)))
-      (should (= 4 (phpinspect-meta-width child)))
-
-      (should (eq child (phpinspect-splayt-find
-                         (phpinspect-meta-children parent)
-                         (phpinspect-meta-parent-offset child)))))))
+;; (ert-deftest phpinspect-meta-with-changeset-revert-parent-relation ()
+;;   (let ((parent (phpinspect-make-meta nil 1 20 "" 'parent))
+;;         (child (phpinspect-make-meta nil 4 8 "" 'child))
+;;         (pctx (phpinspect-make-pctx))
+;;         (other-parent (phpinspect-make-meta nil 3 20 "" 'other-parent))
+;;         parent-offset)
+
+;;     (phpinspect-meta-set-parent child parent)
+;;     (setq parent-offset (phpinspect-meta-parent-offset child))
+
+
+;;     (phpinspect-with-parse-context pctx
+;;       (phpinspect-meta-with-changeset child
+;;         (phpinspect-meta-detach-parent child)
+;;         (phpinspect-meta-set-parent child other-parent)))
+
+;;     (phpinspect-changeset-revert (car (phpinspect-pctx-changesets pctx)))
+
+;;     (should (eq parent (phpinspect-meta-parent child)))
+;;     (let ((children (phpinspect-splayt-to-list
+;;                      (phpinspect-meta-children parent))))
+;;       (should (length= children 1))
+;;       (should (eq 'child (phpinspect-meta-token (car children))))
+
+;;       (should (= parent-offset (phpinspect-meta-parent-offset child)))
+;;       (should (= 4 (phpinspect-meta-start child)))
+;;       (should (= 8 (phpinspect-meta-end child)))
+;;       (should (= 4 (phpinspect-meta-width child)))
+
+;;       (should (eq child (phpinspect-splayt-find
+;;                          (phpinspect-meta-children parent)
+;;                          (phpinspect-meta-parent-offset child)))))))
 
 
 ;;; test-changeset.el ends here
diff --git a/test/test-eldoc.el b/test/test-eldoc.el
index 6153d325c0..a34767de2f 100644
--- a/test/test-eldoc.el
+++ b/test/test-eldoc.el
@@ -26,7 +26,7 @@ class Thing
            (phpinspect-project-root-function (lambda () "phpinspect-test"))
            (phpinspect-eldoc-word-width 100)
            (project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader) :worker 'nil-worker))
-           (buffer (phpinspect-make-buffer :buffer (current-buffer) :-project 
project))
+           (buffer (phpinspect-claim-buffer (current-buffer) project))
            second-arg-pos inside-nested-list-pos first-arg-pos)
       (setq-local phpinspect-current-buffer buffer)
       (insert php-code)
@@ -104,7 +104,7 @@ class Thing
                      (insert php-code)
                      (backward-char)
                      (setq-local phpinspect-current-buffer
-                                 (phpinspect-make-buffer :buffer 
(current-buffer) :-project project))
+                                 (phpinspect-claim-buffer (current-buffer) 
project))
                      (phpinspect-buffer-parse phpinspect-current-buffer)
                      (phpinspect-eldoc-function))))))
 
@@ -131,5 +131,5 @@ class Thing
                    (with-temp-buffer
                      (insert php-code)
                      (setq-local phpinspect-current-buffer
-                                 (phpinspect-make-buffer :buffer 
(current-buffer)))
+                                 (phpinspect-claim-buffer (current-buffer)))
                      (phpinspect-eldoc-function))))))
diff --git a/test/test-imports.el b/test/test-imports.el
index d0c9c9be90..4c12b461b8 100644
--- a/test/test-imports.el
+++ b/test/test-imports.el
@@ -34,10 +34,9 @@
 (ert-deftest phpinspect-fix-imports-single-namespaced-class ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
-        (insert "<?php
+      (insert "<?php
 
 namespace Not\\App;
 
@@ -45,12 +44,9 @@ class Baz {
     private Foo $foo;
     public Bar $bar;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
-        (phpinspect-fix-imports)
-        (should (string= "<?php
+      (phpinspect-fix-imports)
+      (should (string= "<?php
 
 namespace Not\\App;
 
@@ -61,16 +57,15 @@ class Baz {
     private Foo $foo;
     public Bar $bar;
 }"
-                         (buffer-string)))))))
+                       (buffer-string))))))
 
 (ert-deftest phpinspect-fix-imports-no-remove-unused ()
   (dlet ((phpinspect-imports-remove-unused nil))
     (let ((project (phpinspect--make-dummy-composer-project)))
       (with-temp-buffer
-       (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                               :-project project)))
+           (phpinspect-claim-buffer (current-buffer) project)
 
-          (insert "<?php
+        (insert "<?php
 
 namespace Not\\App;
 
@@ -80,12 +75,9 @@ class Baz {
     private Foo $foo;
     public Bar $bar;
 }")
-          ;; Ensure buffer is made aware of changes
-          (setq phpinspect-current-buffer buffer)
-          (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
-          (phpinspect-fix-imports)
-          (should (string= "<?php
+        (phpinspect-fix-imports)
+        (should (string= "<?php
 
 namespace Not\\App;
 
@@ -97,16 +89,15 @@ class Baz {
     private Foo $foo;
     public Bar $bar;
 }"
-                           (buffer-string))))))))
+                         (buffer-string)))))))
 
 
 (ert-deftest phpinspect-fix-imports-multiple-namespaced-classes ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
-        (insert "<?php
+      (insert "<?php
 
 namespace Not\\App;
 
@@ -117,12 +108,9 @@ class Bee {
 class Baz {
     private Foo $foo;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
-        (phpinspect-fix-imports)
-        (should (string= "<?php
+      (phpinspect-fix-imports)
+      (should (string= "<?php
 
 namespace Not\\App;
 
@@ -136,13 +124,12 @@ class Bee {
 class Baz {
     private Foo $foo;
 }"
-                         (buffer-string)))))))
+                         (buffer-string))))))
 
 (ert-deftest phpinspect-fix-imports-namespaced-class-and-enum ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
         (insert "<?php
 
@@ -155,9 +142,6 @@ enum Bee: string {
 class Baz {
     private Foo $foo;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
         (phpinspect-fix-imports)
         (should (string= "<?php
@@ -174,15 +158,14 @@ enum Bee: string {
 class Baz {
     private Foo $foo;
 }"
-                         (buffer-string)))))))
+                         (buffer-string))))))
 
 (ert-deftest phpinspect-fix-imports-namespaced-class-and-function ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
-        (insert "<?php
+      (insert "<?php
 
 namespace Not\\App;
 
@@ -191,12 +174,9 @@ function bar(): Bar {}
 class Baz {
     private Foo $foo;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
-        (phpinspect-fix-imports)
-        (should (string= "<?php
+      (phpinspect-fix-imports)
+      (should (string= "<?php
 
 namespace Not\\App;
 
@@ -208,13 +188,12 @@ function bar(): Bar {}
 class Baz {
     private Foo $foo;
 }"
-                         (buffer-string)))))))
+                       (buffer-string))))))
 
 (ert-deftest phpinspect-fix-imports-aliased ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
         (insert "<?php
 
@@ -228,9 +207,6 @@ class Baz {
     private FooBar $foo;
     private Foo $notAliased;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
 
         (phpinspect-fix-imports)
         (should (string= "<?php
@@ -247,13 +223,12 @@ class Baz {
     private FooBar $foo;
     private Foo $notAliased;
 }"
-                         (buffer-string)))))))
+                         (buffer-string))))))
 
 (ert-deftest phpinspect-fix-imports-fully-qualified-names ()
   (let ((project (phpinspect--make-dummy-composer-project)))
     (with-temp-buffer
-      (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)
-                                             :-project project)))
+      (phpinspect-claim-buffer (current-buffer) project)
 
         (insert "<?php
 
@@ -264,10 +239,6 @@ function bar(): \\App\\Bar {}
 class Baz {
     private \\App\\Foo $foo;
 }")
-        ;; Ensure buffer is made aware of changes
-        (setq phpinspect-current-buffer buffer)
-        (add-hook 'after-change-functions #'phpinspect-after-change-function)
-
         (phpinspect-fix-imports)
         (should (string= "<?php
 
@@ -278,4 +249,4 @@ function bar(): \\App\\Bar {}
 class Baz {
     private \\App\\Foo $foo;
 }"
-                         (buffer-string)))))))
+                         (buffer-string))))))
diff --git a/test/test-parse-context.el b/test/test-parse-context.el
index 7a73774e6c..300eb18697 100644
--- a/test/test-parse-context.el
+++ b/test/test-parse-context.el
@@ -28,23 +28,23 @@
 (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))))
-
-    (should (= 222 (phpinspect-meta-start meta)))
-    (should (= 1234 (phpinspect-meta-end meta)))
-    (should (phpinspect-meta-parent 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))))
+;; (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))))
+
+;;     (should (= 222 (phpinspect-meta-start meta)))
+;;     (should (= 1234 (phpinspect-meta-end meta)))
+;;     (should (phpinspect-meta-parent 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))))
diff --git a/test/test-parser.el b/test/test-parser.el
index 260b2c0f82..224aa485ba 100644
--- a/test/test-parser.el
+++ b/test/test-parser.el
@@ -61,78 +61,78 @@ class TestClass {
       (should (phpinspect-list-p (phpinspect-meta-token parent)))
       (should (phpinspect-block-p (phpinspect-meta-token 
(phpinspect-meta-parent parent)))))))
 
-(ert-deftest phpinspect-parse-string-incrementally-single-edit ()
-  (let ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap))))
-    (phpinspect-with-parse-context ctx
-      (phpinspect-parse-string "  : 'abc'      "))
-
-    (let* ((bmap (phpinspect-pctx-bmap ctx))
-           (children (thread-last (phpinspect-bmap-root-meta bmap)
-                                  (phpinspect-meta-children)
-                                  (phpinspect-splayt-to-list)))
-           (string-meta (car children)))
-
-      (should (length= children 1))
-      (should (phpinspect-string-p (phpinspect-meta-token string-meta)))
-      (should (= 5 (phpinspect-meta-width string-meta)))
-      (should (= 5 (phpinspect-meta-start string-meta)))
-      (should (= 10 (phpinspect-meta-end string-meta)))
-
-      (let ((edit-tracker (phpinspect-make-edtrack)))
-        (phpinspect-edtrack-register-edit edit-tracker 1 1 1)
-
-        (let ((prev-string-meta string-meta)
-              (ctx (phpinspect-make-pctx :incremental t
-                                         :previous-bmap bmap
-                                         :bmap (phpinspect-make-bmap)
-                                         :edtrack edit-tracker)))
-          (phpinspect-with-parse-context ctx
-            (phpinspect-parse-string " : 'abc'      "))
-
-          (setq children (thread-last (phpinspect-bmap-root-meta 
(phpinspect-pctx-bmap ctx))
-                                      (phpinspect-meta-children)
-                                      (phpinspect-splayt-to-list))
-                string-meta (car children))
-
-          ;;(message "%s" (mapcar #'phpinspect-meta-string children))
-
-          (should (length= children 1))
-          (should (eq prev-string-meta string-meta))
-          (should (phpinspect-string-p (phpinspect-meta-token string-meta)))
-          (should (= 5 (phpinspect-meta-width string-meta)))
-          (should (= 4 (phpinspect-meta-start string-meta)))
-          (should (= 9 (phpinspect-meta-end string-meta))))))))
-
-(ert-deftest phpinspect-parse-string-incrementally-muti-edit ()
-  (let ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap))))
-    (phpinspect-with-parse-context ctx
-      (phpinspect-parse-string "  : 'abc'      "))
-
-      (let ((edit-tracker (phpinspect-make-edtrack)))
-        (phpinspect-edtrack-register-edit edit-tracker 1 1 1)
-        (phpinspect-edtrack-register-edit edit-tracker 2 5 0)
-
-        (let ((prev-string-meta (phpinspect-meta-first-child 
(phpinspect-bmap-root-meta (phpinspect-pctx-bmap ctx))))
-              (ctx (phpinspect-make-pctx :incremental t
-                                         :previous-bmap (phpinspect-pctx-bmap 
ctx)
-                                         :bmap (phpinspect-make-bmap)
-                                         :edtrack edit-tracker)))
-          (phpinspect-with-parse-context ctx
-            (phpinspect-parse-string "    : 'abc'      "))
-
-          (let* ((children (thread-last (phpinspect-bmap-root-meta 
(phpinspect-pctx-bmap ctx))
-                                        (phpinspect-meta-children)
-                                        (phpinspect-splayt-to-list)))
-                 (string-meta (car children)))
-
-
-            ;;(message "%s" (mapcar #'phpinspect-meta-string children))
-            (should (length= children 1))
-            (should (eq prev-string-meta string-meta))
-            (should (phpinspect-string-p (phpinspect-meta-token string-meta)))
-            (should (= 5 (phpinspect-meta-width string-meta)))
-            (should (= 7 (phpinspect-meta-start string-meta)))
-            (should (= 12 (phpinspect-meta-end string-meta))))))))
+;; (ert-deftest phpinspect-parse-string-incrementally-single-edit ()
+;;   (let ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap))))
+;;     (phpinspect-with-parse-context ctx
+;;       (phpinspect-parse-string "  : 'abc'      "))
+
+;;     (let* ((bmap (phpinspect-pctx-bmap ctx))
+;;            (children (thread-last (phpinspect-bmap-root-meta bmap)
+;;                                   (phpinspect-meta-children)
+;;                                   (phpinspect-splayt-to-list)))
+;;            (string-meta (car children)))
+
+;;       (should (length= children 1))
+;;       (should (phpinspect-string-p (phpinspect-meta-token string-meta)))
+;;       (should (= 5 (phpinspect-meta-width string-meta)))
+;;       (should (= 5 (phpinspect-meta-start string-meta)))
+;;       (should (= 10 (phpinspect-meta-end string-meta)))
+
+;;       (let ((edit-tracker (phpinspect-make-edtrack)))
+;;         (phpinspect-edtrack-register-edit edit-tracker 1 1 1)
+
+;;         (let ((prev-string-meta string-meta)
+;;               (ctx (phpinspect-make-pctx :incremental t
+;;                                          :previous-bmap bmap
+;;                                          :bmap (phpinspect-make-bmap)
+;;                                          :edtrack edit-tracker)))
+;;           (phpinspect-with-parse-context ctx
+;;             (phpinspect-parse-string " : 'abc'      "))
+
+;;           (setq children (thread-last (phpinspect-bmap-root-meta 
(phpinspect-pctx-bmap ctx))
+;;                                       (phpinspect-meta-children)
+;;                                       (phpinspect-splayt-to-list))
+;;                 string-meta (car children))
+
+;;           ;;(message "%s" (mapcar #'phpinspect-meta-string children))
+
+;;           (should (length= children 1))
+;;           (should (eq prev-string-meta string-meta))
+;;           (should (phpinspect-string-p (phpinspect-meta-token string-meta)))
+;;           (should (= 5 (phpinspect-meta-width string-meta)))
+;;           (should (= 4 (phpinspect-meta-start string-meta)))
+;;           (should (= 9 (phpinspect-meta-end string-meta))))))))
+
+;; (ert-deftest phpinspect-parse-string-incrementally-muti-edit ()
+;;   (let ((ctx (phpinspect-make-pctx :incremental t :bmap 
(phpinspect-make-bmap))))
+;;     (phpinspect-with-parse-context ctx
+;;       (phpinspect-parse-string "  : 'abc'      "))
+
+;;       (let ((edit-tracker (phpinspect-make-edtrack)))
+;;         (phpinspect-edtrack-register-edit edit-tracker 1 1 1)
+;;         (phpinspect-edtrack-register-edit edit-tracker 2 5 0)
+
+;;         (let ((prev-string-meta (phpinspect-meta-first-child 
(phpinspect-bmap-root-meta (phpinspect-pctx-bmap ctx))))
+;;               (ctx (phpinspect-make-pctx :incremental t
+;;                                          :previous-bmap 
(phpinspect-pctx-bmap ctx)
+;;                                          :bmap (phpinspect-make-bmap)
+;;                                          :edtrack edit-tracker)))
+;;           (phpinspect-with-parse-context ctx
+;;             (phpinspect-parse-string "    : 'abc'      "))
+
+;;           (let* ((children (thread-last (phpinspect-bmap-root-meta 
(phpinspect-pctx-bmap ctx))
+;;                                         (phpinspect-meta-children)
+;;                                         (phpinspect-splayt-to-list)))
+;;                  (string-meta (car children)))
+
+
+;;             ;;(message "%s" (mapcar #'phpinspect-meta-string children))
+;;             (should (length= children 1))
+;;             (should (eq prev-string-meta string-meta))
+;;             (should (phpinspect-string-p (phpinspect-meta-token 
string-meta)))
+;;             (should (= 5 (phpinspect-meta-width string-meta)))
+;;             (should (= 7 (phpinspect-meta-start string-meta)))
+;;             (should (= 12 (phpinspect-meta-end string-meta))))))))
 
 (ert-deftest phpinspect-parse-comma ()
   (let* ((code "(,)")
diff --git a/test/test-shadow.el b/test/test-shadow.el
new file mode 100644
index 0000000000..c2547df9d9
--- /dev/null
+++ b/test/test-shadow.el
@@ -0,0 +1,66 @@
+;;; test-shadow.el --- Tests for phpinspect-shadow.el  -*- lexical-binding: t; 
-*-
+
+;; Copyright (C) 2024  Hugo Thunnissen
+
+;; Author: Hugo Thunnissen <de...@hugot.nl>
+;; Keywords:
+
+;; 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-change)
+
+(ert-deftest phpinspect-change-post-position ()
+  (let ((change (phpinspect-make-change
+                 :start 1
+                 :end 11
+                 :prev-length 21)))
+    (should (= 11 (phpi-change-post-position change 21)))
+    (should (= 11 (phpi-change-post-position change 15)))
+    (should (= 11 (phpi-change-post-position change 11)))
+    (should (= 10 (phpi-change-post-position change 10)))
+    (should (= 20 (phpi-change-post-position change 31)))))
+
+(ert-deftest phpinspect-change-pre-position ()
+  (let ((change (phpinspect-make-change
+                 :start 1
+                 :end 11
+                 :prev-length 21)))
+    (should (= 31 (phpi-change-pre-position change 20)))
+    (should (= 26 (phpi-change-pre-position change 15)))
+    (should (= 11 (phpi-change-pre-position change 11)))
+    (should (= 10 (phpi-change-pre-position change 10)))
+
+    (should (= 41 (phpi-change-pre-position change 30)))))
+
+(ert-deftest phpinspect-change-pre-position-shift-right ()
+  (let ((change (phpinspect-make-change
+                 :start 1
+                 :end 21
+                 :prev-length 10)))
+    (should (= 11 (phpi-change-pre-position change 20)))
+    (should (= 11 (phpi-change-pre-position change 15)))
+    (should (= 11 (phpi-change-pre-position change 11)))
+    (should (= 10 (phpi-change-pre-position change 10)))
+
+    (should (= 20 (phpi-change-pre-position change 30)))))
+
+
+(provide 'test-shadow)
+;;; test-shadow.el ends here
diff --git a/test/test-suggest.el b/test/test-suggest.el
index 5edfb4cbb4..41dbd4b5f4 100644
--- a/test/test-suggest.el
+++ b/test/test-suggest.el
@@ -35,11 +35,10 @@
   (with-temp-buffer
     (insert "<?php foreach ($array as $key => $value) {")
 
-    (let* ((buffer (phpinspect-make-buffer :buffer (current-buffer)))
-
-          (rctx (phpinspect-get-resolvecontext
-                 (phpinspect--make-dummy-project) (phpinspect-buffer-parse-map 
buffer) (point)))
-          (variables (phpinspect-suggest-variables-at-point rctx)))
+    (let* ((buffer (phpinspect-claim-buffer (current-buffer)))
+              (rctx (phpinspect-get-resolvecontext
+                         (phpinspect--make-dummy-project) 
(phpinspect-buffer-parse-map buffer) (point)))
+              (variables (phpinspect-suggest-variables-at-point rctx)))
 
       (should (length= variables 3))
       (should (equal (list "array" "key" "value")
@@ -53,23 +52,22 @@ use App\\Foo;
 
 new ")
 
-    (let* ((buffer (phpinspect-make-buffer
-                   :buffer (current-buffer)
-                   :-project 
(phpinspect--make-dummy-composer-project-with-code)))
-          (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
-          (types (phpinspect-suggest-words-at-point rctx)))
+    (let* ((buffer (phpinspect-claim-buffer
+                           (current-buffer)
+                           
(phpinspect--make-dummy-composer-project-with-code)))
+              (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
+              (types (phpinspect-suggest-words-at-point rctx)))
       (setq-local phpinspect-current-buffer buffer)
-      (add-hook 'after-change-functions #'phpinspect-after-change-function)
       (should (length= types 2))
       (should (equal (list "\\App\\Baz" "\\App\\Foo")
-                    (sort (mapcar #'phpinspect--type-name-string types) 
#'string<)))
+                            (sort (mapcar #'phpinspect--type-name-string 
types) #'string<)))
 
       (insert "Fo")
       (setq rctx (phpinspect-buffer-get-resolvecontext buffer (point))
-           types (phpinspect-suggest-words-at-point rctx))
+               types (phpinspect-suggest-words-at-point rctx))
       (should (length= types 2))
       (should (equal (list "\\App\\Baz" "\\App\\Foo")
-                    (sort (mapcar #'phpinspect--type-name-string types) 
#'string<))))))
+                            (sort (mapcar #'phpinspect--type-name-string 
types) #'string<))))))
 
 (ert-deftest phpinspect-suggest-types-at-point-include-current-namespace ()
   (with-temp-buffer
@@ -81,14 +79,14 @@ use \\DateTime;
 
 new ")
 
-      (let* ((buffer (phpinspect-make-buffer
-                     :buffer (current-buffer)
-                     :-project 
(phpinspect--make-dummy-composer-project-with-code)))
-            (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
-            (types (phpinspect-suggest-words-at-point rctx)))
-       (should (length= types 6))
-       (should (equal (list "\\App\\Bar" "\\App\\Barry" "\\App\\Baz" 
"\\App\\Foo" "\\App\\Harry" "\\DateTime")
-                      (sort (mapcar #'phpinspect--type-name-string types) 
#'string<))))))
+    (let* ((buffer (phpinspect-claim-buffer
+                           (current-buffer)
+                           
(phpinspect--make-dummy-composer-project-with-code)))
+              (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
+              (types (phpinspect-suggest-words-at-point rctx)))
+         (should (length= types 6))
+         (should (equal (list "\\App\\Bar" "\\App\\Barry" "\\App\\Baz" 
"\\App\\Foo" "\\App\\Harry" "\\DateTime")
+                            (sort (mapcar #'phpinspect--type-name-string 
types) #'string<))))))
 
 (ert-deftest phpinspect-suggest-keywords-at-point-class-body ()
   (with-temp-buffer
@@ -101,11 +99,11 @@ class Foo
 
  ")
 
-      (let* ((buffer (phpinspect-make-buffer
-                     :buffer (current-buffer)
-                     :-project 
(phpinspect--make-dummy-composer-project-with-code)))
-            (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
-            (words (phpinspect-suggest-words-at-point rctx)))
+      (let* ((buffer (phpinspect-claim-buffer
+                             (current-buffer)
+                             
(phpinspect--make-dummy-composer-project-with-code)))
+                (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
+                (words (phpinspect-suggest-words-at-point rctx)))
        (should (equal (list "const" "function" "private" "protected" "public" 
"static")
                       (sort (mapcar #'phpinspect-suggest-keyword-word words) 
#'string<))))))
 
@@ -121,12 +119,12 @@ class Foo
 public ")
 
     (let* ((buffer (phpinspect-claim-buffer
-                   (current-buffer)
-                   (phpinspect--make-dummy-composer-project-with-code)))
-            (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
-            (results (phpinspect-suggest-words-at-point rctx))
-            (types (seq-filter #'phpinspect--type-p results))
-            (words (seq-filter #'phpinspect-suggest-keyword-p results)))
+                           (current-buffer)
+                           
(phpinspect--make-dummy-composer-project-with-code)))
+              (rctx (phpinspect-buffer-get-resolvecontext buffer (point)))
+              (results (phpinspect-suggest-words-at-point rctx))
+              (types (seq-filter #'phpinspect--type-p results))
+              (words (seq-filter #'phpinspect-suggest-keyword-p results)))
        (should (length= results (+ (length types) (length words))))
        (should (equal (list "function" "static")
                       (sort (mapcar #'phpinspect-suggest-keyword-word words) 
#'string<)))

Reply via email to