branch: elpa/gptel
commit b10cd98e075a8314b93cab93faa6144341f87478
Author: daedsidog <[email protected]>
Commit: karthink <[email protected]>

    gptel-context: Implement DWIM features
    
    gptel-contexter.el: General fixes to context management
    features.
    
    gptel-transient.el: Ditto.
---
 gptel-contexter.el | 277 ++++++++++++-----------
 gptel-openai.el    |  19 +-
 gptel-transient.el | 638 ++++++++++++++++++++++++-----------------------------
 gptel.el           |  43 ++--
 4 files changed, 460 insertions(+), 517 deletions(-)

diff --git a/gptel-contexter.el b/gptel-contexter.el
index 43cb8b55bd..95a6563805 100644
--- a/gptel-contexter.el
+++ b/gptel-contexter.el
@@ -38,7 +38,8 @@
   :type 'symbol)
 
 (defcustom gptel-use-context-in-chat nil
-  "Determines if context should be injected when using the dedicated chat 
buffer."
+  "Determines if context should be injected when using the dedicated chat 
buffer.
+If non-nil, then the model will use the context in the chat buffer."
   :group 'gptel
   :type 'symbol)
 
@@ -53,6 +54,18 @@
   :group 'gptel
   :type 'symbol)
 
+(defcustom gptel-context-preamble "Request context:"
+  "A string to be prepended to the context."
+  :group 'gptel
+  :type 'string)
+
+(defcustom gptel-context-postamble ""
+  "A string to be appended to the context."
+  :group 'gptel
+  :type 'string)
+
+(defvar gptel--context-overlays '())
+
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
 ;;; ------------------------------ FUNCTIONS ------------------------------- 
;;;
 
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
@@ -61,11 +74,13 @@
 (defun gptel-add-context (&optional arg)
   "Add context to GPTel.
 
-When called regularly, adds current buffer as context.
-When ARG is positive, prompts for buffer name to add as context.
-When ARG is negative, removes current buffer from context.
-When called with region selected, adds selected region as context."
-  (interactive)
+When called without a prefix argument, adds the current buffer as context.
+When ARG is positive, prompts for a buffer name and adds it as context.
+When ARG is negative, removes all contexts from the current buffer.
+When called with a region selected, adds the selected region as context.
+
+If there is a context under point, it is removed when called without a prefix."
+  (interactive "P")
   (cond
    ;; A region is selected.
    ((use-region-p)
@@ -74,55 +89,79 @@ When called with region selected, adds selected region as 
context."
                                   (region-end))
     (deactivate-mark)
     (message "Current region added as context."))
-   ;; No region is currently selected, so delete a context under point if there
-   ;; is one.
-   ((gptel-context-at-point)
-    (gptel-remove-context (gptel-context-at-point))
-    (message "Context under point has been removed."))
-   ;; No region is selected and no context is under point.  The default 
behavior
-   ;; is to add the entire buffer as context.
-   (t
-    (gptel--add-region-as-context (current-buffer) (point-min) (point-max))
-    (message "Current buffer added as context."))))
+   ;; No region is selected, and ARG is positive.
+   ((and arg (> (prefix-numeric-value arg) 0))
+    (let ((buffer-name (read-buffer "Choose buffer to add as context: " nil 
t)))
+      (gptel--add-region-as-context (get-buffer buffer-name) (point-min) 
(point-max))
+      (message "Buffer '%s' added as context." buffer-name)))
+   ;; No region is selected, and ARG is negative.
+   ((and arg (< (prefix-numeric-value arg) 0))
+    (when (y-or-n-p "Remove all contexts from this buffer? ")
+      (let ((removed-contexts 0))
+        (cl-loop for cov in
+                 (gptel-contexts-in-region (current-buffer) (point-min) 
(point-max))
+                 do (progn
+                      (cl-incf removed-contexts)
+                      (gptel-remove-context cov)))
+        (message (format "%d context%s removed from current buffer."
+                         removed-contexts
+                         (if (= removed-contexts 1) "" "s"))))))
+   (t ; Default behavior
+    (if (gptel-context-at-point)
+        (progn
+          (gptel-remove-context (car (gptel-contexts-in-region (current-buffer)
+                                                               (max 
(point-min) (1- (point)))
+                                                               (point))))
+          (message "Context under point has been removed."))
+      (gptel--add-region-as-context (current-buffer) (point-min) (point-max))
+      (message "Current buffer added as context.")))))
   
 (defun gptel--make-context-overlay (start end)
   "Highlight the region from START to END."
   (let ((overlay (make-overlay start end)))
+    (overlay-put overlay 'evaporate t)
     (overlay-put overlay 'face gptel-context-highlight-face)
     (overlay-put overlay 'gptel-context t)
+    (push overlay gptel--context-overlays)
     overlay))
 
+(defun gptel--wrap-in-context (message)
+  "Wrap MESSAGE with context.
+The message is usually either a system message or user prompt."
+  ;; Append context before/after system message.
+  (if (and (bound-and-true-p gptel-mode)
+           (not gptel-use-context-in-chat))
+      ;; If we are in the dedicated chat buffer, we would like to consult
+      ;; `gptel-use-context-in-chat' to see if context should be used inside.
+      message
+    (let ((context (gptel-context-string)))
+      (if (> (length context) 0)
+          (if (memq gptel-context-injection-destination
+                    '(:before-system-message
+                      :before-user-prompt))
+              (concat gptel-context-preamble
+                      (when (not (zerop (length gptel-context-preamble))) 
"\n\n")
+                      context
+                      "\n\n"
+                      gptel-context-postamble
+                      (when (not (zerop (length gptel-context-postamble))) 
"\n\n")
+                      message)
+            (concat message
+                    "\n\n"
+                    gptel-context-preamble
+                    (when (not (zerop (length gptel-context-preamble))) "\n\n")
+                    context
+                    (when (not (zerop (length gptel-context-postamble))) 
"\n\n")
+                    gptel-context-postamble))
+        message))))
+
 (cl-defun gptel--add-region-as-context (buffer region-beginning region-end)
   "Add region delimited by REGION-BEGINNING, REGION-END in BUFFER as context."
   ;; Remove existing contexts in the same region, if any.
   (mapc #'gptel-remove-context
         (gptel-contexts-in-region buffer region-beginning region-end))
-  (let ((start (make-marker))
-        (end (make-marker)))
-    (set-marker start region-beginning (current-buffer))
-    (set-marker end region-end (current-buffer))
-    ;; Trim the unnecessary parts of the context content.
-    (let* ((content (buffer-substring-no-properties start end))
-           (fat-at-end (progn
-                         (let ((match-pos
-                                (string-match-p (rx (+ (any "\t" "\n" " ")) 
eos)
-                                                content)))
-                           (when match-pos
-                             (- (- end start) match-pos)))))
-           (fat-at-start (progn
-                           (when (string-match (rx bos (+ (any "\t" "\n" " ")))
-                                               content)
-                             (match-end 0)))))
-      (when fat-at-start
-        (set-marker start (+ start fat-at-start)))
-      (when fat-at-end
-        (set-marker end (- end fat-at-end))))
-    (when (= start end)
-      (message "No content in selected region.")
-      (cl-return-from gptel--add-region-to-contexts nil))
-    ;; First, highlight the region.
-    (prog1 (gptel--make-context-overlay start end)
-      (message "Region added to context buffer."))))
+    (prog1 (gptel--make-context-overlay region-beginning region-end)
+      (message "Region added to context buffer.")))
 
 ;;;###autoload
 (defun gptel-contexts-in-region (buffer start end)
@@ -136,8 +175,10 @@ START and END signify the region delimiters."
 ;;;###autoload
 (defun gptel-context-at-point ()
   "Return the context overlay at point, if any."
-  (car (overlays-in (point) (point))))
-
+  (car (cl-remove-if-not #'(lambda (ov)
+                             (overlay-get ov 'gptel-context))
+                         (overlays-at (point)))))
+    
 ;;;###autoload
 (defun gptel-remove-context (&optional context)
   "Remove the CONTEXT overlay from the contexts list.
@@ -160,17 +201,13 @@ If selection is active, removes all contexts within 
selection."
 
 ;;;###autoload
 (defun gptel-contexts ()
-  "Get the list of all context overlays in all active buffers."
-  (cl-remove-if-not #'(lambda (ov)
-                        (overlay-get ov 'gptel-context))
-                    (let ((all-overlays '()))
-                      (dolist (buf (buffer-list))
-                        (with-current-buffer buf
-                          (setq all-overlays
-                                (append all-overlays
-                                        (overlays-in (point-min)
-                                                     (point-max))))))
-                      all-overlays)))
+  "Get the list of all active context overlays."
+  ;; Get only the non-degenerate overlays, collect them, and update the 
overlays variable.
+  (let ((overlays (cl-loop for ov in gptel--context-overlays
+                           when (overlay-start ov)
+                           collect ov)))
+    (setq gptel--context-overlays overlays)
+    overlays))
 
 ;;;###autoload
 (defun gptel-contexts-in-buffer (buffer)
@@ -232,39 +269,16 @@ representthe regions' boundaries within BUFFER."
           (curr-line-start (line-number-at-pos (car current-region))))
       (= prev-line-end curr-line-start))))
 
-(defun gptel--regions-continuous-p (buffer previous-region current-region)
-  "Return non-nil if CURRENT-REGION is a continuation of PREVIOUS-REGION.
-Pretains only to regions in BUFFER.
-
-A region is considered a continuation of another if it is only separated by
-newlines and whitespaces.  PREVIOUS-REGION and CURRENT-REGION should be cons
-cells (START . END) representing the boundaries of the regions within BUFFER."
-  (with-current-buffer buffer
-    (let ((gap (buffer-substring-no-properties
-                (cdr previous-region) (car current-region))))
-      (string-match-p
-       (rx bos (* (any "\t" "\n" " ")) eos)
-       gap))))
-
-(defun gptel-buffer-context-string (buffer)
-  "Create a context string from all contexts in BUFFER."
+(defun gptel-buffer-context-string (buffer &optional depropertize)
+  "Create a context string from all contexts in BUFFER.
+If DEPROPERTIZE is non-nil, remove the properties from the final substring."
     (let ((is-top-snippet t)
           buffer-file
-          previous-region
-          buffer-point-min
-          buffer-point-max
+          (previous-line 1)
           prog-lang-tag
           (contexts (gptel-contexts-in-buffer buffer)))
       (with-current-buffer buffer
-        (setq buffer-point-min (save-excursion
-                                 (goto-char (point-min))
-                                 (skip-chars-forward " \t\n\r")
-                                 (point))
-              buffer-point-max (save-excursion
-                                 (goto-char (point-max))
-                                 (skip-chars-backward " \t\n\r")
-                                 (point))
-              prog-lang-tag (gptel-major-mode-md-prog-lang
+        (setq prog-lang-tag (gptel-major-mode-md-prog-lang
                              major-mode)))
       (setq buffer-file
             ;; Use file path if buffer has one, otherwise use its regular name.
@@ -279,55 +293,26 @@ cells (START . END) representing the boundaries of the 
regions within BUFFER."
         (cl-loop for context in contexts do
                  (progn
                    (let* ((start (overlay-start context))
-                          (end (overlay-end context))
-                          (region-inline
-                           ;; Does the current region start on the same line 
the
-                           ;; previous region ends?
-                           (when previous-region
-                             (gptel--region-inline-p buffer
-                                                     previous-region
-                                                     (cons start end))))
-                          (region-continuous
-                           ;; Is the current region a continuation of the
-                           ;; previous region? I.e., is it only separated by
-                           ;; newlines and whitespaces?
-                           (when previous-region
-                             (gptel--regions-continuous-p buffer
-                                                          previous-region
-                                                          (cons start end)))))
-                     (unless (<= start buffer-point-min)
-                       (if region-continuous
-                           ;; If the regions are continuous, insert the
-                           ;; whitespaces that separate them.
-                           (insert-buffer-substring-no-properties
-                            buffer
-                            (cdr previous-region)
-                            start)
-                         ;; Regions are not continuous. Are they on the same
-                         ;; line?
-                         (if region-inline
-                             ;; Region is inline but not continuous, so we
-                             ;; should just insert an ellipsis.
-                             (insert " ... ")
-                           ;; Region is neither inline nor continuous, so just
-                           ;; insert an ellipsis on a new line.
-                           (unless is-top-snippet
-                             (insert "\n"))
-                           (insert "...")))
-                       (let (lineno)
-                         (with-current-buffer buffer
-                           (setq lineno (line-number-at-pos start)))
-                         ;; We do not need to insert a line number indicator on
-                         ;; inline regions.
-                         (unless (or region-inline region-continuous)
-                           (insert (format " (Line %d)" lineno)))))
-                     (when (or (and (not region-inline)
-                                    (not region-continuous)
-                                    (not is-top-snippet))
-                               is-top-snippet)
-                       (insert "\n"))
-                     (if is-top-snippet
-                         (setq is-top-snippet nil))
+                          (end (overlay-end context)))
+                     (let (lineno column)
+                       (with-current-buffer buffer
+                         (setq lineno (line-number-at-pos start t))
+                         (setq column (save-excursion
+                                        (goto-char start)
+                                        (current-column))))
+                       ;; We do not need to insert a line number indicator if 
we have two regions
+                       ;; on the same line, because the previous region should 
have already put the
+                       ;; indicator.
+                       (unless (= previous-line lineno)
+                         (unless (= lineno 1)
+                           (insert (format "\n... (Line %d)\n" lineno))))
+                       (setq previous-line lineno)
+                       (unless (zerop column)
+                         (insert " ..."))
+                       (if is-top-snippet
+                           (setq is-top-snippet nil)
+                         (unless (= previous-line lineno)
+                           (insert "\n"))))
                      (let (substring)
                        (with-current-buffer buffer
                          (setq substring (buffer-substring-no-properties
@@ -337,20 +322,28 @@ cells (START . END) representing the boundaries of the 
regions within BUFFER."
                        (put-text-property 0 (length substring)
                                           'gptel-context-overlay
                                           context substring)
-                       (insert substring))
-                     (setq previous-region (cons start end)))))
-        (unless (>= (overlay-end (car (last contexts))) buffer-point-max)
+                       (insert substring)))))
+        (unless (>= (overlay-end (car (last contexts))) (point-max))
           (insert "\n..."))
         (insert "\n```")
-        (buffer-substring (point-min) (point-max)))))
+        (let ((context-snippet (buffer-substring (point-min) (point-max))))
+          (when depropertize
+            (set-text-properties 0 (length context-snippet) nil 
context-snippet))
+          context-snippet))))
 
 ;;;###autoload
