Refactor HTML block syntax (dart-lang/markdown#458)

* Refactor HTML blocks syntax

* Update a comment

* Update some comments

* Put some files back and add deprecation annotation

* Update CHANGELOG.md

* Fix some comments

* Update a comment

* Export HtmlBlockSyntax

* Expand some comments
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index 9125fbe..8900c14 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -2,6 +2,10 @@
 
 * Fix a crash in checkbox lists when mixing checkbox items with
   non-checkbox items.
+* Add a new syntax `BlockHtmlSyntax` to parse HTML blocks.
+* Deprecate `BlockTagBlockHtmlSyntax`, `LongBlockHtmlSyntax` and
+  `OtherTagBlockHtmlSyntax`. These syntaxes will be removed from the next major
+  version.
 
 ## 6.0.0
 
diff --git a/pkgs/markdown/lib/markdown.dart b/pkgs/markdown/lib/markdown.dart
index 6155242..de6792d 100644
--- a/pkgs/markdown/lib/markdown.dart
+++ b/pkgs/markdown/lib/markdown.dart
@@ -40,7 +40,6 @@
 export 'src/block_parser.dart';
 export 'src/block_syntaxes/block_html_syntax.dart';
 export 'src/block_syntaxes/block_syntax.dart';
-export 'src/block_syntaxes/block_tag_block_html_syntax.dart';
 export 'src/block_syntaxes/blockquote_syntax.dart';
 export 'src/block_syntaxes/code_block_syntax.dart';
 export 'src/block_syntaxes/dummy_block_syntax.dart';
@@ -50,11 +49,10 @@
 export 'src/block_syntaxes/header_syntax.dart';
 export 'src/block_syntaxes/header_with_id_syntax.dart';
 export 'src/block_syntaxes/horizontal_rule_syntax.dart';
+export 'src/block_syntaxes/html_block_syntax.dart';
 export 'src/block_syntaxes/list_syntax.dart';
-export 'src/block_syntaxes/long_block_html_syntax.dart';
 export 'src/block_syntaxes/ordered_list_syntax.dart';
 export 'src/block_syntaxes/ordered_list_with_checkbox_syntax.dart';
-export 'src/block_syntaxes/other_tag_block_html_syntax.dart';
 export 'src/block_syntaxes/paragraph_syntax.dart';
 export 'src/block_syntaxes/setext_header_syntax.dart';
 export 'src/block_syntaxes/setext_header_with_id_syntax.dart';
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index cb88d8e..beaa46a 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -4,16 +4,14 @@
 
 import 'ast.dart';
 import 'block_syntaxes/block_syntax.dart';
-import 'block_syntaxes/block_tag_block_html_syntax.dart';
 import 'block_syntaxes/blockquote_syntax.dart';
 import 'block_syntaxes/code_block_syntax.dart';
 import 'block_syntaxes/dummy_block_syntax.dart';
 import 'block_syntaxes/empty_block_syntax.dart';
 import 'block_syntaxes/header_syntax.dart';
 import 'block_syntaxes/horizontal_rule_syntax.dart';
-import 'block_syntaxes/long_block_html_syntax.dart';
+import 'block_syntaxes/html_block_syntax.dart';
 import 'block_syntaxes/ordered_list_syntax.dart';
-import 'block_syntaxes/other_tag_block_html_syntax.dart';
 import 'block_syntaxes/paragraph_syntax.dart';
 import 'block_syntaxes/setext_header_syntax.dart';
 import 'block_syntaxes/unordered_list_syntax.dart';
@@ -43,15 +41,7 @@
   /// The collection of built-in block parsers.
   final List<BlockSyntax> standardBlockSyntaxes = [
     const EmptyBlockSyntax(),
-    const BlockTagBlockHtmlSyntax(),
-    LongBlockHtmlSyntax(r'^ {0,3}<pre(?:\s|>|$)', '</pre>'),
-    LongBlockHtmlSyntax(r'^ {0,3}<script(?:\s|>|$)', '</script>'),
-    LongBlockHtmlSyntax(r'^ {0,3}<style(?:\s|>|$)', '</style>'),
-    LongBlockHtmlSyntax('^ {0,3}<!--', '-->'),
-    LongBlockHtmlSyntax(r'^ {0,3}<\?', r'\?>'),
-    LongBlockHtmlSyntax('^ {0,3}<![A-Z]', '>'),
-    LongBlockHtmlSyntax(r'^ {0,3}<!\[CDATA\[', r'\]\]>'),
-    const OtherTagBlockHtmlSyntax(),
+    const HtmlBlockSyntax(),
     const SetextHeaderSyntax(),
     const HeaderSyntax(),
     const CodeBlockSyntax(),
diff --git a/pkgs/markdown/lib/src/block_syntaxes/block_tag_block_html_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/block_tag_block_html_syntax.dart
index e017696..7e40ac9 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/block_tag_block_html_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/block_tag_block_html_syntax.dart
@@ -7,6 +7,7 @@
 import '../patterns.dart';
 import 'block_html_syntax.dart';
 
+@Deprecated('Use HtmlBlockSyntax instead')
 class BlockTagBlockHtmlSyntax extends BlockHtmlSyntax {
   static final _pattern = RegExp(
       '^ {0,3}</?(?:address|article|aside|base|basefont|blockquote|body|'
diff --git a/pkgs/markdown/lib/src/block_syntaxes/html_block_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/html_block_syntax.dart
new file mode 100644
index 0000000..a25da4b
--- /dev/null
+++ b/pkgs/markdown/lib/src/block_syntaxes/html_block_syntax.dart
@@ -0,0 +1,90 @@
+// Copyright (c) 2022, the Dart project authors.  Please see the AUTHORS file
+// for details. All rights reserved. Use of this source code is governed by a
+// BSD-style license that can be found in the LICENSE file.
+
+import '../ast.dart';
+import '../block_parser.dart';
+import '../patterns.dart';
+import 'block_syntax.dart';
+
+/// Parse HTML blocks.
+// There are seven kinds of HTML block defined in the CommonMark spec:
+// https://spec.commonmark.org/0.30/#html-blocks.
+// These matching conditions and HTML block types mentioned in this syntax
+// correspond to these ones in the CommonMark spec.
+class HtmlBlockSyntax extends BlockSyntax {
+  @override
+  RegExp get pattern => htmlBlockPattern;
+
+  // All types of HTML blocks except type 7 may interrupt a paragraph, see the
+  // second paragraph after https://spec.commonmark.org/0.30/#example-148 for
+  // more detail.
+  @override
+  bool canEndBlock(BlockParser parser) =>
+      pattern.firstMatch(parser.current)!.namedGroup('condition_7') == null;
+
+  static final _endConditions = [
+    // For condition 1, it does not need to match the start tag, see
+    // https://spec.commonmark.org/0.30/#end-condition
+    RegExp('</(?:pre|script|style|textarea)>', caseSensitive: false),
+    RegExp('-->'),
+    RegExp(r'\?>'),
+    RegExp('>'),
+    RegExp(']]>'),
+    emptyPattern,
+    emptyPattern,
+  ];
+
+  const HtmlBlockSyntax();
+
+  @override
+  List<String> parseChildLines(BlockParser parser) {
+    final lines = <String>[];
+
+    final match = pattern.firstMatch(parser.current);
+    var matchedCondition = 0;
+    for (var i = 0; i < match!.groupCount; i++) {
+      if (match.group(i + 1) != null) {
+        matchedCondition = i;
+        break;
+      }
+    }
+
+    final endCondition = _endConditions[matchedCondition];
+    if (endCondition == emptyPattern) {
+      lines.add(parser.current);
+      parser.advance();
+
+      while (!parser.isDone && !endCondition.hasMatch(parser.current)) {
+        lines.add(parser.current);
+        parser.advance();
+      }
+    } else {
+      while (!parser.isDone) {
+        lines.add(parser.current);
+        if (endCondition.hasMatch(parser.current)) {
+          break;
+        }
+        parser.advance();
+      }
+      parser.advance();
+    }
+
+    // If the following lines start an HTML block again, put them together with
+    // current HTML block.
+    if (!parser.isDone &&
+        parser.next != null &&
+        pattern.hasMatch(parser.next!)) {
+      parser.advance();
+      lines.addAll(parseChildLines(parser));
+    }
+
+    return lines;
+  }
+
+  @override
+  Node parse(BlockParser parser) {
+    final childLines = parseChildLines(parser);
+    return Text(childLines.join('\n').trimRight());
+  }
+}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/long_block_html_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/long_block_html_syntax.dart
index 8332bce..89e294a 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/long_block_html_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/long_block_html_syntax.dart
@@ -10,6 +10,7 @@
 ///
 /// In practice this means that the syntax dominates; it is allowed to eat
 /// many lines, including blank lines, before matching its `endPattern`.
+@Deprecated('Use HtmlBlockSyntax instead')
 class LongBlockHtmlSyntax extends BlockHtmlSyntax {
   @override
   final RegExp pattern;
diff --git a/pkgs/markdown/lib/src/block_syntaxes/other_tag_block_html_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/other_tag_block_html_syntax.dart
index edb5bfb..bcdc5c7 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/other_tag_block_html_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/other_tag_block_html_syntax.dart
@@ -5,6 +5,7 @@
 import '../block_parser.dart';
 import 'block_tag_block_html_syntax.dart';
 
+@Deprecated('Use HtmlBlockSyntax instead')
 class OtherTagBlockHtmlSyntax extends BlockTagBlockHtmlSyntax {
   @override
   bool canEndBlock(BlockParser parser) => false;
diff --git a/pkgs/markdown/lib/src/patterns.dart b/pkgs/markdown/lib/src/patterns.dart
index 5602630..71b2570 100644
--- a/pkgs/markdown/lib/src/patterns.dart
+++ b/pkgs/markdown/lib/src/patterns.dart
@@ -124,3 +124,77 @@
 /// A pattern which should never be used. It just satisfies non-nullability of
 /// pattern fields.
 final dummyPattern = RegExp('');
+
+/// A [String] pattern to match a named tag like `<table>` or `</table>`.
+const _namedTagDefinition =
+    // Opening tag begins.
+    '<'
+
+    // Tag name.
+    '[a-z][a-z0-9-]*'
+
+    // Attribute begins, see
+    // https://spec.commonmark.org/0.30/#attribute.
+    r'(?:\s+'
+
+    // Attribute name, see
+    // https://spec.commonmark.org/0.30/#attribute-name.
+    '[a-z_:][a-z0-9._:-]*'
+
+    //
+    '(?:'
+    // Attribute value specification, see
+    // https://spec.commonmark.org/0.30/#attribute-value-specification.
+    r'\s*=\s*'
+
+    // Attribute value, see
+    // https://spec.commonmark.org/0.30/#unquoted-attribute-value.
+    r'''(?:[^\s"'=<>`]+?|'[^']*?'|"[^"]*?")'''
+
+    // Attribute ends.
+    ')?)*'
+
+    // Opening tag ends.
+    r'\s*/?>'
+
+    // Or
+    '|'
+
+    // Closing tag, see
+    // https://spec.commonmark.org/0.30/#closing-tag.
+    r'</[a-z][a-z0-9-]*\s*>';
+
+/// A pattern to match the start of an HTML block.
+///
+/// The 7 conditions here correspond to the 7 start conditions in the Commonmark
+/// specification one by one: https://spec.commonmark.org/0.30/#html-block.
+final htmlBlockPattern = RegExp(
+    '^ {0,3}(?:'
+    '<(?<condition_1>pre|script|style|textarea)'
+    r'(?:\s|>|$)'
+    '|'
+    '(?<condition_2><!--)'
+    '|'
+    r'(?<condition_3><\?)'
+    '|'
+    '(?<condition_4><![a-z])'
+    '|'
+    r'(?<condition_5><!\[CDATA\[)'
+    '|'
+    '</?(?<condition_6>address|article|aside|base|basefont|blockquote|body|'
+    'caption|center|col|colgroup|dd|details|dialog|dir|DIV|dl|dt|fieldset|'
+    'figcaption|figure|footer|form|frame|frameset|h1|h2|h3|h4|h5|h6|head|'
+    'header|hr|html|iframe|legend|li|link|main|menu|menuitem|nav|noframes|ol|'
+    'optgroup|option|p|param|section|source|summary|table|tbody|td|tfoot|th|'
+    'thead|title|tr|track|ul)'
+    r'(?:\s|>|/>|$)'
+    '|'
+
+    // Here we are more restrictive than the Commonmark definition (Rule #7).
+    // Otherwise some raw HTML test cases will fail, for example:
+    // https://spec.commonmark.org/0.30/#example-618.
+    // Because if a line is treated as an HTML block, it will output as Text node
+    // directly, the RawHtmlSyntax does not have a chance to validate if this
+    // HTML tag is legal or not.
+    '(?<condition_7>(?:$_namedTagDefinition)\\s*\$))',
+    caseSensitive: false);
diff --git a/pkgs/markdown/test/common_mark/html_blocks.unit b/pkgs/markdown/test/common_mark/html_blocks.unit
index f9afb32..54b229c 100644
--- a/pkgs/markdown/test/common_mark/html_blocks.unit
+++ b/pkgs/markdown/test/common_mark/html_blocks.unit
@@ -210,8 +210,12 @@
 </textarea>
 <<<
 <textarea>
-<p><em>foo</em></p>
-<p><em>bar</em></p></textarea>
+
+*foo*
+
+_bar_
+
+</textarea>
 >>> HTML blocks - 172
 <style
   type="text/css">
@@ -407,9 +411,13 @@
 
 </table>
 <<<
-<table><tr><td>
+<table>
+<tr>
+<td>
 Hi
-</td></tr></table>
+</td>
+</tr>
+</table>
 >>> HTML blocks - 191
 <table>
 
@@ -423,8 +431,10 @@
 
 </table>
 <<<
-<table>  <tr>
+<table>
+  <tr>
 <pre><code>&lt;td&gt;
   Hi
 &lt;/td&gt;
-</code></pre>  </tr></table>
+</code></pre>  </tr>
+</table>
diff --git a/pkgs/markdown/test/common_mark/raw_html.unit b/pkgs/markdown/test/common_mark/raw_html.unit
index f1fa258..a91a33f 100644
--- a/pkgs/markdown/test/common_mark/raw_html.unit
+++ b/pkgs/markdown/test/common_mark/raw_html.unit
@@ -29,7 +29,7 @@
 >>> Raw HTML - 618
 <a h*#ref="hi">
 <<<
-<a h*#ref="hi">
+<p><a h*#ref="hi"></p>
 >>> Raw HTML - 619
 <a href="hi'> <a href=hi'>
 <<<
@@ -47,7 +47,7 @@
 >>> Raw HTML - 621
 <a href='bar'title=title>
 <<<
-<a href='bar'title=title>
+<p><a href='bar'title=title></p>
 >>> Raw HTML - 622
 </a></foo >
 <<<
@@ -55,7 +55,7 @@
 >>> Raw HTML - 623
 </a href="foo">
 <<<
-</a href="foo">
+<p></a href="foo"></p>
 >>> Raw HTML - 624
 foo <!-- this is a
 comment - with hyphen -->
@@ -96,4 +96,4 @@
 >>> Raw HTML - 632
 <a href="\"">
 <<<
-<a href="\"">
+<p><a href="\""></p>
diff --git a/pkgs/markdown/test/gfm/html_blocks.unit b/pkgs/markdown/test/gfm/html_blocks.unit
index 78c47ab..f2562f4 100644
--- a/pkgs/markdown/test/gfm/html_blocks.unit
+++ b/pkgs/markdown/test/gfm/html_blocks.unit
@@ -395,9 +395,13 @@
 
 </table>
 <<<
-<table><tr><td>
+<table>
+<tr>
+<td>
 Hi
-</td></tr></table>
+</td>
+</tr>
+</table>
 >>> HTML blocks - 160
 <table>
 
@@ -411,8 +415,10 @@
 
 </table>
 <<<
-<table>  <tr>
+<table>
+  <tr>
 <pre><code>&lt;td&gt;
   Hi
 &lt;/td&gt;
-</code></pre>  </tr></table>
+</code></pre>  </tr>
+</table>
diff --git a/pkgs/markdown/test/gfm/raw_html.unit b/pkgs/markdown/test/gfm/raw_html.unit
index f693be6..aec88ab 100644
--- a/pkgs/markdown/test/gfm/raw_html.unit
+++ b/pkgs/markdown/test/gfm/raw_html.unit
@@ -29,7 +29,7 @@
 >>> Raw HTML - 638
 <a h*#ref="hi">
 <<<
-<a h*#ref="hi">
+<p><a h*#ref="hi"></p>
 >>> Raw HTML - 639
 <a href="hi'> <a href=hi'>
 <<<
@@ -47,7 +47,7 @@
 >>> Raw HTML - 641
 <a href='bar'title=title>
 <<<
-<a href='bar'title=title>
+<p><a href='bar'title=title></p>
 >>> Raw HTML - 642
 </a></foo >
 <<<
@@ -55,7 +55,7 @@
 >>> Raw HTML - 643
 </a href="foo">
 <<<
-</a href="foo">
+<p></a href="foo"></p>
 >>> Raw HTML - 644
 foo <!-- this is a
 comment - with hyphen -->
@@ -96,4 +96,4 @@
 >>> Raw HTML - 652
 <a href="\"">
 <<<
-<a href="\"">
+<p><a href="\""></p>
diff --git a/pkgs/markdown/tool/common_mark_stats.json b/pkgs/markdown/tool/common_mark_stats.json
index be58d4a..cdaa19a 100644
--- a/pkgs/markdown/tool/common_mark_stats.json
+++ b/pkgs/markdown/tool/common_mark_stats.json
@@ -333,7 +333,7 @@
   "168": "strict",
   "169": "strict",
   "170": "strict",
-  "171": "loose",
+  "171": "strict",
   "172": "strict",
   "173": "strict",
   "174": "loose",
@@ -352,7 +352,7 @@
   "187": "strict",
   "188": "loose",
   "189": "strict",
-  "190": "loose",
+  "190": "strict",
   "191": "loose"
  },
  "Images": {
@@ -620,7 +620,7 @@
   "620": "fail",
   "621": "fail",
   "622": "strict",
-  "623": "fail",
+  "623": "loose",
   "624": "loose",
   "625": "strict",
   "626": "strict",
diff --git a/pkgs/markdown/tool/common_mark_stats.txt b/pkgs/markdown/tool/common_mark_stats.txt
index 023259f..1ab5d03 100644
--- a/pkgs/markdown/tool/common_mark_stats.txt
+++ b/pkgs/markdown/tool/common_mark_stats.txt
@@ -18,11 +18,11 @@
   22 of   26 –  84.6%  Lists
    8 of    8 – 100.0%  Paragraphs
    1 of    1 – 100.0%  Precedence
-  15 of   21 –  71.4%  Raw HTML
+  16 of   21 –  76.2%  Raw HTML
   26 of   27 –  96.3%  Setext headings
    2 of    2 – 100.0%  Soft line breaks
   11 of   11 – 100.0%  Tabs
    3 of    3 – 100.0%  Textual content
   19 of   19 – 100.0%  Thematic breaks
- 614 of  652 –  94.2%  TOTAL
- 547 of  614 –  89.1%  TOTAL Strict
+ 615 of  652 –  94.3%  TOTAL
+ 549 of  615 –  89.3%  TOTAL Strict
diff --git a/pkgs/markdown/tool/gfm_stats.json b/pkgs/markdown/tool/gfm_stats.json
index ffbe9bd..3ef3e95 100644
--- a/pkgs/markdown/tool/gfm_stats.json
+++ b/pkgs/markdown/tool/gfm_stats.json
@@ -367,7 +367,7 @@
   "156": "strict",
   "157": "loose",
   "158": "strict",
-  "159": "loose",
+  "159": "strict",
   "160": "loose"
  },
  "Images": {
@@ -633,7 +633,7 @@
   "640": "fail",
   "641": "fail",
   "642": "strict",
-  "643": "fail",
+  "643": "loose",
   "644": "loose",
   "645": "strict",
   "646": "strict",
diff --git a/pkgs/markdown/tool/gfm_stats.txt b/pkgs/markdown/tool/gfm_stats.txt
index b82976e..e18f44f 100644
--- a/pkgs/markdown/tool/gfm_stats.txt
+++ b/pkgs/markdown/tool/gfm_stats.txt
@@ -20,7 +20,7 @@
   22 of   26 –  84.6%  Lists
    8 of    8 – 100.0%  Paragraphs
    1 of    1 – 100.0%  Precedence
-  15 of   21 –  71.4%  Raw HTML
+  16 of   21 –  76.2%  Raw HTML
   26 of   27 –  96.3%  Setext headings
    2 of    2 – 100.0%  Soft line breaks
    2 of    2 – 100.0%  Strikethrough (extension)
@@ -28,5 +28,5 @@
   11 of   11 – 100.0%  Tabs
    3 of    3 – 100.0%  Textual content
   19 of   19 – 100.0%  Thematic breaks
- 626 of  671 –  93.3%  TOTAL
- 551 of  626 –  88.0%  TOTAL Strict
+ 627 of  671 –  93.4%  TOTAL
+ 552 of  627 –  88.0%  TOTAL Strict