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:convert' show UTF8; | 7 import 'dart:convert' show UTF8; |
8 import 'dart:math'; | 8 import 'dart:math'; |
9 import 'dart:typed_data' show Uint8List; | 9 import 'dart:typed_data' show Uint8List; |
10 | 10 |
| 11 import 'package:kernel/ast.dart' as kernel show Location, Source; |
| 12 |
11 import 'line_column_provider.dart'; | 13 import 'line_column_provider.dart'; |
12 | 14 |
13 /** | 15 /** |
14 * Represents a file of source code. The content can be either a [String] or | 16 * Represents a file of source code. The content can be either a [String] or |
15 * a UTF-8 encoded [List<int>] of bytes. | 17 * a UTF-8 encoded [List<int>] of bytes. |
16 */ | 18 */ |
17 abstract class SourceFile implements LineColumnProvider { | 19 abstract class SourceFile implements LineColumnProvider { |
18 /// The absolute URI of the source file. | 20 /// The absolute URI of the source file. |
19 Uri get uri; | 21 Uri get uri; |
20 | 22 |
| 23 kernel.Source cachedKernelSource; |
| 24 |
| 25 kernel.Source get kernelSource { |
| 26 return cachedKernelSource ??= new kernel.Source( |
| 27 lineStarts, slowUtf8ZeroTerminatedBytes())..cachedText = slowText(); |
| 28 } |
| 29 |
21 /// The name of the file. | 30 /// The name of the file. |
22 /// | 31 /// |
23 /// This is [uri], maybe relativized to a more human-readable form. | 32 /// This is [uri], maybe relativized to a more human-readable form. |
24 String get filename => uri.toString(); | 33 String get filename => uri.toString(); |
25 | 34 |
26 /** The text content of the file represented as a String. */ | 35 /** The text content of the file represented as a String. */ |
27 String slowText(); | 36 String slowText(); |
28 | 37 |
29 /** | 38 /** |
30 * The content of the file represented as a UTF-8 encoded [List<int>], | 39 * The content of the file represented as a UTF-8 encoded [List<int>], |
(...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
79 } | 88 } |
80 starts.add(text.length + 1); // One additional line start at the end. | 89 starts.add(text.length + 1); // One additional line start at the end. |
81 return starts; | 90 return starts; |
82 } | 91 } |
83 | 92 |
84 /** | 93 /** |
85 * Returns the line number for the offset [position] in the string | 94 * Returns the line number for the offset [position] in the string |
86 * representation of this source file. | 95 * representation of this source file. |
87 */ | 96 */ |
88 int getLine(int position) { | 97 int getLine(int position) { |
89 List<int> starts = lineStarts; | 98 new kernel.Source(lineStarts, null).getLocation(null, position).line; |
90 if (position < 0 || starts.last <= position) { | |
91 throw 'bad position #$position in file $filename with ' | |
92 'length ${length}.'; | |
93 } | |
94 int first = 0; | |
95 int count = starts.length; | |
96 while (count > 1) { | |
97 int step = count ~/ 2; | |
98 int middle = first + step; | |
99 int lineStart = starts[middle]; | |
100 if (position < lineStart) { | |
101 count = step; | |
102 } else { | |
103 first = middle; | |
104 count -= step; | |
105 } | |
106 } | |
107 return first; | |
108 } | 99 } |
109 | 100 |
110 /** | 101 /** |
111 * Returns the column number for the offset [position] in the string | 102 * Returns the column number for the offset [position] in the string |
112 * representation of this source file. | 103 * representation of this source file. |
113 */ | 104 */ |
114 int getColumn(int line, int position) { | 105 int getColumn(int line, int position) { |
115 return position - lineStarts[line]; | 106 return position - lineStarts[line]; |
116 } | 107 } |
117 | 108 |
(...skipping 16 matching lines...) Expand all Loading... |
134 String getLocationMessage(String message, int start, int end, | 125 String getLocationMessage(String message, int start, int end, |
135 {bool includeSourceLine: true, String colorize(String text)}) { | 126 {bool includeSourceLine: true, String colorize(String text)}) { |
136 if (colorize == null) { | 127 if (colorize == null) { |
137 colorize = (text) => text; | 128 colorize = (text) => text; |
138 } | 129 } |
139 if (end > length) { | 130 if (end > length) { |
140 start = length - 1; | 131 start = length - 1; |
141 end = length; | 132 end = length; |
142 } | 133 } |
143 | 134 |
144 int lineStart = getLine(start); | 135 kernel.Location startLocation = kernelSource.getLocation(null, start); |
145 int columnStart = getColumn(lineStart, start); | 136 kernel.Location endLocation = kernelSource.getLocation(null, end); |
146 int lineEnd = getLine(end); | 137 int lineStart = startLocation.line - 1; |
147 int columnEnd = getColumn(lineEnd, end); | 138 int columnStart = startLocation.column - 1; |
| 139 int lineEnd = startLocation.line - 1; |
| 140 int columnEnd = endLocation.column - 1; |
148 | 141 |
149 StringBuffer buf = new StringBuffer('${filename}:'); | 142 StringBuffer buf = new StringBuffer('${filename}:'); |
150 if (start != end || start != 0) { | 143 if (start != end || start != 0) { |
151 // Line/column info is relevant. | 144 // Line/column info is relevant. |
152 buf.write('${lineStart + 1}:${columnStart + 1}:'); | 145 buf.write('${lineStart + 1}:${columnStart + 1}:'); |
153 } | 146 } |
154 buf.write('\n$message\n'); | 147 buf.write('\n$message\n'); |
155 | 148 |
156 if (start != end && includeSourceLine) { | 149 if (start != end && includeSourceLine) { |
157 if (lineStart == lineEnd) { | 150 if (lineStart == lineEnd) { |
158 String textLine = getLineText(lineStart); | 151 String textLine = kernelSource.getTextLine(startLocation.line); |
159 | 152 |
160 int toColumn = min(columnStart + (end - start), textLine.length); | 153 int toColumn = min(columnStart + (end - start), textLine.length); |
161 buf.write(textLine.substring(0, columnStart)); | 154 buf.write(textLine.substring(0, columnStart)); |
162 buf.write(colorize(textLine.substring(columnStart, toColumn))); | 155 buf.write(colorize(textLine.substring(columnStart, toColumn))); |
163 buf.write(textLine.substring(toColumn)); | 156 buf.writeln(textLine.substring(toColumn)); |
164 | 157 |
165 int i = 0; | 158 int i = 0; |
166 for (; i < columnStart; i++) { | 159 for (; i < columnStart; i++) { |
167 buf.write(' '); | 160 buf.write(' '); |
168 } | 161 } |
169 | 162 |
170 for (; i < toColumn; i++) { | 163 for (; i < toColumn; i++) { |
171 buf.write(colorize('^')); | 164 buf.write(colorize('^')); |
172 } | 165 } |
173 } else { | 166 } else { |
174 for (int line = lineStart; line <= lineEnd; line++) { | 167 for (int line = lineStart; line <= lineEnd; line++) { |
175 String textLine = getLineText(line); | 168 String textLine = kernelSource.getTextLine(line + 1); |
176 if (line == lineStart) { | 169 if (line == lineStart) { |
177 buf.write(textLine.substring(0, columnStart)); | 170 buf.write(textLine.substring(0, columnStart)); |
178 buf.write(colorize(textLine.substring(columnStart))); | 171 buf.writeln(colorize(textLine.substring(columnStart))); |
179 } else if (line == lineEnd) { | 172 } else if (line == lineEnd) { |
180 buf.write(colorize(textLine.substring(0, columnEnd))); | 173 buf.write(colorize(textLine.substring(0, columnEnd))); |
181 buf.write(textLine.substring(columnEnd)); | 174 buf.writeln(textLine.substring(columnEnd)); |
182 } else { | 175 } else { |
183 buf.write(colorize(textLine)); | 176 buf.writeln(colorize(textLine)); |
184 } | 177 } |
185 } | 178 } |
186 } | 179 } |
187 } | 180 } |
188 | 181 |
189 return buf.toString(); | 182 return buf.toString(); |
190 } | 183 } |
191 | 184 |
192 int get lines => lineStarts.length - 1; | 185 int get lines => lineStarts.length - 1; |
193 | 186 |
194 /// Returns the text of line at the 0-based [index] within this source file. | 187 /// Returns the text of line at the 0-based [index] within this source |
195 String getLineText(int index) { | 188 /// file. The returned line always ends with `"\n"`. |
196 // +1 for 0-indexing, +1 again to avoid the last line of the file | 189 String getLineText(int index) => kernelSource.getTextLine(line) + "\n"; |
197 if ((index + 2) < lineStarts.length) { | |
198 return slowSubstring(lineStarts[index], lineStarts[index + 1]); | |
199 } else if ((index + 1) < lineStarts.length) { | |
200 return '${slowSubstring(lineStarts[index], length)}\n'; | |
201 } else { | |
202 throw new ArgumentError("Line index $index is out of bounds."); | |
203 } | |
204 } | |
205 } | 190 } |
206 | 191 |
207 List<int> _zeroTerminateIfNecessary(List<int> bytes) { | 192 List<int> _zeroTerminateIfNecessary(List<int> bytes) { |
208 if (bytes.length > 0 && bytes.last == 0) return bytes; | 193 if (bytes.length > 0 && bytes.last == 0) return bytes; |
209 List<int> result = new Uint8List(bytes.length + 1); | 194 List<int> result = new Uint8List(bytes.length + 1); |
210 result.setRange(0, bytes.length, bytes); | 195 result.setRange(0, bytes.length, bytes); |
211 result[result.length - 1] = 0; | 196 result[result.length - 1] = 0; |
212 return result; | 197 return result; |
213 } | 198 } |
214 | 199 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
285 set length(int v) {} | 270 set length(int v) {} |
286 | 271 |
287 String slowText() => text; | 272 String slowText() => text; |
288 | 273 |
289 List<int> slowUtf8ZeroTerminatedBytes() { | 274 List<int> slowUtf8ZeroTerminatedBytes() { |
290 return _zeroTerminateIfNecessary(UTF8.encode(text)); | 275 return _zeroTerminateIfNecessary(UTF8.encode(text)); |
291 } | 276 } |
292 | 277 |
293 String slowSubstring(int start, int end) => text.substring(start, end); | 278 String slowSubstring(int start, int end) => text.substring(start, end); |
294 } | 279 } |
OLD | NEW |