footnoteDefinitionAt function

({String label, List<String> lines, int next})? footnoteDefinitionAt(
  1. List<String> lines,
  2. int start
)

A footnote definition starting at line start, or null.

[^label]: text, then its continuation: following lines up to a blank line, unless they start another definition or a block of their own; and after a blank line, further paragraphs indented by four or more spaces. lines is its body with the marker and the continuation indent removed.

Implementation

({String label, List<String> lines, int next})? footnoteDefinitionAt(
  List<String> lines,
  int start,
) {
  final line = lines[start];
  if (!startsAfterIndent(line, 0x5B /* [ */)) {
    return null;
  }
  final t = line.trimLeft();
  if (!t.startsWith('[^')) {
    return null;
  }
  final close = t.indexOf(']');
  if (close < 3 || close + 1 >= t.length || t[close + 1] != ':') {
    return null;
  }
  final label = t.substring(2, close);
  if (label.contains(RegExp(r'[\s\[]'))) {
    return null;
  }
  var first = t.substring(close + 2);
  if (first.startsWith(' ') || first.startsWith('\t')) {
    first = first.substring(1);
  }
  final body = <String>[first];
  var k = start + 1;
  while (k < lines.length) {
    final next = lines[k];
    if (isBlank(next)) {
      var j = k + 1;
      while (j < lines.length && isBlank(lines[j])) {
        j += 1;
      }
      if (j < lines.length && indentWidth(lines[j]) >= 4) {
        for (var b = k; b < j; b++) {
          body.add('');
        }
        k = j;
        continue;
      }
      break;
    }
    if (indentWidth(next) < 4 &&
        (startsDefinition(next) || _interruptsParagraph(next.trimLeft()))) {
      break;
    }
    body.add(stripIndent(next, 4));
    k += 1;
  }
  while (body.isNotEmpty && body.last.trim().isEmpty) {
    body.removeLast();
  }
  return (label: label, lines: body, next: k);
}