anchor.diff:    When filter returns non-nil, the result is added to `suggestions' and
     `update-notifier' is notified, if `notification-delay' has been exceeded.
   - Last the `filter-postprocessor' is run the SOURCE and the INPUT.
  -- Finally, `ready-notifier' is fired up."
  +  Its return value is assigned to the list of suggestions.
  +- Finally, `ready-notifier' is fired up.
  +
  +The reason we filter in 3 stages is to allow both for asynchronous and
  +synchronous filtering.  The benefit of asynchronous filtering is that it sends
  +feedback to the user while the list of suggestions is being computed."
     (when (and (update-thread source)
                ;; TODO: This is prone to a race condition.
                (bt:thread-alive-p (update-thread source)))
  
positive.code:                     :buffer (make-instance 'jpl-queues:bounded-fifo-queue :capacity size)))))
  
  (defun update (source input new-ready-channel) ; TODO: Store `input' in the source?
    "Update SOURCE to narrow down the list of suggestions according to INPUT.
  If a previous suggestion computation was not finished, it is forcefully terminated.
  
  - First the `filter-preprocessor' is run over a copy of `initial-suggestions'.
  
negative.code:   (setf (update-thread source)
          (bt:make-thread
           (lambda ()
             (flet ((wait-for-initial-suggestions ()
                      (bt:acquire-lock (initial-suggestions-lock source))
                      (bt:release-lock (initial-suggestions-lock source)))
                    (preprocess (initial-suggestions-copy)
                      (if (filter-preprocessor source)
                          (ensure-suggestions-list
                           source
                           (maybe-funcall (filter-preprocessor source)
                                          initial-suggestions-copy source input))
                          (ensure-suggestions-list source input)))
                    (process! (preprocessed-suggestions last-notification-time)
                      (setf (slot-value source 'suggestions) '())
                      (if (or (str:empty? input)
                              (not (functionp (filter source))))
                          (setf (slot-value source 'suggestions) preprocessed-suggestions)
                          (dolist (suggestion preprocessed-suggestions)
                            (sera:and-let* ((processed-suggestion
                                             (funcall (filter source) input suggestion)))
                              (setf (slot-value source 'suggestions)
                                    (insert-item-at suggestion (sort-predicate source)
                                                    (suggestions source)))
                              (let* ((now (get-internal-real-time))
                                     (duration (/ (- now last-notification-time)
                                                  internal-time-units-per-second)))
                                (when (> duration (notification-delay source))
                                  (calispel:! (update-notifier source) t)
                                  (setf last-notification-time now)))))))
                    (postprocess! ()
                      (when (filter-postprocessor source)
                        (setf (slot-value source 'suggestions)
                              (ensure-suggestions-list
                               source
                               ;; TODO: Pass `suggestions' to filter-postprocessor, to mirror filter-preprocessor signature.
                               (maybe-funcall (filter-postprocessor source) source input))))))
               (wait-for-initial-suggestions)
               (let ((last-notification-time (get-internal-real-time)))
                 ;; TODO: Move last-notification-time inside `process'.
                 (process!
                  (preprocess
                   ;; We copy the list of initial-suggestions so that the
                   ;; preprocessor cannot modify it.
                   (mapcar #'copy-object
                           (initial-suggestions source)))
                  last-notification-time))
               ;; TODO: Notify when filter is done.
               (postprocess!)
               (bt:with-lock-held ((wrote-to-ready-channel-lock source))
                 (unless (wrote-to-ready-channel-p source)
                   (calispel:! new-ready-channel source)
                   (setf (wrote-to-ready-channel-p source) t))))))))
  