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 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
174 */ | 174 */ |
175 bool isPathToPackageDescription(String path) => | 175 bool isPathToPackageDescription(String path) => |
176 path == packageDescriptionPath; | 176 path == packageDescriptionPath; |
177 | 177 |
178 /** | 178 /** |
179 * Update the set of dependencies for this context. | 179 * Update the set of dependencies for this context. |
180 */ | 180 */ |
181 void setDependencies(Iterable<String> newDependencies) { | 181 void setDependencies(Iterable<String> newDependencies) { |
182 _dependencies = newDependencies.toSet(); | 182 _dependencies = newDependencies.toSet(); |
183 } | 183 } |
| 184 |
| 185 /** |
| 186 * Return `true` if the given [path] is managed by this context or by |
| 187 * any of its children. |
| 188 */ |
| 189 bool _managesOrHasChildThatManages(String path) { |
| 190 if (parent == null) { |
| 191 for (ContextInfo child in children) { |
| 192 if (child._managesOrHasChildThatManages(path)) { |
| 193 return true; |
| 194 } |
| 195 } |
| 196 return false; |
| 197 } else { |
| 198 if (!folder.isOrContains(path)) { |
| 199 return false; |
| 200 } |
| 201 for (ContextInfo child in children) { |
| 202 if (child._managesOrHasChildThatManages(path)) { |
| 203 return true; |
| 204 } |
| 205 } |
| 206 return !pathFilter.ignored(path); |
| 207 } |
| 208 } |
184 } | 209 } |
185 | 210 |
186 /** | 211 /** |
187 * Class that maintains a mapping from included/excluded paths to a set of | 212 * Class that maintains a mapping from included/excluded paths to a set of |
188 * folders that should correspond to analysis contexts. | 213 * folders that should correspond to analysis contexts. |
189 */ | 214 */ |
190 abstract class ContextManager { | 215 abstract class ContextManager { |
191 // TODO(brianwilkerson) Support: | 216 // TODO(brianwilkerson) Support: |
192 // setting the default analysis options | 217 // setting the default analysis options |
193 // setting the default content cache | 218 // setting the default content cache |
(...skipping 224 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
418 */ | 443 */ |
419 final InstrumentationService _instrumentationService; | 444 final InstrumentationService _instrumentationService; |
420 | 445 |
421 @override | 446 @override |
422 ContextManagerCallbacks callbacks; | 447 ContextManagerCallbacks callbacks; |
423 | 448 |
424 /** | 449 /** |
425 * Virtual [ContextInfo] which acts as the ancestor of all other | 450 * Virtual [ContextInfo] which acts as the ancestor of all other |
426 * [ContextInfo]s. | 451 * [ContextInfo]s. |
427 */ | 452 */ |
428 final ContextInfo _rootInfo = new ContextInfo._root(); | 453 final ContextInfo rootInfo = new ContextInfo._root(); |
429 | 454 |
430 /** | 455 /** |
431 * Stream subscription we are using to watch each analysis root directory for | 456 * Stream subscription we are using to watch each analysis root directory for |
432 * changes. | 457 * changes. |
433 */ | 458 */ |
434 final Map<Folder, StreamSubscription<WatchEvent>> changeSubscriptions = | 459 final Map<Folder, StreamSubscription<WatchEvent>> changeSubscriptions = |
435 <Folder, StreamSubscription<WatchEvent>>{}; | 460 <Folder, StreamSubscription<WatchEvent>>{}; |
436 | 461 |
437 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, | 462 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, |
438 this._packageMapProvider, this._instrumentationService) { | 463 this._packageMapProvider, this._instrumentationService) { |
(...skipping 35 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
474 ContextInfo getContextInfoFor(Folder folder) { | 499 ContextInfo getContextInfoFor(Folder folder) { |
475 ContextInfo info = _getInnermostContextInfoFor(folder.path); | 500 ContextInfo info = _getInnermostContextInfoFor(folder.path); |
476 if (info != null && folder == info.folder) { | 501 if (info != null && folder == info.folder) { |
477 return info; | 502 return info; |
478 } | 503 } |
479 return null; | 504 return null; |
480 } | 505 } |
481 | 506 |
482 @override | 507 @override |
483 bool isIgnored(String path) { | 508 bool isIgnored(String path) { |
484 ContextInfo info = _rootInfo; | 509 ContextInfo info = rootInfo; |
485 do { | 510 do { |
486 info = info.findChildInfoFor(path); | 511 info = info.findChildInfoFor(path); |
487 if (info == null) { | 512 if (info == null) { |
488 return false; | 513 return false; |
489 } | 514 } |
490 if (info.ignored(path)) { | 515 if (info.ignored(path)) { |
491 return true; | 516 return true; |
492 } | 517 } |
493 } while (true); | 518 } while (true); |
494 } | 519 } |
495 | 520 |
496 @override | 521 @override |
497 bool isInAnalysisRoot(String path) { | 522 bool isInAnalysisRoot(String path) { |
498 // check if excluded | 523 // check if excluded |
499 if (_isExcluded(path)) { | 524 if (_isExcluded(path)) { |
500 return false; | 525 return false; |
501 } | 526 } |
502 // check if in one of the roots | 527 // check if in one of the roots |
503 for (ContextInfo info in _rootInfo.children) { | 528 for (ContextInfo info in rootInfo.children) { |
504 if (info.folder.contains(path)) { | 529 if (info.folder.contains(path)) { |
505 return true; | 530 return true; |
506 } | 531 } |
507 } | 532 } |
508 // no | 533 // no |
509 return false; | 534 return false; |
510 } | 535 } |
511 | 536 |
512 /** | 537 /** |
513 * Process [options] for the given context [info]. | 538 * Process [options] for the given context [info]. |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
568 // Set ignore patterns. | 593 // Set ignore patterns. |
569 YamlList exclude = analyzer[AnalyzerOptions.exclude]; | 594 YamlList exclude = analyzer[AnalyzerOptions.exclude]; |
570 if (exclude != null) { | 595 if (exclude != null) { |
571 setIgnorePatternsForContext(info, exclude); | 596 setIgnorePatternsForContext(info, exclude); |
572 } | 597 } |
573 } | 598 } |
574 | 599 |
575 @override | 600 @override |
576 void refresh(List<Resource> roots) { | 601 void refresh(List<Resource> roots) { |
577 // Destroy old contexts | 602 // Destroy old contexts |
578 List<ContextInfo> contextInfos = _rootInfo.descendants.toList(); | 603 List<ContextInfo> contextInfos = rootInfo.descendants.toList(); |
579 if (roots == null) { | 604 if (roots == null) { |
580 contextInfos.forEach(_destroyContext); | 605 contextInfos.forEach(_destroyContext); |
581 } else { | 606 } else { |
582 roots.forEach((Resource resource) { | 607 roots.forEach((Resource resource) { |
583 contextInfos.forEach((ContextInfo contextInfo) { | 608 contextInfos.forEach((ContextInfo contextInfo) { |
584 if (resource is Folder && | 609 if (resource is Folder && |
585 resource.isOrContains(contextInfo.folder.path)) { | 610 resource.isOrContains(contextInfo.folder.path)) { |
586 _destroyContext(contextInfo); | 611 _destroyContext(contextInfo); |
587 } | 612 } |
588 }); | 613 }); |
(...skipping 20 matching lines...) Expand all Loading... |
609 // Normalize all package root sources by mapping them to folders on the | 634 // Normalize all package root sources by mapping them to folders on the |
610 // filesystem. Ignore any package root sources that aren't folders. | 635 // filesystem. Ignore any package root sources that aren't folders. |
611 normalizedPackageRoots = <String, String>{}; | 636 normalizedPackageRoots = <String, String>{}; |
612 packageRoots.forEach((String sourcePath, String targetPath) { | 637 packageRoots.forEach((String sourcePath, String targetPath) { |
613 Resource resource = resourceProvider.getResource(sourcePath); | 638 Resource resource = resourceProvider.getResource(sourcePath); |
614 if (resource is Folder) { | 639 if (resource is Folder) { |
615 normalizedPackageRoots[resource.path] = targetPath; | 640 normalizedPackageRoots[resource.path] = targetPath; |
616 } | 641 } |
617 }); | 642 }); |
618 | 643 |
619 List<ContextInfo> contextInfos = _rootInfo.descendants.toList(); | 644 List<ContextInfo> contextInfos = rootInfo.descendants.toList(); |
620 // included | 645 // included |
621 Set<Folder> includedFolders = new HashSet<Folder>(); | 646 List<Folder> includedFolders = <Folder>[]; |
622 for (int i = 0; i < includedPaths.length; i++) { | 647 { |
623 String path = includedPaths[i]; | 648 // Sort paths to ensure that outer roots are handled before inner roots, |
624 Resource resource = resourceProvider.getResource(path); | 649 // so we can correctly ignore inner roots, which are already managed |
625 if (resource is Folder) { | 650 // by outer roots. |
626 includedFolders.add(resource); | 651 LinkedHashSet<String> uniqueIncludedPaths = |
627 } else if (!resource.exists) { | 652 new LinkedHashSet<String>.from(includedPaths); |
628 // Non-existent resources are ignored. TODO(paulberry): we should set | 653 List<String> sortedIncludedPaths = uniqueIncludedPaths.toList(); |
629 // up a watcher to ensure that if the resource appears later, we will | 654 sortedIncludedPaths.sort((a, b) => a.length - b.length); |
630 // begin analyzing it. | 655 // Convert paths to folders. |
631 } else { | 656 for (String path in sortedIncludedPaths) { |
632 // TODO(scheglov) implemented separate files analysis | 657 Resource resource = resourceProvider.getResource(path); |
633 throw new UnimplementedError('$path is not a folder. ' | 658 if (resource is Folder) { |
634 'Only support for folder analysis is implemented currently.'); | 659 includedFolders.add(resource); |
| 660 } else if (!resource.exists) { |
| 661 // Non-existent resources are ignored. TODO(paulberry): we should set |
| 662 // up a watcher to ensure that if the resource appears later, we will |
| 663 // begin analyzing it. |
| 664 } else { |
| 665 // TODO(scheglov) implemented separate files analysis |
| 666 throw new UnimplementedError('$path is not a folder. ' |
| 667 'Only support for folder analysis is implemented currently.'); |
| 668 } |
635 } | 669 } |
636 } | 670 } |
637 this.includedPaths = includedPaths; | 671 this.includedPaths = includedPaths; |
638 // excluded | 672 // excluded |
639 List<String> oldExcludedPaths = this.excludedPaths; | 673 List<String> oldExcludedPaths = this.excludedPaths; |
640 this.excludedPaths = excludedPaths; | 674 this.excludedPaths = excludedPaths; |
641 // destroy old contexts | 675 // destroy old contexts |
642 for (ContextInfo contextInfo in contextInfos) { | 676 for (ContextInfo contextInfo in contextInfos) { |
643 bool isIncluded = includedFolders.any((folder) { | 677 bool isIncluded = includedFolders.any((folder) { |
644 return folder.isOrContains(contextInfo.folder.path); | 678 return folder.isOrContains(contextInfo.folder.path); |
645 }); | 679 }); |
646 if (!isIncluded) { | 680 if (!isIncluded) { |
647 _destroyContext(contextInfo); | 681 _destroyContext(contextInfo); |
648 } | 682 } |
649 } | 683 } |
650 // Update package roots for existing contexts | 684 // Update package roots for existing contexts |
651 for (ContextInfo info in _rootInfo.descendants) { | 685 for (ContextInfo info in rootInfo.descendants) { |
652 String newPackageRoot = normalizedPackageRoots[info.folder.path]; | 686 String newPackageRoot = normalizedPackageRoots[info.folder.path]; |
653 if (info.packageRoot != newPackageRoot) { | 687 if (info.packageRoot != newPackageRoot) { |
654 info.packageRoot = newPackageRoot; | 688 info.packageRoot = newPackageRoot; |
655 _recomputeFolderDisposition(info); | 689 _recomputeFolderDisposition(info); |
656 } | 690 } |
657 } | 691 } |
658 // create new contexts | 692 // create new contexts |
659 for (Folder includedFolder in includedFolders) { | 693 for (Folder includedFolder in includedFolders) { |
660 bool wasIncluded = contextInfos.any((info) { | 694 String includedPath = includedFolder.path; |
661 return info.folder.isOrContains(includedFolder.path); | 695 bool isManaged = rootInfo._managesOrHasChildThatManages(includedPath); |
662 }); | 696 if (!isManaged) { |
663 if (!wasIncluded) { | 697 ContextInfo parent = _getParentForNewContext(includedPath); |
664 changeSubscriptions[includedFolder] = | 698 changeSubscriptions[includedFolder] = |
665 includedFolder.changes.listen(_handleWatchEvent); | 699 includedFolder.changes.listen(_handleWatchEvent); |
666 _createContexts(_rootInfo, includedFolder, false); | 700 _createContexts(parent, includedFolder, false); |
667 } | 701 } |
668 } | 702 } |
669 // remove newly excluded sources | 703 // remove newly excluded sources |
670 for (ContextInfo info in _rootInfo.descendants) { | 704 for (ContextInfo info in rootInfo.descendants) { |
671 // prepare excluded sources | 705 // prepare excluded sources |
672 Map<String, Source> excludedSources = new HashMap<String, Source>(); | 706 Map<String, Source> excludedSources = new HashMap<String, Source>(); |
673 info.sources.forEach((String path, Source source) { | 707 info.sources.forEach((String path, Source source) { |
674 if (_isExcludedBy(excludedPaths, path) && | 708 if (_isExcludedBy(excludedPaths, path) && |
675 !_isExcludedBy(oldExcludedPaths, path)) { | 709 !_isExcludedBy(oldExcludedPaths, path)) { |
676 excludedSources[path] = source; | 710 excludedSources[path] = source; |
677 } | 711 } |
678 }); | 712 }); |
679 // apply exclusion | 713 // apply exclusion |
680 ChangeSet changeSet = new ChangeSet(); | 714 ChangeSet changeSet = new ChangeSet(); |
681 excludedSources.forEach((String path, Source source) { | 715 excludedSources.forEach((String path, Source source) { |
682 info.sources.remove(path); | 716 info.sources.remove(path); |
683 changeSet.removedSource(source); | 717 changeSet.removedSource(source); |
684 }); | 718 }); |
685 callbacks.applyChangesToContext(info.folder, changeSet); | 719 callbacks.applyChangesToContext(info.folder, changeSet); |
686 } | 720 } |
687 // add previously excluded sources | 721 // add previously excluded sources |
688 for (ContextInfo info in _rootInfo.descendants) { | 722 for (ContextInfo info in rootInfo.descendants) { |
689 ChangeSet changeSet = new ChangeSet(); | 723 ChangeSet changeSet = new ChangeSet(); |
690 _addPreviouslyExcludedSources( | 724 _addPreviouslyExcludedSources( |
691 info, changeSet, info.folder, oldExcludedPaths); | 725 info, changeSet, info.folder, oldExcludedPaths); |
692 callbacks.applyChangesToContext(info.folder, changeSet); | 726 callbacks.applyChangesToContext(info.folder, changeSet); |
693 } | 727 } |
694 } | 728 } |
695 | 729 |
696 /** | 730 /** |
697 * Recursively adds all Dart and HTML files to the [changeSet]. | 731 * Recursively adds all Dart and HTML files to the [changeSet]. |
698 */ | 732 */ |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
815 * the set of sources in the removed context (context.sources), that are | 849 * the set of sources in the removed context (context.sources), that are |
816 * orphaned by this context being removed (no other context includes this | 850 * orphaned by this context being removed (no other context includes this |
817 * file.) | 851 * file.) |
818 */ | 852 */ |
819 List<String> _computeFlushedFiles(ContextInfo info) { | 853 List<String> _computeFlushedFiles(ContextInfo info) { |
820 AnalysisContext context = info.context; | 854 AnalysisContext context = info.context; |
821 HashSet<String> flushedFiles = new HashSet<String>(); | 855 HashSet<String> flushedFiles = new HashSet<String>(); |
822 for (Source source in context.sources) { | 856 for (Source source in context.sources) { |
823 flushedFiles.add(source.fullName); | 857 flushedFiles.add(source.fullName); |
824 } | 858 } |
825 for (ContextInfo contextInfo in _rootInfo.descendants) { | 859 for (ContextInfo contextInfo in rootInfo.descendants) { |
826 AnalysisContext contextN = contextInfo.context; | 860 AnalysisContext contextN = contextInfo.context; |
827 if (context != contextN) { | 861 if (context != contextN) { |
828 for (Source source in contextN.sources) { | 862 for (Source source in contextN.sources) { |
829 flushedFiles.remove(source.fullName); | 863 flushedFiles.remove(source.fullName); |
830 } | 864 } |
831 } | 865 } |
832 } | 866 } |
833 return flushedFiles.toList(growable: false); | 867 return flushedFiles.toList(growable: false); |
834 } | 868 } |
835 | 869 |
(...skipping 239 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1075 /** | 1109 /** |
1076 * Return the [ContextInfo] for the "innermost" context whose associated | 1110 * Return the [ContextInfo] for the "innermost" context whose associated |
1077 * folder is or contains the given path. ("innermost" refers to the nesting | 1111 * folder is or contains the given path. ("innermost" refers to the nesting |
1078 * of contexts, so if there is a context for path /foo and a context for | 1112 * of contexts, so if there is a context for path /foo and a context for |
1079 * path /foo/bar, then the innermost context containing /foo/bar/baz.dart is | 1113 * path /foo/bar, then the innermost context containing /foo/bar/baz.dart is |
1080 * the context for /foo/bar.) | 1114 * the context for /foo/bar.) |
1081 * | 1115 * |
1082 * If no context contains the given path, `null` is returned. | 1116 * If no context contains the given path, `null` is returned. |
1083 */ | 1117 */ |
1084 ContextInfo _getInnermostContextInfoFor(String path) { | 1118 ContextInfo _getInnermostContextInfoFor(String path) { |
1085 ContextInfo info = _rootInfo.findChildInfoFor(path); | 1119 ContextInfo info = rootInfo.findChildInfoFor(path); |
1086 if (info == null) { | 1120 if (info == null) { |
1087 return null; | 1121 return null; |
1088 } | 1122 } |
1089 while (true) { | 1123 while (true) { |
1090 ContextInfo childInfo = info.findChildInfoFor(path); | 1124 ContextInfo childInfo = info.findChildInfoFor(path); |
1091 if (childInfo == null) { | 1125 if (childInfo == null) { |
1092 return info; | 1126 return info; |
1093 } | 1127 } |
1094 info = childInfo; | 1128 info = childInfo; |
1095 } | 1129 } |
1096 } | 1130 } |
1097 | 1131 |
| 1132 /** |
| 1133 * Return the parent for a new [ContextInfo] with the given [path] folder. |
| 1134 */ |
| 1135 ContextInfo _getParentForNewContext(String path) { |
| 1136 ContextInfo parent = _getInnermostContextInfoFor(path); |
| 1137 if (parent != null) { |
| 1138 return parent; |
| 1139 } |
| 1140 return rootInfo; |
| 1141 } |
| 1142 |
1098 void _handleWatchEvent(WatchEvent event) { | 1143 void _handleWatchEvent(WatchEvent event) { |
1099 // Figure out which context this event applies to. | 1144 // Figure out which context this event applies to. |
1100 // TODO(brianwilkerson) If a file is explicitly included in one context | 1145 // TODO(brianwilkerson) If a file is explicitly included in one context |
1101 // but implicitly referenced in another context, we will only send a | 1146 // but implicitly referenced in another context, we will only send a |
1102 // changeSet to the context that explicitly includes the file (because | 1147 // changeSet to the context that explicitly includes the file (because |
1103 // that's the only context that's watching the file). | 1148 // that's the only context that's watching the file). |
1104 ContextInfo info = _getInnermostContextInfoFor(event.path); | 1149 ContextInfo info = _getInnermostContextInfoFor(event.path); |
1105 if (info == null) { | 1150 if (info == null) { |
1106 // This event doesn't apply to any context. This could happen due to a | 1151 // This event doesn't apply to any context. This could happen due to a |
1107 // race condition (e.g. a context was removed while one of its events was | 1152 // race condition (e.g. a context was removed while one of its events was |
(...skipping 434 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1542 ResourceProvider resourceProvider) { | 1587 ResourceProvider resourceProvider) { |
1543 if (packages != null) { | 1588 if (packages != null) { |
1544 // Construct package map for the SdkExtUriResolver. | 1589 // Construct package map for the SdkExtUriResolver. |
1545 Map<String, List<Folder>> packageMap = buildPackageMap(resourceProvider); | 1590 Map<String, List<Folder>> packageMap = buildPackageMap(resourceProvider); |
1546 return <UriResolver>[new SdkExtUriResolver(packageMap)]; | 1591 return <UriResolver>[new SdkExtUriResolver(packageMap)]; |
1547 } else { | 1592 } else { |
1548 return const <UriResolver>[]; | 1593 return const <UriResolver>[]; |
1549 } | 1594 } |
1550 } | 1595 } |
1551 } | 1596 } |
OLD | NEW |