completeOpenMath function

({String completed, int start})? completeOpenMath(
  1. String source, {
  2. required bool dollarsAreMath,
})

The formula left open at the end of source, closed so that it parses.

Returns where its opener starts and the text to render in place of source.substring(start), or null when every formula in source is closed. Returns null too when a code span is still open, since its backticks swallow any \( inside.

\( and \[ are always maths; $$ and $ only when dollarsAreMath. A $ before a digit is a price, as elsewhere in the streaming hold.

Implementation

({int start, String completed})? completeOpenMath(
  String source, {
  required bool dollarsAreMath,
}) {
  final n = source.length;
  var i = 0;
  while (i < n) {
    final c = source.codeUnitAt(i);
    if (c == 0x60 /* ` */ ) {
      final close = source.indexOf('`', i + 1);
      if (close == -1) return null;
      i = close + 1;
      continue;
    }
    if (c == 0x5C /* \ */ && i + 1 < n) {
      final next = source.codeUnitAt(i + 1);
      if (next == 0x28 /* ( */ || next == 0x5B /* [ */ ) {
        final closer = next == 0x28 ? r'\)' : r'\]';
        final end = source.indexOf(closer, i + 2);
        if (end == -1) {
          return _open(source, i, i + 2, source.substring(i, i + 2), closer);
        }
        i = end + 2;
        continue;
      }
      // Any other escape, `\$` included, is one unit.
      i += 2;
      continue;
    }
    if (dollarsAreMath && c == 0x24 /* $ */ ) {
      if (i + 1 < n && source.codeUnitAt(i + 1) == 0x24) {
        final end = source.indexOf(r'$$', i + 2);
        if (end == -1) return _open(source, i, i + 2, r'\[', r'\]');
        i = end + 2;
        continue;
      }
      final next = i + 1 < n ? source.codeUnitAt(i + 1) : -1;
      if (next >= 0x30 && next <= 0x39) {
        i += 1;
        continue;
      }
      final end = _unescapedDollar(source, i + 1);
      if (end == -1) return _open(source, i, i + 1, r'\(', r'\)');
      i = end + 1;
      continue;
    }
    i += 1;
  }
  return null;
}