Skip to content

Commit c242bdf

Browse files
committed
Add assistant response color-coding & role setting
1 parent dcc4e36 commit c242bdf

File tree

1 file changed

+73
-1
lines changed

1 file changed

+73
-1
lines changed

gptel.el

+73-1
Original file line numberDiff line numberDiff line change
@@ -335,6 +335,12 @@ This is an alist mapping major modes to the reply prefix strings. This
335335
is only inserted in dedicated gptel buffers before the AI's response."
336336
:type '(alist :key-type symbol :value-type string))
337337

338+
(defcustom gptel-highlight-assistant-responses nil
339+
"Whether or not the assistant responses should be highlighted.
340+
341+
Applies only to the dedicated gptel chat buffer."
342+
:type 'boolean)
343+
338344
(defcustom gptel-use-header-line t
339345
"Whether `gptel-mode' should use header-line for status information.
340346
@@ -782,6 +788,31 @@ file."
782788
;; NOTE: It's not clear that this is the best strategy:
783789
(add-to-list 'text-property-default-nonsticky '(gptel . t))
784790

791+
(defface gptel-response-highlight-face
792+
'((((class color) (min-colors 257) (background light))
793+
:foreground "#0066cc")
794+
(((class color) (min-colors 88) (background light))
795+
:foreground "#0066cc")
796+
(((class color) (min-colors 88) (background dark))
797+
:foreground "light sky blue")
798+
(((class color)) :foreground "blue"))
799+
"Face used to highlight gptel responses in the dedicated chat buffer."
800+
:group 'gptel)
801+
802+
(defun gptel--response-text-search (bound)
803+
"Search for text with the `gptel' property set to `response' up to BOUND."
804+
(let ((pos (point)))
805+
(while (and (< pos bound)
806+
(not (eq (get-text-property pos 'gptel) 'response)))
807+
(setq pos (next-single-property-change pos 'gptel nil bound)))
808+
(if (and (< pos bound) (eq (get-text-property pos 'gptel) 'response))
809+
(let ((end (next-single-property-change pos 'gptel nil bound)))
810+
(set-match-data (list pos end))
811+
(goto-char end)
812+
t)
813+
(goto-char bound)
814+
nil)))
815+
785816
;;;###autoload
786817
(define-minor-mode gptel-mode
787818
"Minor mode for interacting with LLMs."
@@ -796,6 +827,10 @@ file."
796827
(eq major-mode 'text-mode))
797828
(gptel-mode -1)
798829
(user-error (format "`gptel-mode' is not supported in `%s'." major-mode)))
830+
(when gptel-highlight-assistant-responses
831+
(font-lock-add-keywords
832+
nil '((gptel--response-text-search 0 'gptel-response-highlight-face prepend)) t)
833+
(font-lock-flush))
799834
(add-hook 'before-save-hook #'gptel--save-state nil t)
800835
(gptel--restore-state)
801836
(if gptel-use-header-line
@@ -847,12 +882,49 @@ file."
847882
(setq mode-line-process
848883
'(:eval (concat " "
849884
(buttonize gptel-model
850-
(lambda (&rest _) (gptel-menu))))))))
885+
(lambda (&rest _) (gptel-menu))))))))
886+
(font-lock-remove-keywords
887+
nil '((gptel--response-text-search 0 'gptel-response-highlight-face prepend)))
888+
(font-lock-flush)
851889
(if gptel-use-header-line
852890
(setq header-line-format gptel--old-header-line
853891
gptel--old-header-line nil)
854892
(setq mode-line-process nil))))
855893

894+
(defun gptel--response-region-at-point ()
895+
"Return cons of response start and end points."
896+
(let* ((pos (point))
897+
(start pos)
898+
(end pos))
899+
(cl-flet ((responsep (point)
900+
(member 'gptel (text-properties-at point))))
901+
(while (and (/= start (point-min))
902+
(responsep start))
903+
(setq start (or (previous-property-change start) (point-min))))
904+
(while (and (/= end (point-max))
905+
(responsep end))
906+
(setq end (or (next-property-change end) (point-max))))
907+
(setq start (if (responsep start) start (1+ start)))
908+
(cons start end))))
909+
910+
(defun gptel-toggle-response-role ()
911+
"Toggle the role of the text between the user and the assistant.
912+
If a region is selected, modifies the region. Otherwise, modifies at the point."
913+
(interactive)
914+
(unless gptel-mode
915+
(user-error "This command is only usable in the dedicated gptel chat buffer"))
916+
(let (start end)
917+
(if (region-active-p)
918+
(setf start (region-beginning)
919+
end (region-end))
920+
(let ((response-region (gptel--response-region-at-point)))
921+
(setf start (car response-region)
922+
end (cdr response-region))))
923+
(let ((type (get-text-property start 'gptel)))
924+
(if (eq type 'response)
925+
(put-text-property start end 'gptel nil)
926+
(put-text-property start end 'gptel 'response)))))
927+
856928
(defun gptel--update-status (&optional msg face)
857929
"Update status MSG in FACE."
858930
(when gptel-mode

0 commit comments

Comments
 (0)