Add options to disable default syntaxes (dart-lang/markdown#404)
diff --git a/pkgs/markdown/lib/src/block_parser.dart b/pkgs/markdown/lib/src/block_parser.dart index 256ff1e..4b11b3d 100644 --- a/pkgs/markdown/lib/src/block_parser.dart +++ b/pkgs/markdown/lib/src/block_parser.dart
@@ -104,7 +104,12 @@ BlockParser(this.lines, this.document) { blockSyntaxes.addAll(document.blockSyntaxes); - blockSyntaxes.addAll(standardBlockSyntaxes); + + if (document.withDefaultBlockSyntaxes) { + blockSyntaxes.addAll(standardBlockSyntaxes); + } else { + blockSyntaxes.add(const DummyBlockSyntax()); + } } /// Gets the current line. @@ -1273,3 +1278,31 @@ return true; } } + +/// Walks the parser forward through the lines does not match any [BlockSyntax]. +/// +/// Returns a [UnparsedContent] with the unmatched lines as `textContent`. +class DummyBlockSyntax extends BlockSyntax { + const DummyBlockSyntax(); + + @override + RegExp get pattern => _dummyPattern; + + @override + bool canEndBlock(BlockParser parser) => false; + + @override + bool canParse(BlockParser parser) => true; + + @override + Node parse(BlockParser parser) { + final childLines = <String>[]; + + while (!BlockSyntax.isAtBlockEnd(parser)) { + childLines.add(parser.current); + parser.advance(); + } + + return UnparsedContent(childLines.join('\n')); + } +}
diff --git a/pkgs/markdown/lib/src/document.dart b/pkgs/markdown/lib/src/document.dart index cdaca2d..374f955 100644 --- a/pkgs/markdown/lib/src/document.dart +++ b/pkgs/markdown/lib/src/document.dart
@@ -10,12 +10,22 @@ /// Maintains the context needed to parse a Markdown document. class Document { final Map<String, LinkReference> linkReferences = <String, LinkReference>{}; - final ExtensionSet extensionSet; final Resolver? linkResolver; final Resolver? imageLinkResolver; final bool encodeHtml; + + /// Whether to use default block syntaxes. + final bool withDefaultBlockSyntaxes; + + /// Whether to use default inline syntaxes. + /// + /// Need to set both [withDefaultInlineSyntaxes] and [encodeHtml] to + /// `false` to disable all inline syntaxes including html encoding syntaxes. + final bool withDefaultInlineSyntaxes; + final _blockSyntaxes = <BlockSyntax>{}; final _inlineSyntaxes = <InlineSyntax>{}; + final bool hasCustomInlineSyntaxes; Iterable<BlockSyntax> get blockSyntaxes => _blockSyntaxes; @@ -28,13 +38,25 @@ this.linkResolver, this.imageLinkResolver, this.encodeHtml = true, - }) : extensionSet = extensionSet ?? ExtensionSet.commonMark { - _blockSyntaxes - ..addAll(blockSyntaxes ?? []) - ..addAll(this.extensionSet.blockSyntaxes); - _inlineSyntaxes - ..addAll(inlineSyntaxes ?? []) - ..addAll(this.extensionSet.inlineSyntaxes); + this.withDefaultBlockSyntaxes = true, + this.withDefaultInlineSyntaxes = true, + }) : hasCustomInlineSyntaxes = (inlineSyntaxes?.isNotEmpty ?? false) || + (extensionSet?.inlineSyntaxes.isNotEmpty ?? false) { + _blockSyntaxes.addAll(blockSyntaxes ?? []); + _inlineSyntaxes.addAll(inlineSyntaxes ?? []); + + if (extensionSet == null) { + if (withDefaultBlockSyntaxes) { + _blockSyntaxes.addAll(ExtensionSet.commonMark.blockSyntaxes); + } + + if (withDefaultInlineSyntaxes) { + _inlineSyntaxes.addAll(ExtensionSet.commonMark.inlineSyntaxes); + } + } else { + _blockSyntaxes.addAll(extensionSet.blockSyntaxes); + _inlineSyntaxes.addAll(extensionSet.inlineSyntaxes); + } } /// Parses the given [lines] of Markdown to a series of AST nodes.
diff --git a/pkgs/markdown/lib/src/html_renderer.dart b/pkgs/markdown/lib/src/html_renderer.dart index 761442d..728d35f 100644 --- a/pkgs/markdown/lib/src/html_renderer.dart +++ b/pkgs/markdown/lib/src/html_renderer.dart
@@ -19,6 +19,9 @@ Resolver? linkResolver, Resolver? imageLinkResolver, bool inlineOnly = false, + bool encodeHtml = true, + bool withDefaultBlockSyntaxes = true, + bool withDefaultInlineSyntaxes = true, }) { var document = Document( blockSyntaxes: blockSyntaxes, @@ -26,6 +29,9 @@ extensionSet: extensionSet, linkResolver: linkResolver, imageLinkResolver: imageLinkResolver, + encodeHtml: encodeHtml, + withDefaultBlockSyntaxes: withDefaultBlockSyntaxes, + withDefaultInlineSyntaxes: withDefaultInlineSyntaxes, ); if (inlineOnly) return renderToHtml(document.parseInline(markdown));
diff --git a/pkgs/markdown/lib/src/inline_parser.dart b/pkgs/markdown/lib/src/inline_parser.dart index d14e8fe..bdf6612 100644 --- a/pkgs/markdown/lib/src/inline_parser.dart +++ b/pkgs/markdown/lib/src/inline_parser.dart
@@ -17,7 +17,6 @@ EmailAutolinkSyntax(), AutolinkSyntax(), LineBreakSyntax(), - ImageSyntax(), // Allow any punctuation to be escaped. EscapeSyntax(), // "*" surrounded by spaces is left alone. @@ -70,28 +69,27 @@ // User specified syntaxes are the first syntaxes to be evaluated. syntaxes.addAll(document.inlineSyntaxes); - var hasCustomInlineSyntaxes = document.inlineSyntaxes - .any((s) => !document.extensionSet.inlineSyntaxes.contains(s)); - // This first RegExp matches plain text to accelerate parsing. It's written // so that it does not match any prefix of any following syntaxes. Most // Markdown is plain text, so it's faster to match one RegExp per 'word' // rather than fail to match all the following RegExps at each non-syntax // character position. - if (hasCustomInlineSyntaxes) { + if (document.hasCustomInlineSyntaxes) { // We should be less aggressive in blowing past "words". syntaxes.add(TextSyntax(r'[A-Za-z0-9]+(?=\s)')); } else { syntaxes.add(TextSyntax(r'[ \tA-Za-z0-9]*[A-Za-z0-9](?=\s)')); } - // Custom link resolvers go after the generic text syntax. - syntaxes.addAll([ - LinkSyntax(linkResolver: document.linkResolver), - ImageSyntax(linkResolver: document.imageLinkResolver) - ]); + if (document.withDefaultInlineSyntaxes) { + // Custom link resolvers go after the generic text syntax. + syntaxes.addAll([ + LinkSyntax(linkResolver: document.linkResolver), + ImageSyntax(linkResolver: document.imageLinkResolver) + ]); - syntaxes.addAll(_defaultSyntaxes); + syntaxes.addAll(_defaultSyntaxes); + } if (_encodeHtml) { syntaxes.addAll(_htmlSyntaxes); @@ -147,7 +145,7 @@ return; } var syntax = delimiter.syntax; - if (syntax is LinkSyntax) { + if (syntax is LinkSyntax && syntaxes.any(((e) => e is LinkSyntax))) { var nodeIndex = _tree.lastIndexWhere((n) => n == delimiter.node); var linkNode = syntax.close(this, delimiter, null, getChildren: () { _processEmphasis(index);
diff --git a/pkgs/markdown/test/html_renderer_test.dart b/pkgs/markdown/test/html_renderer_test.dart new file mode 100644 index 0000000..d478fc7 --- /dev/null +++ b/pkgs/markdown/test/html_renderer_test.dart
@@ -0,0 +1,80 @@ +// 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 'package:markdown/markdown.dart'; +import 'package:test/test.dart'; + +void main() { + group('markdownToHtml', () { + const text = '# Hello **Markdown<em>!</em>**\n***'; + + test('with no syntaxes', () { + final result = markdownToHtml( + text, + withDefaultBlockSyntaxes: false, + withDefaultInlineSyntaxes: false, + encodeHtml: false, + ); + expect(result, equals('# Hello **Markdown<em>!</em>**\n***\n')); + }); + + test('with no default syntaxes but with custom syntaxes', () { + final result = markdownToHtml( + text, + withDefaultBlockSyntaxes: false, + withDefaultInlineSyntaxes: false, + encodeHtml: false, + blockSyntaxes: [const HorizontalRuleSyntax()], + inlineSyntaxes: [TagSyntax(r'\*+', requiresDelimiterRun: true)], + ); + + expect( + result, + equals('# Hello <strong>Markdown<em>!</em></strong>\n<hr />\n'), + ); + }); + + test('with only default block syntaxes', () { + final result = markdownToHtml( + text, + withDefaultBlockSyntaxes: true, + withDefaultInlineSyntaxes: false, + encodeHtml: false, + ); + + expect( + result, + equals('<h1>Hello **Markdown<em>!</em>**</h1>\n<hr />\n'), + ); + }); + + test('with only default inline syntaxes', () { + final result = markdownToHtml( + text, + withDefaultBlockSyntaxes: false, + withDefaultInlineSyntaxes: true, + encodeHtml: false, + ); + + expect( + result, + equals('# Hello <strong>Markdown<em>!</em></strong>\n***\n'), + ); + }); + + test('with no default syntaxes but with encodeHtml enabled', () { + final result = markdownToHtml( + text, + withDefaultBlockSyntaxes: false, + withDefaultInlineSyntaxes: false, + encodeHtml: true, + ); + + expect( + result, + equals('# Hello **Markdown<em>!</em>**\n***\n'), + ); + }); + }); +}