Skip to content

Creating asynchronous completion commands

If you have a completion source that’s both dynamic and expensive to generate, ‘completing-read’ may not be the best choice. Instead, ‘consult--read’ serves as a thin wrapper around ‘completing-read’ that provides this functionality. For example, consider the following slow script that splits its input on space:

bash
#!/usr/bin/env bash
# simulate work
sleep .1
# generate completion candidates
printf "%s\n" "$*" | tr " " "\n" | sort

Let’s assume this script is callable as ‘testibus hello world’. To have Consult use it for completion, use ‘consult--process-collection’:

emacs-lisp
(consult--read
 (consult--process-collection
  (lambda (input) (list "testibus" (string-trim input))))
 :prompt "run testibus: ")

If the completion candidates are generated by Lisp instead, use ‘consult--dynamic-collection’:

emacs-lisp
(consult--read
 (consult--dynamic-collection
  (lambda (input)
    (sleep-for 0.1) ;; Simulate work
    (split-string input nil t)))
 :prompt "run testibus: ")

consult--dynamic-collection’ can take a function with a callback such that the completion UI can update for long running computations.

emacs-lisp
(consult--read
 (consult--dynamic-collection
  (lambda (input callback)
    (dotimes (i 3)
      (sleep-for 0.1) ;; Simulate work
      (funcall callback (mapcar (lambda (s) (format "%s%s" s i))
                                (split-string input nil t))))))
 :prompt "run testibus: ")

The asynchronous completion collections ‘consult--dynamic-collection’ and ‘consult--process-collection’ can be used for ‘consult--multi’ sources. Specify them as ‘:async’ field of the source plist.