Refactor list syntax (dart-lang/markdown#499)
* Refactor ListSyntax
* Update CHANGELOG.md
* Some cleanup and fixing
* Add a test case for gitHubFlavored ExtensionSet
* Use `=>` syntax for `canParse`
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index fead92f..91da8b7 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -19,6 +19,8 @@
* Add a new public method `parse(String text)` for `Document`.
* Add a new public method `parseLineList(List<Line> text)` for `Document`.
* Add a new type: `Line`.
+* Add a new optional parameter `parentSyntax` for `parseLines()` of
+ `BlockParser`, which can be used when parsing nested blocks.
## 6.0.1
diff --git a/pkgs/markdown/benchmark/output.html b/pkgs/markdown/benchmark/output.html
index e3831bd..200e4bd 100644
--- a/pkgs/markdown/benchmark/output.html
+++ b/pkgs/markdown/benchmark/output.html
@@ -290,7 +290,7 @@
void main() {
group("complicated algorithm tests", () {
// ...
- }, skip: "the algorithm isn't quite right");
+ }, skip: "the algorithm isn't quite right");
test("error-checking test", () {
// ...
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index 5d930fe..023e9cb 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -110,7 +110,14 @@
return regex.hasMatch(next!.content);
}
- List<Node> parseLines() {
+ /// The parent [BlockSyntax] when it is running inside a nested syntax.
+ BlockSyntax? get parentSyntax => _parentSyntax;
+ BlockSyntax? _parentSyntax;
+
+ List<Node> parseLines({
+ BlockSyntax? parentSyntax,
+ }) {
+ _parentSyntax = parentSyntax;
final blocks = <Node>[];
// If the `_pos` does not change before and after `parse()`, never try to
diff --git a/pkgs/markdown/lib/src/block_syntaxes/code_block_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/code_block_syntax.dart
index fdcb5f1..38c9951 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/code_block_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/code_block_syntax.dart
@@ -34,7 +34,11 @@
pattern.hasMatch(parser.current.content) != true) {
break;
}
- childLines.add(Line(parser.current.content.dedent().text));
+
+ childLines.add(Line(
+ parser.current.content.dedent().text,
+ tabRemaining: parser.current.tabRemaining,
+ ));
parser.advance();
}
@@ -49,7 +53,9 @@
// The Markdown tests expect a trailing newline.
childLines.add(Line(''));
- var content = childLines.map((e) => e.content).join('\n');
+ var content = childLines
+ .map((e) => e.content.prependSpace(e.tabRemaining ?? 0))
+ .join('\n');
if (parser.document.encodeHtml) {
content = escapeHtml(content, escapeApos: false);
}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/list_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/list_syntax.dart
index 1acb080..77f6791 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/list_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/list_syntax.dart
@@ -4,8 +4,11 @@
import '../ast.dart';
import '../block_parser.dart';
+import '../charcode.dart';
import '../line.dart';
import '../patterns.dart';
+import '../text_parser.dart';
+import '../util.dart';
import 'block_syntax.dart';
import 'ordered_list_with_checkbox_syntax.dart';
import 'unordered_list_with_checkbox_syntax.dart';
@@ -25,6 +28,11 @@
/// Base class for both ordered and unordered lists.
abstract class ListSyntax extends BlockSyntax {
@override
+ bool canParse(BlockParser parser) =>
+ pattern.hasMatch(parser.current.content) &&
+ !hrPattern.hasMatch(parser.current.content);
+
+ @override
bool canEndBlock(BlockParser parser) {
// An empty list cannot interrupt a paragraph. See
// https://spec.commonmark.org/0.29/#example-255.
@@ -32,12 +40,27 @@
// which accepts a [BlockParser], but this would be a breaking change,
// so we're going with this temporarily.
final match = pattern.firstMatch(parser.current.content)!;
- // The seventh group, in both [olPattern] and [ulPattern] is the text
- // after the delimiter.
- return match[7]?.isNotEmpty ?? false;
- }
- String get listTag;
+ // Allow only lists starting with 1 to interrupt paragraphs, if it is an
+ // ordered list. See https://spec.commonmark.org/0.30/#example-304.
+ // But there shuold be an exception for nested ordered lists, for example:
+ // ```
+ // 1. one
+ // 2. two
+ // 3. three
+ // 4. four
+ // 5. five
+ // ```
+ if (parser.parentSyntax is! ListSyntax &&
+ match[1] != null &&
+ match[1] != '1') {
+ return false;
+ }
+
+ // An empty list item cannot interrupt a paragraph. See
+ // https://spec.commonmark.org/0.30/#example-285
+ return match[2]?.isNotEmpty ?? false;
+ }
const ListSyntax();
@@ -47,14 +70,14 @@
headerPattern,
hrPattern,
indentPattern,
- ulPattern,
- olPattern
+ listPattern,
];
- static final _whitespaceRe = RegExp('[ \t]*');
-
@override
Node parse(BlockParser parser) {
+ final match = pattern.firstMatch(parser.current.content);
+ final ordered = match![1] != null;
+
final taskListParserEnabled = this is UnorderedListWithCheckboxSyntax ||
this is OrderedListWithCheckboxSyntax;
final items = <ListItem>[];
@@ -92,72 +115,117 @@
}
String? listMarker;
- String? indent;
+ int? indent;
// In case the first number in an ordered list is not 1, use it as the
// "start".
int? startNumber;
+ int? blankLines;
+
while (!parser.isDone) {
- final leadingSpace =
- _whitespaceRe.matchAsPrefix(parser.current.content)!.group(0)!;
- final leadingExpandedTabLength = _expandedTabLength(leadingSpace);
+ final currentIndent = parser.current.content.indentation() +
+ (parser.current.tabRemaining ?? 0);
+
if (parser.current.isBlankLine) {
- if (parser.next?.isBlankLine ?? true) {
- // Two blank lines ends a list.
+ childLines.add(parser.current);
+
+ if (blankLines != null) {
+ blankLines++;
+ }
+ } else if (indent != null && indent <= currentIndent) {
+ // A list item can begin with at most one blank line. See:
+ // https://spec.commonmark.org/0.30/#example-280
+ if (blankLines != null && blankLines > 1) {
break;
}
- // Add a blank line to the current list item.
- childLines.add(Line(''));
- } else if (indent != null && indent.length <= leadingExpandedTabLength) {
- // Strip off indent and add to current item.
- final line = parser.current.content
- .replaceFirst(leadingSpace, ' ' * leadingExpandedTabLength)
- .replaceFirst(indent, '');
- childLines.add(Line(parseTaskListItem(line)));
+
+ final indentedLine = parser.current.content.dedent(indent);
+
+ childLines.add(Line(
+ blankLines == null
+ ? indentedLine.text
+ : parseTaskListItem(indentedLine.text),
+ tabRemaining: indentedLine.tabRemaining,
+ ));
} else if (tryMatch(hrPattern)) {
// Horizontal rule takes precedence to a new list item.
break;
- } else if (tryMatch(ulPattern) || tryMatch(olPattern)) {
+ } else if (tryMatch(listPattern)) {
+ blankLines = null;
final match = possibleMatch!;
- final precedingWhitespace = match[1]!;
- final digits = match[2] ?? '';
- if (startNumber == null && digits.isNotEmpty) {
- startNumber = int.parse(digits);
+ final textParser = TextParser(parser.current.content);
+ var precedingWhitespaces = textParser.moveThroughWhitespace();
+ final markerStart = textParser.pos;
+ final digits = match[1] ?? '';
+ if (digits.isNotEmpty) {
+ startNumber ??= int.parse(digits);
+ textParser.advanceBy(digits.length);
}
- final marker = match[3]!;
- final firstWhitespace = match[5] ?? '';
- final restWhitespace = match[6] ?? '';
- final content = match[7] ?? '';
- final isBlank = content.isEmpty;
- if (listMarker != null && listMarker != marker) {
- // Changing the bullet or ordered list delimiter starts a new list.
+ textParser.advance();
+
+ // See https://spec.commonmark.org/0.30/#ordered-list-marker
+ final marker = textParser.substring(
+ markerStart,
+ textParser.pos,
+ );
+
+ var isBlank = true;
+ var contentWhitespances = 0;
+ var containsTab = false;
+ int? contentBlockStart;
+
+ if (!textParser.isDone) {
+ containsTab = textParser.charAt() == $tab;
+ // Skip the first whitespace.
+ textParser.advance();
+ contentBlockStart = textParser.pos;
+ if (!textParser.isDone) {
+ contentWhitespances = textParser.moveThroughWhitespace();
+
+ if (!textParser.isDone) {
+ isBlank = false;
+ }
+ }
+ }
+
+ // Changing the bullet or ordered list delimiter starts a new list.
+ if (listMarker != null && listMarker.last() != marker.last()) {
break;
}
+
+ // End the current list item and start a new one.
+ endItem();
+
+ // Start a new list item, the last item will be ended up outside of the
+ // `while` loop.
listMarker = marker;
- final markerAsSpaces = ' ' * (digits.length + marker.length);
+ precedingWhitespaces += digits.length + 2;
if (isBlank) {
- // See http://spec.commonmark.org/0.28/#list-items under "3. Item
- // starting with a blank line."
- //
- // If the list item starts with a blank line, the final piece of the
- // indentation is just a single space.
- indent = '$precedingWhitespace$markerAsSpaces ';
- } else if (restWhitespace.length >= 4) {
- // See http://spec.commonmark.org/0.28/#list-items under "2. Item
- // starting with indented code."
+ // See https://spec.commonmark.org/0.30/#example-278.
+ blankLines = 1;
+ indent = precedingWhitespaces;
+ } else if (contentWhitespances >= 4) {
+ // See https://spec.commonmark.org/0.30/#example-270.
//
// If the list item starts with indented code, we need to _not_ count
// any indentation past the required whitespace character.
- indent = precedingWhitespace + markerAsSpaces + firstWhitespace;
+ indent = precedingWhitespaces;
} else {
- indent = precedingWhitespace +
- markerAsSpaces +
- firstWhitespace +
- restWhitespace;
+ indent = precedingWhitespaces + contentWhitespances;
}
- // End the current list item and start a new one.
- endItem();
- childLines.add(Line(parseTaskListItem('$restWhitespace$content')));
+
+ var content = contentBlockStart != null && !isBlank
+ ? parseTaskListItem(textParser.substring(contentBlockStart))
+ : '';
+
+ if (content.isEmpty && containsTab) {
+ content = content.prependSpace(2);
+ }
+
+ childLines.add(Line(
+ content,
+ tabRemaining: containsTab ? 2 : null,
+ ));
} else if (BlockSyntax.isAtBlockEnd(parser)) {
// Done with the list.
break;
@@ -195,7 +263,7 @@
}
final itemParser = BlockParser(item.lines, parser.document);
- final children = itemParser.parseLines();
+ final children = itemParser.parseLines(parentSyntax: this);
final itemElement = checkboxToInsert == null
? Element('li', children)
: (Element('li', [checkboxToInsert, ...children])
@@ -227,8 +295,8 @@
}
}
- final listElement = Element(listTag, itemNodes);
- if (listTag == 'ol' && startNumber != 1) {
+ final listElement = Element(ordered ? 'ol' : 'ul', itemNodes);
+ if (ordered && startNumber != 1) {
listElement.attributes['start'] = '$startNumber';
}
@@ -259,12 +327,4 @@
}
return anyEmpty;
}
-
- static int _expandedTabLength(String input) {
- var length = 0;
- for (final char in input.codeUnits) {
- length += char == 0x9 ? 4 - (length % 4) : 1;
- }
- return length;
- }
}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/ordered_list_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/ordered_list_syntax.dart
index 61570a3..53c4730 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/ordered_list_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/ordered_list_syntax.dart
@@ -8,10 +8,7 @@
/// Parses ordered lists.
class OrderedListSyntax extends ListSyntax {
@override
- RegExp get pattern => olPattern;
-
- @override
- String get listTag => 'ol';
+ RegExp get pattern => listPattern;
const OrderedListSyntax();
}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/setext_header_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/setext_header_syntax.dart
index c9aa4ec..d4777a5 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/setext_header_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/setext_header_syntax.dart
@@ -65,7 +65,6 @@
headerPattern.hasMatch(line) ||
blockquotePattern.hasMatch(line) ||
hrPattern.hasMatch(line) ||
- ulPattern.hasMatch(line) ||
- olPattern.hasMatch(line) ||
+ listPattern.hasMatch(line) ||
emptyPattern.hasMatch(line));
}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/unordered_list_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/unordered_list_syntax.dart
index 357e029..35dd670 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/unordered_list_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/unordered_list_syntax.dart
@@ -9,7 +9,7 @@
/// Parses unordered lists.
class UnorderedListSyntax extends ListSyntax {
@override
- RegExp get pattern => ulPattern;
+ RegExp get pattern => listPattern;
@override
bool canParse(BlockParser parser) {
@@ -26,8 +26,5 @@
return pattern.hasMatch(parser.current.content);
}
- @override
- String get listTag => 'ul';
-
const UnorderedListSyntax();
}
diff --git a/pkgs/markdown/lib/src/patterns.dart b/pkgs/markdown/lib/src/patterns.dart
index 2bd696b..03e618c 100644
--- a/pkgs/markdown/lib/src/patterns.dart
+++ b/pkgs/markdown/lib/src/patterns.dart
@@ -34,91 +34,17 @@
/// SETEXT should win.
final hrPattern = RegExp(r'^ {0,3}([-*_])[ \t]*\1[ \t]*\1(?:\1|[ \t])*$');
-// why `{1}`?
-const _checkbox = r'\[[ xX]{1}\]';
-
-const _groupedWhitespaceAndEverything = r'([ \t])([ \t]*)(.*)';
-
-const _oneToNineDigits = r'\d{1,9}';
-
-const _zeroToFourWhitespace = r'[ \t]{0,4}';
-
-const _zeroToThreeSpaces = '[ ]{0,3}';
-
-/// A line starting with one of these markers: `-`, `*`, `+`.
+/// **Unordered list**
+/// A line starting with one of these markers: `-`, `*`, `+`. May have up to
+/// three leading spaces before the marker and any number of spaces or tabs
+/// after.
///
-/// May have up to three leading spaces before the marker and any number of
-/// spaces or tabs after.
+/// **Ordered list**
///
-/// Contains a dummy group at `[2]`, so that the groups in [ulPattern] and
-/// [olPattern] match up; in both, `[2]` is the length of the number that begins
-/// the list marker.
-final ulPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- // Empty group for group number alignment with [olPattern].
- '()'
- '([*+-])'
- '($_groupedWhitespaceAndEverything)?\$');
-
-/// Similar to [ulPattern] but with a GitHub-style checkbox
-/// (`'[ ]'|'[x]'|'[X]'`) following the number.
-///
-/// The checkbox will be grabbed by group `[5]` and [ulPattern]'s groups
-/// `[4]`, `[5]`, and `[6]` are all shifted 2 places to be `[6]`, `[7]`, and
-/// `[8]`.
-final ulWithCheckBoxPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- // Empty group for group number alignment with [olWithCheckBoxPattern].
- '()'
- '([*+-])'
- '($_zeroToFourWhitespace)'
- '($_checkbox)'
- '($_groupedWhitespaceAndEverything)?\$');
-
-/// Similar to [ulWithCheckBoxPattern] but the checkbox is optional.
-// TODO(srawlins): This is temporary tech debt. I think we will collapse
-// [ulPattern] and [ulWithCheckBoxPattern] into this one pattern.
-final ulWithPossibleCheckboxPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- // Empty group for group number alignment with [olWithCheckBoxPattern].
- '()'
- '([*+-])'
- '(($_zeroToFourWhitespace)($_checkbox))?'
- // [7], [8], [9], and [10].
- '($_groupedWhitespaceAndEverything)?\$');
-
/// A line starting with a number like `123.`. May have up to three leading
/// spaces before the marker and any number of spaces or tabs after.
-final olPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- '($_oneToNineDigits)'
- r'([\.)])'
- '($_groupedWhitespaceAndEverything)?\$');
-
-/// Similar to [olPattern] but with a GitHub-style checkbox
-/// (`'[ ]'|'[x]'|'[X]'`) following the number.
-///
-/// The checkbox will be grabbed by group `[5]` and [olPattern]'s groups
-/// `[4]`, `[5]`, and `[6]` are all shifted 2 places to be `[6]`, `[7]`, and
-/// `[8]`.
-final olWithCheckBoxPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- '($_oneToNineDigits)'
- r'([\.)])'
- '($_zeroToFourWhitespace)'
- '($_checkbox)'
- '($_groupedWhitespaceAndEverything)?\$');
-
-/// Similar to [olWithCheckBoxPattern] but the checkbox is optional.
-// TODO(srawlins): This is temporary tech debt. I think we will collapse
-// [olPattern] and [olWithCheckBoxPattern] into this one pattern.
-final olWithPossibleCheckboxPattern = RegExp(''
- '^($_zeroToThreeSpaces)'
- '($_oneToNineDigits)'
- r'([\.)])'
- '(($_zeroToFourWhitespace)($_checkbox))?'
- // [7], [8], [9], and [10].
- '($_groupedWhitespaceAndEverything)?\$');
+final listPattern =
+ RegExp(r'^[ ]{0,3}(?:(\d{1,9})[\.)]|[*+-])(?:[ \t]+(.*))?$');
/// A line of hyphens separated by at least one pipe.
final tablePattern = RegExp(
diff --git a/pkgs/markdown/lib/src/text_parser.dart b/pkgs/markdown/lib/src/text_parser.dart
new file mode 100644
index 0000000..1492268
--- /dev/null
+++ b/pkgs/markdown/lib/src/text_parser.dart
@@ -0,0 +1,57 @@
+// 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 'charcode.dart';
+
+/// A parser to parse a segment of source text.
+class TextParser {
+ final String source;
+
+ TextParser(this.source);
+
+ /// The current read position.
+ var _position = 0;
+ int get pos => _position;
+
+ /// Whether the read position has reached the end of [source].
+ bool get isDone => _position == length;
+
+ /// The length of [source].
+ int get length => source.length;
+
+ /// Walk the parser forward through any whitespace.
+ ///
+ /// Set [multiLine] `true` to support multiline, otherwise it will stop before
+ /// the line feed [$lf].
+ int moveThroughWhitespace({bool multiLine = false}) {
+ var i = 0;
+ while (!isDone) {
+ final char = charAt();
+ if (char != $space &&
+ char != $tab &&
+ char != $vt &&
+ char != $cr &&
+ char != $ff &&
+ !(multiLine == true && char == $lf)) {
+ return i;
+ }
+
+ i++;
+ advance();
+ }
+ return i;
+ }
+
+ int charAt([int? position]) => source.codeUnitAt(position ?? _position);
+
+ /// Moves the read position one character ahead.
+ void advance() => advanceBy(1);
+
+ /// Moves the read position for [length] characters. [length] can be negative.
+ void advanceBy(int length) {
+ _position += length;
+ }
+
+ /// Substrings the [source] and returns a [String].
+ String substring(int start, [int? end]) => source.substring(start, end);
+}
diff --git a/pkgs/markdown/lib/src/util.dart b/pkgs/markdown/lib/src/util.dart
index 22ca19c..4073445 100644
--- a/pkgs/markdown/lib/src/util.dart
+++ b/pkgs/markdown/lib/src/util.dart
@@ -136,6 +136,20 @@
}
extension StringExtensions on String {
+ /// Calculates the length of indentation a `String` has.
+ ///
+ // The behavior of tabs: https://spec.commonmark.org/0.30/#tabs
+ int indentation() {
+ var length = 0;
+ for (final char in codeUnits) {
+ if (char != $space && char != $tab) {
+ break;
+ }
+ length += char == $tab ? 4 - (length % 4) : 1;
+ }
+ return length;
+ }
+
/// Removes up to [length] characters of leading whitespace.
// The way of handling tabs: https://spec.commonmark.org/0.30/#tabs
DedentedText dedent([int length = 4]) {
@@ -172,11 +186,17 @@
return DedentedText(substring(start), tabRemaining);
}
+ /// Adds [width] of spaces to the beginning of this string.
+ String prependSpace(int width) => '${" " * width}$this';
+
/// Whether this string contains only whitespaces.
bool get isBlank => trim().isEmpty;
/// Converts this string to a list of [Line].
List<Line> toLines() => LineSplitter.split(this).map(Line.new).toList();
+
+ /// Returns the last character.
+ String last([int n = 1]) => substring(length - n);
}
/// A class that describes a dedented text.
diff --git a/pkgs/markdown/test/common_mark/list_items.unit b/pkgs/markdown/test/common_mark/list_items.unit
index 7f9119f..b6554e0 100644
--- a/pkgs/markdown/test/common_mark/list_items.unit
+++ b/pkgs/markdown/test/common_mark/list_items.unit
@@ -115,9 +115,11 @@
bar
<<<
<ul>
-<li>foo</li>
-</ul>
+<li>
+<p>foo</p>
<p>bar</p>
+</li>
+</ul>
>>> List items - 263
1. foo
@@ -152,11 +154,12 @@
<li>
<p>Foo</p>
<pre><code>bar
+
+
+baz
</code></pre>
</li>
</ul>
-<pre><code> baz
-</code></pre>
>>> List items - 265
123456789. ok
<<<
@@ -312,10 +315,9 @@
foo
<<<
<ul>
-<li>
-<p>foo</p>
-</li>
+<li></li>
</ul>
+<p>foo</p>
>>> List items - 281
- foo
-
diff --git a/pkgs/markdown/test/common_mark/lists.unit b/pkgs/markdown/test/common_mark/lists.unit
index 7eb42df..986f2aa 100644
--- a/pkgs/markdown/test/common_mark/lists.unit
+++ b/pkgs/markdown/test/common_mark/lists.unit
@@ -36,10 +36,8 @@
The number of windows in my house is
14. The number of doors is 6.
<<<
-<p>The number of windows in my house is</p>
-<ol start="14">
-<li>The number of doors is 6.</li>
-</ol>
+<p>The number of windows in my house is
+14. The number of doors is 6.</p>
>>> Lists - 305
The number of windows in my house is
1. The number of doors is 6.
@@ -63,9 +61,9 @@
<li>
<p>bar</p>
</li>
-</ul>
-<ul>
-<li>baz</li>
+<li>
+<p>baz</p>
+</li>
</ul>
>>> Lists - 307
- foo
@@ -80,14 +78,15 @@
<ul>
<li>bar
<ul>
-<li>baz</li>
+<li>
+<p>baz</p>
+<p>bim</p>
+</li>
</ul>
</li>
</ul>
</li>
</ul>
-<pre><code> bim
-</code></pre>
>>> Lists - 308
- foo
- bar
@@ -275,11 +274,12 @@
<li>a</li>
<li>
<pre><code>b
+
+
</code></pre>
</li>
+<li>c</li>
</ul>
-<pre><code>- c
-</code></pre>
>>> Lists - 319
- a
- b
diff --git a/pkgs/markdown/test/common_mark/tabs.unit b/pkgs/markdown/test/common_mark/tabs.unit
index cee429d..1239481 100644
--- a/pkgs/markdown/test/common_mark/tabs.unit
+++ b/pkgs/markdown/test/common_mark/tabs.unit
@@ -50,7 +50,7 @@
<<<
<ul>
<li>
-<pre><code>foo
+<pre><code> foo
</code></pre>
</li>
</ul>
diff --git a/pkgs/markdown/test/extensions/ordered_list_with_checkboxes.unit b/pkgs/markdown/test/extensions/ordered_list_with_checkboxes.unit
index 84cf86e..3dce7b7 100644
--- a/pkgs/markdown/test/extensions/ordered_list_with_checkboxes.unit
+++ b/pkgs/markdown/test/extensions/ordered_list_with_checkboxes.unit
@@ -45,21 +45,19 @@
<li>five</li>
</ol>
>>> mixed leading spaces
-1.[ ] zero
-2. [ ] one
-3. [ ] two
-4. [ ] three
-5. [ ] four
-6. [ ] five
+1. [ ] zero
+2. [ ] one
+3. [ ] two
+4. [ ] three
+5. [ ] four
<<<
-<p>1.[ ] zero</p>
-<ol start="2" class="contains-task-list">
+<ol class="contains-task-list">
+<li class="task-list-item"><input type="checkbox"></input>zero</li>
<li class="task-list-item"><input type="checkbox"></input>one</li>
<li class="task-list-item"><input type="checkbox"></input>two</li>
<li class="task-list-item"><input type="checkbox"></input>three</li>
-<li class="task-list-item"><input type="checkbox"></input>four</li>
<li>
-<pre><code>[ ] five
+<pre><code>[ ] four
</code></pre>
</li>
</ol>
\ No newline at end of file
diff --git a/pkgs/markdown/test/gfm/list_items.unit b/pkgs/markdown/test/gfm/list_items.unit
index fae2958..bb2cb72 100644
--- a/pkgs/markdown/test/gfm/list_items.unit
+++ b/pkgs/markdown/test/gfm/list_items.unit
@@ -115,9 +115,11 @@
bar
<<<
<ul>
-<li>foo</li>
-</ul>
+<li>
+<p>foo</p>
<p>bar</p>
+</li>
+</ul>
>>> List items - 241
1. foo
@@ -152,11 +154,12 @@
<li>
<p>Foo</p>
<pre><code>bar
+
+
+baz
</code></pre>
</li>
</ul>
-<pre><code> baz
-</code></pre>
>>> List items - 243
123456789. ok
<<<
@@ -312,10 +315,9 @@
foo
<<<
<ul>
-<li>
-<p>foo</p>
-</li>
+<li></li>
</ul>
+<p>foo</p>
>>> List items - 259
- foo
-
diff --git a/pkgs/markdown/test/gfm/lists.unit b/pkgs/markdown/test/gfm/lists.unit
index 9911dea..80558b7 100644
--- a/pkgs/markdown/test/gfm/lists.unit
+++ b/pkgs/markdown/test/gfm/lists.unit
@@ -36,10 +36,8 @@
The number of windows in my house is
14. The number of doors is 6.
<<<
-<p>The number of windows in my house is</p>
-<ol start="14">
-<li>The number of doors is 6.</li>
-</ol>
+<p>The number of windows in my house is
+14. The number of doors is 6.</p>
>>> Lists - 285
The number of windows in my house is
1. The number of doors is 6.
@@ -63,9 +61,9 @@
<li>
<p>bar</p>
</li>
-</ul>
-<ul>
-<li>baz</li>
+<li>
+<p>baz</p>
+</li>
</ul>
>>> Lists - 287
- foo
@@ -80,14 +78,15 @@
<ul>
<li>bar
<ul>
-<li>baz</li>
+<li>
+<p>baz</p>
+<p>bim</p>
+</li>
</ul>
</li>
</ul>
</li>
</ul>
-<pre><code> bim
-</code></pre>
>>> Lists - 288
- foo
- bar
@@ -275,11 +274,12 @@
<li>a</li>
<li>
<pre><code>b
+
+
</code></pre>
</li>
+<li>c</li>
</ul>
-<pre><code>- c
-</code></pre>
>>> Lists - 299
- a
- b
diff --git a/pkgs/markdown/test/gfm/tabs.unit b/pkgs/markdown/test/gfm/tabs.unit
index cee429d..1239481 100644
--- a/pkgs/markdown/test/gfm/tabs.unit
+++ b/pkgs/markdown/test/gfm/tabs.unit
@@ -50,7 +50,7 @@
<<<
<ul>
<li>
-<pre><code>foo
+<pre><code> foo
</code></pre>
</li>
</ul>
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 58b42c6..f299f79 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -234,4 +234,21 @@
''',
inlineOnly: true);
});
+
+ group('ExtensionSet', () {
+ test(
+ '3 asterisks separated with spaces horizontal rule while it is '
+ 'gitHubFlavored',
+ () {
+ // Because `gitHubFlavored` will put `UnorderedListWithCheckboxSyntax`
+ // before `HorizontalRuleSyntax`, the `* * *` will be parsed into an
+ // empty unordered list if `ListSyntax` does not skip the horizontal
+ // rule structure.
+ expect(
+ markdownToHtml('* * *', extensionSet: ExtensionSet.gitHubFlavored),
+ '<hr />\n',
+ );
+ },
+ );
+ });
}
diff --git a/pkgs/markdown/test/util_test.dart b/pkgs/markdown/test/util_test.dart
index 50a9453..5a2108a 100644
--- a/pkgs/markdown/test/util_test.dart
+++ b/pkgs/markdown/test/util_test.dart
@@ -52,6 +52,29 @@
]);
});
});
+
+ group('String.indentation()', () {
+ test('only spaces', () {
+ expect(' '.indentation(), 3);
+ expect(' '.indentation(), 4);
+ expect(' '.indentation(), 5);
+ });
+
+ test('spaces and tabs', () {
+ expect('\t '.indentation(), 6);
+ expect(' \t '.indentation(), 5);
+ expect(' \t'.indentation(), 4);
+ expect('\t\t '.indentation(), 10);
+ expect(' \t\t '.indentation(), 9);
+ expect(' \t\t'.indentation(), 8);
+ });
+
+ test('spaces, tabs and non whitespace characters', () {
+ expect('\t foo'.indentation(), 6);
+ expect(' \t foo'.indentation(), 5);
+ expect(' \tfoo'.indentation(), 4);
+ });
+ });
}
extension on Line {
diff --git a/pkgs/markdown/tool/common_mark_stats.json b/pkgs/markdown/tool/common_mark_stats.json
index 8ff5f7c..12a63e8 100644
--- a/pkgs/markdown/tool/common_mark_stats.json
+++ b/pkgs/markdown/tool/common_mark_stats.json
@@ -527,9 +527,9 @@
"259": "strict",
"260": "strict",
"261": "strict",
- "262": "fail",
+ "262": "strict",
"263": "strict",
- "264": "fail",
+ "264": "strict",
"265": "strict",
"266": "strict",
"267": "strict",
@@ -545,7 +545,7 @@
"277": "strict",
"278": "strict",
"279": "strict",
- "280": "fail",
+ "280": "strict",
"281": "strict",
"282": "strict",
"283": "strict",
@@ -571,10 +571,10 @@
"301": "strict",
"302": "strict",
"303": "strict",
- "304": "fail",
+ "304": "strict",
"305": "strict",
- "306": "fail",
- "307": "fail",
+ "306": "strict",
+ "307": "strict",
"308": "loose",
"309": "loose",
"310": "strict",
@@ -585,7 +585,7 @@
"315": "strict",
"316": "strict",
"317": "loose",
- "318": "fail",
+ "318": "strict",
"319": "strict",
"320": "strict",
"321": "strict",
@@ -671,7 +671,7 @@
"4": "strict",
"5": "strict",
"6": "loose",
- "7": "loose",
+ "7": "strict",
"8": "strict",
"9": "strict",
"10": "strict",
diff --git a/pkgs/markdown/tool/common_mark_stats.txt b/pkgs/markdown/tool/common_mark_stats.txt
index f5fbe3b..eb0ac59 100644
--- a/pkgs/markdown/tool/common_mark_stats.txt
+++ b/pkgs/markdown/tool/common_mark_stats.txt
@@ -14,8 +14,8 @@
1 of 1 – 100.0% Inlines
21 of 27 – 77.8% Link reference definitions
89 of 90 – 98.9% Links
- 45 of 48 – 93.8% List items
- 22 of 26 – 84.6% Lists
+ 48 of 48 – 100.0% List items
+ 26 of 26 – 100.0% Lists
8 of 8 – 100.0% Paragraphs
1 of 1 – 100.0% Precedence
21 of 21 – 100.0% Raw HTML
@@ -24,5 +24,5 @@
11 of 11 – 100.0% Tabs
3 of 3 – 100.0% Textual content
19 of 19 – 100.0% Thematic breaks
- 637 of 652 – 97.7% TOTAL
- 618 of 637 – 97.0% TOTAL Strict
+ 644 of 652 – 98.8% TOTAL
+ 626 of 644 – 97.2% TOTAL Strict
diff --git a/pkgs/markdown/tool/gfm_stats.json b/pkgs/markdown/tool/gfm_stats.json
index 820d869..43cd122 100644
--- a/pkgs/markdown/tool/gfm_stats.json
+++ b/pkgs/markdown/tool/gfm_stats.json
@@ -540,9 +540,9 @@
"237": "strict",
"238": "strict",
"239": "strict",
- "240": "fail",
+ "240": "strict",
"241": "strict",
- "242": "fail",
+ "242": "strict",
"243": "strict",
"244": "strict",
"245": "strict",
@@ -558,7 +558,7 @@
"255": "strict",
"256": "strict",
"257": "strict",
- "258": "fail",
+ "258": "strict",
"259": "strict",
"260": "strict",
"261": "strict",
@@ -584,10 +584,10 @@
"281": "strict",
"282": "strict",
"283": "strict",
- "284": "fail",
+ "284": "strict",
"285": "strict",
- "286": "fail",
- "287": "fail",
+ "286": "strict",
+ "287": "strict",
"288": "loose",
"289": "loose",
"290": "strict",
@@ -598,7 +598,7 @@
"295": "strict",
"296": "strict",
"297": "loose",
- "298": "fail",
+ "298": "strict",
"299": "strict",
"300": "strict",
"301": "strict",
@@ -698,7 +698,7 @@
"4": "strict",
"5": "strict",
"6": "loose",
- "7": "loose",
+ "7": "strict",
"8": "strict",
"9": "strict",
"10": "strict",
diff --git a/pkgs/markdown/tool/gfm_stats.txt b/pkgs/markdown/tool/gfm_stats.txt
index dbeadd0..c14547e 100644
--- a/pkgs/markdown/tool/gfm_stats.txt
+++ b/pkgs/markdown/tool/gfm_stats.txt
@@ -16,8 +16,8 @@
1 of 1 – 100.0% Inlines
22 of 28 – 78.6% Link reference definitions
86 of 87 – 98.9% Links
- 45 of 48 – 93.8% List items
- 22 of 26 – 84.6% Lists
+ 48 of 48 – 100.0% List items
+ 26 of 26 – 100.0% Lists
8 of 8 – 100.0% Paragraphs
1 of 1 – 100.0% Precedence
21 of 21 – 100.0% Raw HTML
@@ -28,5 +28,5 @@
11 of 11 – 100.0% Tabs
3 of 3 – 100.0% Textual content
19 of 19 – 100.0% Thematic breaks
- 655 of 671 – 97.6% TOTAL
- 635 of 655 – 96.9% TOTAL Strict
+ 662 of 671 – 98.7% TOTAL
+ 643 of 662 – 97.1% TOTAL Strict