Merge pull request #29 from dart-lang/fixes

Fix a couple more highlight bugs
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 44c3ca5..afba8b7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,6 +4,12 @@
   ending one character before the end of the file. This matches the documented
   behavior.
 
+* `FileSpan.context` now includes the full line on which the span appears for
+  empty spans at the beginning and end of lines.
+
+* Fix an edge case where `FileSpan.highlight()` could crash when highlighting a
+  span that ended with an empty line.
+
 # 1.5.1
 
 * Produce better source span highlights for multi-line spans that cover the
diff --git a/lib/src/file.dart b/lib/src/file.dart
index 3941042..ec4db63 100644
--- a/lib/src/file.dart
+++ b/lib/src/file.dart
@@ -300,7 +300,17 @@
     if (endColumn == 0 && endLine != 0) {
       // If [end] is at the very beginning of the line, the span covers the
       // previous newline, so we only want to include the previous line in the
-      // context.
+      // context...
+
+      if (length == 0) {
+        // ...unless this is a point span, in which case we want to include the
+        // next line (or the last line if this is the end of the file).
+        return endLine == file.lines - 1
+            ? file.getText(file.getOffset(endLine - 1))
+            : file.getText(
+                file.getOffset(endLine), file.getOffset(endLine + 1));
+      }
+
       endOffset = _end;
     } else if (endLine == file.lines - 1) {
       // If the span covers the last line of the file, the context should go all
diff --git a/lib/src/highlighter.dart b/lib/src/highlighter.dart
index 3ed1749..f29a89d 100644
--- a/lib/src/highlighter.dart
+++ b/lib/src/highlighter.dart
@@ -202,12 +202,14 @@
 
     var lines = context.split("\n");
 
-    // Trim a trailing newline so we don't add an empty line to the end of the
-    // highlight.
-    if (lines.last.isEmpty && lines.length > 1) lines.removeLast();
+    var lastLineIndex = _span.end.line - _span.start.line;
+    if (lines.last.isEmpty && lines.length > lastLineIndex + 1) {
+      // Trim a trailing newline so we don't add an empty line to the end of the
+      // highlight.
+      lines.removeLast();
+    }
 
     _writeFirstLine(lines.first);
-    var lastLineIndex = _span.end.line - _span.start.line;
     if (_multiline) {
       _writeIntermediateLines(lines.skip(1).take(lastLineIndex - 1));
       _writeLastLine(lines[lastLineIndex]);
diff --git a/pubspec.yaml b/pubspec.yaml
index ffd9df3..fd80bc8 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
 name: source_span
-version: 1.5.2-dev
+version: 1.5.2
 
 description: A library for identifying source spans and locations.
 author: Dart Team <misc@dartlang.org>
diff --git a/test/file_test.dart b/test/file_test.dart
index 3f32a3c..117458d 100644
--- a/test/file_test.dart
+++ b/test/file_test.dart
@@ -257,10 +257,42 @@
       expect(file.span(29).text, equals("p zap zop"));
     });
 
-    test("context contains the span's text", () {
-      var span = file.span(8, 15);
-      expect(span.context.contains(span.text), isTrue);
-      expect(span.context, equals('foo bar baz\nwhiz bang boom\n'));
+    group("context", () {
+      test("contains the span's text", () {
+        var span = file.span(8, 15);
+        expect(span.context.contains(span.text), isTrue);
+        expect(span.context, equals('foo bar baz\nwhiz bang boom\n'));
+      });
+
+      test("contains the previous line for a point span at the end of a line",
+          () {
+        var span = file.span(25, 25);
+        expect(span.context, equals('whiz bang boom\n'));
+      });
+
+      test("contains the next line for a point span at the beginning of a line",
+          () {
+        var span = file.span(12, 12);
+        expect(span.context, equals('whiz bang boom\n'));
+      });
+
+      group("contains the last line for a point span at the end of a file", () {
+        test("without a newline", () {
+          var span = file.span(file.length, file.length);
+          expect(span.context, equals('zip zap zop'));
+        });
+
+        test("with a newline", () {
+          file = new SourceFile.fromString("""
+foo bar baz
+whiz bang boom
+zip zap zop
+""", url: "foo.dart");
+
+          var span = file.span(file.length, file.length);
+          expect(span.context, equals('zip zap zop\n'));
+        });
+      });
     });
 
     group("union()", () {
diff --git a/test/highlight_test.dart b/test/highlight_test.dart
index b9e7ded..8418367 100644
--- a/test/highlight_test.dart
+++ b/test/highlight_test.dart
@@ -157,6 +157,20 @@
   '"""));
     });
 
+    test("highlights the full last line if it's empty", () {
+      var file = new SourceFile.fromString("""
+foo
+
+bar
+""");
+
+      expect(file.span(4, 9).highlight(), equals("""
+  ,
+2 | / 
+3 | \\ bar
+  '"""));
+    });
+
     test("highlights the full last line", () {
       expect(file.span(4, 27).highlight(), equals("""
   ,
@@ -216,6 +230,20 @@
 3 | \\ zip zap zop
   '"""));
     });
+
+    test("highlights the full last line if it's empty", () {
+      var file = new SourceFile.fromString("""
+foo
+
+bar
+""");
+
+      expect(file.span(0, 5).highlight(), equals("""
+  ,
+1 | / foo
+2 | \\ 
+  '"""));
+    });
   });
 
   group("prints tabs as spaces", () {