17. The Waiting Widget
In this lesson, we implement a waiting widget that appears in the mode line while awaiting a response from OpenAI.
To achieve this, we define the chatgpt-mode-line-waiting function. This function either starts a timer that updates the mode line with the string "| ChatGPT" followed by a variable number of dots or stops this timer and remove the waiting widget from the mode line. This is done by updating the global-mode-string variable every 0.66 seconds. The timer is stored in the chatgpt-timer variable.
(defvar chatgpt-timer nil "Timer for waiting widget in mode line")
(defun chatgpt-mode-line-waiting (action)
"Start or stop a waiting widget in mode line.
Accepted values for ACTION includes `start' and `stop'."
(pcase action
('start
(setq chatgpt-timer
(run-with-timer
0 0.66
(let ((idx 0))
(lambda ()
(progn
(setq global-mode-string
`(:eval
,(concat "| ChatGPT."
(make-string (mod idx 3) ?.))))
(force-mode-line-update 'all)
(cl-incf idx)))))))
('stop
(cancel-timer chatgpt-timer)
(setq chatgpt-timer nil)
(setq global-mode-string nil)
(force-mode-line-update 'all))))
Next, we modify the chatgpt-send-request function to incorporate calls to chatgpt-mode-line-waiting. Before sending the request, we invoke this function to initiate the chatgpt-timer. Upon receiving a response from OpenAI, we also call this function to stop the timer in the sentinel function, ensuring it is the first action executed:
(defun chatgpt-send-request (prompt)
"Send the request with PROMPT to OpenAI."
(let* (...)
...
(chatgpt-mode-line-waiting 'start)
(make-process
:name "chatgpt"
:buffer (generate-new-buffer-name "chatgpt")
:command (list "sh" "-c" command)
:sentinel
(lambda (process event)
(chatgpt-mode-line-waiting 'stop)
(if (not (string= event "finished\n"))
...
...)))))
This implementation effectively manages the waiting widget within the mode line, providing us with clear visual feedback while awaiting a response from OpenAI.