Index: pkg/analysis_server/lib/src/source/optimizing_pub_package_map_provider.dart |
diff --git a/pkg/analysis_server/lib/src/source/optimizing_pub_package_map_provider.dart b/pkg/analysis_server/lib/src/source/optimizing_pub_package_map_provider.dart |
new file mode 100644 |
index 0000000000000000000000000000000000000000..ccda5f963e9970ddcf275d80077f0b45c693cf31 |
--- /dev/null |
+++ b/pkg/analysis_server/lib/src/source/optimizing_pub_package_map_provider.dart |
@@ -0,0 +1,134 @@ |
+// Copyright (c) 2015, the Dart project authors. Please see the AUTHORS file |
+// for details. All rights reserved. Use of this source code is governed by a |
+// BSD-style license that can be found in the LICENSE file. |
+ |
+library source.optimizing_pub_package_map_provider; |
+ |
+import 'package:analyzer/file_system/file_system.dart'; |
+import 'package:analyzer/source/package_map_provider.dart'; |
+import 'package:analyzer/source/pub_package_map_provider.dart'; |
+import 'package:analyzer/src/generated/sdk_io.dart'; |
+ |
+/** |
+ * Extension of [PackageMapInfo] that tracks the modification timestamps of |
+ * pub dependencies. This allows the analysis server to avoid making redundant |
+ * calls to "pub list" when nothing has changed. |
+ */ |
+class OptimizingPubPackageMapInfo extends PackageMapInfo { |
+ /** |
+ * Map from file path to the file's modification timestamp prior to running |
+ * "pub list". Since the set of dependencies is not always known prior to |
+ * running "pub list", some or all of the dependencies may be missing from |
+ * this map. |
+ */ |
+ final Map<String, int> modificationTimes; |
+ |
+ OptimizingPubPackageMapInfo(Map<String, List<Folder>> packageMap, |
+ Set<String> dependencies, this.modificationTimes) |
+ : super(packageMap, dependencies); |
+ |
+ /** |
+ * Return `true` if the given [path] is listed as a dependency, and we cannot |
+ * prove using modification timestamps that it is unchanged. |
+ * [resourceProvider] is used (if necessary) to read the [path]'s |
+ * modification time. |
+ */ |
+ bool isChangedDependency(String path, ResourceProvider resourceProvider) { |
+ if (!dependencies.contains(path)) { |
+ // Path is not a dependency. |
+ return false; |
+ } |
+ int lastModificationTime = modificationTimes[path]; |
+ if (lastModificationTime != null) { |
+ Resource resource = resourceProvider.getResource(path); |
+ if (resource is File) { |
+ try { |
+ if (resource.modificationStamp == lastModificationTime) { |
+ // Path is a dependency, but it hasn't changed since the last run |
+ // of "pub list". |
+ return false; |
+ } |
+ } on FileSystemException { |
+ // Path is a dependency, but we can't read its timestamp. Assume |
+ // it's changed to be safe. |
+ } |
+ } |
+ } |
+ // Path is a dependency, and we couldn't prove that it hadn't changed. |
+ // Assume it's changed to be safe. |
+ return true; |
+ } |
+} |
+ |
+/** |
+ * Extension of [PubPackageMapProvider] that outputs additional information to |
+ * allow the analysis server to avoid making redundant calls to "pub list" when |
+ * nothing has changed. |
+ */ |
+class OptimizingPubPackageMapProvider extends PubPackageMapProvider { |
+ OptimizingPubPackageMapProvider( |
+ ResourceProvider resourceProvider, DirectoryBasedDartSdk sdk, [RunPubList runPubList]) |
+ : super(resourceProvider, sdk, runPubList); |
+ |
+ /** |
+ * Compute a package map for the given folder by executing "pub list". If |
+ * [previousInfo] is provided, it is used as a guess of which files the |
+ * package map is likely to depend on; the modification times of those files |
+ * are captured prior to executing "pub list" so that they can be used to |
+ * avoid making redundant calls to "pub list" in the future. |
+ * |
+ * Also, in the case where dependencies can't be determined because of an |
+ * error, the dependencies from [previousInfo] will be preserved. |
+ */ |
+ OptimizingPubPackageMapInfo computePackageMap(Folder folder, |
+ [OptimizingPubPackageMapInfo previousInfo]) { |
+ // Prior to running "pub list", read the modification timestamps of all of |
+ // the old dependencies (if known). |
+ Map<String, int> modificationTimes = <String, int>{}; |
+ if (previousInfo != null) { |
+ for (String path in previousInfo.dependencies) { |
+ Resource resource = resourceProvider.getResource(path); |
+ if (resource is File) { |
+ try { |
+ modificationTimes[path] = resource.modificationStamp; |
+ } on FileSystemException { |
+ // File no longer exists. Don't record a timestamp for it; this |
+ // will ensure that if the file reappears, we will re-run "pub |
+ // list" regardless of the timestamp it reappears with. |
+ } |
+ } |
+ } |
+ } |
+ |
+ // Try running "pub list". |
+ PackageMapInfo info = super.computePackageMap(folder); |
+ if (info == null) { |
+ // Computing the package map resulted in an error. Merge the old |
+ // dependencies with the new ones, if possible. |
+ info = super.computePackageMapError(folder); |
+ if (previousInfo != null) { |
+ info.dependencies.addAll(previousInfo.dependencies); |
+ } |
+ } |
+ |
+ // Discard any elements of modificationTimes that are no longer |
+ // dependencies. |
+ if (previousInfo != null) { |
+ for (String dependency |
+ in previousInfo.dependencies.difference(info.dependencies)) { |
+ modificationTimes.remove(dependency); |
+ } |
+ } |
+ |
+ // Bundle the modificationTimes with the other info. |
+ return new OptimizingPubPackageMapInfo( |
+ info.packageMap, info.dependencies, modificationTimes); |
+ } |
+ |
+ @override |
+ PackageMapInfo computePackageMapError(Folder folder) { |
+ // Return null to indicate to our override of computePackageMap that there |
+ // was an error, so it can compute dependencies correctly. |
+ return null; |
+ } |
+} |