apply method

  1. @override
Future<List<Node>> apply(
  1. Page page,
  2. List<Node> nodes
)
override

Applies the extension to the given Page and parsed Nodes.

The page may be modified in place while the nodes should be returned as a new list.

As the page is already parsed, modifying its content or data may not have any effect. Though it can be used to provide additional context to later applied extensions or layouts.

Implementation

@override
Future<List<Node>> apply(Page page, List<Node> nodes) async {
  final toc = <TocEntry>[];
  final stack = <TocEntry>[];

  final toVisit = [...nodes.reversed];

  while (toVisit.isNotEmpty) {
    final node = toVisit.removeLast();
    if (node is! ElementNode) continue;

    final depth = _headerRegex.firstMatch(node.tag)?.group(1);
    if (depth == null) {
      // Not a header, continue searching children.
      toVisit.addAll(node.children?.reversed ?? []);
      continue;
    }

    // Level 0 = h2, Level 1 = h3, etc.
    final level = int.parse(depth) - 2;
    if (level < 0 || level > maxHeaderDepth - 2) continue;

    final id = node.attributes['id'];
    // Header can't be linked to without an id.
    if (id == null) continue;

    // Don't include if no_toc is specified as a class on the header.
    if (node.attributes['class']?.contains('no_toc') ?? false) continue;

    final text = node.innerText;
    final entry = TocEntry(text, id, []);

    while (level < stack.length) {
      stack.removeLast();
    }

    if (level > stack.length) {
      // Found h(x+1) without previous h(x) header.
      continue;
    }

    if (stack.isEmpty) {
      toc.add(entry);
      stack.add(entry);
    } else {
      stack.last.children.add(entry);
    }
  }

  page.apply(data: {'toc': TableOfContents(toc)});

  return nodes;
}