Support multiple occurrences of text in context
R=nweiz@google.com
Review URL: https://codereview.chromium.org//1041163005
diff --git a/CHANGELOG.md b/CHANGELOG.md
index e749513..51b0fea 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,8 @@
+# 1.1.2
+
+* Fixed validation in `SourceSpanWithContext` to allow multiple occurrences of
+ `text` within `context`.
+
# 1.1.1
* Fixed `FileSpan`'s context to include the full span text, not just the first
diff --git a/lib/src/span_mixin.dart b/lib/src/span_mixin.dart
index a93723f..b4503fa 100644
--- a/lib/src/span_mixin.dart
+++ b/lib/src/span_mixin.dart
@@ -65,11 +65,10 @@
var textLine;
if (this is SourceSpanWithContext) {
var context = (this as SourceSpanWithContext).context;
- var textIndex = context.indexOf(text.split('\n').first);
- var lineStart = context.lastIndexOf('\n', textIndex);
- if (lineStart != -1) {
- buffer.write(context.substring(0, lineStart + 1));
- context = context.substring(lineStart + 1);
+ var lineStart = findLineStart(context, text, column);
+ if (lineStart != null && lineStart > 0) {
+ buffer.write(context.substring(0, lineStart));
+ context = context.substring(lineStart);
}
var endIndex = context.indexOf('\n');
textLine = endIndex == -1 ? context : context.substring(0, endIndex + 1);
diff --git a/lib/src/span_with_context.dart b/lib/src/span_with_context.dart
index edbb8b6..0012e3f 100644
--- a/lib/src/span_with_context.dart
+++ b/lib/src/span_with_context.dart
@@ -6,6 +6,7 @@
import 'location.dart';
import 'span.dart';
+import 'utils.dart';
/// A class that describes a segment of source text with additional context.
class SourceSpanWithContext extends SourceSpanBase {
@@ -23,14 +24,12 @@
SourceSpanWithContext(
SourceLocation start, SourceLocation end, String text, this.context)
: super(start, end, text) {
- var index = context.indexOf(text);
- if (index == -1) {
+ if (!context.contains(text)) {
throw new ArgumentError(
'The context line "$context" must contain "$text".');
}
- var beginningOfLine = context.lastIndexOf('\n', index) + 1;
- if (start.column != index - beginningOfLine) {
+ if (findLineStart(context, text, start.column) == null) {
throw new ArgumentError('The span text "$text" must start at '
'column ${start.column + 1} in a line within "$context".');
}
diff --git a/lib/src/utils.dart b/lib/src/utils.dart
index 4a8eb55..2d33865 100644
--- a/lib/src/utils.dart
+++ b/lib/src/utils.dart
@@ -37,3 +37,20 @@
return max;
}
+/// Finds a line in [context] containing [text] at the specified [column].
+///
+/// Returns the index in [context] where that line begins, or null if none
+/// exists.
+int findLineStart(String context, String text, int column) {
+ var isEmpty = text == '';
+ var index = context.indexOf(text);
+ while (index != -1) {
+ var lineStart = context.lastIndexOf('\n', index) + 1;
+ var textColumn = index - lineStart;
+ if (column == textColumn || (isEmpty && column == textColumn + 1)) {
+ return lineStart;
+ }
+ index = context.indexOf(text, index + 1);
+ }
+ return null;
+}
diff --git a/pubspec.yaml b/pubspec.yaml
index 73c2667..16eee7c 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -1,5 +1,5 @@
name: source_span
-version: 1.1.1
+version: 1.1.2
author: Dart Team <misc@dartlang.org>
description: A library for identifying source spans and locations.
homepage: http://github.com/dart-lang/source_span
diff --git a/test/span_test.dart b/test/span_test.dart
index 39b0b94..2657f5f 100644
--- a/test/span_test.dart
+++ b/test/span_test.dart
@@ -65,6 +65,21 @@
new SourceSpanWithContext(start, end, "abc", "\n---abc--");
new SourceSpanWithContext(start, end, "abc", "\n\n---abc--");
});
+
+ test('text can occur multiple times in context', () {
+ var start1 = new SourceLocation(4, line: 55, column: 2);
+ var end1 = new SourceLocation(7, line: 55, column: 5);
+ var start2 = new SourceLocation(4, line: 55, column: 8);
+ var end2 = new SourceLocation(7, line: 55, column: 11);
+ new SourceSpanWithContext(start1, end1, "abc", "--abc---abc--\n");
+ new SourceSpanWithContext(start1, end1, "abc", "--abc--abc--\n");
+ new SourceSpanWithContext(start2, end2, "abc", "--abc---abc--\n");
+ new SourceSpanWithContext(start2, end2, "abc", "---abc--abc--\n");
+ expect(() => new SourceSpanWithContext(
+ start1, end1, "abc", "---abc--abc--\n"), throwsArgumentError);
+ expect(() => new SourceSpanWithContext(
+ start2, end2, "abc", "--abc--abc--\n"), throwsArgumentError);
+ });
});
group('for union()', () {
@@ -238,6 +253,18 @@
${colors.YELLOW}^^^^^^^${colors.NONE}"""));
});
+ test("underlines correctly when text appears twice", () {
+ var span = new SourceSpanWithContext(
+ new SourceLocation(9, column: 9, sourceUrl: "foo.dart"),
+ new SourceLocation(12, column: 12, sourceUrl: "foo.dart"),
+ "foo",
+ "-----foo foo-----");
+ expect(span.message("oh no", color: colors.YELLOW), equals("""
+line 1, column 10 of foo.dart: oh no
+-----foo ${colors.YELLOW}foo${colors.NONE}-----
+ ${colors.YELLOW}^^^${colors.NONE}"""));
+ });
+
test("supports lines of preceeding context", () {
var span = new SourceSpanWithContext(
new SourceLocation(5, line: 3, column: 5, sourceUrl: "foo.dart"),
diff --git a/test/utils_test.dart b/test/utils_test.dart
index a998847..74975c3 100644
--- a/test/utils_test.dart
+++ b/test/utils_test.dart
@@ -39,6 +39,45 @@
}
});
});
+
+ group('find line start', () {
+ test('skip entries in wrong column', () {
+ var context = '0_bb\n1_bbb\n2b____\n3bbb\n';
+ var index = findLineStart(context, 'b', 1);
+ expect(index, 11);
+ expect(context.substring(index - 1, index + 3), '\n2b_');
+ });
+
+ test('end of line column for empty text', () {
+ var context = '0123\n56789\nabcdefgh\n';
+ var index = findLineStart(context, '', 5);
+ expect(index, 5);
+ expect(context[index], '5');
+ });
+
+ test('column at the end of the file for empty text', () {
+ var context = '0\n2\n45\n';
+ var index = findLineStart(context, '', 2);
+ expect(index, 4);
+ expect(context[index], '4');
+
+ context = '0\n2\n45';
+ index = findLineStart(context, '', 2);
+ expect(index, 4);
+ });
+
+ test('found on the first line', () {
+ var context = '0\n2\n45\n';
+ var index = findLineStart(context, '0', 0);
+ expect(index, 0);
+ });
+
+ test('not found', () {
+ var context = '0\n2\n45\n';
+ var index = findLineStart(context, '0', 1);
+ expect(index, isNull);
+ });
+ });
}
_linearSearch(list, predicate) {