-(defun gptel-context-string ()
-  "Return the context string of all aggregated contexts."
-  (string-trim-right
-   (cl-loop for buffer in
-            (delete-dups (mapcar #'overlay-buffer (gptel-contexts)))
-            concat (concat (gptel-buffer-context-string buffer) "\n\n"))))
+(defun gptel-context-string (&optional propertize)
+  "Return the context string of all aggregated contexts.
+If PROPERTIZE is non-nil, keep the text properties."
+  (without-restriction
+    (let ((context (string-trim-right
+                    (cl-loop for buffer in
+                             (delete-dups (mapcar #'overlay-buffer 
(gptel-contexts)))
+                             concat (concat (gptel-buffer-context-string 
buffer) "\n\n")))))
+      (if propertize
+          context
+        (set-text-properties 0 (length context) nil context)
+        context))))
 
 (provide 'gptel-contexter)
 ;;; gptel-contexter.el ends here.
diff --git a/gptel-openai.el b/gptel-openai.el
index 9b8f375eea..b89d3e747a 100644
--- a/gptel-openai.el
+++ b/gptel-openai.el
@@ -26,6 +26,7 @@
 (eval-when-compile
   (require 'cl-lib))
 (require 'map)
+(require 'gptel-contexter)
 
 (defvar gptel-model)
 (defvar gptel-stream)
@@ -143,8 +144,22 @@ with differing settings.")
       (push (list :role "user"
                   :content
                   (string-trim
-                   (buffer-substring-no-properties (point-min) (point-max))))
-            prompts))
+                   (buffer-substring-no-properties (prop-match-beginning prop)
+                                                   (prop-match-end prop))
+                   (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+                           (regexp-quote (gptel-prompt-prefix-string)))
+                   (format "[\t\r\n ]*\\(?:%s\\)?[\t\r\n ]*"
+                           (regexp-quote (gptel-response-prefix-string)))))
+            prompts)
+      (and max-entries (cl-decf max-entries)))
+    (when (and (memq gptel-context-injection-destination '(:before-user-prompt 
:after-user-prompt))
+               (> (length prompts) 0))
+      ;; Add context to final user prompt.
+      (let* ((last-prompt (last prompts))
+             (last-plist (car last-prompt)))
+        (setf (car last-prompt) (plist-put last-plist :content
+                                           (gptel--wrap-in-context (plist-get 
(car last-prompt)
+                                                                              
:content))))))
     (cons (list :role "system"
                 :content gptel--system-message)
           prompts)))
