Allow resolving links that contain inline syntax.

Fixes dart-lang/markdown#42.

R=sethladd@google.com

Review URL: https://codereview.chromium.org//1274753003 .
diff --git a/pkgs/markdown/.gitignore b/pkgs/markdown/.gitignore
index aac1f4f..282c7f8 100644
--- a/pkgs/markdown/.gitignore
+++ b/pkgs/markdown/.gitignore
@@ -1,3 +1,4 @@
+.idea
 .packages
 .pub
 packages
diff --git a/pkgs/markdown/CHANGELOG.md b/pkgs/markdown/CHANGELOG.md
index a4e3606..5432376 100644
--- a/pkgs/markdown/CHANGELOG.md
+++ b/pkgs/markdown/CHANGELOG.md
@@ -1,3 +1,7 @@
+## 0.7.2
+
+* Allow resolving links that contain inline syntax (#42).
+
 ## 0.7.1+3
 
 * Updated homepage.
diff --git a/pkgs/markdown/lib/src/html_renderer.dart b/pkgs/markdown/lib/src/html_renderer.dart
index b2d96e7..835c178 100644
--- a/pkgs/markdown/lib/src/html_renderer.dart
+++ b/pkgs/markdown/lib/src/html_renderer.dart
@@ -60,8 +60,6 @@
     buffer.write('<${element.tag}');
 
     // Sort the keys so that we generate stable output.
-    // TODO(rnystrom): This assumes keys returns a fresh mutable
-    // collection.
     final attributeNames = element.attributes.keys.toList();
     attributeNames.sort((a, b) => a.compareTo(b));
     for (final name in attributeNames) {
diff --git a/pkgs/markdown/lib/src/inline_parser.dart b/pkgs/markdown/lib/src/inline_parser.dart
index 7638cdd..c9cf98a 100644
--- a/pkgs/markdown/lib/src/inline_parser.dart
+++ b/pkgs/markdown/lib/src/inline_parser.dart
@@ -280,13 +280,10 @@
     if (isNullOrEmpty(match[1])) {
       if (linkResolver == null) return null;
 
-      // Only allow implicit links if the content is just text.
-      // TODO(rnystrom): Do we want to relax this?
-      if (state.children.any((child) => child is! Text)) return null;
-      // If there are multiple children, but they are all text, send the
-      // combined text to linkResolver.
-      var textToResolve =
-          state.children.fold('', (oldVal, child) => oldVal + child.text);
+      // Treat the contents as unparsed text even if they happen to match. This
+      // way, we can handle things like [LINK_WITH_UNDERSCORES] as a link and
+      // not get confused by the emphasis.
+      var textToResolve = parser.source.substring(state.endPos, parser.pos);
 
       // See if we have a resolver that will generate a link for us.
       resolved = true;
diff --git a/pkgs/markdown/pubspec.yaml b/pkgs/markdown/pubspec.yaml
index 32c7931..9a86724 100644
--- a/pkgs/markdown/pubspec.yaml
+++ b/pkgs/markdown/pubspec.yaml
@@ -1,5 +1,5 @@
 name: markdown
-version: 0.7.1+3
+version: 0.7.2-dev
 author: Dart Team <misc@dartlang.org>
 description: A library for converting markdown to HTML.
 homepage: https://github.com/dart-lang/markdown
diff --git a/pkgs/markdown/test/markdown_test.dart b/pkgs/markdown/test/markdown_test.dart
index 8368b84..e2d1b40 100644
--- a/pkgs/markdown/test/markdown_test.dart
+++ b/pkgs/markdown/test/markdown_test.dart
@@ -3,11 +3,14 @@
 // BSD-style license that can be found in the LICENSE file.
 
 /// Unit tests for markdown.
-library markdownTests;
+library markdown.test.markdown_test;
 
 import 'package:unittest/unittest.dart';
+
 import 'package:markdown/markdown.dart';
 
+import 'utils.dart';
+
 /// Most of these tests are based on observing how showdown behaves:
 /// http://softwaremaniacs.org/playground/showdown-highlight/
 void main() {
@@ -949,7 +952,8 @@
   });
 
   group('Resolver', () {
-    var nyanResolver = (text) => new Text('~=[,,_${text}_,,]:3');
+    nyanResolver(text) => new Text('~=[,,_${text}_,,]:3');
+
     validate('simple link resolver', '''
         resolve [this] thing
         ''', '''
@@ -960,12 +964,17 @@
         ''', '''
         <p>resolve ~=[,,_this_,,]:3 thing</p>
         ''', imageLinkResolver: nyanResolver);
+
+    validate('can resolve link containing inline tags', '''
+        resolve [*star* _underline_] thing
+        ''', '''
+        <p>resolve ~=[,,_*star* _underline__,,]:3 thing</p>
+        ''', linkResolver: nyanResolver);
   });
 
   group('Custom inline syntax', () {
-    List<InlineSyntax> nyanSyntax = [
-      new TextSyntax('nyan', sub: '~=[,,_,,]:3')
-    ];
+    var nyanSyntax = [new TextSyntax('nyan', sub: '~=[,,_,,]:3')];
+
     validate('simple inline syntax', '''
         nyan
         ''', '''
@@ -1022,90 +1031,3 @@
         ''', inlineOnly: true);
   });
 }
-
-/**
- * Removes eight spaces of leading indentation from a multiline string.
- *
- * Note that this is very sensitive to how the literals are styled. They should
- * be:
- *     '''
- *     Text starts on own line. Lines up with subsequent lines.
- *     Lines are indented exactly 8 characters from the left margin.'''
- *
- * This does nothing if text is only a single line.
- */
-// TODO(nweiz): Make this auto-detect the indentation level from the first
-// non-whitespace line.
-String cleanUpLiteral(String text) {
-  var lines = text.split('\n');
-  if (lines.length <= 1) return text;
-
-  for (var j = 0; j < lines.length; j++) {
-    if (lines[j].length > 8) {
-      lines[j] = lines[j].substring(8, lines[j].length);
-    } else {
-      lines[j] = '';
-    }
-  }
-
-  return lines.join('\n');
-}
-
-void validate(String description, String markdown, String html,
-    {List<InlineSyntax> inlineSyntaxes,
-    Resolver linkResolver, Resolver imageLinkResolver,
-    bool inlineOnly: false}) {
-  test(description, () {
-    markdown = cleanUpLiteral(markdown);
-    html = cleanUpLiteral(html);
-
-    var result = markdownToHtml(markdown,
-        inlineSyntaxes: inlineSyntaxes,
-        linkResolver: linkResolver,
-        imageLinkResolver: imageLinkResolver,
-        inlineOnly: inlineOnly);
-    var passed = compareOutput(html, result);
-
-    if (!passed) {
-      // Remove trailing newline.
-      html = html.substring(0, html.length - 1);
-
-      var sb = new StringBuffer();
-      sb.writeln('Expected: ${html.replaceAll("\n", "\n          ")}');
-      sb.writeln('  Actual: ${result.replaceAll("\n", "\n          ")}');
-
-      fail(sb.toString());
-    }
-  });
-}
-
-/// Does a loose comparison of the two strings of HTML. Ignores differences in
-/// newlines and indentation.
-bool compareOutput(String a, String b) {
-  int i = 0;
-  int j = 0;
-
-  skipIgnored(String s, int i) {
-    // Ignore newlines.
-    while ((i < s.length) && (s[i] == '\n')) {
-      i++;
-      // Ignore indentation.
-      while ((i < s.length) && (s[i] == ' ')) i++;
-    }
-
-    return i;
-  }
-
-  while (true) {
-    i = skipIgnored(a, i);
-    j = skipIgnored(b, j);
-
-    // If one string runs out of non-ignored strings, the other must too.
-    if (i == a.length) return j == b.length;
-    if (j == b.length) return i == a.length;
-
-    if (a[i] != b[j]) return false;
-    i++;
-    j++;
-  }
-}
diff --git a/pkgs/markdown/test/utils.dart b/pkgs/markdown/test/utils.dart
new file mode 100644
index 0000000..73ad7ef
--- /dev/null
+++ b/pkgs/markdown/test/utils.dart
@@ -0,0 +1,94 @@
+// Copyright (c) 2015, 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.
+
+library markdown.test.utils;
+
+import 'package:unittest/unittest.dart';
+
+import 'package:markdown/markdown.dart';
+
+/// Removes eight spaces of leading indentation from a multiline string.
+///
+/// Note that this is very sensitive to how the literals are styled. They should
+/// be:
+///     '''
+///     Text starts on own line. Lines up with subsequent lines.
+///     Lines are indented exactly 8 characters from the left margin.'''
+///
+/// This does nothing if text is only a single line.
+// TODO(nweiz): Make this auto-detect the indentation level from the first
+// non-whitespace line.
+String cleanUpLiteral(String text) {
+  var lines = text.split('\n');
+  if (lines.length <= 1) return text;
+
+  for (var j = 0; j < lines.length; j++) {
+    if (lines[j].length > 8) {
+      lines[j] = lines[j].substring(8, lines[j].length);
+    } else {
+      lines[j] = '';
+    }
+  }
+
+  return lines.join('\n');
+}
+
+void validate(String description, String markdown, String html,
+    {List<InlineSyntax> inlineSyntaxes,
+    Resolver linkResolver, Resolver imageLinkResolver,
+    bool inlineOnly: false}) {
+  test(description, () {
+    markdown = cleanUpLiteral(markdown);
+    html = cleanUpLiteral(html);
+
+    var result = markdownToHtml(markdown,
+        inlineSyntaxes: inlineSyntaxes,
+        linkResolver: linkResolver,
+        imageLinkResolver: imageLinkResolver,
+        inlineOnly: inlineOnly);
+    var passed = compareOutput(html, result);
+
+    if (!passed) {
+      // Remove trailing newline.
+      html = html.substring(0, html.length - 1);
+
+      var sb = new StringBuffer();
+      sb.writeln('Expected: ${html.replaceAll("\n", "\n          ")}');
+      sb.writeln('  Actual: ${result.replaceAll("\n", "\n          ")}');
+
+      fail(sb.toString());
+    }
+  });
+}
+
+/// Does a loose comparison of the two strings of HTML. Ignores differences in
+/// newlines and indentation.
+bool compareOutput(String a, String b) {
+  int i = 0;
+  int j = 0;
+
+  skipIgnored(String s, int i) {
+    // Ignore newlines.
+    while ((i < s.length) && (s[i] == '\n')) {
+      i++;
+      // Ignore indentation.
+      while ((i < s.length) && (s[i] == ' ')) i++;
+    }
+
+    return i;
+  }
+
+  while (true) {
+    i = skipIgnored(a, i);
+    j = skipIgnored(b, j);
+
+    // If one string runs out of non-ignored strings, the other must too.
+    if (i == a.length) return j == b.length;
+    if (j == b.length) return i == a.length;
+
+    if (a[i] != b[j]) return false;
+    i++;
+    j++;
+  }
+}