anchor.diff:       (filter-preprocessor #'delete-inexact-matches
                             :type (or null function)
                             :documentation
  -                          "Function called over a copy of `initial-suggestions', when
  -input is modified, before filtering the suggestions.")
  +                          "Function called when
  +input is modified, before filtering the suggestions.
  +It is passed the following arguments:
  +- a copy of `initial-suggestions';
  +- the source;
  +- the input.")
   
        (filter-postprocessor nil
                              :type (or null function)
                              :documentation
  -                           "Function called over the source and the input,
  -when input is modified, after filtering the suggestions.")
  +                           "Function called when input is modified, after
  +filtering the suggestions with `filter'.
  +It is passed the following arguments:
  +- the source;
  +- the input.")
   
        (sort-predicate #'score>
                        :type (or null function)
  
positive.code:   (setf (update-thread source)
          (bt:make-thread
           (lambda ()
             ;; Wait until initial-suggestions are ready.
             (bt:acquire-lock (initial-suggestions-lock source))
             (bt:release-lock (initial-suggestions-lock source))
             (let ((last-notification-time (get-internal-real-time))
                   (preprocessed-suggestions (mapcar #'copy-object
                                                     (initial-suggestions source))))
               (setf preprocessed-suggestions
                     (if (filter-preprocessor source)
                         (ensure-suggestions-list
                          source
                          (maybe-funcall (filter-preprocessor source)
                                         preprocessed-suggestions source input))
                         (ensure-suggestions-list source input)))
               ;; TODO: Should we really reset the suggestions here?
               (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)))))))
  
             ;; TODO: Pass `filter-preprocessor' result to source in case filter is not run?
             (maybe-funcall (filter-postprocessor source) source input)
             (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)))))))
  
negative.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'.
  