| 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 |