| Index: pkg/args/lib/args.dart | 
| diff --git a/pkg/args/lib/args.dart b/pkg/args/lib/args.dart | 
| index 0cb81961a3ec2c1743f09a9138c389eb3e3013c1..29da1e262b840885b6d87c41474eec6aa6f0d418 100644 | 
| --- a/pkg/args/lib/args.dart | 
| +++ b/pkg/args/lib/args.dart | 
| @@ -1,4 +1,4 @@ | 
| -// Copyright (c) 2012, the Dart project authors.  Please see the AUTHORS file | 
| +// Copyright (c) 2013, the Dart project authors.  Please see the AUTHORS file | 
| // for details. All rights reserved. Use of this source code is governed by a | 
| // BSD-style license that can be found in the LICENSE file. | 
|  | 
| @@ -145,7 +145,29 @@ | 
| *     var results = parser.parse(['--mode', 'on', '--mode', 'off']); | 
| *     print(results['mode']); // prints '[on, off]' | 
| * | 
| - * ## Usage ## | 
| + * ## Defining commands ## | 
| + * | 
| + * In addition to *options*, you can also define *commands*. A command is a | 
| + * named argument that has its own set of options. For example, when you run: | 
| + * | 
| + *     $ git commit -a | 
| + * | 
| + * The executable is `git`, the command is `commit`, and the `-a` option is an | 
| + * option passed to the command. You can add a command like so: | 
| + * | 
| + *     var parser = new ArgParser(); | 
| + *     var command = parser.addCommand("commit"); | 
| + *     command.addFlag('all', abbr: 'a'); | 
| + * | 
| + * It returns another [ArgParser] which you can use to define options and | 
| + * subcommands on that command. When an argument list is parsed, you can then | 
| + * determine which command was entered and what options were provided for it. | 
| + * | 
| + *     var results = parser.parse(['commit', '-a']); | 
| + *     print(results.command.name); // "commit" | 
| + *     print(results.command['a']); // true | 
| + * | 
| + * ## Displaying usage ## | 
| * | 
| * This library can also be used to automatically generate nice usage help | 
| * text like you get when you run a program with `--help`. To use this, you | 
| @@ -179,7 +201,7 @@ | 
| * | 
| *           [arm]       ARM Holding 32-bit chip | 
| *           [ia32]      Intel x86 | 
| - * | 
| + * | 
| * To assist the formatting of the usage help, single line help text will | 
| * be followed by a single new line. Options with multi-line help text | 
| * will be followed by two new lines. This provides spatial diversity between | 
| @@ -190,39 +212,42 @@ | 
| */ | 
| library args; | 
|  | 
| -import 'dart:math'; | 
| - | 
| -// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. | 
| -import 'src/utils.dart'; | 
| +import 'src/parser.dart'; | 
| +import 'src/usage.dart'; | 
|  | 
| /** | 
| * A class for taking a list of raw command line arguments and parsing out | 
| * options and flags from them. | 
| */ | 
| class ArgParser { | 
| -  static final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); | 
| -  static final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); | 
| -  static final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); | 
| - | 
| -  final Map<String, _Option> _options; | 
| +  /** | 
| +   * The options that have been defined for this parser. | 
| +   */ | 
| +  final Map<String, Option> options = <String, Option>{}; | 
|  | 
| /** | 
| -   * The names of the options, in the order that they were added. This way we | 
| -   * can generate usage information in the same order. | 
| +   * The commands that have been defined for this parser. | 
| */ | 
| -  // TODO(rnystrom): Use an ordered map type, if one appears. | 
| -  final List<String> _optionNames; | 
| +  final Map<String, ArgParser> commands = <String, ArgParser>{}; | 
|  | 
| -  /** The current argument list being parsed. Set by [parse()]. */ | 
| -  List<String> _args; | 
| +  /** Creates a new ArgParser. */ | 
| +  ArgParser(); | 
|  | 
| -  /** Index of the current argument being parsed in [_args]. */ | 
| -  int _current; | 
| +  /** | 
| +   * Defines a command. A command is a named argument which may in turn | 
| +   * define its own options and subcommands. Returns an [ArgParser] that can | 
| +   * be used to define the command's options. | 
| +   */ | 
| +  ArgParser addCommand(String name) { | 
| +    // Make sure the name isn't in use. | 
| +    if (commands.containsKey(name)) { | 
| +      throw new ArgumentError('Duplicate command "$name".'); | 
| +    } | 
|  | 
| -  /** Creates a new ArgParser. */ | 
| -  ArgParser() | 
| -    : _options = <String, _Option>{}, | 
| -      _optionNames = <String>[]; | 
| +    var command = new ArgParser(); | 
| +    commands[name] = command; | 
| +    return command; | 
| +  } | 
|  | 
| /** | 
| * Defines a flag. Throws an [ArgumentError] if: | 
| @@ -254,7 +279,7 @@ class ArgParser { | 
| void callback(value), {bool isFlag, bool negatable: false, | 
| bool allowMultiple: false}) { | 
| // Make sure the name isn't in use. | 
| -    if (_options.containsKey(name)) { | 
| +    if (options.containsKey(name)) { | 
| throw new ArgumentError('Duplicate option "$name".'); | 
| } | 
|  | 
| @@ -265,289 +290,56 @@ class ArgParser { | 
| 'Abbreviation "$abbr" is longer than one character.'); | 
| } | 
|  | 
| -      var existing = _findByAbbr(abbr); | 
| +      var existing = findByAbbreviation(abbr); | 
| if (existing != null) { | 
| throw new ArgumentError( | 
| 'Abbreviation "$abbr" is already used by "${existing.name}".'); | 
| } | 
| } | 
|  | 
| -    _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, | 
| +    options[name] = new Option(name, abbr, help, allowed, allowedHelp, | 
| defaultsTo, callback, isFlag: isFlag, negatable: negatable, | 
| allowMultiple: allowMultiple); | 
| -    _optionNames.add(name); | 
| } | 
|  | 
| /** | 
| * Parses [args], a list of command-line arguments, matches them against the | 
| * flags and options defined by this parser, and returns the result. | 
| */ | 
| -  ArgResults parse(List<String> args) { | 
| -    _args = args; | 
| -    _current = 0; | 
| -    var results = {}; | 
| - | 
| -    // Initialize flags to their defaults. | 
| -    _options.forEach((name, option) { | 
| -      if (option.allowMultiple) { | 
| -        results[name] = []; | 
| -      } else { | 
| -        results[name] = option.defaultValue; | 
| -      } | 
| -    }); | 
| - | 
| -    // Parse the args. | 
| -    for (_current = 0; _current < args.length; _current++) { | 
| -      var arg = args[_current]; | 
| - | 
| -      if (arg == '--') { | 
| -        // Reached the argument terminator, so stop here. | 
| -        _current++; | 
| -        break; | 
| -      } | 
| - | 
| -      // Try to parse the current argument as an option. Note that the order | 
| -      // here matters. | 
| -      if (_parseSoloOption(results)) continue; | 
| -      if (_parseAbbreviation(results)) continue; | 
| -      if (_parseLongOption(results)) continue; | 
| - | 
| -      // If we got here, the argument doesn't look like an option, so stop. | 
| -      break; | 
| -    } | 
| - | 
| -    // Set unspecified multivalued arguments to their default value, | 
| -    // if any, and invoke the callbacks. | 
| -    for (var name in _optionNames) { | 
| -      var option = _options[name]; | 
| -      if (option.allowMultiple && | 
| -          results[name].length == 0 && | 
| -          option.defaultValue != null) { | 
| -        results[name].add(option.defaultValue); | 
| -      } | 
| -      if (option.callback != null) option.callback(results[name]); | 
| -    } | 
| - | 
| -    // Add in the leftover arguments we didn't parse. | 
| -    return new ArgResults(results, | 
| -        _args.getRange(_current, _args.length - _current)); | 
| -  } | 
| +  ArgResults parse(List<String> args) => | 
| +      new Parser(null, this, args.toList()).parse(); | 
|  | 
| /** | 
| * Generates a string displaying usage information for the defined options. | 
| * This is basically the help text shown on the command line. | 
| */ | 
| -  String getUsage() { | 
| -    return new _Usage(this).generate(); | 
| -  } | 
| - | 
| -  /** | 
| -   * Called during parsing to validate the arguments. Throws a | 
| -   * [FormatException] if [condition] is `false`. | 
| -   */ | 
| -  _validate(bool condition, String message) { | 
| -    if (!condition) throw new FormatException(message); | 
| -  } | 
| - | 
| -  /** Validates and stores [value] as the value for [option]. */ | 
| -  _setOption(Map results, _Option option, value) { | 
| -    // See if it's one of the allowed values. | 
| -    if (option.allowed != null) { | 
| -      _validate(option.allowed.any((allow) => allow == value), | 
| -          '"$value" is not an allowed value for option "${option.name}".'); | 
| -    } | 
| - | 
| -    if (option.allowMultiple) { | 
| -      results[option.name].add(value); | 
| -    } else { | 
| -      results[option.name] = value; | 
| -    } | 
| -  } | 
| - | 
| -  /** | 
| -   * Pulls the value for [option] from the next argument in [_args] (where the | 
| -   * current option is at index [_current]. Validates that there is a valid | 
| -   * value there. | 
| -   */ | 
| -  void _readNextArgAsValue(Map results, _Option option) { | 
| -    _current++; | 
| -    // Take the option argument from the next command line arg. | 
| -    _validate(_current < _args.length, | 
| -        'Missing argument for "${option.name}".'); | 
| - | 
| -    // Make sure it isn't an option itself. | 
| -    _validate(!_ABBR_OPT.hasMatch(_args[_current]) && | 
| -              !_LONG_OPT.hasMatch(_args[_current]), | 
| -        'Missing argument for "${option.name}".'); | 
| - | 
| -    _setOption(results, option, _args[_current]); | 
| -  } | 
| - | 
| -  /** | 
| -   * Tries to parse the current argument as a "solo" option, which is a single | 
| -   * hyphen followed by a single letter. We treat this differently than | 
| -   * collapsed abbreviations (like "-abc") to handle the possible value that | 
| -   * may follow it. | 
| -   */ | 
| -  bool _parseSoloOption(Map results) { | 
| -    var soloOpt = _SOLO_OPT.firstMatch(_args[_current]); | 
| -    if (soloOpt == null) return false; | 
| - | 
| -    var option = _findByAbbr(soloOpt[1]); | 
| -    _validate(option != null, | 
| -        'Could not find an option or flag "-${soloOpt[1]}".'); | 
| - | 
| -    if (option.isFlag) { | 
| -      _setOption(results, option, true); | 
| -    } else { | 
| -      _readNextArgAsValue(results, option); | 
| -    } | 
| - | 
| -    return true; | 
| -  } | 
| - | 
| -  /** | 
| -   * Tries to parse the current argument as a series of collapsed abbreviations | 
| -   * (like "-abc") or a single abbreviation with the value directly attached | 
| -   * to it (like "-mrelease"). | 
| -   */ | 
| -  bool _parseAbbreviation(Map results) { | 
| -    var abbrOpt = _ABBR_OPT.firstMatch(_args[_current]); | 
| -    if (abbrOpt == null) return false; | 
| - | 
| -    // If the first character is the abbreviation for a non-flag option, then | 
| -    // the rest is the value. | 
| -    var c = abbrOpt[1].substring(0, 1); | 
| -    var first = _findByAbbr(c); | 
| -    if (first == null) { | 
| -      _validate(false, 'Could not find an option with short name "-$c".'); | 
| -    } else if (!first.isFlag) { | 
| -      // The first character is a non-flag option, so the rest must be the | 
| -      // value. | 
| -      var value = '${abbrOpt[1].substring(1)}${abbrOpt[2]}'; | 
| -      _setOption(results, first, value); | 
| -    } else { | 
| -      // If we got some non-flag characters, then it must be a value, but | 
| -      // if we got here, it's a flag, which is wrong. | 
| -      _validate(abbrOpt[2] == '', | 
| -        'Option "-$c" is a flag and cannot handle value ' | 
| -        '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".'); | 
| - | 
| -      // Not an option, so all characters should be flags. | 
| -      for (var i = 0; i < abbrOpt[1].length; i++) { | 
| -        var c = abbrOpt[1].substring(i, i + 1); | 
| -        var option = _findByAbbr(c); | 
| -        _validate(option != null, | 
| -            'Could not find an option with short name "-$c".'); | 
| - | 
| -        // In a list of short options, only the first can be a non-flag. If | 
| -        // we get here we've checked that already. | 
| -        _validate(option.isFlag, | 
| -            'Option "-$c" must be a flag to be in a collapsed "-".'); | 
| - | 
| -        _setOption(results, option, true); | 
| -      } | 
| -    } | 
| - | 
| -    return true; | 
| -  } | 
| - | 
| -  /** | 
| -   * Tries to parse the current argument as a long-form named option, which | 
| -   * may include a value like "--mode=release" or "--mode release". | 
| -   */ | 
| -  bool _parseLongOption(Map results) { | 
| -    var longOpt = _LONG_OPT.firstMatch(_args[_current]); | 
| -    if (longOpt == null) return false; | 
| - | 
| -    var name = longOpt[1]; | 
| -    var option = _options[name]; | 
| -    if (option != null) { | 
| -      if (option.isFlag) { | 
| -        _validate(longOpt[3] == null, | 
| -            'Flag option "$name" should not be given a value.'); | 
| - | 
| -        _setOption(results, option, true); | 
| -      } else if (longOpt[3] != null) { | 
| -        // We have a value like --foo=bar. | 
| -        _setOption(results, option, longOpt[3]); | 
| -      } else { | 
| -        // Option like --foo, so look for the value as the next arg. | 
| -        _readNextArgAsValue(results, option); | 
| -      } | 
| -    } else if (name.startsWith('no-')) { | 
| -      // See if it's a negated flag. | 
| -      name = name.substring('no-'.length); | 
| -      option = _options[name]; | 
| -      _validate(option != null, 'Could not find an option named "$name".'); | 
| -      _validate(option.isFlag, 'Cannot negate non-flag option "$name".'); | 
| -      _validate(option.negatable, 'Cannot negate option "$name".'); | 
| - | 
| -      _setOption(results, option, false); | 
| -    } else { | 
| -      _validate(option != null, 'Could not find an option named "$name".'); | 
| -    } | 
| - | 
| -    return true; | 
| -  } | 
| - | 
| -  /** | 
| -   * Finds the option whose abbreviation is [abbr], or `null` if no option has | 
| -   * that abbreviation. | 
| -   */ | 
| -  _Option _findByAbbr(String abbr) { | 
| -    for (var option in _options.values) { | 
| -      if (option.abbreviation == abbr) return option; | 
| -    } | 
| - | 
| -    return null; | 
| -  } | 
| +  String getUsage() => new Usage(this).generate(); | 
|  | 
| /** | 
| * Get the default value for an option. Useful after parsing to test | 
| * if the user specified something other than the default. | 
| */ | 
| getDefault(String option) { | 
| -    if (!_options.containsKey(option)) { | 
| +    if (!options.containsKey(option)) { | 
| throw new ArgumentError('No option named $option'); | 
| } | 
| -    return _options[option].defaultValue; | 
| +    return options[option].defaultValue; | 
| } | 
| -} | 
| - | 
| -/** | 
| - * The results of parsing a series of command line arguments using | 
| - * [ArgParser.parse()]. Includes the parsed options and any remaining unparsed | 
| - * command line arguments. | 
| - */ | 
| -class ArgResults { | 
| -  final Map _options; | 
|  | 
| /** | 
| -   * The remaining command-line arguments that were not parsed as options or | 
| -   * flags. If `--` was used to separate the options from the remaining | 
| -   * arguments, it will not be included in this list. | 
| +   * Finds the option whose abbreviation is [abbr], or `null` if no option has | 
| +   * that abbreviation. | 
| */ | 
| -  final List<String> rest; | 
| - | 
| -  /** Creates a new [ArgResults]. */ | 
| -  ArgResults(this._options, this.rest); | 
| - | 
| -  /** Gets the parsed command-line option named [name]. */ | 
| -  operator [](String name) { | 
| -    if (!_options.containsKey(name)) { | 
| -      throw new ArgumentError( | 
| -          'Could not find an option named "$name".'); | 
| -    } | 
| - | 
| -    return _options[name]; | 
| +  Option findByAbbreviation(String abbr) { | 
| +    return options.values.firstMatching((option) => option.abbreviation == abbr, | 
| +        orElse: () => null); | 
| } | 
| - | 
| -  /** Get the names of the options as a [Collection]. */ | 
| -  Collection<String> get options => _options.keys.toList(); | 
| } | 
|  | 
| -class _Option { | 
| +/** | 
| + * A command-line option. Includes both flags and options which take a value. | 
| + */ | 
| +class Option { | 
| final String name; | 
| final String abbreviation; | 
| final List allowed; | 
| @@ -559,227 +351,52 @@ class _Option { | 
| final bool negatable; | 
| final bool allowMultiple; | 
|  | 
| -  _Option(this.name, this.abbreviation, this.help, this.allowed, | 
| +  Option(this.name, this.abbreviation, this.help, this.allowed, | 
| this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, | 
| this.negatable, this.allowMultiple: false}); | 
| } | 
|  | 
| /** | 
| - * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its | 
| - * defined options. Internally, it works like a tabular printer. The output is | 
| - * divided into three horizontal columns, like so: | 
| - * | 
| - *     -h, --help  Prints the usage information | 
| - *     |  |        |                                 | | 
| - * | 
| - * It builds the usage text up one column at a time and handles padding with | 
| - * spaces and wrapping to the next line to keep the cells correctly lined up. | 
| + * The results of parsing a series of command line arguments using | 
| + * [ArgParser.parse()]. Includes the parsed options and any remaining unparsed | 
| + * command line arguments. | 
| */ | 
| -class _Usage { | 
| -  static const NUM_COLUMNS = 3; // Abbreviation, long name, help. | 
| - | 
| -  /** The parser this is generating usage for. */ | 
| -  final ArgParser args; | 
| - | 
| -  /** The working buffer for the generated usage text. */ | 
| -  StringBuffer buffer; | 
| - | 
| -  /** | 
| -   * The column that the "cursor" is currently on. If the next call to | 
| -   * [write()] is not for this column, it will correctly handle advancing to | 
| -   * the next column (and possibly the next row). | 
| -   */ | 
| -  int currentColumn = 0; | 
| - | 
| -  /** The width in characters of each column. */ | 
| -  List<int> columnWidths; | 
| +class ArgResults { | 
| +  final Map _options; | 
|  | 
| /** | 
| -   * The number of sequential lines of text that have been written to the last | 
| -   * column (which shows help info). We track this so that help text that spans | 
| -   * multiple lines can be padded with a blank line after it for separation. | 
| -   * Meanwhile, sequential options with single-line help will be compacted next | 
| -   * to each other. | 
| +   * If these are the results for parsing a command's options, this will be | 
| +   * the name of the command. For top-level results, this returns `null`. | 
| */ | 
| -  int numHelpLines = 0; | 
| +  final String name; | 
|  | 
| /** | 
| -   * How many newlines need to be rendered before the next bit of text can be | 
| -   * written. We do this lazily so that the last bit of usage doesn't have | 
| -   * dangling newlines. We only write newlines right *before* we write some | 
| -   * real content. | 
| +   * The command that was selected, or `null` if none was. This will contain | 
| +   * the options that were selected for that command. | 
| */ | 
| -  int newlinesNeeded = 0; | 
| - | 
| -  _Usage(this.args); | 
| +  final ArgResults command; | 
|  | 
| /** | 
| -   * Generates a string displaying usage information for the defined options. | 
| -   * This is basically the help text shown on the command line. | 
| +   * The remaining command-line arguments that were not parsed as options or | 
| +   * flags. If `--` was used to separate the options from the remaining | 
| +   * arguments, it will not be included in this list. | 
| */ | 
| -  String generate() { | 
| -    buffer = new StringBuffer(); | 
| - | 
| -    calculateColumnWidths(); | 
| - | 
| -    for (var name in args._optionNames) { | 
| -      var option = args._options[name]; | 
| -      write(0, getAbbreviation(option)); | 
| -      write(1, getLongOption(option)); | 
| - | 
| -      if (option.help != null) write(2, option.help); | 
| - | 
| -      if (option.allowedHelp != null) { | 
| -        var allowedNames = option.allowedHelp.keys.toList(); | 
| -        allowedNames.sort(); | 
| -        newline(); | 
| -        for (var name in allowedNames) { | 
| -          write(1, getAllowedTitle(name)); | 
| -          write(2, option.allowedHelp[name]); | 
| -        } | 
| -        newline(); | 
| -      } else if (option.allowed != null) { | 
| -        write(2, buildAllowedList(option)); | 
| -      } else if (option.defaultValue != null) { | 
| -        if (option.isFlag && option.defaultValue == true) { | 
| -          write(2, '(defaults to on)'); | 
| -        } else if (!option.isFlag) { | 
| -          write(2, '(defaults to "${option.defaultValue}")'); | 
| -        } | 
| -      } | 
| - | 
| -      // If any given option displays more than one line of text on the right | 
| -      // column (i.e. help, default value, allowed options, etc.) then put a | 
| -      // blank line after it. This gives space where it's useful while still | 
| -      // keeping simple one-line options clumped together. | 
| -      if (numHelpLines > 1) newline(); | 
| -    } | 
| - | 
| -    return buffer.toString(); | 
| -  } | 
| - | 
| -  String getAbbreviation(_Option option) { | 
| -    if (option.abbreviation != null) { | 
| -      return '-${option.abbreviation}, '; | 
| -    } else { | 
| -      return ''; | 
| -    } | 
| -  } | 
| - | 
| -  String getLongOption(_Option option) { | 
| -    if (option.negatable) { | 
| -      return '--[no-]${option.name}'; | 
| -    } else { | 
| -      return '--${option.name}'; | 
| -    } | 
| -  } | 
| - | 
| -  String getAllowedTitle(String allowed) { | 
| -    return '      [$allowed]'; | 
| -  } | 
| - | 
| -  void calculateColumnWidths() { | 
| -    int abbr = 0; | 
| -    int title = 0; | 
| -    for (var name in args._optionNames) { | 
| -      var option = args._options[name]; | 
| - | 
| -      // Make room in the first column if there are abbreviations. | 
| -      abbr = max(abbr, getAbbreviation(option).length); | 
| - | 
| -      // Make room for the option. | 
| -      title = max(title, getLongOption(option).length); | 
| - | 
| -      // Make room for the allowed help. | 
| -      if (option.allowedHelp != null) { | 
| -        for (var allowed in option.allowedHelp.keys) { | 
| -          title = max(title, getAllowedTitle(allowed).length); | 
| -        } | 
| -      } | 
| -    } | 
| - | 
| -    // Leave a gutter between the columns. | 
| -    title += 4; | 
| -    columnWidths = [abbr, title]; | 
| -  } | 
| - | 
| -  newline() { | 
| -    newlinesNeeded++; | 
| -    currentColumn = 0; | 
| -    numHelpLines = 0; | 
| -  } | 
| - | 
| -  write(int column, String text) { | 
| -    var lines = text.split('\n'); | 
| - | 
| -    // Strip leading and trailing empty lines. | 
| -    while (lines.length > 0 && lines[0].trim() == '') { | 
| -      lines.removeRange(0, 1); | 
| -    } | 
| - | 
| -    while (lines.length > 0 && lines[lines.length - 1].trim() == '') { | 
| -      lines.removeLast(); | 
| -    } | 
| - | 
| -    for (var line in lines) { | 
| -      writeLine(column, line); | 
| -    } | 
| -  } | 
| - | 
| -  writeLine(int column, String text) { | 
| -    // Write any pending newlines. | 
| -    while (newlinesNeeded > 0) { | 
| -      buffer.add('\n'); | 
| -      newlinesNeeded--; | 
| -    } | 
| +  final List<String> rest; | 
|  | 
| -    // Advance until we are at the right column (which may mean wrapping around | 
| -    // to the next line. | 
| -    while (currentColumn != column) { | 
| -      if (currentColumn < NUM_COLUMNS - 1) { | 
| -        buffer.add(padRight('', columnWidths[currentColumn])); | 
| -      } else { | 
| -        buffer.add('\n'); | 
| -      } | 
| -      currentColumn = (currentColumn + 1) % NUM_COLUMNS; | 
| -    } | 
| +  /** Creates a new [ArgResults]. */ | 
| +  ArgResults(this._options, this.name, this.command, this.rest); | 
|  | 
| -    if (column < columnWidths.length) { | 
| -      // Fixed-size column, so pad it. | 
| -      buffer.add(padRight(text, columnWidths[column])); | 
| -    } else { | 
| -      // The last column, so just write it. | 
| -      buffer.add(text); | 
| +  /** Gets the parsed command-line option named [name]. */ | 
| +  operator [](String name) { | 
| +    if (!_options.containsKey(name)) { | 
| +      throw new ArgumentError( | 
| +          'Could not find an option named "$name".'); | 
| } | 
|  | 
| -    // Advance to the next column. | 
| -    currentColumn = (currentColumn + 1) % NUM_COLUMNS; | 
| - | 
| -    // If we reached the last column, we need to wrap to the next line. | 
| -    if (column == NUM_COLUMNS - 1) newlinesNeeded++; | 
| - | 
| -    // Keep track of how many consecutive lines we've written in the last | 
| -    // column. | 
| -    if (column == NUM_COLUMNS - 1) { | 
| -      numHelpLines++; | 
| -    } else { | 
| -      numHelpLines = 0; | 
| -    } | 
| +    return _options[name]; | 
| } | 
|  | 
| -  buildAllowedList(_Option option) { | 
| -    var allowedBuffer = new StringBuffer(); | 
| -    allowedBuffer.add('['); | 
| -    bool first = true; | 
| -    for (var allowed in option.allowed) { | 
| -      if (!first) allowedBuffer.add(', '); | 
| -      allowedBuffer.add(allowed); | 
| -      if (allowed == option.defaultValue) { | 
| -        allowedBuffer.add(' (default)'); | 
| -      } | 
| -      first = false; | 
| -    } | 
| -    allowedBuffer.add(']'); | 
| -    return allowedBuffer.toString(); | 
| -  } | 
| +  /** Get the names of the options as a [Collection]. */ | 
| +  Collection<String> get options => _options.keys.toList(); | 
| } | 
| + | 
|  |