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 context.directory.manager; | 5 library context.directory.manager; |
6 | 6 |
7 import 'dart:async'; | 7 import 'dart:async'; |
8 import 'dart:collection'; | 8 import 'dart:collection'; |
9 import 'dart:convert'; | 9 import 'dart:convert'; |
10 import 'dart:core' hide Resource; | 10 import 'dart:core' hide Resource; |
(...skipping 11 matching lines...) Expand all Loading... |
22 import 'package:analyzer/src/generated/source.dart'; | 22 import 'package:analyzer/src/generated/source.dart'; |
23 import 'package:analyzer/src/generated/source_io.dart'; | 23 import 'package:analyzer/src/generated/source_io.dart'; |
24 import 'package:package_config/packages.dart'; | 24 import 'package:package_config/packages.dart'; |
25 import 'package:package_config/packages_file.dart' as pkgfile show parse; | 25 import 'package:package_config/packages_file.dart' as pkgfile show parse; |
26 import 'package:package_config/src/packages_impl.dart' show MapPackages; | 26 import 'package:package_config/src/packages_impl.dart' show MapPackages; |
27 import 'package:path/path.dart' as pathos; | 27 import 'package:path/path.dart' as pathos; |
28 import 'package:watcher/watcher.dart'; | 28 import 'package:watcher/watcher.dart'; |
29 import 'package:yaml/yaml.dart'; | 29 import 'package:yaml/yaml.dart'; |
30 | 30 |
31 /** | 31 /** |
| 32 * Information tracked by the [ContextManager] for each context. |
| 33 */ |
| 34 class ContextInfo { |
| 35 /** |
| 36 * The [Folder] for which this information object is created. |
| 37 */ |
| 38 final Folder folder; |
| 39 |
| 40 /// The [PathFilter] used to filter sources from being analyzed. |
| 41 final PathFilter pathFilter; |
| 42 |
| 43 /** |
| 44 * The enclosed pubspec-based contexts. |
| 45 */ |
| 46 final List<ContextInfo> children; |
| 47 |
| 48 /** |
| 49 * The package root for this context, or null if there is no package root. |
| 50 */ |
| 51 String packageRoot; |
| 52 |
| 53 /** |
| 54 * The [ContextInfo] that encloses this one. |
| 55 */ |
| 56 ContextInfo parent; |
| 57 |
| 58 /** |
| 59 * The package description file path for this context. |
| 60 */ |
| 61 String packageDescriptionPath; |
| 62 |
| 63 /** |
| 64 * Stream subscription we are using to watch the context's directory for |
| 65 * changes. |
| 66 */ |
| 67 StreamSubscription<WatchEvent> changeSubscription; |
| 68 |
| 69 /** |
| 70 * Stream subscriptions we are using to watch the files |
| 71 * used to determine the package map. |
| 72 */ |
| 73 final List<StreamSubscription<WatchEvent>> dependencySubscriptions = |
| 74 <StreamSubscription<WatchEvent>>[]; |
| 75 |
| 76 /** |
| 77 * The analysis context that was created for the [folder]. |
| 78 */ |
| 79 AnalysisContext context; |
| 80 |
| 81 /** |
| 82 * Map from full path to the [Source] object, for each source that has been |
| 83 * added to the context. |
| 84 */ |
| 85 Map<String, Source> sources = new HashMap<String, Source>(); |
| 86 |
| 87 /** |
| 88 * Info returned by the last call to |
| 89 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the |
| 90 * package map hasn't been computed for this context yet. |
| 91 */ |
| 92 OptimizingPubPackageMapInfo packageMapInfo; |
| 93 |
| 94 ContextInfo( |
| 95 Folder folder, File packagespecFile, this.children, this.packageRoot) |
| 96 : folder = folder, |
| 97 pathFilter = new PathFilter(folder.path, null) { |
| 98 packageDescriptionPath = packagespecFile.path; |
| 99 for (ContextInfo child in children) { |
| 100 child.parent = this; |
| 101 } |
| 102 } |
| 103 |
| 104 /** |
| 105 * Returns `true` if this context is root folder based. |
| 106 */ |
| 107 bool get isRoot => parent == null; |
| 108 |
| 109 /** |
| 110 * Returns `true` if [path] is excluded, as it is in one of the children. |
| 111 */ |
| 112 bool excludes(String path) { |
| 113 return children.any((child) { |
| 114 return child.folder.contains(path); |
| 115 }); |
| 116 } |
| 117 |
| 118 /** |
| 119 * Returns `true` if [resource] is excluded, as it is in one of the children. |
| 120 */ |
| 121 bool excludesResource(Resource resource) => excludes(resource.path); |
| 122 |
| 123 /// Returns `true` if [path] should be ignored. |
| 124 bool ignored(String path) => pathFilter.ignored(path); |
| 125 |
| 126 /** |
| 127 * Returns `true` if [path] is the package description file for this context |
| 128 * (pubspec.yaml or .packages). |
| 129 */ |
| 130 bool isPathToPackageDescription(String path) => |
| 131 path == packageDescriptionPath; |
| 132 } |
| 133 |
| 134 /** |
32 * Class that maintains a mapping from included/excluded paths to a set of | 135 * Class that maintains a mapping from included/excluded paths to a set of |
33 * folders that should correspond to analysis contexts. | 136 * folders that should correspond to analysis contexts. |
34 */ | 137 */ |
35 abstract class ContextManager { | 138 abstract class ContextManager { |
36 // TODO(brianwilkerson) Support: | 139 // TODO(brianwilkerson) Support: |
37 // setting the default analysis options | 140 // setting the default analysis options |
38 // setting the default content cache | 141 // setting the default content cache |
39 // setting the default SDK | 142 // setting the default SDK |
40 // maintaining AnalysisContext.folderMap (or remove it) | 143 // maintaining AnalysisContext.folderMap (or remove it) |
41 // telling server when a context has been added or removed (see onContextsCh
anged) | 144 // telling server when a context has been added or removed (see onContextsCh
anged) |
(...skipping 135 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
177 * File name of pubspec files. | 280 * File name of pubspec files. |
178 */ | 281 */ |
179 static const String PUBSPEC_NAME = 'pubspec.yaml'; | 282 static const String PUBSPEC_NAME = 'pubspec.yaml'; |
180 | 283 |
181 /** | 284 /** |
182 * File name of package spec files. | 285 * File name of package spec files. |
183 */ | 286 */ |
184 static const String PACKAGE_SPEC_NAME = '.packages'; | 287 static const String PACKAGE_SPEC_NAME = '.packages'; |
185 | 288 |
186 /** | 289 /** |
187 * [_ContextInfo] object for each included directory in the most | 290 * [ContextInfo] object for each included directory in the most |
188 * recent successful call to [setRoots]. | 291 * recent successful call to [setRoots]. |
189 */ | 292 */ |
190 Map<Folder, _ContextInfo> _contexts = new HashMap<Folder, _ContextInfo>(); | 293 Map<Folder, ContextInfo> _contexts = new HashMap<Folder, ContextInfo>(); |
191 | 294 |
192 /** | 295 /** |
193 * The [ResourceProvider] using which paths are converted into [Resource]s. | 296 * The [ResourceProvider] using which paths are converted into [Resource]s. |
194 */ | 297 */ |
195 final ResourceProvider resourceProvider; | 298 final ResourceProvider resourceProvider; |
196 | 299 |
197 /** | 300 /** |
198 * The context used to work with file system paths. | 301 * The context used to work with file system paths. |
199 */ | 302 */ |
200 pathos.Context pathContext; | 303 pathos.Context pathContext; |
(...skipping 47 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
248 ContextManagerCallbacks callbacks; | 351 ContextManagerCallbacks callbacks; |
249 | 352 |
250 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, | 353 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, |
251 this._packageMapProvider, this._instrumentationService) { | 354 this._packageMapProvider, this._instrumentationService) { |
252 pathContext = resourceProvider.pathContext; | 355 pathContext = resourceProvider.pathContext; |
253 } | 356 } |
254 | 357 |
255 @override | 358 @override |
256 List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) { | 359 List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) { |
257 List<AnalysisContext> contexts = <AnalysisContext>[]; | 360 List<AnalysisContext> contexts = <AnalysisContext>[]; |
258 _contexts.forEach((Folder contextFolder, _ContextInfo info) { | 361 _contexts.forEach((Folder contextFolder, ContextInfo info) { |
259 if (analysisRoot.isOrContains(contextFolder.path)) { | 362 if (analysisRoot.isOrContains(contextFolder.path)) { |
260 contexts.add(info.context); | 363 contexts.add(info.context); |
261 } | 364 } |
262 }); | 365 }); |
263 return contexts; | 366 return contexts; |
264 } | 367 } |
265 | 368 |
| 369 /** |
| 370 * For testing: get the [ContextInfo] object for the given [folder], if any. |
| 371 */ |
| 372 ContextInfo getContextInfoFor(Folder folder) => _contexts[folder]; |
| 373 |
266 @override | 374 @override |
267 bool isInAnalysisRoot(String path) { | 375 bool isInAnalysisRoot(String path) { |
268 // check if excluded | 376 // check if excluded |
269 if (_isExcluded(path)) { | 377 if (_isExcluded(path)) { |
270 return false; | 378 return false; |
271 } | 379 } |
272 // check if in of the roots | 380 // check if in of the roots |
273 for (Folder root in _contexts.keys) { | 381 for (Folder root in _contexts.keys) { |
274 if (root.contains(path)) { | 382 if (root.contains(path)) { |
275 return true; | 383 return true; |
276 } | 384 } |
277 } | 385 } |
278 // no | 386 // no |
279 return false; | 387 return false; |
280 } | 388 } |
281 | 389 |
282 /// Process [options] for the context [folder]. | 390 /** |
283 void processOptionsForContext(Folder folder, Map<String, YamlNode> options) { | 391 * Process [options] for the context having info [info]. |
284 _ContextInfo info = _contexts[folder]; | 392 */ |
285 if (info == null) { | 393 void processOptionsForContext( |
286 return; | 394 ContextInfo info, Map<String, YamlNode> options) { |
287 } | |
288 YamlMap analyzer = options['analyzer']; | 395 YamlMap analyzer = options['analyzer']; |
289 if (analyzer == null) { | 396 if (analyzer == null) { |
290 // No options for analyzer. | 397 // No options for analyzer. |
291 return; | 398 return; |
292 } | 399 } |
293 | 400 |
294 // Set ignore patterns. | 401 // Set ignore patterns. |
295 YamlList exclude = analyzer['exclude']; | 402 YamlList exclude = analyzer['exclude']; |
296 if (exclude != null) { | 403 if (exclude != null) { |
297 setIgnorePatternsForContext(folder, exclude); | 404 setIgnorePatternsForContext(info, exclude); |
298 } | 405 } |
299 } | 406 } |
300 | 407 |
301 @override | 408 @override |
302 void refresh(List<Resource> roots) { | 409 void refresh(List<Resource> roots) { |
303 // Destroy old contexts | 410 // Destroy old contexts |
304 List<Folder> contextFolders = _contexts.keys.toList(); | 411 List<Folder> contextFolders = _contexts.keys.toList(); |
305 if (roots == null) { | 412 if (roots == null) { |
306 contextFolders.forEach(_destroyContext); | 413 contextFolders.forEach(_destroyContext); |
307 } else { | 414 } else { |
308 roots.forEach((Resource resource) { | 415 roots.forEach((Resource resource) { |
309 contextFolders.forEach((Folder contextFolder) { | 416 contextFolders.forEach((Folder contextFolder) { |
310 if (resource is Folder && resource.isOrContains(contextFolder.path)) { | 417 if (resource is Folder && resource.isOrContains(contextFolder.path)) { |
311 _destroyContext(contextFolder); | 418 _destroyContext(contextFolder); |
312 } | 419 } |
313 }); | 420 }); |
314 }); | 421 }); |
315 } | 422 } |
316 | 423 |
317 // Rebuild contexts based on the data last sent to setRoots(). | 424 // Rebuild contexts based on the data last sent to setRoots(). |
318 setRoots(includedPaths, excludedPaths, packageRoots); | 425 setRoots(includedPaths, excludedPaths, packageRoots); |
319 } | 426 } |
320 | 427 |
321 /// Sets the [ignorePatterns] for the context [folder]. | 428 /** |
322 void setIgnorePatternsForContext(Folder folder, List<String> ignorePatterns) { | 429 * Sets the [ignorePatterns] for the context having info [info]. |
323 _ContextInfo info = _contexts[folder]; | 430 */ |
324 if (info == null) { | 431 void setIgnorePatternsForContext( |
325 return; | 432 ContextInfo info, List<String> ignorePatterns) { |
326 } | 433 info.pathFilter.setIgnorePatterns(ignorePatterns); |
327 var pathFilter = info.pathFilter; | |
328 pathFilter.setIgnorePatterns(ignorePatterns); | |
329 } | 434 } |
330 | 435 |
331 @override | 436 @override |
332 void setRoots(List<String> includedPaths, List<String> excludedPaths, | 437 void setRoots(List<String> includedPaths, List<String> excludedPaths, |
333 Map<String, String> packageRoots) { | 438 Map<String, String> packageRoots) { |
334 this.packageRoots = packageRoots; | 439 this.packageRoots = packageRoots; |
335 | 440 |
336 // Normalize all package root sources by mapping them to folders on the | 441 // Normalize all package root sources by mapping them to folders on the |
337 // filesystem. Ignore any package root sources that aren't folders. | 442 // filesystem. Ignore any package root sources that aren't folders. |
338 normalizedPackageRoots = <String, String>{}; | 443 normalizedPackageRoots = <String, String>{}; |
(...skipping 25 matching lines...) Expand all Loading... |
364 // destroy old contexts | 469 // destroy old contexts |
365 for (Folder contextFolder in contextFolders) { | 470 for (Folder contextFolder in contextFolders) { |
366 bool isIncluded = includedFolders.any((folder) { | 471 bool isIncluded = includedFolders.any((folder) { |
367 return folder.isOrContains(contextFolder.path); | 472 return folder.isOrContains(contextFolder.path); |
368 }); | 473 }); |
369 if (!isIncluded) { | 474 if (!isIncluded) { |
370 _destroyContext(contextFolder); | 475 _destroyContext(contextFolder); |
371 } | 476 } |
372 } | 477 } |
373 // Update package roots for existing contexts | 478 // Update package roots for existing contexts |
374 _contexts.forEach((Folder folder, _ContextInfo info) { | 479 _contexts.forEach((Folder folder, ContextInfo info) { |
375 String newPackageRoot = normalizedPackageRoots[folder.path]; | 480 String newPackageRoot = normalizedPackageRoots[folder.path]; |
376 if (info.packageRoot != newPackageRoot) { | 481 if (info.packageRoot != newPackageRoot) { |
377 info.packageRoot = newPackageRoot; | 482 info.packageRoot = newPackageRoot; |
378 _recomputePackageUriResolver(info); | 483 _recomputePackageUriResolver(info); |
379 } | 484 } |
380 }); | 485 }); |
381 // create new contexts | 486 // create new contexts |
382 for (Folder includedFolder in includedFolders) { | 487 for (Folder includedFolder in includedFolders) { |
383 bool wasIncluded = contextFolders.any((folder) { | 488 bool wasIncluded = contextFolders.any((folder) { |
384 return folder.isOrContains(includedFolder.path); | 489 return folder.isOrContains(includedFolder.path); |
(...skipping 24 matching lines...) Expand all Loading... |
409 _contexts.forEach((folder, info) { | 514 _contexts.forEach((folder, info) { |
410 ChangeSet changeSet = new ChangeSet(); | 515 ChangeSet changeSet = new ChangeSet(); |
411 _addPreviouslyExcludedSources(info, changeSet, folder, oldExcludedPaths); | 516 _addPreviouslyExcludedSources(info, changeSet, folder, oldExcludedPaths); |
412 callbacks.applyChangesToContext(folder, changeSet); | 517 callbacks.applyChangesToContext(folder, changeSet); |
413 }); | 518 }); |
414 } | 519 } |
415 | 520 |
416 /** | 521 /** |
417 * Resursively adds all Dart and HTML files to the [changeSet]. | 522 * Resursively adds all Dart and HTML files to the [changeSet]. |
418 */ | 523 */ |
419 void _addPreviouslyExcludedSources(_ContextInfo info, ChangeSet changeSet, | 524 void _addPreviouslyExcludedSources(ContextInfo info, ChangeSet changeSet, |
420 Folder folder, List<String> oldExcludedPaths) { | 525 Folder folder, List<String> oldExcludedPaths) { |
421 if (info.excludesResource(folder)) { | 526 if (info.excludesResource(folder)) { |
422 return; | 527 return; |
423 } | 528 } |
424 List<Resource> children; | 529 List<Resource> children; |
425 try { | 530 try { |
426 children = folder.getChildren(); | 531 children = folder.getChildren(); |
427 } on FileSystemException { | 532 } on FileSystemException { |
428 // The folder no longer exists, or cannot be read, to there's nothing to | 533 // The folder no longer exists, or cannot be read, to there's nothing to |
429 // do. | 534 // do. |
(...skipping 26 matching lines...) Expand all Loading... |
456 continue; | 561 continue; |
457 } | 562 } |
458 _addPreviouslyExcludedSources(info, changeSet, child, oldExcludedPaths); | 563 _addPreviouslyExcludedSources(info, changeSet, child, oldExcludedPaths); |
459 } | 564 } |
460 } | 565 } |
461 } | 566 } |
462 | 567 |
463 /** | 568 /** |
464 * Resursively adds all Dart and HTML files to the [changeSet]. | 569 * Resursively adds all Dart and HTML files to the [changeSet]. |
465 */ | 570 */ |
466 void _addSourceFiles(ChangeSet changeSet, Folder folder, _ContextInfo info) { | 571 void _addSourceFiles(ChangeSet changeSet, Folder folder, ContextInfo info) { |
467 if (info.excludesResource(folder) || folder.shortName.startsWith('.')) { | 572 if (info.excludesResource(folder) || folder.shortName.startsWith('.')) { |
468 return; | 573 return; |
469 } | 574 } |
470 List<Resource> children = null; | 575 List<Resource> children = null; |
471 try { | 576 try { |
472 children = folder.getChildren(); | 577 children = folder.getChildren(); |
473 } on FileSystemException { | 578 } on FileSystemException { |
474 // The directory either doesn't exist or cannot be read. Either way, there | 579 // The directory either doesn't exist or cannot be read. Either way, there |
475 // are no children that need to be added. | 580 // are no children that need to be added. |
476 return; | 581 return; |
(...skipping 17 matching lines...) Expand all Loading... |
494 continue; | 599 continue; |
495 } | 600 } |
496 _addSourceFiles(changeSet, child, info); | 601 _addSourceFiles(changeSet, child, info); |
497 } | 602 } |
498 } | 603 } |
499 } | 604 } |
500 | 605 |
501 /** | 606 /** |
502 * Cancel all dependency subscriptions for the given context. | 607 * Cancel all dependency subscriptions for the given context. |
503 */ | 608 */ |
504 void _cancelDependencySubscriptions(_ContextInfo info) { | 609 void _cancelDependencySubscriptions(ContextInfo info) { |
505 for (StreamSubscription<WatchEvent> s in info.dependencySubscriptions) { | 610 for (StreamSubscription<WatchEvent> s in info.dependencySubscriptions) { |
506 s.cancel(); | 611 s.cancel(); |
507 } | 612 } |
508 info.dependencySubscriptions.clear(); | 613 info.dependencySubscriptions.clear(); |
509 } | 614 } |
510 | 615 |
511 void _checkForPackagespecUpdate( | 616 void _checkForPackagespecUpdate( |
512 String path, _ContextInfo info, Folder folder) { | 617 String path, ContextInfo info, Folder folder) { |
513 // Check to see if this is the .packages file for this context and if so, | 618 // Check to see if this is the .packages file for this context and if so, |
514 // update the context's source factory. | 619 // update the context's source factory. |
515 if (pathContext.basename(path) == PACKAGE_SPEC_NAME && | 620 if (pathContext.basename(path) == PACKAGE_SPEC_NAME && |
516 info.isPathToPackageDescription(path)) { | 621 info.isPathToPackageDescription(path)) { |
517 File packagespec = resourceProvider.getFile(path); | 622 File packagespec = resourceProvider.getFile(path); |
518 if (packagespec.exists) { | 623 if (packagespec.exists) { |
519 Packages packages = _readPackagespec(packagespec); | 624 Packages packages = _readPackagespec(packagespec); |
520 if (packages != null) { | 625 if (packages != null) { |
521 callbacks.updateContextPackageUriResolver(folder, null, packages); | 626 callbacks.updateContextPackageUriResolver(folder, null, packages); |
522 } | 627 } |
523 } | 628 } |
524 } | 629 } |
525 } | 630 } |
526 | 631 |
527 /** | 632 /** |
528 * Compute the set of files that are being flushed, this is defined as | 633 * Compute the set of files that are being flushed, this is defined as |
529 * the set of sources in the removed context (context.sources), that are | 634 * the set of sources in the removed context (context.sources), that are |
530 * orphaned by this context being removed (no other context includes this | 635 * orphaned by this context being removed (no other context includes this |
531 * file.) | 636 * file.) |
532 */ | 637 */ |
533 List<String> _computeFlushedFiles(Folder folder) { | 638 List<String> _computeFlushedFiles(Folder folder) { |
534 AnalysisContext context = _contexts[folder].context; | 639 AnalysisContext context = _contexts[folder].context; |
535 HashSet<String> flushedFiles = new HashSet<String>(); | 640 HashSet<String> flushedFiles = new HashSet<String>(); |
536 for (Source source in context.sources) { | 641 for (Source source in context.sources) { |
537 flushedFiles.add(source.fullName); | 642 flushedFiles.add(source.fullName); |
538 } | 643 } |
539 for (_ContextInfo contextInfo in _contexts.values) { | 644 for (ContextInfo contextInfo in _contexts.values) { |
540 AnalysisContext contextN = contextInfo.context; | 645 AnalysisContext contextN = contextInfo.context; |
541 if (context != contextN) { | 646 if (context != contextN) { |
542 for (Source source in contextN.sources) { | 647 for (Source source in contextN.sources) { |
543 flushedFiles.remove(source.fullName); | 648 flushedFiles.remove(source.fullName); |
544 } | 649 } |
545 } | 650 } |
546 } | 651 } |
547 return flushedFiles.toList(growable: false); | 652 return flushedFiles.toList(growable: false); |
548 } | 653 } |
549 | 654 |
550 /** | 655 /** |
551 * Compute the appropriate package URI resolver for [folder], and store | 656 * Compute the appropriate package URI resolver for [folder], and store |
552 * dependency information in [info]. Return `null` if no package map can | 657 * dependency information in [info]. Return `null` if no package map can |
553 * be computed. | 658 * be computed. |
554 */ | 659 */ |
555 UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) { | 660 UriResolver _computePackageUriResolver(Folder folder, ContextInfo info) { |
556 _cancelDependencySubscriptions(info); | 661 _cancelDependencySubscriptions(info); |
557 if (info.packageRoot != null) { | 662 if (info.packageRoot != null) { |
558 info.packageMapInfo = null; | 663 info.packageMapInfo = null; |
559 JavaFile packagesDir = new JavaFile(info.packageRoot); | 664 JavaFile packagesDir = new JavaFile(info.packageRoot); |
560 Map<String, List<Folder>> packageMap = new Map<String, List<Folder>>(); | 665 Map<String, List<Folder>> packageMap = new Map<String, List<Folder>>(); |
561 if (packagesDir.isDirectory()) { | 666 if (packagesDir.isDirectory()) { |
562 for (JavaFile file in packagesDir.listFiles()) { | 667 for (JavaFile file in packagesDir.listFiles()) { |
563 // Ensure symlinks in packages directory are canonicalized | 668 // Ensure symlinks in packages directory are canonicalized |
564 // to prevent 'type X cannot be assigned to type X' warnings | 669 // to prevent 'type X cannot be assigned to type X' warnings |
565 String path; | 670 String path; |
(...skipping 53 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
619 return new PackageMapUriResolver( | 724 return new PackageMapUriResolver( |
620 resourceProvider, packageMapInfo.packageMap); | 725 resourceProvider, packageMapInfo.packageMap); |
621 // TODO(paulberry): if any of the dependencies is outside of [folder], | 726 // TODO(paulberry): if any of the dependencies is outside of [folder], |
622 // we'll need to watch their parent folders as well. | 727 // we'll need to watch their parent folders as well. |
623 } | 728 } |
624 } | 729 } |
625 | 730 |
626 /** | 731 /** |
627 * Create a new empty context associated with [folder]. | 732 * Create a new empty context associated with [folder]. |
628 */ | 733 */ |
629 _ContextInfo _createContext( | 734 ContextInfo _createContext( |
630 Folder folder, File packagespecFile, List<_ContextInfo> children) { | 735 Folder folder, File packagespecFile, List<ContextInfo> children) { |
631 _ContextInfo info = new _ContextInfo( | 736 ContextInfo info = new ContextInfo( |
632 folder, packagespecFile, children, normalizedPackageRoots[folder.path]); | 737 folder, packagespecFile, children, normalizedPackageRoots[folder.path]); |
633 _contexts[folder] = info; | 738 _contexts[folder] = info; |
634 var options = analysisOptionsProvider.getOptions(folder); | 739 Map<String, YamlNode> options = analysisOptionsProvider.getOptions(folder); |
635 processOptionsForContext(folder, options); | 740 processOptionsForContext(info, options); |
636 info.changeSubscription = folder.changes.listen((WatchEvent event) { | 741 info.changeSubscription = folder.changes.listen((WatchEvent event) { |
637 _handleWatchEvent(folder, info, event); | 742 _handleWatchEvent(folder, info, event); |
638 }); | 743 }); |
639 try { | 744 try { |
640 Packages packages; | 745 Packages packages; |
641 UriResolver packageUriResolver; | 746 UriResolver packageUriResolver; |
642 | 747 |
643 if (ENABLE_PACKAGESPEC_SUPPORT) { | 748 if (ENABLE_PACKAGESPEC_SUPPORT) { |
644 // Try .packages first. | 749 // Try .packages first. |
645 if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) { | 750 if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) { |
(...skipping 20 matching lines...) Expand all Loading... |
666 * | 771 * |
667 * If there are subfolders with 'pubspec.yaml' files, separate contexts are | 772 * If there are subfolders with 'pubspec.yaml' files, separate contexts are |
668 * created for them and excluded from the context associated with the | 773 * created for them and excluded from the context associated with the |
669 * [folder]. | 774 * [folder]. |
670 * | 775 * |
671 * If [withPackageSpecOnly] is `true`, a context will be created only if there | 776 * If [withPackageSpecOnly] is `true`, a context will be created only if there |
672 * is a 'pubspec.yaml' or '.packages' file in the [folder]. | 777 * is a 'pubspec.yaml' or '.packages' file in the [folder]. |
673 * | 778 * |
674 * Returns created contexts. | 779 * Returns created contexts. |
675 */ | 780 */ |
676 List<_ContextInfo> _createContexts(Folder folder, bool withPackageSpecOnly) { | 781 List<ContextInfo> _createContexts(Folder folder, bool withPackageSpecOnly) { |
677 // Try to find subfolders with pubspecs or .packages files. | 782 // Try to find subfolders with pubspecs or .packages files. |
678 List<_ContextInfo> children = <_ContextInfo>[]; | 783 List<ContextInfo> children = <ContextInfo>[]; |
679 try { | 784 try { |
680 for (Resource child in folder.getChildren()) { | 785 for (Resource child in folder.getChildren()) { |
681 if (child is Folder) { | 786 if (child is Folder) { |
682 children.addAll(_createContexts(child, true)); | 787 children.addAll(_createContexts(child, true)); |
683 } | 788 } |
684 } | 789 } |
685 } on FileSystemException { | 790 } on FileSystemException { |
686 // The directory either doesn't exist or cannot be read. Either way, there | 791 // The directory either doesn't exist or cannot be read. Either way, there |
687 // are no subfolders that need to be added. | 792 // are no subfolders that need to be added. |
688 } | 793 } |
689 | 794 |
690 File packageSpec; | 795 File packageSpec; |
691 | 796 |
692 if (ENABLE_PACKAGESPEC_SUPPORT) { | 797 if (ENABLE_PACKAGESPEC_SUPPORT) { |
693 // Start by looking for .packages. | 798 // Start by looking for .packages. |
694 packageSpec = folder.getChild(PACKAGE_SPEC_NAME); | 799 packageSpec = folder.getChild(PACKAGE_SPEC_NAME); |
695 } | 800 } |
696 | 801 |
697 // Fall back to looking for a pubspec. | 802 // Fall back to looking for a pubspec. |
698 if (packageSpec == null || !packageSpec.exists) { | 803 if (packageSpec == null || !packageSpec.exists) { |
699 packageSpec = folder.getChild(PUBSPEC_NAME); | 804 packageSpec = folder.getChild(PUBSPEC_NAME); |
700 } | 805 } |
701 | 806 |
702 if (packageSpec.exists) { | 807 if (packageSpec.exists) { |
703 return <_ContextInfo>[ | 808 return <ContextInfo>[ |
704 _createContextWithSources(folder, packageSpec, children) | 809 _createContextWithSources(folder, packageSpec, children) |
705 ]; | 810 ]; |
706 } | 811 } |
707 // No packagespec? Done. | 812 // No packagespec? Done. |
708 if (withPackageSpecOnly) { | 813 if (withPackageSpecOnly) { |
709 return children; | 814 return children; |
710 } | 815 } |
711 // OK, create a context without a packagespec. | 816 // OK, create a context without a packagespec. |
712 return <_ContextInfo>[ | 817 return <ContextInfo>[ |
713 _createContextWithSources(folder, packageSpec, children) | 818 _createContextWithSources(folder, packageSpec, children) |
714 ]; | 819 ]; |
715 } | 820 } |
716 | 821 |
717 /** | 822 /** |
718 * Create a new context associated with the given [folder]. The [pubspecFile] | 823 * Create a new context associated with the given [folder]. The [pubspecFile] |
719 * is the `pubspec.yaml` file contained in the folder. Add any sources that | 824 * is the `pubspec.yaml` file contained in the folder. Add any sources that |
720 * are not included in one of the [children] to the context. | 825 * are not included in one of the [children] to the context. |
721 */ | 826 */ |
722 _ContextInfo _createContextWithSources( | 827 ContextInfo _createContextWithSources( |
723 Folder folder, File pubspecFile, List<_ContextInfo> children) { | 828 Folder folder, File pubspecFile, List<ContextInfo> children) { |
724 _ContextInfo info = _createContext(folder, pubspecFile, children); | 829 ContextInfo info = _createContext(folder, pubspecFile, children); |
725 ChangeSet changeSet = new ChangeSet(); | 830 ChangeSet changeSet = new ChangeSet(); |
726 _addSourceFiles(changeSet, folder, info); | 831 _addSourceFiles(changeSet, folder, info); |
727 callbacks.applyChangesToContext(folder, changeSet); | 832 callbacks.applyChangesToContext(folder, changeSet); |
728 return info; | 833 return info; |
729 } | 834 } |
730 | 835 |
731 /** | 836 /** |
732 * Clean up and destroy the context associated with the given folder. | 837 * Clean up and destroy the context associated with the given folder. |
733 */ | 838 */ |
734 void _destroyContext(Folder folder) { | 839 void _destroyContext(Folder folder) { |
735 _ContextInfo info = _contexts[folder]; | 840 ContextInfo info = _contexts[folder]; |
736 info.changeSubscription.cancel(); | 841 info.changeSubscription.cancel(); |
737 _cancelDependencySubscriptions(info); | 842 _cancelDependencySubscriptions(info); |
738 callbacks.removeContext(folder, _computeFlushedFiles(folder)); | 843 callbacks.removeContext(folder, _computeFlushedFiles(folder)); |
739 _contexts.remove(folder); | 844 _contexts.remove(folder); |
740 } | 845 } |
741 | 846 |
742 /** | 847 /** |
743 * Extract a new [packagespecFile]-based context from [oldInfo]. | 848 * Extract a new [packagespecFile]-based context from [oldInfo]. |
744 */ | 849 */ |
745 void _extractContext(_ContextInfo oldInfo, File packagespecFile) { | 850 void _extractContext(ContextInfo oldInfo, File packagespecFile) { |
746 Folder newFolder = packagespecFile.parent; | 851 Folder newFolder = packagespecFile.parent; |
747 _ContextInfo newInfo = _createContext(newFolder, packagespecFile, []); | 852 ContextInfo newInfo = _createContext(newFolder, packagespecFile, []); |
748 newInfo.parent = oldInfo; | 853 newInfo.parent = oldInfo; |
749 // prepare sources to extract | 854 // prepare sources to extract |
750 Map<String, Source> extractedSources = new HashMap<String, Source>(); | 855 Map<String, Source> extractedSources = new HashMap<String, Source>(); |
751 oldInfo.sources.forEach((path, source) { | 856 oldInfo.sources.forEach((path, source) { |
752 if (newFolder.contains(path)) { | 857 if (newFolder.contains(path)) { |
753 extractedSources[path] = source; | 858 extractedSources[path] = source; |
754 } | 859 } |
755 }); | 860 }); |
756 // update new context | 861 // update new context |
757 { | 862 { |
758 ChangeSet changeSet = new ChangeSet(); | 863 ChangeSet changeSet = new ChangeSet(); |
759 extractedSources.forEach((path, source) { | 864 extractedSources.forEach((path, source) { |
760 newInfo.sources[path] = source; | 865 newInfo.sources[path] = source; |
761 changeSet.addedSource(source); | 866 changeSet.addedSource(source); |
762 }); | 867 }); |
763 callbacks.applyChangesToContext(newFolder, changeSet); | 868 callbacks.applyChangesToContext(newFolder, changeSet); |
764 } | 869 } |
765 // update old context | 870 // update old context |
766 { | 871 { |
767 ChangeSet changeSet = new ChangeSet(); | 872 ChangeSet changeSet = new ChangeSet(); |
768 extractedSources.forEach((path, source) { | 873 extractedSources.forEach((path, source) { |
769 oldInfo.sources.remove(path); | 874 oldInfo.sources.remove(path); |
770 changeSet.removedSource(source); | 875 changeSet.removedSource(source); |
771 }); | 876 }); |
772 callbacks.applyChangesToContext(oldInfo.folder, changeSet); | 877 callbacks.applyChangesToContext(oldInfo.folder, changeSet); |
773 } | 878 } |
774 } | 879 } |
775 | 880 |
776 void _handleWatchEvent(Folder folder, _ContextInfo info, WatchEvent event) { | 881 void _handleWatchEvent(Folder folder, ContextInfo info, WatchEvent event) { |
777 // TODO(brianwilkerson) If a file is explicitly included in one context | 882 // TODO(brianwilkerson) If a file is explicitly included in one context |
778 // but implicitly referenced in another context, we will only send a | 883 // but implicitly referenced in another context, we will only send a |
779 // changeSet to the context that explicitly includes the file (because | 884 // changeSet to the context that explicitly includes the file (because |
780 // that's the only context that's watching the file). | 885 // that's the only context that's watching the file). |
781 _instrumentationService.logWatchEvent( | 886 _instrumentationService.logWatchEvent( |
782 folder.path, event.path, event.type.toString()); | 887 folder.path, event.path, event.type.toString()); |
783 String path = event.path; | 888 String path = event.path; |
784 // maybe excluded globally | 889 // maybe excluded globally |
785 if (_isExcluded(path)) { | 890 if (_isExcluded(path)) { |
786 return; | 891 return; |
(...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
947 } | 1052 } |
948 | 1053 |
949 bool _isPackagespec(String path) => | 1054 bool _isPackagespec(String path) => |
950 pathContext.basename(path) == PACKAGE_SPEC_NAME; | 1055 pathContext.basename(path) == PACKAGE_SPEC_NAME; |
951 | 1056 |
952 bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME; | 1057 bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME; |
953 | 1058 |
954 /** | 1059 /** |
955 * Merges [info] context into its parent. | 1060 * Merges [info] context into its parent. |
956 */ | 1061 */ |
957 void _mergeContext(_ContextInfo info) { | 1062 void _mergeContext(ContextInfo info) { |
958 // destroy the context | 1063 // destroy the context |
959 _destroyContext(info.folder); | 1064 _destroyContext(info.folder); |
960 // add files to the parent context | 1065 // add files to the parent context |
961 _ContextInfo parentInfo = info.parent; | 1066 ContextInfo parentInfo = info.parent; |
962 if (parentInfo != null) { | 1067 if (parentInfo != null) { |
963 parentInfo.children.remove(info); | 1068 parentInfo.children.remove(info); |
964 ChangeSet changeSet = new ChangeSet(); | 1069 ChangeSet changeSet = new ChangeSet(); |
965 info.sources.forEach((path, source) { | 1070 info.sources.forEach((path, source) { |
966 parentInfo.sources[path] = source; | 1071 parentInfo.sources[path] = source; |
967 changeSet.addedSource(source); | 1072 changeSet.addedSource(source); |
968 }); | 1073 }); |
969 callbacks.applyChangesToContext(parentInfo.folder, changeSet); | 1074 callbacks.applyChangesToContext(parentInfo.folder, changeSet); |
970 } | 1075 } |
971 } | 1076 } |
972 | 1077 |
973 Packages _readPackagespec(File specFile) { | 1078 Packages _readPackagespec(File specFile) { |
974 try { | 1079 try { |
975 String contents = specFile.readAsStringSync(); | 1080 String contents = specFile.readAsStringSync(); |
976 Map<String, Uri> map = | 1081 Map<String, Uri> map = |
977 pkgfile.parse(UTF8.encode(contents), new Uri.file(specFile.path)); | 1082 pkgfile.parse(UTF8.encode(contents), new Uri.file(specFile.path)); |
978 return new MapPackages(map); | 1083 return new MapPackages(map); |
979 } catch (_) { | 1084 } catch (_) { |
980 //TODO(pquitslund): consider creating an error for the spec file. | 1085 //TODO(pquitslund): consider creating an error for the spec file. |
981 return null; | 1086 return null; |
982 } | 1087 } |
983 } | 1088 } |
984 | 1089 |
985 /** | 1090 /** |
986 * Recompute the package URI resolver for the context described by [info], | 1091 * Recompute the package URI resolver for the context described by [info], |
987 * and update the client appropriately. | 1092 * and update the client appropriately. |
988 */ | 1093 */ |
989 void _recomputePackageUriResolver(_ContextInfo info) { | 1094 void _recomputePackageUriResolver(ContextInfo info) { |
990 // TODO(paulberry): when computePackageMap is changed into an | 1095 // TODO(paulberry): when computePackageMap is changed into an |
991 // asynchronous API call, we'll want to suspend analysis for this context | 1096 // asynchronous API call, we'll want to suspend analysis for this context |
992 // while we're rerunning "pub list", since any analysis we complete while | 1097 // while we're rerunning "pub list", since any analysis we complete while |
993 // "pub list" is in progress is just going to get thrown away anyhow. | 1098 // "pub list" is in progress is just going to get thrown away anyhow. |
994 UriResolver packageUriResolver = | 1099 UriResolver packageUriResolver = |
995 _computePackageUriResolver(info.folder, info); | 1100 _computePackageUriResolver(info.folder, info); |
996 callbacks.updateContextPackageUriResolver( | 1101 callbacks.updateContextPackageUriResolver( |
997 info.folder, packageUriResolver, null); | 1102 info.folder, packageUriResolver, null); |
998 } | 1103 } |
999 | 1104 |
(...skipping 37 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1037 */ | 1142 */ |
1038 final List<AnalysisContext> removed; | 1143 final List<AnalysisContext> removed; |
1039 | 1144 |
1040 /** | 1145 /** |
1041 * Initialize a newly created event to indicate which contexts have changed. | 1146 * Initialize a newly created event to indicate which contexts have changed. |
1042 */ | 1147 */ |
1043 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, | 1148 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, |
1044 this.changed: AnalysisContext.EMPTY_LIST, | 1149 this.changed: AnalysisContext.EMPTY_LIST, |
1045 this.removed: AnalysisContext.EMPTY_LIST}); | 1150 this.removed: AnalysisContext.EMPTY_LIST}); |
1046 } | 1151 } |
1047 | |
1048 /** | |
1049 * Information tracked by the [ContextManager] for each context. | |
1050 */ | |
1051 class _ContextInfo { | |
1052 /** | |
1053 * The [Folder] for which this information object is created. | |
1054 */ | |
1055 final Folder folder; | |
1056 | |
1057 /// The [PathFilter] used to filter sources from being analyzed. | |
1058 final PathFilter pathFilter; | |
1059 | |
1060 /** | |
1061 * The enclosed pubspec-based contexts. | |
1062 */ | |
1063 final List<_ContextInfo> children; | |
1064 | |
1065 /** | |
1066 * The package root for this context, or null if there is no package root. | |
1067 */ | |
1068 String packageRoot; | |
1069 | |
1070 /** | |
1071 * The [_ContextInfo] that encloses this one. | |
1072 */ | |
1073 _ContextInfo parent; | |
1074 | |
1075 /** | |
1076 * The package description file path for this context. | |
1077 */ | |
1078 String packageDescriptionPath; | |
1079 | |
1080 /** | |
1081 * Stream subscription we are using to watch the context's directory for | |
1082 * changes. | |
1083 */ | |
1084 StreamSubscription<WatchEvent> changeSubscription; | |
1085 | |
1086 /** | |
1087 * Stream subscriptions we are using to watch the files | |
1088 * used to determine the package map. | |
1089 */ | |
1090 final List<StreamSubscription<WatchEvent>> dependencySubscriptions = | |
1091 <StreamSubscription<WatchEvent>>[]; | |
1092 | |
1093 /** | |
1094 * The analysis context that was created for the [folder]. | |
1095 */ | |
1096 AnalysisContext context; | |
1097 | |
1098 /** | |
1099 * Map from full path to the [Source] object, for each source that has been | |
1100 * added to the context. | |
1101 */ | |
1102 Map<String, Source> sources = new HashMap<String, Source>(); | |
1103 | |
1104 /** | |
1105 * Info returned by the last call to | |
1106 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the | |
1107 * package map hasn't been computed for this context yet. | |
1108 */ | |
1109 OptimizingPubPackageMapInfo packageMapInfo; | |
1110 | |
1111 _ContextInfo( | |
1112 Folder folder, File packagespecFile, this.children, this.packageRoot) | |
1113 : folder = folder, | |
1114 pathFilter = new PathFilter(folder.path, null) { | |
1115 packageDescriptionPath = packagespecFile.path; | |
1116 for (_ContextInfo child in children) { | |
1117 child.parent = this; | |
1118 } | |
1119 } | |
1120 | |
1121 /** | |
1122 * Returns `true` if this context is root folder based. | |
1123 */ | |
1124 bool get isRoot => parent == null; | |
1125 | |
1126 /** | |
1127 * Returns `true` if [path] is excluded, as it is in one of the children. | |
1128 */ | |
1129 bool excludes(String path) { | |
1130 return children.any((child) { | |
1131 return child.folder.contains(path); | |
1132 }); | |
1133 } | |
1134 | |
1135 /** | |
1136 * Returns `true` if [resource] is excluded, as it is in one of the children. | |
1137 */ | |
1138 bool excludesResource(Resource resource) => excludes(resource.path); | |
1139 | |
1140 /// Returns `true` if [path] should be ignored. | |
1141 bool ignored(String path) => pathFilter.ignored(path); | |
1142 | |
1143 /** | |
1144 * Returns `true` if [path] is the package description file for this context | |
1145 * (pubspec.yaml or .packages). | |
1146 */ | |
1147 bool isPathToPackageDescription(String path) => | |
1148 path == packageDescriptionPath; | |
1149 } | |
OLD | NEW |