Fix markdown bold/italic rendering and sidebar border gap
ober
9c534a0d7e975301d53d1336a8d69076c14c8608
--- a/lib/jcode/ui/tui-markdown.sls +++ b/lib/jcode/ui/tui-markdown.sls @@ -108,21 +108,33 @@ (def (md-inline-segments text) "Parse inline markdown (bold, code, italic, links) → list of segments." (let ([len (string-length text)]) - (let loop ([i 0] [acc '()]) + (let loop ([i 0] [plain-from 0] [acc '()]) (cond - [(>= i len) (reverse (flush-plain acc i i text))] + [(>= i len) + (reverse + (if (< plain-from len) + (cons + (seg (substring text plain-from len) 'assistant-text) + acc) + acc))] [(and (< (+ i 1) len) (char=? (string-ref text i) #\*) (char=? (string-ref text (+ i 1)) #\*)) (let ([end (find-marker text (+ i 2) "**")]) (if end - (let ([acc (flush-plain acc i (- (length acc)) text)]) + (let ([acc (if (< plain-from i) + (cons + (seg (substring text plain-from i) + 'assistant-text) + acc) + acc)]) (loop (+ end 2) + (+ end 2) (cons (seg (substring text (+ i 2) end) 'bold) acc))) - (loop (+ i 1) acc)))] + (loop (+ i 1) plain-from acc)))] [(and (char=? (string-ref text i) #\*) (or (zero? i) (not (char=? (string-ref text (- i 1)) #\*))) @@ -130,23 +142,35 @@ (not (char=? (string-ref text (+ i 1)) #\*))) (let ([end (find-single-char text (+ i 1) #\*)]) (if end - (let ([acc (flush-plain acc i (- (length acc)) text)]) + (let ([acc (if (< plain-from i) + (cons + (seg (substring text plain-from i) + 'assistant-text) + acc) + acc)]) (loop (+ end 1) + (+ end 1) (cons (seg (substring text (+ i 1) end) 'italic) acc))) - (loop (+ i 1) acc)))] + (loop (+ i 1) plain-from acc)))] [(char=? (string-ref text i) #\`) (let ([end (find-single-char text (+ i 1) #\`)]) (if end - (let ([acc (flush-plain acc i (- (length acc)) text)]) + (let ([acc (if (< plain-from i) + (cons + (seg (substring text plain-from i) + 'assistant-text) + acc) + acc)]) (loop (+ end 1) + (+ end 1) (cons (seg (substring text (+ i 1) end) 'code-inline) acc))) - (loop (+ i 1) acc)))] + (loop (+ i 1) plain-from acc)))] [(char=? (string-ref text i) #\[) (let ([close (find-single-char text (+ i 1) #\])]) (if (and close @@ -154,31 +178,23 @@ (char=? (string-ref text (+ close 1)) #\()) (let ([pclose (find-single-char text (+ close 2) #\))]) (if pclose - (let ([acc (flush-plain - acc - i - (- (length acc)) - text)] + (let ([acc (if (< plain-from i) + (cons + (seg (substring + text + plain-from + i) + 'assistant-text) + acc) + acc)] [link-text (substring text (+ i 1) close)]) (loop (+ pclose 1) + (+ pclose 1) (cons (seg link-text 'link) acc))) - (loop (+ i 1) acc))) - (loop (+ i 1) acc)))] - [#t (loop (+ i 1) acc)])))) - (def (flush-plain acc pos marker text) - "Flush accumulated plain text since last formatted segment." - (let ([start (plain-start acc pos text)]) - (if (and (< start pos) (<= pos (string-length text))) - (cons (seg (substring text start pos) 'assistant-text) acc) - acc))) - (def (plain-start acc pos text) - "Calculate where the current plain-text run started." - (let loop ([i (- pos 1)]) - (cond - [(< i 0) 0] - [(char=? (string-ref text i) #\`) i] - [#t (loop (- i 1))]))) + (loop (+ i 1) plain-from acc))) + (loop (+ i 1) plain-from acc)))] + [#t (loop (+ i 1) plain-from acc)])))) (def (find-marker text start marker) (let ([len (string-length text)] [m0 (string-ref marker 0)] --- a/lib/jcode/ui/tui-sidebar.sls +++ b/lib/jcode/ui/tui-sidebar.sls @@ -32,7 +32,7 @@ [dfg (face-fg-attr 'sidebar-divider)]) (let rloop ([row y]) (when (< row (+ y height)) - (let cloop ([col x]) + (let cloop ([col (max 0 (- x 1))]) (when (< col (+ x width)) (tb-change-cell! col row (char->integer #\space) bg bg) (cloop (+ col 1)))) --- a/src/jcode/ui/tui-markdown.ss +++ b/src/jcode/ui/tui-markdown.ss @@ -106,9 +106,12 @@ (def (md-inline-segments text) "Parse inline markdown (bold, code, italic, links) → list of segments." (let ((len (string-length text))) - (let loop ((i 0) (acc '())) + (let loop ((i 0) (plain-from 0) (acc '())) (cond - ((>= i len) (reverse (flush-plain acc i i text))) + ((>= i len) + (reverse (if (< plain-from len) + (cons (seg (substring text plain-from len) 'assistant-text) acc) + acc))) ;; **bold** ((and (< (+ i 1) len) @@ -116,10 +119,12 @@ (char=? (string-ref text (+ i 1)) #\*)) (let ((end (find-marker text (+ i 2) "**"))) (if end - (let ((acc (flush-plain acc i (- (length acc)) text))) - (loop (+ end 2) + (let ((acc (if (< plain-from i) + (cons (seg (substring text plain-from i) 'assistant-text) acc) + acc))) + (loop (+ end 2) (+ end 2) (cons (seg (substring text (+ i 2) end) 'bold) acc))) - (loop (+ i 1) acc)))) + (loop (+ i 1) plain-from acc)))) ;; *italic* ((and (char=? (string-ref text i) #\*) @@ -128,19 +133,23 @@ (not (char=? (string-ref text (+ i 1)) #\*))) (let ((end (find-single-char text (+ i 1) #\*))) (if end - (let ((acc (flush-plain acc i (- (length acc)) text))) - (loop (+ end 1) + (let ((acc (if (< plain-from i) + (cons (seg (substring text plain-from i) 'assistant-text) acc) + acc))) + (loop (+ end 1) (+ end 1) (cons (seg (substring text (+ i 1) end) 'italic) acc))) - (loop (+ i 1) acc)))) + (loop (+ i 1) plain-from acc)))) ;; `inline code` ((char=? (string-ref text i) #\`) (let ((end (find-single-char text (+ i 1) #\`))) (if end - (let ((acc (flush-plain acc i (- (length acc)) text))) - (loop (+ end 1) + (let ((acc (if (< plain-from i) + (cons (seg (substring text plain-from i) 'assistant-text) acc) + acc))) + (loop (+ end 1) (+ end 1) (cons (seg (substring text (+ i 1) end) 'code-inline) acc))) - (loop (+ i 1) acc)))) + (loop (+ i 1) plain-from acc)))) ;; [link](url) — show link text with link face ((char=? (string-ref text i) #\[) @@ -149,33 +158,20 @@ (char=? (string-ref text (+ close 1)) #\()) (let ((pclose (find-single-char text (+ close 2) #\)))) (if pclose - (let ((acc (flush-plain acc i (- (length acc)) text)) + (let ((acc (if (< plain-from i) + (cons (seg (substring text plain-from i) 'assistant-text) acc) + acc)) (link-text (substring text (+ i 1) close))) - (loop (+ pclose 1) + (loop (+ pclose 1) (+ pclose 1) (cons (seg link-text 'link) acc))) - (loop (+ i 1) acc))) - (loop (+ i 1) acc)))) + (loop (+ i 1) plain-from acc))) + (loop (+ i 1) plain-from acc)))) - (#t (loop (+ i 1) acc)))))) + (#t (loop (+ i 1) plain-from acc)))))) ;; ---- Helpers ---- -(def (flush-plain acc pos marker text) - "Flush accumulated plain text since last formatted segment." - ;; Find where plain text started - (let ((start (plain-start acc pos text))) - (if (and (< start pos) (<= pos (string-length text))) - (cons (seg (substring text start pos) 'assistant-text) acc) - acc))) - -(def (plain-start acc pos text) - "Calculate where the current plain-text run started." - ;; Walk backwards through what we've consumed - (let loop ((i (- pos 1))) - (cond - ((< i 0) 0) - ((char=? (string-ref text i) #\`) i) ;; stop heuristic - (#t (loop (- i 1)))))) + (def (find-marker text start marker) (let ((len (string-length text)) --- a/src/jcode/ui/tui-sidebar.ss +++ b/src/jcode/ui/tui-sidebar.ss @@ -36,10 +36,10 @@ (tfg (face-fg-attr 'sidebar-title)) (tbg (face-bg-attr 'sidebar-title)) (dfg (face-fg-attr 'sidebar-divider))) - ;; Clear sidebar area + ;; Clear sidebar area (include 1 col left of divider to fill gap) (let rloop ((row y)) (when (< row (+ y height)) - (let cloop ((col x)) + (let cloop ((col (max 0 (- x 1)))) (when (< col (+ x width)) (tb-change-cell! col row (char->integer #\space) bg bg) (cloop (+ col 1))))