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 services.completion.dart.manager; | 5 library services.completion.dart.manager; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 | 8 |
9 import 'package:analysis_server/plugin/protocol/protocol.dart'; | 9 import 'package:analysis_server/plugin/protocol/protocol.dart'; |
10 import 'package:analysis_server/src/provisional/completion/completion_core.dart' | 10 import 'package:analysis_server/src/provisional/completion/completion_core.dart' |
11 show CompletionContributor, CompletionRequest; | 11 show CompletionContributor, CompletionRequest; |
12 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.
dart'; | 12 import 'package:analysis_server/src/provisional/completion/dart/completion_dart.
dart'; |
13 import 'package:analysis_server/src/provisional/completion/dart/completion_plugi
n.dart'; | 13 import 'package:analysis_server/src/provisional/completion/dart/completion_plugi
n.dart'; |
14 import 'package:analysis_server/src/provisional/completion/dart/completion_targe
t.dart'; | 14 import 'package:analysis_server/src/provisional/completion/dart/completion_targe
t.dart'; |
15 import 'package:analysis_server/src/services/completion/completion_core.dart'; | 15 import 'package:analysis_server/src/services/completion/completion_core.dart'; |
16 import 'package:analysis_server/src/services/completion/completion_performance.d
art'; | 16 import 'package:analysis_server/src/services/completion/completion_performance.d
art'; |
17 import 'package:analysis_server/src/services/completion/dart/common_usage_sorter
.dart'; | 17 import 'package:analysis_server/src/services/completion/dart/common_usage_sorter
.dart'; |
18 import 'package:analysis_server/src/services/completion/dart/contribution_sorter
.dart'; | 18 import 'package:analysis_server/src/services/completion/dart/contribution_sorter
.dart'; |
19 import 'package:analysis_server/src/services/completion/dart/optype.dart'; | 19 import 'package:analysis_server/src/services/completion/dart/optype.dart'; |
20 import 'package:analysis_server/src/services/search/search_engine.dart'; | 20 import 'package:analysis_server/src/services/search/search_engine.dart'; |
21 import 'package:analyzer/dart/element/element.dart'; | 21 import 'package:analyzer/dart/element/element.dart'; |
22 import 'package:analyzer/dart/element/type.dart'; | 22 import 'package:analyzer/dart/element/type.dart'; |
23 import 'package:analyzer/file_system/file_system.dart'; | 23 import 'package:analyzer/file_system/file_system.dart'; |
24 import 'package:analyzer/src/context/context.dart' | 24 import 'package:analyzer/src/context/context.dart' |
25 show AnalysisFutureHelper, AnalysisContextImpl; | 25 show AnalysisFutureHelper, AnalysisContextImpl; |
26 import 'package:analyzer/src/generated/ast.dart'; | 26 import 'package:analyzer/src/generated/ast.dart'; |
27 import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl; | 27 import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl; |
| 28 import 'package:analyzer/src/generated/java_engine.dart'; |
28 import 'package:analyzer/src/generated/scanner.dart'; | 29 import 'package:analyzer/src/generated/scanner.dart'; |
29 import 'package:analyzer/src/generated/source.dart'; | 30 import 'package:analyzer/src/generated/source.dart'; |
30 import 'package:analyzer/src/task/dart.dart'; | 31 import 'package:analyzer/src/task/dart.dart'; |
31 import 'package:analyzer/task/dart.dart'; | 32 import 'package:analyzer/task/dart.dart'; |
| 33 import 'package:analyzer/task/model.dart'; |
32 | 34 |
33 /** | 35 /** |
34 * [DartCompletionManager] determines if a completion request is Dart specific | 36 * [DartCompletionManager] determines if a completion request is Dart specific |
35 * and forwards those requests to all [DartCompletionContributor]s. | 37 * and forwards those requests to all [DartCompletionContributor]s. |
36 */ | 38 */ |
37 class DartCompletionManager implements CompletionContributor { | 39 class DartCompletionManager implements CompletionContributor { |
38 /** | 40 /** |
39 * The [contributionSorter] is a long-lived object that isn't allowed | 41 * The [contributionSorter] is a long-lived object that isn't allowed |
40 * to maintain state between calls to [DartContributionSorter#sort(...)]. | 42 * to maintain state between calls to [DartContributionSorter#sort(...)]. |
41 */ | 43 */ |
42 static DartContributionSorter contributionSorter = new CommonUsageSorter(); | 44 static DartContributionSorter contributionSorter = new CommonUsageSorter(); |
43 | 45 |
44 @override | 46 @override |
45 Future<List<CompletionSuggestion>> computeSuggestions( | 47 Future<List<CompletionSuggestion>> computeSuggestions( |
46 CompletionRequest request) async { | 48 CompletionRequest request) async { |
| 49 request.checkAborted(); |
47 if (!AnalysisEngine.isDartFileName(request.source.shortName)) { | 50 if (!AnalysisEngine.isDartFileName(request.source.shortName)) { |
48 return EMPTY_LIST; | 51 return EMPTY_LIST; |
49 } | 52 } |
50 | 53 |
51 CompletionPerformance performance = | 54 CompletionPerformance performance = |
52 (request as CompletionRequestImpl).performance; | 55 (request as CompletionRequestImpl).performance; |
53 const BUILD_REQUEST_TAG = 'build DartCompletionRequestImpl'; | |
54 performance.logStartTime(BUILD_REQUEST_TAG); | |
55 DartCompletionRequestImpl dartRequest = | 56 DartCompletionRequestImpl dartRequest = |
56 await DartCompletionRequestImpl.from(request); | 57 await DartCompletionRequestImpl.from(request); |
57 performance.logElapseTime(BUILD_REQUEST_TAG); | |
58 | 58 |
59 // Don't suggest in comments. | 59 // Don't suggest in comments. |
60 if (dartRequest.target.isCommentText) { | 60 if (dartRequest.target.isCommentText) { |
61 return EMPTY_LIST; | 61 return EMPTY_LIST; |
62 } | 62 } |
63 | 63 |
64 ReplacementRange range = | 64 ReplacementRange range = |
65 new ReplacementRange.compute(dartRequest.offset, dartRequest.target); | 65 new ReplacementRange.compute(dartRequest.offset, dartRequest.target); |
66 (request as CompletionRequestImpl) | 66 (request as CompletionRequestImpl) |
67 ..replacementOffset = range.offset | 67 ..replacementOffset = range.offset |
68 ..replacementLength = range.length; | 68 ..replacementLength = range.length; |
69 | 69 |
70 // Request Dart specific completions from each contributor | 70 // Request Dart specific completions from each contributor |
71 Map<String, CompletionSuggestion> suggestionMap = | 71 Map<String, CompletionSuggestion> suggestionMap = |
72 <String, CompletionSuggestion>{}; | 72 <String, CompletionSuggestion>{}; |
73 for (DartCompletionContributor contributor | 73 for (DartCompletionContributor contributor |
74 in dartCompletionPlugin.contributors) { | 74 in dartCompletionPlugin.contributors) { |
75 String contributorTag = | 75 String contributorTag = |
76 'DartCompletionManager - ${contributor.runtimeType}'; | 76 'DartCompletionManager - ${contributor.runtimeType}'; |
77 performance.logStartTime(contributorTag); | 77 performance.logStartTime(contributorTag); |
78 List<CompletionSuggestion> contributorSuggestions = | 78 List<CompletionSuggestion> contributorSuggestions = |
79 await contributor.computeSuggestions(dartRequest); | 79 await contributor.computeSuggestions(dartRequest); |
80 performance.logElapseTime(contributorTag); | 80 performance.logElapseTime(contributorTag); |
| 81 request.checkAborted(); |
81 | 82 |
82 for (CompletionSuggestion newSuggestion in contributorSuggestions) { | 83 for (CompletionSuggestion newSuggestion in contributorSuggestions) { |
83 var oldSuggestion = suggestionMap.putIfAbsent( | 84 var oldSuggestion = suggestionMap.putIfAbsent( |
84 newSuggestion.completion, () => newSuggestion); | 85 newSuggestion.completion, () => newSuggestion); |
85 if (newSuggestion != oldSuggestion && | 86 if (newSuggestion != oldSuggestion && |
86 newSuggestion.relevance > oldSuggestion.relevance) { | 87 newSuggestion.relevance > oldSuggestion.relevance) { |
87 suggestionMap[newSuggestion.completion] = newSuggestion; | 88 suggestionMap[newSuggestion.completion] = newSuggestion; |
88 } | 89 } |
89 } | 90 } |
90 } | 91 } |
91 | 92 |
92 // Adjust suggestion relevance before returning | 93 // Adjust suggestion relevance before returning |
93 List<CompletionSuggestion> suggestions = suggestionMap.values.toList(); | 94 List<CompletionSuggestion> suggestions = suggestionMap.values.toList(); |
94 const SORT_TAG = 'DartCompletionManager - sort'; | 95 const SORT_TAG = 'DartCompletionManager - sort'; |
95 performance.logStartTime(SORT_TAG); | 96 performance.logStartTime(SORT_TAG); |
96 await contributionSorter.sort(dartRequest, suggestions); | 97 await contributionSorter.sort(dartRequest, suggestions); |
97 performance.logElapseTime(SORT_TAG); | 98 performance.logElapseTime(SORT_TAG); |
| 99 request.checkAborted(); |
98 return suggestions; | 100 return suggestions; |
99 } | 101 } |
100 } | 102 } |
101 | 103 |
102 /** | 104 /** |
103 * The information about a requested list of completions within a Dart file. | 105 * The information about a requested list of completions within a Dart file. |
104 */ | 106 */ |
105 class DartCompletionRequestImpl implements DartCompletionRequest { | 107 class DartCompletionRequestImpl implements DartCompletionRequest { |
106 @override | 108 @override |
107 final AnalysisContext context; | 109 final AnalysisContext context; |
(...skipping 24 matching lines...) Expand all Loading... |
132 */ | 134 */ |
133 LibraryElement _coreLib; | 135 LibraryElement _coreLib; |
134 | 136 |
135 /** | 137 /** |
136 * The [DartType] for Object in dart:core | 138 * The [DartType] for Object in dart:core |
137 */ | 139 */ |
138 InterfaceType _objectType; | 140 InterfaceType _objectType; |
139 | 141 |
140 OpType _opType; | 142 OpType _opType; |
141 | 143 |
| 144 final CompletionRequest _originalRequest; |
| 145 |
142 final CompletionPerformance performance; | 146 final CompletionPerformance performance; |
143 | 147 |
144 DartCompletionRequestImpl._( | 148 DartCompletionRequestImpl._( |
145 this.context, | 149 this.context, |
146 this.resourceProvider, | 150 this.resourceProvider, |
147 this.searchEngine, | 151 this.searchEngine, |
148 this.librarySource, | 152 this.librarySource, |
149 this.source, | 153 this.source, |
150 this.offset, | 154 this.offset, |
151 CompilationUnit unit, | 155 CompilationUnit unit, |
| 156 this._originalRequest, |
152 this.performance) { | 157 this.performance) { |
153 _updateTargets(unit); | 158 _updateTargets(unit); |
154 } | 159 } |
155 | 160 |
156 @override | 161 @override |
157 LibraryElement get coreLib { | 162 LibraryElement get coreLib { |
158 if (_coreLib == null) { | 163 if (_coreLib == null) { |
159 Source coreUri = context.sourceFactory.forUri('dart:core'); | 164 Source coreUri = context.sourceFactory.forUri('dart:core'); |
160 _coreLib = context.computeLibraryElement(coreUri); | 165 _coreLib = context.computeLibraryElement(coreUri); |
161 } | 166 } |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
193 return _objectType; | 198 return _objectType; |
194 } | 199 } |
195 | 200 |
196 OpType get opType { | 201 OpType get opType { |
197 if (_opType == null) { | 202 if (_opType == null) { |
198 _opType = new OpType.forCompletion(target, offset); | 203 _opType = new OpType.forCompletion(target, offset); |
199 } | 204 } |
200 return _opType; | 205 return _opType; |
201 } | 206 } |
202 | 207 |
| 208 /** |
| 209 * Throw [AbortCompletion] if the completion request has been aborted. |
| 210 */ |
| 211 void checkAborted() { |
| 212 _originalRequest.checkAborted(); |
| 213 } |
| 214 |
203 // For internal use only | 215 // For internal use only |
204 @override | 216 @override |
205 Future<List<Directive>> resolveDirectives() async { | 217 Future<List<Directive>> resolveDirectives() async { |
| 218 checkAborted(); |
| 219 |
206 CompilationUnit libUnit; | 220 CompilationUnit libUnit; |
207 if (librarySource != null) { | 221 if (librarySource != null) { |
208 // TODO(danrubel) only resolve the directives | 222 // TODO(danrubel) only resolve the directives |
209 const RESOLVE_DIRECTIVES_TAG = 'resolve directives'; | 223 libUnit = await _computeAsync( |
210 performance.logStartTime(RESOLVE_DIRECTIVES_TAG); | 224 this, |
211 libUnit = await new AnalysisFutureHelper<CompilationUnit>( | 225 new LibrarySpecificUnit(librarySource, librarySource), |
212 context, | 226 RESOLVED_UNIT3, |
213 new LibrarySpecificUnit(librarySource, librarySource), | 227 performance, |
214 RESOLVED_UNIT3) | 228 'resolve directives'); |
215 .computeAsync(); | |
216 performance.logElapseTime(RESOLVE_DIRECTIVES_TAG); | |
217 } | 229 } |
218 return libUnit?.directives; | 230 return libUnit?.directives; |
219 } | 231 } |
220 | 232 |
221 @override | 233 @override |
222 Future resolveExpression(Expression expression) async { | 234 Future resolveExpression(Expression expression) async { |
| 235 checkAborted(); |
| 236 |
223 // Return immediately if the expression has already been resolved | 237 // Return immediately if the expression has already been resolved |
224 if (expression.propagatedType != null) { | 238 if (expression.propagatedType != null) { |
225 return; | 239 return; |
226 } | 240 } |
227 | 241 |
228 // Gracefully degrade if librarySource cannot be determined | 242 // Gracefully degrade if librarySource cannot be determined |
229 if (librarySource == null) { | 243 if (librarySource == null) { |
230 return; | 244 return; |
231 } | 245 } |
232 | 246 |
233 // Resolve declarations in the target unit | 247 // Resolve declarations in the target unit |
234 // TODO(danrubel) resolve the expression or containing method | 248 // TODO(danrubel) resolve the expression or containing method |
235 // rather than the entire complilation unit | 249 // rather than the entire complilation unit |
236 const RESOLVE_EXPRESSION_TAG = 'resolve expression'; | 250 CompilationUnit resolvedUnit = await _computeAsync( |
237 performance.logStartTime(RESOLVE_EXPRESSION_TAG); | 251 this, |
238 CompilationUnit resolvedUnit = | 252 new LibrarySpecificUnit(librarySource, source), |
239 await new AnalysisFutureHelper<CompilationUnit>(context, | 253 RESOLVED_UNIT, |
240 new LibrarySpecificUnit(librarySource, source), RESOLVED_UNIT) | 254 performance, |
241 .computeAsync(); | 255 'resolve expression'); |
242 performance.logElapseTime(RESOLVE_EXPRESSION_TAG); | |
243 | 256 |
244 // TODO(danrubel) determine if the underlying source has been modified | 257 // TODO(danrubel) determine if the underlying source has been modified |
245 // in a way that invalidates the completion request | 258 // in a way that invalidates the completion request |
246 // and return null | 259 // and return null |
247 | 260 |
248 // Gracefully degrade if unit cannot be resolved | 261 // Gracefully degrade if unit cannot be resolved |
249 if (resolvedUnit == null) { | 262 if (resolvedUnit == null) { |
250 return; | 263 return; |
251 } | 264 } |
252 | 265 |
(...skipping 25 matching lines...) Expand all Loading... |
278 } | 291 } |
279 if (node is PrefixedIdentifier) { | 292 if (node is PrefixedIdentifier) { |
280 if (identical(node.identifier, target.entity)) { | 293 if (identical(node.identifier, target.entity)) { |
281 dotTarget = node.prefix; | 294 dotTarget = node.prefix; |
282 } | 295 } |
283 } | 296 } |
284 } | 297 } |
285 | 298 |
286 /** | 299 /** |
287 * Return a [Future] that completes with a newly created completion request | 300 * Return a [Future] that completes with a newly created completion request |
288 * based on the given [request]. | 301 * based on the given [request]. This method will throw [AbortCompletion] |
| 302 * if the completion request has been aborted. |
289 */ | 303 */ |
290 static Future<DartCompletionRequest> from(CompletionRequest request) async { | 304 static Future<DartCompletionRequest> from(CompletionRequest request) async { |
| 305 request.checkAborted(); |
291 CompletionPerformance performance = | 306 CompletionPerformance performance = |
292 (request as CompletionRequestImpl).performance; | 307 (request as CompletionRequestImpl).performance; |
293 const BUILD_REQUEST_TAG = 'build DartCompletionRequest'; | 308 const BUILD_REQUEST_TAG = 'build DartCompletionRequest'; |
294 performance.logStartTime(BUILD_REQUEST_TAG); | 309 performance.logStartTime(BUILD_REQUEST_TAG); |
295 | 310 |
296 Source source = request.source; | 311 Source source = request.source; |
297 AnalysisContext context = request.context; | 312 AnalysisContext context = request.context; |
298 | 313 |
299 const PARSE_TAG = 'parse unit'; | 314 const PARSE_TAG = 'parse unit'; |
300 performance.logStartTime(PARSE_TAG); | 315 performance.logStartTime(PARSE_TAG); |
301 CompilationUnit unit = request.context.computeResult(source, PARSED_UNIT); | 316 CompilationUnit unit = request.context.computeResult(source, PARSED_UNIT); |
302 performance.logElapseTime(PARSE_TAG); | 317 performance.logElapseTime(PARSE_TAG); |
303 | 318 |
304 Source libSource; | 319 Source libSource; |
305 if (unit.directives.any((d) => d is PartOfDirective)) { | 320 if (unit.directives.any((d) => d is PartOfDirective)) { |
306 List<Source> libraries = context.getLibrariesContaining(source); | 321 List<Source> libraries = context.getLibrariesContaining(source); |
307 if (libraries.isNotEmpty) { | 322 if (libraries.isNotEmpty) { |
308 libSource = libraries[0]; | 323 libSource = libraries[0]; |
309 } | 324 } |
310 } else { | 325 } else { |
311 libSource = source; | 326 libSource = source; |
312 } | 327 } |
313 | 328 |
314 // Most (all?) contributors need declarations in scope to be resolved | 329 // Most (all?) contributors need declarations in scope to be resolved |
315 if (libSource != null) { | 330 if (libSource != null) { |
316 const RESOLVE_DECLARATIONS_TAG = 'resolve declarations'; | 331 unit = await _computeAsync( |
317 performance.logStartTime(RESOLVE_DECLARATIONS_TAG); | 332 request, |
318 unit = await new AnalysisFutureHelper<CompilationUnit>(context, | 333 new LibrarySpecificUnit(libSource, source), |
319 new LibrarySpecificUnit(libSource, source), RESOLVED_UNIT3) | 334 RESOLVED_UNIT3, |
320 .computeAsync(); | 335 performance, |
321 performance.logElapseTime(RESOLVE_DECLARATIONS_TAG); | 336 'resolve declarations'); |
322 } | 337 } |
323 | 338 |
324 DartCompletionRequestImpl dartRequest = new DartCompletionRequestImpl._( | 339 DartCompletionRequestImpl dartRequest = new DartCompletionRequestImpl._( |
325 request.context, | 340 request.context, |
326 request.resourceProvider, | 341 request.resourceProvider, |
327 request.searchEngine, | 342 request.searchEngine, |
328 libSource, | 343 libSource, |
329 request.source, | 344 request.source, |
330 request.offset, | 345 request.offset, |
331 unit, | 346 unit, |
| 347 request, |
332 performance); | 348 performance); |
333 | 349 |
334 // Resolve the expression in which the completion occurs | 350 // Resolve the expression in which the completion occurs |
335 // to properly determine if identifiers should be suggested | 351 // to properly determine if identifiers should be suggested |
336 // rather than invocations. | 352 // rather than invocations. |
337 if (dartRequest.target.maybeFunctionalArgument()) { | 353 if (dartRequest.target.maybeFunctionalArgument()) { |
338 AstNode node = dartRequest.target.containingNode.parent; | 354 AstNode node = dartRequest.target.containingNode.parent; |
339 if (node is Expression) { | 355 if (node is Expression) { |
340 const FUNCTIONAL_ARG_TAG = 'resolve expression for isFunctionalArg'; | 356 const FUNCTIONAL_ARG_TAG = 'resolve expression for isFunctionalArg'; |
341 performance.logStartTime(FUNCTIONAL_ARG_TAG); | 357 performance.logStartTime(FUNCTIONAL_ARG_TAG); |
342 await dartRequest.resolveExpression(node); | 358 await dartRequest.resolveExpression(node); |
343 performance.logElapseTime(FUNCTIONAL_ARG_TAG); | 359 performance.logElapseTime(FUNCTIONAL_ARG_TAG); |
| 360 dartRequest.checkAborted(); |
344 } | 361 } |
345 } | 362 } |
346 | 363 |
347 performance.logElapseTime(BUILD_REQUEST_TAG); | 364 performance.logElapseTime(BUILD_REQUEST_TAG); |
348 return dartRequest; | 365 return dartRequest; |
349 } | 366 } |
| 367 |
| 368 static Future _computeAsync(CompletionRequest request, AnalysisTarget target, |
| 369 ResultDescriptor descriptor, CompletionPerformance performance, String per
fTag) async { |
| 370 request.checkAborted(); |
| 371 performance.logStartTime(perfTag); |
| 372 var result; |
| 373 try { |
| 374 result = |
| 375 await new AnalysisFutureHelper(request.context, target, descriptor) |
| 376 .computeAsync(); |
| 377 } catch (e, s) { |
| 378 if (e is AnalysisNotScheduledError) { |
| 379 request.checkAborted(); |
| 380 } |
| 381 throw new AnalysisException( |
| 382 'failed to $perfTag', new CaughtException(e, s)); |
| 383 } |
| 384 request.checkAborted(); |
| 385 return result; |
| 386 } |
350 } | 387 } |
351 | 388 |
352 /** | 389 /** |
353 * Utility class for computing the code completion replacement range | 390 * Utility class for computing the code completion replacement range |
354 */ | 391 */ |
355 class ReplacementRange { | 392 class ReplacementRange { |
356 int offset; | 393 int offset; |
357 int length; | 394 int length; |
358 | 395 |
359 ReplacementRange(this.offset, this.length); | 396 ReplacementRange(this.offset, this.length); |
(...skipping 38 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
398 // Replacement range for import URI | 435 // Replacement range for import URI |
399 return new ReplacementRange(start, end - start); | 436 return new ReplacementRange(start, end - start); |
400 } | 437 } |
401 } | 438 } |
402 } | 439 } |
403 } | 440 } |
404 } | 441 } |
405 return new ReplacementRange(requestOffset, 0); | 442 return new ReplacementRange(requestOffset, 0); |
406 } | 443 } |
407 } | 444 } |
OLD | NEW |