(markdown: string)
| 110 | }; |
| 111 | |
| 112 | export function parseReplySegments(markdown: string): ReplySegment[] { |
| 113 | const segments: ReplySegment[] = []; |
| 114 | const lines = markdown.split("\n"); |
| 115 | const fenceRegex = /^```([^\n]*)$/; // opening or closing fence line |
| 116 | |
| 117 | let textBuffer: string[] = []; |
| 118 | let codeBuffer: string[] = []; |
| 119 | let openTag: string | null = null; // e.g. tsx{path=src/App.tsx} |
| 120 | |
| 121 | const flushText = () => { |
| 122 | if (textBuffer.length > 0) { |
| 123 | segments.push({ type: "text", content: textBuffer.join("\n") }); |
| 124 | textBuffer = []; |
| 125 | } |
| 126 | }; |
| 127 | |
| 128 | const parseTag = parseFenceTag; |
| 129 | |
| 130 | for (const line of lines) { |
| 131 | const match = line.match(fenceRegex); |
| 132 | if (match && !openTag) { |
| 133 | // Opening fence |
| 134 | openTag = match[1] || ""; |
| 135 | flushText(); |
| 136 | codeBuffer = []; |
| 137 | } else if (match && openTag) { |
| 138 | // Closing fence |
| 139 | const { language, path } = parseTag(openTag); |
| 140 | segments.push({ |
| 141 | type: "file", |
| 142 | code: codeBuffer.join("\n"), |
| 143 | language, |
| 144 | path, |
| 145 | isPartial: false, |
| 146 | }); |
| 147 | openTag = null; |
| 148 | codeBuffer = []; |
| 149 | } else if (openTag) { |
| 150 | codeBuffer.push(line); |
| 151 | } else { |
| 152 | textBuffer.push(line); |
| 153 | } |
| 154 | } |
| 155 | |
| 156 | // If a code fence remains open, emit a partial file segment |
| 157 | if (openTag) { |
| 158 | const { language, path } = parseTag(openTag); |
| 159 | segments.push({ |
| 160 | type: "file", |
| 161 | code: codeBuffer.join("\n"), |
| 162 | language, |
| 163 | path, |
| 164 | isPartial: true, |
| 165 | }); |
| 166 | } else { |
| 167 | flushText(); |
| 168 | } |
| 169 |
no test coverage detected