| OLD | NEW |
| (Empty) |
| 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 | |
| 3 // BSD-style license that can be found in the LICENSE file. | |
| 4 | |
| 5 library services.completion.manager; | |
| 6 | |
| 7 import 'dart:async'; | |
| 8 | |
| 9 import 'package:analysis_server/plugin/protocol/protocol.dart'; | |
| 10 import 'package:analysis_server/src/provisional/completion/completion_core.dart' | |
| 11 show | |
| 12 CompletionContributor, | |
| 13 CompletionContributorFactory, | |
| 14 CompletionRequest, | |
| 15 CompletionResult; | |
| 16 import 'package:analysis_server/src/services/completion/dart_completion_manager.
dart'; | |
| 17 import 'package:analysis_server/src/services/search/search_engine.dart'; | |
| 18 import 'package:analyzer/src/generated/engine.dart'; | |
| 19 import 'package:analyzer/src/generated/source.dart'; | |
| 20 | |
| 21 /** | |
| 22 * Manages completion contributors for a given completion request. | |
| 23 */ | |
| 24 abstract class CompletionManager { | |
| 25 /** | |
| 26 * The context in which the completion was computed. | |
| 27 */ | |
| 28 final AnalysisContext context; | |
| 29 | |
| 30 /** | |
| 31 * The source in which the completion was computed. | |
| 32 */ | |
| 33 final Source source; | |
| 34 | |
| 35 CompletionManager(this.context, this.source); | |
| 36 | |
| 37 /** | |
| 38 * Create a manager for the given request. | |
| 39 */ | |
| 40 factory CompletionManager.create( | |
| 41 AnalysisContext context, | |
| 42 Source source, | |
| 43 SearchEngine searchEngine, | |
| 44 Iterable<CompletionContributor> newContributors) { | |
| 45 if (context != null) { | |
| 46 if (AnalysisEngine.isDartFileName(source.shortName)) { | |
| 47 return new DartCompletionManager.create( | |
| 48 context, searchEngine, source, newContributors); | |
| 49 } | |
| 50 } | |
| 51 return new NoOpCompletionManager(source); | |
| 52 } | |
| 53 | |
| 54 /** | |
| 55 * Compute and cache information in preparation for a possible code | |
| 56 * completion request sometime in the future. The default implementation | |
| 57 * of this method does nothing. Subclasses may override but should not | |
| 58 * count on this method being called before [computeSuggestions]. | |
| 59 * Return a future that completes when the cache is computed with a bool | |
| 60 * indicating success. | |
| 61 */ | |
| 62 Future<bool> computeCache() { | |
| 63 return new Future.value(true); | |
| 64 } | |
| 65 | |
| 66 /** | |
| 67 * Compute completion results for the given reqeust and append them to the str
eam. | |
| 68 * Clients should not call this method directly as it is automatically called | |
| 69 * when a client listens to the stream returned by [results]. | |
| 70 * Subclasses should override this method, append at least one result | |
| 71 * to the [controller], and close the controller stream once complete. | |
| 72 */ | |
| 73 Future<CompletionResult> computeSuggestions(CompletionRequest request); | |
| 74 | |
| 75 /** | |
| 76 * Discard any pending operations. | |
| 77 * Subclasses may override but should call super.dispose | |
| 78 */ | |
| 79 void dispose() {} | |
| 80 } | |
| 81 | |
| 82 /** | |
| 83 * Overall performance of a code completion operation. | |
| 84 */ | |
| 85 class CompletionPerformance { | |
| 86 final DateTime start = new DateTime.now(); | |
| 87 final Map<String, Duration> _startTimes = new Map<String, Duration>(); | |
| 88 final Stopwatch _stopwatch = new Stopwatch(); | |
| 89 final List<OperationPerformance> operations = <OperationPerformance>[]; | |
| 90 | |
| 91 Source source; | |
| 92 String snippet = ''; | |
| 93 int notificationCount = -1; | |
| 94 int suggestionCountFirst = -1; | |
| 95 int suggestionCountLast = -1; | |
| 96 Duration _firstNotification; | |
| 97 | |
| 98 CompletionPerformance() { | |
| 99 _stopwatch.start(); | |
| 100 } | |
| 101 | |
| 102 int get elapsedInMilliseconds => | |
| 103 operations.length > 0 ? operations.last.elapsed.inMilliseconds : 0; | |
| 104 | |
| 105 int get firstNotificationInMilliseconds => | |
| 106 _firstNotification != null ? _firstNotification.inMilliseconds : 0; | |
| 107 | |
| 108 String get startTimeAndMs => '${start.millisecondsSinceEpoch} - $start'; | |
| 109 | |
| 110 String get suggestionCount { | |
| 111 if (notificationCount < 1) return ''; | |
| 112 if (notificationCount == 1) return '$suggestionCountFirst'; | |
| 113 return '$suggestionCountFirst, $suggestionCountLast'; | |
| 114 } | |
| 115 | |
| 116 void complete([String tag = null]) { | |
| 117 _stopwatch.stop(); | |
| 118 _logDuration(tag != null ? tag : 'total time', _stopwatch.elapsed); | |
| 119 } | |
| 120 | |
| 121 logElapseTime(String tag, [f() = null]) { | |
| 122 Duration start; | |
| 123 Duration end = _stopwatch.elapsed; | |
| 124 var result; | |
| 125 if (f == null) { | |
| 126 start = _startTimes[tag]; | |
| 127 if (start == null) { | |
| 128 _logDuration(tag, null); | |
| 129 return null; | |
| 130 } | |
| 131 } else { | |
| 132 result = f(); | |
| 133 start = end; | |
| 134 end = _stopwatch.elapsed; | |
| 135 } | |
| 136 _logDuration(tag, end - start); | |
| 137 return result; | |
| 138 } | |
| 139 | |
| 140 void logFirstNotificationComplete(String tag) { | |
| 141 _firstNotification = _stopwatch.elapsed; | |
| 142 _logDuration(tag, _firstNotification); | |
| 143 } | |
| 144 | |
| 145 void logStartTime(String tag) { | |
| 146 _startTimes[tag] = _stopwatch.elapsed; | |
| 147 } | |
| 148 | |
| 149 void setContentsAndOffset(String contents, int offset) { | |
| 150 snippet = _computeSnippet(contents, offset); | |
| 151 } | |
| 152 | |
| 153 void _logDuration(String tag, Duration elapsed) { | |
| 154 operations.add(new OperationPerformance(tag, elapsed)); | |
| 155 } | |
| 156 | |
| 157 static String _computeSnippet(String contents, int offset) { | |
| 158 if (contents == null || | |
| 159 offset == null || | |
| 160 offset < 0 || | |
| 161 contents.length < offset) { | |
| 162 return '???'; | |
| 163 } | |
| 164 int start = offset; | |
| 165 while (start > 0) { | |
| 166 String ch = contents[start - 1]; | |
| 167 if (ch == '\r' || ch == '\n') { | |
| 168 break; | |
| 169 } | |
| 170 --start; | |
| 171 } | |
| 172 int end = offset; | |
| 173 while (end < contents.length) { | |
| 174 String ch = contents[end]; | |
| 175 if (ch == '\r' || ch == '\n') { | |
| 176 break; | |
| 177 } | |
| 178 ++end; | |
| 179 } | |
| 180 String prefix = contents.substring(start, offset); | |
| 181 String suffix = contents.substring(offset, end); | |
| 182 return '$prefix^$suffix'; | |
| 183 } | |
| 184 } | |
| 185 | |
| 186 /** | |
| 187 * Code completion result generated by an [CompletionManager]. | |
| 188 */ | |
| 189 class CompletionResultImpl implements CompletionResult { | |
| 190 /** | |
| 191 * The length of the text to be replaced if the remainder of the identifier | |
| 192 * containing the cursor is to be replaced when the suggestion is applied | |
| 193 * (that is, the number of characters in the existing identifier). | |
| 194 */ | |
| 195 final int replacementLength; | |
| 196 | |
| 197 /** | |
| 198 * The offset of the start of the text to be replaced. This will be different | |
| 199 * than the offset used to request the completion suggestions if there was a | |
| 200 * portion of an identifier before the original offset. In particular, the | |
| 201 * replacementOffset will be the offset of the beginning of said identifier. | |
| 202 */ | |
| 203 final int replacementOffset; | |
| 204 | |
| 205 /** | |
| 206 * The suggested completions. | |
| 207 */ | |
| 208 final List<CompletionSuggestion> suggestions; | |
| 209 | |
| 210 CompletionResultImpl( | |
| 211 this.replacementOffset, this.replacementLength, this.suggestions); | |
| 212 } | |
| 213 | |
| 214 class NoOpCompletionManager extends CompletionManager { | |
| 215 NoOpCompletionManager(Source source) : super(null, source); | |
| 216 | |
| 217 @override | |
| 218 Future<CompletionResult> computeSuggestions(CompletionRequest request) async { | |
| 219 return new CompletionResultImpl(request.offset, 0, []); | |
| 220 } | |
| 221 } | |
| 222 | |
| 223 /** | |
| 224 * The performance of an operation when computing code completion. | |
| 225 */ | |
| 226 class OperationPerformance { | |
| 227 /** | |
| 228 * The name of the operation | |
| 229 */ | |
| 230 final String name; | |
| 231 | |
| 232 /** | |
| 233 * The elapse time or `null` if undefined. | |
| 234 */ | |
| 235 final Duration elapsed; | |
| 236 | |
| 237 OperationPerformance(this.name, this.elapsed); | |
| 238 } | |
| OLD | NEW |