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 | 9 |
10 import 'package:analyzer/file_system/file_system.dart'; | 10 import 'package:analyzer/file_system/file_system.dart'; |
(...skipping 18 matching lines...) Expand all Loading... |
29 */ | 29 */ |
30 const String PUBSPEC_NAME = 'pubspec.yaml'; | 30 const String PUBSPEC_NAME = 'pubspec.yaml'; |
31 | 31 |
32 | 32 |
33 /** | 33 /** |
34 * Class that maintains a mapping from included/excluded paths to a set of | 34 * Class that maintains a mapping from included/excluded paths to a set of |
35 * folders that should correspond to analysis contexts. | 35 * folders that should correspond to analysis contexts. |
36 */ | 36 */ |
37 abstract class ContextManager { | 37 abstract class ContextManager { |
38 /** | 38 /** |
| 39 * The name of the `lib` directory. |
| 40 */ |
| 41 static const String LIB_DIR_NAME = 'lib'; |
| 42 |
| 43 /** |
39 * [_ContextInfo] object for each included directory in the most | 44 * [_ContextInfo] object for each included directory in the most |
40 * recent successful call to [setRoots]. | 45 * recent successful call to [setRoots]. |
41 */ | 46 */ |
42 Map<Folder, _ContextInfo> _contexts = new HashMap<Folder, _ContextInfo>(); | 47 Map<Folder, _ContextInfo> _contexts = new HashMap<Folder, _ContextInfo>(); |
43 | 48 |
44 /** | 49 /** |
45 * The [ResourceProvider] using which paths are converted into [Resource]s. | 50 * The [ResourceProvider] using which paths are converted into [Resource]s. |
46 */ | 51 */ |
47 final ResourceProvider resourceProvider; | 52 final ResourceProvider resourceProvider; |
48 | 53 |
(...skipping 29 matching lines...) Expand all Loading... |
78 * Provider which is used to determine the mapping from package name to | 83 * Provider which is used to determine the mapping from package name to |
79 * package folder. | 84 * package folder. |
80 */ | 85 */ |
81 final PackageMapProvider _packageMapProvider; | 86 final PackageMapProvider _packageMapProvider; |
82 | 87 |
83 ContextManager(this.resourceProvider, this._packageMapProvider) { | 88 ContextManager(this.resourceProvider, this._packageMapProvider) { |
84 pathContext = resourceProvider.pathContext; | 89 pathContext = resourceProvider.pathContext; |
85 } | 90 } |
86 | 91 |
87 /** | 92 /** |
88 * Called when a new context needs to be created. | 93 * Create and return a new analysis context. |
89 */ | 94 */ |
90 void addContext(Folder folder, UriResolver packageUriResolver); | 95 AnalysisContext addContext(Folder folder, UriResolver packageUriResolver); |
91 | 96 |
92 /** | 97 /** |
93 * Called when the set of files associated with a context have changed (or | 98 * Called when the set of files associated with a context have changed (or |
94 * some of those files have been modified). [changeSet] is the set of | 99 * some of those files have been modified). [changeSet] is the set of |
95 * changes that need to be applied to the context. | 100 * changes that need to be applied to the context. |
96 */ | 101 */ |
97 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet); | 102 void applyChangesToContext(Folder contextFolder, ChangeSet changeSet); |
98 | 103 |
99 /** | 104 /** |
100 * We are about to start computing the package map. | 105 * We are about to start computing the package map. |
(...skipping 174 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
275 continue; | 280 continue; |
276 } | 281 } |
277 _addPreviouslyExcludedSources(info, changeSet, child, oldExcludedPaths); | 282 _addPreviouslyExcludedSources(info, changeSet, child, oldExcludedPaths); |
278 } | 283 } |
279 } | 284 } |
280 } | 285 } |
281 | 286 |
282 /** | 287 /** |
283 * Resursively adds all Dart and HTML files to the [changeSet]. | 288 * Resursively adds all Dart and HTML files to the [changeSet]. |
284 */ | 289 */ |
285 void _addSourceFiles(ChangeSet changeSet, Folder folder, _ContextInfo info) { | 290 void _addSourceFiles(ChangeSet changeSet, Folder folder, _ContextInfo info, |
| 291 bool pubspecExists, bool createPackageUri) { |
286 if (info.excludesResource(folder) || folder.shortName.startsWith('.')) { | 292 if (info.excludesResource(folder) || folder.shortName.startsWith('.')) { |
287 return; | 293 return; |
288 } | 294 } |
289 List<Resource> children = folder.getChildren(); | 295 List<Resource> children = folder.getChildren(); |
290 for (Resource child in children) { | 296 for (Resource child in children) { |
291 String path = child.path; | 297 String path = child.path; |
292 // ignore excluded files or folders | 298 // ignore excluded files or folders |
293 if (_isExcluded(path)) { | 299 if (_isExcluded(path)) { |
294 continue; | 300 continue; |
295 } | 301 } |
296 // add files, recurse into folders | 302 // add files, recurse into folders |
297 if (child is File) { | 303 if (child is File) { |
298 if (_shouldFileBeAnalyzed(child)) { | 304 if (_shouldFileBeAnalyzed(child)) { |
299 Source source = child.createSource(); | 305 Source source = child.createSource(); |
| 306 if (createPackageUri) { |
| 307 String packagePath = info.folder.path; |
| 308 String packageName = |
| 309 resourceProvider.pathContext.basename(packagePath); |
| 310 String libPath = |
| 311 resourceProvider.pathContext.join(packagePath, LIB_DIR_NAME); |
| 312 String relPath = source.fullName.substring(libPath.length); |
| 313 Uri uri = |
| 314 Uri.parse('${PackageMapUriResolver.PACKAGE_SCHEME}:$packageName$
relPath'); |
| 315 source = child.createSource(uri); |
| 316 } |
300 changeSet.addedSource(source); | 317 changeSet.addedSource(source); |
301 info.sources[path] = source; | 318 info.sources[path] = source; |
302 } | 319 } |
303 } else if (child is Folder) { | 320 } else if (child is Folder) { |
304 if (child.shortName == PACKAGES_NAME) { | 321 String shortName = child.shortName; |
| 322 if (shortName == PACKAGES_NAME) { |
305 continue; | 323 continue; |
306 } | 324 } |
307 _addSourceFiles(changeSet, child, info); | 325 if (pubspecExists && |
| 326 !createPackageUri && |
| 327 shortName == LIB_DIR_NAME && |
| 328 child.parent == info.folder) { |
| 329 _addSourceFiles(changeSet, child, info, pubspecExists, true); |
| 330 } else { |
| 331 _addSourceFiles( |
| 332 changeSet, |
| 333 child, |
| 334 info, |
| 335 pubspecExists, |
| 336 createPackageUri); |
| 337 } |
308 } | 338 } |
309 } | 339 } |
310 } | 340 } |
311 | 341 |
312 /** | 342 /** |
313 * Compute the appropriate package URI resolver for [folder], and store | 343 * Compute the appropriate package URI resolver for [folder], and store |
314 * dependency information in [info]. Return `null` if no package map can | 344 * dependency information in [info]. Return `null` if no package map can |
315 * be computed. | 345 * be computed. |
316 */ | 346 */ |
317 UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) { | 347 UriResolver _computePackageUriResolver(Folder folder, _ContextInfo info) { |
(...skipping 13 matching lines...) Expand all Loading... |
331 resourceProvider, | 361 resourceProvider, |
332 packageMapInfo.packageMap); | 362 packageMapInfo.packageMap); |
333 // TODO(paulberry): if any of the dependencies is outside of [folder], | 363 // TODO(paulberry): if any of the dependencies is outside of [folder], |
334 // we'll need to watch their parent folders as well. | 364 // we'll need to watch their parent folders as well. |
335 } | 365 } |
336 } | 366 } |
337 | 367 |
338 /** | 368 /** |
339 * Create a new empty context associated with [folder]. | 369 * Create a new empty context associated with [folder]. |
340 */ | 370 */ |
341 _ContextInfo _createContext(Folder folder, List<_ContextInfo> children) { | 371 _ContextInfo _createContext(Folder folder, File pubspecFile, |
342 _ContextInfo info = | 372 List<_ContextInfo> children) { |
343 new _ContextInfo(folder, children, normalizedPackageRoots[folder.path]); | 373 _ContextInfo info = new _ContextInfo( |
| 374 folder, |
| 375 pubspecFile, |
| 376 children, |
| 377 normalizedPackageRoots[folder.path]); |
344 _contexts[folder] = info; | 378 _contexts[folder] = info; |
345 info.changeSubscription = folder.changes.listen((WatchEvent event) { | 379 info.changeSubscription = folder.changes.listen((WatchEvent event) { |
346 _handleWatchEvent(folder, info, event); | 380 _handleWatchEvent(folder, info, event); |
347 }); | 381 }); |
348 UriResolver packageUriResolver = _computePackageUriResolver(folder, info); | 382 UriResolver packageUriResolver = _computePackageUriResolver(folder, info); |
349 addContext(folder, packageUriResolver); | 383 info.context = addContext(folder, packageUriResolver); |
350 return info; | 384 return info; |
351 } | 385 } |
352 | 386 |
353 /** | 387 /** |
354 * Creates a new context associated with [folder]. | 388 * Creates a new context associated with [folder]. |
355 * | 389 * |
356 * If there are subfolders with 'pubspec.yaml' files, separate contexts | 390 * If there are subfolders with 'pubspec.yaml' files, separate contexts |
357 * are created for them, and excluded from the context associated with | 391 * are created for them, and excluded from the context associated with |
358 * [folder]. | 392 * [folder]. |
359 * | 393 * |
360 * If [folder] itself contains a 'pubspec.yaml' file, subfolders are ignored. | 394 * If [folder] itself contains a 'pubspec.yaml' file, subfolders are ignored. |
361 * | 395 * |
362 * If [withPubspecOnly] is `true`, a context will be created only if there | 396 * If [withPubspecOnly] is `true`, a context will be created only if there |
363 * is a 'pubspec.yaml' file in [folder]. | 397 * is a 'pubspec.yaml' file in [folder]. |
364 * | 398 * |
365 * Returns create pubspec-based contexts. | 399 * Returns create pubspec-based contexts. |
366 */ | 400 */ |
367 List<_ContextInfo> _createContexts(Folder folder, bool withPubspecOnly) { | 401 List<_ContextInfo> _createContexts(Folder folder, bool withPubspecOnly) { |
368 // check if there is a pubspec in the folder | 402 // check whether there is a pubspec in the folder |
369 { | 403 File pubspecFile = folder.getChild(PUBSPEC_NAME); |
370 File pubspecFile = folder.getChild(PUBSPEC_NAME); | 404 bool pubspecExists = pubspecFile.exists; |
371 if (pubspecFile.exists) { | 405 if (pubspecExists) { |
372 _ContextInfo info = _createContextWithSources(folder, <_ContextInfo>[]); | 406 _ContextInfo info = _createContextWithSources( |
373 return [info]; | 407 folder, |
374 } | 408 pubspecFile, |
| 409 pubspecExists, |
| 410 <_ContextInfo>[]); |
| 411 return [info]; |
375 } | 412 } |
376 // try to find subfolders with pubspec files | 413 // try to find subfolders with pubspec files |
377 List<_ContextInfo> children = <_ContextInfo>[]; | 414 List<_ContextInfo> children = <_ContextInfo>[]; |
378 for (Resource child in folder.getChildren()) { | 415 for (Resource child in folder.getChildren()) { |
379 if (child is Folder) { | 416 if (child is Folder) { |
380 List<_ContextInfo> childContexts = _createContexts(child, true); | 417 List<_ContextInfo> childContexts = _createContexts(child, true); |
381 children.addAll(childContexts); | 418 children.addAll(childContexts); |
382 } | 419 } |
383 } | 420 } |
384 // no pubspec, done | 421 // no pubspec, done |
385 if (withPubspecOnly) { | 422 if (withPubspecOnly) { |
386 return children; | 423 return children; |
387 } | 424 } |
388 // OK, create a context without a pubspec | 425 // OK, create a context without a pubspec |
389 _createContextWithSources(folder, children); | 426 _createContextWithSources(folder, pubspecFile, pubspecExists, children); |
390 return children; | 427 return children; |
391 } | 428 } |
392 | 429 |
393 /** | 430 /** |
394 * Create a new context associated with [folder] and fills its with sources. | 431 * Create a new context associated with the given [folder]. The [pubspecFile] |
| 432 * is the `pubspec.yaml` file contained in the folder, and [pubspecExists] is |
| 433 * `true` if the file exists. Add any sources that are not included in one of |
| 434 * the [children] to the context. |
395 */ | 435 */ |
396 _ContextInfo _createContextWithSources(Folder folder, | 436 _ContextInfo _createContextWithSources(Folder folder, File pubspecFile, |
397 List<_ContextInfo> children) { | 437 bool pubspecExists, List<_ContextInfo> children) { |
398 _ContextInfo info = _createContext(folder, children); | 438 _ContextInfo info = _createContext(folder, pubspecFile, children); |
399 ChangeSet changeSet = new ChangeSet(); | 439 ChangeSet changeSet = new ChangeSet(); |
400 _addSourceFiles(changeSet, folder, info); | 440 _addSourceFiles(changeSet, folder, info, pubspecExists, false); |
401 applyChangesToContext(folder, changeSet); | 441 applyChangesToContext(folder, changeSet); |
402 return info; | 442 return info; |
403 } | 443 } |
404 | 444 |
405 /** | 445 /** |
406 * Clean up and destroy the context associated with the given folder. | 446 * Clean up and destroy the context associated with the given folder. |
407 */ | 447 */ |
408 void _destroyContext(Folder folder) { | 448 void _destroyContext(Folder folder) { |
409 _contexts[folder].changeSubscription.cancel(); | 449 _contexts[folder].changeSubscription.cancel(); |
410 _contexts.remove(folder); | 450 _contexts.remove(folder); |
411 removeContext(folder); | 451 removeContext(folder); |
412 } | 452 } |
413 | 453 |
414 /** | 454 /** |
415 * Extract a new [pubspecFile]-based context from [oldInfo]. | 455 * Extract a new [pubspecFile]-based context from [oldInfo]. |
416 */ | 456 */ |
417 void _extractContext(_ContextInfo oldInfo, File pubspecFile) { | 457 void _extractContext(_ContextInfo oldInfo, File pubspecFile) { |
418 Folder newFolder = pubspecFile.parent; | 458 Folder newFolder = pubspecFile.parent; |
419 _ContextInfo newInfo = _createContext(newFolder, []); | 459 _ContextInfo newInfo = _createContext(newFolder, pubspecFile, []); |
420 newInfo.parent = oldInfo; | 460 newInfo.parent = oldInfo; |
421 // prepare sources to extract | 461 // prepare sources to extract |
422 Map<String, Source> extractedSources = new HashMap<String, Source>(); | 462 Map<String, Source> extractedSources = new HashMap<String, Source>(); |
423 oldInfo.sources.forEach((path, source) { | 463 oldInfo.sources.forEach((path, source) { |
424 if (newFolder.contains(path)) { | 464 if (newFolder.contains(path)) { |
425 extractedSources[path] = source; | 465 extractedSources[path] = source; |
426 } | 466 } |
427 }); | 467 }); |
428 // update new context | 468 // update new context |
429 { | 469 { |
(...skipping 192 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
622 */ | 662 */ |
623 String pubspecPath; | 663 String pubspecPath; |
624 | 664 |
625 /** | 665 /** |
626 * Stream subscription we are using to watch the context's directory for | 666 * Stream subscription we are using to watch the context's directory for |
627 * changes. | 667 * changes. |
628 */ | 668 */ |
629 StreamSubscription<WatchEvent> changeSubscription; | 669 StreamSubscription<WatchEvent> changeSubscription; |
630 | 670 |
631 /** | 671 /** |
| 672 * The analysis context that was created for the [folder]. |
| 673 */ |
| 674 AnalysisContext context; |
| 675 |
| 676 /** |
632 * Map from full path to the [Source] object, for each source that has been | 677 * Map from full path to the [Source] object, for each source that has been |
633 * added to the context. | 678 * added to the context. |
634 */ | 679 */ |
635 Map<String, Source> sources = new HashMap<String, Source>(); | 680 Map<String, Source> sources = new HashMap<String, Source>(); |
636 | 681 |
637 /** | 682 /** |
638 * Dependencies of the context's package map. | 683 * Dependencies of the context's package map. |
639 * If any of these files changes, the package map needs to be recomputed. | 684 * If any of these files changes, the package map needs to be recomputed. |
640 */ | 685 */ |
641 Set<String> packageMapDependencies; | 686 Set<String> packageMapDependencies; |
642 | 687 |
643 _ContextInfo(this.folder, this.children, this.packageRoot) { | 688 _ContextInfo(this.folder, File pubspecFile, this.children, this.packageRoot) { |
644 pubspecPath = folder.getChild(PUBSPEC_NAME).path; | 689 pubspecPath = pubspecFile.path; |
645 for (_ContextInfo child in children) { | 690 for (_ContextInfo child in children) { |
646 child.parent = this; | 691 child.parent = this; |
647 } | 692 } |
648 } | 693 } |
649 | 694 |
650 /** | 695 /** |
651 * Returns `true` if this context is root folder based. | 696 * Returns `true` if this context is root folder based. |
652 */ | 697 */ |
653 bool get isRoot => parent == null; | 698 bool get isRoot => parent == null; |
654 | 699 |
(...skipping 13 matching lines...) Expand all Loading... |
668 return excludes(resource.path); | 713 return excludes(resource.path); |
669 } | 714 } |
670 | 715 |
671 /** | 716 /** |
672 * Returns `true` if [path] is the pubspec file of this context. | 717 * Returns `true` if [path] is the pubspec file of this context. |
673 */ | 718 */ |
674 bool isPubspec(String path) { | 719 bool isPubspec(String path) { |
675 return path == pubspecPath; | 720 return path == pubspecPath; |
676 } | 721 } |
677 } | 722 } |
OLD | NEW |