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/pkgs/source_maps/lib/span.dart b/pkgs/source_maps/lib/span.dart index e7bf5bf..7ca0ca9 100644 --- a/pkgs/source_maps/lib/span.dart +++ b/pkgs/source_maps/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/pkgs/source_maps/test/span_test.dart b/pkgs/source_maps/test/span_test.dart index 66bd8a8..c7b9cde 100644 --- a/pkgs/source_maps/test/span_test.dart +++ b/pkgs/source_maps/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'); }); }); });