Chromium Code Reviews| Index: sdk/lib/_internal/pub/lib/src/command.dart |
| diff --git a/sdk/lib/_internal/pub/lib/src/command.dart b/sdk/lib/_internal/pub/lib/src/command.dart |
| index e3dcab5ef072fac8cfb9c72cf00217044dcaeee4..2a4d13e29ab41fd524f9023a214dc04bfb25f836 100644 |
| --- a/sdk/lib/_internal/pub/lib/src/command.dart |
| +++ b/sdk/lib/_internal/pub/lib/src/command.dart |
| @@ -33,7 +33,7 @@ import 'utils.dart'; |
| /// The base class for commands for the pub executable. |
|
nweiz
2014/01/31 21:42:06
Explain how commands vs subcommands work.
Bob Nystrom
2014/02/01 01:49:32
Done.
|
| abstract class PubCommand { |
| /// The commands that pub understands. |
| - static final Map<String, PubCommand> commands = _initCommands(); |
| + static final Map<String, PubCommand> mainCommands = _initCommands(); |
| /// The top-level [ArgParser] used to parse the pub command line. |
| static final pubArgParser = _initArgParser(); |
| @@ -44,35 +44,45 @@ abstract class PubCommand { |
| var buffer = new StringBuffer(); |
| buffer.writeln('Pub is a package manager for Dart.'); |
| buffer.writeln(); |
| - buffer.writeln('Usage: pub command [arguments]'); |
| + buffer.writeln('Usage: pub <command> [arguments]'); |
| buffer.writeln(); |
| buffer.writeln('Global options:'); |
| buffer.writeln(pubArgParser.getUsage()); |
| + listCommands(mainCommands, buffer); |
| buffer.writeln(); |
| + buffer.writeln( |
| + 'Use "pub help [command]" for more information about a command.'); |
| + |
| + log.message(buffer); |
| + } |
| + /// Writes [commands] in a nicely formatted list to [buffer]. |
|
nweiz
2014/01/31 21:42:06
Document [isSubcommand].
Bob Nystrom
2014/02/01 01:49:32
Done.
|
| + static void listCommands(Map<String, PubCommand> commands, |
| + StringBuffer buffer, {bool isSubcommand: false}) { |
|
nweiz
2014/01/31 21:42:06
I really don't like passing in a StringBuffer as o
Bob Nystrom
2014/02/01 01:49:32
Done.
|
| // Show the public commands alphabetically. |
| var names = ordered(commands.keys.where((name) => |
| !commands[name].aliases.contains(name) && |
| !commands[name].hidden)); |
| + // If all of the subcommands are hidden, do nothing. |
| + // TODO(rnystrom): Remove this once cache has a visible command. |
|
nweiz
2014/01/31 21:42:06
I don't think you should remove this even if there
Bob Nystrom
2014/02/01 01:49:32
Yup. It turns out this needs to stay to handle ano
|
| + if (names.isEmpty) return; |
| + |
| var length = names.map((name) => name.length).reduce(math.max); |
| - buffer.writeln('Available commands:'); |
| + buffer.writeln(); |
| + buffer.writeln('Available ${isSubcommand ? "sub" : ""}commands:'); |
| for (var name in names) { |
| buffer.writeln(' ${padRight(name, length)} ' |
| '${commands[name].description}'); |
| } |
| - |
| - buffer.writeln(); |
| - buffer.write( |
| - 'Use "pub help [command]" for more information about a command.'); |
| - log.message(buffer.toString()); |
| } |
| SystemCache cache; |
| /// The parsed options for this command. |
| - ArgResults commandOptions; |
| + ArgResults get commandOptions => _commandOptions; |
| + ArgResults _commandOptions; |
| Entrypoint entrypoint; |
| @@ -102,6 +112,13 @@ abstract class PubCommand { |
| /// The [ArgParser] for this command. |
| final commandParser = new ArgParser(); |
| + /// Subcommands exposed by this command. |
| + /// |
| + /// If empty, then this command has no subcommands. Otherwise, a subcommand |
| + /// must be specified by the user. In that case, this command's [onRun] will |
| + /// not be called and the subcommand's will. |
| + final subcommands = <String, PubCommand>{}; |
| + |
| /// Override this to use offline-only sources instead of hitting the network. |
| /// This will only be called before the [SystemCache] is created. After that, |
| /// it has no effect. |
| @@ -113,13 +130,10 @@ abstract class PubCommand { |
| help: 'Print usage information for this command.'); |
| } |
| - void run(String cacheDir, ArgResults options, List<String> arguments) { |
| - commandOptions = options.command; |
| - |
| - if (commandOptions['help']) { |
| - this.printUsage(); |
| - return; |
| - } |
| + /// Runs this command. |
|
nweiz
2014/01/31 21:42:06
Document the arguments. It's especially unclear wh
Bob Nystrom
2014/02/01 01:49:32
Documented. "global" is a little misleading becaus
|
| + void run(String cacheDir, ArgResults mainOptions, ArgResults commandOptions, |
| + List<String> arguments) { |
| + _commandOptions = commandOptions; |
| cache = new SystemCache.withSources(cacheDir, isOffline: isOffline); |
| @@ -131,7 +145,7 @@ abstract class PubCommand { |
| log.error(getErrorMessage(error)); |
| log.fine("Exception type: ${error.runtimeType}"); |
| - if (options['trace'] || !isUserFacingException(error)) { |
| + if (mainOptions['trace'] || !isUserFacingException(error)) { |
| log.error(chain.terse); |
| } else { |
| log.fine(chain.terse); |
| @@ -143,7 +157,7 @@ abstract class PubCommand { |
| log.fine(message); |
| } |
| - if (options['trace']) { |
| + if (mainOptions['trace']) { |
| log.dumpTranscript(); |
| } else if (!isUserFacingException(error)) { |
| log.error(""" |
| @@ -159,7 +173,10 @@ and include the results in a bug report on http://dartbug.com/new. |
| } |
| var captureStackChains = |
| - options['trace'] || options['verbose'] || options['verbosity'] == 'all'; |
| + mainOptions['trace'] || |
| + mainOptions['verbose'] || |
| + mainOptions['verbosity'] == 'all'; |
| + |
| captureErrors(() { |
| return syncFuture(() { |
| // Make sure there aren't unexpected arguments. |
| @@ -193,7 +210,10 @@ and include the results in a bug report on http://dartbug.com/new. |
| /// Override this to perform the specific command. Return a future that |
| /// completes when the command is done or fails if the command fails. If the |
| /// command is synchronous, it may return `null`. |
|
nweiz
2014/01/31 21:42:06
Mention that this only needs to be overridden for
Bob Nystrom
2014/02/01 01:49:32
Done.
|
| - Future onRun(); |
| + Future onRun() { |
| + // Leaf commands should override this. |
|
nweiz
2014/01/31 21:42:06
For non-leaf commands, this should still never be
Bob Nystrom
2014/02/01 01:49:32
Yup. Changed to assert(false).
|
| + assert(subcommands.isNotEmpty); |
| + } |
| /// Displays usage information for this command. |
| void printUsage([String description]) { |
| @@ -208,6 +228,11 @@ and include the results in a bug report on http://dartbug.com/new. |
| buffer.write(commandUsage); |
| } |
| + if (subcommands.isNotEmpty) { |
| + buffer.writeln(); |
| + listCommands(subcommands, buffer); |
| + } |
| + |
| log.message(buffer.toString()); |
| } |
| @@ -272,9 +297,19 @@ ArgParser _initArgParser() { |
| help: 'Shortcut for "--verbosity=all".'); |
| // Register the commands. |
| - PubCommand.commands.forEach((name, command) { |
| - argParser.addCommand(name, command.commandParser); |
| + PubCommand.mainCommands.forEach((name, command) { |
| + _registerCommand(name, command, argParser); |
| }); |
| return argParser; |
| } |
| + |
| +/// Registers a [command] with [name] on [parser]. |
| +void _registerCommand(String name, PubCommand command, ArgParser parser) { |
| + parser.addCommand(name, command.commandParser); |
| + |
| + // Recursively wire up any subcommands. |
| + command.subcommands.forEach((name, subcommand) { |
| + _registerCommand(name, subcommand, command.commandParser); |
| + }); |
| +} |