| 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:core' hide Resource; | 10 import 'dart:core' hide Resource; |
| 10 | 11 |
| 11 import 'package:analysis_server/src/analysis_server.dart'; | 12 import 'package:analysis_server/src/analysis_server.dart'; |
| 12 import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.d
art'; | 13 import 'package:analysis_server/src/source/optimizing_pub_package_map_provider.d
art'; |
| 13 import 'package:analysis_server/uri/resolver_provider.dart'; | 14 import 'package:analysis_server/uri/resolver_provider.dart'; |
| 14 import 'package:analyzer/file_system/file_system.dart'; | 15 import 'package:analyzer/file_system/file_system.dart'; |
| 15 import 'package:analyzer/instrumentation/instrumentation.dart'; | 16 import 'package:analyzer/instrumentation/instrumentation.dart'; |
| 16 import 'package:analyzer/source/analysis_options_provider.dart'; | 17 import 'package:analyzer/source/analysis_options_provider.dart'; |
| 17 import 'package:analyzer/source/package_map_resolver.dart'; | 18 import 'package:analyzer/source/package_map_resolver.dart'; |
| 18 import 'package:analyzer/source/path_filter.dart'; | 19 import 'package:analyzer/source/path_filter.dart'; |
| 19 import 'package:analyzer/src/generated/engine.dart'; | 20 import 'package:analyzer/src/generated/engine.dart'; |
| 20 import 'package:analyzer/src/generated/java_io.dart'; | 21 import 'package:analyzer/src/generated/java_io.dart'; |
| 21 import 'package:analyzer/src/generated/source.dart'; | 22 import 'package:analyzer/src/generated/source.dart'; |
| 22 import 'package:analyzer/src/generated/source_io.dart'; | 23 import 'package:analyzer/src/generated/source_io.dart'; |
| 24 import 'package:package_config/packages.dart'; |
| 25 import 'package:package_config/packages_file.dart' as pkgfile show parse; |
| 26 import 'package:package_config/src/packages_impl.dart' show MapPackages; |
| 23 import 'package:path/path.dart' as pathos; | 27 import 'package:path/path.dart' as pathos; |
| 24 import 'package:watcher/watcher.dart'; | 28 import 'package:watcher/watcher.dart'; |
| 25 import 'package:yaml/yaml.dart'; | 29 import 'package:yaml/yaml.dart'; |
| 26 | 30 |
| 27 /** | 31 /** |
| 28 * Class that maintains a mapping from included/excluded paths to a set of | 32 * Class that maintains a mapping from included/excluded paths to a set of |
| 29 * folders that should correspond to analysis contexts. | 33 * folders that should correspond to analysis contexts. |
| 30 */ | 34 */ |
| 31 abstract class AbstractContextManager implements ContextManager { | 35 abstract class AbstractContextManager implements ContextManager { |
| 36 |
| 37 /** |
| 38 * Temporary flag to hide WIP .packages support (DEP 5). |
| 39 */ |
| 40 static bool ENABLE_PACKAGESPEC_SUPPORT = false; |
| 41 |
| 32 /** | 42 /** |
| 33 * The name of the `lib` directory. | 43 * The name of the `lib` directory. |
| 34 */ | 44 */ |
| 35 static const String LIB_DIR_NAME = 'lib'; | 45 static const String LIB_DIR_NAME = 'lib'; |
| 36 | 46 |
| 37 /** | 47 /** |
| 38 * The name of `packages` folders. | 48 * The name of `packages` folders. |
| 39 */ | 49 */ |
| 40 static const String PACKAGES_NAME = 'packages'; | 50 static const String PACKAGES_NAME = 'packages'; |
| 41 | 51 |
| 42 /** | 52 /** |
| 43 * File name of pubspec files. | 53 * File name of pubspec files. |
| 44 */ | 54 */ |
| 45 static const String PUBSPEC_NAME = 'pubspec.yaml'; | 55 static const String PUBSPEC_NAME = 'pubspec.yaml'; |
| 46 | 56 |
| 47 /** | 57 /** |
| 58 * File name of package spec files. |
| 59 */ |
| 60 static const String PACKAGE_SPEC_NAME = '.packages'; |
| 61 |
| 62 /** |
| 48 * [_ContextInfo] object for each included directory in the most | 63 * [_ContextInfo] object for each included directory in the most |
| 49 * recent successful call to [setRoots]. | 64 * recent successful call to [setRoots]. |
| 50 */ | 65 */ |
| 51 Map<Folder, _ContextInfo> _contexts = new HashMap<Folder, _ContextInfo>(); | 66 Map<Folder, _ContextInfo> _contexts = new HashMap<Folder, _ContextInfo>(); |
| 52 | 67 |
| 53 /** | 68 /** |
| 54 * The [ResourceProvider] using which paths are converted into [Resource]s. | 69 * The [ResourceProvider] using which paths are converted into [Resource]s. |
| 55 */ | 70 */ |
| 56 final ResourceProvider resourceProvider; | 71 final ResourceProvider resourceProvider; |
| 57 | 72 |
| (...skipping 48 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 106 final InstrumentationService _instrumentationService; | 121 final InstrumentationService _instrumentationService; |
| 107 | 122 |
| 108 AbstractContextManager(this.resourceProvider, this.packageResolverProvider, | 123 AbstractContextManager(this.resourceProvider, this.packageResolverProvider, |
| 109 this._packageMapProvider, this._instrumentationService) { | 124 this._packageMapProvider, this._instrumentationService) { |
| 110 pathContext = resourceProvider.pathContext; | 125 pathContext = resourceProvider.pathContext; |
| 111 } | 126 } |
| 112 | 127 |
| 113 /** | 128 /** |
| 114 * Create and return a new analysis context. | 129 * Create and return a new analysis context. |
| 115 */ | 130 */ |
| 116 AnalysisContext addContext(Folder folder, UriResolver packageUriResolver); | 131 AnalysisContext addContext( |
| 132 Folder folder, UriResolver packageUriResolver, Packages packages); |
| 117 | 133 |
| 118 /** | 134 /** |
| 119 * Called when the set of files associated with a context have changed (or | 135 * Called when the set of files associated with a context have changed (or |
| 120 * some of those files have been modified). [changeSet] is the set of | 136 * some of those files have been modified). [changeSet] is the set of |
| 121 * changes that need to be applied to the context. | 137 * changes that need to be applied to the context. |
| 122 */ | 138 */ |
| 123 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet); | 139 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet); |
| 124 | 140 |
| 125 /** | 141 /** |
| 126 * We are about to start computing the package map. | 142 * We are about to start computing the package map. |
| (...skipping 36 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 163 return contexts; | 179 return contexts; |
| 164 } | 180 } |
| 165 | 181 |
| 166 /** | 182 /** |
| 167 * We have finished computing the package map. | 183 * We have finished computing the package map. |
| 168 */ | 184 */ |
| 169 void endComputePackageMap() { | 185 void endComputePackageMap() { |
| 170 // Do nothing. | 186 // Do nothing. |
| 171 } | 187 } |
| 172 | 188 |
| 173 /// Sets the [ignorePatterns] for the context [folder]. | 189 @override |
| 174 void setIgnorePatternsForContext(Folder folder, List<String> ignorePatterns) { | 190 bool isInAnalysisRoot(String path) { |
| 175 _ContextInfo info = _contexts[folder]; | 191 // check if excluded |
| 176 if (info == null) { | 192 if (_isExcluded(path)) { |
| 177 return; | 193 return false; |
| 178 } | 194 } |
| 179 var pathFilter = info.pathFilter; | 195 // check if in of the roots |
| 180 pathFilter.setIgnorePatterns(ignorePatterns); | 196 for (Folder root in _contexts.keys) { |
| 197 if (root.contains(path)) { |
| 198 return true; |
| 199 } |
| 200 } |
| 201 // no |
| 202 return false; |
| 181 } | 203 } |
| 182 | 204 |
| 183 /// Process [options] for the context [folder]. | 205 /// Process [options] for the context [folder]. |
| 184 void processOptionsForContext(Folder folder, Map<String, YamlNode> options) { | 206 void processOptionsForContext(Folder folder, Map<String, YamlNode> options) { |
| 185 _ContextInfo info = _contexts[folder]; | 207 _ContextInfo info = _contexts[folder]; |
| 186 if (info == null) { | 208 if (info == null) { |
| 187 return; | 209 return; |
| 188 } | 210 } |
| 189 YamlMap analyzer = options['analyzer']; | 211 YamlMap analyzer = options['analyzer']; |
| 190 if (analyzer == null) { | 212 if (analyzer == null) { |
| 191 // No options for analyzer. | 213 // No options for analyzer. |
| 192 return; | 214 return; |
| 193 } | 215 } |
| 194 | 216 |
| 195 // Set ignore patterns. | 217 // Set ignore patterns. |
| 196 YamlList exclude = analyzer['exclude']; | 218 YamlList exclude = analyzer['exclude']; |
| 197 if (exclude != null) { | 219 if (exclude != null) { |
| 198 setIgnorePatternsForContext(folder, exclude); | 220 setIgnorePatternsForContext(folder, exclude); |
| 199 } | 221 } |
| 200 } | 222 } |
| 201 | 223 |
| 202 @override | 224 @override |
| 203 bool isInAnalysisRoot(String path) { | |
| 204 // check if excluded | |
| 205 if (_isExcluded(path)) { | |
| 206 return false; | |
| 207 } | |
| 208 // check if in of the roots | |
| 209 for (Folder root in _contexts.keys) { | |
| 210 if (root.contains(path)) { | |
| 211 return true; | |
| 212 } | |
| 213 } | |
| 214 // no | |
| 215 return false; | |
| 216 } | |
| 217 | |
| 218 @override | |
| 219 void refresh(List<Resource> roots) { | 225 void refresh(List<Resource> roots) { |
| 220 // Destroy old contexts | 226 // Destroy old contexts |
| 221 List<Folder> contextFolders = _contexts.keys.toList(); | 227 List<Folder> contextFolders = _contexts.keys.toList(); |
| 222 if (roots == null) { | 228 if (roots == null) { |
| 223 contextFolders.forEach(_destroyContext); | 229 contextFolders.forEach(_destroyContext); |
| 224 } else { | 230 } else { |
| 225 roots.forEach((Resource resource) { | 231 roots.forEach((Resource resource) { |
| 226 contextFolders.forEach((Folder contextFolder) { | 232 contextFolders.forEach((Folder contextFolder) { |
| 227 if (resource is Folder && resource.isOrContains(contextFolder.path)) { | 233 if (resource is Folder && resource.isOrContains(contextFolder.path)) { |
| 228 _destroyContext(contextFolder); | 234 _destroyContext(contextFolder); |
| 229 } | 235 } |
| 230 }); | 236 }); |
| 231 }); | 237 }); |
| 232 } | 238 } |
| 233 | 239 |
| 234 // Rebuild contexts based on the data last sent to setRoots(). | 240 // Rebuild contexts based on the data last sent to setRoots(). |
| 235 setRoots(includedPaths, excludedPaths, packageRoots); | 241 setRoots(includedPaths, excludedPaths, packageRoots); |
| 236 } | 242 } |
| 237 | 243 |
| 238 /** | 244 /** |
| 239 * Remove the context associated with the given [folder]. | 245 * Remove the context associated with the given [folder]. |
| 240 */ | 246 */ |
| 241 void removeContext(Folder folder); | 247 void removeContext(Folder folder); |
| 242 | 248 |
| 249 /// Sets the [ignorePatterns] for the context [folder]. |
| 250 void setIgnorePatternsForContext(Folder folder, List<String> ignorePatterns) { |
| 251 _ContextInfo info = _contexts[folder]; |
| 252 if (info == null) { |
| 253 return; |
| 254 } |
| 255 var pathFilter = info.pathFilter; |
| 256 pathFilter.setIgnorePatterns(ignorePatterns); |
| 257 } |
| 258 |
| 243 @override | 259 @override |
| 244 void setRoots(List<String> includedPaths, List<String> excludedPaths, | 260 void setRoots(List<String> includedPaths, List<String> excludedPaths, |
| 245 Map<String, String> packageRoots) { | 261 Map<String, String> packageRoots) { |
| 246 this.packageRoots = packageRoots; | 262 this.packageRoots = packageRoots; |
| 247 | 263 |
| 248 // Normalize all package root sources by mapping them to folders on the | 264 // Normalize all package root sources by mapping them to folders on the |
| 249 // filesystem. Ignore any package root sources that aren't folders. | 265 // filesystem. Ignore any package root sources that aren't folders. |
| 250 normalizedPackageRoots = <String, String>{}; | 266 normalizedPackageRoots = <String, String>{}; |
| 251 packageRoots.forEach((String sourcePath, String targetPath) { | 267 packageRoots.forEach((String sourcePath, String targetPath) { |
| 252 Resource resource = resourceProvider.getResource(sourcePath); | 268 Resource resource = resourceProvider.getResource(sourcePath); |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 327 | 343 |
| 328 /** | 344 /** |
| 329 * Return `true` if the given [file] should be analyzed. | 345 * Return `true` if the given [file] should be analyzed. |
| 330 */ | 346 */ |
| 331 bool shouldFileBeAnalyzed(File file); | 347 bool shouldFileBeAnalyzed(File file); |
| 332 | 348 |
| 333 /** | 349 /** |
| 334 * Called when the package map for a context has changed. | 350 * Called when the package map for a context has changed. |
| 335 */ | 351 */ |
| 336 void updateContextPackageUriResolver( | 352 void updateContextPackageUriResolver( |
| 337 Folder contextFolder, UriResolver packageUriResolver); | 353 Folder contextFolder, UriResolver packageUriResolver, Packages packages); |
| 338 | 354 |
| 339 /** | 355 /** |
| 340 * Resursively adds all Dart and HTML files to the [changeSet]. | 356 * Resursively adds all Dart and HTML files to the [changeSet]. |
| 341 */ | 357 */ |
| 342 void _addPreviouslyExcludedSources(_ContextInfo info, ChangeSet changeSet, | 358 void _addPreviouslyExcludedSources(_ContextInfo info, ChangeSet changeSet, |
| 343 Folder folder, List<String> oldExcludedPaths) { | 359 Folder folder, List<String> oldExcludedPaths) { |
| 344 if (info.excludesResource(folder)) { | 360 if (info.excludesResource(folder)) { |
| 345 return; | 361 return; |
| 346 } | 362 } |
| 347 List<Resource> children; | 363 List<Resource> children; |
| (...skipping 76 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 424 /** | 440 /** |
| 425 * Cancel all dependency subscriptions for the given context. | 441 * Cancel all dependency subscriptions for the given context. |
| 426 */ | 442 */ |
| 427 void _cancelDependencySubscriptions(_ContextInfo info) { | 443 void _cancelDependencySubscriptions(_ContextInfo info) { |
| 428 for (StreamSubscription<WatchEvent> s in info.dependencySubscriptions) { | 444 for (StreamSubscription<WatchEvent> s in info.dependencySubscriptions) { |
| 429 s.cancel(); | 445 s.cancel(); |
| 430 } | 446 } |
| 431 info.dependencySubscriptions.clear(); | 447 info.dependencySubscriptions.clear(); |
| 432 } | 448 } |
| 433 | 449 |
| 450 void _checkForPackagespecUpdate( |
| 451 String path, _ContextInfo info, Folder folder) { |
| 452 // Check to see if this is the .packages file for this context and if so, |
| 453 // update the context's source factory. |
| 454 if (pathContext.basename(path) == PACKAGE_SPEC_NAME && |
| 455 info.isPathToPackageDescription(path)) { |
| 456 File packagespec = resourceProvider.getFile(path); |
| 457 if (packagespec.exists) { |
| 458 Packages packages = _readPackagespec(packagespec); |
| 459 if (packages != null) { |
| 460 updateContextPackageUriResolver(folder, null, packages); |
| 461 } |
| 462 } |
| 463 } |
| 464 } |
| 465 |
| 434 /** | 466 /** |
| 435 * Compute the appropriate package URI resolver for [folder], and store | 467 * Compute the appropriate package URI resolver for [folder], and store |
| 436 * dependency information in [info]. Return `null` if no package map can | 468 * dependency information in [info]. Return `null` if no package map can |
| 437 * be computed. | 469 * be computed. |
| 438 */ | 470 */ |
| 439 UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) { | 471 UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) { |
| 440 _cancelDependencySubscriptions(info); | 472 _cancelDependencySubscriptions(info); |
| 441 if (info.packageRoot != null) { | 473 if (info.packageRoot != null) { |
| 442 info.packageMapInfo = null; | 474 info.packageMapInfo = null; |
| 443 JavaFile packagesDir = new JavaFile(info.packageRoot); | 475 JavaFile packagesDir = new JavaFile(info.packageRoot); |
| (...skipping 60 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 504 resourceProvider, packageMapInfo.packageMap); | 536 resourceProvider, packageMapInfo.packageMap); |
| 505 // TODO(paulberry): if any of the dependencies is outside of [folder], | 537 // TODO(paulberry): if any of the dependencies is outside of [folder], |
| 506 // we'll need to watch their parent folders as well. | 538 // we'll need to watch their parent folders as well. |
| 507 } | 539 } |
| 508 } | 540 } |
| 509 | 541 |
| 510 /** | 542 /** |
| 511 * Create a new empty context associated with [folder]. | 543 * Create a new empty context associated with [folder]. |
| 512 */ | 544 */ |
| 513 _ContextInfo _createContext( | 545 _ContextInfo _createContext( |
| 514 Folder folder, File pubspecFile, List<_ContextInfo> children) { | 546 Folder folder, File packagespecFile, List<_ContextInfo> children) { |
| 515 _ContextInfo info = new _ContextInfo( | 547 _ContextInfo info = new _ContextInfo( |
| 516 folder, pubspecFile, children, normalizedPackageRoots[folder.path]); | 548 folder, packagespecFile, children, normalizedPackageRoots[folder.path]); |
| 517 _contexts[folder] = info; | 549 _contexts[folder] = info; |
| 518 var options = analysisOptionsProvider.getOptions(folder); | 550 var options = analysisOptionsProvider.getOptions(folder); |
| 519 processOptionsForContext(folder, options); | 551 processOptionsForContext(folder, options); |
| 520 info.changeSubscription = folder.changes.listen((WatchEvent event) { | 552 info.changeSubscription = folder.changes.listen((WatchEvent event) { |
| 521 _handleWatchEvent(folder, info, event); | 553 _handleWatchEvent(folder, info, event); |
| 522 }); | 554 }); |
| 523 try { | 555 try { |
| 524 UriResolver packageUriResolver = _computePackageUriResolver(folder, info); | 556 Packages packages; |
| 525 info.context = addContext(folder, packageUriResolver); | 557 UriResolver packageUriResolver; |
| 558 |
| 559 if (ENABLE_PACKAGESPEC_SUPPORT) { |
| 560 // Try .packages first. |
| 561 if (pathos.basename(packagespecFile.path) == PACKAGE_SPEC_NAME) { |
| 562 packages = _readPackagespec(packagespecFile); |
| 563 } |
| 564 } |
| 565 |
| 566 // Next resort to a package uri resolver. |
| 567 if (packages == null) { |
| 568 packageUriResolver = _computePackageUriResolver(folder, info); |
| 569 } |
| 570 |
| 571 info.context = addContext(folder, packageUriResolver, packages); |
| 526 info.context.name = folder.path; | 572 info.context.name = folder.path; |
| 527 } catch (_) { | 573 } catch (_) { |
| 528 info.changeSubscription.cancel(); | 574 info.changeSubscription.cancel(); |
| 529 rethrow; | 575 rethrow; |
| 530 } | 576 } |
| 531 return info; | 577 return info; |
| 532 } | 578 } |
| 533 | 579 |
| 534 /** | 580 /** |
| 535 * Potentially create a new context associated with the given [folder]. | 581 * Potentially create a new context associated with the given [folder]. |
| 536 * | 582 * |
| 537 * If there are subfolders with 'pubspec.yaml' files, separate contexts are | 583 * If there are subfolders with 'pubspec.yaml' files, separate contexts are |
| 538 * created for them and excluded from the context associated with the | 584 * created for them and excluded from the context associated with the |
| 539 * [folder]. | 585 * [folder]. |
| 540 * | 586 * |
| 541 * If [withPubspecOnly] is `true`, a context will be created only if there | 587 * If [withPackageSpecOnly] is `true`, a context will be created only if there |
| 542 * is a 'pubspec.yaml' file in the [folder]. | 588 * is a 'pubspec.yaml' or '.packages' file in the [folder]. |
| 543 * | 589 * |
| 544 * Returns create pubspec-based contexts. | 590 * Returns created contexts. |
| 545 */ | 591 */ |
| 546 List<_ContextInfo> _createContexts(Folder folder, bool withPubspecOnly) { | 592 List<_ContextInfo> _createContexts(Folder folder, bool withPackageSpecOnly) { |
| 547 // try to find subfolders with pubspec files | 593 // Try to find subfolders with pubspecs or .packages files. |
| 548 List<_ContextInfo> children = <_ContextInfo>[]; | 594 List<_ContextInfo> children = <_ContextInfo>[]; |
| 549 try { | 595 try { |
| 550 for (Resource child in folder.getChildren()) { | 596 for (Resource child in folder.getChildren()) { |
| 551 if (child is Folder) { | 597 if (child is Folder) { |
| 552 children.addAll(_createContexts(child, true)); | 598 children.addAll(_createContexts(child, true)); |
| 553 } | 599 } |
| 554 } | 600 } |
| 555 } on FileSystemException { | 601 } on FileSystemException { |
| 556 // The directory either doesn't exist or cannot be read. Either way, there | 602 // The directory either doesn't exist or cannot be read. Either way, there |
| 557 // are no subfolders that need to be added. | 603 // are no subfolders that need to be added. |
| 558 } | 604 } |
| 559 // check whether there is a pubspec in the folder | 605 |
| 560 File pubspecFile = folder.getChild(PUBSPEC_NAME); | 606 File packageSpec; |
| 561 if (pubspecFile.exists) { | 607 |
| 608 if (ENABLE_PACKAGESPEC_SUPPORT) { |
| 609 // Start by looking for .packages. |
| 610 packageSpec = folder.getChild(PACKAGE_SPEC_NAME); |
| 611 } |
| 612 |
| 613 // Fall back to looking for a pubspec. |
| 614 if (packageSpec == null || !packageSpec.exists) { |
| 615 packageSpec = folder.getChild(PUBSPEC_NAME); |
| 616 } |
| 617 |
| 618 if (packageSpec.exists) { |
| 562 return <_ContextInfo>[ | 619 return <_ContextInfo>[ |
| 563 _createContextWithSources(folder, pubspecFile, children) | 620 _createContextWithSources(folder, packageSpec, children) |
| 564 ]; | 621 ]; |
| 565 } | 622 } |
| 566 // no pubspec, done | 623 // No packagespec? Done. |
| 567 if (withPubspecOnly) { | 624 if (withPackageSpecOnly) { |
| 568 return children; | 625 return children; |
| 569 } | 626 } |
| 570 // OK, create a context without a pubspec | 627 // OK, create a context without a packagespec. |
| 571 return <_ContextInfo>[ | 628 return <_ContextInfo>[ |
| 572 _createContextWithSources(folder, pubspecFile, children) | 629 _createContextWithSources(folder, packageSpec, children) |
| 573 ]; | 630 ]; |
| 574 } | 631 } |
| 575 | 632 |
| 576 /** | 633 /** |
| 577 * Create a new context associated with the given [folder]. The [pubspecFile] | 634 * Create a new context associated with the given [folder]. The [pubspecFile] |
| 578 * is the `pubspec.yaml` file contained in the folder. Add any sources that | 635 * is the `pubspec.yaml` file contained in the folder. Add any sources that |
| 579 * are not included in one of the [children] to the context. | 636 * are not included in one of the [children] to the context. |
| 580 */ | 637 */ |
| 581 _ContextInfo _createContextWithSources( | 638 _ContextInfo _createContextWithSources( |
| 582 Folder folder, File pubspecFile, List<_ContextInfo> children) { | 639 Folder folder, File pubspecFile, List<_ContextInfo> children) { |
| 583 _ContextInfo info = _createContext(folder, pubspecFile, children); | 640 _ContextInfo info = _createContext(folder, pubspecFile, children); |
| 584 ChangeSet changeSet = new ChangeSet(); | 641 ChangeSet changeSet = new ChangeSet(); |
| 585 _addSourceFiles(changeSet, folder, info); | 642 _addSourceFiles(changeSet, folder, info); |
| 586 applyChangesToContext(folder, changeSet); | 643 applyChangesToContext(folder, changeSet); |
| 587 return info; | 644 return info; |
| 588 } | 645 } |
| 589 | 646 |
| 590 /** | 647 /** |
| 591 * Clean up and destroy the context associated with the given folder. | 648 * Clean up and destroy the context associated with the given folder. |
| 592 */ | 649 */ |
| 593 void _destroyContext(Folder folder) { | 650 void _destroyContext(Folder folder) { |
| 594 _ContextInfo info = _contexts[folder]; | 651 _ContextInfo info = _contexts[folder]; |
| 595 info.changeSubscription.cancel(); | 652 info.changeSubscription.cancel(); |
| 596 _cancelDependencySubscriptions(info); | 653 _cancelDependencySubscriptions(info); |
| 597 removeContext(folder); | 654 removeContext(folder); |
| 598 _contexts.remove(folder); | 655 _contexts.remove(folder); |
| 599 } | 656 } |
| 600 | 657 |
| 601 /** | 658 /** |
| 602 * Extract a new [pubspecFile]-based context from [oldInfo]. | 659 * Extract a new [packagespecFile]-based context from [oldInfo]. |
| 603 */ | 660 */ |
| 604 void _extractContext(_ContextInfo oldInfo, File pubspecFile) { | 661 void _extractContext(_ContextInfo oldInfo, File packagespecFile) { |
| 605 Folder newFolder = pubspecFile.parent; | 662 Folder newFolder = packagespecFile.parent; |
| 606 _ContextInfo newInfo = _createContext(newFolder, pubspecFile, []); | 663 _ContextInfo newInfo = _createContext(newFolder, packagespecFile, []); |
| 607 newInfo.parent = oldInfo; | 664 newInfo.parent = oldInfo; |
| 608 // prepare sources to extract | 665 // prepare sources to extract |
| 609 Map<String, Source> extractedSources = new HashMap<String, Source>(); | 666 Map<String, Source> extractedSources = new HashMap<String, Source>(); |
| 610 oldInfo.sources.forEach((path, source) { | 667 oldInfo.sources.forEach((path, source) { |
| 611 if (newFolder.contains(path)) { | 668 if (newFolder.contains(path)) { |
| 612 extractedSources[path] = source; | 669 extractedSources[path] = source; |
| 613 } | 670 } |
| 614 }); | 671 }); |
| 615 // update new context | 672 // update new context |
| 616 { | 673 { |
| (...skipping 33 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 650 } | 707 } |
| 651 if (info.ignored(path)) { | 708 if (info.ignored(path)) { |
| 652 return; | 709 return; |
| 653 } | 710 } |
| 654 // handle the change | 711 // handle the change |
| 655 switch (event.type) { | 712 switch (event.type) { |
| 656 case ChangeType.ADD: | 713 case ChangeType.ADD: |
| 657 if (_isInPackagesDir(path, folder)) { | 714 if (_isInPackagesDir(path, folder)) { |
| 658 return; | 715 return; |
| 659 } | 716 } |
| 717 |
| 660 Resource resource = resourceProvider.getResource(path); | 718 Resource resource = resourceProvider.getResource(path); |
| 661 // pubspec was added in a sub-folder, extract a new context | 719 |
| 662 if (_isPubspec(path) && info.isRoot && !info.isPubspec(path)) { | 720 if (ENABLE_PACKAGESPEC_SUPPORT) { |
| 663 _extractContext(info, resource); | 721 String directoryPath = pathContext.dirname(path); |
| 664 return; | 722 |
| 723 // Check to see if we need to create a new context. |
| 724 if (info.isRoot) { |
| 725 |
| 726 // Only create a new context if this is not the same directory |
| 727 // described by our info object. |
| 728 if (info.folder.path != directoryPath) { |
| 729 if (_isPubspec(path)) { |
| 730 // Check for a sibling .packages file. |
| 731 if (!resourceProvider.getFile( |
| 732 pathos.join(directoryPath, PACKAGE_SPEC_NAME)).exists) { |
| 733 _extractContext(info, resource); |
| 734 return; |
| 735 } |
| 736 } |
| 737 if (_isPackagespec(path)) { |
| 738 // Check for a sibling pubspec.yaml file. |
| 739 if (!resourceProvider |
| 740 .getFile(pathos.join(directoryPath, PUBSPEC_NAME)).exists) { |
| 741 _extractContext(info, resource); |
| 742 return; |
| 743 } |
| 744 } |
| 745 } |
| 746 } |
| 747 } else { |
| 748 // pubspec was added in a sub-folder, extract a new context |
| 749 if (_isPubspec(path) && |
| 750 info.isRoot && |
| 751 !info.isPathToPackageDescription(path)) { |
| 752 _extractContext(info, resource); |
| 753 return; |
| 754 } |
| 665 } | 755 } |
| 756 |
| 666 // If the file went away and was replaced by a folder before we | 757 // If the file went away and was replaced by a folder before we |
| 667 // had a chance to process the event, resource might be a Folder. In | 758 // had a chance to process the event, resource might be a Folder. In |
| 668 // that case don't add it. | 759 // that case don't add it. |
| 669 if (resource is File) { | 760 if (resource is File) { |
| 670 File file = resource; | 761 File file = resource; |
| 671 if (shouldFileBeAnalyzed(file)) { | 762 if (shouldFileBeAnalyzed(file)) { |
| 672 ChangeSet changeSet = new ChangeSet(); | 763 ChangeSet changeSet = new ChangeSet(); |
| 673 Source source = createSourceInContext(info.context, file); | 764 Source source = createSourceInContext(info.context, file); |
| 674 changeSet.addedSource(source); | 765 changeSet.addedSource(source); |
| 675 applyChangesToContext(folder, changeSet); | 766 applyChangesToContext(folder, changeSet); |
| 676 info.sources[path] = source; | 767 info.sources[path] = source; |
| 677 } | 768 } |
| 678 } | 769 } |
| 679 break; | 770 break; |
| 680 case ChangeType.REMOVE: | 771 case ChangeType.REMOVE: |
| 681 // pubspec was removed, merge the context into its parent | 772 |
| 682 if (info.isPubspec(path) && !info.isRoot) { | 773 // If package spec info is removed, check to see if we can merge context
s. |
| 683 _mergeContext(info); | 774 // Note that it's important to verify that there is NEITHER a .packages
nor a |
| 684 return; | 775 // lingering pubspec.yaml before merging. |
| 776 if (!info.isRoot) { |
| 777 if (ENABLE_PACKAGESPEC_SUPPORT) { |
| 778 String directoryPath = pathContext.dirname(path); |
| 779 |
| 780 // Only merge if this is the same directory described by our info ob
ject. |
| 781 if (info.folder.path == directoryPath) { |
| 782 if (_isPubspec(path)) { |
| 783 // Check for a sibling .packages file. |
| 784 if (!resourceProvider.getFile( |
| 785 pathos.join(directoryPath, PACKAGE_SPEC_NAME)).exists) { |
| 786 _mergeContext(info); |
| 787 return; |
| 788 } |
| 789 } |
| 790 if (_isPackagespec(path)) { |
| 791 // Check for a sibling pubspec.yaml file. |
| 792 if (!resourceProvider |
| 793 .getFile(pathos.join(directoryPath, PUBSPEC_NAME)).exists) { |
| 794 _mergeContext(info); |
| 795 return; |
| 796 } |
| 797 } |
| 798 } |
| 799 } else { |
| 800 if (info.isPathToPackageDescription(path)) { |
| 801 _mergeContext(info); |
| 802 return; |
| 803 } |
| 804 } |
| 685 } | 805 } |
| 806 |
| 686 List<Source> sources = info.context.getSourcesWithFullName(path); | 807 List<Source> sources = info.context.getSourcesWithFullName(path); |
| 687 if (!sources.isEmpty) { | 808 if (!sources.isEmpty) { |
| 688 ChangeSet changeSet = new ChangeSet(); | 809 ChangeSet changeSet = new ChangeSet(); |
| 689 sources.forEach((Source source) { | 810 sources.forEach((Source source) { |
| 690 changeSet.removedSource(source); | 811 changeSet.removedSource(source); |
| 691 }); | 812 }); |
| 692 applyChangesToContext(folder, changeSet); | 813 applyChangesToContext(folder, changeSet); |
| 693 info.sources.remove(path); | 814 info.sources.remove(path); |
| 694 } | 815 } |
| 695 break; | 816 break; |
| 696 case ChangeType.MODIFY: | 817 case ChangeType.MODIFY: |
| 697 List<Source> sources = info.context.getSourcesWithFullName(path); | 818 List<Source> sources = info.context.getSourcesWithFullName(path); |
| 698 if (!sources.isEmpty) { | 819 if (!sources.isEmpty) { |
| 699 ChangeSet changeSet = new ChangeSet(); | 820 ChangeSet changeSet = new ChangeSet(); |
| 700 sources.forEach((Source source) { | 821 sources.forEach((Source source) { |
| 701 changeSet.changedSource(source); | 822 changeSet.changedSource(source); |
| 702 }); | 823 }); |
| 703 applyChangesToContext(folder, changeSet); | 824 applyChangesToContext(folder, changeSet); |
| 704 } | 825 } |
| 705 break; | 826 break; |
| 706 } | 827 } |
| 707 | 828 |
| 829 //TODO(pquitslund): find the right place for this |
| 830 _checkForPackagespecUpdate(path, info, folder); |
| 831 |
| 708 if (info.packageMapInfo != null && | 832 if (info.packageMapInfo != null && |
| 709 info.packageMapInfo.isChangedDependency(path, resourceProvider)) { | 833 info.packageMapInfo.isChangedDependency(path, resourceProvider)) { |
| 710 _recomputePackageUriResolver(info); | 834 _recomputePackageUriResolver(info); |
| 711 } | 835 } |
| 712 } | 836 } |
| 713 | 837 |
| 714 /** | 838 /** |
| 715 * Returns `true` if the given [path] is excluded by [excludedPaths]. | 839 * Returns `true` if the given [path] is excluded by [excludedPaths]. |
| 716 */ | 840 */ |
| 717 bool _isExcluded(String path) { | 841 bool _isExcluded(String path) => _isExcludedBy(excludedPaths, path); |
| 718 return _isExcludedBy(excludedPaths, path); | |
| 719 } | |
| 720 | 842 |
| 721 /** | 843 /** |
| 722 * Returns `true` if the given [path] is excluded by [excludedPaths]. | 844 * Returns `true` if the given [path] is excluded by [excludedPaths]. |
| 723 */ | 845 */ |
| 724 bool _isExcludedBy(List<String> excludedPaths, String path) { | 846 bool _isExcludedBy(List<String> excludedPaths, String path) { |
| 725 return excludedPaths.any((excludedPath) { | 847 return excludedPaths.any((excludedPath) { |
| 726 if (pathContext.isWithin(excludedPath, path)) { | 848 if (pathContext.isWithin(excludedPath, path)) { |
| 727 return true; | 849 return true; |
| 728 } | 850 } |
| 729 return path == excludedPath; | 851 return path == excludedPath; |
| 730 }); | 852 }); |
| 731 } | 853 } |
| 732 | 854 |
| 733 /** | 855 /** |
| 734 * Determine if the path from [folder] to [path] contains a 'packages' | 856 * Determine if the path from [folder] to [path] contains a 'packages' |
| 735 * directory. | 857 * directory. |
| 736 */ | 858 */ |
| 737 bool _isInPackagesDir(String path, Folder folder) { | 859 bool _isInPackagesDir(String path, Folder folder) { |
| 738 String relativePath = pathContext.relative(path, from: folder.path); | 860 String relativePath = pathContext.relative(path, from: folder.path); |
| 739 List<String> pathParts = pathContext.split(relativePath); | 861 List<String> pathParts = pathContext.split(relativePath); |
| 740 return pathParts.contains(PACKAGES_NAME); | 862 return pathParts.contains(PACKAGES_NAME); |
| 741 } | 863 } |
| 742 | 864 |
| 743 /** | 865 bool _isPackagespec(String path) => |
| 744 * Returns `true` if the given absolute [path] is a pubspec file. | 866 pathContext.basename(path) == PACKAGE_SPEC_NAME; |
| 745 */ | 867 |
| 746 bool _isPubspec(String path) { | 868 bool _isPubspec(String path) => pathContext.basename(path) == PUBSPEC_NAME; |
| 747 return pathContext.basename(path) == PUBSPEC_NAME; | |
| 748 } | |
| 749 | 869 |
| 750 /** | 870 /** |
| 751 * Merges [info] context into its parent. | 871 * Merges [info] context into its parent. |
| 752 */ | 872 */ |
| 753 void _mergeContext(_ContextInfo info) { | 873 void _mergeContext(_ContextInfo info) { |
| 754 // destroy the context | 874 // destroy the context |
| 755 _destroyContext(info.folder); | 875 _destroyContext(info.folder); |
| 756 // add files to the parent context | 876 // add files to the parent context |
| 757 _ContextInfo parentInfo = info.parent; | 877 _ContextInfo parentInfo = info.parent; |
| 758 if (parentInfo != null) { | 878 if (parentInfo != null) { |
| 759 parentInfo.children.remove(info); | 879 parentInfo.children.remove(info); |
| 760 ChangeSet changeSet = new ChangeSet(); | 880 ChangeSet changeSet = new ChangeSet(); |
| 761 info.sources.forEach((path, source) { | 881 info.sources.forEach((path, source) { |
| 762 parentInfo.sources[path] = source; | 882 parentInfo.sources[path] = source; |
| 763 changeSet.addedSource(source); | 883 changeSet.addedSource(source); |
| 764 }); | 884 }); |
| 765 applyChangesToContext(parentInfo.folder, changeSet); | 885 applyChangesToContext(parentInfo.folder, changeSet); |
| 766 } | 886 } |
| 767 } | 887 } |
| 768 | 888 |
| 889 Packages _readPackagespec(File specFile) { |
| 890 try { |
| 891 String contents = specFile.readAsStringSync(); |
| 892 Map<String, Uri> map = |
| 893 pkgfile.parse(UTF8.encode(contents), new Uri.file(specFile.path)); |
| 894 return new MapPackages(map); |
| 895 } catch (_) { |
| 896 //TODO(pquitslund): consider creating an error for the spec file. |
| 897 return null; |
| 898 } |
| 899 } |
| 900 |
| 769 /** | 901 /** |
| 770 * Recompute the package URI resolver for the context described by [info], | 902 * Recompute the package URI resolver for the context described by [info], |
| 771 * and update the client appropriately. | 903 * and update the client appropriately. |
| 772 */ | 904 */ |
| 773 void _recomputePackageUriResolver(_ContextInfo info) { | 905 void _recomputePackageUriResolver(_ContextInfo info) { |
| 774 // TODO(paulberry): when computePackageMap is changed into an | 906 // TODO(paulberry): when computePackageMap is changed into an |
| 775 // asynchronous API call, we'll want to suspend analysis for this context | 907 // asynchronous API call, we'll want to suspend analysis for this context |
| 776 // while we're rerunning "pub list", since any analysis we complete while | 908 // while we're rerunning "pub list", since any analysis we complete while |
| 777 // "pub list" is in progress is just going to get thrown away anyhow. | 909 // "pub list" is in progress is just going to get thrown away anyhow. |
| 778 UriResolver packageUriResolver = | 910 UriResolver packageUriResolver = |
| 779 _computePackageUriResolver(info.folder, info); | 911 _computePackageUriResolver(info.folder, info); |
| 780 updateContextPackageUriResolver(info.folder, packageUriResolver); | 912 updateContextPackageUriResolver(info.folder, packageUriResolver, null); |
| 781 } | 913 } |
| 782 | 914 |
| 783 /** | 915 /** |
| 784 * Create and return a source representing the given [file] within the given | 916 * Create and return a source representing the given [file] within the given |
| 785 * [context]. | 917 * [context]. |
| 786 */ | 918 */ |
| 787 static Source createSourceInContext(AnalysisContext context, File file) { | 919 static Source createSourceInContext(AnalysisContext context, File file) { |
| 788 // TODO(brianwilkerson) Optimize this, by allowing support for source | 920 // TODO(brianwilkerson) Optimize this, by allowing support for source |
| 789 // factories to restore URI's from a file path rather than a source. | 921 // factories to restore URI's from a file path rather than a source. |
| 790 Source source = file.createSource(); | 922 Source source = file.createSource(); |
| (...skipping 125 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 916 * The package root for this context, or null if there is no package root. | 1048 * The package root for this context, or null if there is no package root. |
| 917 */ | 1049 */ |
| 918 String packageRoot; | 1050 String packageRoot; |
| 919 | 1051 |
| 920 /** | 1052 /** |
| 921 * The [_ContextInfo] that encloses this one. | 1053 * The [_ContextInfo] that encloses this one. |
| 922 */ | 1054 */ |
| 923 _ContextInfo parent; | 1055 _ContextInfo parent; |
| 924 | 1056 |
| 925 /** | 1057 /** |
| 926 * The `pubspec.yaml` file path for this context. | 1058 * The package description file path for this context. |
| 927 */ | 1059 */ |
| 928 String pubspecPath; | 1060 String packageDescriptionPath; |
| 929 | 1061 |
| 930 /** | 1062 /** |
| 931 * Stream subscription we are using to watch the context's directory for | 1063 * Stream subscription we are using to watch the context's directory for |
| 932 * changes. | 1064 * changes. |
| 933 */ | 1065 */ |
| 934 StreamSubscription<WatchEvent> changeSubscription; | 1066 StreamSubscription<WatchEvent> changeSubscription; |
| 935 | 1067 |
| 936 /** | 1068 /** |
| 937 * Stream subscriptions we are using to watch the files | 1069 * Stream subscriptions we are using to watch the files |
| 938 * used to determine the package map. | 1070 * used to determine the package map. |
| (...skipping 12 matching lines...) Expand all Loading... |
| 951 */ | 1083 */ |
| 952 Map<String, Source> sources = new HashMap<String, Source>(); | 1084 Map<String, Source> sources = new HashMap<String, Source>(); |
| 953 | 1085 |
| 954 /** | 1086 /** |
| 955 * Info returned by the last call to | 1087 * Info returned by the last call to |
| 956 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the | 1088 * [OptimizingPubPackageMapProvider.computePackageMap], or `null` if the |
| 957 * package map hasn't been computed for this context yet. | 1089 * package map hasn't been computed for this context yet. |
| 958 */ | 1090 */ |
| 959 OptimizingPubPackageMapInfo packageMapInfo; | 1091 OptimizingPubPackageMapInfo packageMapInfo; |
| 960 | 1092 |
| 961 _ContextInfo(Folder folder, File pubspecFile, this.children, this.packageRoot) | 1093 _ContextInfo( |
| 1094 Folder folder, File packagespecFile, this.children, this.packageRoot) |
| 962 : folder = folder, | 1095 : folder = folder, |
| 963 pathFilter = new PathFilter(folder.path, null) { | 1096 pathFilter = new PathFilter(folder.path, null) { |
| 964 pubspecPath = pubspecFile.path; | 1097 packageDescriptionPath = packagespecFile.path; |
| 965 for (_ContextInfo child in children) { | 1098 for (_ContextInfo child in children) { |
| 966 child.parent = this; | 1099 child.parent = this; |
| 967 } | 1100 } |
| 968 } | 1101 } |
| 969 | 1102 |
| 970 /** | 1103 /** |
| 971 * Returns `true` if this context is root folder based. | 1104 * Returns `true` if this context is root folder based. |
| 972 */ | 1105 */ |
| 973 bool get isRoot => parent == null; | 1106 bool get isRoot => parent == null; |
| 974 | 1107 |
| 975 /** | 1108 /** |
| 976 * Returns `true` if [path] is excluded, as it is in one of the children. | 1109 * Returns `true` if [path] is excluded, as it is in one of the children. |
| 977 */ | 1110 */ |
| 978 bool excludes(String path) { | 1111 bool excludes(String path) { |
| 979 return children.any((child) { | 1112 return children.any((child) { |
| 980 return child.folder.contains(path); | 1113 return child.folder.contains(path); |
| 981 }); | 1114 }); |
| 982 } | 1115 } |
| 983 | 1116 |
| 1117 /** |
| 1118 * Returns `true` if [resource] is excluded, as it is in one of the children. |
| 1119 */ |
| 1120 bool excludesResource(Resource resource) => excludes(resource.path); |
| 1121 |
| 984 /// Returns `true` if [path] should be ignored. | 1122 /// Returns `true` if [path] should be ignored. |
| 985 bool ignored(String path) => pathFilter.ignored(path); | 1123 bool ignored(String path) => pathFilter.ignored(path); |
| 986 | 1124 |
| 987 /** | 1125 /** |
| 988 * Returns `true` if [resource] is excluded, as it is in one of the children. | 1126 * Returns `true` if [path] is the package description file for this context |
| 1127 * (pubspec.yaml or .packages). |
| 989 */ | 1128 */ |
| 990 bool excludesResource(Resource resource) { | 1129 bool isPathToPackageDescription(String path) => |
| 991 return excludes(resource.path); | 1130 path == packageDescriptionPath; |
| 992 } | |
| 993 | |
| 994 /** | |
| 995 * Returns `true` if [path] is the pubspec file of this context. | |
| 996 */ | |
| 997 bool isPubspec(String path) { | |
| 998 return path == pubspecPath; | |
| 999 } | |
| 1000 } | 1131 } |
| OLD | NEW |