minad / cape

🦸cape.el - Completion At Point Extensions
GNU General Public License v3.0
573 stars 20 forks source link

async requests with capfs? #83

Closed mooseyboots closed 1 year ago

mooseyboots commented 1 year ago

hey, apols if this is not the place to ask, but i thought i'd head straight to the capf guru source.

i use capf completion in mastodon.el for user mentions and tags when composing a post. as this first requires a request to the server, and handling the resulting json, it is slow. (and somehow quite a bit slower than when i only had a company backend, not knowing capfs existed.)

i have been trying to use async functions to fetch the data, but this doesn't work with completion-at-point-functions.

the relevant part of my capf backend looks like more or less like so:

      (list start
            end
            (completion-table-dynamic
             (lambda (_)
               (fetch-completion-candidates-function start end)))
            :exclusive 'no
            :annotation-function
            (lambda (candidate)
              (concat " "
                      (annotation-function candidate)))))))

which just follows the manual.

is it possible to combine async requests with capfs at all? [i did see #7 and #14, but i wasn't sure they were the same issue, and the discussion was beyond me also.]

minad commented 1 year ago

If your company backend performs well, you can also use that via cape-company-to-capf. The performance should be as good then with Corfu. You can also write a capf which performs well but you should ensure that your caching is sufficient. In the capf given above you are likely observing many calls to your completion function with the same input?

mooseyboots commented 1 year ago

my code calls fetch-completion-candidates-function only once per input state, after 3 letters entered. then it is called again if another letter is typed.

i moved away from company because users requested a generic implementation/i was told it was bad form for a package to impose completion framework choice.

i still would like to have completion handle async requets though.

minad commented 1 year ago

I assume you use Company with your Mastodon Capf and completion hangs while typing? You can use while-no-input to make your synchronous requests interruptible. Company uses while-no-input internally to implement its :async completion futures.

(defun test-capf ()
  (when-let ((bounds (bounds-of-thing-at-point 'word)))
    (let ((start (copy-marker (car bounds)))
          (end (copy-marker (cdr bounds) t)))
      (list start end
            (completion-table-dynamic
             (lambda (_)
               ;; Interruptible candidate computation
               (let ((result (while-no-input
                               (test-fetch (buffer-substring-no-properties start end)))))
                 (and (consp result) result))))
            :exclusive 'no))))

(defun test-fetch (str)
  (sleep-for 0.1) ;; Simulate slow network access
  (list
   (concat str "mastodon1")
   (concat str "mastodon2")
   (concat str "mastodon3")
   (concat str "mastodon4")))

(setq-local completion-at-point-functions (list #'test-capf))
mooseyboots commented 1 year ago

ah thanks for the suggestion! i had tried that before but must have fumbled it, as your version has more of an effect.