diff --git a/gptel-transient.el b/gptel-transient.el
index 30db75950d..d59f0a07c6 100644
--- a/gptel-transient.el
+++ b/gptel-transient.el
@@ -236,6 +236,32 @@ This is used only for setting this variable via 
`gptel-menu'.")
              (oset obj model-value model-value)
              gptel--set-buffer-locally)))
 
+(defclass gptel-context-destination-variable (transient-lisp-variable)
+  ((display-value :initarg :display-value
+                  :initform (lambda (value)
+                              (pcase value
+                                (:nowhere "Nowhere")
+                                (:before-system-message "Before system 
message")
+                                (:after-system-message "After system message")
+                                (:before-user-prompt "Before user prompt")
+                                (:after-user-prompt "After user prompt"))))))
+
+(cl-defmethod transient-format-value ((obj gptel-context-destination-variable))
+  (propertize (funcall (oref obj display-value)
+                       (buffer-local-value (oref obj variable) 
transient--original-buffer))
+              'face 'transient-value))
+
+(cl-defmethod transient-infix-set ((obj gptel-context-destination-variable) 
value)
+  (funcall (oref obj set-value)
+           (oref obj variable)
+           (pcase value
+             ("Nowhere" :nowhere)
+             ("Before system message" :before-system-message)
+             ("After system message" :after-system-message)
+             ("Before user prompt" :before-user-prompt)
+             ("After user prompt" :after-user-prompt))
+           gptel--set-buffer-locally))
+
 (defclass gptel-option-overlaid (transient-option)
   ((display-nil :initarg :display-nil)
    (overlay :initarg :overlay))
@@ -288,6 +314,10 @@ Also format its value in the Transient menu."
     "Instructions"
     ("s" "Set system message" gptel-system-prompt :transient t)
     (gptel--infix-add-directive)]]
+  [["Context" :if (lambda () gptel-expert-commands)
+    (gptel--suffix-context-buffer)
+    (gptel--infix-context-destination)
+    (gptel--infix-use-context-in-chat :if (lambda () gptel-mode))]]
   [["Model Parameters"
     :pad-keys t
     (gptel--infix-variable-scope)
@@ -470,73 +500,27 @@ Customize `gptel-directives' for task-specific prompts."
 
 ;; ** Infixes for context aggregation
 
