| OLD | NEW |
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library dart2js.io.source_file; | 5 library dart2js.io.source_file; |
| 6 | 6 |
| 7 import 'dart:math'; | 7 import 'dart:math'; |
| 8 import 'dart:convert' show UTF8; | 8 import 'dart:convert' show UTF8; |
| 9 import 'dart:typed_data' show Uint8List; | 9 import 'dart:typed_data' show Uint8List; |
| 10 | 10 |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 82 } | 82 } |
| 83 | 83 |
| 84 /** | 84 /** |
| 85 * Returns the line number for the offset [position] in the string | 85 * Returns the line number for the offset [position] in the string |
| 86 * representation of this source file. | 86 * representation of this source file. |
| 87 */ | 87 */ |
| 88 int getLine(int position) { | 88 int getLine(int position) { |
| 89 List<int> starts = lineStarts; | 89 List<int> starts = lineStarts; |
| 90 if (position < 0 || starts.last <= position) { | 90 if (position < 0 || starts.last <= position) { |
| 91 throw 'bad position #$position in file $filename with ' | 91 throw 'bad position #$position in file $filename with ' |
| 92 'length ${length}.'; | 92 'length ${length}.'; |
| 93 } | 93 } |
| 94 int first = 0; | 94 int first = 0; |
| 95 int count = starts.length; | 95 int count = starts.length; |
| 96 while (count > 1) { | 96 while (count > 1) { |
| 97 int step = count ~/ 2; | 97 int step = count ~/ 2; |
| 98 int middle = first + step; | 98 int middle = first + step; |
| 99 int lineStart = starts[middle]; | 99 int lineStart = starts[middle]; |
| 100 if (position < lineStart) { | 100 if (position < lineStart) { |
| 101 count = step; | 101 count = step; |
| 102 } else { | 102 } else { |
| (...skipping 22 matching lines...) Expand all Loading... |
| 125 * range `[start, end]` in this file. | 125 * range `[start, end]` in this file. |
| 126 * | 126 * |
| 127 * If [includeSourceLine] is `true` the first source line code line that | 127 * If [includeSourceLine] is `true` the first source line code line that |
| 128 * contains the range will be included as well as marker characters ('^') | 128 * contains the range will be included as well as marker characters ('^') |
| 129 * underlining the range. | 129 * underlining the range. |
| 130 * | 130 * |
| 131 * Use [colorize] to wrap source code text and marker characters in color | 131 * Use [colorize] to wrap source code text and marker characters in color |
| 132 * escape codes. | 132 * escape codes. |
| 133 */ | 133 */ |
| 134 String getLocationMessage(String message, int start, int end, | 134 String getLocationMessage(String message, int start, int end, |
| 135 {bool includeSourceLine: true, | 135 {bool includeSourceLine: true, String colorize(String text)}) { |
| 136 String colorize(String text)}) { | |
| 137 if (colorize == null) { | 136 if (colorize == null) { |
| 138 colorize = (text) => text; | 137 colorize = (text) => text; |
| 139 } | 138 } |
| 140 if (end > length) { | 139 if (end > length) { |
| 141 start = length - 1; | 140 start = length - 1; |
| 142 end = length; | 141 end = length; |
| 143 } | 142 } |
| 144 | 143 |
| 145 int lineStart = getLine(start); | 144 int lineStart = getLine(start); |
| 146 int columnStart = getColumn(lineStart, start); | 145 int columnStart = getColumn(lineStart, start); |
| 147 int lineEnd = getLine(end); | 146 int lineEnd = getLine(end); |
| 148 int columnEnd = getColumn(lineEnd, end); | 147 int columnEnd = getColumn(lineEnd, end); |
| 149 | 148 |
| 150 StringBuffer buf = new StringBuffer('${filename}:'); | 149 StringBuffer buf = new StringBuffer('${filename}:'); |
| 151 if (start != end || start != 0) { | 150 if (start != end || start != 0) { |
| 152 // Line/column info is relevant. | 151 // Line/column info is relevant. |
| 153 buf.write('${lineStart + 1}:${columnStart + 1}:'); | 152 buf.write('${lineStart + 1}:${columnStart + 1}:'); |
| 154 } | 153 } |
| 155 buf.write('\n$message\n'); | 154 buf.write('\n$message\n'); |
| 156 | 155 |
| 157 if (start != end && includeSourceLine) { | 156 if (start != end && includeSourceLine) { |
| 158 if (lineStart == lineEnd) { | 157 if (lineStart == lineEnd) { |
| 159 String textLine = getLineText(lineStart); | 158 String textLine = getLineText(lineStart); |
| 160 | 159 |
| 161 int toColumn = min(columnStart + (end-start), textLine.length); | 160 int toColumn = min(columnStart + (end - start), textLine.length); |
| 162 buf.write(textLine.substring(0, columnStart)); | 161 buf.write(textLine.substring(0, columnStart)); |
| 163 buf.write(colorize(textLine.substring(columnStart, toColumn))); | 162 buf.write(colorize(textLine.substring(columnStart, toColumn))); |
| 164 buf.write(textLine.substring(toColumn)); | 163 buf.write(textLine.substring(toColumn)); |
| 165 | 164 |
| 166 int i = 0; | 165 int i = 0; |
| 167 for (; i < columnStart; i++) { | 166 for (; i < columnStart; i++) { |
| 168 buf.write(' '); | 167 buf.write(' '); |
| 169 } | 168 } |
| 170 | 169 |
| 171 for (; i < toColumn; i++) { | 170 for (; i < toColumn; i++) { |
| (...skipping 17 matching lines...) Expand all Loading... |
| 189 | 188 |
| 190 return buf.toString(); | 189 return buf.toString(); |
| 191 } | 190 } |
| 192 | 191 |
| 193 int get lines => lineStarts.length - 1; | 192 int get lines => lineStarts.length - 1; |
| 194 | 193 |
| 195 /// Returns the text of line at the 0-based [index] within this source file. | 194 /// Returns the text of line at the 0-based [index] within this source file. |
| 196 String getLineText(int index) { | 195 String getLineText(int index) { |
| 197 // +1 for 0-indexing, +1 again to avoid the last line of the file | 196 // +1 for 0-indexing, +1 again to avoid the last line of the file |
| 198 if ((index + 2) < lineStarts.length) { | 197 if ((index + 2) < lineStarts.length) { |
| 199 return slowSubstring(lineStarts[index], lineStarts[index+1]); | 198 return slowSubstring(lineStarts[index], lineStarts[index + 1]); |
| 200 } else if ((index + 1) < lineStarts.length) { | 199 } else if ((index + 1) < lineStarts.length) { |
| 201 return '${slowSubstring(lineStarts[index], length)}\n'; | 200 return '${slowSubstring(lineStarts[index], length)}\n'; |
| 202 } else { | 201 } else { |
| 203 throw new ArgumentError("Line index $index is out of bounds."); | 202 throw new ArgumentError("Line index $index is out of bounds."); |
| 204 } | 203 } |
| 205 } | 204 } |
| 206 } | 205 } |
| 207 | 206 |
| 208 List<int> _zeroTerminateIfNecessary(List<int> bytes) { | 207 List<int> _zeroTerminateIfNecessary(List<int> bytes) { |
| 209 if (bytes.length > 0 && bytes.last == 0) return bytes; | 208 if (bytes.length > 0 && bytes.last == 0) return bytes; |
| 210 List<int> result = new Uint8List(bytes.length + 1); | 209 List<int> result = new Uint8List(bytes.length + 1); |
| 211 result.setRange(0, bytes.length, bytes); | 210 result.setRange(0, bytes.length, bytes); |
| 212 result[result.length - 1] = 0; | 211 result[result.length - 1] = 0; |
| 213 return result; | 212 return result; |
| 214 } | 213 } |
| 215 | 214 |
| 216 class Utf8BytesSourceFile extends SourceFile { | 215 class Utf8BytesSourceFile extends SourceFile { |
| 217 final Uri uri; | 216 final Uri uri; |
| 218 | 217 |
| 219 /** The UTF-8 encoded content of the source file. */ | 218 /** The UTF-8 encoded content of the source file. */ |
| 220 final List<int> zeroTerminatedContent; | 219 final List<int> zeroTerminatedContent; |
| 221 | 220 |
| 222 /** | 221 /** |
| 223 * Creates a Utf8BytesSourceFile. | 222 * Creates a Utf8BytesSourceFile. |
| 224 * | 223 * |
| 225 * If possible, the given [content] should be zero-terminated. If it isn't, | 224 * If possible, the given [content] should be zero-terminated. If it isn't, |
| 226 * the constructor clones the content and adds a trailing 0. | 225 * the constructor clones the content and adds a trailing 0. |
| 227 */ | 226 */ |
| 228 Utf8BytesSourceFile(this.uri, List<int> content) | 227 Utf8BytesSourceFile(this.uri, List<int> content) |
| 229 : this.zeroTerminatedContent = _zeroTerminateIfNecessary(content); | 228 : this.zeroTerminatedContent = _zeroTerminateIfNecessary(content); |
| 230 | 229 |
| 231 String slowText() { | 230 String slowText() { |
| 232 // Don't convert the trailing zero byte. | 231 // Don't convert the trailing zero byte. |
| 233 return UTF8.decoder.convert( | 232 return UTF8.decoder |
| 234 zeroTerminatedContent, 0, zeroTerminatedContent.length - 1); | 233 .convert(zeroTerminatedContent, 0, zeroTerminatedContent.length - 1); |
| 235 } | 234 } |
| 236 | 235 |
| 237 List<int> slowUtf8ZeroTerminatedBytes() => zeroTerminatedContent; | 236 List<int> slowUtf8ZeroTerminatedBytes() => zeroTerminatedContent; |
| 238 | 237 |
| 239 String slowSubstring(int start, int end) { | 238 String slowSubstring(int start, int end) { |
| 240 // TODO(lry): to make this faster, the scanner could record the UTF-8 slack | 239 // TODO(lry): to make this faster, the scanner could record the UTF-8 slack |
| 241 // for all positions of the source text. We could use [:content.sublist:]. | 240 // for all positions of the source text. We could use [:content.sublist:]. |
| 242 return slowText().substring(start, end); | 241 return slowText().substring(start, end); |
| 243 } | 242 } |
| 244 | 243 |
| 245 int get length { | 244 int get length { |
| 246 if (lengthCache == -1) { | 245 if (lengthCache == -1) { |
| 247 // During scanning the length is not yet assigned, so we use a slow path. | 246 // During scanning the length is not yet assigned, so we use a slow path. |
| 248 lengthCache = slowText().length; | 247 lengthCache = slowText().length; |
| 249 } | 248 } |
| 250 return lengthCache; | 249 return lengthCache; |
| 251 } | 250 } |
| 251 |
| 252 set length(int v) => lengthCache = v; | 252 set length(int v) => lengthCache = v; |
| 253 int lengthCache = -1; | 253 int lengthCache = -1; |
| 254 } | 254 } |
| 255 | 255 |
| 256 class CachingUtf8BytesSourceFile extends Utf8BytesSourceFile { | 256 class CachingUtf8BytesSourceFile extends Utf8BytesSourceFile { |
| 257 String cachedText; | 257 String cachedText; |
| 258 final String filename; | 258 final String filename; |
| 259 | 259 |
| 260 CachingUtf8BytesSourceFile(Uri uri, this.filename, List<int> content) | 260 CachingUtf8BytesSourceFile(Uri uri, this.filename, List<int> content) |
| 261 : super(uri, content); | 261 : super(uri, content); |
| (...skipping 13 matching lines...) Expand all Loading... |
| 275 | 275 |
| 276 StringSourceFile(this.uri, this.filename, this.text); | 276 StringSourceFile(this.uri, this.filename, this.text); |
| 277 | 277 |
| 278 StringSourceFile.fromUri(Uri uri, String text) | 278 StringSourceFile.fromUri(Uri uri, String text) |
| 279 : this(uri, uri.toString(), text); | 279 : this(uri, uri.toString(), text); |
| 280 | 280 |
| 281 StringSourceFile.fromName(String filename, String text) | 281 StringSourceFile.fromName(String filename, String text) |
| 282 : this(new Uri(path: filename), filename, text); | 282 : this(new Uri(path: filename), filename, text); |
| 283 | 283 |
| 284 int get length => text.length; | 284 int get length => text.length; |
| 285 set length(int v) { } | 285 set length(int v) {} |
| 286 | 286 |
| 287 String slowText() => text; | 287 String slowText() => text; |
| 288 | 288 |
| 289 List<int> slowUtf8ZeroTerminatedBytes() { | 289 List<int> slowUtf8ZeroTerminatedBytes() { |
| 290 return _zeroTerminateIfNecessary(UTF8.encode(text)); | 290 return _zeroTerminateIfNecessary(UTF8.encode(text)); |
| 291 } | 291 } |
| 292 | 292 |
| 293 String slowSubstring(int start, int end) => text.substring(start, end); | 293 String slowSubstring(int start, int end) => text.substring(start, end); |
| 294 } | 294 } |
| OLD | NEW |