Rewrite link reference definitions (dart-lang/markdown#506)
* Rewrite link reference definitions
* Update link_parser.dart
* Make some fields setter private
---------
Co-authored-by: Kevin Moore <kevmoo@google.com>
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart
index 2564759..675d8c7 100644
--- a/pkgs/markdown/lib/src/block_parser.dart
+++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -11,6 +11,7 @@
import 'block_syntaxes/header_syntax.dart';
import 'block_syntaxes/horizontal_rule_syntax.dart';
import 'block_syntaxes/html_block_syntax.dart';
+import 'block_syntaxes/link_reference_definition_syntax.dart';
import 'block_syntaxes/ordered_list_syntax.dart';
import 'block_syntaxes/paragraph_syntax.dart';
import 'block_syntaxes/setext_header_syntax.dart';
@@ -62,6 +63,7 @@
const HorizontalRuleSyntax(),
const UnorderedListSyntax(),
const OrderedListSyntax(),
+ const LinkReferenceDefinitionSyntax(),
const ParagraphSyntax()
];
@@ -100,14 +102,21 @@
return lines[_pos + linesAhead];
}
+ /// Advances the reading position by one line.
void advance() {
_pos++;
}
+ /// Retreats the reading position by one line.
void retreat() {
_pos--;
}
+ /// Retreats the reading position by [count] lines.
+ void retreatBy(int count) {
+ _pos -= count;
+ }
+
bool get isDone => _pos >= lines.length;
/// Gets whether or not the current line matches the given pattern.
@@ -166,7 +175,9 @@
}
neverMatch = _pos != positionBefore ? null : syntax;
- if (block != null || syntax is EmptyBlockSyntax) {
+ if (block != null ||
+ syntax is EmptyBlockSyntax ||
+ syntax is LinkReferenceDefinitionSyntax) {
_start = _pos;
}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/link_reference_definition_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/link_reference_definition_syntax.dart
new file mode 100644
index 0000000..5b2b1b5
--- /dev/null
+++ b/pkgs/markdown/lib/src/block_syntaxes/link_reference_definition_syntax.dart
@@ -0,0 +1,65 @@
+// Copyright (c) 2023, 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 '../document.dart';
+import '../line.dart';
+import '../link_parser.dart';
+import '../patterns.dart';
+import '../util.dart';
+import 'block_syntax.dart';
+
+class LinkReferenceDefinitionSyntax extends BlockSyntax {
+ @override
+ RegExp get pattern => linkReferenceDefinitionPattern;
+
+ @override
+ bool canEndBlock(BlockParser parser) => false;
+
+ const LinkReferenceDefinitionSyntax();
+
+ @override
+ Node? parse(BlockParser parser) {
+ final lines = <Line>[parser.current];
+ parser.advance();
+
+ while (!BlockSyntax.isAtBlockEnd(parser)) {
+ lines.add(parser.current);
+ parser.advance();
+ }
+
+ if (!_parseLinkReferenceDefinition(lines, parser)) {
+ parser.retreatBy(lines.length);
+ }
+
+ return null;
+ }
+
+ bool _parseLinkReferenceDefinition(List<Line> lines, BlockParser parser) {
+ final linkParser = LinkParser(lines.map((e) => e.content).join('\n'))
+ ..parseDefinition();
+
+ if (!linkParser.valid) {
+ return false;
+ }
+
+ // Retreat the parsing position back to where the link reference definition
+ // ends, so that the next syntax can continue parsing from there.
+ parser.retreatBy(linkParser.unconsumedLines);
+
+ final labelString = normalizeLinkLabel(linkParser.label!);
+
+ parser.document.linkReferences.putIfAbsent(
+ labelString,
+ () => LinkReference(
+ labelString,
+ linkParser.destination!,
+ linkParser.title,
+ ),
+ );
+
+ return true;
+ }
+}
diff --git a/pkgs/markdown/lib/src/block_syntaxes/paragraph_syntax.dart b/pkgs/markdown/lib/src/block_syntaxes/paragraph_syntax.dart
index affdefd..53d496d 100644
--- a/pkgs/markdown/lib/src/block_syntaxes/paragraph_syntax.dart
+++ b/pkgs/markdown/lib/src/block_syntaxes/paragraph_syntax.dart
@@ -4,18 +4,12 @@
import '../ast.dart';
import '../block_parser.dart';
-import '../document.dart';
import '../patterns.dart';
-import '../util.dart';
import 'block_syntax.dart';
import 'setext_header_syntax.dart';
/// Parses paragraphs of regular text.
class ParagraphSyntax extends BlockSyntax {
- static final _reflinkDefinitionStart = RegExp(r'[ ]{0,3}\[');
-
- static final _whitespacePattern = RegExp(r'^\s*$');
-
@override
RegExp get pattern => dummyPattern;
@@ -49,146 +43,7 @@
return null;
}
- final paragraphLines = _extractReflinkDefinitions(parser, childLines);
- if (paragraphLines == null) {
- // Paragraph consisted solely of reference link definitions.
- return Text('');
- } else {
- final contents = UnparsedContent(paragraphLines.join('\n').trimRight());
- return Element('p', [contents]);
- }
- }
-
- /// Extract reference link definitions from the front of the paragraph, and
- /// return the remaining paragraph lines.
- List<String>? _extractReflinkDefinitions(
- BlockParser parser,
- List<String> lines,
- ) {
- bool lineStartsReflinkDefinition(int i) =>
- lines[i].startsWith(_reflinkDefinitionStart);
-
- var i = 0;
- loopOverDefinitions:
- while (true) {
- // Check for reflink definitions.
- if (!lineStartsReflinkDefinition(i)) {
- // It's paragraph content from here on out.
- break;
- }
- var contents = lines[i];
- var j = i + 1;
- while (j < lines.length) {
- // Check to see if the _next_ line might start a new reflink definition.
- // Even if it turns out not to be, but it started with a '[', then it
- // is not a part of _this_ possible reflink definition.
- if (lineStartsReflinkDefinition(j)) {
- // Try to parse [contents] as a reflink definition.
- if (_parseReflinkDefinition(parser, contents)) {
- // Loop again, starting at the next possible reflink definition.
- i = j;
- continue loopOverDefinitions;
- } else {
- // Could not parse [contents] as a reflink definition.
- break;
- }
- } else {
- contents = '$contents\n${lines[j]}';
- j++;
- }
- }
- // End of the block.
- if (_parseReflinkDefinition(parser, contents)) {
- i = j;
- break;
- }
-
- // It may be that there is a reflink definition starting at [i], but it
- // does not extend all the way to [j], such as:
- //
- // [link]: url // line i
- // "title"
- // garbage
- // [link2]: url // line j
- //
- // In this case, [i, i+1] is a reflink definition, and the rest is
- // paragraph content.
- while (j >= i) {
- // This isn't the most efficient loop, what with this big ole'
- // Iterable allocation (`getRange`) followed by a big 'ole String
- // allocation, but we
- // must walk backwards, checking each range.
- contents = lines.getRange(i, j).join('\n');
- if (_parseReflinkDefinition(parser, contents)) {
- // That is the last reflink definition. The rest is paragraph
- // content.
- i = j;
- break;
- }
- j--;
- }
- // The ending was not a reflink definition at all. Just paragraph
- // content.
-
- break;
- }
-
- if (i == lines.length) {
- // No paragraph content.
- return null;
- } else {
- // Ends with paragraph content.
- return lines.sublist(i);
- }
- }
-
- // Parse [contents] as a reference link definition.
- //
- // Also adds the reference link definition to the document.
- //
- // Returns whether [contents] could be parsed as a reference link definition.
- bool _parseReflinkDefinition(BlockParser parser, String contents) {
- final pattern = RegExp(
- // Leading indentation.
- '''^[ ]{0,3}'''
- // Reference id in brackets, and URL.
- r'''\[((?:\\\]|[^\]])+)\]:\s*(?:<(\S+)>|(\S+))\s*'''
- // Title in double or single quotes, or parens.
- r'''("[^"]+"|'[^']+'|\([^)]+\)|)\s*$''',
- multiLine: true,
- );
- final match = pattern.firstMatch(contents);
- if (match == null) {
- // Not a reference link definition.
- return false;
- }
- if (match.match.length < contents.length) {
- // Trailing text. No good.
- return false;
- }
-
- var label = match[1]!;
- final destination = match[2] ?? match[3]!;
- var title = match[4];
-
- // The label must contain at least one non-whitespace character.
- if (_whitespacePattern.hasMatch(label)) {
- return false;
- }
-
- if (title == '') {
- // No title.
- title = null;
- } else {
- // Remove "", '', or ().
- title = title!.substring(1, title.length - 1);
- }
-
- // References are case-insensitive, and internal whitespace is compressed.
- label = normalizeLinkLabel(label);
-
- parser.document.linkReferences
- .putIfAbsent(label, () => LinkReference(label, destination, title));
- return true;
+ final contents = UnparsedContent(childLines.join('\n').trimRight());
+ return Element('p', [contents]);
}
}
diff --git a/pkgs/markdown/lib/src/link_parser.dart b/pkgs/markdown/lib/src/link_parser.dart
new file mode 100644
index 0000000..4db22ad
--- /dev/null
+++ b/pkgs/markdown/lib/src/link_parser.dart
@@ -0,0 +1,270 @@
+// Copyright (c) 2023, 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';
+import 'text_parser.dart';
+import 'util.dart';
+
+class LinkParser extends TextParser {
+ /// If there is a valid link formed.
+ bool get valid => _valid;
+ bool _valid = false;
+
+ /// Link label.
+ String? get label => _label;
+ String? _label;
+
+ /// Link destination.
+ String? get destination => _destination;
+ String? _destination;
+
+ /// Link title.
+ String? get title => _title;
+ String? _title;
+
+ LinkParser(super.source);
+
+ /// How many lines of the [source] have been consumed by link reference
+ /// definition.
+ int get unconsumedLines => _unconsumedLines;
+ int _unconsumedLines = 0;
+
+ /// Parses [source] to a link reference definition.
+ void parseDefinition() {
+ if (!parseLabel() || isDone || charAt() != $colon) {
+ return;
+ }
+
+ // Advance to the next character after the colon.
+ advance();
+ if (!_parseDestination()) {
+ return;
+ }
+
+ var precedingWhitespaces = moveThroughWhitespace();
+ if (isDone) {
+ _valid = true;
+ return;
+ }
+
+ final multiline = charAt() == $lf;
+ precedingWhitespaces += moveThroughWhitespace(multiLine: true);
+
+ // The title must be preceded by whitespaces.
+ if (precedingWhitespaces == 0 || isDone) {
+ _valid = isDone;
+ return;
+ }
+
+ final hasValidTitle = _parseTitle();
+ // For example: `[foo]: <bar> "baz` is a invalid definition, but this one is
+ // valid:
+ // ```
+ // [foo]: <bar>
+ // "baz
+ // ```
+ if (!hasValidTitle && !multiline) {
+ return;
+ }
+
+ if (hasValidTitle) {
+ moveThroughWhitespace();
+ if (!isDone && charAt() != $lf) {
+ // It is not a valid definition if the title is followed by
+ // non-whitespace characters, for example: `[foo]: <bar> "baz" hello`.
+ // See https://spec.commonmark.org/0.30/#example-209.
+ if (!multiline) {
+ return;
+ }
+ // But it is a valid link reference definition if this definition is
+ // multiline, see https://spec.commonmark.org/0.30/#example-210.
+ _title = null;
+ }
+ }
+
+ final linesUnconsumed = source.substring(pos).split('\n');
+ if (linesUnconsumed.isNotEmpty && linesUnconsumed.first.isBlank) {
+ linesUnconsumed.removeAt(0);
+ }
+ _unconsumedLines = linesUnconsumed.length;
+
+ _valid = true;
+ }
+
+ /// Parses the link label, returns `true` if there is a valid link label.
+ bool parseLabel() {
+ moveThroughWhitespace(multiLine: true);
+
+ if (length - pos < 2) {
+ return false;
+ }
+
+ if (charAt() != $lbracket) {
+ return false;
+ }
+
+ // Advance past the opening `[`.
+ advance();
+ final start = pos;
+
+ // A link label can have at most 999 characters inside the square brackets.
+ // See https://spec.commonmark.org/0.30/#link-label.
+ var maxLoop = 999;
+ while (true) {
+ if (maxLoop-- < 0) {
+ return false;
+ }
+ final char = charAt(pos);
+ if (char == $backslash) {
+ advance();
+ } else if (char == $lbracket) {
+ return false;
+ } else if (char == $rbracket) {
+ break;
+ }
+ advance();
+ if (isDone) {
+ return false;
+ }
+ }
+
+ final text = substring(start, pos);
+ if (text.isBlank) {
+ return false;
+ }
+
+ // Advance past the closing `]`.
+ advance();
+ _label = text;
+ return true;
+ }
+
+ /// Parses the link destination, returns `true` there is a valid link
+ /// destination.
+ bool _parseDestination() {
+ moveThroughWhitespace(multiLine: true);
+ if (isDone) {
+ return false;
+ }
+
+ final isValidDestination = charAt() == $lt
+ ? _parseBracketedDestination()
+ : _parseBareDestination();
+
+ return isValidDestination;
+ }
+
+ /// Parses bracketed destinations (destinations wrapped in `<...>`). The
+ /// current position of the parser must be the first character of the
+ /// destination.
+ ///
+ /// Returns `true` if there is a valid link destination.
+ bool _parseBracketedDestination() {
+ // Walk past the opening `<`.
+ advance();
+
+ final start = pos;
+ while (true) {
+ final char = charAt();
+ if (char == $backslash) {
+ advance();
+ } else if (char == $lf || char == $cr || char == $ff) {
+ return false;
+ } else if (char == $gt) {
+ break;
+ }
+ advance();
+ if (isDone) {
+ return false;
+ }
+ }
+
+ _destination = substring(start, pos);
+
+ // Advance past the closing `>`.
+ advance();
+ return true;
+ }
+
+ /// Parse "bare" destinations (destinations _not_ wrapped in `<...>`). The
+ /// current position of the parser must be the first character of the
+ /// destination.
+ ///
+ /// Returns `true` if there is a valid link destination.
+ bool _parseBareDestination() {
+ var parenCount = 0;
+ final start = pos;
+
+ while (true) {
+ final char = charAt();
+ if (char == $backslash) {
+ advance();
+ } else if (char == $space || char == $lf || char == $cr || char == $ff) {
+ break;
+ } else if (char == $lparen) {
+ parenCount++;
+ } else if (char == $rparen) {
+ parenCount--;
+ if (parenCount == 0) {
+ advance();
+ break;
+ }
+ }
+ advance();
+
+ // There is no ending delimiter, so `isDone` also means it is at the end
+ // of a link destination.
+ if (isDone) {
+ break;
+ }
+ }
+
+ _destination = substring(start, pos);
+ return true;
+ }
+
+ /// Parses the **optional** link title, returns `true` if there is a valid
+ /// link title.
+ bool _parseTitle() {
+ // See: https://spec.commonmark.org/0.30/#link-title
+ // The whitespace should be followed by a title delimiter.
+ final delimiter = charAt();
+ if (delimiter != $apostrophe &&
+ delimiter != $quote &&
+ delimiter != $lparen) {
+ return false;
+ }
+
+ final closeDelimiter = delimiter == $lparen ? $rparen : delimiter;
+ advance();
+ if (isDone) {
+ return false;
+ }
+ final start = pos;
+
+ // Looking for an un-escaped closing delimiter.
+ while (true) {
+ final char = charAt();
+ if (char == $backslash) {
+ advance();
+ } else if (char == closeDelimiter) {
+ break;
+ }
+ advance();
+ if (isDone) {
+ return false;
+ }
+ }
+
+ if (isDone) {
+ return false;
+ }
+
+ _title = substring(start, pos);
+
+ // Advance past the closing delimiter.
+ advance();
+ return true;
+ }
+}
diff --git a/pkgs/markdown/lib/src/patterns.dart b/pkgs/markdown/lib/src/patterns.dart
index 03e618c..1fdddfc 100644
--- a/pkgs/markdown/lib/src/patterns.dart
+++ b/pkgs/markdown/lib/src/patterns.dart
@@ -142,3 +142,6 @@
'&(?:([a-z0-9]+)|#([0-9]{1,7})|#x([a-f0-9]{1,6}));',
caseSensitive: false,
);
+
+/// A line starts with `[`.
+final linkReferenceDefinitionPattern = RegExp(r'[ ]{0,3}\[');
diff --git a/pkgs/markdown/test/common_mark/link_reference_definitions.unit b/pkgs/markdown/test/common_mark/link_reference_definitions.unit
index 8d9ae8f..0677259 100644
--- a/pkgs/markdown/test/common_mark/link_reference_definitions.unit
+++ b/pkgs/markdown/test/common_mark/link_reference_definitions.unit
@@ -25,10 +25,7 @@
[Foo bar]
<<<
-<p>[Foo bar]:
-<my url>
-'title'</p>
-<p>[Foo bar]</p>
+<p><a href="my%20url" title="title">Foo bar</a></p>
>>> Link reference definitions - 196
[foo]: /url '
title
@@ -72,20 +69,20 @@
[foo]
<<<
-<p><a href="%3C%3E">foo</a></p>
+<p><a href="">foo</a></p>
>>> Link reference definitions - 201
[foo]: <bar>(baz)
[foo]
<<<
-<p><a href="bar" title="baz">foo</a></p>
+<p>[foo]: <bar>(baz)</p>
+<p>[foo]</p>
>>> Link reference definitions - 202
[foo]: /url\bar\*baz "foo\"bar\baz"
[foo]
<<<
-<p>[foo]: /url\bar*baz "foo"bar\baz"</p>
-<p>[foo]</p>
+<p><a href="/url%5Cbar*baz" title="foo"bar\baz">foo</a></p>
>>> Link reference definitions - 203
[foo]
@@ -173,16 +170,15 @@
===
[foo]
<<<
-<h1>[foo]: /url
-bar</h1>
-<p>[foo]</p>
+<h1>bar</h1>
+<p><a href="/url">foo</a></p>
>>> Link reference definitions - 216
[foo]: /url
===
[foo]
<<<
-<h1>[foo]: /url</h1>
-<p>[foo]</p>
+<p>===
+<a href="/url">foo</a></p>
>>> Link reference definitions - 217
[foo]: /foo-url "foo"
[bar]: /bar-url
diff --git a/pkgs/markdown/test/common_mark/links.unit b/pkgs/markdown/test/common_mark/links.unit
index 4015350..1cc94bd 100644
--- a/pkgs/markdown/test/common_mark/links.unit
+++ b/pkgs/markdown/test/common_mark/links.unit
@@ -322,6 +322,7 @@
[ref[]: /uri
<<<
<p>[foo][ref[]</p>
+<p>[ref[]: /uri</p>
>>> Links - 546
[foo][ref[bar]]
diff --git a/pkgs/markdown/test/common_mark/lists.unit b/pkgs/markdown/test/common_mark/lists.unit
index 986f2aa..e0422fd 100644
--- a/pkgs/markdown/test/common_mark/lists.unit
+++ b/pkgs/markdown/test/common_mark/lists.unit
@@ -256,7 +256,8 @@
<p>a</p>
</li>
<li>
-<p>b</p></li>
+<p>b</p>
+</li>
<li>
<p>d</p>
</li>
diff --git a/pkgs/markdown/test/gfm/link_reference_definitions.unit b/pkgs/markdown/test/gfm/link_reference_definitions.unit
index 442c431..b09236f 100644
--- a/pkgs/markdown/test/gfm/link_reference_definitions.unit
+++ b/pkgs/markdown/test/gfm/link_reference_definitions.unit
@@ -25,10 +25,7 @@
[Foo bar]
<<<
-<p>[Foo bar]:
-<my url>
-'title'</p>
-<p>[Foo bar]</p>
+<p><a href="my%20url" title="title">Foo bar</a></p>
>>> Link reference definitions - 165
[foo]: /url '
title
@@ -72,20 +69,20 @@
[foo]
<<<
-<p><a href="%3C%3E">foo</a></p>
+<p><a href="">foo</a></p>
>>> Link reference definitions - 170
[foo]: <bar>(baz)
[foo]
<<<
-<p><a href="bar" title="baz">foo</a></p>
+<p>[foo]: <bar>(baz)</p>
+<p>[foo]</p>
>>> Link reference definitions - 171
[foo]: /url\bar\*baz "foo\"bar\baz"
[foo]
<<<
-<p>[foo]: /url\bar*baz "foo"bar\baz"</p>
-<p>[foo]</p>
+<p><a href="/url%5Cbar*baz" title="foo"bar\baz">foo</a></p>
>>> Link reference definitions - 172
[foo]
@@ -173,16 +170,15 @@
===
[foo]
<<<
-<h1>[foo]: /url
-bar</h1>
-<p>[foo]</p>
+<h1>bar</h1>
+<p><a href="/url">foo</a></p>
>>> Link reference definitions - 185
[foo]: /url
===
[foo]
<<<
-<h1>[foo]: /url</h1>
-<p>[foo]</p>
+<p>===
+<a href="/url">foo</a></p>
>>> Link reference definitions - 186
[foo]: /foo-url "foo"
[bar]: /bar-url
diff --git a/pkgs/markdown/test/gfm/links.unit b/pkgs/markdown/test/gfm/links.unit
index 38a861e..7288c79 100644
--- a/pkgs/markdown/test/gfm/links.unit
+++ b/pkgs/markdown/test/gfm/links.unit
@@ -310,6 +310,7 @@
[ref[]: /uri
<<<
<p>[foo][ref[]</p>
+<p>[ref[]: /uri</p>
>>> Links - 555
[foo][ref[bar]]
diff --git a/pkgs/markdown/test/gfm/lists.unit b/pkgs/markdown/test/gfm/lists.unit
index 80558b7..bc675e5 100644
--- a/pkgs/markdown/test/gfm/lists.unit
+++ b/pkgs/markdown/test/gfm/lists.unit
@@ -256,7 +256,8 @@
<p>a</p>
</li>
<li>
-<p>b</p></li>
+<p>b</p>
+</li>
<li>
<p>d</p>
</li>
diff --git a/pkgs/markdown/tool/common_mark_stats.json b/pkgs/markdown/tool/common_mark_stats.json
index d009416..b3d17a9 100644
--- a/pkgs/markdown/tool/common_mark_stats.json
+++ b/pkgs/markdown/tool/common_mark_stats.json
@@ -400,14 +400,14 @@
"192": "strict",
"193": "strict",
"194": "strict",
- "195": "fail",
+ "195": "strict",
"196": "strict",
"197": "strict",
"198": "strict",
"199": "strict",
- "200": "fail",
- "201": "fail",
- "202": "fail",
+ "200": "strict",
+ "201": "strict",
+ "202": "strict",
"203": "strict",
"204": "strict",
"205": "strict",
@@ -420,8 +420,8 @@
"212": "strict",
"213": "strict",
"214": "strict",
- "215": "fail",
- "216": "fail",
+ "215": "strict",
+ "216": "strict",
"217": "strict",
"218": "strict"
},
@@ -490,7 +490,7 @@
"542": "strict",
"543": "strict",
"544": "strict",
- "545": "fail",
+ "545": "strict",
"546": "strict",
"547": "strict",
"548": "strict",
@@ -584,7 +584,7 @@
"314": "strict",
"315": "strict",
"316": "strict",
- "317": "loose",
+ "317": "strict",
"318": "strict",
"319": "strict",
"320": "strict",
diff --git a/pkgs/markdown/tool/common_mark_stats.txt b/pkgs/markdown/tool/common_mark_stats.txt
index 4bd7bda..faf5757 100644
--- a/pkgs/markdown/tool/common_mark_stats.txt
+++ b/pkgs/markdown/tool/common_mark_stats.txt
@@ -12,8 +12,8 @@
22 of 22 – 100.0% Images
12 of 12 – 100.0% Indented code blocks
1 of 1 – 100.0% Inlines
- 21 of 27 – 77.8% Link reference definitions
- 89 of 90 – 98.9% Links
+ 27 of 27 – 100.0% Link reference definitions
+ 90 of 90 – 100.0% Links
48 of 48 – 100.0% List items
26 of 26 – 100.0% Lists
8 of 8 – 100.0% Paragraphs
@@ -24,5 +24,5 @@
11 of 11 – 100.0% Tabs
3 of 3 – 100.0% Textual content
19 of 19 – 100.0% Thematic breaks
- 645 of 652 – 98.9% TOTAL
- 627 of 645 – 97.2% TOTAL Strict
+ 652 of 652 – 100.0% TOTAL
+ 635 of 652 – 97.4% TOTAL Strict
diff --git a/pkgs/markdown/tool/gfm_stats.json b/pkgs/markdown/tool/gfm_stats.json
index 8e9f23c..7b5f993 100644
--- a/pkgs/markdown/tool/gfm_stats.json
+++ b/pkgs/markdown/tool/gfm_stats.json
@@ -415,14 +415,14 @@
"161": "strict",
"162": "strict",
"163": "strict",
- "164": "fail",
+ "164": "strict",
"165": "strict",
"166": "strict",
"167": "strict",
"168": "strict",
- "169": "fail",
- "170": "fail",
- "171": "fail",
+ "169": "strict",
+ "170": "strict",
+ "171": "strict",
"172": "strict",
"173": "strict",
"174": "strict",
@@ -435,8 +435,8 @@
"181": "strict",
"182": "strict",
"183": "strict",
- "184": "fail",
- "185": "fail",
+ "184": "strict",
+ "185": "strict",
"186": "strict",
"187": "strict",
"188": "loose"
@@ -503,7 +503,7 @@
"551": "strict",
"552": "strict",
"553": "strict",
- "554": "fail",
+ "554": "strict",
"555": "strict",
"556": "strict",
"557": "strict",
@@ -597,7 +597,7 @@
"294": "strict",
"295": "strict",
"296": "strict",
- "297": "loose",
+ "297": "strict",
"298": "strict",
"299": "strict",
"300": "strict",
diff --git a/pkgs/markdown/tool/gfm_stats.txt b/pkgs/markdown/tool/gfm_stats.txt
index e7ef10c..03a5fe1 100644
--- a/pkgs/markdown/tool/gfm_stats.txt
+++ b/pkgs/markdown/tool/gfm_stats.txt
@@ -14,8 +14,8 @@
22 of 22 – 100.0% Images
12 of 12 – 100.0% Indented code blocks
1 of 1 – 100.0% Inlines
- 22 of 28 – 78.6% Link reference definitions
- 86 of 87 – 98.9% Links
+ 28 of 28 – 100.0% Link reference definitions
+ 87 of 87 – 100.0% Links
48 of 48 – 100.0% List items
26 of 26 – 100.0% Lists
8 of 8 – 100.0% Paragraphs
@@ -28,5 +28,5 @@
11 of 11 – 100.0% Tabs
3 of 3 – 100.0% Textual content
19 of 19 – 100.0% Thematic breaks
- 662 of 670 – 98.8% TOTAL
- 641 of 662 – 96.8% TOTAL Strict
+ 669 of 670 – 99.9% TOTAL
+ 649 of 669 – 97.0% TOTAL Strict