Get rid of unneeded Future.immediate() calls.

Future.then() will now implicitly wrap the callback result in a
future if it isn't one, so this manual wrapping can be removed.

Also take advantage of the fact that Future.immediate() is always
async now and remove some workarounds.

Finally, noticed there were two copies of the git-related code
floating around so killed the old one.

Review URL: https://codereview.chromium.org//12042053

git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge@17519 260f80e4-7a28-3924-810f-c04153c831b5
diff --git a/lib/src/command_uploader.dart b/lib/src/command_uploader.dart
index eb3cd4a..1e184a3 100644
--- a/lib/src/command_uploader.dart
+++ b/lib/src/command_uploader.dart
@@ -60,7 +60,7 @@
 
     return new Future.immediate(null).then((_) {
       var package = commandOptions['package'];
-      if (package != null) return new Future.immediate(package);
+      if (package != null) return package;
       return Entrypoint.load(path.current, cache)
           .then((entrypoint) => entrypoint.root.name);
     }).then((package) {
diff --git a/lib/src/entrypoint.dart b/lib/src/entrypoint.dart
index 4f895b3..c250897 100644
--- a/lib/src/entrypoint.dart
+++ b/lib/src/entrypoint.dart
@@ -74,7 +74,7 @@
     var future = ensureDir(dirname(packageDir)).then((_) {
       return exists(packageDir);
     }).then((exists) {
-      if (!exists) return new Future.immediate(null);
+      if (!exists) return;
       // TODO(nweiz): figure out when to actually delete the directory, and when
       // we can just re-use the existing symlink.
       log.fine("Deleting package directory for ${id.name} before install.");
@@ -149,7 +149,7 @@
     return fileExists(lockFilePath).then((exists) {
       if (!exists) {
         log.fine("No lock file at $lockFilePath, creating empty one.");
-        return new Future<LockFile>.immediate(new LockFile.empty());
+        return new LockFile.empty();
       }
 
       return readTextFile(lockFilePath).then((text) =>
@@ -175,7 +175,7 @@
     var linkPath = join(path, root.name);
     return exists(linkPath).then((exists) {
       // Create the symlink if it doesn't exist.
-      if (exists) return new Future.immediate(null);
+      if (exists) return;
       return ensureDir(path).then(
           (_) => createPackageSymlink(root.name, root.dir, linkPath,
               isSelfLink: true));
@@ -192,7 +192,7 @@
     var toolDir = join(root.dir, 'tool');
     var webDir = join(root.dir, 'web');
     return dirExists(binDir).then((exists) {
-      if (!exists) return new Future.immediate(null);
+      if (!exists) return;
       return _linkSecondaryPackageDir(binDir);
     }).then((_) => _linkSecondaryPackageDirsRecursively(exampleDir))
       .then((_) => _linkSecondaryPackageDirsRecursively(testDir))
@@ -204,13 +204,13 @@
   /// subdirectories.
   Future _linkSecondaryPackageDirsRecursively(String dir) {
     return dirExists(dir).then((exists) {
-      if (!exists) return new Future.immediate(null);
+      if (!exists) return;
       return _linkSecondaryPackageDir(dir)
         .then((_) => _listDirWithoutPackages(dir))
         .then((files) {
         return Future.wait(files.mappedBy((file) {
           return dirExists(file).then((isDir) {
-            if (!isDir) return new Future.immediate(null);
+            if (!isDir) return;
             return _linkSecondaryPackageDir(file);
           });
         }));
@@ -226,7 +226,7 @@
       return Future.wait(files.mappedBy((file) {
         if (basename(file) == 'packages') return new Future.immediate([]);
         return dirExists(file).then((isDir) {
-          if (!isDir) return new Future.immediate([]);
+          if (!isDir) return [];
           return _listDirWithoutPackages(file);
         }).then((subfiles) {
           var fileAndSubfiles = [file];
@@ -241,7 +241,7 @@
   Future _linkSecondaryPackageDir(String dir) {
     var to = join(dir, 'packages');
     return exists(to).then((exists) {
-      if (exists) return new Future.immediate(null);
+      if (exists) return;
       return createSymlink(path, to);
     });
   }
diff --git a/lib/src/git.dart b/lib/src/git.dart
index ecbc8a0..47519c2 100644
--- a/lib/src/git.dart
+++ b/lib/src/git.dart
@@ -13,9 +13,7 @@
 /// Tests whether or not the git command-line app is available for use.
 Future<bool> get isInstalled {
   if (_isGitInstalledCache != null) {
-    // TODO(rnystrom): The sleep is to pump the message queue. Can use
-    // Future.immediate() when #3356 is fixed.
-    return sleep(0).then((_) => _isGitInstalledCache);
+    return new Future.immediate(_isGitInstalledCache);
   }
 
   return _gitCommand.then((git) => git != null);
@@ -23,9 +21,11 @@
 
 /// Run a git process with [args] from [workingDir]. Returns the stdout as a
 /// list of strings if it succeeded. Completes to an exception if it failed.
-Future<List<String>> run(List<String> args, {String workingDir}) {
+Future<List<String>> run(List<String> args,
+    {String workingDir, Map<String, String> environment}) {
   return _gitCommand.then((git) {
-    return runProcess(git, args, workingDir: workingDir);
+    return runProcess(git, args, workingDir: workingDir,
+        environment: environment);
   }).then((result) {
     if (!result.success) throw new Exception(
         'Git error. Command: git ${Strings.join(args, " ")}\n'
@@ -43,13 +43,12 @@
 /// Returns the name of the git command-line app, or null if Git could not be
 /// found on the user's PATH.
 Future<String> get _gitCommand {
-  // TODO(nweiz): Just use Future.immediate once issue 3356 is fixed.
   if (_gitCommandCache != null) {
-    return sleep(0).then((_) => _gitCommandCache);
+    return new Future.immediate(_gitCommandCache);
   }
 
   return _tryGitCommand("git").then((success) {
-    if (success) return new Future.immediate("git");
+    if (success) return "git";
 
     // Git is sometimes installed on Windows as `git.cmd`
     return _tryGitCommand("git.cmd").then((success) {
diff --git a/lib/src/git_source.dart b/lib/src/git_source.dart
index 0a3e2b9..477cc17 100644
--- a/lib/src/git_source.dart
+++ b/lib/src/git_source.dart
@@ -49,11 +49,11 @@
       revisionCachePath = path;
       return exists(revisionCachePath);
     }).then((exists) {
-      if (exists) return new Future.immediate(null);
+      if (exists) return;
       return _clone(_repoCachePath(id), revisionCachePath, mirror: false);
     }).then((_) {
       var ref = _getEffectiveRef(id);
-      if (ref == 'HEAD') return new Future.immediate(null);
+      if (ref == 'HEAD') return;
       return _checkOut(revisionCachePath, ref);
     }).then((_) {
       return Package.load(id.name, revisionCachePath, systemCache.sources);
diff --git a/lib/src/http.dart b/lib/src/http.dart
index 0a2bf2e..e148418 100644
--- a/lib/src/http.dart
+++ b/lib/src/http.dart
@@ -50,9 +50,7 @@
       var status = streamedResponse.statusCode;
       // 401 responses should be handled by the OAuth2 client. It's very
       // unlikely that they'll be returned by non-OAuth2 requests.
-      if (status < 400 || status == 401) {
-        return new Future.immediate(streamedResponse);
-      }
+      if (status < 400 || status == 401) return streamedResponse;
 
       return http.Response.fromStream(streamedResponse).then((response) {
         throw new PubHttpException(response);
diff --git a/lib/src/io.dart b/lib/src/io.dart
index b227378..0ad5053 100644
--- a/lib/src/io.dart
+++ b/lib/src/io.dart
@@ -15,11 +15,6 @@
 import 'log.dart' as log;
 import 'utils.dart';
 
-bool _isGitInstalledCache;
-
-/// The cached Git command.
-String _gitCommandCache;
-
 final NEWLINE_PATTERN = new RegExp("\r\n?|\n\r?");
 
 /// Joins a number of path string parts into a single path. Handles
@@ -184,7 +179,7 @@
   return dirExists(path).then((exists) {
     if (exists) {
       log.fine("Directory $path already exists.");
-      return new Future.immediate(new Directory(path));
+      return new Directory(path);
     }
 
     return ensureDir(dirname(path)).then((_) {
@@ -411,7 +406,7 @@
                   'you will not be able to import any libraries from it.');
     }
 
-    return new Future.immediate(to);
+    return to;
   });
 }
 
@@ -771,65 +766,6 @@
   });
 }
 
-/// Tests whether or not the git command-line app is available for use.
-Future<bool> get isGitInstalled {
-  if (_isGitInstalledCache != null) {
-    // TODO(rnystrom): The sleep is to pump the message queue. Can use
-    // Future.immediate() when #3356 is fixed.
-    return sleep(0).then((_) => _isGitInstalledCache);
-  }
-
-  return _gitCommand.then((git) => git != null);
-}
-
-/// Run a git process with [args] from [workingDir].
-Future<PubProcessResult> runGit(List<String> args,
-    {String workingDir, Map<String, String> environment}) {
-  return _gitCommand.then((git) => runProcess(git, args,
-        workingDir: workingDir, environment: environment));
-}
-
-/// Returns the name of the git command-line app, or null if Git could not be
-/// found on the user's PATH.
-Future<String> get _gitCommand {
-  // TODO(nweiz): Just use Future.immediate once issue 3356 is fixed.
-  if (_gitCommandCache != null) {
-    return sleep(0).then((_) => _gitCommandCache);
-  }
-
-  return _tryGitCommand("git").then((success) {
-    if (success) return new Future.immediate("git");
-
-    // Git is sometimes installed on Windows as `git.cmd`
-    return _tryGitCommand("git.cmd").then((success) {
-      if (success) return "git.cmd";
-      return null;
-    });
-  }).then((command) {
-    _gitCommandCache = command;
-    return command;
-  });
-}
-
-/// Checks whether [command] is the Git command for this computer.
-Future<bool> _tryGitCommand(String command) {
-  var completer = new Completer<bool>();
-
-  // If "git --version" prints something familiar, git is working.
-  var future = runProcess(command, ["--version"]);
-
-  future.then((results) {
-    var regex = new RegExp("^git version");
-    completer.complete(results.stdout.length == 1 &&
-                       regex.hasMatch(results.stdout[0]));
-  }).catchError((err) {
-    // If the process failed, they probably don't have it.
-    completer.complete(false);
-  });
-
-  return completer.future;
-}
-
 /// Extracts a `.tar.gz` file from [stream] to [destination], which can be a
 /// directory or a path. Returns whether or not the extraction was successful.
 Future<bool> extractTarGz(InputStream stream, destination) {
diff --git a/lib/src/oauth2.dart b/lib/src/oauth2.dart
index 24d798a..206eb53 100644
--- a/lib/src/oauth2.dart
+++ b/lib/src/oauth2.dart
@@ -56,7 +56,6 @@
   var credentialsFile = _credentialsFile(cache);
   return fileExists(credentialsFile).then((exists) {
     if (exists) return deleteFile(credentialsFile);
-    return new Future.immediate(null);
   });
 }
 
@@ -97,8 +96,8 @@
 Future<Client> _getClient(SystemCache cache) {
   return _loadCredentials(cache).then((credentials) {
     if (credentials == null) return _authorize();
-    return new Future.immediate(new Client(
-        _identifier, _secret, credentials, httpClient: curlClient));
+    return new Client(_identifier, _secret, credentials,
+        httpClient: curlClient);
   }).then((client) {
     return _saveCredentials(cache, client.credentials).then((_) => client);
   });
@@ -119,7 +118,7 @@
   return fileExists(path).then((credentialsExist) {
     if (!credentialsExist) {
       log.fine('No credentials found at $path.');
-      return new Future.immediate(null);
+      return;
     }
 
     return readTextFile(_credentialsFile(cache)).then((credentialsJson) {
diff --git a/lib/src/pub.dart b/lib/src/pub.dart
index 03dab44..8a21ecc 100644
--- a/lib/src/pub.dart
+++ b/lib/src/pub.dart
@@ -245,16 +245,14 @@
       this.entrypoint = entrypoint;
       try {
         var commandFuture = onRun();
-        if (commandFuture == null) return new Future.immediate(true);
+        if (commandFuture == null) return true;
 
         return commandFuture;
       } catch (error, trace) {
         handleError(error, trace);
-        return new Future.immediate(null);
       }
     });
 
-
     future
       .then((_) => cache_.deleteTempDir())
       .catchError((asyncError) {
diff --git a/lib/src/system_cache.dart b/lib/src/system_cache.dart
index 50b022e..1e26554 100644
--- a/lib/src/system_cache.dart
+++ b/lib/src/system_cache.dart
@@ -93,7 +93,7 @@
   Future deleteTempDir() {
     log.fine('Clean up system cache temp directory $tempDir.');
     return dirExists(tempDir).then((exists) {
-      if (!exists) return new Future.immediate(null);
+      if (!exists) return;
       return deleteDir(tempDir);
     });
   }
diff --git a/lib/src/validator.dart b/lib/src/validator.dart
index d3bc0a7..5ec0f23 100644
--- a/lib/src/validator.dart
+++ b/lib/src/validator.dart
@@ -53,13 +53,8 @@
       new DirectoryValidator(entrypoint)
     ];
 
-    // TODO(nweiz): The sleep 0 here forces us to go async. This works around
-    // 3356, which causes a bug if all validators are (synchronously) using
-    // Future.immediate and an error is thrown before a handler is set up.
-    return sleep(0).then((_) {
-      return Future.wait(
-          validators.mappedBy((validator) => validator.validate()));
-    }).then((_) {
+    return Future.wait(validators.mappedBy((validator) => validator.validate()))
+      .then((_) {
       var errors =
           flatten(validators.mappedBy((validator) => validator.errors));
       var warnings =
diff --git a/lib/src/validator/lib.dart b/lib/src/validator/lib.dart
index c9c5aba..e679847 100644
--- a/lib/src/validator/lib.dart
+++ b/lib/src/validator/lib.dart
@@ -27,7 +27,7 @@
       if (!libDirExists) {
         errors.add('You must have a "lib" directory.\n'
             "Without that, users cannot import any code from your package.");
-        return new Future.immediate(null);
+        return;
       }
 
       return listDir(libDir).then((files) {
diff --git a/lib/src/validator/name.dart b/lib/src/validator/name.dart
index 67e2da0..eb1bc85 100644
--- a/lib/src/validator/name.dart
+++ b/lib/src/validator/name.dart
@@ -48,7 +48,7 @@
   Future<List<String>> get _libraries {
     var libDir = join(entrypoint.root.dir, "lib");
     return dirExists(libDir).then((libDirExists) {
-      if (!libDirExists) return new Future.immediate([]);
+      if (!libDirExists) return [];
       return listDir(libDir, recursive: true);
     }).then((files) {
       return files