Throw, if `BlockSyntax.parseLines` loops indefinitely (dart-lang/markdown#533)

We throw an `AssertionError` if `BlockSyntax.parseLines` enters an
infinite loop. This should have no effect, if there are no bugs.
But if there is a bug, it's easier to discover it in production if the
failure mode isn't an infinite loop eating up CPU, but just an unhandled
exception.
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index fc0c1ac..efe2352 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -164,7 +164,9 @@
     // number of cells, which makes a table like structure not be recognized.
     BlockSyntax? neverMatch;
 
+    var iterationsWithoutProgress = 0;
     while (!isDone) {
+      final positionBefore = _pos;
       for (final syntax in blockSyntaxes) {
         if (neverMatch == syntax) {
           continue;
@@ -173,7 +175,6 @@
         if (syntax.canParse(this)) {
           _previousSyntax = _currentSyntax;
           _currentSyntax = syntax;
-          final positionBefore = _pos;
           final block = syntax.parse(this);
           if (block != null) {
             blocks.add(block);
@@ -189,6 +190,25 @@
           break;
         }
       }
+      // Count the number of iterations without progress.
+      // This ensures that we don't have an infinite loop. And if we have an
+      // infinite loop, it's easier to gracefully recover from an error, than
+      // it is to discover an kill an isolate that's stuck in an infinite loop.
+      // Technically, it should be perfectly safe to remove this check
+      // But as it's possible to inject custom BlockSyntax implementations and
+      // combine existing ones, it is hard to promise that no combination can't
+      // trigger an infinite loop
+      if (positionBefore == _pos) {
+        iterationsWithoutProgress++;
+        if (iterationsWithoutProgress > 2) {
+          // If this happens we throw an error to avoid having the parser
+          // running in an infinite loop. An error is easier to handle.
+          // If you see this error in production please file a bug!
+          throw AssertionError('BlockParser.parseLines is not advancing');
+        }
+      } else {
+        iterationsWithoutProgress = 0;
+      }
     }
 
     return blocks;