dvFindableHtml function

String dvFindableHtml(
  1. String content
)

content with each paragraph in a section the browser's find searches.

A paragraph here is any element found at container level that is not a container itself -- a heading, a paragraph, a link on its own line, a code block, a whole list -- wrapped in <section hidden="until-found" data-dv-anchor="N">, N counting from zero in document order. Wrapped rather than given the attribute: until-found does nothing on an inline element, and an <li> or a <p> given attributes would stop being the <li>/<p> every other reader of the block matches on.

A list is one section rather than one per item, because a <section> between <ul> and <li> is not a list any more. The runtime maps a match to the rendered paragraph by its text, so a coarser section costs a little precision and nothing else.

Implementation

String dvFindableHtml(String content) {
  final StringBuffer out = StringBuffer();
  int anchor = 0;
  int cursor = 0;

  // The leaf being written, if any: its name, and how deep inside another of
  // the same name the scanner is -- a list inside a list item.
  String? leaf;
  int depth = 0;

  for (final RegExpMatch match in _tag.allMatches(content)) {
    final bool closing = match.group(1) == '/';
    final String name = match.group(2)!.toLowerCase();
    final bool selfClosing = match.group(3) == '/' || _void.contains(name);

    if (leaf != null) {
      // Inside a section: only its own end tag matters.
      if (name == leaf && !selfClosing) {
        depth += closing ? -1 : 1;
        if (depth == 0) {
          out
            ..write(content.substring(cursor, match.end))
            ..write('</section>');
          cursor = match.end;
          leaf = null;
        }
      }
      continue;
    }

    if (closing || selfClosing || _containers.contains(name)) continue;

    // A paragraph starts here.
    out
      ..write(content.substring(cursor, match.start))
      ..write(_openSection(anchor++));
    cursor = match.start;
    leaf = name;
    depth = 1;
  }

  out.write(content.substring(cursor));
  // An element never closed -- somebody's template, not the semantics tree --
  // still ends inside its section, rather than leaving one open to swallow
  // whatever the page writes after the block.
  if (leaf != null) out.write('</section>');
  return out.toString();
}