gptel-transient: Allow redirection to any buffer

* gptel.el (gptel-request): Update docstring to clarify what
BUFFER and POSITION do.  Addresses #191.

* gptel-transient.el (gptel-menu, gptel--suffix-send): Replace
"new session" and "existing session" redirection options with
"gptel session" and "any buffer", allowing for more flexibility
when redirecting.  "gptel session" can be an existing or new
session.  Fix bug where the prompt was generated from the contents
of the destination buffer instead of the current buffer when
redirecting to a gptel session.  Add comments demarcating blocks
in `gptel--suffix-send`.
This commit is contained in:
Karthik Chikmagalur 2024-01-22 17:11:26 -08:00
parent 89decb4201
commit d2f56c62a0
2 changed files with 75 additions and 66 deletions

View file

@ -127,25 +127,25 @@ which see."
("i" "Replace/Delete prompt" "i")
"Response to:"
("m" "Minibuffer instead" "m")
("n" "New session" "n"
("g" "gptel session" "g"
:class transient-option
:prompt "Name for new session: "
:prompt "Existing or new gptel session: "
:reader
(lambda (prompt _ history)
(read-string
(lambda (prompt _ _history)
(read-buffer
prompt (generate-new-buffer-name
(concat "*" (gptel-backend-name gptel-backend) "*"))
history)))
("e" "Existing session" "e"
nil (lambda (buf-name)
(if (consp buf-name) (setq buf-name (car buf-name)))
(let ((buf (get-buffer buf-name)))
(and (buffer-local-value 'gptel-mode buf)
(not (eq (current-buffer) buf))))))))
("b" "Any buffer" "b"
:class transient-option
:prompt "Existing session: "
:prompt "Output to buffer: "
:reader
(lambda (prompt _ history)
(completing-read
prompt (mapcar #'buffer-name (buffer-list))
(lambda (buf) (and (buffer-local-value 'gptel-mode (get-buffer buf))
(not (equal (current-buffer) buf))))
t nil history)))
(lambda (prompt _ _history)
(read-buffer prompt (buffer-name (other-buffer)) nil)))
("k" "Kill-ring" "k")]
[:description gptel--refactor-or-rewrite
:if use-region-p
@ -394,6 +394,7 @@ will get progressively longer!"
(backend-name (gptel-backend-name gptel-backend))
(buffer) (position)
(callback) (gptel-buffer-name)
;; Input redirection: grab prompt from elsewhere?
(prompt
(cond
((member "p" args)
@ -409,6 +410,8 @@ will get progressively longer!"
(if current-prefix-arg
(read-from-kill-ring "Prompt from kill-ring: ")
(current-kill 0))))))
;; Output redirection: Send response elsewhere?
(cond
((member "m" args)
(setq stream nil)
@ -428,45 +431,13 @@ will get progressively longer!"
backend-name
(truncate-string-to-width resp 30))))))
((setq gptel-buffer-name
(cl-some (lambda (s) (and (string-prefix-p "n" s)
(cl-some (lambda (s) (and (string-prefix-p "g" s)
(substring s 1)))
args))
(setq buffer
(gptel gptel-buffer-name
(condition-case nil
(gptel--get-api-key)
((error user-error)
(setq gptel-api-key
(read-passwd
(format "%s API key: "
(gptel-backend-name
gptel-backend))))))
(or prompt
(if (use-region-p)
(buffer-substring-no-properties (region-beginning)
(region-end))
(buffer-substring-no-properties
(save-excursion
(text-property-search-backward
'gptel 'response
(when (get-char-property (max (point-min) (1- (point)))
'gptel)
t))
(point))
(gptel--at-word-end (point)))))))
(with-current-buffer buffer
(setq gptel-backend backend)
(setq gptel-model model)
(gptel--update-status " Waiting..." 'warning)
(setq position (point)))
(setq output-to-other-buffer-p t))
((setq gptel-buffer-name
(cl-some (lambda (s) (and (string-prefix-p "e" s)
(substring s 1)))
args))
(setq buffer (get-buffer gptel-buffer-name))
(setq output-to-other-buffer-p t)
(let ((reduced-prompt
(let ((reduced-prompt ;For inserting into the gptel buffer as
;context, not the prompt used for the
;request itself
(or prompt
(if (use-region-p)
(buffer-substring-no-properties (region-beginning)
@ -480,18 +451,51 @@ will get progressively longer!"
t))
(point))
(gptel--at-word-end (point)))))))
(with-current-buffer buffer
(goto-char (point-max))
(if (or buffer-read-only
(get-char-property (point) 'read-only))
(setq prompt reduced-prompt)
(insert reduced-prompt))
(setq position (point))
(when gptel-mode
(gptel--update-status " Waiting..." 'warning))))))
(cond
((buffer-live-p (get-buffer gptel-buffer-name))
;; Insert into existing gptel session
(progn
(setq buffer (get-buffer gptel-buffer-name))
(with-current-buffer buffer
(goto-char (point-max))
(unless (or buffer-read-only
(get-char-property (point) 'read-only))
(insert reduced-prompt))
(setq position (point))
(when gptel-mode
(gptel--update-status " Waiting..." 'warning)))))
;; Insert into new gptel session
(t (setq buffer
(gptel gptel-buffer-name
(condition-case nil
(gptel--get-api-key)
((error user-error)
(setq gptel-api-key
(read-passwd
(format "%s API key: "
(gptel-backend-name
gptel-backend))))))
reduced-prompt))
;; Set backend and model in new session from current buffer
(with-current-buffer buffer
(setq gptel-backend backend)
(setq gptel-model model)
(gptel--update-status " Waiting..." 'warning)
(setq position (point)))))))
((setq gptel-buffer-name
(cl-some (lambda (s) (and (string-prefix-p "b" s)
(substring s 1)))
args))
(setq output-to-other-buffer-p t)
(setq buffer (get-buffer-create gptel-buffer-name))
(with-current-buffer buffer (setq position (point)))))
;; Create prompt, unless doing input-redirection above
(unless prompt
(setq prompt (gptel--create-prompt (gptel--at-word-end (point)))))
(when in-place
(setq prompt (gptel--create-prompt (point)))
;; Kill the latest prompt
(let ((beg
(if (use-region-p)
(region-beginning)

View file

@ -755,8 +755,10 @@ RESPONSE is nil if there was no response or an error.
The INFO plist has (at least) the following keys:
:prompt - The full prompt that was sent with the request
:position - marker at the point the request was sent.
:buffer - The buffer current when the request was sent.
:position - marker at the point the request was sent, unless
POSITION is specified.
:buffer - The buffer current when the request was sent,
unless BUFFER is specified.
:status - Short string describing the result of the request
Example of a callback that messages the user with the response
@ -780,12 +782,15 @@ Or, for just the response:
If CALLBACK is omitted, the response is inserted at the point the
request was sent.
BUFFER is the buffer the request belongs to. If omitted the
current buffer is recorded.
BUFFER and POSITION are the buffer and position (integer or
marker) at which the response is inserted. If a CALLBACK is
specified, no response is inserted and these arguments are
ignored, but they are still available in the INFO plist passed
to CALLBACK for you to use.
POSITION is a buffer position (integer or marker). If omitted,
the value of (point) or (region-end) is recorded, depending on
whether the region is active.
BUFFER defaults to the current buffer, and POSITION to the value
of (point) or (region-end), depending on whether the region is
active.
CONTEXT is any additional data needed for the callback to run. It
is included in the INFO argument to the callback.