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 |