Conditionally escape HTML in links; fixes #272

diff --git a/lib/src/ast.dart b/lib/src/ast.dart
index 9765b24..dd672c1 100644
--- a/lib/src/ast.dart
+++ b/lib/src/ast.dart
@@ -52,14 +52,15 @@
     }
   }
 
-  String get textContent => children == null
-      ? ''
-      : children.map((Node child) => child.textContent).join('');
+  String get textContent {
+    return (children ?? []).map((Node child) => child.textContent).join('');
+  }
 }
 
 /// A plain text element.
 class Text implements Node {
   final String text;
+
   Text(this.text);
 
   void accept(NodeVisitor visitor) => visitor.visitText(this);
@@ -75,6 +76,7 @@
 /// definitions.
 class UnparsedContent implements Node {
   final String textContent;
+
   UnparsedContent(this.textContent);
 
   void accept(NodeVisitor visitor) => null;
diff --git a/lib/src/block_parser.dart b/lib/src/block_parser.dart
index eb9980b..56faaab 100644
--- a/lib/src/block_parser.dart
+++ b/lib/src/block_parser.dart
@@ -397,9 +397,10 @@
     // The Markdown tests expect a trailing newline.
     childLines.add('');
 
-    var content = parser.document.encodeHtml
-        ? escapeHtml(childLines.join('\n'))
-        : childLines.join('\n');
+    var content = childLines.join('\n');
+    if (parser.document.encodeHtml) {
+      content = escapeHtml(content);
+    }
 
     return Element('pre', [Element.text('code', content)]);
   }
@@ -516,7 +517,7 @@
       r'figcaption|figure|footer|form|frame|frameset|h1|head|header|hr|html|'
       r'iframe|legend|li|link|main|menu|menuitem|meta|nav|noframes|ol|optgroup|'
       r'option|p|param|section|source|summary|table|tbody|td|tfoot|th|thead|'
-      'title|tr|track|ul)'
+      r'title|tr|track|ul)'
       r'(?:\s|>|/>|$)');
 
   /// The [_pattern] regular expression above is very expensive, even on
