Recognize Unicode ellipsis as punctuation (#270)

Recognize Unicode ellipsis as punctuation
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 90a48f1..096d301 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,8 @@
 ## 2.1.2-dev
 
-* Dropping support for Dart 2.0.0 through 2.1.0.
+* Drop support for Dart 2.0.0 through 2.1.0.
+* Recognize Unicode ellipsis (…) and other Unicode punctuation as punctuation
+  when parsing potential emphasis.
 
 ## 2.1.1
 
diff --git a/lib/src/inline_parser.dart b/lib/src/inline_parser.dart
index 10b56f2..0b1a25e 100644
--- a/lib/src/inline_parser.dart
+++ b/lib/src/inline_parser.dart
@@ -422,7 +422,37 @@
 }
 
 class _DelimiterRun {
-  static final String punctuation = r'''!"#$%&'()*+,-./:;<=>?@[\]^_`{|}~''';
+  /// According to
+  /// [CommonMark](https://spec.commonmark.org/0.29/#punctuation-character):
+  ///
+  /// > A punctuation character is an ASCII punctuation character or anything in
+  /// > the general Unicode categories `Pc`, `Pd`, `Pe`, `Pf`, `Pi`, `Po`, or
+  /// > `Ps`.
+  // This RegExp is inspired by
+  // https://github.com/commonmark/commonmark.js/blob/1f7d09099c20d7861a674674a5a88733f55ff729/lib/inlines.js#L39.
+  // I don't know if there is any way to simplify it or maintain it.
+  static final RegExp punctuation = RegExp(r'['
+      r'''!"#$%&'()*+,\-./:;<=>?@\[\]\\^_`{|}~'''
+      r'\xA1\xA7\xAB\xB6\xB7\xBB\xBF\u037E\u0387\u055A-\u055F\u0589\u058A\u05BE'
+      r'\u05C0\u05C3\u05C6\u05F3\u05F4\u0609\u060A\u060C\u060D\u061B\u061E'
+      r'\u061F\u066A-\u066D\u06D4\u0700-\u070D\u07F7-\u07F9\u0830-\u083E\u085E'
+      r'\u0964\u0965\u0970\u0AF0\u0DF4\u0E4F\u0E5A\u0E5B\u0F04-\u0F12\u0F14'
+      r'\u0F3A-\u0F3D\u0F85\u0FD0-\u0FD4\u0FD9\u0FDA\u104A-\u104F\u10FB'
+      r'\u1360-\u1368\u1400\u166D\u166E\u169B\u169C\u16EB-\u16ED\u1735\u1736'
+      r'\u17D4-\u17D6\u17D8-\u17DA\u1800-\u180A\u1944\u1945\u1A1E\u1A1F'
+      r'\u1AA0-\u1AA6\u1AA8-\u1AAD\u1B5A-\u1B60\u1BFC-\u1BFF\u1C3B-\u1C3F\u1C7E'
+      r'\u1C7F\u1CC0-\u1CC7\u1CD3\u2010-\u2027\u2030-\u2043\u2045-\u2051'
+      r'\u2053-\u205E\u207D\u207E\u208D\u208E\u2308-\u230B\u2329\u232A'
+      r'\u2768-\u2775\u27C5\u27C6\u27E6-\u27EF\u2983-\u2998\u29D8-\u29DB\u29FC'
+      r'\u29FD\u2CF9-\u2CFC\u2CFE\u2CFF\u2D70\u2E00-\u2E2E\u2E30-\u2E42'
+      r'\u3001-\u3003\u3008-\u3011\u3014-\u301F\u3030\u303D\u30A0\u30FB\uA4FE'
+      r'\uA4FF\uA60D-\uA60F\uA673\uA67E\uA6F2-\uA6F7\uA874-\uA877\uA8CE\uA8CF'
+      r'\uA8F8-\uA8FA\uA8FC\uA92E\uA92F\uA95F\uA9C1-\uA9CD\uA9DE\uA9DF'
+      r'\uAA5C-\uAA5F\uAADE\uAADF\uAAF0\uAAF1\uABEB\uFD3E\uFD3F\uFE10-\uFE19'
+      r'\uFE30-\uFE52\uFE54-\uFE61\uFE63\uFE68\uFE6A\uFE6B\uFF01-\uFF03'
+      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';
 
@@ -453,7 +483,7 @@
     } else {
       preceding = parser.source.substring(runStart - 1, runStart);
     }
-    precededByPunctuation = punctuation.contains(preceding);
+    precededByPunctuation = punctuation.hasMatch(preceding);
 
     if (runEnd == parser.source.length - 1) {
       leftFlanking = false;
@@ -461,7 +491,7 @@
     } else {
       following = parser.source.substring(runEnd + 1, runEnd + 2);
     }
-    followedByPunctuation = punctuation.contains(following);
+    followedByPunctuation = punctuation.hasMatch(following);
 
     // http://spec.commonmark.org/0.28/#left-flanking-delimiter-run
     if (whitespace.contains(following)) {
diff --git a/test/markdown_test.dart b/test/markdown_test.dart
index d88b91b..f4769c9 100644
--- a/test/markdown_test.dart
+++ b/test/markdown_test.dart
@@ -39,6 +39,12 @@
 ''', '''
 <pre><code class="language-&quot;/&gt;&lt;a/href=&quot;url&quot;&gt;arbitrary_html&lt;/a&gt;"></code></pre>
 ''');
+
+    validateCore('Unicode ellipsis as punctuation', '''
+"Connecting dot **A** to **B.**…"
+''', '''
+<p>"Connecting dot <strong>A</strong> to <strong>B.</strong>…"</p>
+''');
   });
 
   group('Resolver', () {