From 9925dc91b4b8fb8aa0df6ba6da91f97d0a97c03a Mon Sep 17 00:00:00 2001 From: Karthik Chikmagalur Date: Wed, 13 Mar 2024 00:48:22 -0700 Subject: [PATCH] gptel: Improve markdown converter * gptel.el (gptel--stream-convert-markdown->org, gptel--replace-source-marker): Handle backquote conversion much better during stream-based conversion. One-shot conversions still require fixing. --- gptel.el | 67 ++++++++++++++++++++++++++++++++++++++++++-------------- 1 file changed, 50 insertions(+), 17 deletions(-) diff --git a/gptel.el b/gptel.el index e436108..2ef17f7 100644 --- a/gptel.el +++ b/gptel.el @@ -1325,6 +1325,22 @@ elements." (delete-char -1) (insert "-")))))) (buffer-string))) +(defun gptel--replace-source-marker (num-ticks &optional end) + "Replace markdown style backticks with Org equivalents. + +NUM-TICKS is the number of backticks being replaced. If END is +true these are \"ending\" backticks. + +This is intended for use in the markdown to org stream converter." + (let ((from (match-beginning 0))) + (delete-region from (point)) + (if (and (= num-ticks 3) + (save-excursion (beginning-of-line) + (skip-chars-forward " \t") + (eq (point) from))) + (insert (if end "#+end_src" "#+begin_src ")) + (insert "=")))) + (defun gptel--stream-convert-markdown->org () "Return a Markdown to Org converter. @@ -1334,6 +1350,7 @@ text stream." (letrec ((in-src-block nil) ;explicit nil to address BUG #183 (temp-buf (generate-new-buffer-name "*gptel-temp*")) (start-pt (make-marker)) + (ticks-total 0) (cleanup-fn (lambda (&rest _) (when (buffer-live-p (get-buffer temp-buf)) @@ -1342,29 +1359,45 @@ text stream." (remove-hook 'gptel-post-response-functions cleanup-fn)))) (add-hook 'gptel-post-response-functions cleanup-fn) (lambda (str) - (let ((noop-p)) + (let ((noop-p) (ticks 0)) (with-current-buffer (get-buffer-create temp-buf) - (save-excursion (goto-char (point-max)) - (insert str)) + (save-excursion (goto-char (point-max)) (insert str)) (when (marker-position start-pt) (goto-char start-pt)) + (when in-src-block (setq ticks ticks-total)) (save-excursion (while (re-search-forward "`\\|\\*\\{1,2\\}\\|_" nil t) (pcase (match-string 0) ("`" - (cond - ((looking-at "``") - (backward-char 1) - (delete-char 3) - (if in-src-block - (progn (insert "#+end_src") - (setq in-src-block nil)) - (insert "#+begin_src ") - (setq in-src-block t))) - ((looking-at "`\\|$") - (setq noop-p t) - (set-marker start-pt (1- (point))) - (unless (eobp) (forward-char 1))) - ((not in-src-block) (replace-match "=")))) + ;; Count number of consecutive backticks + (backward-char) + (while (and (char-after) (eq (char-after) ?`)) + (forward-char) + (if in-src-block (cl-decf ticks) (cl-incf ticks))) + ;; Set the verbatim state of the parser + (if (and (eobp) + ;; Special case heuristic: If the response ends with + ;; ^``` we don't wait for more input. + ;; FIXME: This can have false positives. + (not (save-excursion (beginning-of-line) + (looking-at "^```$")))) + ;; End of input => there could be more backticks coming, + ;; so we wait for more input + (progn (setq noop-p t) (set-marker start-pt (match-beginning 0))) + ;; We reached a character other than a backtick + (cond + ;; Ticks balanced, end src block + ((= ticks 0) + (progn (setq in-src-block nil) + (gptel--replace-source-marker ticks-total 'end))) + ;; Positive number of ticks, start an src block + ((and (> ticks 0) (not in-src-block)) + (setq ticks-total ticks + in-src-block t) + (gptel--replace-source-marker ticks-total)) + ;; Negative number of ticks or in a src block already, + ;; reset ticks + (t (setq ticks ticks-total))))) + ;; Handle other chars: emphasis, bold and bullet items ((and "**" (guard (not in-src-block))) (cond ((looking-at "\\*\\(?:[[:word:]]\\|\s\\)")