5959 :type 'number
6060 :group 'chatgpt )
6161
62+ (defcustom chatgpt-input-method 'window
63+ " The method receive input."
64+ :type '(choice (const :tag " Read from minibuffer" minibuffer)
65+ (const :tag " Read inside new window" window))
66+ :group 'chatgpt )
67+
6268(defconst chatgpt-buffer-name-format " *ChatGPT: <%s>*"
6369 " Name of the buffer to use for the `chatgpt' instance." )
6470
8894; ;
8995; ;; Util
9096
97+ (defun chatgpt--kill-buffer (buffer-or-name )
98+ " Like function `kill-buffer' but in the safe way."
99+ (when-let ((buffer (get-buffer chatgpt-input-buffer-name)))
100+ (when (buffer-live-p buffer)
101+ (kill-buffer buffer))))
102+
91103(defun chatgpt--pop-to-buffer (buffer-or-name )
92104 " Wrapper to function `pop-to-buffer' .
93105
@@ -234,33 +246,108 @@ The data is consist of ROLE and CONTENT."
234246 (chatgpt--fill-region start (point )))))
235247 (cl-incf chatgpt--display-pointer)))
236248
249+ (defun chatgpt-send-response (response )
250+ " Send RESPONSE to ChatGPT."
251+ (let ((user (chatgpt-user))
252+ (instance chatgpt-instance))
253+ (when (string-empty-p response)
254+ (user-error " [INFO] Invalid response or instruction: %s" response))
255+ (chatgpt--add-message user response) ; add user's response
256+ (chatgpt-with-instance instance
257+ (chatgpt--display-messages)) ; display it
258+ (setq chatgpt-requesting-p t )
259+ (openai-chat chatgpt-chat-history
260+ (lambda (data )
261+ (chatgpt-with-instance instance
262+ (setq chatgpt-requesting-p nil )
263+ (chatgpt--add-tokens data)
264+ (chatgpt--add-response-messages data)
265+ (chatgpt--display-messages)))
266+ :model chatgpt-model
267+ :max-tokens chatgpt-max-tokens
268+ :temperature chatgpt-temperature
269+ :user user)))
270+
237271(defun chatgpt-type-response ()
238272 " Type response to OpenAI."
239273 (interactive )
240274 (cond
241275 (chatgpt-requesting-p
242276 (message " [BUSY] Waiting for OpanAI to response... " ))
243277 (t
244- (let ((response (read-string " Type response: " ))
245- (user (chatgpt-user))
246- (instance chatgpt-instance))
247- (when (string-empty-p response)
248- (user-error " [INFO] Invalid response or instruction: %s" response))
249- (chatgpt--add-message user response) ; add user's response
250- (chatgpt-with-instance instance
251- (chatgpt--display-messages)) ; display it
252- (setq chatgpt-requesting-p t )
253- (openai-chat chatgpt-chat-history
254- (lambda (data )
255- (chatgpt-with-instance instance
256- (setq chatgpt-requesting-p nil )
257- (chatgpt--add-tokens data)
258- (chatgpt--add-response-messages data)
259- (chatgpt--display-messages)))
260- :model chatgpt-model
261- :max-tokens chatgpt-max-tokens
262- :temperature chatgpt-temperature
263- :user user)))))
278+ (cl-case chatgpt-input-method
279+ (`minibuffer
280+ (chatgpt-send-response (read-string " Type response: " )))
281+ (`window
282+ (chatgpt--start-input chatgpt-instance))
283+ (t
284+ (user-error " Invalid input method: %s" chatgpt-input-method))))))
285+
286+ ; ;
287+ ; ;; Input
288+
289+ (defconst chatgpt-input-buffer-name " *ChatGPT-Input*"
290+ " Buffer name to receive input." )
291+
292+ (defvar chatgpt-input-instance nil
293+ " The current instance; there is only one instance at a time." )
294+
295+ (defun chatgpt--start-input (instance )
296+ " Start input from INSTANCE."
297+ (chatgpt--kill-buffer chatgpt-input-buffer-name) ; singleton
298+ (let ((dir (if (window-parameter nil 'window-side )
299+ 'bottom 'down ))
300+ (buffer (get-buffer-create chatgpt-input-buffer-name)))
301+ (with-current-buffer buffer
302+ (chatgpt-input-mode)
303+ (setq chatgpt-input-instance instance)
304+ (erase-buffer )
305+ (insert " Type response here..." )
306+ (mark-whole-buffer )) ; waiting for deletion
307+ (pop-to-buffer buffer `((display-buffer-in-direction)
308+ (direction . , dir )
309+ (dedicated . t )
310+ (window-height . fit-window-to-buffer)))))
311+
312+ (defun chatgpt-input-send ()
313+ " Send the input."
314+ (interactive )
315+ (cond
316+ ((not (eq major-mode #'chatgpt-input-mode )) ) ; does nothing
317+ (chatgpt-requesting-p
318+ (message " [BUSY] Waiting for OpanAI to response... " ))
319+ (t
320+ (if (use-region-p )
321+ (delete-region (region-beginning ) (region-end ))
322+ (let ((response (buffer-string )))
323+ (chatgpt-with-instance chatgpt-input-instance
324+ (chatgpt-send-response response))
325+ (erase-buffer ))))))
326+
327+ (defun chatgpt-input--post-command ()
328+ " Execution after input."
329+ (let ((max-lines (line-number-at-pos (point-max ))))
330+ (fit-window-to-buffer )
331+ (enlarge-window (- max-lines (window-text-height )))))
332+
333+ (defun chatgpt-input-header-line ()
334+ " The display for input header line."
335+ (format " Session: %s " (cdr chatgpt-input-instance)))
336+
337+ (defvar chatgpt-input-mode-map
338+ (let ((map (make-sparse-keymap )))
339+ (define-key map (kbd " S-<return>" ) #'newline )
340+ (define-key map (kbd " RET" ) #'chatgpt-input-send )
341+ map)
342+ " Keymap for `chatgpt-mode' ." )
343+
344+ ;;;### autoload
345+ (define-derived-mode chatgpt-input-mode fundamental-mode " ChatGPT Input"
346+ " Major mode for `chatgpt-input-mode' .
347+
348+ \\ <chatgpt-input-mode-map>"
349+ (setq-local header-line-format `((:eval (chatgpt-input-header-line))))
350+ (add-hook 'post-command-hook #'chatgpt-input--post-command nil t ))
264351
265352; ;
266353; ;; Info
@@ -282,7 +369,7 @@ The data is consist of ROLE and CONTENT."
282369(defun chatgpt-info ()
283370 " Show session information."
284371 (interactive )
285- (when (eq major-mode 'chatgpt-mode )
372+ (when (eq major-mode # 'chatgpt-mode )
286373 (lv-message
287374 (concat
288375 (format " session: %s " (cdr chatgpt-instance)) " \n "
@@ -303,6 +390,10 @@ The data is consist of ROLE and CONTENT."
303390; ;
304391; ;; Entry
305392
393+ (defun chatgpt-mode-kill-buffer ()
394+ " "
395+ )
396+
306397(defvar chatgpt-mode-map
307398 (let ((map (make-sparse-keymap )))
308399 (define-key map (kbd " RET" ) #'chatgpt-type-response )
@@ -315,7 +406,8 @@ The data is consist of ROLE and CONTENT."
315406
316407\\ <chatgpt-mode-map>"
317408 (setq-local buffer-read-only t )
318- (font-lock-mode -1 ))
409+ (font-lock-mode -1 )
410+ (add-hook 'kill-buffer-hook #'chatgpt-mode-kill-buffer nil t ))
319411
320412(defun chatgpt-register-instance (index buffer-or-name )
321413 " Register BUFFER-OR-NAME with INDEX as an instance.
0 commit comments