Index: sdk/lib/core/exceptions.dart |
diff --git a/sdk/lib/core/exceptions.dart b/sdk/lib/core/exceptions.dart |
index 800a6529ab209a109d82c40a115d020f15734e16..bccc8b2f0b8724adfa2b348cd8a65dd2ba8332e6 100644 |
--- a/sdk/lib/core/exceptions.dart |
+++ b/sdk/lib/core/exceptions.dart |
@@ -44,13 +44,122 @@ class FormatException implements Exception { |
* A message describing the format error. |
*/ |
final String message; |
+ /** |
+ * The source that caused the error. |
nweiz
2014/07/14 19:33:50
The documentation and name doesn't make it clear a
Lasse Reichstein Nielsen
2014/07/15 08:55:59
I'll try to make it more explicit, including the w
|
+ * |
+ * This is usually a [String], but can be other types too. If it is a string, |
+ * parts of it may be included in the [toString] message. |
+ * |
+ * May also be `null` if omitted. |
+ */ |
+ final source; |
nweiz
2014/07/14 19:33:49
It would be a lot more useful to consumers of this
Lasse Reichstein Nielsen
2014/07/15 08:55:59
I don't want to mandate a type. It will almost alw
|
+ /** |
+ * The position in source where the error was detected. |
nweiz
2014/07/14 19:33:49
Please be clearer about what you mean by "position
Lasse Reichstein Nielsen
2014/07/15 08:55:59
I like "offset", so I changed it everywhere.
Also
|
+ * |
+ * May be omitted. If present, [source] should also be present. |
+ */ |
+ final int position; |
/** |
* Creates a new FormatException with an optional error [message]. |
+ * |
+ * Optionally also supply the [source] that had the incorrect format, and |
+ * even the [position] in the format where this was detected. |
*/ |
- const FormatException([this.message = ""]); |
+ const FormatException([this.message = "", this.source, this.position]); |
- String toString() => "FormatException: $message"; |
+ /** |
+ * Returns a description of the format exception. |
+ * |
+ * The description always contains the [message]. |
+ * If [source] was provided, the description will contain (at least a part of) |
+ * the source. |
+ * If [position] is also provided, the part of the source included will |
+ * contain that position, and the position will be marked. |
+ * |
+ * If the source contains a line break before position, only the line |
+ * containing position will be included, and its line number will also be |
+ * part of the description. Line and character offsets are 1-based. |
+ */ |
+ String toString() { |
+ String report = "FormatException"; |
+ if (message != null && message.isNotEmpty) { |
+ report = "$report: $message"; |
+ } |
+ int position = this.position; |
+ if (source is! String) { |
+ if (position != null) { |
+ report += " (at position $position)"; |
+ } |
+ return report; |
+ } |
+ if (position != null && (position < 0 || position > source.length)) { |
+ position = null; |
+ } |
+ // Source is string and position is null or valid. |
+ if (position == null) { |
+ String source = this.source; |
+ if (source.length > 78) { |
+ source = source.substring(0, 75) + "..."; |
+ } |
+ return "$report\n$source"; |
+ } |
+ int lineNum = 1; |
+ int lineStart = 0; |
+ bool lastWasCR; |
+ for (int i = 0; i < position; i++) { |
+ int char = source.codeUnitAt(i); |
+ if (char == 0x0a) { |
+ if (lineStart != i || !lastWasCR) { |
+ lineNum++; |
+ } |
+ lineStart = i + 1; |
+ lastWasCR = false; |
+ } else if (char == 0x0d) { |
+ lineNum++; |
+ lineStart = i + 1; |
+ lastWasCR = true; |
+ } |
+ } |
+ if (lineNum > 1) { |
+ report += " (at line $lineNum, character ${position - lineStart + 1})\n"; |
+ } else { |
+ report += " (at character ${position + 1})\n"; |
+ } |
+ int lineEnd = source.length; |
+ for (int i = position; i < source.length; i++) { |
+ int char = source.codeUnitAt(i); |
+ if (char == 0x0a || char == 0x0d) { |
+ lineEnd = i; |
+ break; |
+ } |
+ } |
+ int length = lineEnd - lineStart; |
+ int start = lineStart; |
+ int end = lineEnd; |
+ String prefix = ""; |
+ String postfix = ""; |
+ if (length > 78) { |
+ // Can't show entire line. Try to anchor at the nearest end, if |
+ // one is within reach. |
+ int index = position - lineStart; |
+ if (index < 75) { |
+ end = start + 75; |
+ postfix = "..."; |
+ } else if (end - position < 75) { |
+ start = end - 75; |
+ prefix = "..."; |
+ } else { |
+ // Neither end is near, just pick an area around the position. |
+ start = position - 36; |
+ end = position + 36; |
+ prefix = postfix = "..."; |
+ } |
+ } |
+ String slice = source.substring(start, end); |
+ int markOffset = position - start + prefix.length; |
+ return "$report$prefix$slice$postfix\n${" " * markOffset}^\n"; |
+ } |
} |
class IntegerDivisionByZeroException implements Exception { |