@@ -335,6 +335,12 @@ This is an alist mapping major modes to the reply prefix strings. This
335
335
is only inserted in dedicated gptel buffers before the AI's response."
336
336
:type '(alist :key-type symbol :value-type string))
337
337
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
+
338
344
(defcustom gptel-use-header-line t
339
345
" Whether `gptel-mode' should use header-line for status information.
340
346
@@ -782,6 +788,31 @@ file."
782
788
; ; NOTE: It's not clear that this is the best strategy:
783
789
(add-to-list 'text-property-default-nonsticky '(gptel . t ))
784
790
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
+
785
816
;;;### autoload
786
817
(define-minor-mode gptel-mode
787
818
" Minor mode for interacting with LLMs."
@@ -796,6 +827,10 @@ file."
796
827
(eq major-mode 'text-mode ))
797
828
(gptel-mode -1 )
798
829
(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 ))
799
834
(add-hook 'before-save-hook #'gptel--save-state nil t )
800
835
(gptel--restore-state)
801
836
(if gptel-use-header-line
@@ -847,12 +882,49 @@ file."
847
882
(setq mode-line-process
848
883
'(:eval (concat " "
849
884
(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 )
851
889
(if gptel-use-header-line
852
890
(setq header-line-format gptel--old-header-line
853
891
gptel--old-header-line nil )
854
892
(setq mode-line-process nil ))))
855
893
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
+
856
928
(defun gptel--update-status (&optional msg face )
857
929
" Update status MSG in FACE."
858
930
(when gptel-mode
0 commit comments