Fixes in sourcemaps discovered in csslib
R=dgrove@google.com
Review URL: https://codereview.chromium.org//18749005
git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/source_maps@24881 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/span.dart b/lib/span.dart
index e7bf5bf..7ca0ca9 100644
--- a/lib/span.dart
+++ b/lib/span.dart
@@ -219,11 +219,20 @@
int getLine(int offset) => binarySearch(_lineStarts, (o) => o > offset) - 1;
/// Gets the 0-based column corresponding to an offset.
- int getColumn(int line, int offset) => offset - _lineStarts[line];
+ int getColumn(int line, int offset) {
+ if (line < 0 || line >= _lineStarts.length) return 0;
+ return offset - _lineStarts[line];
+ }
/// Get the offset for a given line and column
- int getOffset(int line, int column) =>
- _lineStarts[max(min(line, _lineStarts.length - 1), 0)] + column;
+ int getOffset(int line, int column) {
+ if (line < 0) return getOffset(0, 0);
+ if (line < _lineStarts.length) {
+ return _lineStarts[line] + column;
+ } else {
+ return _decodedChars.length;
+ }
+ }
/// Gets the text at the given offsets.
String getText(int start, [int end]) =>
@@ -251,7 +260,6 @@
// +1 for 0-indexing, +1 again to avoid the last line
var textLine = getText(getOffset(line, 0), getOffset(line + 1, 0));
-
column = min(column, textLine.length - 1);
int toColumn = min(column + end - start, textLine.length);
if (useColors) {
@@ -265,6 +273,7 @@
buf.write(textLine.substring(toColumn));
} else {
buf.write(textLine);
+ if (textLine != '' && !textLine.endsWith('\n')) buf.write('\n');
}
int i = 0;
@@ -290,13 +299,13 @@
final int _baseOffset;
final int _baseLine;
final int _baseColumn;
+ final int _maxOffset;
- // TODO(sigmund): consider providing an end-offset to correctly truncate
- // values passed the end of the segment.
SourceFileSegment(String url, String textSegment, Location startOffset)
: _baseOffset = startOffset.offset,
_baseLine = startOffset.line,
_baseColumn = startOffset.column,
+ _maxOffset = startOffset.offset + textSegment.length,
super.text(url, textSegment);
/// Craete a span, where [start] is relative to this segment's base offset.
@@ -313,9 +322,13 @@
/// Return the line on the underlying file associated with the [offset] of the
/// underlying file. This method operates on the real offsets from the
- /// original file, so that error messages can be reported accurately.
- int getLine(int offset) =>
- super.getLine(max(offset - _baseOffset, 0)) + _baseLine;
+ /// original file, so that error messages can be reported accurately. When the
+ /// requested offset is past the length of the segment, this returns the line
+ /// number after the end of the segment (total lines + 1).
+ int getLine(int offset) {
+ var res = super.getLine(max(offset - _baseOffset, 0)) + _baseLine;
+ return (offset > _maxOffset) ? res + 1 : res;
+ }
/// Return the column on the underlying file associated with [line] and
/// [offset], where [line] is absolute from the beginning of the underlying
@@ -330,13 +343,12 @@
/// on the real offsets from the original file, so that error messages can be
/// reported accurately.
int getOffset(int line, int column) =>
- super.getOffset(line - _baseLine,
- line == _baseLine ? column - _baseColumn : column) + _baseOffset;
+ super.getOffset(line - _baseLine,
+ line == _baseLine ? column - _baseColumn : column) + _baseOffset;
/// Retrieve the text associated with the specified range. This method
/// operates on the real offsets from the original file, so that error
/// messages can be reported accurately.
String getText(int start, [int end]) =>
- super.getText(start - _baseOffset,
- end == null ? null : end - _baseOffset);
+ super.getText(start - _baseOffset, end == null ? null : end - _baseOffset);
}
diff --git a/test/span_test.dart b/test/span_test.dart
index 66bd8a8..c7b9cde 100644
--- a/test/span_test.dart
+++ b/test/span_test.dart
@@ -69,22 +69,80 @@
expect(file.getText(line + 2, line + 11), '34+6789_1');
});
- test('get location message', () {
- // fifth line (including 4 new lines), columns 2 .. 11
- var line = 10 + 80 + 31 + 27 + 4;
- expect(file.getLocationMessage('the message', line + 2, line + 11),
- 'file:5:3: the message\n'
- '1234+6789_1234\n'
- ' ^^^^^^^^^');
- });
+ group('location message', () {
+ test('first line', () {
+ expect(file.getLocationMessage('the message', 1, 3),
+ 'file:1:2: the message\n'
+ '+23456789_\n'
+ ' ^^');
+ });
- test('get location message - no file url', () {
- var line = 10 + 80 + 31 + 27 + 4;
- expect(new SourceFile.text(null, TEST_FILE).getLocationMessage(
- 'the message', line + 2, line + 11),
- ':5:3: the message\n'
- '1234+6789_1234\n'
- ' ^^^^^^^^^');
+ test('in the middle of the file', () {
+ // fifth line (including 4 new lines), columns 2 .. 11
+ var line = 10 + 80 + 31 + 27 + 4;
+ expect(file.getLocationMessage('the message', line + 2, line + 11),
+ 'file:5:3: the message\n'
+ '1234+6789_1234\n'
+ ' ^^^^^^^^^');
+ });
+
+ test('no file url', () {
+ var line = 10 + 80 + 31 + 27 + 4;
+ expect(new SourceFile.text(null, TEST_FILE).getLocationMessage(
+ 'the message', line + 2, line + 11),
+ ':5:3: the message\n'
+ '1234+6789_1234\n'
+ ' ^^^^^^^^^');
+ });
+
+ test('penultimate line', () {
+ // We search '\n' backwards twice because last line is \n terminated:
+ int index = TEST_FILE.lastIndexOf('\n');
+ var start = TEST_FILE.lastIndexOf('\n', index - 1) - 3;
+ expect(file.getLocationMessage('the message', start, start + 2),
+ 'file:11:41: the message\n'
+ '123456789_+23456789_123456789_123456789_123\n'
+ ' ^^');
+ });
+
+ test('last line', () {
+ var start = TEST_FILE.lastIndexOf('\n') - 2;
+ expect(file.getLocationMessage('the message', start, start + 1),
+ 'file:12:28: the message\n'
+ '123456789_1+3456789_123456789\n'
+ ' ^');
+ });
+
+ group('no trailing empty-line at the end -', () {
+ var text = TEST_FILE.substring(0, TEST_FILE.length - 1);
+ var file2 = new SourceFile.text('file', text);
+
+ test('penultimate line', () {
+ var start = text.lastIndexOf('\n') - 3;
+ expect(file2.getLocationMessage('the message', start, start + 2),
+ 'file:11:41: the message\n'
+ '123456789_+23456789_123456789_123456789_123\n'
+ ' ^^');
+ });
+
+ test('last line', () {
+ var start = text.length - 2;
+ expect(file2.getLocationMessage('the message', start, start + 1),
+ 'file:12:28: the message\n'
+ '123456789_1+3456789_123456789\n'
+ ' ^');
+ });
+ });
+
+ test('single line', () {
+ var text = "this is a single line";
+ int start = text.indexOf(' ') + 1;
+ var file2 = new SourceFile.text('file', text);
+ expect(file2.getLocationMessage('the message', start, start + 2),
+ 'file:1:${start + 1}: the message\n'
+ 'this is a single line\n'
+ ' ^^');
+ });
});
test('location getters', () {
@@ -242,17 +300,11 @@
'123456789_1+3456789_123456789\n'
' ^');
- // TODO(sigmund): consider also fixing this and report file:10:4
// The answer below is different because the segment parsing only knows
// about the 10 lines it has (and nothing about the possible extra lines
// afterwards)
expect(segment.getLocationMessage('the message', start, start + 1),
- 'file:10:112: the message\n');
-
- // The number 112 includes the count of extra characters past the
- // segment, which we verify here:
- var lastSegmentLineStart = segmentText.lastIndexOf('\n');
- expect(start - baseOffset - lastSegmentLineStart, 112);
+ 'file:11:1: the message\n');
});
});
});