emacs-elpa-diffs
[Top][All Lists]
Advanced

[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



reply via email to

[Prev in Thread] Current Thread [Next in Thread]