From d2f56c62a0ea8e529abec0fd0927e674081534b3 Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur Date: Mon, 22 Jan 2024 17:11:26 -0800 Subject: [PATCH] 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`. --- gptel-transient.el | 122 +++++++++++++++++++++++---------------------- gptel.el | 19 ++++--- 2 files changed, 75 insertions(+), 66 deletions(-) diff --git a/gptel-transient.el b/gptel-transient.el index 8add342..e8d1f53 100644 --- a/gptel-transient.el +++ b/gptel-transient.el @@ -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) diff --git a/gptel.el b/gptel.el index 0d806d7..65151a9 100644 --- a/gptel.el +++ b/gptel.el @@ -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.