Chromium Code Reviews| 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 analysis.server; | 5 library analysis.server; |
| 6 | 6 |
| 7 import 'dart:async'; | 7 import 'dart:async'; |
| 8 | 8 |
| 9 import 'package:analysis_server/src/analysis_logger.dart'; | 9 import 'package:analysis_server/src/analysis_logger.dart'; |
| 10 import 'package:analysis_server/src/channel.dart'; | 10 import 'package:analysis_server/src/channel.dart'; |
| 11 import 'package:analysis_server/src/protocol.dart'; | 11 import 'package:analysis_server/src/protocol.dart'; |
| 12 import 'package:analyzer/src/generated/engine.dart'; | 12 import 'package:analyzer/src/generated/engine.dart'; |
| 13 import 'package:analyzer/src/generated/error.dart'; | 13 import 'package:analyzer/src/generated/error.dart'; |
| 14 import 'package:analyzer/src/generated/java_core.dart'; | 14 import 'package:analyzer/src/generated/java_core.dart'; |
| 15 import 'package:analyzer/src/generated/source.dart'; | |
| 15 | 16 |
| 16 /** | 17 /** |
| 17 * Instances of the class [AnalysisServer] implement a server that listens on a | 18 * Instances of the class [AnalysisServer] implement a server that listens on a |
| 18 * [CommunicationChannel] for analysis requests and process them. | 19 * [CommunicationChannel] for analysis requests and process them. |
| 19 */ | 20 */ |
| 20 class AnalysisServer { | 21 class AnalysisServer { |
| 21 /** | 22 /** |
| 22 * The name of the notification of new errors associated with a source. | 23 * The name of the notification of new errors associated with a source. |
| 23 */ | 24 */ |
| 24 static const String ERROR_NOTIFICATION_NAME = 'context.errors'; | 25 static const String ERROR_NOTIFICATION_NAME = 'context.errors'; |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 37 * The name of the parameter whose value is a source. | 38 * The name of the parameter whose value is a source. |
| 38 */ | 39 */ |
| 39 static const String SOURCE_PARAM = 'source'; | 40 static const String SOURCE_PARAM = 'source'; |
| 40 | 41 |
| 41 /** | 42 /** |
| 42 * The event name of the connected notification. | 43 * The event name of the connected notification. |
| 43 */ | 44 */ |
| 44 static const String CONNECTED_NOTIFICATION = 'server.connected'; | 45 static const String CONNECTED_NOTIFICATION = 'server.connected'; |
| 45 | 46 |
| 46 /** | 47 /** |
| 48 * The event name of the status notification. | |
| 49 */ | |
| 50 static const String STATUS_NOTIFICATION = 'server.status'; | |
| 51 | |
| 52 /** | |
| 47 * The channel from which requests are received and to which responses should | 53 * The channel from which requests are received and to which responses should |
| 48 * be sent. | 54 * be sent. |
| 49 */ | 55 */ |
| 50 final ServerCommunicationChannel channel; | 56 final ServerCommunicationChannel channel; |
| 51 | 57 |
| 52 /** | 58 /** |
| 53 * A flag indicating whether the server is running. When false, contexts | 59 * A flag indicating whether the server is running. When false, contexts |
| 54 * will no longer be added to [contextWorkQueue], and [performTask] will | 60 * will no longer be added to [contextWorkQueue], and [performTask] will |
| 55 * discard any tasks it finds on [contextWorkQueue]. | 61 * discard any tasks it finds on [contextWorkQueue]. |
| 56 */ | 62 */ |
| (...skipping 19 matching lines...) Expand all Loading... | |
| 76 * A list of the analysis contexts for which analysis work needs to be | 82 * A list of the analysis contexts for which analysis work needs to be |
| 77 * performed. | 83 * performed. |
| 78 * | 84 * |
| 79 * Invariant: when this list is non-empty, there is exactly one pending call | 85 * Invariant: when this list is non-empty, there is exactly one pending call |
| 80 * to [performTask] on the event queue. When this list is empty, there are | 86 * to [performTask] on the event queue. When this list is empty, there are |
| 81 * no calls to [performTask] on the event queue. | 87 * no calls to [performTask] on the event queue. |
| 82 */ | 88 */ |
| 83 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); | 89 final List<AnalysisContext> contextWorkQueue = new List<AnalysisContext>(); |
| 84 | 90 |
| 85 /** | 91 /** |
| 92 * The time at which a status message was sent to the client. | |
| 93 */ | |
| 94 final Stopwatch statusStopwatch = new Stopwatch(); | |
| 95 | |
| 96 /** | |
| 86 * Initialize a newly created server to receive requests from and send | 97 * Initialize a newly created server to receive requests from and send |
| 87 * responses to the given [channel]. | 98 * responses to the given [channel]. |
| 88 */ | 99 */ |
| 89 AnalysisServer(this.channel) { | 100 AnalysisServer(this.channel) { |
| 90 AnalysisEngine.instance.logger = new AnalysisLogger(); | 101 AnalysisEngine.instance.logger = new AnalysisLogger(); |
| 91 running = true; | 102 running = true; |
| 92 Notification notification = new Notification(CONNECTED_NOTIFICATION); | 103 Notification notification = new Notification(CONNECTED_NOTIFICATION); |
| 93 channel.sendNotification(notification); | 104 channel.sendNotification(notification); |
| 94 channel.listen(handleRequest, onDone: done, onError: error); | 105 channel.listen(handleRequest, onDone: done, onError: error); |
| 95 } | 106 } |
| (...skipping 65 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 161 } | 172 } |
| 162 if (contextWorkQueue.isEmpty) { | 173 if (contextWorkQueue.isEmpty) { |
| 163 // Nothing to do. | 174 // Nothing to do. |
| 164 return; | 175 return; |
| 165 } | 176 } |
| 166 // | 177 // |
| 167 // Look for a context that has work to be done and then perform one task. | 178 // Look for a context that has work to be done and then perform one task. |
| 168 // | 179 // |
| 169 List<ChangeNotice> notices = null; | 180 List<ChangeNotice> notices = null; |
| 170 String contextId; | 181 String contextId; |
| 182 AnalysisResult result; | |
| 171 try { | 183 try { |
| 172 AnalysisContext context = contextWorkQueue[0]; | 184 AnalysisContext context = contextWorkQueue[0]; |
| 173 contextId = contextIdMap[context]; | 185 contextId = contextIdMap[context]; |
| 174 AnalysisResult result = context.performAnalysisTask(); | 186 result = context.performAnalysisTask(); |
| 175 notices = result.changeNotices; | 187 notices = result.changeNotices; |
| 176 } finally { | 188 } finally { |
| 177 if (notices == null) { | 189 if (notices == null) { |
| 178 // Either we have no more work to do for this context, or there was an | 190 // Either we have no more work to do for this context, or there was an |
| 179 // unhandled exception trying to perform the analysis. In either case, | 191 // unhandled exception trying to perform the analysis. In either case, |
| 180 // remove the context form the work queue so we won't try to do more | 192 // remove the context form the work queue so we won't try to do more |
| 181 // analysis on it. | 193 // analysis on it. |
| 182 contextWorkQueue.removeAt(0); | 194 contextWorkQueue.removeAt(0); |
| 183 } | 195 } |
| 184 // | 196 // |
| 185 // Schedule this method to be run again if there is any more work to be | 197 // Schedule this method to be run again if there is any more work to be |
| 186 // done. | 198 // done. |
| 187 // | 199 // |
| 188 if (!contextWorkQueue.isEmpty) { | 200 if (!contextWorkQueue.isEmpty) { |
| 189 _scheduleTask(); | 201 _scheduleTask(); |
| 190 } | 202 } |
| 191 } | 203 } |
| 204 sendStatusNotification(result); | |
| 192 if (notices != null) { | 205 if (notices != null) { |
| 193 sendNotices(contextId, notices); | 206 sendNotices(contextId, notices); |
| 194 } | 207 } |
| 195 } | 208 } |
| 196 | 209 |
| 197 /** | 210 /** |
| 198 * Send the information in the given list of notices back to the client. | 211 * Send the information in the given list of notices back to the client. |
| 199 */ | 212 */ |
| 200 void sendNotices(String contextId, List<ChangeNotice> notices) { | 213 void sendNotices(String contextId, List<ChangeNotice> notices) { |
| 201 for (int i = 0; i < notices.length; i++) { | 214 for (int i = 0; i < notices.length; i++) { |
| 202 ChangeNotice notice = notices[i]; | 215 ChangeNotice notice = notices[i]; |
| 203 Notification notification = new Notification(ERROR_NOTIFICATION_NAME); | 216 Notification notification = new Notification(ERROR_NOTIFICATION_NAME); |
| 204 notification.setParameter(CONTEXT_ID_PARAM, contextId); | 217 notification.setParameter(CONTEXT_ID_PARAM, contextId); |
| 205 notification.setParameter(SOURCE_PARAM, notice.source.encoding); | 218 notification.setParameter(SOURCE_PARAM, notice.source.encoding); |
| 206 notification.setParameter(ERRORS_PARAM, notice.errors.map( | 219 notification.setParameter(ERRORS_PARAM, notice.errors.map( |
| 207 errorToJson).toList()); | 220 errorToJson).toList()); |
| 208 sendNotification(notification); | 221 sendNotification(notification); |
| 209 } | 222 } |
| 210 } | 223 } |
| 211 | 224 |
| 225 /** | |
| 226 * Send status notification to the client. | |
| 227 */ | |
| 228 void sendStatusNotification(AnalysisResult result) { | |
| 229 Notification notification = new Notification(STATUS_NOTIFICATION); | |
| 230 if (result.changeNotices != null) { | |
| 231 // Throttle status messages to at most 1 every half second | |
| 232 if (!statusStopwatch.isRunning) { | |
| 233 statusStopwatch.start(); | |
| 234 } else if (statusStopwatch.elapsedMilliseconds > 500) { | |
| 235 statusStopwatch.reset(); | |
| 236 } else { | |
| 237 return; | |
|
Paul Berry
2014/05/23 15:56:45
This technique for throttling status notifications
| |
| 238 } | |
| 239 String shortMessage = 'Analyzing'; | |
| 240 String longMessage = 'Analyzing'; | |
| 241 if (result.changeNotices.length > 0) { | |
| 242 ChangeNotice notice = result.changeNotices[0]; | |
| 243 if (notice != null) { | |
| 244 shortMessage = 'Analyzing ${notice.source.shortName}'; | |
|
Paul Berry
2014/05/23 15:56:45
Similar concern here. As you've arranged things,
| |
| 245 longMessage = 'Analyzing ${notice.source.fullName}'; | |
| 246 } | |
| 247 } | |
| 248 // TODO(danrubel): improve message once AnalysisResult can provide | |
| 249 // something more human readable | |
| 250 notification.params['shortMessage'] = shortMessage; | |
| 251 notification.params['longMessage'] = longMessage; | |
| 252 notification.params['isAnalyzing'] = true; | |
| 253 } else { | |
| 254 // Reset to ensure notification when analysis starts again | |
| 255 statusStopwatch..stop()..reset(); | |
| 256 notification.params['shortMessage'] = 'Analysis complete'; | |
| 257 notification.params['longMessage'] = 'Analysis complete'; | |
| 258 notification.params['isAnalyzing'] = false; | |
| 259 } | |
| 260 channel.sendNotification(notification); | |
| 261 } | |
| 262 | |
| 212 static Map<String, Object> errorToJson(AnalysisError analysisError) { | 263 static Map<String, Object> errorToJson(AnalysisError analysisError) { |
| 213 // TODO(paulberry): move this function into the AnalysisError class. | 264 // TODO(paulberry): move this function into the AnalysisError class. |
| 214 | 265 |
| 215 // TODO(paulberry): we really shouldn't be exposing errorCode.ordinal | 266 // TODO(paulberry): we really shouldn't be exposing errorCode.ordinal |
| 216 // outside the analyzer, since the ordinal numbers change whenever we | 267 // outside the analyzer, since the ordinal numbers change whenever we |
| 217 // regenerate the analysis engine. | 268 // regenerate the analysis engine. |
| 218 Map<String, Object> result = { | 269 Map<String, Object> result = { |
| 219 'source': analysisError.source.encoding, | 270 'source': analysisError.source.encoding, |
| 220 'errorCode': (analysisError.errorCode as Enum).ordinal, | 271 'errorCode': (analysisError.errorCode as Enum).ordinal, |
| 221 'offset': analysisError.offset, | 272 'offset': analysisError.offset, |
| (...skipping 12 matching lines...) Expand all Loading... | |
| 234 void sendNotification(Notification notification) { | 285 void sendNotification(Notification notification) { |
| 235 channel.sendNotification(notification); | 286 channel.sendNotification(notification); |
| 236 } | 287 } |
| 237 | 288 |
| 238 void _scheduleTask() { | 289 void _scheduleTask() { |
| 239 new Future(performTask).catchError((ex, st) { | 290 new Future(performTask).catchError((ex, st) { |
| 240 AnalysisEngine.instance.logger.logError("${ex}\n${st}"); | 291 AnalysisEngine.instance.logger.logError("${ex}\n${st}"); |
| 241 }); | 292 }); |
| 242 } | 293 } |
| 243 } | 294 } |
| OLD | NEW |