-(defclass gptel-keyword-variable (transient-lisp-variable)
-  ((choices :initarg :choices)
-   (always-read :initform t)
-   (set-value :initarg :set-value :initform #'set))
-  "Class for handling variables with keyword choices.")
-
-(cl-defmethod transient-format-value ((obj gptel-keyword-variable))
-  (let ((keyword-value (oref obj value))
-        (choices (oref obj choices)))
-    (propertize (cdr (assoc keyword-value choices)) 'face 'transient-value)))
-
-(cl-defmethod transient-infix-set ((obj gptel-keyword-variable) value)
-  (let ((keyword (car (rassoc value (oref obj choices)))))
-    (funcall (oref obj set-value)
-             (oref obj variable)
-             (oset obj value keyword))))
-
-(defun gptel--keyword-reader (prompt choices)
-  (let* ((display-choices (mapcar #'cdr choices))
-         (selected-string (completing-read prompt display-choices nil t)))
-    selected-string))
-
 (transient-define-infix gptel--infix-context-destination ()
   "Describe target destination for context injection."
   :description "Context destination"
-  :class 'gptel-keyword-variable
+  :class 'gptel-context-destination-variable
   :variable 'gptel-context-injection-destination
+  :set-value #'gptel--set-with-scope
   :key "-xd"
-  :choices '((:nowhere . "nowhere")
-             (:before-system-message . "before system message")
-             (:after-system-message . "after system message")
-             (:before-user-prompt . "before user prompt")
-             (:after-user-prompt . "after user prompt"))
   :reader (lambda (prompt &rest _)
-            (gptel--keyword-reader
-             prompt 
-             '((:nowhere . "nowhere")
-               (:before-system-message . "before system message")
-               (:after-system-message . "after system message")
-               (:before-user-prompt . "before user prompt")
-               (:after-user-prompt . "after user prompt")))))
-
-(defclass gptel-boolean-variable (transient-lisp-variable)
-  ((always-read :initform t)
-   (set-value :initarg :set-value :initform #'set))
-  "Class for handling boolean variables.")
-
-(cl-defmethod transient-format-value ((obj gptel-boolean-variable))
-  (let ((value (oref obj value)))
-    (propertize (if value "yes" "no") 'face 'transient-value)))
-
-(cl-defmethod transient-infix-set ((obj gptel-boolean-variable) value)
-  (funcall (oref obj set-value)
-           (oref obj variable)
-           (oset obj value (equal value "yes"))))
-
-(defun gptel--boolean-reader (prompt _ history)
-  (let* ((choice (completing-read prompt '("yes" "no") nil t nil history)))
-    choice))
+            (completing-read prompt '("Nowhere" "Before system message" "After 
system message"
+                                      "Before user prompt" "After user prompt")
+                             nil t)))
 
 (transient-define-infix gptel--infix-use-context-in-chat ()
   "Determine if context should be passed to the LLM during the chat."
   :description "Use in chat"
-  :class 'gptel-boolean-variable
+  :class 'gptel--switches
   :variable 'gptel-use-context-in-chat
-  :key "-xc"
-  :reader 'gptel--boolean-reader)
+  :set-value #'gptel--set-with-scope
+  :display-if-true "Yes"
+  :display-if-false "No"
+  :key "-xc")
 
 ;; ** Infixes for model parameters
 
@@ -954,296 +938,260 @@ When LOCAL is non-nil, set the system message only in 
the current buffer."
 
 ;; ** Suffix for displaying and removing context
 
-(defun gptel--context-edge-point (edge-type direction &optional inclusive)
-  "Find context edge point of EDGE-TYPE from current point.
-EDGE-TYPE is either :start or :end.
-DIRECTION is either :next or :previous.
-If INCLUSIVE is non-nil, return the current point if it is on an edge."
-  (let* ((point nil)
-         (get-edge-point #'(lambda (direction)
-                             (if (and inclusive
-                                      (when (get-text-property (point) 
'gptel-context-overlay)
-                                        (if (eq edge-type :start)
-                                            (when (and (/= (point) (point-min))
-                                                       (not (get-text-property
-                                                             (1- (point))
-                                                             
'gptel-context-overlay)))
-                                              t)
-                                          (when (and (/= (point) (point-max))
-                                                     (not (get-text-property
-                                                           (1+ (point))
-                                                           
'gptel-context-overlay)))
-                                            t))))
-                                 (point)
-                               (if (eq direction :next)
-                                   (setq point (next-single-property-change
-                                                (point)
-                                                'gptel-context-overlay nil 
nil))
-                                 (setq point (previous-single-property-change
-                                                (point)
-                                                'gptel-context-overlay nil 
nil)))))))
-    (save-excursion
-      (if (eq edge-type :end)
-          (progn
-            (funcall get-edge-point direction)
-            (when point
-              (goto-char point)
-              (if (get-text-property (1+ point) 'gptel-context-overlay)
-                  ;; This is actually a starting edge, not an ending edge.
-                  (funcall get-edge-point direction)
-                point)))
-        (funcall get-edge-point direction)
-        (when point
-          (goto-char point)
-          (if (get-text-property (1- point) 'gptel-context-overlay)
-              ;; This is actually an ending edge, not a starting edge.
-              (funcall get-edge-point direction)))))
-    ;; Handle some edge cases (pun unintended).
-    (unless point
-      (if (eq edge-type :end)
-          (when (get-text-property (max (point-min) (1- (point))) 
'gptel-context-overlay)
-            (setq point (point)))
-        (when (get-text-property (min (point-max) (1+ (point))) 
'gptel-context-overlay)
-            (setq point (point)))))
-    point))
-
-(let* ((highlight-start nil)
-       (highlight-end nil)
-       (highlight-overlay nil)
-       (moved-backwards nil)) ; This is used for some deletion navigation QoL.
-  
-  (transient-define-suffix gptel--suffix-context-buffer ()
-    "Display all contexts from all buffers & files."
-    :transient 'transient--do-exit
-    :key "-xb"
-    :description (lambda ()
-                   (let* ((contexts (gptel-contexts))
-                          (buffer-count (length (delete-dups (mapcar 
#'overlay-buffer contexts)))))
-                     (concat "Display context buffer "
-                             (propertize
-                              (format "%d context%s in %d buffer%s"
-                                      (length contexts)
-                                      (if (/= (length contexts) 1) "s" "")
-                                      buffer-count
-                                      (if (/= buffer-count 1) "s" ""))
-                              'face 'transient-value))))
-    (interactive)
-    (let ((orig-buf (current-buffer)))
-      (with-current-buffer (get-buffer-create "*gptel-context*")
-        (read-only-mode 1)
-        (setq highlight-start nil
-              highlight-end nil
-              highlight-overlay nil)
-        (let ((inhibit-read-only t))
-          (erase-buffer)
-          (setq header-line-format
-                (concat
-                 "Mark/unmark deletion with "
-                 (propertize "d" 'face 'help-key-binding)
-                 ", jump to next/previous with "
-                 (propertize "n" 'face 'help-key-binding)
-                 "/"
-                 (propertize "p" 'face 'help-key-binding)
-                 ", respectively. "
-                 (propertize "C-c C-c" 'face 'help-key-binding)
-                 " to apply, or "
-                 (propertize "C-c C-k" 'face 'help-key-binding)
-                 " to abort."))
-          (save-excursion
-            (let ((contexts (gptel-contexts)))
-              (if (> (length contexts) 0)
-                  (insert (gptel-context-string))
-                (insert "There are no active contexts in any buffer.")))))
-        (display-buffer (current-buffer)
-                        `((display-buffer-below-selected)
-                          (body-function . ,#'select-window)
-                          (window-height . ,#'fit-window-to-buffer)))
-        ;; Add hook to change the highlight whenever the point has moved beyond
-        ;; that of the current highlight.
-        (add-hook
-         'post-command-hook
+(transient-define-suffix gptel--suffix-context-buffer ()
+  "Display all contexts from all buffers & files."
+  :transient 'transient--do-exit
+  :key "-xb"
+  :description (lambda ()
+                 (let* ((contexts (gptel-contexts))
+                        (buffer-count (length (delete-dups (mapcar 
#'overlay-buffer contexts)))))
+                   (concat "Display context buffer "
+                           (format
+                            (propertize "(%s)" 'face 'transient-delimiter)
+                            (propertize (format "%d context%s in %d buffer%s"
+                                                (length contexts)
+                                                (if (/= (length contexts) 1) 
"s" "")
+                                                buffer-count
+                                                (if (/= buffer-count 1) "s" 
""))
+                                        'face (if (zerop (length contexts))
+                                                  'transient-inactive-value
+                                                'transient-value))))))
+  (interactive)
+  (let ((orig-buf (current-buffer))
+        (highlight-overlay nil)
+        (moved-backwards nil)) ; This is used for some deletion navigation QoL.
+    (with-current-buffer (get-buffer-create "*gptel-context*")
+      (read-only-mode 1)
+      (let ((inhibit-read-only t))
+        (erase-buffer)
+        (setq header-line-format
+              (concat
+               "Mark/unmark deletion with "
+               (propertize "d" 'face 'help-key-binding)
+               ", jump to next/previous with "
+               (propertize "n" 'face 'help-key-binding)
+               "/"
+               (propertize "p" 'face 'help-key-binding)
+               ", respectively. "
+               (propertize "C-c C-c" 'face 'help-key-binding)
+               " to apply, or "
+               (propertize "C-c C-k" 'face 'help-key-binding)
+               " to abort."))
+        (save-excursion
+          (let ((contexts (gptel-contexts)))
+            (if (> (length contexts) 0)
+                (progn
+                  (insert (gptel-context-string t))
+                  ;; Mark the inserted context chunks with an overlay to 
simplify bookkeeping.
+                  (goto-char (point-min))
+                  (while (not (eobp))
+                    (let* ((beg (next-single-property-change (point) 
'gptel-context-overlay))
+                           (end (when beg
+                                  (next-single-property-change beg 
'gptel-context-overlay))))
+                      (if end
+                          (progn
+                            (when (get-text-property beg 
'gptel-context-overlay)
+                              (let ((ov (make-overlay beg end)))
+                                ;; We want to make the highlighting overlay a 
higher priority than
+                                ;; the deletion overlay.  We superimpose both 
overlays to have an
+                                ;; effect that allows for both highlighting 
and deletion overlays
+                                ;; to exist simutaniously for the same context 
chunk.
+                                (overlay-put ov 'priority 1)
+                                (overlay-put ov 'gptel-context-highlight t)))
+                            (goto-char end))
+                        (goto-char (point-max))))))
+              (insert "There are no active contexts in any buffer.")))))
+      (display-buffer (current-buffer)
+                      `((display-buffer-below-selected)
+                        (body-function . ,#'select-window)
+                        (window-height . ,#'fit-window-to-buffer)))
+      ;; Add hook to change the highlight whenever the point has moved beyond
+      ;; that of the current highlight.
+      (add-hook
+       'post-command-hook
+       #'(lambda ()
+           ;; Only update if point moved outside the current region.
+           (unless (member highlight-overlay (overlays-at (point)))
+             (let ((context-overlay (car (cl-loop
+                                          for ov in (overlays-at (point))
+                                          when (overlay-get ov 
'gptel-context-highlight)
+                                          collect ov))))
+               (when highlight-overlay
+                 (overlay-put highlight-overlay 'face nil))
+               (when context-overlay
+                 (overlay-put context-overlay 'face 'highlight))
+               (setq highlight-overlay context-overlay))))
+       nil t)
+      (let* ((quit-to-menu
+              (lambda ()
+                (interactive)
+                (local-unset-key (kbd "d"))
+                (local-unset-key (kbd "n"))
+                (local-unset-key (kbd "p"))
+                (local-unset-key (kbd "C-c C-c"))
+                (local-unset-key (kbd "C-c C-k"))
+                (quit-window)
+                (display-buffer
+                 orig-buf
+                 `((display-buffer-reuse-window
+                    display-buffer-use-some-window)
+                   (body-function . ,#'select-window)))
+                (call-interactively #'gptel-menu)))
+             ;; Function used to detect whether or not we are at the edges of 
an overlay.  This is
+             ;; used to know how many overlay changes we should jump over in 
order to reach the
+             ;; start or end of overlays.  Refers to inner edge.
+             (at-overlay-edge-p #'(lambda (pos left-edge)
+                                    ;; Obviously, if we have no overlay at the 
point, we cannot be
+                                    ;; at an edge.
+                                    (when (overlays-at (point))
+                                      (not (if left-edge
+                                               (overlays-at (max (1- pos) 
(point-min)))
+                                             (not (overlays-at (min (1+ pos) 
(point-max)))))))))
+             (move-forward
+              #'(lambda ()
+                  (interactive)
+                  (let ((point-is-inside-overlay (overlays-at (point)))
+                        (next-start (next-overlay-change (point))))
+                    (when (and (/= (point-max) next-start) 
point-is-inside-overlay)
+                      ;; We were inside the overlay, so we want the next 
overlay change, which
+                      ;; would be the start of the next overlay.
+                      (setq next-start (next-overlay-change next-start)))
+                    (when (/= next-start (point-max))
+                      (setq moved-backwards nil)
+                      (goto-char next-start)))))
+             (move-backward
+              #'(lambda ()
+                  (interactive)
+                  (let ((point-is-inside-overlay (overlays-at (point)))
+                        (previous-end (previous-overlay-change (point))))
+                    (when (and (/= (point-min) previous-end) 
point-is-inside-overlay
+                               ;; Handele the edge case where the caret is 
located right at the
+                               ;; beginning of an overlay.
+                               (overlays-at (max (1- (point)) (point-min))))
+                      ;; We were inside an overlay, so are currently at the 
start of the current
+                      ;; overlay.
+                      (setq previous-end (previous-overlay-change 
previous-end)))
+                    (when (/= (point-min) previous-end)
+                      (setq moved-backwards t)
+                      (goto-char (1- previous-end)))))))
+        (local-set-key (kbd "n") move-forward)
+        (local-set-key (kbd "p") move-backward)
+        (local-set-key
+         (kbd "d") ; Marking overlays for deletion
          #'(lambda ()
-             ;; Only update if point moved outside the current region.
-             (unless (and highlight-start highlight-end
-                          (>= (point) highlight-start)
-                          (<= (point) highlight-end))
-               ;; Remove the old region.
-               (when highlight-overlay (delete-overlay highlight-overlay))
-               (setq highlight-end nil
-                     highlight-start nil)
-               ;; Find new region to highlight.
-               (let* ((point-is-within-context
-                       (get-text-property (point) 'gptel-context-overlay))
-                      (start (previous-single-property-change
-                              (point)
-                              'gptel-context-overlay
-                              nil
-                              nil))
-                      (end (next-single-property-change
-                            (point)
-                            'gptel-context-overlay
-                            nil
-                            nil)))
-                 ;; Handle the edge cases where the point is located at the 
ends
-                 ;; of the context.
-                 (when (or (not start)
-                           (not (get-text-property (1+ start)
-                                                   'gptel-context-overlay)))
-                   (setq start (point)))
-                 (when (and start end (<= start (point) end)
-                            point-is-within-context)
-                   (setq highlight-start start)
-                   (setq highlight-end (1- end))
-                   ;; Create new overlay for highlighting.
-                   (setq highlight-overlay (make-overlay start end))
-                   (overlay-put highlight-overlay 'face 'highlight)
-                   (overlay-put highlight-overlay 'priority 1)
-                   (overlay-put highlight-overlay 'gptel-context-highlight 
t)))))
-         nil t)
-        (let ((quit-to-menu
-               (lambda ()
-                 (interactive)
-                 (local-unset-key (kbd "d"))
-                 (local-unset-key (kbd "n"))
-                 (local-unset-key (kbd "p"))
-                 (local-unset-key (kbd "C-c C-c"))
-                 (local-unset-key (kbd "C-c C-k"))
-                 (quit-window)
-                 (display-buffer
-                  orig-buf
-                  `((display-buffer-reuse-window
-                     display-buffer-use-some-window)
-                    (body-function . ,#'select-window)))
-                 (call-interactively #'gptel-menu)))
-              (forward-movement-func
-               #'(lambda ()
-                   (interactive)
-                   (let ((next-start (gptel--context-edge-point :start :next)))
-                     (when next-start
-                       (setq moved-backwards nil)
-                       (goto-char next-start)))))
-              (backward-movement-func
-               #'(lambda ()
-                   (interactive)
-                   (let ((previous-end (gptel--context-edge-point :end 
:previous)))
-                     (when (and previous-end (/= previous-end (point)))
-                       (setq moved-backwards t)
-                       (goto-char (1- previous-end)))))))
-          (local-set-key (kbd "n") forward-movement-func)
-          (local-set-key (kbd "p") backward-movement-func)
-          (local-set-key
-           (kbd "d") ; Marking overlays for deletion
-           #'(lambda ()
-               (interactive)
-               (if (not (region-active-p)) ; Separate functiaonlity with just 
points vs. regions.
-                   (progn
-                     (let ((overlays (overlays-at (point)))
-                           (deletion-overlay-found nil)
-                           (highlighting-overlay nil)
-                           (something-marked-or-unmarked nil))
-                       ;; Loop through all overlays at point to check for 
deletion mark or
-                       ;; highlight.
-                       (dolist (overlay overlays)
-                         (cond
-                          ((overlay-get overlay 'gptel-context-deletion-mark)
-                           ;; If deletion mark is found, delete the overlay 
and set flag to true.
-                           (delete-overlay overlay)
-                           (setq something-marked-or-unmarked t)
-                           (setq deletion-overlay-found t))
-                          ((overlay-get overlay 'gptel-context-highlight)
-                           (setq highlighting-overlay overlay))))
-                       (when (and highlighting-overlay
-                                  (not deletion-overlay-found)
-                                  (overlay-get highlighting-overlay 
'gptel-context-highlight))
-                         (let* ((start (overlay-start highlighting-overlay))
-                                (end (overlay-end highlighting-overlay))
-                                (new-overlay (make-overlay start end)))
-                           ;; We want to have 0 priority so that the 
highlighting overlay takes
-                           ;; precedence.
-                           (setq something-marked-or-unmarked t)
-                           (overlay-put new-overlay 'priority 0)
-                           (overlay-put new-overlay 'face 
'diff-indicator-removed)
-                           (overlay-put new-overlay 
'gptel-context-deletion-mark t)))
-                       (when something-marked-or-unmarked
-                         (if moved-backwards
-                             (progn
-                               (let ((point (point)))
-                                 (funcall backward-movement-func)
-                                 (when (eq point (point))
-                                   ;; We haven't moved.  Disregard previous 
movement and just go
-                                   ;; forwards.
-                                   (setq moved-backwards nil)
-                                   (funcall forward-movement-func))))
-                           (funcall forward-movement-func)))))
-                 ;; We have a region selected, so we must iterate all the 
overlays in it to do the
-                 ;; same as we have done above.
-                 (let ((marking-action :mark-all) ; :mark-all, :unmark-all
-                       (context-region-and-mark '())
-                       (start (region-beginning))
-                       (end (region-end))
-                       (unmarked-context-found nil)
-                       (highlight-overlay-region-at-point
-                        #'(lambda ()
-                            ;; We can't get the overlay, because the hook 
isn't triggered, so the
-                            ;; highlighting overlay won't work when we use 
`goto-char'.
-                            (when (get-text-property (point) 
'gptel-context-overlay)
-                              (cons (gptel--context-edge-point :start 
:previous t)
-                                    (gptel--context-edge-point :end :next 
t)))))
-                       (deletion-overlay-at-point
-                        #'(lambda ()
-                            (car (cl-loop
-                                  for ov in (overlays-at (point))
-                                  when (overlay-get ov 
'gptel-context-deletion-mark)
-                                  collect ov)))))
-                   (deactivate-mark)
-                   ;; We want to collect the context regions and see if they 
have deletion marks to
-                   ;; determine what we want to do.
-                   (save-excursion
-                     (goto-char start)
-                     (cl-loop for previous-point = start then (point)
-                              do (progn
-                                   (let ((hov-region (funcall 
highlight-overlay-region-at-point))
-                                         (deletion-ov nil))
-                                     (when hov-region
-                                       (setq deletion-ov (funcall 
deletion-overlay-at-point))
-                                       (push (cons hov-region
-                                                   deletion-ov)
-                                             context-region-and-mark)
-                                       (unless deletion-ov
-                                         (setq unmarked-context-found t)))
-                                     (funcall forward-movement-func)))
-                              until (or (= previous-point (point))
-                                        (> (point) end))))
-                   (unless unmarked-context-found
-                     (setq marking-action :unmark-all))
-                   (cl-loop for (hov-region . dov) in context-region-and-mark 
do
-                            (if (eq marking-action :mark-all)
-                                (unless dov ; Do not make a duplicate deletion 
overlay.
-                                  (let* ((start (car hov-region))
-                                         (end (cdr hov-region))
-                                         (new-overlay (make-overlay start 
end)))
-                                    (overlay-put new-overlay 'priority 0)
-                                    (overlay-put new-overlay 'face 
'diff-indicator-removed)
-                                    (overlay-put new-overlay 
'gptel-context-deletion-mark t)))
-                              ;; marking-action is :unmark-all.
-                              (delete-overlay dov)))))))
-          (local-set-key (kbd "C-c C-c")
-                         #'(lambda ()
-                             (interactive)
-                             ;; Delete all the context overlays that have been 
marked for deletion.
-                             (cl-loop for dov in
-                                      (cl-loop for ov in (overlays-in 
(point-min) (point-max))
-                                               when
-                                               (and (overlay-get ov 
'gptel-context-deletion-mark)
-                                                    ;; Ignore zero-length 
overlays.  Not sure why
-                                                    ;; these appear at the 
start of the buffer.
-                                                    (/= (overlay-start ov) 
(overlay-end ov)))
-                                               collect ov)
-                                      do (delete-overlay
-                                          (get-text-property (overlay-start 
dov)
-                                                             
'gptel-context-overlay)))
-                             (funcall quit-to-menu)))
-          (local-set-key (kbd "C-c C-k") quit-to-menu))))))
+             (interactive)
+             (if (not (region-active-p)) ; Separate functionality with just 
points vs. regions.
+                 (progn
+                   (let ((overlays (overlays-at (point)))
+                         (deletion-overlay-found nil)
+                         (highlighting-overlay nil)
+                         (something-marked-or-unmarked nil))
+                     ;; Loop through all overlays at point to check for 
deletion mark or
+                     ;; highlight.
+                     (dolist (overlay overlays)
+                       (cond
+                        ((overlay-get overlay 'gptel-context-deletion-mark)
+                         ;; If deletion mark is found, delete the overlay and 
set flag to true.
+                         (delete-overlay overlay)
+                         (setq something-marked-or-unmarked t)
+                         (setq deletion-overlay-found t))
+                        ((overlay-get overlay 'gptel-context-highlight)
+                         (setq highlighting-overlay overlay))))
+                     (when (and highlighting-overlay
+                                (not deletion-overlay-found)
+                                (overlay-get highlighting-overlay 
'gptel-context-highlight))
+                       (let* ((start (overlay-start highlighting-overlay))
+                              (end (overlay-end highlighting-overlay))
+                              (new-overlay (make-overlay start end)))
+                         ;; We want to have 0 priority so that the 
highlighting overlay takes
+                         ;; precedence.
+                         (setq something-marked-or-unmarked t)
+                         (overlay-put new-overlay 'priority 0)
+                         (overlay-put new-overlay 'face 
'diff-indicator-removed)
+                         (overlay-put new-overlay 'gptel-context-deletion-mark 
t)))
+                     (when something-marked-or-unmarked
+                       (if moved-backwards
+                           (progn
+                             (let ((point (point)))
+                               (funcall move-backward)
+                               (when (eq point (point))
+                                 ;; We haven't moved.  Disregard previous 
movement and just go
+                                 ;; forwards.
+                                 (setq moved-backwards nil)
+                                 (funcall move-forward))))
+                         (funcall move-forward)))))
+               ;; We have a region selected, so we must iterate all the 
overlays in it to do the
+               ;; same as we have done above.
+               (let ((marking-action :mark-all) ; :mark-all, :unmark-all
+                     (context-region-and-mark '())
+                     (start (region-beginning))
+                     (end (region-end))
+                     (unmarked-context-found nil)
+                     (highlight-overlay-region-at-point
+                      #'(lambda ()
+                          ;; We can't get the overlay, because the hook isn't 
triggered, so the
+                          ;; highlighting overlay won't work when we use 
`goto-char'.
+                          (let ((ov (car (cl-loop for ov in (overlays-at 
(point))
+                                                  when (overlay-get ov 
'gptel-context-highlight)
+                                                  collect ov))))
+                            (cons (overlay-start ov) (overlay-end ov)))))
+                     (deletion-overlay-at-point
+                      #'(lambda ()
+                          (car (cl-loop
+                                for ov in (overlays-at (point))
+                                when (overlay-get ov 
'gptel-context-deletion-mark)
+                                collect ov)))))
+                 (deactivate-mark)
+                 ;; We want to collect the context regions and see if they 
have deletion marks to
+                 ;; determine what we want to do.
+                 (save-excursion
+                   (goto-char start)
+                   (cl-loop for previous-point = start then (point)
+                            do (progn
+                                 (let ((hov-region (funcall 
highlight-overlay-region-at-point))
+                                       (deletion-ov nil))
+                                   (when hov-region
+                                     (setq deletion-ov (funcall 
deletion-overlay-at-point))
+                                     (push (cons hov-region
+                                                 deletion-ov)
+                                           context-region-and-mark)
+                                     (unless deletion-ov
+                                       (setq unmarked-context-found t)))
+                                   (funcall move-forward)))
+                            until (or (= previous-point (point))
+                                      (> (point) end))))
+                 (unless unmarked-context-found
+                   (setq marking-action :unmark-all))
+                 (cl-loop for (hov-region . dov) in context-region-and-mark do
+                          (if (eq marking-action :mark-all)
+                              (unless dov ; Do not make a duplicate deletion 
overlay.
+                                (let* ((start (car hov-region))
+                                       (end (cdr hov-region))
+                                       (new-overlay (make-overlay start end)))
+                                  (overlay-put new-overlay 'priority 0)
+                                  (overlay-put new-overlay 'face 
'diff-indicator-removed)
+                                  (overlay-put new-overlay 
'gptel-context-deletion-mark t)))
+                            ;; marking-action is :unmark-all.
+                            (delete-overlay dov)))))))
+        (local-set-key (kbd "C-c C-c")
+                       #'(lambda ()
+                           (interactive)
+                           ;; Delete all the context overlays that have been 
marked for deletion.
+                           (cl-loop for dov in
+                                    (cl-loop for ov in (overlays-in 
(point-min) (point-max))
+                                             when
+                                             (and (overlay-get ov 
'gptel-context-deletion-mark)
+                                                  ;; Ignore zero-length 
overlays.  Not sure why
+                                                  ;; these appear at the start 
of the buffer.
+                                                  (/= (overlay-start ov) 
(overlay-end ov)))
+                                             collect ov)
+                                    do (delete-overlay
+                                        ;; The text property from the context 
string points to the
+                                        ;; actual context overlay located in 
the buffers.
+                                        (get-text-property (overlay-start dov)
+                                                           
'gptel-context-overlay)))
+                           (funcall quit-to-menu)))
+        (local-set-key (kbd "C-c C-k") quit-to-menu)))))
 
 ;; ** Suffixes for rewriting/refactoring
 
diff --git a/gptel.el b/gptel.el
index e72db51ccf..335d9e5e99 100644
--- a/gptel.el
+++ b/gptel.el
@@ -154,6 +154,7 @@
 (require 'text-property-search)
 (require 'cl-generic)
 (require 'gptel-openai)
+(require 'gptel-contexter)
 
 (with-eval-after-load 'org
   (require 'gptel-org))
@@ -902,11 +903,22 @@ Model parameters can be let-bound around calls to this 
function."
             (set-marker (make-marker) position buffer))))
          (full-prompt
           (cond
-           ((null prompt) (gptel--create-prompt start-marker))
+           ((null prompt)
+            (let ((gptel--system-message (if (memq 
gptel-context-injection-destination
+                                                   '(:before-system-message
+                                                     :after-system-message))
+                                             (gptel--wrap-in-context system)
+                                           system)))
+              (gptel--create-prompt start-marker)))
            ((stringp prompt)
             ;; FIXME Dear reader, welcome to Jank City:
             (with-temp-buffer
-              (let ((gptel-model (buffer-local-value 'gptel-model buffer))
+              (let ((gptel--system-message (if (memq 
gptel-context-injection-destination
+                                                     '(:before-system-message
+                                                       :after-system-message))
+                                               (gptel--wrap-in-context system)
+                                             system))
+                    (gptel-model (buffer-local-value 'gptel-model buffer))
                     (gptel-backend (buffer-local-value 'gptel-backend buffer)))
                 (insert prompt)
                 (gptel--create-prompt))))
@@ -915,6 +927,7 @@ Model parameters can be let-bound around calls to this 
function."
          (info (list :data request-data
                      :buffer buffer
                      :position start-marker)))
+    ;; This context should not be confused with the context aggregation 
context!
     (when context (plist-put info :context context))
     (when in-place (plist-put info :in-place in-place))
     (unless dry-run
@@ -1378,32 +1391,6 @@ context for the ediff session."
   (interactive "p")
   (gptel--previous-variant (- arg)))
 
-(defun gptel-clean-up-llm-code (buffer beg end)
-  "Clean up LLM response between BEG & END in BUFFER.
-
-Removes any markup formatting and indents the code within the parameters of the
-current buffer."
-  (with-current-buffer buffer
-    (save-excursion
-      (let* ((res-beg beg)
-             (res-end end)
-             (contents nil))
-        (setq contents (buffer-substring-no-properties res-beg
-                                                       res-end))
-        (setq contents (replace-regexp-in-string
-                        "^\\(```.*\n\\)\\|\n\\(```.*\\)$"
-                        ""
-                        contents))
-        (delete-region res-beg res-end)
-        (goto-char res-beg)
-        (insert contents)
-        (setq res-end (point))
-        ;; Indent the code to match the buffer indentation if it's messed up.
-        (unless (eq indent-line-function #'indent-relative)
-          (indent-region res-beg res-end))
-        (pulse-momentary-highlight-region res-beg res-end)
-        (setq res-beg (next-single-property-change res-beg 'gptel))))))
-
 (provide 'gptel)
 ;;; gptel.el ends here
 

Reply via email to