Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(58)

Side by Side Diff: pkg/analysis_server/lib/src/analysis_server.dart

Issue 362893004: Implementation for 'analysis.getHover'. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
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 import 'dart:collection'; 8 import 'dart:collection';
9 9
10 import 'package:analysis_server/src/analysis_logger.dart'; 10 import 'package:analysis_server/src/analysis_logger.dart';
11 import 'package:analysis_server/src/channel.dart'; 11 import 'package:analysis_server/src/channel.dart';
12 import 'package:analysis_server/src/constants.dart'; 12 import 'package:analysis_server/src/constants.dart';
13 import 'package:analysis_server/src/context_directory_manager.dart'; 13 import 'package:analysis_server/src/context_directory_manager.dart';
14 import 'package:analysis_server/src/domain_analysis.dart'; 14 import 'package:analysis_server/src/domain_analysis.dart';
15 import 'package:analysis_server/src/operation/operation.dart';
15 import 'package:analysis_server/src/operation/operation_analysis.dart'; 16 import 'package:analysis_server/src/operation/operation_analysis.dart';
16 import 'package:analysis_server/src/operation/operation.dart';
17 import 'package:analysis_server/src/operation/operation_queue.dart'; 17 import 'package:analysis_server/src/operation/operation_queue.dart';
18 import 'package:analysis_server/src/package_map_provider.dart'; 18 import 'package:analysis_server/src/package_map_provider.dart';
19 import 'package:analysis_server/src/package_uri_resolver.dart'; 19 import 'package:analysis_server/src/package_uri_resolver.dart';
20 import 'package:analysis_server/src/protocol.dart'; 20 import 'package:analysis_server/src/protocol.dart';
21 import 'package:analysis_server/src/resource.dart'; 21 import 'package:analysis_server/src/resource.dart';
22 import 'package:analyzer/src/generated/ast.dart'; 22 import 'package:analyzer/src/generated/ast.dart';
23 import 'package:analyzer/src/generated/engine.dart'; 23 import 'package:analyzer/src/generated/engine.dart';
24 import 'package:analyzer/src/generated/error.dart'; 24 import 'package:analyzer/src/generated/error.dart';
25 import 'package:analyzer/src/generated/source.dart'; 25 import 'package:analyzer/src/generated/index.dart';
26 import 'package:analyzer/src/generated/java_engine.dart';
26 import 'package:analyzer/src/generated/sdk.dart'; 27 import 'package:analyzer/src/generated/sdk.dart';
27 import 'package:analyzer/src/generated/sdk_io.dart'; 28 import 'package:analyzer/src/generated/sdk_io.dart';
29 import 'package:analyzer/src/generated/source.dart';
28 import 'package:analyzer/src/generated/source_io.dart'; 30 import 'package:analyzer/src/generated/source_io.dart';
29 import 'package:analyzer/src/generated/java_engine.dart';
30 import 'package:analyzer/src/generated/index.dart';
31 31
32 32
33 /** 33 /**
34 * An instance of [DirectoryBasedDartSdk] that is shared between 34 * An instance of [DirectoryBasedDartSdk] that is shared between
35 * [AnalysisServer] instances to improve performance. 35 * [AnalysisServer] instances to improve performance.
36 */ 36 */
37 final DirectoryBasedDartSdk SHARED_SDK = DirectoryBasedDartSdk.defaultSdk; 37 final DirectoryBasedDartSdk SHARED_SDK = DirectoryBasedDartSdk.defaultSdk;
38 38
39 class AnalysisServerContextDirectoryManager extends ContextDirectoryManager { 39 typedef void OptionUpdater(AnalysisOptionsImpl options);
40 final AnalysisServer analysisServer;
41
42 /**
43 * The default options used to create new analysis contexts.
44 */
45 AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
46
47 AnalysisServerContextDirectoryManager(
48 this.analysisServer, ResourceProvider resourceProvider,
49 PackageMapProvider packageMapProvider)
50 : super(resourceProvider, packageMapProvider);
51
52 @override
53 void addContext(Folder folder, Map<String, List<Folder>> packageMap) {
54 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
55 analysisServer.folderMap[folder] = context;
56 context.sourceFactory = _createSourceFactory(packageMap);
57 context.analysisOptions = new AnalysisOptionsImpl.con1(defaultOptions);
58 analysisServer.schedulePerformAnalysisOperation(context);
59 }
60
61 @override
62 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
63 AnalysisContext context = analysisServer.folderMap[contextFolder];
64 context.applyChanges(changeSet);
65 analysisServer.schedulePerformAnalysisOperation(context);
66 }
67
68 @override
69 void removeContext(Folder folder) {
70 analysisServer.folderMap.remove(folder);
71 }
72
73 @override
74 void updateContextPackageMap(Folder contextFolder,
75 Map<String, List<Folder>> packageMap) {
76 AnalysisContext context = analysisServer.folderMap[contextFolder];
77 context.sourceFactory = _createSourceFactory(packageMap);
78 analysisServer.schedulePerformAnalysisOperation(context);
79 }
80
81 /**
82 * Set up a [SourceFactory] that resolves packages using the given
83 * [packageMap].
84 */
85 SourceFactory _createSourceFactory(Map<String, List<Folder>> packageMap) {
86 List<UriResolver> resolvers = <UriResolver>[
87 new DartUriResolver(analysisServer.defaultSdk),
88 new ResourceUriResolver(resourceProvider),
89 new PackageMapUriResolver(resourceProvider, packageMap)
90 ];
91 return new SourceFactory(resolvers);
92 }
93 }
94 40
95 /** 41 /**
96 * Instances of the class [AnalysisServer] implement a server that listens on a 42 * Instances of the class [AnalysisServer] implement a server that listens on a
97 * [CommunicationChannel] for analysis requests and process them. 43 * [CommunicationChannel] for analysis requests and process them.
98 */ 44 */
99 class AnalysisServer { 45 class AnalysisServer {
100 /** 46 /**
101 * The channel from which requests are received and to which responses should 47 * The channel from which requests are received and to which responses should
102 * be sent. 48 * be sent.
103 */ 49 */
(...skipping 30 matching lines...) Expand all
134 List<RequestHandler> handlers; 80 List<RequestHandler> handlers;
135 81
136 /** 82 /**
137 * The current default [DartSdk]. 83 * The current default [DartSdk].
138 */ 84 */
139 DartSdk defaultSdk = SHARED_SDK; 85 DartSdk defaultSdk = SHARED_SDK;
140 86
141 /** 87 /**
142 * A table mapping [Folder]s to the [AnalysisContext]s associated with them. 88 * A table mapping [Folder]s to the [AnalysisContext]s associated with them.
143 */ 89 */
144 final Map<Folder, AnalysisContext> folderMap = 90 final Map<Folder, AnalysisContext> folderMap = new HashMap<Folder,
Brian Wilkerson 2014/07/02 13:58:06 You probably have no control, but I liked the old
scheglov 2014/07/02 17:33:09 Yes, we need to improve formatter. Rolled back for
145 new HashMap<Folder, AnalysisContext>(); 91 AnalysisContext>();
146 92
147 /** 93 /**
148 * A queue of the operations to perform in this server. 94 * A queue of the operations to perform in this server.
149 * 95 *
150 * Invariant: when this queue is non-empty, there is exactly one pending call 96 * Invariant: when this queue is non-empty, there is exactly one pending call
151 * to [performOperation] on the event queue. When this list is empty, there a re 97 * to [performOperation] on the event queue. When this list is empty, there a re
152 * no calls to [performOperation] on the event queue. 98 * no calls to [performOperation] on the event queue.
153 */ 99 */
154 ServerOperationQueue operationQueue; 100 ServerOperationQueue operationQueue;
155 101
(...skipping 18 matching lines...) Expand all
174 /** 120 /**
175 * Initialize a newly created server to receive requests from and send 121 * Initialize a newly created server to receive requests from and send
176 * responses to the given [channel]. 122 * responses to the given [channel].
177 * 123 *
178 * If [rethrowExceptions] is true, then any exceptions thrown by analysis are 124 * If [rethrowExceptions] is true, then any exceptions thrown by analysis are
179 * propagated up the call stack. The default is true to allow analysis 125 * propagated up the call stack. The default is true to allow analysis
180 * exceptions to show up in unit tests, but it should be set to false when 126 * exceptions to show up in unit tests, but it should be set to false when
181 * running a full analysis server. 127 * running a full analysis server.
182 */ 128 */
183 AnalysisServer(this.channel, ResourceProvider resourceProvider, 129 AnalysisServer(this.channel, ResourceProvider resourceProvider,
184 PackageMapProvider packageMapProvider, this.index, 130 PackageMapProvider packageMapProvider, this.index, {this.rethrowExceptions :
Brian Wilkerson 2014/07/02 13:58:06 I liked the old formatting better here too.
185 {this.rethrowExceptions: true}) { 131 true}) {
186 operationQueue = new ServerOperationQueue(this); 132 operationQueue = new ServerOperationQueue(this);
187 contextDirectoryManager = new AnalysisServerContextDirectoryManager( 133 contextDirectoryManager = new AnalysisServerContextDirectoryManager(this,
188 this, resourceProvider, packageMapProvider); 134 resourceProvider, packageMapProvider);
189 AnalysisEngine.instance.logger = new AnalysisLogger(); 135 AnalysisEngine.instance.logger = new AnalysisLogger();
190 running = true; 136 running = true;
191 Notification notification = new Notification(SERVER_CONNECTED); 137 Notification notification = new Notification(SERVER_CONNECTED);
192 channel.sendNotification(notification); 138 channel.sendNotification(notification);
193 channel.listen(handleRequest, onDone: done, onError: error); 139 channel.listen(handleRequest, onDone: done, onError: error);
194 } 140 }
195 141
196 /** 142 /**
197 * Schedules execution of the given [ServerOperation].
198 */
199 void scheduleOperation(ServerOperation operation) {
200 bool wasEmpty = operationQueue.isEmpty;
201 addOperation(operation);
202 if (wasEmpty) {
203 _schedulePerformOperation();
204 }
205 }
206
207 /**
208 * Schedules analysis of the given context.
209 */
210 void schedulePerformAnalysisOperation(AnalysisContext context) {
211 scheduleOperation(new PerformAnalysisOperation(context, false));
212 }
213
214 /**
215 * Send the given [notification] to the client.
216 */
217 void sendNotification(Notification notification) {
218 channel.sendNotification(notification);
219 }
220
221 /**
222 * Set the priority files to the given [files].
223 */
224 void setPriorityFiles(Request request, List<String> files) {
225 Map<AnalysisContext, List<Source>> sourceMap =
226 new HashMap<AnalysisContext, List<Source>>();
227 List<String> unanalyzed = new List<String>();
228 files.forEach((file) {
229 AnalysisContext analysisContext = _getAnalysisContext(file);
230 if (analysisContext == null) {
231 unanalyzed.add(file);
232 } else {
233 List<Source> sourceList = sourceMap[analysisContext];
234 if (sourceList == null) {
235 sourceList = <Source>[];
236 sourceMap[analysisContext] = sourceList;
237 }
238 sourceList.add(_getSource(file));
239 }
240 });
241 if (unanalyzed.isNotEmpty) {
242 StringBuffer buffer = new StringBuffer();
243 buffer.writeAll(unanalyzed, ', ');
244 throw new RequestFailure(new Response.unanalyzedPriorityFiles(request,
245 buffer.toString()));
246 }
247 folderMap.forEach((Folder folder, AnalysisContext context) {
248 List<Source> sourceList = sourceMap[context];
249 if (sourceList == null) {
250 sourceList = Source.EMPTY_ARRAY;
251 }
252 context.analysisPriorityOrder = sourceList;
253 });
254 }
255
256 /**
257 * Use the given updaters to update the values of the options in every
258 * existing analysis context.
259 */
260 void updateOptions(List<OptionUpdater> optionUpdaters) {
261 //
262 // Update existing contexts.
263 //
264 folderMap.forEach((Folder folder, AnalysisContext context) {
265 AnalysisOptionsImpl options = new AnalysisOptionsImpl.con1(context.analysi sOptions);
266 optionUpdaters.forEach((OptionUpdater optionUpdater) {
267 optionUpdater(options);
268 });
269 context.analysisOptions = options;
270 });
271 //
272 // Update the defaults used to create new contexts.
273 //
274 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions;
275 optionUpdaters.forEach((OptionUpdater optionUpdater) {
276 optionUpdater(options);
277 });
278 }
279
280 /**
281 * Adds the given [ServerOperation] to the queue, but does not schedule 143 * Adds the given [ServerOperation] to the queue, but does not schedule
282 * operations execution. 144 * operations execution.
283 */ 145 */
284 void addOperation(ServerOperation operation) { 146 void addOperation(ServerOperation operation) {
285 operationQueue.add(operation); 147 operationQueue.add(operation);
286 } 148 }
287 149
288 /** 150 /**
289 * The socket from which requests are being read has been closed. 151 * The socket from which requests are being read has been closed.
290 */ 152 */
291 void done() { 153 void done() {
292 index.stop(); 154 index.stop();
293 running = false; 155 running = false;
294 } 156 }
295 157
296 /** 158 /**
297 * There was an error related to the socket from which requests are being 159 * There was an error related to the socket from which requests are being
298 * read. 160 * read.
299 */ 161 */
300 void error(argument) { 162 void error(argument) {
301 running = false; 163 running = false;
302 } 164 }
303 165
304 // TODO(brianwilkerson) Add the following method after 'prioritySources' has 166 /**
305 // been added to InternalAnalysisContext. 167 * Returns the [CompilationUnit] of the Dart file with the given [path] that
306 // /** 168 * should be used to resend notifications for already resolved unit.
307 // * Return a list containing the full names of all of the sources that are 169 * Returns `null` if the file is not a part of any context, library has not
308 // * priority sources. 170 * been yet resolved, or any problem happened.
309 // */ 171 */
310 // List<String> getPriorityFiles() { 172 CompilationUnit getResolvedCompilationUnitToResendNotification(String path) {
311 // List<String> priorityFiles = new List<String>(); 173 // prepare AnalysisContext
312 // folderMap.values.forEach((ContextDirectory directory) { 174 AnalysisContext context = getAnalysisContext(path);
313 // InternalAnalysisContext context = directory.context; 175 if (context == null) {
314 // context.prioritySources.forEach((Source source) { 176 return null;
315 // priorityFiles.add(source.fullName); 177 }
316 // }); 178 // prepare sources
317 // }); 179 Source unitSource = getSource(path);
318 // return priorityFiles; 180 List<Source> librarySources = context.getLibrariesContaining(unitSource);
319 // } 181 if (librarySources.isEmpty) {
182 return null;
183 }
184 // if library has not been resolved yet, the unit will be resolved later
185 Source librarySource = librarySources[0];
186 if (context.getLibraryElement(librarySource) == null) {
187 return null;
188 }
189 // if library has been already resolved, resolve unit
190 return context.resolveCompilationUnit2(unitSource, librarySource);
191 }
320 192
321 /** 193 /**
322 * Handle a [request] that was read from the communication channel. 194 * Handle a [request] that was read from the communication channel.
323 */ 195 */
324 void handleRequest(Request request) { 196 void handleRequest(Request request) {
325 int count = handlers.length; 197 int count = handlers.length;
326 for (int i = 0; i < count; i++) { 198 for (int i = 0; i < count; i++) {
327 try { 199 try {
328 Response response = handlers[i].handleRequest(request); 200 Response response = handlers[i].handleRequest(request);
329 if (response != null) { 201 if (response != null) {
(...skipping 17 matching lines...) Expand all
347 } 219 }
348 220
349 /** 221 /**
350 * Returns `true` if the given [AnalysisContext] is a priority one. 222 * Returns `true` if the given [AnalysisContext] is a priority one.
351 */ 223 */
352 bool isPriorityContext(AnalysisContext context) { 224 bool isPriorityContext(AnalysisContext context) {
353 // TODO(scheglov) implement support for priority sources/contexts 225 // TODO(scheglov) implement support for priority sources/contexts
354 return false; 226 return false;
355 } 227 }
356 228
229 // TODO(brianwilkerson) Add the following method after 'prioritySources' has
230 // been added to InternalAnalysisContext.
231 // /**
232 // * Return a list containing the full names of all of the sources that are
233 // * priority sources.
234 // */
235 // List<String> getPriorityFiles() {
236 // List<String> priorityFiles = new List<String>();
237 // folderMap.values.forEach((ContextDirectory directory) {
238 // InternalAnalysisContext context = directory.context;
239 // context.prioritySources.forEach((Source source) {
240 // priorityFiles.add(source.fullName);
241 // });
242 // });
243 // return priorityFiles;
244 // }
245
357 /** 246 /**
358 * Perform the next available [ServerOperation]. 247 * Perform the next available [ServerOperation].
359 */ 248 */
360 void performOperation() { 249 void performOperation() {
361 if (!running) { 250 if (!running) {
362 // An error has occurred, or the connection to the client has been 251 // An error has occurred, or the connection to the client has been
363 // closed, since this method was scheduled on the event queue. So 252 // closed, since this method was scheduled on the event queue. So
364 // don't do anything. Instead clear the operation queue. 253 // don't do anything. Instead clear the operation queue.
365 operationQueue.clear(); 254 operationQueue.clear();
366 return; 255 return;
367 } 256 }
368 // prepare next operation 257 // prepare next operation
369 ServerOperation operation = operationQueue.take(); 258 ServerOperation operation = operationQueue.take();
370 sendStatusNotification(operation); 259 sendStatusNotification(operation);
371 // perform the operation 260 // perform the operation
372 try { 261 try {
373 operation.perform(this); 262 operation.perform(this);
374 } catch (exception, stackTrace) { 263 } catch (exception, stackTrace) {
375 AnalysisEngine.instance.logger.logError("${exception}\n${stackTrace}"); 264 AnalysisEngine.instance.logger.logError("${exception}\n${stackTrace}");
376 if (rethrowExceptions) { 265 if (rethrowExceptions) {
377 throw new AnalysisException( 266 throw new AnalysisException('Unexpected exception during analysis',
378 'Unexpected exception during analysis',
379 new CaughtException(exception, stackTrace)); 267 new CaughtException(exception, stackTrace));
380 } 268 }
381 } finally { 269 } finally {
382 if (!operationQueue.isEmpty) { 270 if (!operationQueue.isEmpty) {
383 _schedulePerformOperation(); 271 _schedulePerformOperation();
384 } else { 272 } else {
385 sendStatusNotification(null); 273 sendStatusNotification(null);
386 } 274 }
387 } 275 }
388 } 276 }
389 277
390 /** 278 /**
279 * Schedules execution of the given [ServerOperation].
280 */
281 void scheduleOperation(ServerOperation operation) {
282 bool wasEmpty = operationQueue.isEmpty;
283 addOperation(operation);
284 if (wasEmpty) {
285 _schedulePerformOperation();
286 }
287 }
288
289 /**
290 * Schedules analysis of the given context.
291 */
292 void schedulePerformAnalysisOperation(AnalysisContext context) {
293 scheduleOperation(new PerformAnalysisOperation(context, false));
294 }
295
296 /**
297 * Send the given [notification] to the client.
298 */
299 void sendNotification(Notification notification) {
300 channel.sendNotification(notification);
301 }
302
303 /**
391 * Send status notification to the client. The `operation` is the operation 304 * Send status notification to the client. The `operation` is the operation
392 * being performed or `null` if analysis is complete. 305 * being performed or `null` if analysis is complete.
393 */ 306 */
394 void sendStatusNotification(ServerOperation operation) { 307 void sendStatusNotification(ServerOperation operation) {
395 // Only send status when it changes 308 // Only send status when it changes
396 bool isAnalyzing = operation != null; 309 bool isAnalyzing = operation != null;
397 if (statusAnalyzing == isAnalyzing) { 310 if (statusAnalyzing == isAnalyzing) {
398 return; 311 return;
399 } 312 }
400 statusAnalyzing = isAnalyzing; 313 statusAnalyzing = isAnalyzing;
401 Notification notification = new Notification(SERVER_STATUS); 314 Notification notification = new Notification(SERVER_STATUS);
402 Map<String, Object> analysis = new HashMap(); 315 Map<String, Object> analysis = new HashMap();
403 analysis['analyzing'] = isAnalyzing; 316 analysis['analyzing'] = isAnalyzing;
404 notification.params['analysis'] = analysis; 317 notification.params['analysis'] = analysis;
405 channel.sendNotification(notification); 318 channel.sendNotification(notification);
406 } 319 }
407 320
408 /** 321 /**
409 * Implementation for `analysis.setAnalysisRoots`. 322 * Implementation for `analysis.setAnalysisRoots`.
410 * 323 *
411 * TODO(scheglov) implement complete projects/contexts semantics. 324 * TODO(scheglov) implement complete projects/contexts semantics.
412 * 325 *
413 * The current implementation is intentionally simplified and expected 326 * The current implementation is intentionally simplified and expected
414 * that only folders are given each given folder corresponds to the exactly 327 * that only folders are given each given folder corresponds to the exactly
415 * one context. 328 * one context.
416 * 329 *
417 * So, we can start working in parallel on adding services and improving 330 * So, we can start working in parallel on adding services and improving
418 * projects/contexts support. 331 * projects/contexts support.
419 */ 332 */
420 void setAnalysisRoots(String requestId, 333 void setAnalysisRoots(String requestId, List<String> includedPaths,
421 List<String> includedPaths, 334 List<String> excludedPaths) {
422 List<String> excludedPaths) {
423 try { 335 try {
424 contextDirectoryManager.setRoots(includedPaths, excludedPaths); 336 contextDirectoryManager.setRoots(includedPaths, excludedPaths);
425 } on UnimplementedError catch (e) { 337 } on UnimplementedError catch (e) {
426 throw new RequestFailure( 338 throw new RequestFailure(new Response.unsupportedFeature(requestId,
427 new Response.unsupportedFeature( 339 e.message));
428 requestId, e.message));
429 } 340 }
430 } 341 }
431 342
432 /** 343 /**
433 * Implementation for `analysis.updateContent`.
434 */
435 void updateContent(Map<String, ContentChange> changes) {
436 changes.forEach((file, change) {
437 AnalysisContext analysisContext = _getAnalysisContext(file);
438 // TODO(paulberry): handle the case where a file is referred to by more
439 // than one context (e.g package A depends on package B using a local
440 // path, user has both packages open for editing in separate contexts,
441 // and user modifies a file in package B).
442 if (analysisContext != null) {
443 Source source = _getSource(file);
444 if (change.offset == null) {
445 analysisContext.setContents(source, change.content);
446 } else {
447 analysisContext.setChangedContents(source, change.content,
448 change.offset, change.oldLength, change.newLength);
449 }
450 schedulePerformAnalysisOperation(analysisContext);
451 }
452 });
453 }
454
455 /**
456 * Implementation for `analysis.setSubscriptions`. 344 * Implementation for `analysis.setSubscriptions`.
457 */ 345 */
458 void setAnalysisSubscriptions(Map<AnalysisService, Set<String>> subscriptions) { 346 void setAnalysisSubscriptions(Map<AnalysisService,
347 Set<String>> subscriptions) {
459 // send notifications for already analyzed sources 348 // send notifications for already analyzed sources
460 subscriptions.forEach((service, Set<String> newFiles) { 349 subscriptions.forEach((service, Set<String> newFiles) {
461 Set<String> oldFiles = analysisServices[service]; 350 Set<String> oldFiles = analysisServices[service];
462 Set<String> todoFiles = oldFiles != null ? newFiles.difference(oldFiles) : newFiles; 351 Set<String> todoFiles = oldFiles != null ? newFiles.difference(oldFiles) :
352 newFiles;
463 for (String file in todoFiles) { 353 for (String file in todoFiles) {
464 Source source = _getSource(file); 354 Source source = getSource(file);
465 AnalysisContext context = _getAnalysisContext(file); 355 AnalysisContext context = getAnalysisContext(file);
466 // errors 356 // errors
467 if (service == AnalysisService.ERRORS) { 357 if (service == AnalysisService.ERRORS) {
468 LineInfo lineInfo = context.getLineInfo(source); 358 LineInfo lineInfo = context.getLineInfo(source);
469 List<AnalysisError> errors = context.getErrors(source).errors; 359 List<AnalysisError> errors = context.getErrors(source).errors;
470 sendAnalysisNotificationErrors(this, file, lineInfo, errors); 360 sendAnalysisNotificationErrors(this, file, lineInfo, errors);
471 } 361 }
472 // Dart unit notifications. 362 // Dart unit notifications.
473 if (AnalysisEngine.isDartFileName(file)) { 363 if (AnalysisEngine.isDartFileName(file)) {
474 CompilationUnit dartUnit = getResolvedCompilationUnitToResendNotificat ion(file); 364 CompilationUnit dartUnit =
365 getResolvedCompilationUnitToResendNotification(file);
475 if (dartUnit != null) { 366 if (dartUnit != null) {
476 switch (service) { 367 switch (service) {
477 case AnalysisService.HIGHLIGHTS: 368 case AnalysisService.HIGHLIGHTS:
478 sendAnalysisNotificationHighlights(this, file, dartUnit); 369 sendAnalysisNotificationHighlights(this, file, dartUnit);
479 break; 370 break;
480 case AnalysisService.NAVIGATION: 371 case AnalysisService.NAVIGATION:
481 // TODO(scheglov) consider support for one unit in 2+ libraries 372 // TODO(scheglov) consider support for one unit in 2+ libraries
482 sendAnalysisNotificationNavigation(this, file, dartUnit); 373 sendAnalysisNotificationNavigation(this, file, dartUnit);
483 break; 374 break;
484 case AnalysisService.OUTLINE: 375 case AnalysisService.OUTLINE:
485 sendAnalysisNotificationOutline(this, context, source, dartUnit) ; 376 sendAnalysisNotificationOutline(this, context, source,
377 dartUnit);
486 break; 378 break;
487 } 379 }
488 } 380 }
489 } 381 }
490 } 382 }
491 }); 383 });
492 // remember new subscriptions 384 // remember new subscriptions
493 this.analysisServices = subscriptions; 385 this.analysisServices = subscriptions;
494 } 386 }
495 387
496 /** 388 /**
497 * Return the [AnalysisContext] that is used to analyze the given [path]. 389 * Set the priority files to the given [files].
498 * Return `null` if there is no such context.
499 */ 390 */
500 AnalysisContext _getAnalysisContext(String path) { 391 void setPriorityFiles(Request request, List<String> files) {
501 for (Folder folder in folderMap.keys) { 392 Map<AnalysisContext, List<Source>> sourceMap = new HashMap<AnalysisContext,
502 if (path.startsWith(folder.path)) { 393 List<Source>>();
503 return folderMap[folder]; 394 List<String> unanalyzed = new List<String>();
395 files.forEach((file) {
396 AnalysisContext analysisContext = getAnalysisContext(file);
397 if (analysisContext == null) {
398 unanalyzed.add(file);
399 } else {
400 List<Source> sourceList = sourceMap[analysisContext];
401 if (sourceList == null) {
402 sourceList = <Source>[];
403 sourceMap[analysisContext] = sourceList;
404 }
405 sourceList.add(getSource(file));
504 } 406 }
407 });
408 if (unanalyzed.isNotEmpty) {
409 StringBuffer buffer = new StringBuffer();
410 buffer.writeAll(unanalyzed, ', ');
411 throw new RequestFailure(new Response.unanalyzedPriorityFiles(request,
412 buffer.toString()));
505 } 413 }
506 return null; 414 folderMap.forEach((Folder folder, AnalysisContext context) {
415 List<Source> sourceList = sourceMap[context];
416 if (sourceList == null) {
417 sourceList = Source.EMPTY_ARRAY;
418 }
419 context.analysisPriorityOrder = sourceList;
420 });
507 } 421 }
508 422
509 /** 423 /**
510 * Return the [Source] of the Dart file with the given [path]. 424 * Return `true` if all operations have been performed in this [AnalysisServer ].
511 */ 425 */
512 Source _getSource(String path) { 426 bool test_areOperationsFinished() {
513 File file = contextDirectoryManager.resourceProvider.getResource(path); 427 return operationQueue.isEmpty;
514 return file.createSource(UriKind.FILE_URI);
515 } 428 }
516 429
517 /** 430 /**
518 * Returns the [CompilationUnit] of the Dart file with the given [path] that
519 * should be used to resend notifications for already resolved unit.
520 * Returns `null` if the file is not a part of any context, library has not
521 * been yet resolved, or any problem happened.
522 */
523 CompilationUnit getResolvedCompilationUnitToResendNotification(String path) {
524 // prepare AnalysisContext
525 AnalysisContext context = _getAnalysisContext(path);
526 if (context == null) {
527 return null;
528 }
529 // prepare sources
530 Source unitSource = _getSource(path);
531 List<Source> librarySources = context.getLibrariesContaining(unitSource);
532 if (librarySources.isEmpty) {
533 return null;
534 }
535 // if library has not been resolved yet, the unit will be resolved later
536 Source librarySource = librarySources[0];
537 if (context.getLibraryElement(librarySource) == null) {
538 return null;
539 }
540 // if library has been already resolved, resolve unit
541 return context.resolveCompilationUnit2(unitSource, librarySource);
542 }
543
544 /**
545 * Return the [CompilationUnit] of the Dart file with the given [path]. 431 * Return the [CompilationUnit] of the Dart file with the given [path].
546 * Return `null` if the file is not a part of any context. 432 * Return `null` if the file is not a part of any context.
547 */ 433 */
548 CompilationUnit test_getResolvedCompilationUnit(String path) { 434 CompilationUnit test_getResolvedCompilationUnit(String path) {
549 // prepare AnalysisContext 435 // prepare AnalysisContext
550 AnalysisContext context = _getAnalysisContext(path); 436 AnalysisContext context = getAnalysisContext(path);
551 if (context == null) { 437 if (context == null) {
552 return null; 438 return null;
553 } 439 }
554 // prepare sources 440 // prepare sources
555 Source unitSource = _getSource(path); 441 Source unitSource = getSource(path);
556 List<Source> librarySources = context.getLibrariesContaining(unitSource); 442 List<Source> librarySources = context.getLibrariesContaining(unitSource);
557 if (librarySources.isEmpty) { 443 if (librarySources.isEmpty) {
558 return null; 444 return null;
559 } 445 }
560 // get a resolved unit 446 // get a resolved unit
561 return context.getResolvedCompilationUnit2(unitSource, librarySources[0]); 447 return context.getResolvedCompilationUnit2(unitSource, librarySources[0]);
562 } 448 }
563 449
564 /** 450 /**
565 * Return `true` if all operations have been performed in this [AnalysisServer ]. 451 * Implementation for `analysis.updateContent`.
566 */ 452 */
567 bool test_areOperationsFinished() { 453 void updateContent(Map<String, ContentChange> changes) {
568 return operationQueue.isEmpty; 454 changes.forEach((file, change) {
455 AnalysisContext analysisContext = getAnalysisContext(file);
456 // TODO(paulberry): handle the case where a file is referred to by more
457 // than one context (e.g package A depends on package B using a local
458 // path, user has both packages open for editing in separate contexts,
459 // and user modifies a file in package B).
460 if (analysisContext != null) {
461 Source source = getSource(file);
462 if (change.offset == null) {
463 analysisContext.setContents(source, change.content);
464 } else {
465 analysisContext.setChangedContents(source, change.content,
466 change.offset, change.oldLength, change.newLength);
467 }
468 schedulePerformAnalysisOperation(analysisContext);
469 }
470 });
569 } 471 }
570 472
571 /** 473 /**
474 * Use the given updaters to update the values of the options in every
475 * existing analysis context.
476 */
477 void updateOptions(List<OptionUpdater> optionUpdaters) {
478 //
479 // Update existing contexts.
480 //
481 folderMap.forEach((Folder folder, AnalysisContext context) {
482 AnalysisOptionsImpl options = new AnalysisOptionsImpl.con1(
483 context.analysisOptions);
484 optionUpdaters.forEach((OptionUpdater optionUpdater) {
485 optionUpdater(options);
486 });
487 context.analysisOptions = options;
488 });
489 //
490 // Update the defaults used to create new contexts.
491 //
492 AnalysisOptionsImpl options = contextDirectoryManager.defaultOptions;
493 optionUpdaters.forEach((OptionUpdater optionUpdater) {
494 optionUpdater(options);
495 });
496 }
497
498 /**
499 * Return the [AnalysisContext] that is used to analyze the given [path].
500 * Return `null` if there is no such context.
501 */
502 AnalysisContext getAnalysisContext(String path) {
503 for (Folder folder in folderMap.keys) {
504 if (path.startsWith(folder.path)) {
505 return folderMap[folder];
506 }
507 }
508 return null;
509 }
510
511 /**
512 * Return the [Source] of the Dart file with the given [path].
513 */
514 Source getSource(String path) {
515 File file = contextDirectoryManager.resourceProvider.getResource(path);
516 return file.createSource(UriKind.FILE_URI);
517 }
518
519 /**
572 * Schedules [performOperation] exection. 520 * Schedules [performOperation] exection.
573 */ 521 */
574 void _schedulePerformOperation() { 522 void _schedulePerformOperation() {
575 new Future(performOperation); 523 new Future(performOperation);
576 } 524 }
577 } 525 }
578 526
579 527
528 class AnalysisServerContextDirectoryManager extends ContextDirectoryManager {
529 final AnalysisServer analysisServer;
530
531 /**
532 * The default options used to create new analysis contexts.
533 */
534 AnalysisOptionsImpl defaultOptions = new AnalysisOptionsImpl();
535
536 AnalysisServerContextDirectoryManager(this.analysisServer,
537 ResourceProvider resourceProvider, PackageMapProvider packageMapProvider)
538 : super(resourceProvider, packageMapProvider);
539
540 @override
541 void addContext(Folder folder, Map<String, List<Folder>> packageMap) {
542 AnalysisContext context = AnalysisEngine.instance.createAnalysisContext();
543 analysisServer.folderMap[folder] = context;
544 context.sourceFactory = _createSourceFactory(packageMap);
545 context.analysisOptions = new AnalysisOptionsImpl.con1(defaultOptions);
546 analysisServer.schedulePerformAnalysisOperation(context);
547 }
548
549 @override
550 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet) {
551 AnalysisContext context = analysisServer.folderMap[contextFolder];
552 context.applyChanges(changeSet);
553 analysisServer.schedulePerformAnalysisOperation(context);
554 }
555
556 @override
557 void removeContext(Folder folder) {
558 analysisServer.folderMap.remove(folder);
559 }
560
561 @override
562 void updateContextPackageMap(Folder contextFolder, Map<String,
563 List<Folder>> packageMap) {
564 AnalysisContext context = analysisServer.folderMap[contextFolder];
565 context.sourceFactory = _createSourceFactory(packageMap);
566 analysisServer.schedulePerformAnalysisOperation(context);
567 }
568
569 /**
570 * Set up a [SourceFactory] that resolves packages using the given
571 * [packageMap].
572 */
573 SourceFactory _createSourceFactory(Map<String, List<Folder>> packageMap) {
574 List<UriResolver> resolvers = <UriResolver>[new DartUriResolver(
575 analysisServer.defaultSdk), new ResourceUriResolver(resourceProvider),
576 new PackageMapUriResolver(resourceProvider, packageMap)];
577 return new SourceFactory(resolvers);
578 }
579 }
580
581
580 /** 582 /**
581 * An enumeration of the services provided by the analysis domain. 583 * An enumeration of the services provided by the analysis domain.
582 */ 584 */
583 class AnalysisService extends Enum2<AnalysisService> { 585 class AnalysisService extends Enum2<AnalysisService> {
584 static const AnalysisService ERRORS = const AnalysisService('ERRORS', 0); 586 static const AnalysisService ERRORS = const AnalysisService('ERRORS', 0);
585 static const AnalysisService HIGHLIGHTS = const AnalysisService('HIGHLIGHTS', 1); 587 static const AnalysisService HIGHLIGHTS = const AnalysisService('HIGHLIGHTS',
586 static const AnalysisService NAVIGATION = const AnalysisService('NAVIGATION', 2); 588 1);
589 static const AnalysisService NAVIGATION = const AnalysisService('NAVIGATION',
590 2);
587 static const AnalysisService OUTLINE = const AnalysisService('OUTLINE', 3); 591 static const AnalysisService OUTLINE = const AnalysisService('OUTLINE', 3);
588 592
589 static const List<AnalysisService> VALUES = 593 static const List<AnalysisService> VALUES = const [ERRORS, HIGHLIGHTS,
590 const [ERRORS, HIGHLIGHTS, NAVIGATION, OUTLINE]; 594 NAVIGATION, OUTLINE];
591 595
592 const AnalysisService(String name, int ordinal) : super(name, ordinal); 596 const AnalysisService(String name, int ordinal) : super(name, ordinal);
593 } 597 }
594 598
595
596 typedef void OptionUpdater(AnalysisOptionsImpl options);
597
598 /** 599 /**
599 * An enumeration of the services provided by the server domain. 600 * An enumeration of the services provided by the server domain.
600 */ 601 */
601 class ServerService extends Enum2<ServerService> { 602 class ServerService extends Enum2<ServerService> {
602 static const ServerService STATUS = const ServerService('STATUS', 0); 603 static const ServerService STATUS = const ServerService('STATUS', 0);
603 604
604 static const List<ServerService> VALUES = const [STATUS]; 605 static const List<ServerService> VALUES = const [STATUS];
605 606
606 const ServerService(String name, int ordinal) : super(name, ordinal); 607 const ServerService(String name, int ordinal) : super(name, ordinal);
607 } 608 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698