Fix a bug when highlighting an end-of-file point span (#30)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index afba8b7..6e79fbc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,9 @@
+# 1.5.3
+
+* Fix an edge case where `FileSpan.highlight()` would put the highlight
+ indicator in the wrong position when highlighting a point span after the end
+ of a file.
+
# 1.5.2
* `SourceFile.span()` now goes to the end of the file by default, rather than
diff --git a/lib/src/highlighter.dart b/lib/src/highlighter.dart
index f29a89d..8100f18 100644
--- a/lib/src/highlighter.dart
+++ b/lib/src/highlighter.dart
@@ -82,7 +82,7 @@
new SourceLocation(span.end.offset,
sourceUrl: span.sourceUrl,
line: countCodeUnits(span.text, $lf),
- column: _lastColumn(span.text)),
+ column: _lastLineLength(span.text)),
span.text,
span.text);
@@ -126,34 +126,55 @@
end = new SourceLocation(span.end.offset - 1,
sourceUrl: span.sourceUrl,
line: span.end.line - 1,
- column: _lastColumn(text));
+ column: _lastLineLength(text));
start = span.start.offset == span.end.offset ? end : span.start;
}
return new SourceSpanWithContext(start, end, text, context);
}
- /// Normalizes [span] so that the end location is at the end of a line, rather
- /// than on the beginning of the next line.
+ /// Normalizes [span] so that the end location is at the end of a line rather
+ /// than at the beginning of the next line.
static SourceSpanWithContext _normalizeEndOfLine(SourceSpanWithContext span) {
if (span.end.column != 0) return span;
- if (span.end.line == span.start.line) return span;
- assert(span.text.endsWith("\n"));
+ if (span.length == 0) {
+ if (span.end.offset == 0) return span;
- var text = span.text.substring(0, span.text.length - 1);
- return new SourceSpanWithContext(
- span.start,
- new SourceLocation(span.end.offset - 1,
- sourceUrl: span.sourceUrl,
- line: span.end.line - 1,
- column: _lastColumn(text)),
- text,
- span.context);
+ // If [span] is a point span with an empty context, there's no useful
+ // adjustment we can do.
+ if (span.context.isEmpty) return span;
+
+ var location = new SourceLocation(span.end.offset - 1,
+ sourceUrl: span.sourceUrl,
+ line: span.end.line - 1,
+ column: _lastLineLength(span.context));
+ return new SourceSpanWithContext(location, location, "", span.context);
+ } else {
+ if (span.end.line == span.start.line) return span;
+
+ var text = span.text.substring(0, span.text.length - 1);
+
+ return new SourceSpanWithContext(
+ span.start,
+ new SourceLocation(span.end.offset - 1,
+ sourceUrl: span.sourceUrl,
+ line: span.end.line - 1,
+ column: _lastLineLength(text)),
+ text,
+ span.context);
+ }
}
- /// Returns the (0-based) column number of the last column of the last line in [text].
- static int _lastColumn(String text) =>
- text.length - text.lastIndexOf("\n") + 1;
+ /// Returns the length of the last line in [text], whether or not it ends in a
+ /// newline.
+ static int _lastLineLength(String text) {
+ 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;
+ }
/// Returns whether [span]'s text runs all the way to the end of its context.
static bool _isTextAtEndOfContext(SourceSpanWithContext span) =>
diff --git a/pubspec.yaml b/pubspec.yaml
index fd80bc8..c4b4ccf 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: source_span
-version: 1.5.2
+version: 1.5.3
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 8418367..7832e51 100644
--- a/test/highlight_test.dart
+++ b/test/highlight_test.dart
@@ -53,6 +53,14 @@
'"""));
});
+ test("works for a point span at the beginning of the file", () {
+ expect(file.location(0).pointSpan().highlight(), equals("""
+ ,
+1 | foo bar baz
+ | ^
+ '"""));
+ });
+
test("works for a point span at the end of a line", () {
expect(file.location(11).pointSpan().highlight(), equals("""
,
@@ -69,9 +77,28 @@
'"""));
});
+ test("works for a point span after the end of the file", () {
+ expect(file.location(39).pointSpan().highlight(), equals("""
+ ,
+3 | zip zap zop
+ | ^
+ '"""));
+ });
+
test("works for a point span at the end of the file with no trailing newline",
() {
file = new SourceFile.fromString("zip zap zop");
+ expect(file.location(10).pointSpan().highlight(), equals("""
+ ,
+1 | zip zap zop
+ | ^
+ '"""));
+ });
+
+ test(
+ "works for a point span after the end of the file with no trailing newline",
+ () {
+ file = new SourceFile.fromString("zip zap zop");
expect(file.location(11).pointSpan().highlight(), equals("""
,
1 | zip zap zop
@@ -157,7 +184,7 @@
'"""));
});
- test("highlights the full last line if it's empty", () {
+ test("highlights the full first line if it's empty", () {
var file = new SourceFile.fromString("""
foo