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'; |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
46 */ | 46 */ |
47 final List<CompletionPerformance> performanceList = | 47 final List<CompletionPerformance> performanceList = |
48 new List<CompletionPerformance>(); | 48 new List<CompletionPerformance>(); |
49 | 49 |
50 /** | 50 /** |
51 * Performance for the last priority change event. | 51 * Performance for the last priority change event. |
52 */ | 52 */ |
53 CompletionPerformance computeCachePerformance; | 53 CompletionPerformance computeCachePerformance; |
54 | 54 |
55 /** | 55 /** |
| 56 * The current request being processed or `null` if none. |
| 57 */ |
| 58 CompletionRequestImpl _currentRequest; |
| 59 |
| 60 /** |
56 * Initialize a new request handler for the given [server]. | 61 * Initialize a new request handler for the given [server]. |
57 */ | 62 */ |
58 CompletionDomainHandler(this.server); | 63 CompletionDomainHandler(this.server); |
59 | 64 |
60 /** | 65 /** |
61 * Compute completion results for the given reqeust and append them to the str
eam. | 66 * Compute completion results for the given request and append them to the str
eam. |
62 * Clients should not call this method directly as it is automatically called | 67 * Clients should not call this method directly as it is automatically called |
63 * when a client listens to the stream returned by [results]. | 68 * when a client listens to the stream returned by [results]. |
64 * Subclasses should override this method, append at least one result | 69 * Subclasses should override this method, append at least one result |
65 * to the [controller], and close the controller stream once complete. | 70 * to the [controller], and close the controller stream once complete. |
66 */ | 71 */ |
67 Future<CompletionResult> computeSuggestions( | 72 Future<CompletionResult> computeSuggestions( |
68 CompletionRequestImpl request) async { | 73 CompletionRequestImpl request) async { |
69 Iterable<CompletionContributor> newContributors = | 74 Iterable<CompletionContributor> newContributors = |
70 server.serverPlugin.completionContributors; | 75 server.serverPlugin.completionContributors; |
71 List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; | 76 List<CompletionSuggestion> suggestions = <CompletionSuggestion>[]; |
72 | 77 |
73 const COMPUTE_SUGGESTIONS_TAG = 'computeSuggestions'; | 78 const COMPUTE_SUGGESTIONS_TAG = 'computeSuggestions'; |
74 performance.logStartTime(COMPUTE_SUGGESTIONS_TAG); | 79 performance.logStartTime(COMPUTE_SUGGESTIONS_TAG); |
75 | 80 |
76 for (CompletionContributor contributor in newContributors) { | 81 for (CompletionContributor contributor in newContributors) { |
77 String contributorTag = 'computeSuggestions - ${contributor.runtimeType}'; | 82 String contributorTag = 'computeSuggestions - ${contributor.runtimeType}'; |
78 performance.logStartTime(contributorTag); | 83 performance.logStartTime(contributorTag); |
79 suggestions.addAll(await contributor.computeSuggestions(request)); | 84 try { |
| 85 suggestions.addAll(await contributor.computeSuggestions(request)); |
| 86 } on AbortCompletion { |
| 87 suggestions.clear(); |
| 88 break; |
| 89 } |
80 performance.logElapseTime(contributorTag); | 90 performance.logElapseTime(contributorTag); |
81 } | 91 } |
82 | 92 |
83 performance.logElapseTime(COMPUTE_SUGGESTIONS_TAG); | 93 performance.logElapseTime(COMPUTE_SUGGESTIONS_TAG); |
84 | 94 |
85 // TODO (danrubel) if request is obsolete | 95 // TODO (danrubel) if request is obsolete |
86 // (processAnalysisRequest returns false) | 96 // (processAnalysisRequest returns false) |
87 // then send empty results | 97 // then send empty results |
88 | 98 |
89 return new CompletionResult( | 99 return new CompletionResult( |
(...skipping 42 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
132 if (params.offset < 0 || params.offset > contents.data.length) { | 142 if (params.offset < 0 || params.offset > contents.data.length) { |
133 return new Response.invalidParameter( | 143 return new Response.invalidParameter( |
134 request, | 144 request, |
135 'params.offset', | 145 'params.offset', |
136 'Expected offset between 0 and source length inclusive,' | 146 'Expected offset between 0 and source length inclusive,' |
137 ' but found ${params.offset}'); | 147 ' but found ${params.offset}'); |
138 } | 148 } |
139 | 149 |
140 recordRequest(performance, context, source, params.offset); | 150 recordRequest(performance, context, source, params.offset); |
141 | 151 |
142 CompletionRequest completionRequest = new CompletionRequestImpl( | 152 CompletionRequestImpl completionRequest = new CompletionRequestImpl( |
143 context, | 153 context, |
144 server.resourceProvider, | 154 server.resourceProvider, |
145 server.searchEngine, | 155 server.searchEngine, |
146 source, | 156 source, |
147 params.offset, | 157 params.offset, |
148 performance); | 158 performance); |
149 String completionId = (_nextCompletionId++).toString(); | 159 String completionId = (_nextCompletionId++).toString(); |
150 | 160 |
| 161 _abortCurrentRequest(); |
| 162 _currentRequest = completionRequest; |
| 163 |
151 // Compute suggestions in the background | 164 // Compute suggestions in the background |
152 computeSuggestions(completionRequest).then((CompletionResult result) { | 165 computeSuggestions(completionRequest).then((CompletionResult result) { |
153 const SEND_NOTIFICATION_TAG = 'send notification'; | 166 const SEND_NOTIFICATION_TAG = 'send notification'; |
154 performance.logStartTime(SEND_NOTIFICATION_TAG); | 167 performance.logStartTime(SEND_NOTIFICATION_TAG); |
155 sendCompletionNotification(completionId, result.replacementOffset, | 168 sendCompletionNotification(completionId, result.replacementOffset, |
156 result.replacementLength, result.suggestions); | 169 result.replacementLength, result.suggestions); |
157 performance.logElapseTime(SEND_NOTIFICATION_TAG); | 170 performance.logElapseTime(SEND_NOTIFICATION_TAG); |
158 | 171 |
159 performance.notificationCount = 1; | 172 performance.notificationCount = 1; |
160 performance.logFirstNotificationComplete('notification 1 complete'); | 173 performance.logFirstNotificationComplete('notification 1 complete'); |
161 performance.suggestionCountFirst = result.suggestions.length; | 174 performance.suggestionCountFirst = result.suggestions.length; |
162 performance.suggestionCountLast = result.suggestions.length; | 175 performance.suggestionCountLast = result.suggestions.length; |
163 performance.complete(); | 176 performance.complete(); |
| 177 }).whenComplete(() { |
| 178 if (_currentRequest == completionRequest) { |
| 179 _currentRequest = null; |
| 180 } |
164 }); | 181 }); |
165 | 182 |
166 // initial response without results | 183 // initial response without results |
167 return new CompletionGetSuggestionsResult(completionId) | 184 return new CompletionGetSuggestionsResult(completionId) |
168 .toResponse(request.id); | 185 .toResponse(request.id); |
169 } | 186 } |
170 | 187 |
171 /** | 188 /** |
172 * If tracking code completion performance over time, then | 189 * If tracking code completion performance over time, then |
173 * record addition information about the request in the performance record. | 190 * record addition information about the request in the performance record. |
(...skipping 17 matching lines...) Expand all Loading... |
191 | 208 |
192 /** | 209 /** |
193 * Send completion notification results. | 210 * Send completion notification results. |
194 */ | 211 */ |
195 void sendCompletionNotification(String completionId, int replacementOffset, | 212 void sendCompletionNotification(String completionId, int replacementOffset, |
196 int replacementLength, Iterable<CompletionSuggestion> results) { | 213 int replacementLength, Iterable<CompletionSuggestion> results) { |
197 server.sendNotification(new CompletionResultsParams( | 214 server.sendNotification(new CompletionResultsParams( |
198 completionId, replacementOffset, replacementLength, results, true) | 215 completionId, replacementOffset, replacementLength, results, true) |
199 .toNotification()); | 216 .toNotification()); |
200 } | 217 } |
| 218 |
| 219 /** |
| 220 * Abort the current completion request, if any. |
| 221 */ |
| 222 void _abortCurrentRequest() { |
| 223 if (_currentRequest != null) { |
| 224 _currentRequest.abort(); |
| 225 _currentRequest = null; |
| 226 } |
| 227 } |
201 } | 228 } |
202 | 229 |
203 /** | 230 /** |
204 * The result of computing suggestions for code completion. | 231 * The result of computing suggestions for code completion. |
205 */ | 232 */ |
206 class CompletionResult { | 233 class CompletionResult { |
207 /** | 234 /** |
208 * The length of the text to be replaced if the remainder of the identifier | 235 * The length of the text to be replaced if the remainder of the identifier |
209 * containing the cursor is to be replaced when the suggestion is applied | 236 * containing the cursor is to be replaced when the suggestion is applied |
210 * (that is, the number of characters in the existing identifier). | 237 * (that is, the number of characters in the existing identifier). |
211 */ | 238 */ |
212 final int replacementLength; | 239 final int replacementLength; |
213 | 240 |
214 /** | 241 /** |
215 * The offset of the start of the text to be replaced. This will be different | 242 * The offset of the start of the text to be replaced. This will be different |
216 * than the offset used to request the completion suggestions if there was a | 243 * than the offset used to request the completion suggestions if there was a |
217 * portion of an identifier before the original offset. In particular, the | 244 * portion of an identifier before the original offset. In particular, the |
218 * replacementOffset will be the offset of the beginning of said identifier. | 245 * replacementOffset will be the offset of the beginning of said identifier. |
219 */ | 246 */ |
220 final int replacementOffset; | 247 final int replacementOffset; |
221 | 248 |
222 /** | 249 /** |
223 * The suggested completions. | 250 * The suggested completions. |
224 */ | 251 */ |
225 final List<CompletionSuggestion> suggestions; | 252 final List<CompletionSuggestion> suggestions; |
226 | 253 |
227 CompletionResult( | 254 CompletionResult( |
228 this.replacementOffset, this.replacementLength, this.suggestions); | 255 this.replacementOffset, this.replacementLength, this.suggestions); |
229 } | 256 } |
OLD | NEW |