| Index: utils/pub/io.dart
|
| diff --git a/utils/pub/io.dart b/utils/pub/io.dart
|
| index d7e06343462a99de528a7892a116f2426130f216..bea81a6c2ff0165561f0cb1dcc3484aeeb750b58 100644
|
| --- a/utils/pub/io.dart
|
| +++ b/utils/pub/io.dart
|
| @@ -245,50 +245,74 @@ Future<Directory> deleteDir(dir) {
|
| dir.delete(recursive: true)));
|
| }
|
|
|
| -/**
|
| - * Asynchronously lists the contents of [dir], which can be a [String] directory
|
| - * path or a [Directory]. If [recursive] is `true`, lists subdirectory contents
|
| - * (defaults to `false`). If [includeHiddenFiles] is `true`, includes files and
|
| - * directories beginning with `.` (defaults to `false`).
|
| - */
|
| +/// Asynchronously lists the contents of [dir], which can be a [String]
|
| +/// directory path or a [Directory]. If [recursive] is `true`, lists
|
| +/// subdirectory contents (defaults to `false`). If [includeHiddenFiles] is
|
| +/// `true`, includes files and directories beginning with `.` (defaults to
|
| +/// `false`).
|
| +///
|
| +/// If [dir] is a string, the returned paths are guaranteed to begin with it.
|
| Future<List<String>> listDir(dir,
|
| {bool recursive: false, bool includeHiddenFiles: false}) {
|
| - final completer = new Completer<List<String>>();
|
| - final contents = <String>[];
|
| + Future<List<String>> doList(Directory dir, Set<String> listedDirectories) {
|
| + var contents = <String>[];
|
| + var completer = new Completer<List<String>>();
|
| +
|
| + // Avoid recursive symlinks.
|
| + var resolvedPath = new File(dir.path).fullPathSync();
|
| + if (listedDirectories.contains(resolvedPath)) {
|
| + return new Future.immediate([]);
|
| + }
|
| + listedDirectories = new Set<String>.from(listedDirectories);
|
| + listedDirectories.add(resolvedPath);
|
| +
|
| + log.io("Listing directory ${dir.path}.");
|
| + var lister = dir.list();
|
| +
|
| + lister.onDone = (done) {
|
| + // TODO(rnystrom): May need to sort here if it turns out onDir and onFile
|
| + // aren't guaranteed to be called in a certain order. So far, they seem to.
|
| + if (done) {
|
| + log.fine("Listed directory ${dir.path}:\n"
|
| + "${Strings.join(contents, '\n')}");
|
| + completer.complete(contents);
|
| + }
|
| + };
|
|
|
| - dir = _getDirectory(dir);
|
| - log.io("Listing directory ${dir.path}.");
|
| - var lister = dir.list(recursive: recursive);
|
| -
|
| - lister.onDone = (done) {
|
| - // TODO(rnystrom): May need to sort here if it turns out onDir and onFile
|
| - // aren't guaranteed to be called in a certain order. So far, they seem to.
|
| - if (done) {
|
| - log.fine("Listed directory ${dir.path}:\n"
|
| - "${Strings.join(contents, '\n')}");
|
| - completer.complete(contents);
|
| + // TODO(nweiz): remove this when issue 4061 is fixed.
|
| + var stackTrace;
|
| + try {
|
| + throw "";
|
| + } catch (_, localStackTrace) {
|
| + stackTrace = localStackTrace;
|
| }
|
| - };
|
|
|
| - // TODO(nweiz): remove this when issue 4061 is fixed.
|
| - var stackTrace;
|
| - try {
|
| - throw "";
|
| - } catch (_, localStackTrace) {
|
| - stackTrace = localStackTrace;
|
| - }
|
| + var children = [];
|
| + lister.onError = (error) => completer.completeException(error, stackTrace);
|
| + lister.onDir = (file) {
|
| + if (!includeHiddenFiles && basename(file).startsWith('.')) return;
|
| + file = join(dir, basename(file));
|
| + contents.add(file);
|
|
|
| - lister.onError = (error) => completer.completeException(error, stackTrace);
|
| - lister.onDir = (file) {
|
| - if (!includeHiddenFiles && basename(file).startsWith('.')) return;
|
| - contents.add(file);
|
| - };
|
| - lister.onFile = (file) {
|
| - if (!includeHiddenFiles && basename(file).startsWith('.')) return;
|
| - contents.add(file);
|
| - };
|
| + // TODO(nweiz): don't manually recurse once issue 7358 is fixed.
|
| + if (recursive) {
|
| + children.add(doList(new Directory(file), listedDirectories));
|
| + }
|
| + };
|
| + lister.onFile = (file) {
|
| + if (!includeHiddenFiles && basename(file).startsWith('.')) return;
|
| + contents.add(join(dir, basename(file)));
|
| + };
|
| +
|
| + return completer.future.chain((contents) {
|
| + return Futures.wait(children).transform((childContents) {
|
| + contents.addAll(flatten(childContents));
|
| + return contents;
|
| + });
|
| + });
|
| + }
|
|
|
| - return completer.future;
|
| + return doList(_getDirectory(dir), new Set<String>());
|
| }
|
|
|
| /**
|
|
|