[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
[nongnu] elpa/gptel ca642d0f23 2/6: gptel-rewrite: Change rewriting UI (
From: |
ELPA Syncer |
Subject: |
[nongnu] elpa/gptel ca642d0f23 2/6: gptel-rewrite: Change rewriting UI (again!) |
Date: |
Mon, 2 Dec 2024 07:00:01 -0500 (EST) |
branch: elpa/gptel
commit ca642d0f23fea03075158ae8d96dade2dca004fc
Author: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
Commit: Karthik Chikmagalur <karthikchikmagalur@gmail.com>
gptel-rewrite: Change rewriting UI (again!)
* gptel-rewrite.el:
(gptel-rewrite-default-action): Make it easier to set without
setopt. It now takes the symbols `merge', `accept', `diff' and
`ediff' as values.
(gptel--rewrite-dispatch): New command to signal available options
after rewriting. We are now bombarding the user with hints from
every direction: Eldoc, minibuffer messages, the transient menu,
mouseover text (help-echo), and now this dispatch command invoked
via RET or clicking the overlay. Hopefully that's enough!
(gptel--rewrite-callback, gptel--suffix-rewrite): Extract the
rewrite callback into a separate function. Modify to handle
streaming. Change the logic: it now displays the response text in
an overlay so it's easier to see what's changed. The overlay is
now less busy.
(gptel--rewrite-apply, gptel--rewrite-accept, ): Rename -apply to -accept.
(gptel--suffix-rewrite-clear, gptel--suffix-rewrite-reject):
Rename -clear to -reject.
(gptel-rewrite-menu, gptel--suffix-rewrite-accept,
gptel--rewrite-prepare-buffer, gptel--rewrite-key-help,
gptel-rewrite-actions-map, gptel--rewrite-merge): Changes due to
renaming.
---
gptel-rewrite.el | 194 +++++++++++++++++++++++++++++++++++--------------------
1 file changed, 123 insertions(+), 71 deletions(-)
diff --git a/gptel-rewrite.el b/gptel-rewrite.el
index 9fbfaad803..dd817a0259 100644
--- a/gptel-rewrite.el
+++ b/gptel-rewrite.el
@@ -52,18 +52,18 @@ for a particular major-mode or project."
When the LLM response with the rewritten text is received, you can
- merge it with the current region, possibly creating a merge conflict,
- diff or ediff against the original region,
-- or accept it in place, overwriting the original region.
+- or accept it in place, replacing the original region.
If this option is nil (the default), gptel waits for an explicit
-command. Set it to the symbol merge, diff, ediff or replace to
-automatically do one of these things instead."
+command. Set it to the symbol `merge', `diff', `ediff' or
+`accept' to automatically do one of these things instead."
:group 'gptel
:type '(choice
(const :tag "Wait" nil)
- (const :tag "Merge with current region" gptel--rewrite-merge)
- (const :tag "Diff against current region" gptel--rewrite-diff)
- (const :tag "Ediff against current region" gptel--rewrite-ediff)
- (const :tag "Replace current region" gptel--rewrite-apply)
+ (const :tag "Merge with current region" merge)
+ (const :tag "Diff against current region" diff)
+ (const :tag "Ediff against current region" ediff)
+ (const :tag "Accept rewrite" accept)
(function :tag "Custom action")))
(defface gptel-rewrite-highlight-face
@@ -79,8 +79,10 @@ automatically do one of these things instead."
(defvar-keymap gptel-rewrite-actions-map
:doc "Keymap for gptel rewrite actions at point."
- "C-c C-k" #'gptel--rewrite-clear
- "C-c C-a" #'gptel--rewrite-apply
+ "RET" #'gptel--rewrite-dispatch
+ "<mouse-1>" #'gptel--rewrite-dispatch
+ "C-c C-k" #'gptel--rewrite-reject
+ "C-c C-a" #'gptel--rewrite-accept
"C-c C-d" #'gptel--rewrite-diff
"C-c C-e" #'gptel--rewrite-ediff
"C-c C-n" #'gptel--rewrite-next
@@ -170,7 +172,7 @@ CALLBACK is supplied by Eldoc, see
(when (and gptel--rewrite-overlays
(get-char-property (point) 'gptel-rewrite))
(funcall callback
- (format (substitute-command-keys "%s rewrite available: accept
\\[gptel--rewrite-apply], clear \\[gptel--rewrite-clear], merge
\\[gptel--rewrite-merge], diff \\[gptel--rewrite-diff] or ediff
\\[gptel--rewrite-ediff]")
+ (format (substitute-command-keys "%s rewrite available: accept
\\[gptel--rewrite-accept], clear \\[gptel--rewrite-reject], merge
\\[gptel--rewrite-merge], diff \\[gptel--rewrite-diff] or ediff
\\[gptel--rewrite-ediff]")
(propertize (concat (gptel-backend-name gptel-backend)
":" (gptel--model-name gptel-model))
'face 'mode-line-emphasis)))))
@@ -239,12 +241,12 @@ the changed regions. BUF is the (current) buffer."
;; (delay-mode-hooks (funcall mode))
;; Apply the changes to the new buffer
(save-excursion
- (gptel--rewrite-apply ovs newbuf)))
+ (gptel--rewrite-accept ovs newbuf)))
newbuf)))
;; * Refactor action functions
-(defun gptel--rewrite-clear (&optional ovs)
+(defun gptel--rewrite-reject (&optional ovs)
"Clear pending LLM responses in OVS or at point."
(interactive (list (gptel--rewrite-overlay-at)))
(dolist (ov (ensure-list ovs))
@@ -254,7 +256,7 @@ the changed regions. BUF is the (current) buffer."
(remove-hook 'eldoc-documentation-functions 'gptel--rewrite-key-help
'local))
(message "Cleared pending LLM response(s)."))
-(defun gptel--rewrite-apply (&optional ovs buf)
+(defun gptel--rewrite-accept (&optional ovs buf)
"Apply pending LLM responses in OVS or at point.
BUF is the buffer to modify, defaults to the overlay buffer."
@@ -325,7 +327,104 @@ BUF is the buffer to modify, defaults to the overlay
buffer."
"\n>>>>>>> " (gptel-backend-name gptel-backend) "\n")
(setq changed t))))
(when changed (smerge-mode 1)))
- (gptel--rewrite-clear ovs))))
+ (gptel--rewrite-reject ovs))))
+
+(defun gptel--rewrite-dispatch (choice)
+ "Dispatch actions for gptel rewrites."
+ (interactive
+ (list
+ (if-let* ((ov (cdr-safe (get-char-property-and-overlay (point)
'gptel-rewrite))))
+ (unwind-protect
+ (pcase-let ((choices '((?a "accept") (?k "reject") (?m "merge")
+ (?d "diff") (?e "ediff")))
+ (hint-str (concat "[" (gptel--model-name gptel-model)
"]\n")))
+ (overlay-put
+ ov 'before-string
+ (concat
+ (unless (eq (char-before (overlay-start ov)) ?\n) "\n")
+ (propertize "REWRITE READY: " 'face 'success)
+ (mapconcat (lambda (e) (cdr e)) (mapcar
#'rmc--add-key-description choices) ", ")
+ (propertize
+ " " 'display `(space :align-to (- right ,(1+ (length
hint-str)))))
+ (propertize hint-str 'face 'success)))
+ (read-multiple-choice "Action: " choices))
+ (overlay-put ov 'before-string nil))
+ (user-error "No gptel rewrite at point!"))))
+ (call-interactively
+ (intern (concat "gptel--rewrite-" (cadr choice)))))
+
+(defun gptel--rewrite-callback (response info)
+ "Callback for gptel rewrite actions.
+
+Show the rewrite result in an overlay over the original text, and
+set up dispatch actions.
+
+RESPONSE is the response received. It may also be t (to indicate
+success) or the symbol `error' (to indicate failure.)
+
+INFO is the async communication channel for the rewrite request."
+ (when-let* ((ov-and-buf (plist-get info :context))
+ (ov (car ov-and-buf))
+ (proc-buf (cdr ov-and-buf))
+ (buf (overlay-buffer ov)))
+ (cond
+ ((stringp response) ;partial or fully successful result
+ (with-current-buffer proc-buf ;auxiliary buffer, insert text here
and copy to overlay
+ (let ((inhibit-modification-hooks nil))
+ (when (= (buffer-size) 0)
+ (buffer-disable-undo)
+ (insert-buffer-substring buf (overlay-start ov) (overlay-end ov))
+ (when (eq (char-before (point-max)) ?\n)
+ (plist-put info :newline t))
+ (delay-mode-hooks (funcall (buffer-local-value 'major-mode buf)))
+ (add-text-properties (point-min) (point-max) '(face shadow
font-lock-face shadow))
+ (goto-char (point-min)))
+ (insert response)
+ (unless (eobp) (with-demoted-errors (delete-char (length response))))
+ (font-lock-ensure)
+ (cl-callf concat (overlay-get ov 'gptel-rewrite) response)
+ (overlay-put ov 'display (buffer-string))))
+ (unless (plist-get info :stream) (gptel--rewrite-callback t info)))
+ ((eq response 'error) ;finished with error
+ (message (concat "LLM response error: %s. Rewrite/refactor in buffer %s
canceled.")
+ (plist-get info :status) (plist-get info :buffer))
+ (when-let* ((proc-buf (cdr-safe (plist-get info :context))))
+ (kill-buffer proc-buf))
+ (delete-overlay ov))
+ (t (let ((proc-buf (cdr-safe (plist-get info :context))) ;finished
successfully
+ (mkb (propertize "<mouse-1>" 'face 'help-key-binding)))
+ (with-current-buffer proc-buf
+ (delete-region (point) (point-max))
+ (when (and (plist-get info :newline)
+ (not (eq (char-before (point-max)) ?\n)))
+ (insert "\n"))
+ (font-lock-ensure)
+ (overlay-put ov 'display (buffer-string))
+ (kill-buffer proc-buf))
+ (when (buffer-live-p buf)
+ (with-current-buffer buf
+ (pulse-momentary-highlight-region (overlay-start ov)
(overlay-end ov))
+ (add-hook 'eldoc-documentation-functions
#'gptel--rewrite-key-help nil 'local)
+ ;; (overlay-put ov 'gptel-rewrite response)
+ (overlay-put ov 'face 'gptel-rewrite-highlight-face)
+ (overlay-put ov 'keymap gptel-rewrite-actions-map)
+ (overlay-put ov 'mouse-face 'highlight)
+ (overlay-put
+ ov 'help-echo
+ (format (concat "%s rewrite available: %s or
\\[gptel--rewrite-dispatch] for options")
+ (concat (gptel-backend-name gptel-backend) ":"
(gptel--model-name gptel-model))
+ mkb))
+ (push ov gptel--rewrite-overlays))
+ (if-let* ((sym gptel-rewrite-default-action))
+ (if-let* ((action (intern (concat "gptel--rewrite-"
(symbol-name sym))))
+ ((functionp action)))
+ (funcall action ov) (funcall sym ov))
+ (message (concat
+ "LLM rewrite output"
+ (unless (eq (current-buffer) buf)
+ (format " in buffer %s " (buffer-name buf)))
+ (concat " ready: " mkb ", " (propertize "RET" 'face
'help-key-binding)
+ " or " (substitute-command-keys
"\\[gptel-menu] to continue.")))))))))))
;; * Transient Prefixes for rewriting/refactoring
@@ -396,10 +495,10 @@ By default, gptel uses the directive associated with the
`rewrite'
[:description (lambda () (concat "Continue " (gptel--refactor-or-rewrite)
"s"))
:if (lambda () (gptel--rewrite-sanitize-overlays))
(gptel--suffix-rewrite-merge)
- (gptel--suffix-rewrite-apply)]
+ (gptel--suffix-rewrite-accept)]
[:description (lambda () (concat "Reject " (gptel--refactor-or-rewrite)
"s"))
:if (lambda () (gptel--rewrite-sanitize-overlays))
- (gptel--suffix-rewrite-clear)]]
+ (gptel--suffix-rewrite-reject)]]
(interactive)
(unless gptel--rewrite-message
(setq gptel--rewrite-message
@@ -416,6 +515,7 @@ By default, gptel uses the directive associated with the
`rewrite'
:class 'gptel-lisp-variable
:variable 'gptel--rewrite-message
:set-value #'gptel--set-with-scope
+ :display-nil "(None)"
:key "d"
:format " %k %d %v"
:prompt (concat "Instructions " gptel--read-with-prefix-help)
@@ -482,61 +582,13 @@ generated from functions."
(gptel-request prompt
:dry-run dry-run
:system gptel--rewrite-directive
+ :stream gptel-stream
:context
(let ((ov (make-overlay (region-beginning) (region-end))))
(overlay-put ov 'category 'gptel)
(overlay-put ov 'evaporate t)
- ov)
- :callback
- (lambda (response info)
- (if (not response)
- (message (concat "LLM response error: %s. Rewrite/refactor in
buffer %s canceled."
- (propertize "❌" 'face 'error))
- (plist-get info :status)
- (plist-get info :buffer))
- ;; Store response
- (let ((buf (plist-get info :buffer))
- (ov (plist-get info :context))
- (action-str) (hint-str))
- (when (buffer-live-p buf)
- (with-current-buffer buf
- (if (derived-mode-p 'prog-mode)
- (progn
- (setq action-str "refactor")
- (when (string-match-p "^```" response)
- (setq response (replace-regexp-in-string "^```.*$" ""
response))))
- (setq action-str "rewrite"))
- (setq hint-str (concat "[" (gptel-backend-name gptel-backend)
- ":" (gptel--model-name gptel-model) "] "
- (upcase action-str) " READY ✓\n"))
- (add-hook 'eldoc-documentation-functions
#'gptel--rewrite-key-help nil 'local)
- (overlay-put ov 'gptel-rewrite response)
- (overlay-put ov 'face 'gptel-rewrite-highlight-face)
- (overlay-put ov 'keymap gptel-rewrite-actions-map)
- (overlay-put ov 'before-string
- (concat (propertize
- " " 'display `(space :align-to (- right
,(1+ (length hint-str)))))
- (propertize hint-str 'face 'success)))
- (overlay-put
- ov 'help-echo
- (format "%s rewrite available:
-- accept \\[gptel--rewrite-apply],
-- clear \\[gptel--rewrite-clear],
-- merge \\[gptel--accept-merge],
-- diff \\[gptel--rewrite-diff],
-- ediff \\[gptel--rewrite-ediff]"
- (propertize (concat (gptel-backend-name gptel-backend)
- ":" (gptel--model-name
gptel-model)))))
- (push ov gptel--rewrite-overlays))
- (if (functionp gptel-rewrite-default-action)
- (funcall gptel-rewrite-default-action ov)
- ;; Message user
- (message
- (concat
- "LLM %s output"
- (unless (eq (current-buffer) buf) (format " in buffer %s "
buf))
- (substitute-command-keys " ready, \\[gptel-menu] to
continue."))
- action-str)))))))))
+ (cons ov (generate-new-buffer "*gptel-rewrite*")))
+ :callback #'gptel--rewrite-callback)))
(transient-define-suffix gptel--suffix-rewrite-diff (&optional switches)
"Diff LLM output against buffer."
@@ -562,15 +614,15 @@ generated from functions."
(interactive)
(gptel--rewrite-merge gptel--rewrite-overlays))
-(transient-define-suffix gptel--suffix-rewrite-apply ()
+(transient-define-suffix gptel--suffix-rewrite-accept ()
"Accept pending LLM rewrites."
:if (lambda () gptel--rewrite-overlays)
:key "ca"
:description "Accept in-place"
(interactive)
- (gptel--rewrite-apply gptel--rewrite-overlays))
+ (gptel--rewrite-accept gptel--rewrite-overlays))
-(transient-define-suffix gptel--suffix-rewrite-clear ()
+(transient-define-suffix gptel--suffix-rewrite-reject ()
"Clear pending LLM rewrites."
:if (lambda () gptel--rewrite-overlays)
:key "ck"
@@ -578,7 +630,7 @@ generated from functions."
(downcase (gptel--refactor-or-rewrite))
"s")
(interactive)
- (gptel--rewrite-clear gptel--rewrite-overlays))
+ (gptel--rewrite-reject gptel--rewrite-overlays))
(provide 'gptel-rewrite)
;;; gptel-rewrite.el ends here
- [nongnu] elpa/gptel updated (b6ba8f2641 -> f90a77b711), ELPA Syncer, 2024/12/02
- [nongnu] elpa/gptel 3a7a7a4cc6 3/6: gptel-rewrite: Tweak rewrite menu, ELPA Syncer, 2024/12/02
- [nongnu] elpa/gptel 19b9514b84 1/6: gptel-curl: Changes to the streaming API, ELPA Syncer, 2024/12/02
- [nongnu] elpa/gptel f90a77b711 6/6: README: Add new video demo, ELPA Syncer, 2024/12/02
- [nongnu] elpa/gptel 4ca7085d68 5/6: README: Update rewrite section and fix typos, ELPA Syncer, 2024/12/02
- [nongnu] elpa/gptel ca642d0f23 2/6: gptel-rewrite: Change rewriting UI (again!),
ELPA Syncer <=
- [nongnu] elpa/gptel 4147b3fff9 4/6: gptel-rewrite: Make gptel-rewrite a command, ELPA Syncer, 2024/12/02