[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/gptel bf994c0765 204/273: gptel: Add response regeneration
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/gptel bf994c0765 204/273: gptel: Add response regeneration, history and ediff |
Date: |
Wed, 1 May 2024 10:02:24 -0400 (EDT) |
branch: elpa/gptel
commit bf994c0765f69cc2ce467bc5f023c99be608aee5
Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Commit: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
gptel: Add response regeneration, history and ediff
* gptel.el (gptel--attach-response-history, gptel--ediff,
gptel--next-variant, gptel--previous-variant,
gptel--mark-response):
Add `gptel--attach-response-history` -- this can be used to add
text properties to the next gptel response in the buffer. This
is (currently) useful for tracking changes when the response
overwrites existing text.
The next three commands -- `gptel--ediff`,
`gptel--previous-variant`, `gptel--next-variant` -- provide
facilities for manipulating a gptel response at point when there
is history. `gptel--mark-response` marks the response at point.
These are considered internal functions for now and can be
accessed from the transient menu, where they work together with
`gptel--regenerate`.
The input arguments to these commands are expected to change to
support copilot-style functionality in the near future.
* gptel-transient.el (gptel-menu, gptel--suffix-send,
gptel--regenerate):
Change the transient menu layout to be more compact (with a newly
added column.) When overwriting the prompt with a response, save
the prompt to the gptel response's history. Add
`gptel--regenerate` to regenerate a response. This is accessible
from the transient menu when the point is inside response text.
---
gptel-transient.el | 55 ++++++++++++++++++++++----
gptel.el | 111 +++++++++++++++++++++++++++++++++++++++++++++++++++++
2 files changed, 159 insertions(+), 7 deletions(-)
diff --git a/gptel-transient.el b/gptel-transient.el
index 4e998c169b..00ae53b970 100644
--- a/gptel-transient.el
+++ b/gptel-transient.el
@@ -126,11 +126,12 @@ which see."
(gptel--infix-max-tokens)
(gptel--infix-num-messages-to-send)
(gptel--infix-temperature)]
- ["Prompt:"
- ("p" "From minibuffer instead" "p")
- ("y" "From kill-ring instead" "y")
- ("i" "Replace/Delete prompt" "i")
- "Response to:"
+ ["Prompt from"
+ ("p" "Minibuffer instead" "p")
+ ("y" "Kill-ring instead" "y")
+ ""
+ ("i" "Replace/Delete prompt" "i")]
+ ["Response to"
("m" "Minibuffer instead" "m")
("g" "gptel session" "g"
:class transient-option
@@ -151,7 +152,10 @@ which see."
:reader
(lambda (prompt _ _history)
(read-buffer prompt (buffer-name (other-buffer)) nil)))
- ("k" "Kill-ring" "k")]
+ ("k" "Kill-ring" "k")]]
+ [["Send"
+ (gptel--suffix-send)
+ ("M-RET" "Regenerate" gptel--regenerate :if gptel--in-response-p)]
[:description gptel--refactor-or-rewrite
:if use-region-p
("r"
@@ -161,7 +165,16 @@ which see."
(lambda () (if (derived-mode-p 'prog-mode)
"Refactor" "Rewrite"))
gptel-rewrite-menu)]
- ["Send" (gptel--suffix-send)]]
+ ["Tweak Response" :if gptel--in-response-p :pad-keys t
+ ("SPC" "Mark" gptel--mark-response)
+ ("P" "Previous variant" gptel--previous-variant
+ :if gptel--at-response-history-p
+ :transient t)
+ ("N" "Next variant" gptel--previous-variant
+ :if gptel--at-response-history-p
+ :transient t)
+ ("E" "Ediff previous" gptel--ediff
+ :if gptel--at-response-history-p)]]
(interactive)
(gptel--sanitize-model)
(transient-setup 'gptel-menu))
@@ -510,6 +523,10 @@ responses."
t))
(point))))
(end (if (use-region-p) (region-end) (point))))
+ (unless output-to-other-buffer-p
+ ;; store the killed text in gptel-history
+ (gptel--attach-response-history
+ (list (buffer-substring-no-properties beg end))))
(kill-region beg end)))
(gptel-request
@@ -527,6 +544,30 @@ responses."
display-buffer-pop-up-window)
(reusable-frames . visible))))))
+;; ** Suffix to regenerate response
+
+(defun gptel--regenerate ()
+ "Regenerate gptel response at point."
+ (interactive)
+ (when (gptel--in-response-p)
+ (pcase-let* ((`(,beg . ,end) (gptel--get-bounds))
+ (history (get-char-property (point) 'gptel-history))
+ (prev-responses (cons (buffer-substring-no-properties beg end)
+ history)))
+ (when gptel-mode ;Remove prefix/suffix
+ (save-excursion
+ (goto-char beg)
+ (when (looking-back (concat "\n+" (regexp-quote
(gptel-response-prefix-string)))
+ (point-min) 'greedy)
+ (setq beg (match-beginning 0)))
+ (goto-char end)
+ (when (looking-at
+ (concat "\n+" (regexp-quote (gptel-prompt-prefix-string))))
+ (setq end (match-end 0)))))
+ (delete-region beg end)
+ (gptel--attach-response-history prev-responses)
+ (call-interactively #'gptel--suffix-send))))
+
;; ** Set system message
(defun gptel--read-crowdsourced-prompt ()
"Pick a crowdsourced system prompt for gptel.
diff --git a/gptel.el b/gptel.el
index 3add09eda3..4d3256be2e 100644
--- a/gptel.el
+++ b/gptel.el
@@ -1352,5 +1352,116 @@ text stream."
(prog1 (buffer-substring (point) (point-max))
(set-marker start-pt (point-max)))))))))
+
+;; Response tweaking commands
+
+(defun gptel--attach-response-history (history &optional buf)
+ "Attach HISTORY to the next gptel response in buffer BUF.
+
+HISTORY is a list of strings typically containing text replaced
+by gptel. BUF is the current buffer if not specified.
+
+This is used to maintain variants of prompts or responses to diff
+against if required."
+ (with-current-buffer (or buf (current-buffer))
+ (letrec ((gptel--attach-after
+ (lambda (b e)
+ (put-text-property b e 'gptel-history
+ (append (ensure-list history)
+ (get-char-property (1- e)
'gptel-history)))
+ (remove-hook 'gptel-post-response-functions
+ gptel--attach-after 'local))))
+ (add-hook 'gptel-post-response-functions gptel--attach-after
+ nil 'local))))
+
+(defun gptel--ediff (&optional arg bounds-func)
+ "Ediff response at point against previous gptel responses.
+
+If prefix ARG is non-nil, select the previous response to ediff
+against interactively.
+
+If specified, use BOUNDS-FUNC to compute the bounds of the
+response at point. This can be used to include additional
+context for the ediff session."
+ (interactive "P")
+ (when (gptel--at-response-history-p)
+ (pcase-let* ((`(,beg . ,end) (funcall (or bounds-func
#'gptel--get-bounds)))
+ (prev-response
+ (if arg
+ (completing-read "Choose response variant to diff
against: "
+ (get-char-property (point)
'gptel-history)
+ nil t)
+ (car-safe (get-char-property (point) 'gptel-history))))
+ (buffer-mode major-mode)
+ (bufname (buffer-name))
+ (`(,new-buf ,new-beg ,new-end)
+ (with-current-buffer
+ (get-buffer-create (concat bufname "-PREVIOUS-*"))
+ (let ((inhibit-read-only t))
+ (erase-buffer)
+ (delay-mode-hooks (funcall buffer-mode))
+ (insert prev-response)
+ (goto-char (point-min))
+ (list (current-buffer) (point-min) (point-max))))))
+ (unless prev-response (user-error "gptel response is additive: no
changes to ediff"))
+ (require 'ediff)
+ (letrec ((cwc (current-window-configuration))
+ (gptel--ediff-restore
+ (lambda ()
+ (when (window-configuration-p cwc)
+ (set-window-configuration cwc))
+ (kill-buffer (get-buffer (concat bufname "-PREVIOUS-*")))
+ (kill-buffer (get-buffer (concat bufname "-CURRENT-*")))
+ (remove-hook 'ediff-quit-hook gptel--ediff-restore))))
+ (add-hook 'ediff-quit-hook gptel--ediff-restore)
+ (apply
+ #'ediff-regions-internal
+ (get-buffer (ediff-make-cloned-buffer (current-buffer) "-CURRENT-*"))
+ beg end new-buf new-beg new-end
+ nil
+ (list 'ediff-regions-wordwise 'word-wise nil)
+ ;; (if (transient-arg-value "-w" args)
+ ;; (list 'ediff-regions-wordwise 'word-wise nil)
+ ;; (list 'ediff-regions-linewise nil nil))
+ )))))
+
+(defun gptel--mark-response ()
+ "Mark gptel response at point, if any."
+ (interactive)
+ (unless (gptel--in-response-p) (user-error "No gptel response at point"))
+ (pcase-let* ((`(,beg . ,end) (gptel--get-bounds)))
+ (goto-char beg) (push-mark) (goto-char end) (activate-mark)))
+
+(defun gptel--previous-variant (&optional arg)
+ "Switch to previous gptel-response at this point, if it exists."
+ (interactive "p")
+ (pcase-let* ((`(,beg . ,end) (gptel--get-bounds))
+ (history (get-char-property (point) 'gptel-history))
+ (alt-response (car-safe history))
+ (offset))
+ (unless (and history alt-response)
+ (user-error "No variant responses available"))
+ (if (> arg 0)
+ (setq history (append (cdr history)
+ (list (buffer-substring-no-properties beg end))))
+ (setq
+ alt-response (car (last history))
+ history (cons (buffer-substring-no-properties beg end)
+ (nbutlast history))))
+ (add-text-properties
+ 0 (length alt-response)
+ `(gptel response rear-nonsticky t gptel-history ,history)
+ alt-response)
+ (setq offset (min (- (point) beg) (1- (length alt-response))))
+ (delete-region beg end)
+ (insert alt-response)
+ (goto-char (+ beg offset))
+ (pulse-momentary-highlight-region beg (+ beg (length alt-response)))))
+
+(defun gptel--next-variant (&optional arg)
+ "Switch to next gptel-response at this point, if it exists."
+ (interactive "p")
+ (gptel--previous-variant (- arg)))
+
(provide 'gptel)
;;; gptel.el ends here
- [nongnu] elpa/gptel c9795fe9e8 060/273: gptel: org support for streaming WIP, (continued)
- [nongnu] elpa/gptel c9795fe9e8 060/273: gptel: org support for streaming WIP, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 644fc1de2f 118/273: gptel-transient: Handle empty input when setting temperature, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 3c01477c37 129/273: gptel: api-key shenanigans, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 644e341244 141/273: Add multiline prefixes & AI response prefixes (#142), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel a202911009 148/273: gptel: Add post-stream hook, scroll commands, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel c3ca4fd0a0 158/273: gptel-transient: Set suffix-state explicitly for directives, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 0fce1d86d1 171/273: README: fix typo (#168), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 92a8c0bdac 183/273: gptel: letrec expansion error in Emacs 27.2, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel b34e217bbf 182/273: README: Mention gptel-request, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel d2f56c62a0 193/273: gptel-transient: Allow redirection to any buffer, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel bf994c0765 204/273: gptel: Add response regeneration, history and ediff,
ELPA Syncer <=
- [nongnu] elpa/gptel 87c190076e 212/273: README: Clarify example configuration code, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 12340eda46 228/273: gptel-transient: Truncate system prompt when messaging, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel f58ad9435c 225/273: gptel: Use libjansson support if available, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 161c77ad7f 235/273: gptel-transient: Adjust several menu options, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 2b938114cf 264/273: gptel: Add GPT 4 Turbo (#286), ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 44feb1637f 267/273: gptel-transient: Update header-line in gptel--suffix-send, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel f98293f004 016/273: gptel: Check header-line-format before updating, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 3f7c81012b 015/273: gptel: Bump version and prepare for transient menus, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel 9da22155de 025/273: gptel-transient: Fix autoloads for gptel-send-menu, ELPA Syncer, 2024/05/01
- [nongnu] elpa/gptel a3109a4b68 022/273: gptel: Insert response below point, not at point-max, ELPA Syncer, 2024/05/01