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 |