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

    WIP create common base library for threading operations
---
 phpinspect-autoload.el |   6 +-
 phpinspect-buffer.el   | 224 ++++++++++++--------------
 phpinspect-pipeline.el | 415 ++++++++++++++++++++-----------------------------
 phpinspect-project.el  |   2 +-
 phpinspect-thread.el   | 183 ++++++++++++++++++++++
 test/test-autoload.el  |   6 +-
 test/test-buffer.el    |  21 +--
 test/test-eldoc.el     |   5 +-
 test/test-pipeline.el  |  31 +++-
 9 files changed, 497 insertions(+), 396 deletions(-)

diff --git a/phpinspect-autoload.el b/phpinspect-autoload.el
index 53453d25ec..87a7643937 100644
--- a/phpinspect-autoload.el
+++ b/phpinspect-autoload.el
@@ -383,7 +383,7 @@ FILE-NAME does not contain any wildcards, instead of nil."
                               (t (phpinspect--log "Error indexing file %s: %s" 
file err))))))
     (phpinspect--log "indexing files list: %s" list)
     (phpinspect-pipeline list
-      :into (funcall :with-context wrapped-indexer))))
+      :into `(funcall :with-context ,wrapped-indexer))))
 
 (cl-defmethod phpinspect-autoloader-put-type-bag ((al phpinspect-autoloader) 
(type-fqn (head phpinspect-name)))
   (let* ((base-name (phpinspect-name-base type-fqn))
@@ -538,8 +538,8 @@ FILE-NAME does not contain any wildcards, instead of nil."
                                    (hash-table-count 
(phpinspect-autoloader-types autoloader))
                                    (string-to-number (format-time-string 
"%s%3N" (time-since time-start)))))))
                           result error))
-                :into (phpinspect-iterate-composer-jsons :with-context 
autoloader)
-                :into phpinspect-al-strategy-execute))))))
+                :into `(phpinspect-iterate-composer-jsons :with-context 
,autoloader)
+                :into #'phpinspect-al-strategy-execute))))))
 
   (provide 'phpinspect-autoload)
 ;;; phpinspect-autoload.el ends here
diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index 665c326ef9..f22e51e7a5 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -23,6 +23,7 @@
 
 ;;; Code:
 
+(require 'phpinspect-thread)
 (require 'phpinspect-parser)
 (require 'phpinspect-bmap)
 (require 'phpinspect-edtrack)
@@ -101,6 +102,7 @@ edits does not count as fresh (because incremental parsing 
has its flaws)."
 (defun phpinspect-buffer-reparse-if-not-fresh (buffer)
   "If BUFFER's tree is fresh, return it. Otherwise reparse the
  buffer and return the result."
+  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer))
   (if (phpinspect-buffer-fresh-p buffer)
       (phpinspect-buffer-tree buffer)
     (phpinspect-buffer-reparse buffer)))
@@ -256,7 +258,6 @@ tokens that have been deleted from a buffer."
         (t (error "Cannot delete index for token %s" token))))
 
 (defun phpinspect-buffer--delete-function-index-reference (buffer token)
-  (message "Deleting func %s" token)
   (when-let ((func (gethash token (phpinspect-buffer-token-index buffer))))
     (let ((arg-list (phpinspect-function-argument-list token)))
       (if-let ((arg-list-meta (phpinspect-buffer-token-meta buffer arg-list))
@@ -273,20 +274,15 @@ tokens that have been deleted from a buffer."
                              (phpinspect-function-p))))
           (progn
             ;; Declaration is equal, update index reference
-            (message "updating index ref, %s was equal to %s"
-                     (phpinspect-meta-token new-declaration)
-                     (phpinspect-function-declaration token))
             (phpinspect-buffer-update-index-reference-for-token
              buffer token (phpinspect-meta-token (phpinspect-meta-parent 
new-declaration))))
 
         (progn
-          (message "Deleting index")
           ;; Declaration is not equal, delete index
           (remhash token (phpinspect-buffer-token-index buffer))
           (cond ((phpinspect-project-p (car func))
                  (phpinspect-project-delete-function 
(phpinspect-buffer-project buffer) (phpinspect--function-name (cdr func))))
                 ((phpinspect--type-p (car func))
-                 (message "Getting typedef for %s" (car func))
                  (when-let ((class (phpinspect-project-get-typedef
                                     (phpinspect-buffer-project buffer)
                                     (car func))))
@@ -301,16 +297,16 @@ tokens that have been deleted from a buffer."
 
   (setf (phpinspect-buffer-tree buffer) nil
         (phpinspect-buffer--tokens buffer) nil
-        (phpinspect-buffer-map buffer) (phpinspect-make-bmap)
+        (phpinspect-buffer-map buffer) nil
 
         ;; TODO: figure out what the desired behaviour is here
         ;; (phpinspect-buffer--additions buffer) nil
         ;; (phpinspect-buffer--deletions buffer) nil
 
         (phpinspect-buffer-token-index buffer)
-        (make-hash-table :test 'eq :size 100 :rehash-size 1.5))
+        (make-hash-table :test 'eq :size 100 :rehash-size 1.5)))
+
 
-  (phpi-shadow-enqueue-task (phpinspect-buffer-shadow buffer) 'parse-fresh))
 
 (defun phpinspect-buffer-state (buffer)
   (interactive (list (or phpinspect-current-buffer
@@ -330,13 +326,13 @@ tokens that have been deleted from a buffer."
   (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))
+  (phpi-shadow-await-synced (phpi-buffer-shadow buffer))
+  (phpinspect-buffer-tree buffer))
 
 (defun phpinspect-buffer-reindex (buffer)
   "Delete all existing index entities for tokens in BUFFER and re-index."
   (interactive (list (or phpinspect-current-buffer (error "Not a phpinspect 
buffer"))))
   (dolist (token (hash-table-keys (phpinspect-buffer-token-index buffer)))
-
     (phpinspect-buffer-delete-index-for-token buffer token))
 
   (phpinspect-buffer-reparse buffer)
@@ -696,6 +692,7 @@ continuing execution."
 (defun phpinspect-buffer-tokens-enclosing-point (buffer point)
   "Return token metadata objects for tokens enclosing POINT in BUFFER."
   (cl-assert (phpinspect-buffer-p buffer))
+  (phpi-shadow-await-synced (phpinspect-buffer-shadow buffer))
   (phpinspect-buffer--query-with-cache buffer `(tokens-enclosing ,point)
     (phpinspect-bmap-tokens-overlapping (phpinspect-buffer-map buffer) point)))
 
@@ -784,6 +781,7 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 ;;;;;;;;;; SHADOWING ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 
 (defvar phpinspect--shadow-counter 0)
+(defvar phpinspect--shadow-run-sync nil)
 
 (defvar phpinspect-shadow-pause-time 0.05)
 
@@ -792,13 +790,14 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 
 (cl-defstruct (phpinspect-shadow (:constructor 
phpinspect-make-shadow-generated)
                                  (:conc-name phpi-shadow-))
-  (synced-p t :type boolean)
-  (index-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)
+  (-last-change nil)
+  (-synced-change nil)
+  (-indexed-change nil)
   (-deletions nil :type list)
   (-additions (make-hash-table :test #'eq) :type hash-table))
 
@@ -812,14 +811,16 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
     (setf (phpinspect-buffer-map buffer) bmap)
 
     (if buffer-tokens
-        (let ((local-additions (phpi-shadow--additions shadow)))
-          ;; Determine which tokens are new and which were already present in 
the
-          ;; buffer
-          (maphash
-           (lambda (token meta) (unless (gethash token buffer-tokens)
-                                  (puthash token meta buffer-tokens)
-                                  (puthash token meta local-additions)))
-           (phpinspect-bmap-meta bmap)))
+        (let ((local-additions (with-memoization (phpi-shadow--additions 
shadow)
+                                 (make-hash-table :test #'eq))))
+            ;; Determine which tokens are new and which were already present 
in the
+            ;; buffer
+            (maphash
+             (lambda (token meta) (unless (gethash token buffer-tokens)
+                                    (puthash token meta buffer-tokens)
+                                    (puthash token meta local-additions)))
+             (phpinspect-bmap-meta bmap)))
+
       ;; There were no tokens registered, so we can adopt the map's token table
       (setf (phpinspect-buffer--tokens buffer) (phpinspect-bmap-meta bmap)
             ;; All tokens are new additions
@@ -837,7 +838,7 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
             (remhash (phpinspect-meta-token deletion) buffer-tokens)))
 
       ;; There is no previous bmap, so there should also not be any previous 
additions
-      (setf (phpi-shadow--additions shadow) (make-hash-table :test #'eq)))
+      (setf (phpi-shadow--additions shadow) (phpinspect-bmap-meta bmap)))
 
     ;; A new bmap was provided, so the structure of the token tree was
     ;; changed. All previous query results should be regarded as invalid.
@@ -846,22 +847,12 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 (defun phpi-shadow-wakeup-thread (shadow)
   (thread-signal (phpi-shadow-thread shadow) 'phpinspect-wakeup-shadow nil))
 
-(defun phpi-shadow-thread-check-pause ()
-  (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))
-    (ignore-errors (thread-yield))))
-
 (defun phpi-shadow-make-queue-subscription (shadow)
   (lambda ()
-    (setf (phpi-shadow-synced-p shadow) nil)
-    (setf (phpi-shadow-index-synced-p shadow) nil)
     (phpi-shadow-wakeup-thread shadow)))
 
 (defun phpi-shadow--thread-make-parser-interrupt-predicate ()
-  (lambda () (phpi-shadow-thread-check-pause) nil))
+  (lambda () (phpi-thread-yield) nil))
 
 (defun phpi-shadow-process-change (shadow change)
   (with-current-buffer (phpi-shadow-buffer shadow)
@@ -884,7 +875,9 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
         (setf (phpinspect-buffer-tree buffer) result))
 
       (phpi-shadow--set-buffer-map
-       shadow (phpinspect-pctx-bmap pctx) (phpinspect-pctx-previous-bmap 
pctx)))))
+       shadow (phpinspect-pctx-bmap pctx) (phpinspect-pctx-previous-bmap pctx))
+
+      (setf (phpi-shadow--synced-change shadow) change))))
 
 (defun phpi-shadow-parse-fresh (shadow)
   (with-current-buffer (phpi-shadow-buffer shadow)
@@ -903,57 +896,27 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
         (setf (phpinspect-buffer-tree buffer) result)
 
         (phpi-shadow--set-buffer-map
-         shadow (phpinspect-pctx-bmap pctx) nil)))))
+         shadow (phpinspect-pctx-bmap pctx) nil)
+
+        (setf (phpi-shadow--synced-change shadow) 'parse-fresh)))))
 
 (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)
-  (lambda ()
-    (let ((inhibit-quit t))
-      (while t
-        (condition-case err
-            (ignore-error phpinspect-wakeup-shadow
-              (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 parsing work to do, we're in sync with the origin buffer
-                (setf (phpi-shadow-synced-p shadow) t)
-
-                (if (phpi-shadow-index-synced-p shadow)
-                    ;; Index has been synced, we can join the main thread and
-                    ;; wait for more tasks.
-                    (ignore-errors
-                      (thread-join main-thread))
-                  ;; Update project index
-                  (phpi-shadow-update-project-index shadow)
-
-                  ;; Only flag index as synced when no additional tasks have 
been
-                  ;; enqueued while indexing.
-                  (unless (phpinspect-queue-first (phpi-shadow-queue shadow))
-                    (setf (phpi-shadow-index-synced-p shadow) t)))))
-          (error
-           (phpinspect-message "%s encountered unexpected error: %s"
-                               (thread-name (current-thread))
-                               err)))))))
-        ;; (error
-        ;;  (phpinspect-message "%s exited: %s"
-        ;;                      (thread-name (current-thread))
-        ;;                      err))))))
+(defun phpi-shadow-perform-task (shadow task)
+  (pcase task
+    ((pred phpinspect-change-p)
+     (phpi-shadow-process-change shadow task))
+    ('parse-fresh
+     (phpi-shadow-parse-fresh shadow))
+    ('update-project-index
+     (phpi-shadow-update-project-index shadow))
+    (_
+     (phpinspect-message
+      "Shadow thread received unsupported task type: %s"
+      (type-of task)))))
 
 (defun phpi-shadow-thread-live-p (shadow)
   (thread-live-p (phpi-shadow-thread shadow)))
@@ -977,9 +940,11 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
 
       (phpi-shadow-assert-live-p shadow)
 
-      (phpi-shadow-wakeup-thread shadow)
       (thread-yield))))
 
+(defun phpi-shadow-synced-p (shadow)
+  (eq (phpi-shadow--synced-change shadow) (phpi-shadow--last-change shadow)))
+
 (defun phpi-shadow-await-synced (shadow &optional allow-interrupt)
   (phpi-shadow-await-predicate shadow #'phpi-shadow-synced-p allow-interrupt))
 
@@ -990,16 +955,17 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
   (let ((buffer (phpi-shadow-origin shadow)))
     ;; Process deleted tokens
     (dolist (deletion (phpi-shadow--deletions shadow))
-      (pcase (phpinspect-meta-token deletion)
-        ((pred phpinspect--can-delete-buffer-index-for-token)
-         (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deletion)))
-        ((pred phpinspect-use-trait-p)
-         (when-let ((class (seq-find (phpinspect-meta-token-predicate 
#'phpinspect-class-p)
-                                     (phpinspect-buffer-tokens-enclosing-point
-                                      buffer (phpinspect-meta-start 
deletion))))
-                    (declaration 
(phpinspect-meta-find-first-child-matching-token
-                                  class #'phpinspect-class-declaration-p)))
-           (phpinspect-buffer--index-class-declaration buffer declaration 
'force)))))
+      (phpi-progn
+       (pcase (phpinspect-meta-token deletion)
+         ((pred phpinspect--can-delete-buffer-index-for-token)
+          (phpinspect-buffer-delete-index-for-token buffer 
(phpinspect-meta-token deletion)))
+         ((pred phpinspect-use-trait-p)
+          (when-let ((class (seq-find (phpinspect-meta-token-predicate 
#'phpinspect-class-p)
+                                      (phpinspect-buffer-tokens-enclosing-point
+                                       buffer (phpinspect-meta-start 
deletion))))
+                     (declaration 
(phpinspect-meta-find-first-child-matching-token
+                                   class #'phpinspect-class-declaration-p)))
+            (phpinspect-buffer--index-class-declaration buffer declaration 
'force))))))
 
     (setf (phpi-shadow--deletions shadow) nil)))
 
@@ -1009,36 +975,48 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
              (buffer (phpi-shadow-origin shadow)))
     (maphash
      (lambda (token addition)
-       (pcase (phpinspect-meta-token addition)
-         ((pred phpinspect-class-declaration-p)
-          (phpinspect-buffer--index-class-declaration buffer addition))
-         ((pred phpinspect-function-p)
-          (phpinspect-buffer--index-function buffer addition))
-         ((pred phpinspect-use-trait-p)
-          (phpinspect-buffer--index-trait-use buffer addition))
-         ((pred phpinspect-this-p)
-          (phpinspect-buffer--index-this buffer addition))
-         ((or (pred phpinspect-class-variable-p)
-              (pred phpinspect-const-p))
-          (phpinspect-buffer--index-class-variable buffer addition)))
-
-       ;; Pause in between potentially expensive indexing operations.
-       (phpi-shadow-thread-check-pause))
+       (phpi-progn
+        (pcase (phpinspect-meta-token addition)
+          ((pred phpinspect-class-declaration-p)
+           (phpinspect-buffer--index-class-declaration buffer addition))
+          ((pred phpinspect-function-p)
+           (phpinspect-buffer--index-function buffer addition))
+          ((pred phpinspect-use-trait-p)
+           (phpinspect-buffer--index-trait-use buffer addition))
+          ((pred phpinspect-this-p)
+           (phpinspect-buffer--index-this buffer addition))
+          ((or (pred phpinspect-class-variable-p)
+               (pred phpinspect-const-p))
+           (phpinspect-buffer--index-class-variable buffer addition)))))
      additions))
 
     (setf (phpi-shadow--additions shadow) nil))
 
 (defun phpi-shadow-update-project-index (shadow)
-  (when (phpinspect-buffer-project (phpi-shadow-origin shadow))
-    (phpinspect--log "Updating project index")
-    (phpi-shadow-process-deletions shadow)
-    (phpi-shadow-thread-check-pause)
-    (phpi-shadow-process-additions shadow)))
+  (let ((change (phpi-shadow--last-change shadow)))
+    (when (phpinspect-buffer-project (phpi-shadow-origin shadow))
+      (phpi-shadow-process-deletions shadow)
+      (phpi-shadow-process-additions shadow)
+
+      (setf (phpi-shadow--indexed-change shadow) change))))
+
+(defun phpi-shadow--handle-job (shadow job)
+  (phpi-progn
+   (phpi-shadow-perform-task shadow job))
+
+  (when (and (phpi-shadow-synced-p shadow)
+             (not (phpi-shadow-index-synced-p shadow)))
+    (phpi-shadow-enqueue-task shadow 'update-project-index)))
 
-(defun phpi-shadow-make-thread (shadow)
-  (make-thread
-   (phpi-shadow-make-thread-function shadow)
-   (format " **phpinspect-shadow-thread**<%d>" (phpi-shadow-id shadow))))
+(defun phpi-shadow-index-synced-p (shadow)
+  (and (phpi-shadow-synced-p shadow)
+       (eq (phpi-shadow--synced-change shadow)
+           (phpi-shadow--indexed-change shadow))))
+
+(defun phpi-shadow-make-job-queue (shadow)
+  (phpi-start-job-queue  (format " **phpinspect-shadow-thread**<%d>" 
(phpi-shadow-id shadow))
+    (lambda (job)
+      (phpi-shadow--handle-job shadow job))))
 
 (defun phpinspect-make-shadow (origin)
   (cl-assert (phpinspect-buffer-p origin))
@@ -1054,19 +1032,21 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
     (with-current-buffer (phpi-shadow-buffer shadow)
       (insert (phpinspect-with-current-buffer origin (buffer-string))))
 
+    (let ((job-queue (phpi-shadow-make-job-queue shadow)))
 
+      (setf (phpi-shadow-queue shadow) job-queue
+            (phpi-shadow-thread shadow) (phpi-job-queue-thread job-queue)))
 
-    (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)
+    (phpi-shadow-enqueue-task shadow 'parse-fresh)
 
     shadow))
 
 (defun phpi-shadow-enqueue-task (shadow task)
-  (phpinspect-queue-enqueue (phpi-shadow-queue shadow) task))
+  (when (or (phpinspect-change-p task) (eq 'parse-fresh task))
+    (setf (phpi-shadow--last-change shadow) task))
+
+  (if phpinspect--shadow-run-sync
+      (phpi-shadow--handle-job shadow task)
+    (phpinspect-queue-enqueue (phpi-shadow-queue shadow) task)))
 
 (provide 'phpinspect-buffer)
diff --git a/phpinspect-pipeline.el b/phpinspect-pipeline.el
index 4ffd54fec6..4893d853b5 100644
--- a/phpinspect-pipeline.el
+++ b/phpinspect-pipeline.el
@@ -24,10 +24,14 @@
 ;;; Code:
 (require 'phpinspect-queue)
 (require 'phpinspect-util)
+(require 'phpinspect-thread)
 
 (define-error 'phpinspect-pipeline-incoming "Signal for incoming pipeline 
data")
 (define-error 'phpinspect-pipeline-error "Signal for pipeline errors")
 
+(defun phpinspect-pipeline-error-p (obj)
+  (eq 'phpinspect-pipeline-error (car-safe obj)))
+
 (defcustom phpinspect-pipeline-pause-time 0.1
   "Number of seconds to pause a pipeline thread when emacs receives
 user input. This is similar to `phpinspect-worker-pause-time',
@@ -74,7 +78,8 @@ directories."
 
 (cl-defmethod phpinspect-pipeline-ctx-register-end ((ctx 
phpinspect-pipeline-ctx) (end phpinspect-pipeline-end))
   (let ((thread (phpinspect-pipeline-ctx-get-thread ctx 
(phpinspect-pipeline-end-thread end))))
-    (setf (phpinspect-pipeline-thread-end thread) end)))
+    (unless (phpinspect-pipeline-thread-end thread)
+      (setf (phpinspect-pipeline-thread-end thread) end))))
 
 (cl-defmethod phpinspect-pipeline-ctx-close ((ctx phpinspect-pipeline-ctx))
   (let (errors err end thread-live)
@@ -86,8 +91,10 @@ directories."
 
       (when thread-live
         (if end
-            (setq errors (nconc errors (list (format "Thread %s ended 
pipeline, but is still running"
-                                                     (thread-name (car 
thread))))))
+            ;; Give thread cpu time to wrap up
+            (thread-join (car thread))
+          ;; Even if thread is still live, it should have signaled its end at
+          ;; this point.
           (setq errors (nconc errors (list (format "Thread %s is still running 
when pipeline is closing"
                                                    (thread-name (car 
thread))))))))
 
@@ -97,10 +104,7 @@ directories."
                                                  err)))))
       (unless end
         (setq errors (nconc errors (list (format "Thread %s never ended"
-                                                 (thread-name (car 
thread)))))))
-
-      (when (thread-live-p (car thread))
-        (thread-signal (car thread) 'quit nil)))
+                                                 (thread-name (car 
thread))))))))
 
     (when errors
       (signal 'phpinspect-pipeline-error errors))))
@@ -128,13 +132,7 @@ directories."
 
 (define-inline phpinspect-pipeline-pause ()
   "Pause the current pipeline thread"
-  (inline-quote
-   ;; If quit flag is set, behave as it input is pending.
-   (if (or quit-flag (phpinspect--input-pending-p))
-       (let ((mx (make-mutex)))
-         (phpinspect-thread-pause
-          phpinspect-pipeline-pause-time mx (make-condition-variable mx 
"phpinspect-pipeline-pause")))
-     (ignore-errors (thread-yield)))))
+  (inline-quote (phpi-thread-yield)))
 
 (define-inline phpinspect--read-pipeline-emission (&rest body)
   (push 'progn body)
@@ -143,240 +141,7 @@ directories."
      ,body
      nil)))
 
-(defmacro phpinspect--run-as-pipeline-step (func-name queue consumer-queue 
pipeline-ctx &optional local-ctx)
-  (unless (symbolp func-name)
-    (error "Function name must be a symbol, got: %s" func-name))
-
-
-  (let* ((thread-name (concat "phpinspect-pipeline-" (symbol-name func-name)))
-         (statement (list func-name))
-         (statement-rear statement)
-         (incoming (gensym "incoming"))
-         (outgoing (gensym "outgoing"))
-         (inc-queue (gensym "queue"))
-         (out-queue (gensym "queue"))
-         (context-sym (gensym "context"))
-         (continue-running (gensym "continue-running"))
-         (pctx-sym (gensym "pipeline-ctx"))
-         (incoming-end (gensym "incoming-end"))
-         (end (gensym "end")))
-
-      (when local-ctx
-        (setq statement-rear (setcdr statement-rear (cons context-sym nil))))
-
-      (setq statement-rear (setcdr statement-rear (cons incoming nil)))
-
-      `(let ((,inc-queue ,queue)
-             (,out-queue ,consumer-queue)
-             (,context-sym ,local-ctx)
-             (,pctx-sym ,pipeline-ctx))
-         (make-thread
-          (lambda ()
-            (let ((,continue-running t)
-                  ;; Inhibit quitting
-                  (inhibit-quit t)
-                  ,incoming ,outgoing ,end ,incoming-end)
-
-              (phpinspect-pipeline--register-wakeup-function ,inc-queue)
-              (while ,continue-running
-                (condition-case err
-                    (progn
-                        (phpinspect-pipeline-pause)
-                        (setq ,incoming (phpinspect-pipeline-receive 
,inc-queue))
-
-                        (if (phpinspect-pipeline-end-p ,incoming)
-                            (progn
-                              (setq ,incoming-end ,incoming)
-                              (when (phpinspect-pipeline-end-value ,incoming)
-                                (progn
-                                  (setq ,incoming 
(phpinspect-pipeline-end-value ,incoming)
-                                        ,outgoing 
(phpinspect--read-pipeline-emission ,statement))
-                                  (phpinspect-pipeline--enqueue ,out-queue 
,outgoing 'no-notify)))
-
-                              (setq ,end (phpinspect-make-pipeline-end :thread 
(current-thread)))
-                              (phpinspect-pipeline-ctx-register-end ,pctx-sym 
,end)
-                              (setq ,continue-running nil)
-                              (phpinspect-pipeline--enqueue ,out-queue ,end))
-
-                          ;; Else
-                          (setq ,outgoing (phpinspect--read-pipeline-emission 
,statement))
-                          (when (phpinspect-pipeline-end-p ,outgoing)
-                            (setq ,end (phpinspect-make-pipeline-end :thread 
(current-thread)))
-                            (phpinspect-pipeline-ctx-register-end ,pctx-sym 
,end)
-                            (setq ,continue-running nil))
-                          (phpinspect-pipeline--enqueue ,out-queue ,outgoing)))
-                  (phpinspect-pipeline-incoming)
-                  (t (phpinspect-message "Pipeline thread errored: %s" err)
-                     (setq ,end (phpinspect-make-pipeline-end :thread 
(current-thread) :error err))
-                     (setq ,continue-running nil)
-                     (phpinspect-pipeline-ctx-register-end ,pctx-sym ,end)
-                     (phpinspect-pipeline--enqueue ,out-queue ,end))))))
-          ,thread-name))))
-
-
-(defun phpinspect--chain-pipeline-steps (steps start-queue end-queue ctx)
-  (let ((result (gensym "result"))
-        (incoming (gensym "incoming"))
-        (outgoing (gensym "outgoing"))
-        (ctx-sym (gensym "ctx"))
-        body name step statement)
-    (while (setq step (pop steps))
-      (setq name (phpinspect--pipeline-step-name step))
-
-      (setq statement
-            (if (phpinspect--pipeline-step--context-var-name step)
-                `(phpinspect--run-as-pipeline-step
-                  ,name ,incoming ,outgoing ,ctx-sym 
,(phpinspect--pipeline-step--context-var-name step))
-              `(phpinspect--run-as-pipeline-step ,name ,incoming ,outgoing 
,ctx-sym)))
-      (setq body (nconc body `(,(if steps
-                                    `(setq ,outgoing (phpinspect-make-queue))
-                                  `(setq ,outgoing ,end-queue))
-                               (phpinspect-pipeline-ctx-register-thread 
,ctx-sym ,statement ,incoming)
-                               (setq ,incoming ,outgoing)))))
-
-    `(let ((,incoming ,start-queue) (,ctx-sym ,ctx) ,result ,outgoing)
-       ,@body)))
-
-(cl-defstruct (phpinspect--pipeline-step (:constructor 
phpinspect--make-pipeline-step))
-  (context nil
-           :type any
-           :documentation
-           "An object that is passed as first argument to all step executions")
-  (-context-var-name nil
-                     :type symbol
-                     :documentation
-                     "Variable name used to store context in")
-  (name nil
-        :type symbol
-        :documentation
-        "The name of this step"))
-
-(defmacro phpinspect--pipeline (seed-form &rest parameters)
-  (let (key value steps let-vars)
-
-    (while parameters
-      (setq key (pop parameters)
-            value (pop parameters))
-
-      (pcase key
-        (:into
-         (let* ((construct-params (cons nil nil))
-                (cons-params-rear construct-params)
-                parameters name)
-
-           (if (listp value)
-               (progn
-                 (setq name (car value)
-                       parameters (cdr value)))
-             (setq name value))
-
-           (unless (symbolp name)
-             (error "Step name should be a symbol"))
-
-           (let (key value)
-             (while parameters
-               (setq key (pop parameters)
-                     value (pop parameters))
-               (setq key (intern (string-replace ":with-" ":" (symbol-name 
key))))
-               (setq cons-params-rear
-                     (setcdr cons-params-rear (cons key (cons value nil))))))
-           (push (apply #'phpinspect--make-pipeline-step `(,@(cdr 
construct-params) :name ,name))
-                 steps)))
-        (_ (error "unexpected key %s" key))))
-
-    (setq steps (nreverse steps))
-
-    (dolist (step steps)
-      (when (phpinspect--pipeline-step-context step)
-        (setf (phpinspect--pipeline-step--context-var-name step) (gensym 
"ctx"))
-        (push `(,(phpinspect--pipeline-step--context-var-name step)
-                ,(phpinspect--pipeline-step-context step))
-              let-vars)))
-
-    (let ((queue-sym (gensym "queue"))
-          (end-queue-sym (gensym "end-queue"))
-          (ctx-sym (gensym "ctx"))
-          (recv-sym (gensym))
-          (result-sym (gensym))
-          (seed-sym (gensym))
-          (collecting-sym (gensym)))
-      `(progn
-         (when (eq main-thread (current-thread))
-           (error "Pipelines should not run in the main thread"))
-
-         (let* (,@let-vars
-                (,ctx-sym (phpinspect-make-pipeline-ctx))
-                (,queue-sym (phpinspect-make-queue))
-                (,end-queue-sym (phpinspect-make-queue))
-                (,collecting-sym t)
-                (inhibit-quit t)
-                ,recv-sym ,result-sym ,seed-sym)
-
-           ,(phpinspect--chain-pipeline-steps steps queue-sym end-queue-sym 
ctx-sym)
-
-           (setq ,seed-sym ,seed-form)
-           (when ,seed-sym
-             (phpinspect-pipeline--enqueue
-              ,queue-sym
-              (phpinspect-make-pipeline-emission :collection ,seed-sym) 
'no-notify))
-
-           (phpinspect-pipeline--enqueue
-            ,queue-sym (phpinspect-make-pipeline-end :thread (current-thread)))
-
-           (while ,collecting-sym
-             (ignore-error phpinspect-pipeline-incoming
-               (progn
-                 (phpinspect-pipeline--register-wakeup-function ,end-queue-sym)
-                 (while (not (phpinspect-pipeline-end-p
-                              (setq ,recv-sym (phpinspect-pipeline-receive 
,end-queue-sym))))
-                   (setq ,result-sym (nconc ,result-sym (list ,recv-sym))))
-                 (setq ,collecting-sym nil))))
-
-           (phpinspect-pipeline-ctx-close ,ctx-sym)
-           ,result-sym)))))
-
-(defmacro phpinspect-pipeline (seed-form &rest parameters)
-  (declare (indent defun))
-  (let ((result (gensym))
-        (async-sym (gensym))
-        key value async macro-params)
-    (while parameters
-      (setq key (pop parameters)
-            value (pop parameters))
-
-      (pcase key
-        (:async (setq async value))
-        (_ (setq macro-params (nconc macro-params (list key value))))))
-
-    `(if-let ((,async-sym ,async))
-         (make-thread
-          (lambda ()
-            (let ((inhibit-quit t))
-              (condition-case err
-                  (let ((,result (phpinspect--pipeline ,seed-form 
,@macro-params)))
-                    (funcall ,async-sym (or ,result 
'phpinspect-pipeline-nil-result) nil))
-                (error (funcall ,async-sym nil err)))))
-          "phpinspect-pipeline-async")
-       (phpinspect--pipeline ,seed-form ,@macro-params))))
-
-(define-inline phpinspect-pipeline-receive (queue)
-  (inline-letevals (queue)
-    (inline-quote
-     (let ((val))
-       (while (not (setq val (phpinspect-queue-dequeue ,queue)))
-         (thread-yield))
-       val))))
-
-(defun phpinspect-pipeline-step-name (name &optional suffix)
-  (intern (concat (symbol-name name) (if suffix (concat "-" suffix) ""))))
-
-(define-inline phpinspect-pipeline--register-wakeup-function (queue)
-  (inline-quote
-   (let ((thread (current-thread)))
-     (setf (phpinspect-queue-subscription ,queue)
-           (lambda () (thread-signal thread 'phpinspect-pipeline-incoming 
nil))))))
-
-(define-inline phpinspect-pipeline--enqueue (queue emission &optional 
no-notify)
+(define-inline phpinspect--pipeline-enqueue (queue emission &optional 
no-notify)
   (inline-letevals (queue emission no-notify)
     (inline-quote
      (when ,emission
@@ -388,7 +153,159 @@ directories."
                 ,no-notify))
              (phpinspect-queue-enqueue
               ,queue (pop (phpinspect-pipeline-emission-collection ,emission)) 
,no-notify))
-         (phpinspect-queue-enqueue ,queue ,emission ,no-notify))))))
+         (if (and (phpinspect-pipeline-end-p ,emission)
+                  (phpinspect-pipeline-end-value ,emission))
+             (progn
+               (phpinspect-queue-enqueue ,queue (phpinspect-pipeline-end-value 
,emission) ,no-notify)
+               (phpinspect-queue-enqueue ,queue ,emission ,no-notify))
+           (phpinspect-queue-enqueue ,queue ,emission ,no-notify)))))))
+
+(defun phpinspect--pipeline-parse-step (step-arguments)
+  (pcase-let ((`(,name ,step)
+               (if (listp step-arguments)
+                   (list (car step-arguments)
+                         (apply #'phpinspect--make-pipeline-step
+                                (append (cdr step-arguments)
+                                        (list :name (car step-arguments)))))
+                 (list step-arguments
+                       (phpinspect--make-pipeline-step :name 
step-arguments)))))
+
+    (unless (and (symbolp name) (fboundp name))
+      (error "Pipeline step name must be a symbol bound to a function"))
+
+    step))
+
+(defun phpinspect--pipeline-parse-steps (arguments-plist &optional steps async)
+  (if arguments-plist
+      (let ((key (pop arguments-plist))
+            (value (pop arguments-plist)))
+        (pcase key
+          (:into
+           (phpinspect--pipeline-parse-steps
+            arguments-plist
+            (cons (phpinspect--pipeline-parse-step value) steps)
+            async))
+          (:async
+           (phpinspect--pipeline-parse-steps
+            arguments-plist steps value))
+          (_ (error "Unexpected pipeline argument key: %s" key))))
+
+    (list async steps)))
+
+(defun phpinspect-pipeline-step-format-name (step)
+  (format "PHPInspect pipeline thread [%s]"
+          (symbol-name (phpinspect--pipeline-step-name step))))
+
+(defun phpinspect--pipeline-fn-wrap-with-read-pipeline (fn step)
+  (if (phpinspect--pipeline-step-with-auto-emit step)
+      fn
+    (lambda (task) (phpinspect--read-pipeline-emission (funcall fn task)))))
+
+(defun phpinspect--pipeline-fn-wrap-with-end-handler (fn pipeline-ctx)
+  (lambda (task)
+    (let ((result (funcall fn task)))
+      (when (phpinspect-pipeline-end-p result)
+        (phpinspect-pipeline-ctx-register-end pipeline-ctx result))
+
+      result)))
+
+(defun phpinspect--pipeline-fn-wrap-with-error-handler (fn step pipeline-ctx)
+  (lambda (task)
+    (condition-case err
+        (funcall fn task)
+      (t
+       (phpinspect-make-pipeline-end
+        :error err :thread (current-thread))))))
+
+(defun phpinspect--pipeline-fn-wrap-with-ctx (fn ctx)
+  (if ctx
+      (lambda (task)
+        (funcall fn ctx task))
+    (lambda (task) (funcall fn task))))
+
+(defun phpinspect--pipeline-fn-wrap-with-emitter (fn out-queue)
+  (lambda (task)
+    (let ((emission (if (phpinspect-pipeline-end-p task)
+                        (phpinspect-make-pipeline-end :thread (current-thread))
+                      (funcall fn task))))
+      (phpinspect--pipeline-enqueue out-queue emission)
+      emission)))
+
+(defun phpinspect--pipeline-make-run-step-function (pipeline-ctx step 
out-queue)
+  (let ((step-fn (thread-first
+                   (phpinspect--pipeline-step-name step)
+                   (phpinspect--pipeline-fn-wrap-with-ctx 
(phpinspect--pipeline-step-with-context step))
+                   (phpinspect--pipeline-fn-wrap-with-read-pipeline step)
+                   (phpinspect--pipeline-fn-wrap-with-error-handler step 
pipeline-ctx)
+                   (phpinspect--pipeline-fn-wrap-with-emitter out-queue)
+                   (phpinspect--pipeline-fn-wrap-with-end-handler 
pipeline-ctx))))
+    (lambda (task)
+      (let ((result (funcall step-fn task)))
+        (when (phpinspect-pipeline-end-p task)
+          (phpi-job-queue-end))))))
+
+(defun phpinspect--pipeline-chain (ctx steps &optional out-queue)
+  (if-let ((step (pop steps)))
+      (let* ((job-queue (phpi-start-job-queue 
(phpinspect-pipeline-step-format-name step)
+                          (phpinspect--pipeline-make-run-step-function ctx 
step out-queue))))
+        (phpinspect-pipeline-ctx-register-thread ctx (phpi-job-queue-thread 
job-queue) job-queue)
+        (phpinspect--pipeline-chain ctx steps job-queue))
+    out-queue))
+
+(defun phpinspect-pipeline (seed-form &rest arguments-plist)
+  (declare (indent defun))
+  (pcase-let ((`(,async ,steps) (phpinspect--pipeline-parse-steps 
arguments-plist))
+              (ctx (phpinspect-make-pipeline-ctx)))
+    (when seed-form
+      (let* (results
+             (in-queue (phpi-start-job-queue "Pipeline result accumulator"
+                         (lambda (result)
+                           (if (phpinspect-pipeline-end-p result)
+                               (phpi-progn
+                                (setq results (nreverse results))
+                                (phpi-job-queue-end))
+                             (push result results))))))
+
+        (let ((out-queue (phpinspect--pipeline-chain ctx steps in-queue)))
+          (phpinspect--pipeline-enqueue
+           out-queue
+           (phpinspect-make-pipeline-emission :collection seed-form) 
'no-notify)
+
+          (phpinspect--pipeline-enqueue
+           out-queue (phpinspect-make-pipeline-end :thread (current-thread))))
+
+        (if async
+            (phpi-run-threaded "Pipeline result awaiter"
+              (thread-join (phpi-job-queue-thread in-queue))
+
+              (condition-case err
+                  (progn
+                    (phpinspect-pipeline-ctx-close ctx)
+                    ;; async consumers may use the result being non-nil as a
+                    ;; means to determine whether the pipeline has finished
+                    ;; executing or not. So we return a symbol when the result
+                    ;; is nil to prevent consumer threads from waiting
+                    ;; endlessly.
+                    (funcall async (or results 
'phpinspect-pipeline-nil-result) nil))
+                (t
+                 (funcall async results err))))
+          (progn
+            (thread-join (phpi-job-queue-thread in-queue))
+            (phpinspect-pipeline-ctx-close ctx)
+            results))))))
+
+
+(cl-defstruct (phpinspect--pipeline-step (:constructor 
phpinspect--make-pipeline-step))
+  (with-context nil
+           :type any
+           :documentation
+           "An object that is passed as first argument to all step executions")
+  (with-auto-emit nil
+                  :type boolean)
+  (name nil
+        :type symbol
+        :documentation
+        "The name of this step"))
 
 (provide 'phpinspect-pipeline)
 ;;; phpinspect-pipeline.el ends here
diff --git a/phpinspect-project.el b/phpinspect-project.el
index e6f99391ff..a895cd322b 100644
--- a/phpinspect-project.el
+++ b/phpinspect-project.el
@@ -446,7 +446,7 @@ before the search is executed."
          (dir (phpinspect-index-dir-task-dir task)))
     (phpinspect--log "Indexing directory %s" dir)
     (phpinspect-pipeline (phpinspect-fs-directory-files-recursively fs dir 
"\\.php$")
-      :into (phpinspect-project-add-file-index :with-context project))))
+      :into `(phpinspect-project-add-file-index :with-context ,project))))
 
 (provide 'phpinspect-project)
 ;;; phpinspect-project.el ends here
diff --git a/phpinspect-thread.el b/phpinspect-thread.el
new file mode 100644
index 0000000000..d5449ed809
--- /dev/null
+++ b/phpinspect-thread.el
@@ -0,0 +1,183 @@
+;;; phpinspect-thread.el --- Threading functions for phpinspect  -*- 
lexical-binding: t; -*-
+
+;; Copyright (C) 2024  Hugo Thunnissen
+
+;; Author: Hugo Thunnissen <de...@hugot.nl>
+
+;; This program is free software; you can redistribute it and/or modify
+;; it under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+
+;; This program is distributed in the hope that it will be useful,
+;; but WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+;; GNU General Public License for more details.
+
+;; You should have received a copy of the GNU General Public License
+;; along with this program.  If not, see <https://www.gnu.org/licenses/>.
+
+;;; Commentary:
+
+;;
+
+;;; Code:
+
+(require 'phpinspect-queue)
+(require 'phpinspect-util)
+
+(cl-defstruct (phpi-condition (:constructor phpi--make-condition))
+  (-value nil)
+  (-condvar nil :type condition-variable)
+  (-mx nil :type mutex))
+
+(gv-define-setter phpi-condition-value (val condition)
+  `(phpi-condition--set-value ,condition ,val))
+
+(define-error 'phpinspect-kill-thread
+              "Thread killed")
+
+(define-error 'phpinspect-job-queue-end
+              "Job queue ended")
+
+(defun phpi-condition-value (condition)
+  (phpi-condition--value condition))
+
+(defun phpi-condition-notify (condition)
+  (with-mutex (phpi-condition--mx condition)
+    (condition-notify (phpi-condition--condvar condition) t)))
+
+(defun phpi-condition--set-value (condition value)
+  (setf (phpi-condition--value condition) value)
+  (phpi-condition-notify condition))
+
+(defun phpi-condition-wait (condition)
+  (let ((mx (phpi-condition--mx condition))
+        (condvar (phpi-condition--condvar condition))
+        result)
+
+    (while (not (setq result (phpi-condition--value condition)))
+      (with-mutex mx
+        (condition-wait condvar)))
+
+    (setf (phpi-condition--value condition) nil)
+    result))
+
+(defun phpi-make-condition (&optional value)
+  (let* ((mx (make-mutex))
+         (condvar (make-condition-variable mx)))
+    (phpi--make-condition :-mx mx :-condvar condvar :-value value)))
+
+(defvar phpinspect--main-thread-starving (phpi-make-condition 'no))
+
+(defun phpi-thread-kill (thread)
+  (thread-signal thread 'phpinspect-kill-thread nil))
+
+(defmacro phpi-run-threaded (thread-name &rest body)
+  (declare (indent 1))
+  (let ((err-sym (gensym)))
+  `(make-thread
+    (lambda ()
+      (condition-case ,err-sym
+          (progn ,@body)
+        (phpinspect-kill-thread)
+        (error
+         (phpinspect-message
+          "Thread [%s (exited)] encountered an error: %s"
+          (thread-name (current-thread))
+          ,err-sym))))
+
+    ,thread-name)))
+
+(defun phpi--main-thread-starving-p ()
+  (if (or quit-flag (phpinspect--input-pending-p))
+      'yes
+    'no))
+
+(defun phpi-main-thread-starving-p ()
+  (let ((starving (phpi--main-thread-starving-p)))
+    (when (eq 'yes starving)
+      (setf (phpi-condition-value phpinspect--main-thread-starving) starving)
+      t)))
+
+(defun phpi-await-main-thread-nourished ()
+  (when (phpi-main-thread-startving-p)
+    (while
+        (eq 'yes (phpi-condition-wait
+                  phpinspect--main-thread-starving)))))
+
+(defun phpi-job-queue-end ()
+  (signal 'phpinspect-job-queue-end nil))
+
+(defun phpi--notify-main-thread-nourished ()
+  (setf (phpi-condition-value phpinspect--main-thread-starving) 'no))
+
+(defvar phpinspect-main-thread-nourishment 0.1
+  "Amount of seconds to pause all threads when input is pending.")
+
+(defvar phpinspect-main-thread-nourishment-timer
+  (run-with-idle-timer phpinspect-main-thread-nourishment t 
#'phpi--notify-main-thread-nourished))
+
+(define-inline phpi-thread-yield ()
+  "Like `thread-yield', but takes extra care not to starve the main thread.
+
+If current thread is the main thread, this function does nothing."
+  (inline-quote
+   (unless (eq main-thread (current-thread))
+     (if (phpi-main-thread-starving-p)
+         (phpi-await-main-thread-nourished)
+       (thread-yield)))))
+
+(defmacro phpi-progn (&rest body)
+  `(prog1
+       (progn ,@body)
+     (phpi-thread-yield)))
+
+(cl-defstruct (phpinspect-job-queue (:constructor phpi--make-job-queue)
+                                    (:conc-name phpi-job-queue-)
+                                    (:include phpinspect-queue))
+  (thread nil :type thread))
+
+(defun phpi-start-job-queue (name job-handler)
+  (declare (indent 1))
+  (let* ((condition (phpi-make-condition))
+         queue)
+    (setq queue (phpi--make-job-queue
+                 :subscription
+                 (lambda ()
+                   (setf (phpi-condition-value condition)
+                         (phpinspect-queue-first queue)))))
+
+    (setf (phpi-job-queue-thread queue)
+          (phpi-run-threaded (format "(job queue) %s" name)
+            (let ((ended nil))
+              (catch 'phpi--break
+                (while t
+                  (if-let ((job (phpinspect-queue-dequeue queue)))
+                      (phpi-progn
+                       (condition-case err
+                           (funcall job-handler job)
+                         (phpinspect-job-queue-end
+                          ;; If job queue end is signaled, exit after queue has
+                          ;; been fully depleted.
+                          (setq ended t)
+                          (unless (phpinspect-queue-first queue)
+                            (throw 'phpi--break nil)))))
+
+                    (if ended
+                        ;; End was signaled previously and the queue is empty. 
Exit.
+                        (throw 'phpi--break nil)
+                      (phpi-condition-wait condition))))))))
+    queue))
+
+(defun phpi-job-queue-live-p (queue)
+  (and (threadp (phpi-job-queue-thread queue))
+       (thread-live-p (phpi-job-queue-thread queue))))
+
+(defun phpi-job-queue-kill (queue)
+  (when (phpi-job-queue-live-p queue)
+    (phpi-kill-thread (phpi-job-queue-thread queue))))
+
+
+(provide 'phpinspect-thread)
+;;; phpinspect-thread.el ends here
diff --git a/test/test-autoload.el b/test/test-autoload.el
index e210c041dc..64583d0850 100644
--- a/test/test-autoload.el
+++ b/test/test-autoload.el
@@ -88,7 +88,7 @@
       :async (lambda (res err)
                (setq result res
                      error err))
-      :into (phpinspect-iterate-composer-jsons :with-context autoloader))
+      :into `(phpinspect-iterate-composer-jsons :with-context ,autoloader))
 
     (while (not (or result error))
       (thread-yield))
@@ -159,8 +159,8 @@
       :async (lambda (res err)
                (setq result res
                      error err))
-      :into (phpinspect-iterate-composer-jsons :with-context autoloader)
-      :into phpinspect-al-strategy-execute)
+      :into `(phpinspect-iterate-composer-jsons :with-context ,autoloader)
+      :into #'phpinspect-al-strategy-execute)
 
     (while (not (or result error))
       (thread-yield))
diff --git a/test/test-buffer.el b/test/test-buffer.el
index 26724f87d3..d426ac6297 100644
--- a/test/test-buffer.el
+++ b/test/test-buffer.el
@@ -772,15 +772,16 @@ class TestClass
 ;;       (should (seq-every-p #'phpinspect-meta-p 
(phpinspect-buffer--deletions buffer))))))
 
 (ert-deftest phpinspect-buffer-parse-class-insertion ()
-  (with-temp-buffer
-    (let ((buffer (phpinspect-claim-buffer (current-buffer))))
-      (insert "<?php ")
+  (dlet ((phpi-shadow--run-sync t))
+    (with-temp-buffer
+      (let ((buffer (phpinspect-claim-buffer (current-buffer))))
+        (insert "<?php ")
 
-      (insert "class")
-      (phpinspect-buffer-parse buffer)
-      (insert " Name")
-      (phpinspect-buffer-parse buffer)
-      (insert " {} ")
+        (insert "class")
+        (phpinspect-buffer-parse buffer)
+        (insert " Name")
+        (phpinspect-buffer-parse buffer)
+        (insert " {} ")
 
-      (should (equal '(:root (:class (:class-declaration (:word "Name")) 
(:block)))
-                     (phpinspect-buffer-parse buffer))))))
+        (should (equal '(:root (:class (:class-declaration (:word "Name")) 
(:block)))
+                       (phpinspect-buffer-parse buffer)))))))
diff --git a/test/test-eldoc.el b/test/test-eldoc.el
index a34767de2f..5752266e35 100644
--- a/test/test-eldoc.el
+++ b/test/test-eldoc.el
@@ -28,7 +28,6 @@ class Thing
            (project (phpinspect--make-project :autoload 
(phpinspect-make-autoloader) :worker 'nil-worker))
            (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)
 
       (backward-char)
@@ -38,8 +37,6 @@ class Thing
       (backward-char 8)
       (setq first-arg-pos (point))
 
-      (phpinspect-buffer-reindex buffer)
-
       ;; Strategy should not trigger inside constructor/function arguments
       (let ((query (phpinspect-make-eldoc-query :point inside-nested-list-pos 
:buffer buffer))
             (strat (phpinspect-make-eld-function-args))
@@ -52,6 +49,8 @@ class Thing
               (strat (phpinspect-make-eld-function-args))
               (rctx (phpinspect-get-resolvecontext project 
(phpinspect-buffer-parse-map buffer) (car expected))))
 
+          (phpinspect-buffer-update-project-index buffer)
+
           ;; Subject is correct
           (should (phpinspect-word-p (car (phpinspect--resolvecontext-subject 
rctx))))
 
diff --git a/test/test-pipeline.el b/test/test-pipeline.el
index 27e3334723..131d86d0c4 100644
--- a/test/test-pipeline.el
+++ b/test/test-pipeline.el
@@ -33,7 +33,7 @@
   (let (result error)
 
     (phpinspect-pipeline (list "Linux" "Emacs")
-      :into phpinspect--correct-the-record
+      :into #'phpinspect--correct-the-record
       :async (lambda (res err)
                (setq result res
                      error err)))
@@ -52,7 +52,7 @@
 
   (let (result error)
     (phpinspect-pipeline (list "Holy smokey")
-      :into phpinspect--aah-it-broke
+      :into #'phpinspect--aah-it-broke
       :async (lambda (res err)
                (setq result res
                      error err)))
@@ -61,6 +61,27 @@
       (thread-yield))
 
     (should error)
-    (should (equal '(phpinspect-pipeline-error
-                     "Thread phpinspect-pipeline-phpinspect--aah-it-broke 
signaled error: (it-brokey . Holy smokey)")
-                   error))))
+    (should (phpinspect-pipeline-error-p error))
+    (should (string-suffix-p "(it-brokey . Holy smokey)" (cadr error)))))
+
+(ert-deftest phpinspect-pipeline-auto-emit ()
+  (let (result error)
+
+    (phpinspect-pipeline (list "Linux" "Emacs")
+      :into `(format :with-context "It's not %s"
+                     :with-auto-emit t)
+      :into `(format :with-context "%s, but GNU/... are you listening?"
+                     :with-auto-emit t)
+      :async (lambda (res err)
+               (setq result res
+                     error err)))
+
+    (while (not (or result error))
+      (thread-yield))
+
+    (should-not error)
+
+    (should (equal '("It's not Linux, but GNU/... are you listening?"
+                     "It's not Emacs, but GNU/... are you listening?")
+                   result))
+    (should-not error)))

Reply via email to