diff --git a/CHANGELOG.md b/CHANGELOG.md index 0072bcb..9125fbe 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md
@@ -1,4 +1,9 @@ -## 6.0.0-dev +## 6.0.1 + +* Fix a crash in checkbox lists when mixing checkbox items with + non-checkbox items. + +## 6.0.0 * Require Dart 2.17 * Add support to GFM extension for GitHub task lists (aka checkboxes). These
diff --git a/analysis_options.yaml b/analysis_options.yaml index 8dc8263..4190e9a 100644 --- a/analysis_options.yaml +++ b/analysis_options.yaml
@@ -20,10 +20,12 @@ - directives_ordering - only_throw_errors - package_api_docs + - prefer_const_declarations + - prefer_final_locals + - prefer_single_quotes - test_types_in_equals - throw_in_finally - - prefer_final_locals + - unnecessary_raw_strings - use_if_null_to_convert_nulls_to_bools - use_raw_strings - use_super_parameters - - unnecessary_raw_strings
diff --git a/example/app.dart b/example/app.dart index 0b06b6b..d8ea6e7 100644 --- a/example/app.dart +++ b/example/app.dart
@@ -4,6 +4,7 @@ import 'dart:async'; import 'dart:html'; + import 'package:markdown/markdown.dart' as md; import 'highlight.dart'; @@ -14,11 +15,15 @@ final nullSanitizer = NullTreeSanitizer(); const typing = Duration(milliseconds: 150); -final introText = '''Markdown is the **best**! +const introText = '''Markdown is the **best**! * It has lists. * It has [links](https://dart.dev). -* It has _so much more_...'''; +* It has... + ```dart + void sourceCode() {} + ``` +* ...and _so much more_...'''; // Flavor support. final basicRadio = querySelector('#basic-radio') as HtmlElement;
diff --git a/lib/src/block_syntaxes/list_syntax.dart b/lib/src/block_syntaxes/list_syntax.dart index 483a0d8..4b7619f 100644 --- a/lib/src/block_syntaxes/list_syntax.dart +++ b/lib/src/block_syntaxes/list_syntax.dart
@@ -6,21 +6,32 @@ import '../block_parser.dart'; import '../patterns.dart'; import 'block_syntax.dart'; +import 'ordered_list_with_checkbox_syntax.dart'; +import 'unordered_list_with_checkbox_syntax.dart'; + +/// As of Markdown 6.0.1 invisible indicators for checked/unchecked checkboxes are +/// no longer used. These constants are now empty strings to reflect that. +@Deprecated( + 'This string is no longer used internally. It will be removed in a future version.') +const indicatorForUncheckedCheckBox = ''; + +/// As of Markdown 6.0.1 invisible indicators for checked/unchecked checkboxes are +/// no longer used. These constants are now empty strings to reflect that. +@Deprecated( + 'This string is no longer used internally. It be will be removed in a future version.') +const indicatorForCheckedCheckBox = ''; class ListItem { - ListItem(this.lines); + const ListItem( + this.lines, { + this.taskListItemState, + }); - bool forceBlock = false; final List<String> lines; + final TaskListItemState? taskListItemState; } -/// Invisible string used to placehold for an *unchecked* CheckBox. -/// The character is Unicode Zero Width Space (U+200B). -const indicatorForUncheckedCheckBox = '\u{200B}'; - -/// Invisible string used to placehold for a *checked* CheckBox. -/// This is 2 Unicode Zero Width Space (U+200B) characters. -const indicatorForCheckedCheckBox = '\u{200B}\u{200B}'; +enum TaskListItemState { checked, unchecked } /// Base class for both ordered and unordered lists. abstract class ListSyntax extends BlockSyntax { @@ -55,18 +66,36 @@ @override Node parse(BlockParser parser) { + final taskListParserEnabled = this is UnorderedListWithCheckboxSyntax || + this is OrderedListWithCheckboxSyntax; final items = <ListItem>[]; var childLines = <String>[]; - final isCheckboxListSubclass = - (listTag == 'ol_with_checkbox' || listTag == 'ul_with_checkbox'); + TaskListItemState? taskListItemState; void endItem() { if (childLines.isNotEmpty) { - items.add(ListItem(childLines)); + items.add(ListItem(childLines, taskListItemState: taskListItemState)); childLines = <String>[]; } } + String parseTaskListItem(String text) { + final pattern = RegExp(r'^ {0,3}\[([ xX])\][ \t]'); + + if (taskListParserEnabled && pattern.hasMatch(text)) { + return text.replaceFirstMapped(pattern, ((match) { + taskListItemState = match[1] == ' ' + ? TaskListItemState.unchecked + : TaskListItemState.checked; + + return ''; + })); + } else { + taskListItemState = null; + return text; + } + } + late Match? possibleMatch; bool tryMatch(RegExp pattern) { possibleMatch = pattern.firstMatch(parser.current); @@ -83,7 +112,7 @@ final leadingSpace = _whitespaceRe.matchAsPrefix(parser.current)!.group(0)!; final leadingExpandedTabLength = _expandedTabLength(leadingSpace); - if (tryMatch(emptyPattern)) { + if (emptyPattern.hasMatch(parser.current)) { if (emptyPattern.hasMatch(parser.next ?? '')) { // Two blank lines ends a list. break; @@ -95,47 +124,21 @@ final line = parser.current .replaceFirst(leadingSpace, ' ' * leadingExpandedTabLength) .replaceFirst(indent, ''); - childLines.add(line); + childLines.add(parseTaskListItem(line)); } else if (tryMatch(hrPattern)) { // Horizontal rule takes precedence to a new list item. break; - } else if ((isCheckboxListSubclass && - (tryMatch(ulWithCheckBoxPattern) || - tryMatch(olWithCheckBoxPattern))) || - tryMatch(ulPattern) || - tryMatch(olPattern)) { - // We know we have a valid [possibleMatch] now, so capture it. - final successfulMatch = possibleMatch!; - // The checkbox "subclass" patterns ([ulWithCheckBoxPattern] and - // [olWithCheckBoxPattern]) have 2 extra capturing groups at the 5 - // position to capture the checkbox. These shift the other groups - // forward by 2 slots. - final cbGroupOffset = isCheckboxListSubclass ? 2 : 0; - final precedingWhitespace = successfulMatch[1]!; - final digits = successfulMatch[2] ?? ''; + } else if (tryMatch(ulPattern) || tryMatch(olPattern)) { + final match = possibleMatch!; + final precedingWhitespace = match[1]!; + final digits = match[2] ?? ''; if (startNumber == null && digits.isNotEmpty) { startNumber = int.parse(digits); } - final marker = successfulMatch[3]!; - // [checkBoxIndicator] is always empty unless a checkbox was found. - String checkBoxIndicator = ''; - if (isCheckboxListSubclass) { - // Look at checkbox capture group and get checkbox state. - // If we find a checked or unchecked checkbox then we will - // set [checkBoxIndicator] to one of our invisible - // codes that we can later detect to know if we need to insert - // a check or unchecked checkbox when we are inserting the - // listitem li node. - final String checkboxGroup = successfulMatch[5]!.toLowerCase(); - if (checkboxGroup == '[ ]') { - checkBoxIndicator = indicatorForUncheckedCheckBox; - } else if (checkboxGroup == '[x]') { - checkBoxIndicator = indicatorForCheckedCheckBox; - } - } - final firstWhitespace = successfulMatch[5 + cbGroupOffset] ?? ''; - final restWhitespace = successfulMatch[6 + cbGroupOffset] ?? ''; - final content = successfulMatch[7 + cbGroupOffset] ?? ''; + 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. @@ -165,7 +168,7 @@ } // End the current list item and start a new one. endItem(); - childLines.add('$checkBoxIndicator$restWhitespace$content'); + childLines.add(parseTaskListItem('$restWhitespace$content')); } else if (BlockSyntax.isAtBlockEnd(parser)) { // Done with the list. break; @@ -189,34 +192,27 @@ items.forEach(_removeLeadingEmptyLine); final anyEmptyLines = _removeTrailingEmptyLines(items); var anyEmptyLinesBetweenBlocks = false; + var containsTaskList = false; for (final item in items) { - final itemParser = BlockParser(item.lines, parser.document); - final children = itemParser.parseLines(); - // If this is a checkbox sublass of ListSyntax then we must check - // for possible invisible checkbox placeholder characters at - // the start of first node's text to see if we need to insert a checkbox. Element? checkboxToInsert; - if (isCheckboxListSubclass) { - if (children.isNotEmpty) { - if (children.first.textContent - .startsWith(indicatorForCheckedCheckBox)) { - checkboxToInsert = Element.withTag('input') - ..attributes['type'] = 'checkbox' - ..attributes['checked'] = 'true'; - } else if (children.first.textContent - .startsWith(indicatorForUncheckedCheckBox)) { - checkboxToInsert = Element.withTag('input') - ..attributes['type'] = 'checkbox'; - } + if (item.taskListItemState != null) { + containsTaskList = true; + checkboxToInsert = Element.withTag('input') + ..attributes['type'] = 'checkbox'; + if (item.taskListItemState == TaskListItemState.checked) { + checkboxToInsert.attributes['checked'] = 'true'; } } - if (checkboxToInsert != null) { - itemNodes.add(Element('li', [checkboxToInsert, ...children]) - ..attributes['class'] = 'task-list-item'); - } else { - itemNodes.add(Element('li', children)); - } + + final itemParser = BlockParser(item.lines, parser.document); + final children = itemParser.parseLines(); + final itemElement = checkboxToInsert == null + ? Element('li', children) + : (Element('li', [checkboxToInsert, ...children]) + ..attributes['class'] = 'task-list-item'); + + itemNodes.add(itemElement); anyEmptyLinesBetweenBlocks = anyEmptyLinesBetweenBlocks || itemParser.encounteredBlankLine; } @@ -242,21 +238,15 @@ } } - if (listTag == 'ol_with_checkbox') { - final olWithCheckbox = Element('ol', itemNodes) - ..attributes['class'] = 'contains-task-list'; - if (startNumber != 1) { - olWithCheckbox.attributes['start'] = '$startNumber'; - } - return olWithCheckbox; - } else if (listTag == 'ul_with_checkbox') { - return Element('ul', itemNodes) - ..attributes['class'] = 'contains-task-list'; - } else if (listTag == 'ol' && startNumber != 1) { - return Element(listTag, itemNodes)..attributes['start'] = '$startNumber'; - } else { - return Element(listTag, itemNodes); + final listElement = Element(listTag, itemNodes); + if (listTag == 'ol' && startNumber != 1) { + listElement.attributes['start'] = '$startNumber'; } + + if (containsTaskList) { + listElement.attributes['class'] = 'contains-task-list'; + } + return listElement; } void _removeLeadingEmptyLine(ListItem item) {
diff --git a/lib/src/block_syntaxes/ordered_list_with_checkbox_syntax.dart b/lib/src/block_syntaxes/ordered_list_with_checkbox_syntax.dart index 2f8a66a..8d865e8 100644 --- a/lib/src/block_syntaxes/ordered_list_with_checkbox_syntax.dart +++ b/lib/src/block_syntaxes/ordered_list_with_checkbox_syntax.dart
@@ -2,16 +2,9 @@ // 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 '../patterns.dart'; -import 'list_syntax.dart'; +import 'ordered_list_syntax.dart'; -/// Parses ordered lists. -class OrderedListWithCheckBoxSyntax extends ListSyntax { - @override - RegExp get pattern => olWithCheckBoxPattern; - - @override - String get listTag => 'ol_with_checkbox'; - - const OrderedListWithCheckBoxSyntax(); +/// Parses ordered lists with checkboxes. +class OrderedListWithCheckboxSyntax extends OrderedListSyntax { + const OrderedListWithCheckboxSyntax(); }
diff --git a/lib/src/block_syntaxes/unordered_list_syntax.dart b/lib/src/block_syntaxes/unordered_list_syntax.dart index 6b1ae10..66bdf0f 100644 --- a/lib/src/block_syntaxes/unordered_list_syntax.dart +++ b/lib/src/block_syntaxes/unordered_list_syntax.dart
@@ -2,6 +2,7 @@ // 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 '../block_parser.dart'; import '../patterns.dart'; import 'list_syntax.dart'; @@ -11,6 +12,21 @@ RegExp get pattern => ulPattern; @override + bool canParse(BlockParser parser) { + // Check if it matches `hrPattern`, otherwise it will produce an infinite + // loop if put `UnorderedListSyntax` or `UnorderedListWithCheckboxSyntax` + // bofore `HorizontalRuleSyntax` and parse: + // ``` + // * * * + // ``` + if (hrPattern.hasMatch(parser.current)) { + return false; + } + + return pattern.hasMatch(parser.current); + } + + @override String get listTag => 'ul'; const UnorderedListSyntax();
diff --git a/lib/src/block_syntaxes/unordered_list_with_checkbox_syntax.dart b/lib/src/block_syntaxes/unordered_list_with_checkbox_syntax.dart index 795d8a7..5f3ddf0 100644 --- a/lib/src/block_syntaxes/unordered_list_with_checkbox_syntax.dart +++ b/lib/src/block_syntaxes/unordered_list_with_checkbox_syntax.dart
@@ -2,16 +2,9 @@ // 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 '../patterns.dart'; -import 'list_syntax.dart'; +import 'unordered_list_syntax.dart'; -/// Parses unordered lists. -class UnorderedListWithCheckBoxSyntax extends ListSyntax { - @override - RegExp get pattern => ulWithCheckBoxPattern; - - @override - String get listTag => 'ul_with_checkbox'; - - const UnorderedListWithCheckBoxSyntax(); +/// Parses unordered lists with checkboxes. +class UnorderedListWithCheckboxSyntax extends UnorderedListSyntax { + const UnorderedListWithCheckboxSyntax(); }
diff --git a/lib/src/extension_set.dart b/lib/src/extension_set.dart index 7f2be48..bbbd8ef 100644 --- a/lib/src/extension_set.dart +++ b/lib/src/extension_set.dart
@@ -60,8 +60,8 @@ const HeaderWithIdSyntax(), const SetextHeaderWithIdSyntax(), const TableSyntax(), - const UnorderedListWithCheckBoxSyntax(), - const OrderedListWithCheckBoxSyntax(), + const UnorderedListWithCheckboxSyntax(), + const OrderedListWithCheckboxSyntax(), ], ), List<InlineSyntax>.unmodifiable( @@ -82,8 +82,8 @@ <BlockSyntax>[ const FencedCodeBlockSyntax(), const TableSyntax(), - const UnorderedListWithCheckBoxSyntax(), - const OrderedListWithCheckBoxSyntax(), + const UnorderedListWithCheckboxSyntax(), + const OrderedListWithCheckboxSyntax(), ], ), List<InlineSyntax>.unmodifiable(
diff --git a/lib/src/html_renderer.dart b/lib/src/html_renderer.dart index f317707..a6abfcd 100644 --- a/lib/src/html_renderer.dart +++ b/lib/src/html_renderer.dart
@@ -103,13 +103,13 @@ @override void visitText(Text text) { - var content = text.text; + var content = text.textContent; if (const ['br', 'p', 'li'].contains(_lastVisitedTag)) { final lines = LineSplitter.split(content); content = content.contains('<pre>') ? lines.join('\n') : lines.map((line) => line.trimLeft()).join('\n'); - if (text.text.endsWith('\n')) { + if (text.textContent.endsWith('\n')) { content = '$content\n'; } }
diff --git a/lib/src/inline_syntaxes/code_syntax.dart b/lib/src/inline_syntaxes/code_syntax.dart index efc415d..afd6371 100644 --- a/lib/src/inline_syntaxes/code_syntax.dart +++ b/lib/src/inline_syntaxes/code_syntax.dart
@@ -20,7 +20,7 @@ // // This conforms to the delimiters of inline code, both in Markdown.pl, and // CommonMark. - static final String _pattern = r'(`+(?!`))((?:.|\n)*?[^`])\1(?!`)'; + static const _pattern = r'(`+(?!`))((?:.|\n)*?[^`])\1(?!`)'; CodeSyntax() : super(_pattern);
diff --git a/lib/src/inline_syntaxes/color_swatch_syntax.dart b/lib/src/inline_syntaxes/color_swatch_syntax.dart index 0998d74..e44fdbf 100644 --- a/lib/src/inline_syntaxes/color_swatch_syntax.dart +++ b/lib/src/inline_syntaxes/color_swatch_syntax.dart
@@ -35,7 +35,7 @@ /// * `hsl(540,70%,50%)` /// * `HSLA(540,70%,50%,0.3)` /// * `Hsla(540,70%,50%,0.3)` - static final String _pattern = + static const _pattern = '`((#([A-Fa-f0-9]{3}|[A-Fa-f0-9]{4}|[A-Fa-f0-9]{6}|[A-Fa-f0-9]{8}))|' r'([Rr][Gg][Bb][Aa]?\((\d+[%]?),(\d+[%]?),(\d+[%]?),?(\d+\.?\d+[%]?)?\))|' r'([Hh][Ss][Ll][Aa]?\((\d+[%]?),(\d+[%]?),(\d+[%]?),?(\d+\.?\d+[%]?)?\)))`';
diff --git a/lib/src/inline_syntaxes/delimiter_syntax.dart b/lib/src/inline_syntaxes/delimiter_syntax.dart index 7b48ce2..4c71b17 100644 --- a/lib/src/inline_syntaxes/delimiter_syntax.dart +++ b/lib/src/inline_syntaxes/delimiter_syntax.dart
@@ -217,7 +217,7 @@ ']'); // TODO(srawlins): Unicode whitespace - static final String whitespace = ' \t\r\n'; + static const whitespace = ' \t\r\n'; @override Text node;
diff --git a/lib/src/inline_syntaxes/email_autolink_syntax.dart b/lib/src/inline_syntaxes/email_autolink_syntax.dart index f5b5962..e7b90ff 100644 --- a/lib/src/inline_syntaxes/email_autolink_syntax.dart +++ b/lib/src/inline_syntaxes/email_autolink_syntax.dart
@@ -12,7 +12,7 @@ /// /// See <http://spec.commonmark.org/0.28/#email-address>. class EmailAutolinkSyntax extends InlineSyntax { - static final _email = + static const _email = r'''[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}''' r'''[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*''';
diff --git a/lib/src/patterns.dart b/lib/src/patterns.dart index f2aa7d8..5602630 100644 --- a/lib/src/patterns.dart +++ b/lib/src/patterns.dart
@@ -31,32 +31,91 @@ /// SETEXT should win. final hrPattern = RegExp(r'^ {0,3}([-*_])[ \t]*\1[ \t]*\1(?:\1|[ \t])*$'); -/// 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. +// 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: `-`, `*`, `+`. +/// +/// May have up to three leading spaces before the marker and any number of +/// spaces or tabs after. /// /// 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(r'^([ ]{0,3})()([*+-])(([ \t])([ \t]*)(.*))?$'); +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 `[5,6,7]` are all -/// shifted 2 places to be `[7,8,9]` -final ulWithCheckBoxPattern = RegExp( - r'^([ ]{0,3})()([*+-])([ \t]{0,4})(\[[ xX]{1}\])(([ \t])([ \t]*)(.*))?$'); +/// 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(r'^([ ]{0,3})(\d{1,9})([\.)])(([ \t])([ \t]*)(.*))?$'); +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 `[5,6,7]` are all -/// shifted 2 places to be `[7,8,9]` -final olWithCheckBoxPattern = RegExp( - r'^([ ]{0,3})(\d{1,9})([\.)])([ \t]{0,4})(\[[ xX]{1}\])(([ \t])([ \t]*)(.*))?$'); +/// 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)?\$'); /// A line of hyphens separated by at least one pipe. final tablePattern = RegExp(
diff --git a/lib/src/version.dart b/lib/src/version.dart index 92964be..da01dc7 100644 --- a/lib/src/version.dart +++ b/lib/src/version.dart
@@ -1,2 +1,2 @@ // Generated code. Do not modify. -const packageVersion = '6.0.0-dev'; +const packageVersion = '6.0.1';
diff --git a/pubspec.yaml b/pubspec.yaml index 0dc576f..ab02324 100644 --- a/pubspec.yaml +++ b/pubspec.yaml
@@ -1,5 +1,5 @@ name: markdown -version: 6.0.0-dev +version: 6.0.1 description: A portable Markdown library written in Dart that can parse Markdown into HTML.
diff --git a/test/document_test.dart b/test/document_test.dart index 8a26900..42ca40f 100644 --- a/test/document_test.dart +++ b/test/document_test.dart
@@ -54,7 +54,7 @@ test('encodeHtml spaces are preserved in text', () { // Example to get a <p> tag rendered before a text node. - final contents = 'Sample\n\n<pre>\n A\n B\n</pre>'; + const contents = 'Sample\n\n<pre>\n A\n B\n</pre>'; final document = Document(encodeHtml: true); final lines = LineSplitter.split(contents).toList(); final nodes = BlockParser(lines, document).parseLines(); @@ -64,7 +64,7 @@ test('encode double quotes, greater than, and less than when escaped', () { - final contents = r'\>\"\< Hello'; + const contents = r'\>\"\< Hello'; final document = Document(encodeHtml: true); final nodes = document.parseInline(contents); expect(nodes, hasLength(1)); @@ -113,7 +113,7 @@ }); test('leave double quotes, greater than, and less than when escaped', () { - final contents = r'\>\"\< Hello'; + const contents = r'\>\"\< Hello'; final document = Document(encodeHtml: false); final nodes = document.parseInline(contents); expect(nodes, hasLength(1));
diff --git a/test/extensions/ordered_list_with_checkboxes.unit b/test/extensions/ordered_list_with_checkboxes.unit new file mode 100644 index 0000000..84cf86e --- /dev/null +++ b/test/extensions/ordered_list_with_checkboxes.unit
@@ -0,0 +1,65 @@ +>>> checkbox with space +1. [ ] one +2. [ ] two +<<< +<ol class="contains-task-list"> +<li class="task-list-item"><input type="checkbox"></input>one</li> +<li class="task-list-item"><input type="checkbox"></input>two</li> +</ol> +>>> empty checkbox +1. [] one +2. [] two +<<< +<ol> +<li>[] one</li> +<li>[] two</li> +</ol> +>>> checkbox with x +1. [x] one +2. [x] two +<<< +<ol class="contains-task-list"> +<li class="task-list-item"><input type="checkbox" checked="true"></input>one</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>two</li> +</ol> +>>> checkbox with X +1. [X] one +2. [X] two +<<< +<ol class="contains-task-list"> +<li class="task-list-item"><input type="checkbox" checked="true"></input>one</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>two</li> +</ol> +>>> mixed checkboxes +1. [ ] one +2. [] two +3. [x] three +4. [X] four +5. five +<<< +<ol class="contains-task-list"> +<li class="task-list-item"><input type="checkbox"></input>one</li> +<li>[] two</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>three</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>four</li> +<li>five</li> +</ol> +>>> mixed leading spaces +1.[ ] zero +2. [ ] one +3. [ ] two +4. [ ] three +5. [ ] four +6. [ ] five +<<< +<p>1.[ ] zero</p> +<ol start="2" class="contains-task-list"> +<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 +</code></pre> +</li> +</ol> \ No newline at end of file
diff --git a/test/extensions/unordered_list_with_checkboxes.unit b/test/extensions/unordered_list_with_checkboxes.unit new file mode 100644 index 0000000..170b6de --- /dev/null +++ b/test/extensions/unordered_list_with_checkboxes.unit
@@ -0,0 +1,65 @@ +>>> checkbox with space +* [ ] one +* [ ] two +<<< +<ul class="contains-task-list"> +<li class="task-list-item"><input type="checkbox"></input>one</li> +<li class="task-list-item"><input type="checkbox"></input>two</li> +</ul> +>>> empty checkbox +* [] one +* [] two +<<< +<ul> +<li>[] one</li> +<li>[] two</li> +</ul> +>>> checkbox with x +* [x] one +* [x] two +<<< +<ul class="contains-task-list"> +<li class="task-list-item"><input type="checkbox" checked="true"></input>one</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>two</li> +</ul> +>>> checkbox with X +* [X] one +* [X] two +<<< +<ul class="contains-task-list"> +<li class="task-list-item"><input type="checkbox" checked="true"></input>one</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>two</li> +</ul> +>>> mixed checkboxes +* [ ] one +* [] two +* [x] three +* [X] four +* five +<<< +<ul class="contains-task-list"> +<li class="task-list-item"><input type="checkbox"></input>one</li> +<li>[] two</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>three</li> +<li class="task-list-item"><input type="checkbox" checked="true"></input>four</li> +<li>five</li> +</ul> +>>> mixed leading spaces +*[ ] zero +* [ ] one +* [ ] two +* [ ] three +* [ ] four +* [ ] five +<<< +<p>*[ ] zero</p> +<ul class="contains-task-list"> +<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 +</code></pre> +</li> +</ul> \ No newline at end of file
diff --git a/test/markdown_test.dart b/test/markdown_test.dart index a9f6fde..0b05c73 100644 --- a/test/markdown_test.dart +++ b/test/markdown_test.dart
@@ -10,7 +10,11 @@ void main() async { await testDirectory('original'); - // Block syntax extensions + // Block syntax extensions. + testFile( + 'extensions/fenced_blockquotes.unit', + blockSyntaxes: [const FencedBlockquoteSyntax()], + ); testFile( 'extensions/fenced_code_blocks.unit', blockSyntaxes: [const FencedCodeBlockSyntax()], @@ -20,6 +24,10 @@ blockSyntaxes: [const HeaderWithIdSyntax()], ); testFile( + 'extensions/ordered_list_with_checkboxes.unit', + blockSyntaxes: [const OrderedListWithCheckboxSyntax()], + ); + testFile( 'extensions/setext_headers_with_ids.unit', blockSyntaxes: [const SetextHeaderWithIdSyntax()], ); @@ -28,8 +36,8 @@ blockSyntaxes: [const TableSyntax()], ); testFile( - 'extensions/fenced_blockquotes.unit', - blockSyntaxes: [const FencedBlockquoteSyntax()], + 'extensions/unordered_list_with_checkboxes.unit', + blockSyntaxes: [const UnorderedListWithCheckboxSyntax()], ); // Inline syntax extensions
diff --git a/tool/dartdoc_compare.dart b/tool/dartdoc_compare.dart index e0195ba..7186d79 100644 --- a/tool/dartdoc_compare.dart +++ b/tool/dartdoc_compare.dart
@@ -121,7 +121,7 @@ } final out = Directory.systemTemp .createTempSync('dartdoc-compare-${markdownRef}__'); - final cmd = 'dart'; + const cmd = 'dart'; final args = [dartdocBin, '--output=${out.path}']; if (sdk) {
diff --git a/tool/stats.dart b/tool/stats.dart index a212e6c..d09b0df 100644 --- a/tool/stats.dart +++ b/tool/stats.dart
@@ -220,7 +220,7 @@ sink = stdout; } - final encoder = const JsonEncoder.withIndent(' ', _convert); + const encoder = JsonEncoder.withIndent(' ', _convert); try { sink.writeln(encoder.convert(scores)); } on JsonUnsupportedObjectError catch (e) {
diff --git a/tool/update_blns.dart b/tool/update_blns.dart index b48b8a7..1f50e36 100644 --- a/tool/update_blns.dart +++ b/tool/update_blns.dart
@@ -2,9 +2,9 @@ import 'dart:convert'; import 'dart:io'; -final _blnsJsonRawUrl = +const _blnsJsonRawUrl = 'https://github.com/minimaxir/big-list-of-naughty-strings/raw/master/blns.json'; -final _blnsFilePath = 'test/blns.dart'; +const _blnsFilePath = 'test/blns.dart'; Future<void> main() async { final client = HttpClient();
diff --git a/tool/update_emojis.dart b/tool/update_emojis.dart index 6b11e7a..396be33 100644 --- a/tool/update_emojis.dart +++ b/tool/update_emojis.dart
@@ -9,9 +9,9 @@ // update_github_emojis.dart now generates the emoji list using the GitHub API // to retrieve the emoji list. It uses this emoji source as a source to keep // binary compatibility with the Unicode sequences for each emoji found here. -final _emojisJsonRawUrl = +const _emojisJsonRawUrl = 'https://raw.githubusercontent.com/muan/emojilib/v2.4.0/emojis.json'; -final _emojisFilePath = 'lib/src/legacy_emojis.dart'; +const _emojisFilePath = 'lib/src/legacy_emojis.dart'; Future<void> main() async { final client = HttpClient();
diff --git a/tool/update_github_emojis.dart b/tool/update_github_emojis.dart index 5431d31..5fbf60c 100644 --- a/tool/update_github_emojis.dart +++ b/tool/update_github_emojis.dart
@@ -26,11 +26,11 @@ /// The 'cricket' emoji changes from `🏏` to `🦗`, legacy available as 'cricket_game'. /// (if the -g flag us used to force using the GitHub Unicode sequences for the /// emoji then additionally the 'email' emoji changes from '✉️' to '📧'). -final _emojisJsonRawUrl = 'https://api.github.com/emojis'; -final _emojisFilePath = 'lib/src/emojis.dart'; +const _emojisJsonRawUrl = 'https://api.github.com/emojis'; +const _emojisFilePath = 'lib/src/emojis.dart'; /// Reference to emoji map within legacy_emojis.dart -final legacyEmojis = legacy.emojis; +const legacyEmojis = legacy.emojis; /// AUTO GENERATED by [reconcile_emojis.dart] - this only needed to be done ONCE /// during the reconciliation process with the legacy emoji.