Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2014, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 library 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 25 matching lines...) Expand all Loading... | |
| 36 * The [Folder] for which this information object is created. | 36 * The [Folder] for which this information object is created. |
| 37 */ | 37 */ |
| 38 final Folder folder; | 38 final Folder folder; |
| 39 | 39 |
| 40 /// The [PathFilter] used to filter sources from being analyzed. | 40 /// The [PathFilter] used to filter sources from being analyzed. |
| 41 final PathFilter pathFilter; | 41 final PathFilter pathFilter; |
| 42 | 42 |
| 43 /** | 43 /** |
| 44 * The enclosed pubspec-based contexts. | 44 * The enclosed pubspec-based contexts. |
| 45 */ | 45 */ |
| 46 final List<ContextInfo> children; | 46 final List<ContextInfo> children = <ContextInfo>[]; |
| 47 | 47 |
| 48 /** | 48 /** |
| 49 * The package root for this context, or null if there is no package root. | 49 * The package root for this context, or null if there is no package root. |
| 50 */ | 50 */ |
| 51 String packageRoot; | 51 String packageRoot; |
| 52 | 52 |
| 53 /** | 53 /** |
| 54 * The [ContextInfo] that encloses this one. | 54 * The [ContextInfo] that encloses this one, or `null` if this is the virtual |
| 55 * [ContextInfo] object that acts as the ancestor of all other [ContextInfo] | |
| 56 * objects. | |
| 55 */ | 57 */ |
| 56 ContextInfo parent; | 58 ContextInfo parent; |
| 57 | 59 |
| 58 /** | 60 /** |
| 59 * The package description file path for this context. | 61 * The package description file path for this context. |
| 60 */ | 62 */ |
| 61 String packageDescriptionPath; | 63 String packageDescriptionPath; |
| 62 | 64 |
| 63 /** | 65 /** |
| 64 * Stream subscription we are using to watch the context's directory for | 66 * Stream subscription we are using to watch the context's directory for |
| (...skipping 20 matching lines...) Expand all Loading... | |
| 85 Map<String, Source> sources = new HashMap<String, Source>(); | 87 Map<String, Source> sources = new HashMap<String, Source>(); |
| 86 | 88 |
| 87 /** | 89 /** |
| 88 * Info returned by the last call to | 90 * Info returned by the last call to |
| 89 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the | 91 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the |
| 90 * package map hasn't been computed for this context yet. | 92 * package map hasn't been computed for this context yet. |
| 91 */ | 93 */ |
| 92 OptimizingPubPackageMapInfo packageMapInfo; | 94 OptimizingPubPackageMapInfo packageMapInfo; |
| 93 | 95 |
| 94 ContextInfo( | 96 ContextInfo( |
| 95 Folder folder, File packagespecFile, this.children, this.packageRoot) | 97 this.parent, Folder folder, File packagespecFile, this.packageRoot) |
| 96 : folder = folder, | 98 : folder = folder, |
| 97 pathFilter = new PathFilter(folder.path, null) { | 99 pathFilter = new PathFilter(folder.path, null) { |
| 98 packageDescriptionPath = packagespecFile.path; | 100 packageDescriptionPath = packagespecFile.path; |
| 99 for (ContextInfo child in children) { | 101 parent.children.add(this); |
| 100 child.parent = this; | |
| 101 } | |
| 102 } | 102 } |
| 103 | 103 |
| 104 /** | 104 /** |
| 105 * Returns `true` if this context is root folder based. | 105 * Create the virtual [ContextInfo] which acts as an ancestor to all other |
| 106 * [ContextInfo]s. | |
| 106 */ | 107 */ |
| 107 bool get isRoot => parent == null; | 108 ContextInfo._ancestor() |
|
Brian Wilkerson
2015/07/22 13:52:24
Perhaps "ContextInfo._root"?
Paul Berry
2015/07/22 17:31:49
Acknowledged. After discussion this morning, I wi
| |
| 109 : folder = null, | |
| 110 pathFilter = null; | |
| 111 | |
| 112 /** | |
| 113 * Returns `true` if the folder associated with this context is not contained | |
| 114 * within any other folders that have an associated context. | |
| 115 */ | |
| 116 bool get isRoot => parent.parent == null; | |
| 108 | 117 |
| 109 /** | 118 /** |
| 110 * Returns `true` if [path] is excluded, as it is in one of the children. | 119 * Returns `true` if [path] is excluded, as it is in one of the children. |
| 111 */ | 120 */ |
| 112 bool excludes(String path) { | 121 bool excludes(String path) { |
| 113 return children.any((child) { | 122 return children.any((child) { |
| 114 return child.folder.contains(path); | 123 return child.folder.contains(path); |
| 115 }); | 124 }); |
| 116 } | 125 } |
| 117 | 126 |
| (...skipping 225 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 343 new AnalysisOptionsProvider(); | 352 new AnalysisOptionsProvider(); |
| 344 | 353 |
| 345 /** | 354 /** |
| 346 * The instrumentation service used to report instrumentation data. | 355 * The instrumentation service used to report instrumentation data. |
| 347 */ | 356 */ |
| 348 final InstrumentationService _instrumentationService; | 357 final InstrumentationService _instrumentationService; |
| 349 | 358 |
| 350 @override | 359 @override |
| 351 ContextManagerCallbacks callbacks; | 360 ContextManagerCallbacks callbacks; |
| 352 | 361 |
| 362 /** | |
| 363 * Virtual [ContextInfo] which acts as the ancestor of all other | |
| 364 * [ContextInfo]s. | |
| 365 */ | |
| 366 ContextInfo _ancestorInfo = new ContextInfo._ancestor(); | |
|
scheglov
2015/07/22 00:05:51
final?
Brian Wilkerson
2015/07/22 13:52:24
Perhaps "_rootInfo"?
Paul Berry
2015/07/22 17:31:49
Done.
| |
| 367 | |
| 353 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, | 368 ContextManagerImpl(this.resourceProvider, this.packageResolverProvider, |
| 354 this._packageMapProvider, this._instrumentationService) { | 369 this._packageMapProvider, this._instrumentationService) { |
| 355 pathContext = resourceProvider.pathContext; | 370 pathContext = resourceProvider.pathContext; |
| 356 } | 371 } |
| 357 | 372 |
| 358 @override | 373 @override |
| 359 List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) { | 374 List<AnalysisContext> contextsInAnalysisRoot(Folder analysisRoot) { |
| 360 List<AnalysisContext> contexts = <AnalysisContext>[]; | 375 List<AnalysisContext> contexts = <AnalysisContext>[]; |
| 361 _contexts.forEach((Folder contextFolder, ContextInfo info) { | 376 _contexts.forEach((Folder contextFolder, ContextInfo info) { |
| 362 if (analysisRoot.isOrContains(contextFolder.path)) { | 377 if (analysisRoot.isOrContains(contextFolder.path)) { |
| (...skipping 119 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 482 info.packageRoot = newPackageRoot; | 497 info.packageRoot = newPackageRoot; |
| 483 _recomputePackageUriResolver(info); | 498 _recomputePackageUriResolver(info); |
| 484 } | 499 } |
| 485 }); | 500 }); |
| 486 // create new contexts | 501 // create new contexts |
| 487 for (Folder includedFolder in includedFolders) { | 502 for (Folder includedFolder in includedFolders) { |
| 488 bool wasIncluded = contextFolders.any((folder) { | 503 bool wasIncluded = contextFolders.any((folder) { |
| 489 return folder.isOrContains(includedFolder.path); | 504 return folder.isOrContains(includedFolder.path); |
| 490 }); | 505 }); |
| 491 if (!wasIncluded) { | 506 if (!wasIncluded) { |
| 492 _createContexts(includedFolder, false); | 507 _createContexts(_ancestorInfo, includedFolder, false); |
| 493 } | 508 } |
| 494 } | 509 } |
| 495 // remove newly excluded sources | 510 // remove newly excluded sources |
| 496 _contexts.forEach((folder, info) { | 511 _contexts.forEach((folder, info) { |
| 497 // prepare excluded sources | 512 // prepare excluded sources |
| 498 Map<String, Source> excludedSources = new HashMap<String, Source>(); | 513 Map<String, Source> excludedSources = new HashMap<String, Source>(); |
| 499 info.sources.forEach((String path, Source source) { | 514 info.sources.forEach((String path, Source source) { |
| 500 if (_isExcludedBy(excludedPaths, path) && | 515 if (_isExcludedBy(excludedPaths, path) && |
| 501 !_isExcludedBy(oldExcludedPaths, path)) { | 516 !_isExcludedBy(oldExcludedPaths, path)) { |
| 502 excludedSources[path] = source; | 517 excludedSources[path] = source; |
| (...skipping 219 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 722 return null; | 737 return null; |
| 723 } | 738 } |
| 724 return new PackageMapUriResolver( | 739 return new PackageMapUriResolver( |
| 725 resourceProvider, packageMapInfo.packageMap); | 740 resourceProvider, packageMapInfo.packageMap); |
| 726 // TODO(paulberry): if any of the dependencies is outside of [folder], | 741 // TODO(paulberry): if any of the dependencies is outside of [folder], |
| 727 // we'll need to watch their parent folders as well. | 742 // we'll need to watch their parent folders as well. |
| 728 } | 743 } |
| 729 } | 744 } |
| 730 | 745 |
| 731 /** | 746 /** |
| 732 * Create a new empty context associated with [folder]. | 747 * Create a new empty context associated with [folder], having parent |
| 748 * [parent] and using [packagespecFile] to resolve package URI's. | |
| 733 */ | 749 */ |
| 734 ContextInfo _createContext( | 750 ContextInfo _createContext( |
| 735 Folder folder, File packagespecFile, List<ContextInfo> children) { | 751 ContextInfo parent, Folder folder, File packagespecFile) { |
| 736 ContextInfo info = new ContextInfo( | 752 ContextInfo info = new ContextInfo( |
| 737 folder, packagespecFile, children, normalizedPackageRoots[folder.path]); | 753 parent, folder, packagespecFile, normalizedPackageRoots[folder.path]); |
| 738 _contexts[folder] = info; | 754 _contexts[folder] = info; |
| 739 Map<String, YamlNode> options = analysisOptionsProvider.getOptions(folder); | 755 Map<String, YamlNode> options = analysisOptionsProvider.getOptions(folder); |
| 740 processOptionsForContext(info, options); | 756 processOptionsForContext(info, options); |
| 741 info.changeSubscription = folder.changes.listen((WatchEvent event) { | 757 info.changeSubscription = folder.changes.listen((WatchEvent event) { |
| 742 _handleWatchEvent(folder, info, event); | 758 _handleWatchEvent(folder, info, event); |
| 743 }); | 759 }); |
| 744 try { | 760 try { |
| 745 Packages packages; | 761 Packages packages; |
| 746 UriResolver packageUriResolver; | 762 UriResolver packageUriResolver; |
| 747 | 763 |
| (...skipping 21 matching lines...) Expand all Loading... | |
| 769 /** | 785 /** |
| 770 * Potentially create a new context associated with the given [folder]. | 786 * Potentially create a new context associated with the given [folder]. |
| 771 * | 787 * |
| 772 * If there are subfolders with 'pubspec.yaml' files, separate contexts are | 788 * If there are subfolders with 'pubspec.yaml' files, separate contexts are |
| 773 * created for them and excluded from the context associated with the | 789 * created for them and excluded from the context associated with the |
| 774 * [folder]. | 790 * [folder]. |
| 775 * | 791 * |
| 776 * If [withPackageSpecOnly] is `true`, a context will be created only if there | 792 * If [withPackageSpecOnly] is `true`, a context will be created only if there |
| 777 * is a 'pubspec.yaml' or '.packages' file in the [folder]. | 793 * is a 'pubspec.yaml' or '.packages' file in the [folder]. |
| 778 * | 794 * |
| 779 * Returns created contexts. | 795 * [parent] should be the parent of any contexts that are created. |
| 780 */ | 796 */ |
| 781 List<ContextInfo> _createContexts(Folder folder, bool withPackageSpecOnly) { | 797 void _createContexts( |
| 782 // Try to find subfolders with pubspecs or .packages files. | 798 ContextInfo parent, Folder folder, bool withPackageSpecOnly) { |
| 783 List<ContextInfo> children = <ContextInfo>[]; | 799 // Decide whether a context needs to be created for [folder] here, and if |
| 784 try { | 800 // so, create it. |
| 785 for (Resource child in folder.getChildren()) { | |
| 786 if (child is Folder) { | |
| 787 children.addAll(_createContexts(child, true)); | |
| 788 } | |
| 789 } | |
| 790 } on FileSystemException { | |
| 791 // The directory either doesn't exist or cannot be read. Either way, there | |
| 792 // are no subfolders that need to be added. | |
| 793 } | |
| 794 | |
| 795 File packageSpec; | 801 File packageSpec; |
| 796 | 802 |
| 797 if (ENABLE_PACKAGESPEC_SUPPORT) { | 803 if (ENABLE_PACKAGESPEC_SUPPORT) { |
| 798 // Start by looking for .packages. | 804 // Start by looking for .packages. |
| 799 packageSpec = folder.getChild(PACKAGE_SPEC_NAME); | 805 packageSpec = folder.getChild(PACKAGE_SPEC_NAME); |
| 800 } | 806 } |
| 801 | 807 |
| 802 // Fall back to looking for a pubspec. | 808 // Fall back to looking for a pubspec. |
| 803 if (packageSpec == null || !packageSpec.exists) { | 809 if (packageSpec == null || !packageSpec.exists) { |
| 804 packageSpec = folder.getChild(PUBSPEC_NAME); | 810 packageSpec = folder.getChild(PUBSPEC_NAME); |
| 805 } | 811 } |
| 806 | 812 |
| 807 if (packageSpec.exists) { | 813 bool parentCreated = false; |
| 808 return <ContextInfo>[ | 814 if (packageSpec.exists || !withPackageSpecOnly) { |
| 809 _createContextWithSources(folder, packageSpec, children) | 815 parentCreated = true; |
| 810 ]; | 816 parent = _createContext(parent, folder, packageSpec); |
| 811 } | 817 } |
| 812 // No packagespec? Done. | 818 |
| 813 if (withPackageSpecOnly) { | 819 // Try to find subfolders with pubspecs or .packages files. |
| 814 return children; | 820 try { |
| 821 for (Resource child in folder.getChildren()) { | |
| 822 if (child is Folder) { | |
| 823 _createContexts(parent, child, true); | |
| 824 } | |
| 825 } | |
| 826 } on FileSystemException { | |
| 827 // The directory either doesn't exist or cannot be read. Either way, there | |
| 828 // are no subfolders that need to be added. | |
| 815 } | 829 } |
| 816 // OK, create a context without a packagespec. | 830 |
| 817 return <ContextInfo>[ | 831 if (parentCreated) { |
| 818 _createContextWithSources(folder, packageSpec, children) | 832 // Now that the child contexts have been created, add the sources that |
| 819 ]; | 833 // don't belong to the children. |
| 834 ChangeSet changeSet = new ChangeSet(); | |
| 835 _addSourceFiles(changeSet, folder, parent); | |
| 836 callbacks.applyChangesToContext(folder, changeSet); | |
| 837 } | |
| 820 } | 838 } |
| 821 | 839 |
| 822 /** | 840 /** |
| 823 * Create a new context associated with the given [folder]. The [pubspecFile] | |
| 824 * is the `pubspec.yaml` file contained in the folder. Add any sources that | |
| 825 * are not included in one of the [children] to the context. | |
| 826 */ | |
| 827 ContextInfo _createContextWithSources( | |
| 828 Folder folder, File pubspecFile, List<ContextInfo> children) { | |
| 829 ContextInfo info = _createContext(folder, pubspecFile, children); | |
| 830 ChangeSet changeSet = new ChangeSet(); | |
| 831 _addSourceFiles(changeSet, folder, info); | |
| 832 callbacks.applyChangesToContext(folder, changeSet); | |
| 833 return info; | |
| 834 } | |
| 835 | |
| 836 /** | |
| 837 * Clean up and destroy the context associated with the given folder. | 841 * Clean up and destroy the context associated with the given folder. |
| 838 */ | 842 */ |
| 839 void _destroyContext(Folder folder) { | 843 void _destroyContext(Folder folder) { |
| 840 ContextInfo info = _contexts[folder]; | 844 ContextInfo info = _contexts[folder]; |
| 841 info.changeSubscription.cancel(); | 845 info.changeSubscription.cancel(); |
| 842 _cancelDependencySubscriptions(info); | 846 _cancelDependencySubscriptions(info); |
| 843 callbacks.removeContext(folder, _computeFlushedFiles(folder)); | 847 callbacks.removeContext(folder, _computeFlushedFiles(folder)); |
| 848 bool wasRemoved = info.parent.children.remove(info); | |
| 849 assert(wasRemoved); | |
| 844 _contexts.remove(folder); | 850 _contexts.remove(folder); |
| 845 } | 851 } |
| 846 | 852 |
| 847 /** | 853 /** |
| 848 * Extract a new [packagespecFile]-based context from [oldInfo]. | 854 * Extract a new [packagespecFile]-based context from [oldInfo]. |
| 849 */ | 855 */ |
| 850 void _extractContext(ContextInfo oldInfo, File packagespecFile) { | 856 void _extractContext(ContextInfo oldInfo, File packagespecFile) { |
| 851 Folder newFolder = packagespecFile.parent; | 857 Folder newFolder = packagespecFile.parent; |
| 852 ContextInfo newInfo = _createContext(newFolder, packagespecFile, []); | 858 ContextInfo newInfo = _createContext(oldInfo, newFolder, packagespecFile); |
| 853 newInfo.parent = oldInfo; | |
| 854 // prepare sources to extract | 859 // prepare sources to extract |
| 855 Map<String, Source> extractedSources = new HashMap<String, Source>(); | 860 Map<String, Source> extractedSources = new HashMap<String, Source>(); |
| 856 oldInfo.sources.forEach((path, source) { | 861 oldInfo.sources.forEach((path, source) { |
| 857 if (newFolder.contains(path)) { | 862 if (newFolder.contains(path)) { |
| 858 extractedSources[path] = source; | 863 extractedSources[path] = source; |
| 859 } | 864 } |
| 860 }); | 865 }); |
| 861 // update new context | 866 // update new context |
| 862 { | 867 { |
| 863 ChangeSet changeSet = new ChangeSet(); | 868 ChangeSet changeSet = new ChangeSet(); |
| 864 extractedSources.forEach((path, source) { | 869 extractedSources.forEach((path, source) { |
| 865 newInfo.sources[path] = source; | 870 newInfo.sources[path] = source; |
| 866 changeSet.addedSource(source); | 871 changeSet.addedSource(source); |
| 867 }); | 872 }); |
| 868 callbacks.applyChangesToContext(newFolder, changeSet); | 873 callbacks.applyChangesToContext(newFolder, changeSet); |
| 869 } | 874 } |
| 870 // update old context | 875 // update old context |
| 871 { | 876 { |
| 872 ChangeSet changeSet = new ChangeSet(); | 877 ChangeSet changeSet = new ChangeSet(); |
| 873 extractedSources.forEach((path, source) { | 878 extractedSources.forEach((path, source) { |
| 874 oldInfo.sources.remove(path); | 879 oldInfo.sources.remove(path); |
| 875 changeSet.removedSource(source); | 880 changeSet.removedSource(source); |
| 876 }); | 881 }); |
| 877 callbacks.applyChangesToContext(oldInfo.folder, changeSet); | 882 callbacks.applyChangesToContext(oldInfo.folder, changeSet); |
| 878 } | 883 } |
| 884 // TODO(paulberry): every context that was previously a child of oldInfo is | |
| 885 // is still a child of oldInfo. This is wrong--some of them ought to be | |
| 886 // adopted by newInfo now. | |
| 879 } | 887 } |
| 880 | 888 |
| 881 void _handleWatchEvent(Folder folder, ContextInfo info, WatchEvent event) { | 889 void _handleWatchEvent(Folder folder, ContextInfo info, WatchEvent event) { |
| 882 // TODO(brianwilkerson) If a file is explicitly included in one context | 890 // TODO(brianwilkerson) If a file is explicitly included in one context |
| 883 // but implicitly referenced in another context, we will only send a | 891 // but implicitly referenced in another context, we will only send a |
| 884 // changeSet to the context that explicitly includes the file (because | 892 // changeSet to the context that explicitly includes the file (because |
| 885 // that's the only context that's watching the file). | 893 // that's the only context that's watching the file). |
| 886 _instrumentationService.logWatchEvent( | 894 _instrumentationService.logWatchEvent( |
| 887 folder.path, event.path, event.type.toString()); | 895 folder.path, event.path, event.type.toString()); |
| 888 String path = event.path; | 896 String path = event.path; |
| (...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 1142 */ | 1150 */ |
| 1143 final List<AnalysisContext> removed; | 1151 final List<AnalysisContext> removed; |
| 1144 | 1152 |
| 1145 /** | 1153 /** |
| 1146 * Initialize a newly created event to indicate which contexts have changed. | 1154 * Initialize a newly created event to indicate which contexts have changed. |
| 1147 */ | 1155 */ |
| 1148 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, | 1156 ContextsChangedEvent({this.added: AnalysisContext.EMPTY_LIST, |
| 1149 this.changed: AnalysisContext.EMPTY_LIST, | 1157 this.changed: AnalysisContext.EMPTY_LIST, |
| 1150 this.removed: AnalysisContext.EMPTY_LIST}); | 1158 this.removed: AnalysisContext.EMPTY_LIST}); |
| 1151 } | 1159 } |
| OLD | NEW |