Fix a highlighting bug (#33)

If a span covered a trailing newline and a single additional line, it
went down a code path that resulted in a range error. That code path
has now been fixed.

Closes #32
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a07a0e6..ddb4ff0 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.5.5
+
+* Fix a bug where `FileSpan.highlight()` would crash for spans that covered a
+  trailing newline and a single additional empty line.
+
 # 1.5.4
 
 * `FileSpan.highlight()` now properly highlights point spans at the beginning of
diff --git a/lib/src/highlighter.dart b/lib/src/highlighter.dart
index 8a928b3..17a47bc 100644
--- a/lib/src/highlighter.dart
+++ b/lib/src/highlighter.dart
@@ -117,6 +117,10 @@
       SourceSpanWithContext span) {
     if (!span.context.endsWith("\n")) return span;
 
+    // If there's a full blank line on the end of [span.context], it's probably
+    // significant, so we shouldn't trim it.
+    if (span.text.endsWith("\n\n")) return span;
+
     var context = span.context.substring(0, span.context.length - 1);
     var text = span.text;
     var start = span.start;
@@ -156,9 +160,13 @@
     if (text.isEmpty) return 0;
 
     // The "- 1" here avoids counting the newline itself.
-    return text.codeUnitAt(text.length - 1) == $lf
-        ? text.length - text.lastIndexOf("\n", text.length - 2) - 1
-        : text.length - text.lastIndexOf("\n") - 1;
+    if (text.codeUnitAt(text.length - 1) == $lf) {
+      return text.length == 1
+          ? 0
+          : text.length - text.lastIndexOf("\n", text.length - 2) - 1;
+    } else {
+      return text.length - text.lastIndexOf("\n") - 1;
+    }
   }
 
   /// Returns whether [span]'s text runs all the way to the end of its context.
diff --git a/pubspec.yaml b/pubspec.yaml
index a470875..0cb9326 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: source_span
-version: 1.5.4
+version: 1.5.5
 
 description: A library for identifying source spans and locations.
 author: Dart Team <misc@dartlang.org>
diff --git a/test/highlight_test.dart b/test/highlight_test.dart
index af9cd1b..5fdf622 100644
--- a/test/highlight_test.dart
+++ b/test/highlight_test.dart
@@ -120,6 +120,15 @@
   | ^
   '"""));
     });
+
+    test("on an empty line", () {
+      var file = new SourceFile.fromString("foo\n\nbar");
+      expect(file.location(4).pointSpan().highlight(), equals("""
+  ,
+2 | 
+  | ^
+  '"""));
+    });
   });
 
   test("highlights a single-line file without a newline", () {
@@ -131,6 +140,15 @@
   '"""));
   });
 
+  test("highlights a single empty line", () {
+    expect(new SourceFile.fromString("foo\n\nbar").span(4, 5).highlight(),
+        equals("""
+  ,
+2 | 
+  | ^
+  '"""));
+  });
+
   group("with a multiline span", () {
     test("highlights the middle of the first and last lines", () {
       expect(file.span(4, 34).highlight(), equals("""
@@ -278,6 +296,27 @@
 2 | \\ 
   '"""));
     });
+
+    test("highlights multiple empty lines", () {
+      var file = new SourceFile.fromString("foo\n\n\n\nbar");
+      expect(file.span(4, 7).highlight(), equals("""
+  ,
+2 | / 
+3 | | 
+4 | \\ 
+  '"""));
+    });
+
+    // Regression test for #32
+    test("highlights the end of a line and an empty line", () {
+      var file = new SourceFile.fromString("foo\n\n");
+      expect(file.span(3, 5).highlight(), equals("""
+  ,
+1 |   foo
+  | ,----^
+2 | \\ 
+  '"""));
+    });
   });
 
   group("prints tabs as spaces", () {