@@ -792,6 +793,7 @@
 /// Parses unordered lists.
 class UnorderedListSyntax extends ListSyntax {
   RegExp get pattern => _ulPattern;
+
   String get listTag => 'ul';
 
   const UnorderedListSyntax();
@@ -800,6 +802,7 @@
 /// Parses ordered lists.
 class OrderedListSyntax extends ListSyntax {
   RegExp get pattern => _olPattern;
+
   String get listTag => 'ol';
 
   const OrderedListSyntax();
diff --git a/lib/src/document.dart b/lib/src/document.dart
index f42086b..5f510e0 100644
--- a/lib/src/document.dart
+++ b/lib/src/document.dart
@@ -18,16 +18,17 @@
   final _inlineSyntaxes = Set<InlineSyntax>();
 
   Iterable<BlockSyntax> get blockSyntaxes => _blockSyntaxes;
+
   Iterable<InlineSyntax> get inlineSyntaxes => _inlineSyntaxes;
 
-  Document(
-      {Iterable<BlockSyntax> blockSyntaxes,
-      Iterable<InlineSyntax> inlineSyntaxes,
-      ExtensionSet extensionSet,
-      this.linkResolver,
-      this.imageLinkResolver,
-      this.encodeHtml = true})
-      : this.extensionSet = extensionSet ?? ExtensionSet.commonMark {
+  Document({
+    Iterable<BlockSyntax> blockSyntaxes,
+    Iterable<InlineSyntax> inlineSyntaxes,
+    ExtensionSet extensionSet,
+    this.linkResolver,
+    this.imageLinkResolver,
+    this.encodeHtml = true,
+  }) : this.extensionSet = extensionSet ?? ExtensionSet.commonMark {
     this._blockSyntaxes
       ..addAll(blockSyntaxes ?? [])
       ..addAll(this.extensionSet.blockSyntaxes);
diff --git a/lib/src/extension_set.dart b/lib/src/extension_set.dart
index c9a4ea9..d740c64 100644
--- a/lib/src/extension_set.dart
+++ b/lib/src/extension_set.dart
@@ -20,8 +20,10 @@
   /// The [commonMark] extension set is close to compliance with [CommonMark].
   ///
   /// [CommonMark]: http://commonmark.org/
-  static final ExtensionSet commonMark =
-      ExtensionSet([const FencedCodeBlockSyntax()], [InlineHtmlSyntax()]);
+  static final ExtensionSet commonMark = ExtensionSet(
+    [const FencedCodeBlockSyntax()],
+    [InlineHtmlSyntax()],
+  );
 
   /// The [gitHubWeb] extension set renders Markdown similarly to GitHub.
   ///
diff --git a/lib/src/html_renderer.dart b/lib/src/html_renderer.dart
index 25bcd09..2ffc21d 100644
--- a/lib/src/html_renderer.dart
+++ b/lib/src/html_renderer.dart
@@ -12,19 +12,22 @@
 import 'inline_parser.dart';
 
 /// Converts the given string of Markdown to HTML.
-String markdownToHtml(String markdown,
-    {Iterable<BlockSyntax> blockSyntaxes,
-    Iterable<InlineSyntax> inlineSyntaxes,
-    ExtensionSet extensionSet,
-    Resolver linkResolver,
-    Resolver imageLinkResolver,
-    bool inlineOnly = false}) {
+String markdownToHtml(
+  String markdown, {
+  Iterable<BlockSyntax> blockSyntaxes,
+  Iterable<InlineSyntax> inlineSyntaxes,
+  ExtensionSet extensionSet,
+  Resolver linkResolver,
+  Resolver imageLinkResolver,
+  bool inlineOnly = false,
+}) {
   var document = Document(
-      blockSyntaxes: blockSyntaxes,
-      inlineSyntaxes: inlineSyntaxes,
-      extensionSet: extensionSet,
-      linkResolver: linkResolver,
-      imageLinkResolver: imageLinkResolver);
+    blockSyntaxes: blockSyntaxes,
+    inlineSyntaxes: inlineSyntaxes,
+    extensionSet: extensionSet,
+    linkResolver: linkResolver,
+    imageLinkResolver: imageLinkResolver,
+  );
 
   if (inlineOnly) return renderToHtml(document.parseInline(markdown));
 
@@ -78,7 +81,7 @@
     var content = text.text;
     if (const ['p', 'li'].contains(_lastVisitedTag)) {
       var lines = LineSplitter.split(content);
-      content = (content.contains('<pre>'))
+      content = content.contains('<pre>')
           ? lines.join('\n')
           : lines.map((line) => line.trimLeft()).join('\n');
       if (text.text.endsWith('\n')) {
diff --git a/lib/src/inline_parser.dart b/lib/src/inline_parser.dart
index ed4513f..e17a3ba 100644
--- a/lib/src/inline_parser.dart
+++ b/lib/src/inline_parser.dart
@@ -67,7 +67,7 @@
     // User specified syntaxes are the first syntaxes to be evaluated.
     syntaxes.addAll(document.inlineSyntaxes);
 
-    var documentHasCustomInlineSyntaxes = 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
@@ -75,7 +75,7 @@
     // 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 (documentHasCustomInlineSyntaxes) {
+    if (hasCustomInlineSyntaxes) {
       // We should be less aggressive in blowing past "words".
       syntaxes.add(TextSyntax(r'[A-Za-z0-9]+(?=\s)'));
     } else {
@@ -302,7 +302,8 @@
 
   bool onMatch(InlineParser parser, Match match) {
     var url = match[1];
-    var anchor = Element.text('a', escapeHtml(url));
+    var text = parser.document.encodeHtml ? escapeHtml(url) : url;
+    var anchor = Element.text('a', text);
     anchor.attributes['href'] = Uri.encodeFull('mailto:$url');
     parser.addNode(anchor);
 
@@ -316,7 +317,8 @@
 
   bool onMatch(InlineParser parser, Match match) {
     var url = match[1];
-    var anchor = Element.text('a', escapeHtml(url));
+    var text = parser.document.encodeHtml ? escapeHtml(url) : url;
+    var anchor = Element.text('a', text);
     anchor.attributes['href'] = Uri.encodeFull(url);
     parser.addNode(anchor);
 
@@ -331,17 +333,21 @@
   // Autolinks can only come at the beginning of a line, after whitespace, or
   // any of the delimiting characters *, _, ~, and (.
   static const start = r'(?:^|[\s*_~(>])';
+
   // An extended url autolink will be recognized when one of the schemes
   // http://, https://, or ftp://, followed by a valid domain
   static const scheme = r'(?:(?:https?|ftp):\/\/|www\.)';
+
   // A valid domain consists of alphanumeric characters, underscores (_),
   // hyphens (-) and periods (.). There must be at least one period, and no
   // underscores may be present in the last two segments of the domain.
   static const domainPart = r'\w\-';
   static const domain = '[$domainPart][$domainPart.]+';
+
   // A valid domain consists of alphanumeric characters, underscores (_),
   // hyphens (-) and periods (.).
   static const path = r'[^\s<]*';
+
   // Trailing punctuation (specifically, ?, !, ., ,, :, *, _, and ~) will not
   // be considered part of the autolink
   static const truncatingPunctuationPositive = r'[?!.,:*_~]';
@@ -427,7 +433,8 @@
       href = 'http://$href';
     }
 
-    final anchor = Element.text('a', escapeHtml(url));
+    final text = parser.document.encodeHtml ? escapeHtml(url) : url;
+    final anchor = Element.text('a', text);
     anchor.attributes['href'] = Uri.encodeFull(href);
     parser.addNode(anchor);
 
@@ -478,6 +485,7 @@
       r'\uFF05-\uFF0A\uFF0C-\uFF0F\uFF1A\uFF1B\uFF1F\uFF20\uFF3B-\uFF3D\uFF3F'
       r'\uFF5B\uFF5D\uFF5F-\uFF65'
       ']');
+
   // TODO(srawlins): Unicode whitespace
   static final String whitespace = ' \t\r\n';
 
@@ -488,13 +496,14 @@
   final bool isPrecededByPunctuation;
   final bool isFollowedByPunctuation;
 
-  _DelimiterRun._(
-      {this.char,
-      this.length,
-      this.isLeftFlanking,
-      this.isRightFlanking,
-      this.isPrecededByPunctuation,
-      this.isFollowedByPunctuation});
+  _DelimiterRun._({
+    this.char,
+    this.length,
+    this.isLeftFlanking,
+    this.isRightFlanking,
+    this.isPrecededByPunctuation,
+    this.isFollowedByPunctuation,
+  });
 
   static _DelimiterRun tryParse(InlineParser parser, int runStart, int runEnd) {
     bool leftFlanking,
@@ -542,12 +551,13 @@
     }
 
     return _DelimiterRun._(
-        char: parser.charAt(runStart),
-        length: runEnd - runStart + 1,
-        isLeftFlanking: leftFlanking,
-        isRightFlanking: rightFlanking,
-        isPrecededByPunctuation: precededByPunctuation,
-        isFollowedByPunctuation: followedByPunctuation);
+      char: parser.charAt(runStart),
+      length: runEnd - runStart + 1,
+      isLeftFlanking: leftFlanking,
+      isRightFlanking: rightFlanking,
+      isPrecededByPunctuation: precededByPunctuation,
+      isFollowedByPunctuation: followedByPunctuation,
+    );
   }
 
   String toString() =>
@@ -769,7 +779,10 @@
   ///
   /// [label] does not need to be normalized.
   Node _resolveReferenceLink(
-      String label, TagState state, Map<String, LinkReference> linkReferences) {
+    String label,
+    TagState state,
+    Map<String, LinkReference> linkReferences,
+  ) {
     var normalizedLabel = label.toLowerCase();
     var linkReference = linkReferences[normalizedLabel];
     if (linkReference != null) {
@@ -1096,7 +1109,7 @@
 
   Node _createNode(TagState state, String destination, String title) {
     var element = Element.empty('img');
-    element.attributes['src'] = escapeHtml(destination);
+    element.attributes['src'] = destination;
     element.attributes['alt'] = state?.textContent ?? '';
     if (title != null && title.isNotEmpty) {
       element.attributes['title'] =