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 |