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 |