package markdown import "core:strings" // generate_toc scans rendered HTML for

-

tags with id attributes and // builds a nested \n") return strings.to_string(b) } // next_heading finds the next tag with an id attribute starting from pos. // Returns level=0 if none found. next_heading :: proc( html: string, start: int, ) -> ( idx: int, level: int, id: string, text: string, next_pos: int, ) { i := start for i + 3 < len(html) { if html[i] == '<' && html[i + 1] == 'h' { d := html[i + 2] if d >= '1' && d <= '6' { level = int(d - '0') idx = i break } } i += 1 } if level == 0 { return 0, 0, "", "", len(html) } // Find end of opening tag tag_end := strings.index_byte(html[idx:], '>') if tag_end < 0 { return 0, 0, "", "", len(html) } tag_end += idx // Find id="..." within the tag tag := html[idx:tag_end + 1] id_pos := strings.index(tag, `id="`) if id_pos < 0 { // No id — skip this heading, continue searching return next_heading(html, tag_end + 1) } id_start := idx + id_pos + 4 id_end_rel := strings.index_byte(html[id_start:], '"') if id_end_rel < 0 { return 0, 0, "", "", len(html) } id = html[id_start:id_start + id_end_rel] // Text between > and text_start := tag_end + 1 close_idx := strings.index(html[text_start:], " tag at pos. close_tag_len :: proc(html: string, pos: int) -> int { if pos + 4 > len(html) { return 4 } end := strings.index_byte(html[pos:], '>') if end < 0 { return 4 } return end + 1 } // strip_tags removes HTML tags from a string, leaving only text content. strip_tags :: proc(s: string) -> string { b: strings.Builder strings.builder_init(&b, context.temp_allocator) i := 0 for i < len(s) { if s[i] == '<' { end := strings.index_byte(s[i:], '>') if end >= 0 { i += end + 1 continue } } strings.write_byte(&b, s[i]) i += 1 } return strings.to_string(b) }