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&lt;em&gt;!&lt;/em&gt;**\n***\n'),
+      );
+    });
+  });
+}