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

    Implement computed-on-demand dynamic completion table
    
    This is a massive quality of life improvement that seems to remove almost 
all
    editing latency that was introduced by triggering of completion functions 
while
    editing the buffer.
---
 phpinspect-buffer.el     |  14 +++-
 phpinspect-completion.el | 211 +++++++++++++++++++++--------------------------
 phpinspect-thread.el     |  19 +++--
 test/test-completion.el  |  19 ++---
 4 files changed, 125 insertions(+), 138 deletions(-)

diff --git a/phpinspect-buffer.el b/phpinspect-buffer.el
index caaab766d0..b5689fe32a 100644
--- a/phpinspect-buffer.el
+++ b/phpinspect-buffer.el
@@ -747,6 +747,7 @@ If provided, PROJECT must be an instance of 
`phpinspect-project'."
   (queue nil :type phpinspect--queue)
   (thread nil :type thread)
   (id nil :type integer)
+  (-index-waiting-thread nil)
   (-last-change (phpi-make-condition))
   (-synced-change (phpi-make-condition))
   (-indexed-change (phpi-make-condition))
@@ -866,7 +867,17 @@ indexation"))
     ('parse-fresh
      (phpi-shadow-parse-fresh shadow))
     ('update-project-index
-     (phpi-shadow-update-project-index shadow))
+     (let ((autoloader (phpinspect-project-autoload
+                        (phpinspect-buffer-project
+                         (phpi-shadow-origin shadow)))))
+       (if (phpinspect-autoloader-refreshing-p autoloader)
+           (unless (and-let* ((thread (phpi-shadow--index-waiting-thread 
shadow))
+                              ((thread-live-p thread))))
+             (setf (phpi-shadow--index-waiting-thread shadow)
+                   (phpi-run-threaded "shadow-index-notifier"
+                     (phpinspect-autoloader-await-refresh autoloader)
+                     (phpi-shadow-enqueue-task shadow 'update-project-index))))
+       (phpi-shadow-update-project-index shadow))))
     (_
      (phpinspect-message
       "Shadow thread received unsupported task type: %s"
@@ -902,6 +913,7 @@ indexation"))
 
 (defun phpi-shadow-await-synced (shadow &optional _allow-interrupt)
   (phpi-shadow-assert-live-p shadow)
+
   (unless (phpi-shadow-is-me-p shadow)
     (phpi-condition-wait (phpi-shadow--synced-change shadow)
                          (lambda (change)
diff --git a/phpinspect-completion.el b/phpinspect-completion.el
index ea70f752f0..a476057b3a 100644
--- a/phpinspect-completion.el
+++ b/phpinspect-completion.el
@@ -51,19 +51,45 @@
   "Creates a `phpinspect--completion` for a possible completion
 candidate. Candidates can be indexed functions and variables.")
 
-(cl-defstruct (phpinspect--completion-list
-               (:constructor phpinspect--make-completion-list))
+
+(cl-defstruct (phpi-comp-result
+               (:constructor phpi-make-comp-result))
   "Contains all data for a completion at point"
   (completion-start nil
                     :type integer)
   (completion-end nil
                   :type integer)
+  (query nil
+         :type phpinspect-completion-query)
+  (rctx nil
+        :type phpinspect--resolvecontext)
+  (strategy nil
+            :type list)
+
+  (-list nil
+        :type phpinspect--completion-list))
+
+(cl-defstruct (phpinspect--completion-list
+               (:constructor phpinspect--make-completion-list))
+  "Contains all data for a completion at point"
   (completions (make-hash-table :test #'equal :size 10000 :rehash-size 2)
                :type hash-table
                :documentation
                "A list of completion strings")
   (has-candidates nil))
 
+(defun phpi-comp-result-list (result)
+  (with-memoization (phpi-comp-result--list result)
+    (let ((clist (phpinspect--make-completion-list)))
+      (when (phpi-comp-result-strategy result)
+        (dolist (candidate (phpinspect-comp-strategy-execute
+                            (phpi-comp-result-strategy result)
+                            (phpi-comp-result-query result)
+                            (phpi-comp-result-rctx result)))
+          (phpinspect--completion-list-add clist (phpinspect--make-completion 
candidate))))
+
+      clist)))
+
 (cl-defgeneric phpinspect--completion-list-add
     (comp-list completion)
   "Add a completion to a completion-list.")
@@ -307,86 +333,40 @@ completion result.")
   "Execute QUERY.
 
 Returns list of `phpinspect--completion'."
