| OLD | NEW |
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 domain.completion; | 5 library domain.completion; |
| 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/analysis_server.dart'; | 10 import 'package:analysis_server/src/analysis_server.dart'; |
| 11 import 'package:analysis_server/src/constants.dart'; | 11 import 'package:analysis_server/src/constants.dart'; |
| 12 import 'package:analysis_server/src/provisional/completion/completion_core.dart' | 12 import 'package:analysis_server/src/provisional/completion/completion_core.dart'
; |
| 13 show CompletionRequest, CompletionResult; | |
| 14 import 'package:analysis_server/src/services/completion/completion_core.dart'; | 13 import 'package:analysis_server/src/services/completion/completion_core.dart'; |
| 15 import 'package:analysis_server/src/services/completion/completion_manager.dart'
; | 14 import 'package:analysis_server/src/services/completion/completion_performance.d
art'; |
| 16 import 'package:analyzer/src/generated/engine.dart'; | 15 import 'package:analyzer/src/generated/engine.dart'; |
| 17 import 'package:analyzer/src/generated/source.dart'; | 16 import 'package:analyzer/src/generated/source.dart'; |
| 18 | 17 |
| 19 export 'package:analysis_server/src/services/completion/completion_manager.dart' | |
| 20 show CompletionPerformance, CompletionRequest, OperationPerformance; | |
| 21 | |
| 22 /** | 18 /** |
| 23 * Instances of the class [CompletionDomainHandler] implement a [RequestHandler] | 19 * Instances of the class [CompletionDomainHandler] implement a [RequestHandler] |
| 24 * that handles requests in the search domain. | 20 * that handles requests in the search domain. |
| 25 */ | 21 */ |
| 26 class CompletionDomainHandler implements RequestHandler { | 22 class CompletionDomainHandler implements RequestHandler { |
| 27 /** | 23 /** |
| 28 * The maximum number of performance measurements to keep. | 24 * The maximum number of performance measurements to keep. |
| 29 */ | 25 */ |
| 30 static const int performanceListMaxLength = 50; | 26 static const int performanceListMaxLength = 50; |
| 31 | 27 |
| (...skipping 23 matching lines...) Expand all Loading... |
| 55 * Performance for the last priority change event. | 51 * Performance for the last priority change event. |
| 56 */ | 52 */ |
| 57 CompletionPerformance computeCachePerformance; | 53 CompletionPerformance computeCachePerformance; |
| 58 | 54 |
| 59 /** | 55 /** |
| 60 * Initialize a new request handler for the given [server]. | 56 * Initialize a new request handler for the given [server]. |
| 61 */ | 57 */ |
| 62 CompletionDomainHandler(this.server); | 58 CompletionDomainHandler(this.server); |
| 63 | 59 |
| 64 /** | 60 /** |
| 65 * Return the [CompletionManager] for the given [context] and [source], | 61 * Compute completion results for the given reqeust and append them to the str
eam. |
| 66 * creating a new manager or returning an existing manager as necessary. | 62 * Clients should not call this method directly as it is automatically called |
| 63 * when a client listens to the stream returned by [results]. |
| 64 * Subclasses should override this method, append at least one result |
| 65 * to the [controller], and close the controller stream once complete. |
| 67 */ | 66 */ |
| 68 CompletionManager completionManagerFor( | 67 Future<CompletionResult> computeSuggestions( |
| 69 AnalysisContext context, Source source) { | 68 CompletionRequestImpl request) async { |
| 70 return createCompletionManager(server, context, source); | 69 Iterable<CompletionContributor> newContributors = |
| 71 } | 70 server.serverPlugin.completionContributors; |
| 71 List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
| 72 | 72 |
| 73 CompletionManager createCompletionManager( | 73 const COMPUTE_SUGGESTIONS_TAG = 'computeSuggestions'; |
| 74 AnalysisServer server, AnalysisContext context, Source source) { | 74 performance.logStartTime(COMPUTE_SUGGESTIONS_TAG); |
| 75 return new CompletionManager.create(context, source, server.searchEngine, | 75 |
| 76 server.serverPlugin.completionContributors); | 76 for (CompletionContributor contributor in newContributors) { |
| 77 String contributorTag = 'computeSuggestions - ${contributor.runtimeType}'; |
| 78 performance.logStartTime(contributorTag); |
| 79 suggestions.addAll(await contributor.computeSuggestions(request)); |
| 80 performance.logElapseTime(contributorTag); |
| 81 } |
| 82 |
| 83 performance.logElapseTime(COMPUTE_SUGGESTIONS_TAG); |
| 84 |
| 85 // TODO (danrubel) if request is obsolete |
| 86 // (processAnalysisRequest returns false) |
| 87 // then send empty results |
| 88 |
| 89 return new CompletionResult( |
| 90 request.replacementOffset, request.replacementLength, suggestions); |
| 77 } | 91 } |
| 78 | 92 |
| 79 @override | 93 @override |
| 80 Response handleRequest(Request request) { | 94 Response handleRequest(Request request) { |
| 81 if (server.searchEngine == null) { | 95 if (server.searchEngine == null) { |
| 82 return new Response.noIndexGenerated(request); | 96 return new Response.noIndexGenerated(request); |
| 83 } | 97 } |
| 84 return runZoned(() { | 98 return runZoned(() { |
| 85 try { | 99 try { |
| 86 String requestName = request.method; | 100 String requestName = request.method; |
| 87 if (requestName == COMPLETION_GET_SUGGESTIONS) { | 101 if (requestName == COMPLETION_GET_SUGGESTIONS) { |
| 88 return processRequest(request); | 102 return processRequest(request); |
| 89 } | 103 } |
| 90 } on RequestFailure catch (exception) { | 104 } on RequestFailure catch (exception) { |
| 91 return exception.response; | 105 return exception.response; |
| 92 } | 106 } |
| 93 return null; | 107 return null; |
| 94 }, onError: (exception, stackTrace) { | 108 }, onError: (exception, stackTrace) { |
| 95 server.sendServerErrorNotification( | 109 server.sendServerErrorNotification( |
| 96 'Failed to handle completion domain request: ${request.toJson()}', | 110 'Failed to handle completion domain request: ${request.toJson()}', |
| 97 exception, | 111 exception, |
| 98 stackTrace); | 112 stackTrace); |
| 99 }); | 113 }); |
| 100 } | 114 } |
| 101 | 115 |
| 102 /** | 116 /** |
| 103 * Process a `completion.getSuggestions` request. | 117 * Process a `completion.getSuggestions` request. |
| 104 */ | 118 */ |
| 105 Response processRequest(Request request, [CompletionManager manager]) { | 119 Response processRequest(Request request) { |
| 106 performance = new CompletionPerformance(); | 120 performance = new CompletionPerformance(); |
| 107 | 121 |
| 108 // extract and validate params | 122 // extract and validate params |
| 109 CompletionGetSuggestionsParams params = | 123 CompletionGetSuggestionsParams params = |
| 110 new CompletionGetSuggestionsParams.fromRequest(request); | 124 new CompletionGetSuggestionsParams.fromRequest(request); |
| 111 ContextSourcePair contextSource = server.getContextSourcePair(params.file); | 125 ContextSourcePair contextSource = server.getContextSourcePair(params.file); |
| 112 AnalysisContext context = contextSource.context; | 126 AnalysisContext context = contextSource.context; |
| 113 Source source = contextSource.source; | 127 Source source = contextSource.source; |
| 114 if (context == null || !context.exists(source)) { | 128 if (context == null || !context.exists(source)) { |
| 115 return new Response.unknownSource(request); | 129 return new Response.unknownSource(request); |
| 116 } | 130 } |
| 117 TimestampedData<String> contents = context.getContents(source); | 131 TimestampedData<String> contents = context.getContents(source); |
| 118 if (params.offset < 0 || params.offset > contents.data.length) { | 132 if (params.offset < 0 || params.offset > contents.data.length) { |
| 119 return new Response.invalidParameter( | 133 return new Response.invalidParameter( |
| 120 request, | 134 request, |
| 121 'params.offset', | 135 'params.offset', |
| 122 'Expected offset between 0 and source length inclusive,' | 136 'Expected offset between 0 and source length inclusive,' |
| 123 ' but found ${params.offset}'); | 137 ' but found ${params.offset}'); |
| 124 } | 138 } |
| 125 | 139 |
| 126 // schedule completion analysis | |
| 127 recordRequest(performance, context, source, params.offset); | 140 recordRequest(performance, context, source, params.offset); |
| 128 if (manager == null) { | 141 |
| 129 manager = completionManagerFor(context, source); | |
| 130 } | |
| 131 CompletionRequest completionRequest = new CompletionRequestImpl(context, | 142 CompletionRequest completionRequest = new CompletionRequestImpl(context, |
| 132 server.resourceProvider, server.searchEngine, source, params.offset); | 143 server.resourceProvider, server.searchEngine, source, params.offset); |
| 133 String completionId = (_nextCompletionId++).toString(); | 144 String completionId = (_nextCompletionId++).toString(); |
| 134 manager | 145 |
| 135 .computeSuggestions(completionRequest) | 146 // Compute suggestions in the background |
| 136 .then((CompletionResult result) { | 147 computeSuggestions(completionRequest).then((CompletionResult result) { |
| 137 const SEND_NOTIFICATION_TAG = 'send notification'; | 148 const SEND_NOTIFICATION_TAG = 'send notification'; |
| 138 performance.logStartTime(SEND_NOTIFICATION_TAG); | 149 performance.logStartTime(SEND_NOTIFICATION_TAG); |
| 139 sendCompletionNotification(completionId, result.replacementOffset, | 150 sendCompletionNotification(completionId, result.replacementOffset, |
| 140 result.replacementLength, result.suggestions); | 151 result.replacementLength, result.suggestions); |
| 141 performance.logElapseTime(SEND_NOTIFICATION_TAG); | 152 performance.logElapseTime(SEND_NOTIFICATION_TAG); |
| 142 | 153 |
| 143 performance.notificationCount = 1; | 154 performance.notificationCount = 1; |
| 144 performance.logFirstNotificationComplete('notification 1 complete'); | 155 performance.logFirstNotificationComplete('notification 1 complete'); |
| 145 performance.suggestionCountFirst = result.suggestions.length; | 156 performance.suggestionCountFirst = result.suggestions.length; |
| 146 performance.suggestionCountLast = result.suggestions.length; | 157 performance.suggestionCountLast = result.suggestions.length; |
| 147 performance.complete(); | 158 performance.complete(); |
| 148 }); | 159 }); |
| 160 |
| 149 // initial response without results | 161 // initial response without results |
| 150 return new CompletionGetSuggestionsResult(completionId) | 162 return new CompletionGetSuggestionsResult(completionId) |
| 151 .toResponse(request.id); | 163 .toResponse(request.id); |
| 152 } | 164 } |
| 153 | 165 |
| 154 /** | 166 /** |
| 155 * If tracking code completion performance over time, then | 167 * If tracking code completion performance over time, then |
| 156 * record addition information about the request in the performance record. | 168 * record addition information about the request in the performance record. |
| 157 */ | 169 */ |
| 158 void recordRequest(CompletionPerformance performance, AnalysisContext context, | 170 void recordRequest(CompletionPerformance performance, AnalysisContext context, |
| (...skipping 16 matching lines...) Expand all Loading... |
| 175 /** | 187 /** |
| 176 * Send completion notification results. | 188 * Send completion notification results. |
| 177 */ | 189 */ |
| 178 void sendCompletionNotification(String completionId, int replacementOffset, | 190 void sendCompletionNotification(String completionId, int replacementOffset, |
| 179 int replacementLength, Iterable<CompletionSuggestion> results) { | 191 int replacementLength, Iterable<CompletionSuggestion> results) { |
| 180 server.sendNotification(new CompletionResultsParams( | 192 server.sendNotification(new CompletionResultsParams( |
| 181 completionId, replacementOffset, replacementLength, results, true) | 193 completionId, replacementOffset, replacementLength, results, true) |
| 182 .toNotification()); | 194 .toNotification()); |
| 183 } | 195 } |
| 184 } | 196 } |
| 197 |
| 198 /** |
| 199 * The result of computing suggestions for code completion. |
| 200 */ |
| 201 class CompletionResult { |
| 202 /** |
| 203 * The length of the text to be replaced if the remainder of the identifier |
| 204 * containing the cursor is to be replaced when the suggestion is applied |
| 205 * (that is, the number of characters in the existing identifier). |
| 206 */ |
| 207 final int replacementLength; |
| 208 |
| 209 /** |
| 210 * The offset of the start of the text to be replaced. This will be different |
| 211 * than the offset used to request the completion suggestions if there was a |
| 212 * portion of an identifier before the original offset. In particular, the |
| 213 * replacementOffset will be the offset of the beginning of said identifier. |
| 214 */ |
| 215 final int replacementOffset; |
| 216 |
| 217 /** |
| 218 * The suggested completions. |
| 219 */ |
| 220 final List<CompletionSuggestion> suggestions; |
| 221 |
| 222 CompletionResult( |
| 223 this.replacementOffset, this.replacementLength, this.suggestions); |
| 224 } |
| OLD | NEW |