splitTopLevelCodeBlocks scans the input line by line and splits it into alternating prose and TOP-LEVEL fenced code segments. A fence is top-level only when its ``` (or ~~~) opener starts at column 0 — indented fences (e.g. inside a list item) stay within their prose segment so glamour keeps the sur
(input string)
| 116 | // a regex because fence info strings and nested back-ticks make regex |
| 117 | // matching brittle (a lesson the codebase already encodes elsewhere). |
| 118 | func splitTopLevelCodeBlocks(input string) []mdSegment { |
| 119 | lines := strings.Split(input, "\n") |
| 120 | var segs []mdSegment |
| 121 | var prose []string |
| 122 | |
| 123 | flushProse := func() { |
| 124 | if len(prose) > 0 { |
| 125 | segs = append(segs, mdSegment{text: strings.Join(prose, "\n")}) |
| 126 | prose = prose[:0] |
| 127 | } |
| 128 | } |
| 129 | |
| 130 | for i := 0; i < len(lines); i++ { |
| 131 | fence, lang, ok := topLevelFenceOpener(lines[i]) |
| 132 | if !ok { |
| 133 | prose = append(prose, lines[i]) |
| 134 | continue |
| 135 | } |
| 136 | // Found an opener at column 0; collect through the matching closer. |
| 137 | block := []string{lines[i]} |
| 138 | j := i + 1 |
| 139 | closed := false |
| 140 | for ; j < len(lines); j++ { |
| 141 | block = append(block, lines[j]) |
| 142 | if isFenceCloser(lines[j], fence) { |
| 143 | closed = true |
| 144 | break |
| 145 | } |
| 146 | } |
| 147 | if !closed { |
| 148 | // Unterminated fence — treat the rest as prose so we never drop |
| 149 | // content. Glamour will still render it sensibly. |
| 150 | prose = append(prose, lines[i:]...) |
| 151 | break |
| 152 | } |
| 153 | flushProse() |
| 154 | segs = append(segs, mdSegment{text: strings.Join(block, "\n"), isCode: true, lang: lang}) |
| 155 | i = j |
| 156 | } |
| 157 | flushProse() |
| 158 | return segs |
| 159 | } |
| 160 | |
| 161 | // topLevelFenceOpener reports whether a line opens a fenced code block at |
| 162 | // column 0, returning the fence token ("```" or "~~~") and the declared |