| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library analyzer.src.task.html_work_manager; | |
| 6 | |
| 7 import 'dart:collection'; | |
| 8 | |
| 9 import 'package:analyzer/src/context/cache.dart'; | |
| 10 import 'package:analyzer/src/generated/engine.dart' | |
| 11 show | |
| 12 AnalysisEngine, | |
| 13 AnalysisErrorInfo, | |
| 14 AnalysisErrorInfoImpl, | |
| 15 AnalysisOptions, | |
| 16 CacheState, | |
| 17 InternalAnalysisContext; | |
| 18 import 'package:analyzer/src/generated/error.dart'; | |
| 19 import 'package:analyzer/src/generated/source.dart'; | |
| 20 import 'package:analyzer/src/generated/utilities_collection.dart'; | |
| 21 import 'package:analyzer/src/task/driver.dart'; | |
| 22 import 'package:analyzer/src/task/html.dart'; | |
| 23 import 'package:analyzer/task/general.dart'; | |
| 24 import 'package:analyzer/task/html.dart'; | |
| 25 import 'package:analyzer/task/model.dart'; | |
| 26 | |
| 27 /** | |
| 28 * The manager for HTML specific analysis. | |
| 29 */ | |
| 30 class HtmlWorkManager implements WorkManager { | |
| 31 /** | |
| 32 * The context for which work is being managed. | |
| 33 */ | |
| 34 final InternalAnalysisContext context; | |
| 35 | |
| 36 /** | |
| 37 * The [TargetedResult]s that should be computed with priority. | |
| 38 */ | |
| 39 final LinkedHashSet<TargetedResult> priorityResultQueue = | |
| 40 new LinkedHashSet<TargetedResult>(); | |
| 41 | |
| 42 /** | |
| 43 * The HTML sources. | |
| 44 */ | |
| 45 final LinkedHashSet<Source> sourceQueue = new LinkedHashSet<Source>(); | |
| 46 | |
| 47 /** | |
| 48 * Initialize a newly created manager. | |
| 49 */ | |
| 50 HtmlWorkManager(this.context) { | |
| 51 analysisCache.onResultInvalidated.listen(onResultInvalidated); | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * Returns the correctly typed result of `context.analysisCache`. | |
| 56 */ | |
| 57 AnalysisCache get analysisCache => context.analysisCache; | |
| 58 | |
| 59 /** | |
| 60 * The partition that contains analysis results that are not shared with other | |
| 61 * contexts. | |
| 62 */ | |
| 63 CachePartition get privateAnalysisCachePartition => | |
| 64 context.privateAnalysisCachePartition; | |
| 65 | |
| 66 /** | |
| 67 * Specifies that the client want the given [result] of the given [target] | |
| 68 * to be computed with priority. | |
| 69 */ | |
| 70 void addPriorityResult(AnalysisTarget target, ResultDescriptor result) { | |
| 71 priorityResultQueue.add(new TargetedResult(target, result)); | |
| 72 } | |
| 73 | |
| 74 /** | |
| 75 * Notifies the manager about changes in the explicit source list. | |
| 76 */ | |
| 77 void applyChange(List<Source> addedSources, List<Source> changedSources, | |
| 78 List<Source> removedSources) { | |
| 79 addedSources = addedSources.where(_isHtmlSource).toList(); | |
| 80 changedSources = changedSources.where(_isHtmlSource).toList(); | |
| 81 removedSources = removedSources.where(_isHtmlSource).toList(); | |
| 82 // source queue | |
| 83 sourceQueue.addAll(addedSources); | |
| 84 sourceQueue.addAll(changedSources); | |
| 85 sourceQueue.removeAll(removedSources); | |
| 86 } | |
| 87 | |
| 88 @override | |
| 89 void applyPriorityTargets(List<AnalysisTarget> targets) { | |
| 90 // Unschedule the old targets. | |
| 91 List<TargetedResult> resultsToUnschedule = <TargetedResult>[]; | |
| 92 for (TargetedResult result in priorityResultQueue) { | |
| 93 if (result.result == HTML_ERRORS) { | |
| 94 resultsToUnschedule.add(result); | |
| 95 } | |
| 96 } | |
| 97 priorityResultQueue.removeAll(resultsToUnschedule); | |
| 98 // Schedule new targets. | |
| 99 for (AnalysisTarget target in targets) { | |
| 100 if (_isHtmlSource(target)) { | |
| 101 addPriorityResult(target, HTML_ERRORS); | |
| 102 } | |
| 103 } | |
| 104 } | |
| 105 | |
| 106 /** | |
| 107 * Return an [AnalysisErrorInfo] containing the list of all of the errors and | |
| 108 * the line info associated with the given [source]. The list of errors will | |
| 109 * be empty if the source is not known to the context or if there are no | |
| 110 * errors in the source. The errors contained in the list can be incomplete. | |
| 111 */ | |
| 112 AnalysisErrorInfo getErrors(Source source) { | |
| 113 if (analysisCache.getState(source, HTML_ERRORS) == CacheState.VALID) { | |
| 114 List<AnalysisError> errors = analysisCache.getValue(source, HTML_ERRORS); | |
| 115 LineInfo lineInfo = analysisCache.getValue(source, LINE_INFO); | |
| 116 return new AnalysisErrorInfoImpl(errors, lineInfo); | |
| 117 } | |
| 118 List<AnalysisError> errors = <AnalysisError>[]; | |
| 119 errors.addAll(analysisCache.getValue(source, HTML_DOCUMENT_ERRORS)); | |
| 120 List<DartScript> scripts = analysisCache.getValue(source, DART_SCRIPTS); | |
| 121 for (DartScript script in scripts) { | |
| 122 errors.addAll(context.getErrors(script).errors); | |
| 123 } | |
| 124 LineInfo lineInfo = analysisCache.getValue(source, LINE_INFO); | |
| 125 return new AnalysisErrorInfoImpl(errors, lineInfo); | |
| 126 } | |
| 127 | |
| 128 @override | |
| 129 TargetedResult getNextResult() { | |
| 130 // Try to find a priority result to compute. | |
| 131 while (priorityResultQueue.isNotEmpty) { | |
| 132 TargetedResult result = priorityResultQueue.first; | |
| 133 if (!_needsComputing(result.target, result.result)) { | |
| 134 priorityResultQueue.remove(result); | |
| 135 continue; | |
| 136 } | |
| 137 return result; | |
| 138 } | |
| 139 // Try to find a new HTML file to analyze. | |
| 140 while (sourceQueue.isNotEmpty) { | |
| 141 Source htmlSource = sourceQueue.first; | |
| 142 // Maybe done with this library. | |
| 143 if (!_needsComputing(htmlSource, HTML_ERRORS)) { | |
| 144 sourceQueue.remove(htmlSource); | |
| 145 continue; | |
| 146 } | |
| 147 // Analyze this library. | |
| 148 return new TargetedResult(htmlSource, HTML_ERRORS); | |
| 149 } | |
| 150 // No results to compute. | |
| 151 return null; | |
| 152 } | |
| 153 | |
| 154 @override | |
| 155 WorkOrderPriority getNextResultPriority() { | |
| 156 if (priorityResultQueue.isNotEmpty) { | |
| 157 return WorkOrderPriority.PRIORITY; | |
| 158 } | |
| 159 if (sourceQueue.isNotEmpty) { | |
| 160 return WorkOrderPriority.NORMAL; | |
| 161 } | |
| 162 return WorkOrderPriority.NONE; | |
| 163 } | |
| 164 | |
| 165 /** | |
| 166 * Notifies the manager about analysis options changes. | |
| 167 */ | |
| 168 void onAnalysisOptionsChanged() { | |
| 169 _invalidateAllLocalResolutionInformation(false); | |
| 170 } | |
| 171 | |
| 172 /** | |
| 173 * Notifies the manager that a result has been invalidated. | |
| 174 */ | |
| 175 onResultInvalidated(InvalidatedResult event) { | |
| 176 ResultDescriptor descriptor = event.descriptor; | |
| 177 if (descriptor == HTML_ERRORS) { | |
| 178 sourceQueue.add(event.entry.target); | |
| 179 } else if (descriptor == DART_SCRIPTS) { | |
| 180 // TODO(brianwilkerson) Remove the scripts from the DartWorkManager's | |
| 181 // queues. | |
| 182 } | |
| 183 } | |
| 184 | |
| 185 /** | |
| 186 * Notifies the manager about [SourceFactory] changes. | |
| 187 */ | |
| 188 void onSourceFactoryChanged() { | |
| 189 _invalidateAllLocalResolutionInformation(true); | |
| 190 } | |
| 191 | |
| 192 @override | |
| 193 void resultsComputed( | |
| 194 AnalysisTarget target, Map<ResultDescriptor, dynamic> outputs) { | |
| 195 // Update notice. | |
| 196 if (_isHtmlSource(target)) { | |
| 197 bool shouldSetErrors = false; | |
| 198 outputs.forEach((ResultDescriptor descriptor, value) { | |
| 199 if (descriptor == HTML_ERRORS) { | |
| 200 shouldSetErrors = true; | |
| 201 } else if (descriptor == DART_SCRIPTS) { | |
| 202 // List<DartScript> scripts = value; | |
| 203 if (priorityResultQueue.contains(target)) { | |
| 204 // TODO(brianwilkerson) Add the scripts to the DartWorkManager's | |
| 205 // priority queue. | |
| 206 } else { | |
| 207 // TODO(brianwilkerson) Add the scripts to the DartWorkManager's | |
| 208 // library queue. | |
| 209 } | |
| 210 } | |
| 211 }); | |
| 212 if (shouldSetErrors) { | |
| 213 AnalysisErrorInfo info = getErrors(target); | |
| 214 context.getNotice(target).setErrors(info.errors, info.lineInfo); | |
| 215 } | |
| 216 } | |
| 217 } | |
| 218 | |
| 219 /** | |
| 220 * Invalidate all of the resolution results computed by this context. The flag | |
| 221 * [invalidateUris] should be `true` if the cached results of converting URIs | |
| 222 * to source files should also be invalidated. | |
| 223 */ | |
| 224 void _invalidateAllLocalResolutionInformation(bool invalidateUris) { | |
| 225 CachePartition partition = privateAnalysisCachePartition; | |
| 226 // Prepare targets and values to invalidate. | |
| 227 List<Source> htmlSources = <Source>[]; | |
| 228 List<DartScript> scriptTargets = <DartScript>[]; | |
| 229 MapIterator<AnalysisTarget, CacheEntry> iterator = partition.iterator(); | |
| 230 while (iterator.moveNext()) { | |
| 231 AnalysisTarget target = iterator.key; | |
| 232 if (_isHtmlSource(target)) { | |
| 233 htmlSources.add(target); | |
| 234 } | |
| 235 if (target is DartScript) { | |
| 236 scriptTargets.add(target); | |
| 237 } | |
| 238 } | |
| 239 // Invalidate targets and values. | |
| 240 scriptTargets.forEach(partition.remove); | |
| 241 for (Source htmlSource in htmlSources) { | |
| 242 CacheEntry entry = partition.get(htmlSource); | |
| 243 if (htmlSource != null) { | |
| 244 entry.setState(HTML_ERRORS, CacheState.INVALID); | |
| 245 if (invalidateUris) { | |
| 246 entry.setState(REFERENCED_LIBRARIES, CacheState.INVALID); | |
| 247 } | |
| 248 } | |
| 249 } | |
| 250 } | |
| 251 | |
| 252 /** | |
| 253 * Returns `true` if the given [result] of the given [target] needs | |
| 254 * computing, i.e. it is not in the valid and not in the error state. | |
| 255 */ | |
| 256 bool _needsComputing(AnalysisTarget target, ResultDescriptor result) { | |
| 257 CacheState state = analysisCache.getState(target, result); | |
| 258 return state != CacheState.VALID && state != CacheState.ERROR; | |
| 259 } | |
| 260 | |
| 261 /** | |
| 262 * Return `true` if the given target is an HTML source. | |
| 263 */ | |
| 264 static bool _isHtmlSource(AnalysisTarget target) { | |
| 265 return target is Source && AnalysisEngine.isHtmlFileName(target.fullName); | |
| 266 } | |
| 267 } | |
| OLD | NEW |