| 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/optype.dart'; | 16 import 'package:analysis_server/src/services/completion/optype.dart'; |
| 17 import 'package:analysis_server/src/services/search/search_engine.dart'; | 17 import 'package:analysis_server/src/services/search/search_engine.dart'; |
| 18 import 'package:analyzer/dart/element/element.dart'; | 18 import 'package:analyzer/dart/element/element.dart'; |
| 19 import 'package:analyzer/dart/element/type.dart'; | 19 import 'package:analyzer/dart/element/type.dart'; |
| 20 import 'package:analyzer/file_system/file_system.dart'; | 20 import 'package:analyzer/file_system/file_system.dart'; |
| 21 import 'package:analyzer/src/context/context.dart' | 21 import 'package:analyzer/src/context/context.dart' |
| 22 show AnalysisFutureHelper, AnalysisContextImpl; | 22 show AnalysisFutureHelper, AnalysisContextImpl; |
| 23 import 'package:analyzer/src/generated/ast.dart'; | 23 import 'package:analyzer/src/generated/ast.dart'; |
| 24 import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl; | 24 import 'package:analyzer/src/generated/engine.dart' hide AnalysisContextImpl; |
| 25 import 'package:analyzer/src/generated/source.dart'; | 25 import 'package:analyzer/src/generated/source.dart'; |
| 26 import 'package:analyzer/src/task/dart.dart'; | 26 import 'package:analyzer/src/task/dart.dart'; |
| 27 import 'package:analyzer/task/dart.dart'; | 27 import 'package:analyzer/task/dart.dart'; |
| 28 import 'package:analyzer/src/generated/scanner.dart'; |
| 28 | 29 |
| 29 /** | 30 /** |
| 30 * [DartCompletionManager] determines if a completion request is Dart specific | 31 * [DartCompletionManager] determines if a completion request is Dart specific |
| 31 * and forwards those requests to all [DartCompletionContributor]s. | 32 * and forwards those requests to all [DartCompletionContributor]s. |
| 32 */ | 33 */ |
| 33 class DartCompletionManager implements CompletionContributor { | 34 class DartCompletionManager implements CompletionContributor { |
| 34 @override | 35 @override |
| 35 Future<List<CompletionSuggestion>> computeSuggestions( | 36 Future<List<CompletionSuggestion>> computeSuggestions( |
| 36 CompletionRequest request) async { | 37 CompletionRequest request) async { |
| 37 if (!AnalysisEngine.isDartFileName(request.source.shortName)) { | 38 if (!AnalysisEngine.isDartFileName(request.source.shortName)) { |
| 38 return EMPTY_LIST; | 39 return EMPTY_LIST; |
| 39 } | 40 } |
| 40 | 41 |
| 41 DartCompletionRequestImpl dartRequest = | 42 DartCompletionRequestImpl dartRequest = |
| 42 await DartCompletionRequestImpl.from(request); | 43 await DartCompletionRequestImpl.from(request); |
| 44 ReplacementRange range = |
| 45 new ReplacementRange.compute(dartRequest.offset, dartRequest.target); |
| 46 (request as CompletionRequestImpl) |
| 47 ..replacementOffset = range.offset |
| 48 ..replacementLength = range.length; |
| 43 | 49 |
| 44 // Don't suggest in comments. | 50 // Don't suggest in comments. |
| 45 if (dartRequest.target.isCommentText) { | 51 if (dartRequest.target.isCommentText) { |
| 46 return EMPTY_LIST; | 52 return EMPTY_LIST; |
| 47 } | 53 } |
| 48 | 54 |
| 49 // Request Dart specific completions from each contributor | 55 // Request Dart specific completions from each contributor |
| 50 Map<String, CompletionSuggestion> suggestionMap = | 56 Map<String, CompletionSuggestion> suggestionMap = |
| 51 <String, CompletionSuggestion>{}; | 57 <String, CompletionSuggestion>{}; |
| 52 for (DartCompletionContributor contributor | 58 for (DartCompletionContributor contributor |
| (...skipping 193 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 246 } | 252 } |
| 247 } else { | 253 } else { |
| 248 libSource = source; | 254 libSource = source; |
| 249 } | 255 } |
| 250 | 256 |
| 251 // Most (all?) contributors need declarations in scope to be resolved | 257 // Most (all?) contributors need declarations in scope to be resolved |
| 252 if (libSource != null) { | 258 if (libSource != null) { |
| 253 unit = await new AnalysisFutureHelper<CompilationUnit>(context, | 259 unit = await new AnalysisFutureHelper<CompilationUnit>(context, |
| 254 new LibrarySpecificUnit(libSource, source), RESOLVED_UNIT3) | 260 new LibrarySpecificUnit(libSource, source), RESOLVED_UNIT3) |
| 255 .computeAsync(); | 261 .computeAsync(); |
| 256 | |
| 257 } | 262 } |
| 258 | 263 |
| 259 DartCompletionRequestImpl dartRequest = new DartCompletionRequestImpl._( | 264 DartCompletionRequestImpl dartRequest = new DartCompletionRequestImpl._( |
| 260 request.context, | 265 request.context, |
| 261 request.resourceProvider, | 266 request.resourceProvider, |
| 262 request.searchEngine, | 267 request.searchEngine, |
| 263 libSource, | 268 libSource, |
| 264 request.source, | 269 request.source, |
| 265 request.offset, | 270 request.offset, |
| 266 unit); | 271 unit); |
| 267 | 272 |
| 268 // Resolve the expression in which the completion occurs | 273 // Resolve the expression in which the completion occurs |
| 269 // to properly determine if identifiers should be suggested | 274 // to properly determine if identifiers should be suggested |
| 270 // rather than invocations. | 275 // rather than invocations. |
| 271 if (dartRequest.target.maybeFunctionalArgument()) { | 276 if (dartRequest.target.maybeFunctionalArgument()) { |
| 272 AstNode node = dartRequest.target.containingNode.parent; | 277 AstNode node = dartRequest.target.containingNode.parent; |
| 273 if (node is Expression) { | 278 if (node is Expression) { |
| 274 await dartRequest.resolveExpression(node); | 279 await dartRequest.resolveExpression(node); |
| 275 } | 280 } |
| 276 } | 281 } |
| 277 | 282 |
| 278 return dartRequest; | 283 return dartRequest; |
| 279 } | 284 } |
| 280 } | 285 } |
| 286 |
| 287 /** |
| 288 * Utility class for computing the code completion replacement range |
| 289 */ |
| 290 class ReplacementRange { |
| 291 int offset; |
| 292 int length; |
| 293 |
| 294 ReplacementRange(this.offset, this.length); |
| 295 |
| 296 factory ReplacementRange.compute(int requestOffset, CompletionTarget target) { |
| 297 bool isKeywordOrIdentifier(Token token) => |
| 298 token.type == TokenType.KEYWORD || token.type == TokenType.IDENTIFIER; |
| 299 |
| 300 //TODO(danrubel) Ideally this needs to be pushed down into the contributors |
| 301 // but that implies that each suggestion can have a different |
| 302 // replacement offsent/length which would mean an API change |
| 303 |
| 304 var entity = target.entity; |
| 305 Token token = entity is AstNode ? entity.beginToken : entity; |
| 306 if (token != null && requestOffset < token.offset) { |
| 307 token = token.previous; |
| 308 } |
| 309 if (token != null) { |
| 310 if (requestOffset == token.offset && !isKeywordOrIdentifier(token)) { |
| 311 // If the insertion point is at the beginning of the current token |
| 312 // and the current token is not an identifier |
| 313 // then check the previous token to see if it should be replaced |
| 314 token = token.previous; |
| 315 } |
| 316 if (token != null && isKeywordOrIdentifier(token)) { |
| 317 if (token.offset <= requestOffset && requestOffset <= token.end) { |
| 318 // Replacement range for typical identifier completion |
| 319 return new ReplacementRange(token.offset, token.length); |
| 320 } |
| 321 } |
| 322 if (token is StringToken) { |
| 323 SimpleStringLiteral uri = new SimpleStringLiteral(token, token.lexeme); |
| 324 Token previous = token.previous; |
| 325 if (previous is KeywordToken) { |
| 326 Keyword keyword = previous.keyword; |
| 327 if (keyword == Keyword.IMPORT || |
| 328 keyword == Keyword.EXPORT || |
| 329 keyword == Keyword.PART) { |
| 330 int start = uri.contentsOffset; |
| 331 var end = uri.contentsEnd; |
| 332 if (start <= requestOffset && requestOffset <= end) { |
| 333 // Replacement range for import URI |
| 334 return new ReplacementRange(start, end - start); |
| 335 } |
| 336 } |
| 337 } |
| 338 } |
| 339 } |
| 340 return new ReplacementRange(requestOffset, 0); |
| 341 } |
| 342 } |
| OLD | NEW |