OLD | NEW |
---|---|
1 // Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2015, 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 analyzer_cli.src.error_formatter; | 5 library analyzer_cli.src.error_formatter; |
6 | 6 |
7 import 'package:analyzer/error/error.dart'; | 7 import 'package:analyzer/error/error.dart'; |
8 import 'package:analyzer/src/generated/engine.dart'; | 8 import 'package:analyzer/src/generated/engine.dart'; |
9 import 'package:analyzer/src/generated/source.dart'; | 9 import 'package:analyzer/src/generated/source.dart'; |
10 import 'package:analyzer_cli/src/ansi.dart'; | 10 import 'package:analyzer_cli/src/ansi.dart'; |
11 import 'package:analyzer_cli/src/options.dart'; | 11 import 'package:analyzer_cli/src/options.dart'; |
12 import 'package:path/path.dart' as path; | 12 import 'package:path/path.dart' as path; |
13 | 13 |
14 /// Returns the given error's severity. | 14 /// Returns the given error's severity. |
15 ProcessedSeverity _identity(AnalysisError error) => | 15 ProcessedSeverity _severityIdentity(AnalysisError error) => |
16 new ProcessedSeverity(error.errorCode.errorSeverity); | 16 new ProcessedSeverity(error.errorCode.errorSeverity); |
17 | 17 |
18 String _pluralize(String word, int count) => count == 1 ? word : word + "s"; | 18 String _pluralize(String word, int count) => count == 1 ? word : word + "s"; |
19 | 19 |
20 /// Returns desired severity for the given [error] (or `null` if it's to be | 20 /// Returns desired severity for the given [error] (or `null` if it's to be |
21 /// suppressed). | 21 /// suppressed). |
22 typedef ProcessedSeverity _SeverityProcessor(AnalysisError error); | 22 typedef ProcessedSeverity SeverityProcessor(AnalysisError error); |
23 | 23 |
24 /// Analysis statistics counter. | 24 /// Analysis statistics counter. |
25 class AnalysisStats { | 25 class AnalysisStats { |
26 /// The total number of diagnostics sent to [formatErrors]. | 26 /// The total number of diagnostics sent to [formatErrors]. |
27 int unfilteredCount; | 27 int unfilteredCount = 0; |
28 | 28 |
29 int errorCount; | 29 int errorCount = 0; |
30 int hintCount; | 30 int hintCount = 0; |
31 int lintCount; | 31 int lintCount = 0; |
32 int warnCount; | 32 int warnCount = 0; |
33 | 33 |
34 AnalysisStats() { | 34 AnalysisStats(); |
35 init(); | |
36 } | |
37 | 35 |
38 /// The total number of diagnostics reported to the user. | 36 /// The total number of diagnostics reported to the user. |
39 int get filteredCount => errorCount + warnCount + hintCount + lintCount; | 37 int get filteredCount => errorCount + warnCount + hintCount + lintCount; |
40 | 38 |
41 /// (Re)set initial values. | |
42 void init() { | |
43 unfilteredCount = 0; | |
44 errorCount = 0; | |
45 hintCount = 0; | |
46 lintCount = 0; | |
47 warnCount = 0; | |
48 } | |
49 | |
50 /// Print statistics to [out]. | 39 /// Print statistics to [out]. |
51 void print(StringSink out) { | 40 void print(StringSink out) { |
52 var hasErrors = errorCount != 0; | 41 bool hasErrors = errorCount != 0; |
53 var hasWarns = warnCount != 0; | 42 bool hasWarns = warnCount != 0; |
54 var hasHints = hintCount != 0; | 43 bool hasHints = hintCount != 0; |
55 var hasLints = lintCount != 0; | 44 bool hasLints = lintCount != 0; |
56 bool hasContent = false; | 45 bool hasContent = false; |
57 if (hasErrors) { | 46 if (hasErrors) { |
58 out.write(errorCount); | 47 out.write(errorCount); |
59 out.write(' '); | 48 out.write(' '); |
60 out.write(_pluralize("error", errorCount)); | 49 out.write(_pluralize("error", errorCount)); |
61 hasContent = true; | 50 hasContent = true; |
62 } | 51 } |
63 if (hasWarns) { | 52 if (hasWarns) { |
64 if (hasContent) { | 53 if (hasContent) { |
65 if (!hasHints && !hasLints) { | 54 if (!hasHints && !hasLints) { |
66 out.write(' and '); | 55 out.write(' and '); |
67 } else { | 56 } else { |
68 out.write(", "); | 57 out.write(", "); |
69 } | 58 } |
70 } | 59 } |
71 out.write(warnCount); | 60 out.write(warnCount); |
72 out.write(' '); | 61 out.write(' '); |
73 out.write(_pluralize("warning", warnCount)); | 62 out.write(_pluralize("warning", warnCount)); |
74 hasContent = true; | 63 hasContent = true; |
75 } | 64 } |
65 if (hasLints) { | |
66 if (hasContent) { | |
67 out.write(hasHints ? ', ' : ' and '); | |
68 } | |
69 out.write(lintCount); | |
70 out.write(' '); | |
71 out.write(_pluralize("lint", lintCount)); | |
72 hasContent = true; | |
73 } | |
76 if (hasHints) { | 74 if (hasHints) { |
77 if (hasContent) { | 75 if (hasContent) { |
78 if (!hasLints) { | 76 out.write(" and "); |
79 out.write(' and '); | |
80 } else { | |
81 out.write(", "); | |
82 } | |
83 } | 77 } |
84 out.write(hintCount); | 78 out.write(hintCount); |
85 out.write(' '); | 79 out.write(' '); |
86 out.write(_pluralize("hint", hintCount)); | 80 out.write(_pluralize("hint", hintCount)); |
87 hasContent = true; | 81 hasContent = true; |
88 } | 82 } |
89 if (hasLints) { | |
90 if (hasContent) { | |
91 out.write(" and "); | |
92 } | |
93 out.write(lintCount); | |
94 out.write(' '); | |
95 out.write(_pluralize("lint", lintCount)); | |
96 hasContent = true; | |
97 } | |
98 if (hasContent) { | 83 if (hasContent) { |
99 out.writeln(" found."); | 84 out.writeln(" found."); |
100 } else { | 85 } else { |
101 out.writeln("No issues found!"); | 86 out.writeln("No issues found!"); |
102 } | 87 } |
103 } | 88 } |
104 } | 89 } |
105 | 90 |
106 /// Helper for formatting [AnalysisError]s. | 91 /// Helper for formatting [AnalysisError]s. |
107 /// | 92 /// |
108 /// The two format options are a user consumable format and a machine consumable | 93 /// The two format options are a user consumable format and a machine consumable |
109 /// format. | 94 /// format. |
110 class ErrorFormatter { | 95 abstract class ErrorFormatter { |
96 final StringSink out; | |
97 final CommandLineOptions options; | |
98 final AnalysisStats stats; | |
99 SeverityProcessor _severityProcessor; | |
100 | |
101 ErrorFormatter(this.out, this.options, this.stats, | |
102 {SeverityProcessor severityProcessor}) { | |
103 _severityProcessor = | |
104 severityProcessor == null ? _severityIdentity : severityProcessor; | |
105 } | |
106 | |
107 /// Compute the severity for this [error] or `null` if this error should be | |
108 /// filtered. | |
109 ErrorSeverity _computeSeverity(AnalysisError error) => | |
110 _severityProcessor(error)?.severity; | |
111 | |
112 void formatErrors(List<AnalysisErrorInfo> errorInfos) { | |
113 stats.unfilteredCount += errorInfos.length; | |
114 | |
115 List<AnalysisError> errors = new List<AnalysisError>(); | |
116 Map<AnalysisError, LineInfo> errorToLine = | |
117 new Map<AnalysisError, LineInfo>(); | |
118 for (AnalysisErrorInfo errorInfo in errorInfos) { | |
119 for (AnalysisError error in errorInfo.errors) { | |
120 if (_computeSeverity(error) != null) { | |
121 errors.add(error); | |
122 errorToLine[error] = errorInfo.lineInfo; | |
123 } | |
124 } | |
125 } | |
126 | |
127 for (AnalysisError error in errors) { | |
128 formatError(errorToLine, error); | |
129 } | |
130 } | |
131 | |
132 void formatError( | |
133 Map<AnalysisError, LineInfo> errorToLine, AnalysisError error); | |
134 | |
135 /// Call to write any batched up errors from [formatErrors]. | |
136 void flush(); | |
137 } | |
138 | |
139 class MachineErrorFormatter extends ErrorFormatter { | |
111 static final int _pipeCodeUnit = '|'.codeUnitAt(0); | 140 static final int _pipeCodeUnit = '|'.codeUnitAt(0); |
112 static final int _slashCodeUnit = '\\'.codeUnitAt(0); | 141 static final int _slashCodeUnit = '\\'.codeUnitAt(0); |
113 static final int _newline = '\n'.codeUnitAt(0); | 142 static final int _newline = '\n'.codeUnitAt(0); |
114 static final int _return = '\r'.codeUnitAt(0); | 143 static final int _return = '\r'.codeUnitAt(0); |
115 | 144 |
116 final StringSink out; | 145 MachineErrorFormatter( |
117 final CommandLineOptions options; | 146 StringSink out, CommandLineOptions options, AnalysisStats stats, |
118 final AnalysisStats stats; | 147 {SeverityProcessor severityProcessor}) |
119 | 148 : super(out, options, stats, severityProcessor: severityProcessor); |
120 final _SeverityProcessor processSeverity; | |
121 | |
122 AnsiLogger ansi; | |
123 | |
124 ErrorFormatter(this.out, this.options, this.stats, | |
125 [this.processSeverity = _identity]) { | |
126 ansi = new AnsiLogger(this.options.color); | |
127 } | |
128 | |
129 /// Compute the severity for this [error] or `null` if this error should be | |
130 /// filtered. | |
131 ErrorSeverity computeSeverity(AnalysisError error) => | |
132 processSeverity(error)?.severity; | |
133 | 149 |
134 void formatError( | 150 void formatError( |
135 Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { | 151 Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { |
136 Source source = error.source; | 152 Source source = error.source; |
137 LineInfo_Location location = errorToLine[error].getLocation(error.offset); | 153 LineInfo_Location location = errorToLine[error].getLocation(error.offset); |
138 int length = error.length; | 154 int length = error.length; |
139 | 155 |
140 ProcessedSeverity processedSeverity = processSeverity(error); | 156 ProcessedSeverity processedSeverity = _severityProcessor(error); |
141 ErrorSeverity severity = processedSeverity.severity; | 157 ErrorSeverity severity = processedSeverity.severity; |
142 | 158 |
143 if (options.machineFormat) { | 159 if (!processedSeverity.overridden) { |
144 if (!processedSeverity.overridden) { | 160 if (severity == ErrorSeverity.WARNING && options.warningsAreFatal) { |
145 if (severity == ErrorSeverity.WARNING && options.warningsAreFatal) { | 161 severity = ErrorSeverity.ERROR; |
146 severity = ErrorSeverity.ERROR; | |
147 } | |
148 } | |
149 out.write(severity); | |
150 out.write('|'); | |
151 out.write(error.errorCode.type); | |
152 out.write('|'); | |
153 out.write(error.errorCode.name); | |
154 out.write('|'); | |
155 out.write(escapeForMachineMode(source.fullName)); | |
156 out.write('|'); | |
157 out.write(location.lineNumber); | |
158 out.write('|'); | |
159 out.write(location.columnNumber); | |
160 out.write('|'); | |
161 out.write(length); | |
162 out.write('|'); | |
163 out.write(escapeForMachineMode(error.message)); | |
164 out.writeln(); | |
165 } else { | |
166 // Get display name. | |
167 String errorType = severity.displayName; | |
168 | |
169 // Translate INFOs into LINTS and HINTS. | |
170 if (severity == ErrorSeverity.INFO) { | |
171 if (error.errorCode.type == ErrorType.HINT || | |
172 error.errorCode.type == ErrorType.LINT) { | |
173 errorType = error.errorCode.type.displayName; | |
174 } | |
175 } | |
176 | |
177 final int errLength = ErrorSeverity.WARNING.displayName.length; | |
178 final int indent = errLength + 5; | |
179 | |
180 // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning | |
181 String message = error.message; | |
182 // Remove any terminating '.' from the end of the message. | |
183 if (message.endsWith('.')) { | |
184 message = message.substring(0, message.length - 1); | |
185 } | |
186 String issueColor = | |
187 (severity == ErrorSeverity.ERROR || severity == ErrorSeverity.WARNING) | |
188 ? ansi.red | |
189 : ''; | |
190 out.write(' $issueColor${errorType.padLeft(errLength)}${ansi.none} ' | |
191 '${ansi.bullet} ${ansi.bold}$message${ansi.none} '); | |
192 String sourceName; | |
193 if (source.uriKind == UriKind.DART_URI) { | |
194 sourceName = source.uri.toString(); | |
195 } else if (source.uriKind == UriKind.PACKAGE_URI) { | |
196 sourceName = _relative(source.fullName); | |
197 if (sourceName == source.fullName) { | |
198 // If we weren't able to shorten the path name, use the package: versi on. | |
199 sourceName = source.uri.toString(); | |
200 } | |
201 } else { | |
202 sourceName = _relative(source.fullName); | |
203 } | |
204 out.write('at $sourceName'); | |
205 out.write(':${location.lineNumber}:${location.columnNumber} '); | |
206 out.write('${ansi.bullet} ${error.errorCode.name.toLowerCase()}'); | |
207 out.writeln(); | |
208 | |
209 // If verbose, also print any associated correction. | |
210 if (options.verbose && error.correction != null) { | |
211 out.writeln('${' '.padLeft(indent)}${error.correction}'); | |
212 } | 162 } |
213 } | 163 } |
164 | |
165 if (severity == ErrorSeverity.ERROR) { | |
166 stats.errorCount++; | |
167 } else if (severity == ErrorSeverity.WARNING) { | |
168 // Only treat a warning as an error if it's not been set by a processor. | |
169 if (!processedSeverity.overridden && options.warningsAreFatal) { | |
170 stats.errorCount++; | |
171 } else { | |
172 stats.warnCount++; | |
173 } | |
174 } else if (error.errorCode.type == ErrorType.HINT) { | |
175 stats.hintCount++; | |
176 } else if (error.errorCode.type == ErrorType.LINT) { | |
177 stats.lintCount++; | |
178 } | |
179 | |
180 out.write(severity); | |
181 out.write('|'); | |
182 out.write(error.errorCode.type); | |
183 out.write('|'); | |
184 out.write(error.errorCode.name); | |
185 out.write('|'); | |
186 out.write(_escapeForMachineMode(source.fullName)); | |
187 out.write('|'); | |
188 out.write(location.lineNumber); | |
189 out.write('|'); | |
190 out.write(location.columnNumber); | |
191 out.write('|'); | |
192 out.write(length); | |
193 out.write('|'); | |
194 out.write(_escapeForMachineMode(error.message)); | |
195 out.writeln(); | |
214 } | 196 } |
215 | 197 |
216 void formatErrors(List<AnalysisErrorInfo> errorInfos) { | 198 static String _escapeForMachineMode(String input) { |
217 stats.unfilteredCount += errorInfos.length; | |
218 | |
219 var errors = new List<AnalysisError>(); | |
220 var errorToLine = new Map<AnalysisError, LineInfo>(); | |
221 for (AnalysisErrorInfo errorInfo in errorInfos) { | |
222 for (AnalysisError error in errorInfo.errors) { | |
223 if (computeSeverity(error) != null) { | |
224 errors.add(error); | |
225 errorToLine[error] = errorInfo.lineInfo; | |
226 } | |
227 } | |
228 } | |
229 // Sort errors. | |
230 errors.sort((AnalysisError error1, AnalysisError error2) { | |
231 // Severity. | |
232 ErrorSeverity severity1 = computeSeverity(error1); | |
233 ErrorSeverity severity2 = computeSeverity(error2); | |
234 int compare = severity2.compareTo(severity1); | |
235 if (compare != 0) { | |
236 return compare; | |
237 } | |
238 // Path. | |
239 compare = Comparable.compare(error1.source.fullName.toLowerCase(), | |
240 error2.source.fullName.toLowerCase()); | |
241 if (compare != 0) { | |
242 return compare; | |
243 } | |
244 // Offset. | |
245 return error1.offset - error2.offset; | |
246 }); | |
247 // Format errors. | |
248 for (AnalysisError error in errors) { | |
249 ProcessedSeverity processedSeverity = processSeverity(error); | |
250 ErrorSeverity severity = processedSeverity.severity; | |
251 if (severity == ErrorSeverity.ERROR) { | |
252 stats.errorCount++; | |
253 } else if (severity == ErrorSeverity.WARNING) { | |
254 // Only treat a warning as an error if it's not been set by a processor. | |
255 if (!processedSeverity.overridden && options.warningsAreFatal) { | |
256 stats.errorCount++; | |
257 } else { | |
258 stats.warnCount++; | |
259 } | |
260 } else if (error.errorCode.type == ErrorType.HINT) { | |
261 stats.hintCount++; | |
262 } else if (error.errorCode.type == ErrorType.LINT) { | |
263 stats.lintCount++; | |
264 } | |
265 formatError(errorToLine, error); | |
266 } | |
267 } | |
268 | |
269 static String escapeForMachineMode(String input) { | |
270 StringBuffer result = new StringBuffer(); | 199 StringBuffer result = new StringBuffer(); |
271 for (int c in input.codeUnits) { | 200 for (int c in input.codeUnits) { |
272 if (c == _newline) { | 201 if (c == _newline) { |
273 result.write(r'\n'); | 202 result.write(r'\n'); |
274 } else if (c == _return) { | 203 } else if (c == _return) { |
275 result.write(r'\r'); | 204 result.write(r'\r'); |
276 } else { | 205 } else { |
277 if (c == _slashCodeUnit || c == _pipeCodeUnit) { | 206 if (c == _slashCodeUnit || c == _pipeCodeUnit) { |
278 result.write('\\'); | 207 result.write('\\'); |
279 } | 208 } |
280 result.writeCharCode(c); | 209 result.writeCharCode(c); |
281 } | 210 } |
282 } | 211 } |
283 return result.toString(); | 212 return result.toString(); |
284 } | 213 } |
214 | |
215 void flush() {} | |
216 } | |
217 | |
218 class HumanErrorFormatter extends ErrorFormatter { | |
219 AnsiLogger ansi; | |
220 | |
221 List<CLIError> batchedErrors = []; | |
danrubel
2017/04/03 15:32:03
Is there a reason to batch errors first and then d
devoncarew
2017/04/03 15:36:10
Making it a Set would be better :) I'll do that.
| |
222 | |
223 HumanErrorFormatter( | |
224 StringSink out, CommandLineOptions options, AnalysisStats stats, | |
225 {SeverityProcessor severityProcessor}) | |
226 : super(out, options, stats, severityProcessor: severityProcessor) { | |
227 ansi = new AnsiLogger(this.options.color); | |
228 } | |
229 | |
230 void formatError( | |
231 Map<AnalysisError, LineInfo> errorToLine, AnalysisError error) { | |
232 Source source = error.source; | |
233 LineInfo_Location location = errorToLine[error].getLocation(error.offset); | |
234 | |
235 ProcessedSeverity processedSeverity = _severityProcessor(error); | |
236 ErrorSeverity severity = processedSeverity.severity; | |
237 | |
238 // Get display name; translate INFOs into LINTS and HINTS. | |
239 String errorType = severity.displayName; | |
240 if (severity == ErrorSeverity.INFO) { | |
241 if (error.errorCode.type == ErrorType.HINT || | |
242 error.errorCode.type == ErrorType.LINT) { | |
243 errorType = error.errorCode.type.displayName; | |
244 } | |
245 } | |
246 | |
247 // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning | |
248 String message = error.message; | |
249 // Remove any terminating '.' from the end of the message. | |
250 if (message.endsWith('.')) { | |
251 message = message.substring(0, message.length - 1); | |
252 } | |
253 String sourcePath; | |
254 if (source.uriKind == UriKind.DART_URI) { | |
255 sourcePath = source.uri.toString(); | |
256 } else if (source.uriKind == UriKind.PACKAGE_URI) { | |
257 sourcePath = _relative(source.fullName); | |
258 if (sourcePath == source.fullName) { | |
259 // If we weren't able to shorten the path name, use the package: version . | |
260 sourcePath = source.uri.toString(); | |
261 } | |
262 } else { | |
263 sourcePath = _relative(source.fullName); | |
264 } | |
265 | |
266 batchedErrors.add(new CLIError( | |
267 severity: errorType, | |
268 sourcePath: sourcePath, | |
269 offset: error.offset, | |
270 line: location.lineNumber, | |
271 column: location.columnNumber, | |
272 message: message, | |
273 errorCode: error.errorCode.name.toLowerCase(), | |
274 correction: error.correction, | |
275 )); | |
276 } | |
277 | |
278 void flush() { | |
279 // de-dup CLI errors | |
280 Set<CLIError> uniqueErrors = new Set.from(batchedErrors); | |
281 | |
282 // sort | |
283 List<CLIError> sortedErrors = uniqueErrors.toList()..sort(); | |
284 | |
285 // print | |
286 for (CLIError error in sortedErrors) { | |
287 if (error.isError) { | |
288 stats.errorCount++; | |
289 } else if (error.isWarning) { | |
290 stats.warnCount++; | |
291 } else if (error.isLint) { | |
292 stats.lintCount++; | |
293 } else if (error.isHint) { | |
294 stats.hintCount++; | |
295 } | |
296 | |
297 // warning • 'foo' is not a bar at lib/foo.dart:1:2 • foo_warning | |
298 String issueColor = (error.isError == ErrorSeverity.ERROR || | |
299 error.isWarning == ErrorSeverity.WARNING) | |
300 ? ansi.red | |
301 : ''; | |
302 out.write(' $issueColor${error.severity}${ansi.none} ' | |
303 '${ansi.bullet} ${ansi.bold}${error.message}${ansi.none} '); | |
304 out.write('at ${error.sourcePath}'); | |
305 out.write(':${error.line}:${error.column} '); | |
306 out.write('${ansi.bullet} ${error.errorCode}'); | |
307 out.writeln(); | |
308 | |
309 // If verbose, also print any associated correction. | |
310 if (options.verbose && error.correction != null) { | |
311 out.writeln( | |
312 '${' '.padLeft(error.severity.length + 2)}${error.correction}'); | |
313 } | |
314 } | |
315 | |
316 batchedErrors.clear(); | |
317 } | |
318 } | |
319 | |
320 final Map<String, int> _severityCompare = { | |
321 'error': 5, | |
322 'warning': 4, | |
323 'info': 3, | |
324 'lint': 2, | |
325 'hint': 1, | |
326 }; | |
327 | |
328 /// An [AnalysisError] with line and column information. | |
329 class CLIError implements Comparable<CLIError> { | |
330 final String severity; | |
331 final String sourcePath; | |
332 final int offset; | |
333 final int line; | |
334 final int column; | |
335 final String message; | |
336 final String errorCode; | |
337 final String correction; | |
338 | |
339 CLIError({ | |
340 this.severity, | |
341 this.sourcePath, | |
342 this.offset, | |
343 this.line, | |
344 this.column, | |
345 this.message, | |
346 this.errorCode, | |
347 this.correction, | |
348 }); | |
349 | |
350 bool get isError => severity == 'error'; | |
351 bool get isWarning => severity == 'warning'; | |
352 bool get isLint => severity == 'lint'; | |
353 bool get isHint => severity == 'hint'; | |
354 | |
355 @override | |
356 int get hashCode => | |
357 severity.hashCode ^ sourcePath.hashCode ^ errorCode.hashCode ^ offset; | |
358 | |
359 @override | |
360 bool operator ==(other) { | |
361 if (other is! CLIError) return false; | |
362 | |
363 return severity == other.severity && | |
364 sourcePath == other.sourcePath && | |
365 errorCode == other.errorCode && | |
366 offset == other.offset; | |
367 } | |
368 | |
369 @override | |
370 int compareTo(CLIError other) { | |
371 // severity | |
372 int compare = | |
373 _severityCompare[other.severity] - _severityCompare[this.severity]; | |
374 if (compare != 0) return compare; | |
375 | |
376 // path | |
377 compare = Comparable.compare( | |
378 this.sourcePath.toLowerCase(), other.sourcePath.toLowerCase()); | |
379 if (compare != 0) return compare; | |
380 | |
381 // offset | |
382 return this.offset - other.offset; | |
383 } | |
285 } | 384 } |
286 | 385 |
287 /// A severity with awareness of whether it was overridden by a processor. | 386 /// A severity with awareness of whether it was overridden by a processor. |
288 class ProcessedSeverity { | 387 class ProcessedSeverity { |
289 ErrorSeverity severity; | 388 final ErrorSeverity severity; |
290 bool overridden; | 389 final bool overridden; |
291 ProcessedSeverity(this.severity, [this.overridden = false]); | 390 ProcessedSeverity(this.severity, [this.overridden = false]); |
292 } | 391 } |
293 | 392 |
294 /// Given an absolute path, return a relative path if the file is contained in | 393 /// Given an absolute path, return a relative path if the file is contained in |
295 /// the current directory; return the original path otherwise. | 394 /// the current directory; return the original path otherwise. |
296 String _relative(String file) { | 395 String _relative(String file) { |
297 return file.startsWith(path.current) ? path.relative(file) : file; | 396 return file.startsWith(path.current) ? path.relative(file) : file; |
298 } | 397 } |
OLD | NEW |