| 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.src.task.html; | 5 library analyzer.src.task.html; |
| 6 | 6 |
| 7 import 'dart:collection'; | 7 import 'dart:collection'; |
| 8 | 8 |
| 9 import 'package:analyzer/error/error.dart'; |
| 10 import 'package:analyzer/exception/exception.dart'; |
| 9 import 'package:analyzer/src/context/cache.dart'; | 11 import 'package:analyzer/src/context/cache.dart'; |
| 10 import 'package:analyzer/src/generated/engine.dart' hide AnalysisTask; | 12 import 'package:analyzer/src/dart/scanner/scanner.dart'; |
| 11 import 'package:analyzer/src/generated/error.dart'; | 13 import 'package:analyzer/src/generated/engine.dart'; |
| 12 import 'package:analyzer/src/generated/java_engine.dart'; | 14 import 'package:analyzer/src/generated/java_engine.dart'; |
| 13 import 'package:analyzer/src/generated/scanner.dart'; | |
| 14 import 'package:analyzer/src/generated/source.dart'; | 15 import 'package:analyzer/src/generated/source.dart'; |
| 15 import 'package:analyzer/src/plugin/engine_plugin.dart'; | 16 import 'package:analyzer/src/plugin/engine_plugin.dart'; |
| 16 import 'package:analyzer/src/task/general.dart'; | 17 import 'package:analyzer/src/task/general.dart'; |
| 17 import 'package:analyzer/task/dart.dart'; | 18 import 'package:analyzer/task/dart.dart'; |
| 18 import 'package:analyzer/task/general.dart'; | 19 import 'package:analyzer/task/general.dart'; |
| 19 import 'package:analyzer/task/html.dart'; | 20 import 'package:analyzer/task/html.dart'; |
| 20 import 'package:analyzer/task/model.dart'; | 21 import 'package:analyzer/task/model.dart'; |
| 21 import 'package:html/dom.dart'; | 22 import 'package:html/dom.dart'; |
| 22 import 'package:html/parser.dart'; | 23 import 'package:html/parser.dart'; |
| 23 import 'package:source_span/source_span.dart'; | 24 import 'package:source_span/source_span.dart'; |
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 67 @override | 68 @override |
| 68 String get encoding => source.encoding; | 69 String get encoding => source.encoding; |
| 69 | 70 |
| 70 @override | 71 @override |
| 71 String get fullName => source.fullName; | 72 String get fullName => source.fullName; |
| 72 | 73 |
| 73 @override | 74 @override |
| 74 bool get isInSystemLibrary => source.isInSystemLibrary; | 75 bool get isInSystemLibrary => source.isInSystemLibrary; |
| 75 | 76 |
| 76 @override | 77 @override |
| 78 Source get librarySource => source; |
| 79 |
| 80 @override |
| 77 int get modificationStamp => source.modificationStamp; | 81 int get modificationStamp => source.modificationStamp; |
| 78 | 82 |
| 79 @override | 83 @override |
| 80 String get shortName => source.shortName; | 84 String get shortName => source.shortName; |
| 81 | 85 |
| 82 @override | 86 @override |
| 83 Uri get uri => throw new StateError('uri not supported for scripts'); | 87 Uri get uri => source.uri |
| 88 .replace(queryParameters: {'offset': fragments[0].offset.toString()}); |
| 84 | 89 |
| 85 @override | 90 @override |
| 86 UriKind get uriKind => | 91 UriKind get uriKind => |
| 87 throw new StateError('uriKind not supported for scripts'); | 92 throw new StateError('uriKind not supported for scripts'); |
| 88 | 93 |
| 89 @override | 94 @override |
| 90 bool exists() => source.exists(); | 95 bool exists() => source.exists(); |
| 91 | |
| 92 @override | |
| 93 Uri resolveRelativeUri(Uri relativeUri) => | |
| 94 throw new StateError('resolveRelativeUri not supported for scripts'); | |
| 95 } | 96 } |
| 96 | 97 |
| 97 /** | 98 /** |
| 98 * A task that looks for Dart scripts in an HTML file and computes both the Dart | 99 * A task that looks for Dart scripts in an HTML file and computes both the Dart |
| 99 * libraries that are referenced by those scripts and the embedded Dart scripts. | 100 * libraries that are referenced by those scripts and the embedded Dart scripts. |
| 100 */ | 101 */ |
| 101 class DartScriptsTask extends SourceBasedAnalysisTask { | 102 class DartScriptsTask extends SourceBasedAnalysisTask { |
| 102 /** | 103 /** |
| 103 * The name of the [HTML_DOCUMENT] input. | 104 * The name of the [HTML_DOCUMENT] input. |
| 104 */ | 105 */ |
| (...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 libraries.isEmpty ? Source.EMPTY_LIST : libraries; | 164 libraries.isEmpty ? Source.EMPTY_LIST : libraries; |
| 164 outputs[DART_SCRIPTS] = | 165 outputs[DART_SCRIPTS] = |
| 165 inlineScripts.isEmpty ? DartScript.EMPTY_LIST : inlineScripts; | 166 inlineScripts.isEmpty ? DartScript.EMPTY_LIST : inlineScripts; |
| 166 } | 167 } |
| 167 | 168 |
| 168 /** | 169 /** |
| 169 * Return a map from the names of the inputs of this kind of task to the task | 170 * Return a map from the names of the inputs of this kind of task to the task |
| 170 * input descriptors describing those inputs for a task with the | 171 * input descriptors describing those inputs for a task with the |
| 171 * given [target]. | 172 * given [target]. |
| 172 */ | 173 */ |
| 173 static Map<String, TaskInput> buildInputs(Source target) { | 174 static Map<String, TaskInput> buildInputs(AnalysisTarget target) { |
| 174 return <String, TaskInput>{DOCUMENT_INPUT: HTML_DOCUMENT.of(target)}; | 175 return <String, TaskInput>{DOCUMENT_INPUT: HTML_DOCUMENT.of(target)}; |
| 175 } | 176 } |
| 176 | 177 |
| 177 /** | 178 /** |
| 178 * Create a [DartScriptsTask] based on the given [target] in the given | 179 * Create a [DartScriptsTask] based on the given [target] in the given |
| 179 * [context]. | 180 * [context]. |
| 180 */ | 181 */ |
| 181 static DartScriptsTask createTask( | 182 static DartScriptsTask createTask( |
| 182 AnalysisContext context, AnalysisTarget target) { | 183 AnalysisContext context, AnalysisTarget target) { |
| 183 return new DartScriptsTask(context, target); | 184 return new DartScriptsTask(context, target); |
| (...skipping 50 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 234 // Record outputs. | 235 // Record outputs. |
| 235 // | 236 // |
| 236 outputs[HTML_ERRORS] = AnalysisError.mergeLists(errorLists); | 237 outputs[HTML_ERRORS] = AnalysisError.mergeLists(errorLists); |
| 237 } | 238 } |
| 238 | 239 |
| 239 /** | 240 /** |
| 240 * Return a map from the names of the inputs of this kind of task to the task | 241 * Return a map from the names of the inputs of this kind of task to the task |
| 241 * input descriptors describing those inputs for a task with the | 242 * input descriptors describing those inputs for a task with the |
| 242 * given [target]. | 243 * given [target]. |
| 243 */ | 244 */ |
| 244 static Map<String, TaskInput> buildInputs(Source target) { | 245 static Map<String, TaskInput> buildInputs(AnalysisTarget target) { |
| 245 EnginePlugin enginePlugin = AnalysisEngine.instance.enginePlugin; | 246 EnginePlugin enginePlugin = AnalysisEngine.instance.enginePlugin; |
| 246 Map<String, TaskInput> inputs = <String, TaskInput>{ | 247 Map<String, TaskInput> inputs = <String, TaskInput>{ |
| 247 DART_ERRORS_INPUT: DART_SCRIPTS.of(target).toListOf(DART_ERRORS) | 248 DART_ERRORS_INPUT: DART_SCRIPTS.of(target).toListOf(DART_ERRORS) |
| 248 }; | 249 }; |
| 249 for (ResultDescriptor result in enginePlugin.htmlErrors) { | 250 for (ResultDescriptor result in enginePlugin.htmlErrors) { |
| 250 String inputName = result.name + INPUT_SUFFIX; | 251 String inputName = result.name + INPUT_SUFFIX; |
| 251 inputs[inputName] = result.of(target); | 252 inputs[inputName] = result.of(target); |
| 252 } | 253 } |
| 253 return inputs; | 254 return inputs; |
| 254 } | 255 } |
| (...skipping 11 matching lines...) Expand all Loading... |
| 266 /** | 267 /** |
| 267 * A task that scans the content of a file, producing a set of Dart tokens. | 268 * A task that scans the content of a file, producing a set of Dart tokens. |
| 268 */ | 269 */ |
| 269 class ParseHtmlTask extends SourceBasedAnalysisTask { | 270 class ParseHtmlTask extends SourceBasedAnalysisTask { |
| 270 /** | 271 /** |
| 271 * The name of the input whose value is the content of the file. | 272 * The name of the input whose value is the content of the file. |
| 272 */ | 273 */ |
| 273 static const String CONTENT_INPUT_NAME = 'CONTENT_INPUT_NAME'; | 274 static const String CONTENT_INPUT_NAME = 'CONTENT_INPUT_NAME'; |
| 274 | 275 |
| 275 /** | 276 /** |
| 277 * The name of the input whose value is the modification time of the file. |
| 278 */ |
| 279 static const String MODIFICATION_TIME_INPUT = 'MODIFICATION_TIME_INPUT'; |
| 280 |
| 281 /** |
| 276 * The task descriptor describing this kind of task. | 282 * The task descriptor describing this kind of task. |
| 277 */ | 283 */ |
| 278 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( | 284 static final TaskDescriptor DESCRIPTOR = new TaskDescriptor( |
| 279 'ParseHtmlTask', | 285 'ParseHtmlTask', |
| 280 createTask, | 286 createTask, |
| 281 buildInputs, | 287 buildInputs, |
| 282 <ResultDescriptor>[HTML_DOCUMENT, HTML_DOCUMENT_ERRORS, LINE_INFO]); | 288 <ResultDescriptor>[HTML_DOCUMENT, HTML_DOCUMENT_ERRORS, LINE_INFO], |
| 289 suitabilityFor: suitabilityFor); |
| 283 | 290 |
| 284 /** | 291 /** |
| 285 * Initialize a newly created task to access the content of the source | 292 * Initialize a newly created task to access the content of the source |
| 286 * associated with the given [target] in the given [context]. | 293 * associated with the given [target] in the given [context]. |
| 287 */ | 294 */ |
| 288 ParseHtmlTask(InternalAnalysisContext context, AnalysisTarget target) | 295 ParseHtmlTask(InternalAnalysisContext context, AnalysisTarget target) |
| 289 : super(context, target); | 296 : super(context, target); |
| 290 | 297 |
| 291 @override | 298 @override |
| 292 TaskDescriptor get descriptor => DESCRIPTOR; | 299 TaskDescriptor get descriptor => DESCRIPTOR; |
| 293 | 300 |
| 294 @override | 301 @override |
| 295 void internalPerform() { | 302 void internalPerform() { |
| 296 String content = getRequiredInput(CONTENT_INPUT_NAME); | 303 String content = getRequiredInput(CONTENT_INPUT_NAME); |
| 297 | 304 |
| 298 if (context.getModificationStamp(target.source) < 0) { | 305 int modificationTime = getRequiredInput(MODIFICATION_TIME_INPUT); |
| 306 if (modificationTime < 0) { |
| 299 String message = 'Content could not be read'; | 307 String message = 'Content could not be read'; |
| 300 if (context is InternalAnalysisContext) { | 308 if (context is InternalAnalysisContext) { |
| 301 CacheEntry entry = | 309 CacheEntry entry = |
| 302 (context as InternalAnalysisContext).getCacheEntry(target); | 310 (context as InternalAnalysisContext).getCacheEntry(target); |
| 303 CaughtException exception = entry.exception; | 311 CaughtException exception = entry.exception; |
| 304 if (exception != null) { | 312 if (exception != null) { |
| 305 message = exception.toString(); | 313 message = exception.toString(); |
| 306 } | 314 } |
| 307 } | 315 } |
| 308 | 316 |
| 309 outputs[HTML_DOCUMENT] = new Document(); | 317 outputs[HTML_DOCUMENT] = new Document(); |
| 310 outputs[HTML_DOCUMENT_ERRORS] = <AnalysisError>[ | 318 outputs[HTML_DOCUMENT_ERRORS] = <AnalysisError>[ |
| 311 new AnalysisError( | 319 new AnalysisError( |
| 312 target.source, 0, 0, ScannerErrorCode.UNABLE_GET_CONTENT, [message]) | 320 target.source, 0, 0, ScannerErrorCode.UNABLE_GET_CONTENT, [message]) |
| 313 ]; | 321 ]; |
| 314 outputs[LINE_INFO] = new LineInfo(<int>[0]); | 322 outputs[LINE_INFO] = new LineInfo(<int>[0]); |
| 315 } else { | 323 } else { |
| 316 HtmlParser parser = new HtmlParser(content, generateSpans: true); | 324 HtmlParser parser = new HtmlParser(content, |
| 325 generateSpans: true, lowercaseAttrName: false); |
| 317 parser.compatMode = 'quirks'; | 326 parser.compatMode = 'quirks'; |
| 318 Document document = parser.parse(); | 327 Document document = parser.parse(); |
| 319 // | 328 // |
| 320 // Convert errors. | 329 // Convert errors. |
| 321 // | 330 // |
| 322 List<ParseError> parseErrors = parser.errors; | |
| 323 List<AnalysisError> errors = <AnalysisError>[]; | 331 List<AnalysisError> errors = <AnalysisError>[]; |
| 324 for (ParseError parseError in parseErrors) { | 332 // TODO(scheglov) https://github.com/dart-lang/sdk/issues/24643 |
| 325 if (parseError.errorCode == 'expected-doctype-but-got-start-tag') { | 333 // List<ParseError> parseErrors = parser.errors; |
| 326 continue; | 334 // for (ParseError parseError in parseErrors) { |
| 327 } | 335 // if (parseError.errorCode == 'expected-doctype-but-got-start-tag') { |
| 328 SourceSpan span = parseError.span; | 336 // continue; |
| 329 errors.add(new AnalysisError(target.source, span.start.offset, | 337 // } |
| 330 span.length, HtmlErrorCode.PARSE_ERROR, [parseError.message])); | 338 // SourceSpan span = parseError.span; |
| 331 } | 339 // errors.add(new AnalysisError(target.source, span.start.offset, |
| 340 // span.length, HtmlErrorCode.PARSE_ERROR, [parseError.message])); |
| 341 // } |
| 332 // | 342 // |
| 333 // Record outputs. | 343 // Record outputs. |
| 334 // | 344 // |
| 335 outputs[HTML_DOCUMENT] = document; | 345 outputs[HTML_DOCUMENT] = document; |
| 336 outputs[HTML_DOCUMENT_ERRORS] = errors; | 346 outputs[HTML_DOCUMENT_ERRORS] = errors; |
| 337 outputs[LINE_INFO] = _computeLineInfo(content); | 347 outputs[LINE_INFO] = _computeLineInfo(content); |
| 338 } | 348 } |
| 339 } | 349 } |
| 340 | 350 |
| 341 /** | 351 /** |
| 342 * Return a map from the names of the inputs of this kind of task to the task | 352 * Return a map from the names of the inputs of this kind of task to the task |
| 343 * input descriptors describing those inputs for a task with the given | 353 * input descriptors describing those inputs for a task with the given |
| 344 * [source]. | 354 * [source]. |
| 345 */ | 355 */ |
| 346 static Map<String, TaskInput> buildInputs(Source source) { | 356 static Map<String, TaskInput> buildInputs(AnalysisTarget source) { |
| 347 return <String, TaskInput>{CONTENT_INPUT_NAME: CONTENT.of(source)}; | 357 return <String, TaskInput>{ |
| 358 CONTENT_INPUT_NAME: CONTENT.of(source), |
| 359 MODIFICATION_TIME_INPUT: MODIFICATION_TIME.of(source) |
| 360 }; |
| 348 } | 361 } |
| 349 | 362 |
| 350 /** | 363 /** |
| 351 * Create a [ParseHtmlTask] based on the given [target] in the given [context]
. | 364 * Create a [ParseHtmlTask] based on the given [target] in the given [context]
. |
| 352 */ | 365 */ |
| 353 static ParseHtmlTask createTask( | 366 static ParseHtmlTask createTask( |
| 354 AnalysisContext context, AnalysisTarget target) { | 367 AnalysisContext context, AnalysisTarget target) { |
| 355 return new ParseHtmlTask(context, target); | 368 return new ParseHtmlTask(context, target); |
| 356 } | 369 } |
| 357 | 370 |
| 358 /** | 371 /** |
| 372 * Return an indication of how suitable this task is for the given [target]. |
| 373 */ |
| 374 static TaskSuitability suitabilityFor(AnalysisTarget target) { |
| 375 if (target is Source) { |
| 376 String name = target.shortName; |
| 377 if (name.endsWith(AnalysisEngine.SUFFIX_HTML) || |
| 378 name.endsWith(AnalysisEngine.SUFFIX_HTM)) { |
| 379 return TaskSuitability.HIGHEST; |
| 380 } |
| 381 } |
| 382 return TaskSuitability.NONE; |
| 383 } |
| 384 |
| 385 /** |
| 359 * Compute [LineInfo] for the given [content]. | 386 * Compute [LineInfo] for the given [content]. |
| 360 */ | 387 */ |
| 361 static LineInfo _computeLineInfo(String content) { | 388 static LineInfo _computeLineInfo(String content) { |
| 362 List<int> lineStarts = <int>[0]; | 389 List<int> lineStarts = StringUtilities.computeLineStarts(content); |
| 363 for (int index = 0; index < content.length; index++) { | |
| 364 if (content.codeUnitAt(index) == 0x0A) { | |
| 365 lineStarts.add(index + 1); | |
| 366 } | |
| 367 } | |
| 368 return new LineInfo(lineStarts); | 390 return new LineInfo(lineStarts); |
| 369 } | 391 } |
| 370 } | 392 } |
| 371 | 393 |
| 372 /** | 394 /** |
| 373 * A fragment of a [DartScript]. | 395 * A fragment of a [DartScript]. |
| 374 */ | 396 */ |
| 375 class ScriptFragment { | 397 class ScriptFragment { |
| 376 /** | 398 /** |
| 377 * The offset of the first character of the fragment, relative to the start of | 399 * The offset of the first character of the fragment, relative to the start of |
| (...skipping 16 matching lines...) Expand all Loading... |
| 394 * The content of the fragment. | 416 * The content of the fragment. |
| 395 */ | 417 */ |
| 396 final String content; | 418 final String content; |
| 397 | 419 |
| 398 /** | 420 /** |
| 399 * Initialize a newly created script fragment to have the given [offset] and | 421 * Initialize a newly created script fragment to have the given [offset] and |
| 400 * [content]. | 422 * [content]. |
| 401 */ | 423 */ |
| 402 ScriptFragment(this.offset, this.line, this.column, this.content); | 424 ScriptFragment(this.offset, this.line, this.column, this.content); |
| 403 } | 425 } |
| OLD | NEW |