How to provide completion of interactive function from a request?

150 Views Asked by At

I would like to provide completion for an emacs interactive function whose content would be based on a HTTP request's content. Here, the TODO explains it all.

(defun sensei-record-flow (flow-type)
  "Interactive function to record change in flow."
  
  (interactive (list (completing-read
                      "Flow: "
                      ;; TODO need to get the completion from a variable filled with user's
                      ;; flow types
                      '(("Foo" Foo) ("Bar" Bar) ("Baz" Baz))
                      nil t)))
  (let ((directory (projectile-project-root)))
    (setq sensei-cur-directory directory)
    (sensei-send-event-flow directory flow-type))
  )

How can I do that? request.el provides a :completion key to block until completion of a request, but it's not clear to me how to do that? I think what I need is to make the interactive function a continuation of the call to senseiilist-flows but I don't know how to do that in elisp.

EDIT: Here is the code of sensei-list-flows based on request.el

(defun sensei-list-flows (on-success)
  "List available flow types for the current user.

ON-SUCCESS is a function that's called upon successful completion of the call
and is passed a list of symbols listing user-defined flow names."
  (let* ((config (sensei-read-config))
         (auth-token (cdr (assoc 'authToken config)))
         (username (cdr (assoc 'configUser config)))
         (server-uri (cdr (assoc 'serverUri config))))
    (request (concat server-uri "api/users/" username)
      :headers `(("Content-Type" . "application/json")
                 ("X-API-Version" . "0.38.0")
                 ("Authorization" . ,(concat "Bearer " auth-token)))
      :parser 'json-read
      :error  (cl-function (lambda (&rest args &key error-thrown &allow-other-keys)
                             (message "Got error: %S" error-thrown)))
      :success  (cl-function (lambda (&key data &allow-other-keys)
                               (funcall on-success
                                     (map 'list 'car (cdr (assoc 'userFlowTypes data)))))))))
2

There are 2 best solutions below

2
On

I don't know anything about request.el and you haven't included any call to a list-flows in your quoted code and so that's impossible to comment on; but OOTB you would use C-hf url-retrieve-synchronously to fetch something via http like that.

In the resulting buffer, the url-http-end-of-headers variable is of particular note (+1 to get to the start of the content); and you may wish to check the url-http-* vars in that buffer in general.

0
On

I was able to achieve what I want using url-insert-file-contents which is similar to url-retrieve-synchronously:

(defun sensei-list-flows ()
  "List available flow types for the current user."
  (let* ((config (sensei-read-config))
         (auth-token (cdr (assoc 'authToken config)))
         (username (cdr (assoc 'configUser config)))
         (server-uri (cdr (assoc 'serverUri config)))
         (url-request-extra-headers `(("Content-Type" . "application/json")
                 ("X-API-Version" . "0.38.0")
                 ("Authorization" . ,(concat "Bearer " auth-token)))))
    (with-temp-buffer
      (url-insert-file-contents (concat server-uri "api/users/" username))
      (let ((flows (cdr (assoc 'userFlowTypes (json-parse-buffer :object-type 'alist)))))
        (map 'list 'car flows)))))

Then interactive completion is trivial:

(defun sensei-record-flow (flow-type)
  "Interactive function to record change in flow."

  (interactive (list (completing-read
                      "Flow: "
                      (sensei-list-flows)
                      nil t)))
  (let ((directory (projectile-project-root)))
    (setq sensei-cur-directory directory)
    (sensei-send-event-flow directory flow-type))
  )