-  (let* ((last-parameters phpinspect--last-completion-parameters)
-         ;; Check if caching is at all possible, before parsing the buffer. 
This
-         ;; needs to happen now, as tainted regions are removed from the taint
-         ;; pool after a buffer parse. We need the tainted region to determine
-         ;; if the only edit is one performed during completion.
-         (maybe-cache? (and
-                        last-parameters
-                        (phpinspect--completion-query-maybe-should-cache
-                         (phpinspect--completion-parameters-query 
last-parameters)
-                         query))))
-
-    (let* ((buffer (phpinspect-completion-query-buffer query))
-           (point (phpinspect-completion-query-point query))
-           (buffer-map (phpinspect-buffer-parse-map buffer))
-           (rctx (phpinspect-get-resolvecontext
-                  (phpinspect-buffer-project buffer) buffer-map point))
-           (completion-list (phpinspect--make-completion-list)))
-
-      (phpinspect-buffer-update-project-index buffer)
-
-      (setq phpinspect--last-completion-list
-            (catch 'phpinspect--return
-              (dolist (strategy phpinspect-completion-strategies)
-                (when-let (region (phpinspect-comp-strategy-supports strategy 
query rctx))
-                  ;; Check if using the cached completion list is possible.
-                  (if-let ((nil)
-                           (maybe-cache?)
-                           ;; There is a previous list available
-                           (last-list phpinspect--last-completion-list)
-                           ;; The list had candidates in it
-                           ((phpinspect--completion-list-has-candidates 
last-list))
-                           ;; The subject of the last resolvecontext is the 
same
-                           ;; (so likely to evaluate to the same results).
-                           ((equal (butlast 
(phpinspect--resolvecontext-subject rctx))
-                                   (butlast 
(phpinspect--completion-parameters-subject
-                                             last-parameters))))
-                           ;; The completion strategy is the same as the last
-                           ;; one used.
-                           ((eq strategy
-                                (phpinspect--completion-parameters-strategy
-                                 last-parameters))))
-                      ;; We can safely use the cached list: All parameters used
-                      ;; for the last completion seem to match the current one.
-                      (progn
-                        ;; Update the region, this is necessary for
-                        ;; completion-at-point to determine what is being
-                        ;; completed.
-                        (setf (phpinspect--completion-list-completion-start 
last-list)
-                              (car region)
-                              (phpinspect--completion-list-completion-end 
last-list)
-                              (cadr region))
-
-                        (throw 'phpinspect--return last-list))
-
-                    ;; We can't use the cached list, proceed executing the
-                    ;; strategy.
-                    (setf (phpinspect--completion-list-completion-start 
completion-list)
-                          (car region)
-                          (phpinspect--completion-list-completion-end 
completion-list)
-                          (cadr region))
-
-                    ;; update last used parameters
-                    (setq phpinspect--last-completion-parameters
-                          (phpinspect--make-completion-parameters
-                           :subject (phpinspect--resolvecontext-subject rctx)
-                           :strategy strategy
-                           :query query))
-
-                    (phpinspect--log "Found matching completion strategy. 
Executing...")
-                    (dolist (candidate (phpinspect-comp-strategy-execute 
strategy query rctx))
-                      (phpinspect--completion-list-add
-                       completion-list (phpinspect--make-completion 
candidate)))
-
-                    (throw 'phpinspect--return completion-list))))
-                ;; return empty list
-                completion-list))))
-
-  (phpinspect--log "Returning completion list %s" 
phpinspect--last-completion-list)
-  phpinspect--last-completion-list)
-
+  (let* (result thread)
+
+    (setq thread
+          (phpi-run-threaded "completion"
+            (setq result
+                  (let* ((buffer (phpinspect-completion-query-buffer query))
+                         (point (phpinspect-completion-query-point query))
+                         (buffer-map (phpinspect-buffer-parse-map buffer))
+                         (rctx (phpinspect-get-resolvecontext
+                                (phpinspect-buffer-project buffer) buffer-map 
point)))
+
+                    (catch 'phpinspect--return
+                      (dolist (strategy phpinspect-completion-strategies)
+                        (when-let (region (phpinspect-comp-strategy-supports 
strategy query rctx))
+                          (throw 'phpinspect--return
+                                 (phpi-make-comp-result
+                                  :completion-start (phpinspect-region-start 
region)
+                                  :completion-end (phpinspect-region-end 
region)
+                                  :rctx rctx
+                                  :query query
+                                  :strategy strategy)))))))))
+
+    (let ((cancelled t))
+      (with-timeout (0.2)
+        (while-no-input
+          (while (and (thread-live-p thread) (not (input-pending-p t)))
+            (sleep-for 0.02))
+          (setq cancelled nil)))
+
+      (if cancelled
+          (phpi-make-comp-result
+           :completion-start (point)
+           :completion-end (point))
+        result))))
 
 ;; FIXME: completions should be stored in an LRU cache keyed with object
 ;; pointers of the things they represent (variables/functions). Re-creating 
them
@@ -466,20 +446,20 @@ Returns list of `phpinspect--completion'."
        ('variable "<va> ")))))
 
 
-(defun phpinspect--get-completion-at-point ()
+(defun phpinspect-complete-at-point ()
   (catch 'phpinspect-interrupted
-    (let ((comp-list (phpinspect-completion-query-execute 
(phpinspect--get-completion-query))))
-
-      (and (phpinspect--completion-list-has-candidates comp-list)
-           (list (phpinspect--completion-list-completion-start comp-list)
-                 (phpinspect--completion-list-completion-end comp-list)
+    (when-let ((result (phpinspect-completion-query-execute 
(phpinspect--get-completion-query))))
+      (list (phpi-comp-result-completion-start result)
+            (phpi-comp-result-completion-end result)
                  (completion-table-dynamic (lambda (_)
-                                             
(phpinspect--completion-list-strings comp-list)))
+                                             
(phpinspect--completion-list-strings
+                                              (phpi-comp-result-list result))))
                  :affixation-function
                  (lambda (completions)
                    (let (affixated completion)
                      (dolist (comp completions)
-                       (setq completion 
(phpinspect--completion-list-get-metadata comp-list comp))
+                       (setq completion 
(phpinspect--completion-list-get-metadata
+                                         (phpi-comp-result-list result) comp))
                        (push (list comp (phpinspect--prefix-for-completion 
completion)
                                    (concat " " (phpinspect--completion-meta 
completion)))
                              affixated))
@@ -487,7 +467,7 @@ Returns list of `phpinspect--completion'."
                  :exit-function
                  (lambda (comp-name state)
                    (let ((comp (phpinspect--completion-list-get-metadata
-                                phpinspect--last-completion-list
+                                (phpi-comp-result-list result)
                                 comp-name)))
                      (when (and (eq 'finished state)
                                 (eq 'function (phpinspect--completion-kind 
comp)))
@@ -498,36 +478,33 @@ Returns list of `phpinspect--completion'."
                  :company-kind (lambda (comp-name)
                                  (let ((comp
                                         
(phpinspect--completion-list-get-metadata
-                                         phpinspect--last-completion-list
+                                         (phpi-comp-result-list result)
                                          comp-name)))
                                    (if comp
                                        (phpinspect--completion-kind comp)
                                      (phpinspect--log  "Unable to find 
matching completion for name %s" comp-name)
-                                     nil))))))))
-
-(defun phpinspect-complete-at-point ()
-(let* ((buf (current-buffer))
-       result thread
-       (completion-ready nil)
-       (start (point))
-       (end (point)))
-
-  (setq thread (phpi-run-threaded "completion-at-point"
-                 (with-current-buffer buf
-                   (setq result (phpinspect--get-completion-at-point))
-                   (setq completion-ready t)
-                   (message "set result"))))
-
-  (message "STARTING WAIT")
-  (while (thread-live-p thread)
-    (message "waiting")
-    (sit-for 0.001))
-
-  (message "RESULT: %s" completion-ready)
-  (if completion-ready
-      (list start end result)
-    ;; Return a dummy completion table that will pass the try-completion test
-    (list start end '("") :exclusive 'no)))  )
+                                     nil)))
+                 :exclusive (if (phpi-comp-result-strategy result) 'yes 
'no)))))
+
+;; (defun phpinspect-complete-at-point ()
+;;   (phpinspect--get-completion-at-point))
+
+;;   (setq thread (phpi-run-threaded "completion-at-point"
+;;                  (with-current-buffer buf
+;;                    (setq result
+;;                    (setq completion-ready t)
+;;                    (message "set result"))))
+
+;;   (message "STARTING WAIT")
+;;   (while (thread-live-p thread)
+;;     (message "waiting")
+;;     (sit-for 0.001))
+
+;;   (message "RESULT: %s" completion-ready)
+;;   (if completion-ready
+;;       (list start end result)
+;;     ;; Return a dummy completion table that will pass the try-completion 
test
+;;     (list start end '("") :exclusive 'no)))  )
 
 ;; (let* ((buf (current-buffer))
   ;;        result thread)
diff --git a/phpinspect-thread.el b/phpinspect-thread.el
index 543b928e47..d80058aa9a 100644
--- a/phpinspect-thread.el
+++ b/phpinspect-thread.el
@@ -68,9 +68,9 @@
 
     result))
 
-(defun phpi-make-condition (&optional value)
+(defun phpi-make-condition (&optional value name)
   (let* ((mx (make-mutex))
-         (condvar (make-condition-variable mx)))
+         (condvar (make-condition-variable mx name)))
     (phpi--make-condition :-mx mx :-condvar condvar :-value value)))
 
 (defvar phpinspect--main-thread-starving (phpi-make-condition 'no))
@@ -119,7 +119,7 @@
 If current thread is the main thread, this function does nothing."
   (inline-quote
    (unless (eq main-thread (current-thread))
-     (thread-yield))))
+       (thread-yield))))
 
 (defmacro phpi-progn (&rest body)
   `(prog1
@@ -133,7 +133,7 @@ If current thread is the main thread, this function does 
nothing."
 
 (defun phpi-start-job-queue (name job-handler)
   (declare (indent 1))
-  (let* ((condition (phpi-make-condition))
+  (let* ((condition (phpi-make-condition nil (format "%s condition" name)))
          queue)
     (setq queue (phpi--make-job-queue
                  :subscription
@@ -143,7 +143,7 @@ If current thread is the main thread, this function does 
nothing."
 
     (setf (phpi-job-queue-thread queue)
           (phpi-run-threaded (format "(job queue) %s" name)
-            (let ((ended nil)
+            (let (ended
                   (inhibit-quit t))
               (catch 'phpi--break
                 (while t
@@ -155,14 +155,15 @@ If current thread is the main thread, this function does 
nothing."
                           ;; If job queue end is signaled, exit after queue has
                           ;; been fully depleted.
                           (setq ended t)
-                          (if (phpinspect-queue-first queue)
-                              (setq ended nil)
+                          (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))))))))
+                      (progn
+                        (setf (phpi-condition--value condition)
+                              (phpinspect-queue-first queue))
+                        (phpi-condition-wait condition #'identity)))))))))
     queue))
 
 (defun phpi-job-queue-live-p (queue)
diff --git a/test/test-completion.el b/test/test-completion.el
index 1fb1d3472b..775f8f3e5b 100644
--- a/test/test-completion.el
+++ b/test/test-completion.el
@@ -50,13 +50,13 @@ class Foo
     (let* ((query (phpinspect--get-completion-query))
            result)
 
-      (setq result (phpinspect-completion-query-execute query))
+      (setq result (phpi-comp-result-list (phpinspect-completion-query-execute 
query)))
       (should-not (phpinspect--completion-list-has-candidates result))
 
       (insert "F")
 
       (setq query (phpinspect--get-completion-query)
-            result (phpinspect-completion-query-execute query))
+            result (phpi-comp-result-list (phpinspect-completion-query-execute 
query)))
       (should-not (phpinspect--completion-list-has-candidates result)))))
 
 (ert-deftest phpinspect-complete-types-at-point ()
@@ -73,7 +73,7 @@ new ")
      (current-buffer) (phpinspect--make-dummy-composer-project-with-code))
 
     (let* ((query (phpinspect--get-completion-query))
-              (completions (phpinspect-completion-query-execute query))
+              (completions (phpi-comp-result-list 
(phpinspect-completion-query-execute query)))
            (types (phpinspect--completion-list-strings completions)))
 
          (should (length= types 6))
@@ -97,7 +97,7 @@ public ")
 
     (let* ((query (phpinspect--get-completion-query))
               (completions (phpinspect-completion-query-execute query))
-           (strings (phpinspect--completion-list-strings completions)))
+           (strings (phpinspect--completion-list-strings 
(phpi-comp-result-list completions))))
 
       (should strings))))
 
@@ -116,10 +116,9 @@ public function a() {} ")
      (current-buffer) (phpinspect--make-dummy-composer-project-with-code))
 
     (let* ((query (phpinspect--get-completion-query))
-              (completions (phpinspect-completion-query-execute query))
-           (strings (phpinspect--completion-list-strings completions)))
+              (completions (phpinspect-completion-query-execute query)))
 
-      (should-not strings))))
+      (should-not completions))))
 
 (ert-deftest phpinspect-dont-complete-within-comments ()
   (let ((cases (list "function a() { new // "
@@ -140,8 +139,6 @@ class A { ")
          (current-buffer) (phpinspect--make-dummy-composer-project-with-code))
 
         (let* ((query (phpinspect--get-completion-query))
-                  (completions (phpinspect-completion-query-execute query))
-               (strings (phpinspect--completion-list-strings completions)))
+                  (completions (phpinspect-completion-query-execute query)))
 
-          (should-not (phpinspect--completion-list-has-candidates completions))
-          (should-not strings))))))
+          (should-not completions))))))

Reply via email to