From b1841d59c6326d84401383afde2834035d40ce3f Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Fri, 17 Aug 2012 20:15:29 +0000 Subject: [PATCH 001/263] Fix syntax error in args.dart, reenable args tests. Review URL: https://chromiumcodereview.appspot.com//10837315 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@10920 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/args.dart | 772 ++++++++++++++++++++++++++++++++++ pkgs/args/example.dart | 131 ++++++ pkgs/args/test/args_test.dart | 666 +++++++++++++++++++++++++++++ pkgs/args/utils.dart | 19 + 4 files changed, 1588 insertions(+) create mode 100644 pkgs/args/args.dart create mode 100644 pkgs/args/example.dart create mode 100644 pkgs/args/test/args_test.dart create mode 100644 pkgs/args/utils.dart diff --git a/pkgs/args/args.dart b/pkgs/args/args.dart new file mode 100644 index 00000000..f49c3bc9 --- /dev/null +++ b/pkgs/args/args.dart @@ -0,0 +1,772 @@ +// Copyright (c) 2012, 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. + +/** + * This library lets you define parsers for parsing raw command-line arguments + * into a set of options and values using [GNU][] and [POSIX][] style options. + * + * ## Defining options ## + * + * To use this library, you create an [ArgParser] object which will contain + * the set of options you support: + * + * var parser = new ArgParser(); + * + * Then you define a set of options on that parser using [addOption()] and + * [addFlag()]. The minimal way to create an option is: + * + * parser.addOption('name'); + * + * This creates an option named "name". Options must be given a value on the + * command line. If you have a simple on/off flag, you can instead use: + * + * parser.addFlag('name'); + * + * (From here on out "option" will refer to both "regular" options and flags. + * In cases where the distinction matters, we'll use "non-flag option".) + * + * Options may have an optional single-character abbreviation: + * + * parser.addOption('mode', abbr: 'm'); + * parser.addFlag('verbose', abbr: 'v'); + * + * They may also specify a default value. The default value will be used if the + * option isn't provided: + * + * parser.addOption('mode', defaultsTo: 'debug'); + * parser.addFlag('verbose', defaultsTo: false); + * + * The default value for non-flag options can be any [String]. For flags, it + * must be a [bool]. + * + * To validate non-flag options, you may provide an allowed set of values. When + * you do, it will throw a [FormatException] when you parse the arguments if + * the value for an option is not in the allowed set: + * + * parser.addOption('mode', allowed: ['debug', 'release']); + * + * You can provide a callback when you define an option. When you later parse + * a set of arguments, the callback for that option will be invoked with the + * value provided for it: + * + * parser.addOption('mode', callback: (mode) => print('Got mode $mode)); + * parser.addFlag('verbose', callback: (verbose) { + * if (verbose) print('Verbose'); + * }); + * + * The callback for each option will *always* be called when you parse a set of + * arguments. If the option isn't provided in the args, the callback will be + * passed the default value, or `null` if there is none set. + * + * ## Parsing arguments ## + * + * Once you have an [ArgParser] set up with some options and flags, you use it + * by calling [ArgParser.parse()] with a set of arguments: + * + * var results = parser.parse(['some', 'command', 'line', 'args']); + * + * These will usually come from `new Options().arguments`, but you can pass in + * any list of strings. It returns an instance of [ArgResults]. This is a + * map-like object that will return the value of any parsed option. + * + * var parser = new ArgParser(); + * parser.addOption('mode'); + * parser.addFlag('verbose', defaultsTo: true); + * var results = parser.parse('['--mode', 'debug', 'something', 'else']); + * + * print(results['mode']); // debug + * print(results['verbose']); // true + * + * The [parse()] method will stop as soon as it reaches `--` or anything that + * it doesn't recognize as an option, flag, or option value. If there are still + * arguments left, they will be provided to you in + * [ArgResults.rest]. + * + * print(results.rest); // ['something', 'else'] + * + * ## Specifying options ## + * + * To actually pass in options and flags on the command line, use GNU or POSIX + * style. If you define an option like: + * + * parser.addOption('name', abbr: 'n'); + * + * Then a value for it can be specified on the command line using any of: + * + * --name=somevalue + * --name somevalue + * -nsomevalue + * -n somevalue + * + * Given this flag: + * + * parser.addFlag('name', abbr: 'n'); + * + * You can set it on using one of: + * + * --name + * -n + * + * Or set it off using: + * + * --no-name + * + * Multiple flag abbreviation can also be collapsed into a single argument. If + * you define: + * + * parser.addFlag('verbose', abbr: 'v'); + * parser.addFlag('french', abbr: 'f'); + * parser.addFlag('iambic-pentameter', abbr: 'i'); + * + * Then all three flags could be set using: + * + * -vfi + * + * By default, an option has only a single value, with later option values + * overriding earlier ones; for example: + * + * var parser = new ArgParser(); + * parser.addOption('mode'); + * var results = parser.parse(['--mode', 'on', '--mode', 'off']); + * print(results['mode']); // prints 'off' + * + * If you need multiple values, set the [allowMultiple] flag. In that + * case the option can occur multiple times and when parsing arguments a + * List of values will be returned: + * + * var parser = new ArgParser(); + * parser.addOption('mode', allowMultiple: true); + * var results = parser.parse(['--mode', 'on', '--mode', 'off']); + * print(results['mode']); // prints '[on, off]' + * + * ## 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 + * will also want to provide some help text when you create your options. To + * define help text for the entire option, do: + * + * parser.addOption('mode', help: 'The compiler configuration', + * allowed: ['debug', 'release']); + * parser.addFlag('verbose', help: 'Show additional diagnostic info'); + * + * For non-flag options, you can also provide detailed help for each expected + * value using a map: + * + * parser.addOption('arch', help: 'The architecture to compile for', + * allowedHelp: { + * 'ia32': 'Intel x86', + * 'arm': 'ARM Holding 32-bit chip' + * }); + * + * If you define a set of options like the above, then calling this: + * + * print(parser.getUsage()); + * + * Will display something like: + * + * --mode The compiler configuration + * [debug, release] + * + * --[no-]verbose Show additional diagnostic info + * --arch The architecture to compile for + * + * [arm] ARM Holding 32-bit chip + * [ia32] Intel x86 + * + * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 + * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces + */ +#library('args'); + +#import('utils.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 = const RegExp(@'^-([a-zA-Z0-9])$'); + static final _ABBR_OPT = const RegExp(@'^-([a-zA-Z0-9]+)(.*)$'); + static final _LONG_OPT = const RegExp(@'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + + final Map _options; + + /** + * The names of the options, in the order that they were added. This way we + * can generate usage information in the same order. + */ + // TODO(rnystrom): Use an ordered map type, if one appears. + final List _optionNames; + + /** The current argument list being parsed. Set by [parse()]. */ + List _args; + + /** Index of the current argument being parsed in [_args]. */ + int _current; + + /** Creates a new ArgParser. */ + ArgParser() + : _options = {}, + _optionNames = []; + + /** + * Defines a flag. Throws an [IllegalArgumentException] if: + * + * * There is already an option named [name]. + * * There is already an option using abbreviation [abbr]. + */ + void addFlag(String name, [String abbr, String help, bool defaultsTo = false, + bool negatable = true, void callback(bool value)]) { + _addOption(name, abbr, help, null, null, defaultsTo, callback, + isFlag: true, negatable: negatable); + } + + /** + * Defines a value-taking option. Throws an [IllegalArgumentException] if: + * + * * There is already an option with name [name]. + * * There is already an option using abbreviation [abbr]. + */ + void addOption(String name, [String abbr, String help, List allowed, + Map allowedHelp, String defaultsTo, + void callback(bool value), bool allowMultiple = false]) { + _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, + callback, isFlag: false, allowMultiple: allowMultiple); + } + + void _addOption(String name, String abbr, String help, List allowed, + Map allowedHelp, defaultsTo, + void callback(bool value), [bool isFlag, bool negatable = false, + bool allowMultiple = false]) { + // Make sure the name isn't in use. + if (_options.containsKey(name)) { + throw new IllegalArgumentException('Duplicate option "$name".'); + } + + // Make sure the abbreviation isn't too long or in use. + if (abbr != null) { + if (abbr.length > 1) { + throw new IllegalArgumentException( + 'Abbreviation "$abbr" is longer than one character.'); + } + + var existing = _findByAbbr(abbr); + if (existing != null) { + throw new IllegalArgumentException( + 'Abbreviation "$abbr" is already used by "${existing.name}".'); + } + } + + _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 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)); + } + + /** + * 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.some((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.getValues()) { + if (option.abbreviation == abbr) return option; + } + + return null; + } + + /** + * 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)) { + throw new IllegalArgumentException('No option named $option'); + } + 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. + */ + final List 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 IllegalArgumentException( + 'Could not find an option named "$name".'); + } + + return _options[name]; + } + + /** Get the names of the options as a [Collection]. */ + Collection get options() => _options.getKeys(); +} + +class _Option { + final String name; + final String abbreviation; + final List allowed; + final defaultValue; + final Function callback; + final String help; + final Map allowedHelp; + final bool isFlag; + final bool negatable; + final bool allowMultiple; + + _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. + */ +class _Usage { + static final 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 columnWidths; + + /** + * 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. + */ + int numHelpLines = 0; + + /** + * 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. + */ + int newlinesNeeded = 0; + + _Usage(this.args); + + /** + * Generates a string displaying usage information for the defined options. + * This is basically the help text shown on the command line. + */ + 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.getKeys(); + allowedNames.sort((a, b) => a.compareTo(b)); + 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 = Math.max(abbr, getAbbreviation(option).length); + + // Make room for the option. + title = Math.max(title, getLongOption(option).length); + + // Make room for the allowed help. + if (option.allowedHelp != null) { + for (var allowed in option.allowedHelp.getKeys()) { + title = Math.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--; + } + + // 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; + } + + 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); + } + + // 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; + } + } + + 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(); + } +} diff --git a/pkgs/args/example.dart b/pkgs/args/example.dart new file mode 100644 index 00000000..58b9fb73 --- /dev/null +++ b/pkgs/args/example.dart @@ -0,0 +1,131 @@ +// Copyright (c) 2012, 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. + +/** + * This is an example of converting the args in test.dart to use this API. + * It shows what it looks like to build an [ArgParser] and then, when the code + * is run, demonstrates what the generated usage text looks like. + */ +#library('example'); + +#import('dart:io'); + +#import('args.dart'); + +main() { + var parser = new ArgParser(); + + parser.addOption('mode', abbr: 'm', defaultsTo: 'debug', + help: 'Mode in which to run the tests', + allowed: ['all', 'debug', 'release']); + + parser.addOption('compiler', abbr: 'c', defaultsTo: 'none', + help: 'Specify any compilation step (if needed).', + allowed: ['none', 'dart2js', 'dartc', 'frog'], + allowedHelp: { + 'none': 'Do not compile the Dart code (run native Dart code on the' + ' VM).\n(only valid with the following runtimes: vm, drt)', + 'dart2js': 'Compile dart code to JavaScript by running dart2js (leg).\n' + '(only valid with the following runtimes: same as frog)', + 'dartc': 'Perform static analysis on Dart code by running dartc.\n' + '(only valid with the following runtimes: none)', + 'frog': '(DEPRECATED) Compile dart code to JavaScript by running the\n' + 'frog compiler. (only valid with the following runtimes: d8,\n' + 'drt, chrome, safari, ie, firefox, opera, none (compile only))' + }); + + parser.addOption('runtime', abbr: 'r', defaultsTo: 'vm', + help: 'Where the tests should be run.', + allowed: ['vm', 'd8', 'drt', 'dartium', 'ff', 'firefox', 'chrome', + 'safari', 'ie', 'opera', 'none'], + allowedHelp: { + 'vm': 'Run Dart code on the standalone dart vm.', + 'd8': 'Run JavaScript from the command line using v8.', + 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' + 'DumpRenderTree.', + 'dartium': 'Run Dart or JavaScript in Dartium.', + 'ff': 'Run JavaScript in Firefox', + 'chrome': 'Run JavaScript in Chrome', + 'safari': 'Run JavaScript in Safari', + 'ie': 'Run JavaScript in Internet Explorer', + 'opera': 'Run JavaScript in Opera', + 'none': 'No runtime, compile only (for example, used for dartc static\n' + 'analysis tests).', + }); + + parser.addOption('arch', abbr: 'a', defaultsTo: 'ia32', + help: 'The architecture to run tests for', + allowed: ['all', 'ia32', 'x64', 'simarm']); + + parser.addOption('system', abbr: 's', defaultsTo: Platform.operatingSystem, + help: 'The operating system to run tests on', + allowed: ['linux', 'macos', 'windows']); + + parser.addFlag('checked', defaultsTo: false, + help: 'Run tests in checked mode'); + + parser.addFlag('host-checked', defaultsTo: false, + help: 'Run compiler in checked mode'); + + parser.addOption('timeout', abbr: 't', + help: 'Timeout in seconds'); + + parser.addOption('progress', abbr: 'p', defaultsTo: 'compact', + help: 'Progress indication mode', + allowed: ['compact', 'color', 'line', 'verbose', 'silent', 'status', + 'buildbot']); + + parser.addFlag('report', defaultsTo: false, + help: 'Print a summary report of the number of tests, by expectation'); + + parser.addOption('tasks', abbr: 'j', + defaultsTo: Platform.numberOfProcessors.toString(), + help: 'The number of parallel tasks to run'); + + parser.addOption('shards', defaultsTo: '1', + help: 'The number of instances that the tests will be sharded over'); + + parser.addOption('shard', defaultsTo: '1', + help: 'The index of this instance when running in sharded mode'); + + parser.addFlag('verbose', abbr: 'v', defaultsTo: false, + help: 'Verbose output'); + + parser.addFlag('list', defaultsTo: false, + help: 'List tests only, do not run them'); + + parser.addFlag('keep-generated-tests', defaultsTo: false, + help: 'Keep the generated files in the temporary directory'); + + parser.addFlag('valgrind', defaultsTo: false, + help: 'Run tests through valgrind'); + + parser.addOption('special-command', + help: """ +Special command support. Wraps the command line in +a special command. The special command should contain +an '@' character which will be replaced by the normal +command. + +For example if the normal command that will be executed +is 'dart file.dart' and you specify special command +'python -u valgrind.py @ suffix' the final command will be +'python -u valgrind.py dart file.dart suffix'"""); + + parser.addFlag('time', + help: 'Print timing information after running tests', + defaultsTo: false); + + parser.addOption('frog', help: 'Path to frog script or executable'); + parser.addOption('dart', help: 'Path to dart executable'); + parser.addOption('drt', help: 'Path to DumpRenderTree executable'); + parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); + parser.addOption('froglib', help: 'Path to frog library'); + + parser.addFlag('batch', abbr: 'b', + help: 'Run browser tests in batch mode', + defaultsTo: true); + + print(parser.getUsage()); +} diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart new file mode 100644 index 00000000..98bd799a --- /dev/null +++ b/pkgs/args/test/args_test.dart @@ -0,0 +1,666 @@ +// Copyright (c) 2012, 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. + +#library('args_test'); + +#import('../../unittest/unittest.dart'); +#import('../args.dart'); + +main() { + group('ArgParser.addFlag()', () { + test('throws IllegalArgumentException if the flag already exists', () { + var parser = new ArgParser(); + parser.addFlag('foo'); + throwsIllegalArg(() => parser.addFlag('foo')); + }); + + test('throws IllegalArgumentException if the option already exists', () { + var parser = new ArgParser(); + parser.addOption('foo'); + throwsIllegalArg(() => parser.addFlag('foo')); + }); + + test('throws IllegalArgumentException if the abbreviation exists', () { + var parser = new ArgParser(); + parser.addFlag('foo', abbr: 'f'); + throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'f')); + }); + + test('throws IllegalArgumentException if the abbreviation is longer ' + 'than one character', () { + var parser = new ArgParser(); + throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); + }); + }); + + group('ArgParser.addOption()', () { + test('throws IllegalArgumentException if the flag already exists', () { + var parser = new ArgParser(); + parser.addFlag('foo'); + throwsIllegalArg(() => parser.addOption('foo')); + }); + + test('throws IllegalArgumentException if the option already exists', () { + var parser = new ArgParser(); + parser.addOption('foo'); + throwsIllegalArg(() => parser.addOption('foo')); + }); + + test('throws IllegalArgumentException if the abbreviation exists', () { + var parser = new ArgParser(); + parser.addFlag('foo', abbr: 'f'); + throwsIllegalArg(() => parser.addOption('flummox', abbr: 'f')); + }); + + test('throws IllegalArgumentException if the abbreviation is longer ' + 'than one character', () { + var parser = new ArgParser(); + throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); + }); + }); + + group('ArgParser.parse()', () { + group('flags', () { + test('are true if present', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse(['--verbose']); + expect(args['verbose'], isTrue); + }); + + test('default if missing', () { + var parser = new ArgParser(); + parser.addFlag('a', defaultsTo: true); + parser.addFlag('b', defaultsTo: false); + + var args = parser.parse([]); + expect(args['a'], isTrue); + expect(args['b'], isFalse); + }); + + test('are false if missing with no default', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse([]); + expect(args['verbose'], isFalse); + }); + + test('throws if given a value', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + throwsFormat(parser, ['--verbose=true']); + }); + }); + + group('flags negated with "no-"', () { + test('set the flag to false', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse(['--no-verbose']); + expect(args['verbose'], isFalse); + }); + + test('set the flag to true if the flag actually starts with "no-"', () { + var parser = new ArgParser(); + parser.addFlag('no-body'); + + var args = parser.parse(['--no-body']); + expect(args['no-body'], isTrue); + }); + + test('are not preferred over a colliding one without', () { + var parser = new ArgParser(); + parser.addFlag('no-strum'); + parser.addFlag('strum'); + + var args = parser.parse(['--no-strum']); + expect(args['no-strum'], isTrue); + expect(args['strum'], isFalse); + }); + + test('fail for non-negatable flags', () { + var parser = new ArgParser(); + parser.addFlag('strum', negatable: false); + + throwsFormat(parser, ['--no-strum']); + }); + }); + + group('callbacks', () { + test('for present flags are invoked with the value', () { + var a; + var parser = new ArgParser(); + parser.addFlag('a', callback: (value) => a = value); + + var args = parser.parse(['--a']); + expect(a, isTrue); + }); + + test('for absent flags are invoked with the default value', () { + var a; + var parser = new ArgParser(); + parser.addFlag('a', defaultsTo: false, + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isFalse); + }); + + test('are invoked even if the flag is not present', () { + var a = 'not called'; + var parser = new ArgParser(); + parser.addFlag('a', callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isFalse); + }); + + test('for present options are invoked with the value', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', callback: (value) => a = value); + + var args = parser.parse(['--a=v']); + expect(a, equals('v')); + }); + + test('for absent options are invoked with the default value', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', defaultsTo: 'v', + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, equals('v')); + }); + + test('are invoked even if the option is not present', () { + var a = 'not called'; + var parser = new ArgParser(); + parser.addOption('a', callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isNull); + }); + }); + + group('abbreviations', () { + test('are parsed with a preceding "-"', () { + var parser = new ArgParser(); + parser.addFlag('arg', abbr: 'a'); + + var args = parser.parse(['-a']); + expect(args['arg'], isTrue); + }); + + test('can use multiple after a single "-"', () { + var parser = new ArgParser(); + parser.addFlag('first', abbr: 'f'); + parser.addFlag('second', abbr: 's'); + parser.addFlag('third', abbr: 't'); + + var args = parser.parse(['-tf']); + expect(args['first'], isTrue); + expect(args['second'], isFalse); + expect(args['third'], isTrue); + }); + + test('can have multiple "-" args', () { + var parser = new ArgParser(); + parser.addFlag('first', abbr: 'f'); + parser.addFlag('second', abbr: 's'); + parser.addFlag('third', abbr: 't'); + + var args = parser.parse(['-s', '-tf']); + expect(args['first'], isTrue); + expect(args['second'], isTrue); + expect(args['third'], isTrue); + }); + + test('can take arguments without a space separating', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + var args = parser.parse(['-flip']); + expect(args['file'], equals('lip')); + }); + + test('can take arguments with a space separating', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + var args = parser.parse(['-f', 'name']); + expect(args['file'], equals('name')); + }); + + test('allow non-option characters in the value', () { + var parser = new ArgParser(); + parser.addOption('apple', abbr: 'a'); + + var args = parser.parse(['-ab?!c']); + expect(args['apple'], equals('b?!c')); + }); + + test('throw if unknown', () { + var parser = new ArgParser(); + throwsFormat(parser, ['-f']); + }); + + test('throw if the value is missing', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + throwsFormat(parser, ['-f']); + }); + + test('throw if the value looks like an option', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + parser.addOption('other'); + + throwsFormat(parser, ['-f', '--other']); + throwsFormat(parser, ['-f', '--unknown']); + throwsFormat(parser, ['-f', '-abbr']); + }); + + test('throw if the value is not allowed', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']); + + throwsFormat(parser, ['-mprofile']); + }); + + test('throw if any but the first is not a flag', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + parser.addOption('banana', abbr: 'b'); // Takes an argument. + parser.addFlag('cherry', abbr: 'c'); + + throwsFormat(parser, ['-abc']); + }); + + test('throw if it has a value but the option is a flag', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + parser.addFlag('banana', abbr: 'b'); + + // The '?!' means this can only be understood as '--apple b?!c'. + throwsFormat(parser, ['-ab?!c']); + }); + }); + + group('options', () { + test('are parsed if present', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse(['--mode=release']); + expect(args['mode'], equals('release')); + }); + + test('are null if not present', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse([]); + expect(args['mode'], isNull); + }); + + test('default if missing', () { + var parser = new ArgParser(); + parser.addOption('mode', defaultsTo: 'debug'); + var args = parser.parse([]); + expect(args['mode'], equals('debug')); + }); + + test('allow the value to be separated by whitespace', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse(['--mode', 'release']); + expect(args['mode'], equals('release')); + }); + + test('throw if unknown', () { + var parser = new ArgParser(); + throwsFormat(parser, ['--unknown']); + throwsFormat(parser, ['--nobody']); // Starts with "no". + }); + + test('throw if the arg does not include a value', () { + var parser = new ArgParser(); + parser.addOption('mode'); + throwsFormat(parser, ['--mode']); + }); + + test('throw if the value looks like an option', () { + var parser = new ArgParser(); + parser.addOption('mode'); + parser.addOption('other'); + + throwsFormat(parser, ['--mode', '--other']); + throwsFormat(parser, ['--mode', '--unknown']); + throwsFormat(parser, ['--mode', '-abbr']); + }); + + test('do not throw if the value is in the allowed set', () { + var parser = new ArgParser(); + parser.addOption('mode', allowed: ['debug', 'release']); + var args = parser.parse(['--mode=debug']); + expect(args['mode'], equals('debug')); + }); + + test('throw if the value is not in the allowed set', () { + var parser = new ArgParser(); + parser.addOption('mode', allowed: ['debug', 'release']); + throwsFormat(parser, ['--mode=profile']); + }); + + test('returns last provided value', () { + var parser = new ArgParser(); + parser.addOption('define'); + var args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals('2')); + }); + + test('returns a List if multi-valued', () { + var parser = new ArgParser(); + parser.addOption('define', allowMultiple: true); + var args = parser.parse(['--define=1']); + expect(args['define'], equals(['1'])); + args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals(['1','2'])); + }); + + test('returns the default value for multi-valued arguments ' + 'if not explicitly set', () { + var parser = new ArgParser(); + parser.addOption('define', defaultsTo: '0', allowMultiple: true); + var args = parser.parse(['']); + expect(args['define'], equals(['0'])); + }); + }); + + group('query default values', () { + test('queries the default value', () { + var parser = new ArgParser(); + parser.addOption('define', defaultsTo: '0'); + expect(()=>parser.getDefault('undefine'), + throwsIllegalArgumentException); + }); + + test('queries the default value for an unknown option', () { + var parser = new ArgParser(); + parser.addOption('define', defaultsTo: '0'); + expect(()=>parser.getDefault('undefine'), + throwsIllegalArgumentException); + }); + }); + + group('gets the option names from an ArgsResult', () { + test('queries the set options', () { + var parser = new ArgParser(); + parser.addFlag('woof', defaultsTo: false); + parser.addOption('meow', defaultsTo: 'kitty'); + var args = parser.parse([]); + expect(args.options, hasLength(2)); + expect(args.options.some((o) => o == 'woof'), isTrue); + expect(args.options.some((o) => o == 'meow'), isTrue); + }); + }); + + group('remaining args', () { + test('stops parsing args when a non-option-like arg is encountered', () { + var parser = new ArgParser(); + parser.addFlag('woof'); + parser.addOption('meow'); + parser.addOption('tweet', defaultsTo: 'bird'); + + var results = parser.parse(['--woof', '--meow', 'v', 'not', 'option']); + expect(results['woof'], isTrue); + expect(results['meow'], equals('v')); + expect(results['tweet'], equals('bird')); + expect(results.rest, orderedEquals(['not', 'option'])); + }); + + test('stops parsing at "--"', () { + var parser = new ArgParser(); + parser.addFlag('woof', defaultsTo: false); + parser.addOption('meow', defaultsTo: 'kitty'); + + var results = parser.parse(['--woof', '--', '--meow']); + expect(results['woof'], isTrue); + expect(results['meow'], equals('kitty')); + expect(results.rest, orderedEquals(['--meow'])); + }); + + test('handles options with case-sensitivity', () { + var parser = new ArgParser(); + parser.addFlag('recurse', defaultsTo: false, abbr:'R'); + var results = parser.parse(['-R']); + expect(results['recurse'], isTrue); + expect(results.rest, [ ]); + throwsFormat(parser, ['-r']); + }); + }); + }); + + group('ArgParser.getUsage()', () { + test('negatable flags show "no-" in title', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'The mode'); + + validateUsage(parser, + ''' + --[no-]mode The mode + '''); + }); + + test('non-negatable flags don\'t show "no-" in title', () { + var parser = new ArgParser(); + parser.addFlag('mode', negatable: false, help: 'The mode'); + + validateUsage(parser, + ''' + --mode The mode + '''); + }); + + test('if there are no abbreviations, there is no column for them', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'The mode'); + + validateUsage(parser, + ''' + --[no-]mode The mode + '''); + }); + + test('options are lined up past abbreviations', () { + var parser = new ArgParser(); + parser.addFlag('mode', abbr: 'm', help: 'The mode'); + parser.addOption('long', help: 'Lacks an abbreviation'); + + validateUsage(parser, + ''' + -m, --[no-]mode The mode + --long Lacks an abbreviation + '''); + }); + + test('help text is lined up past the longest option', () { + var parser = new ArgParser(); + parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); + parser.addOption('a-really-long-name', help: 'Its help text'); + + validateUsage(parser, + ''' + -m, --[no-]mode Lined up with below + --a-really-long-name Its help text + '''); + }); + + test('leading empty lines are ignored in help text', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); + + validateUsage(parser, + ''' + --[no-]mode After newlines + '''); + }); + + test('trailing empty lines are ignored in help text', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); + + validateUsage(parser, + ''' + --[no-]mode Before newlines + '''); + }); + + test('options are documented in the order they were added', () { + var parser = new ArgParser(); + parser.addFlag('zebra', help: 'First'); + parser.addFlag('monkey', help: 'Second'); + parser.addFlag('wombat', help: 'Third'); + + validateUsage(parser, + ''' + --[no-]zebra First + --[no-]monkey Second + --[no-]wombat Third + '''); + }); + + test('the default value for a flag is shown if on', () { + var parser = new ArgParser(); + parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); + parser.addFlag('negate', help: 'Should be off', defaultsTo: false); + + validateUsage(parser, + ''' + --[no-]affirm Should be on + (defaults to on) + + --[no-]negate Should be off + '''); + }); + + test('the default value for an option with no allowed list is shown', () { + var parser = new ArgParser(); + parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); + + validateUsage(parser, + ''' + --any Can be anything + (defaults to "whatevs") + '''); + }); + + test('the allowed list is shown', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', + allowed: ['spades', 'clubs', 'hearts', 'diamonds']); + + validateUsage(parser, + ''' + --suit Like in cards + [spades, clubs, hearts, diamonds] + '''); + }); + + test('the default is highlighted in the allowed list', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + allowed: ['spades', 'clubs', 'hearts', 'diamonds']); + + validateUsage(parser, + ''' + --suit Like in cards + [spades, clubs (default), hearts, diamonds] + '''); + }); + + test('the allowed help is shown', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + allowed: ['spades', 'clubs', 'diamonds', 'hearts'], + allowedHelp: { + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage(parser, + ''' + --suit Like in cards + + [clubs] Weapons of war + [diamonds] Money for this art + [hearts] The shape of my heart + [spades] Swords of a soldier + '''); + }); + }); + + group('ArgResults[]', () { + test('throws if the name is not an option', () { + var parser = new ArgParser(); + var results = parser.parse([]); + throwsIllegalArg(() => results['unknown']); + }); + }); +} + +throwsIllegalArg(function) { + expect(function, throwsIllegalArgumentException); +} + +throwsFormat(ArgParser parser, List args) { + expect(() => parser.parse(args), throwsFormatException); +} + +validateUsage(ArgParser parser, String expected) { + expected = unindentString(expected); + expect(parser.getUsage(), equals(expected)); +} + +// TODO(rnystrom): Replace one in test_utils. +String unindentString(String text) { + var lines = text.split('\n'); + + // Count the indentation of the last line. + var whitespace = const RegExp('^ *'); + var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; + + // Drop the last line. It only exists for specifying indentation. + lines.removeLast(); + + // Strip indentation from the remaining lines. + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.length <= indent) { + // It's short, so it must be nothing but whitespace. + if (line.trim() != '') { + throw new IllegalArgumentException( + 'Line "$line" does not have enough indentation.'); + } + + lines[i] = ''; + } else { + if (line.substring(0, indent).trim() != '') { + throw new IllegalArgumentException( + 'Line "$line" does not have enough indentation.'); + } + + lines[i] = line.substring(indent); + } + } + + return Strings.join(lines, '\n'); +} diff --git a/pkgs/args/utils.dart b/pkgs/args/utils.dart new file mode 100644 index 00000000..029ada30 --- /dev/null +++ b/pkgs/args/utils.dart @@ -0,0 +1,19 @@ +// Copyright (c) 2012, 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. + +// TODO(rnystrom): This file was copied from pub. +/** Generic utility functions. Stuff that should possibly be in core. */ +#library('args_utils'); + +/** Pads [source] to [length] by adding spaces at the end. */ +String padRight(String source, int length) { + final result = new StringBuffer(); + result.add(source); + + while (result.length < length) { + result.add(' '); + } + + return result.toString(); +} \ No newline at end of file From 53c2a44d25a42038ba364e1f15dd304fa05a9780 Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Fri, 17 Aug 2012 22:16:15 +0000 Subject: [PATCH 002/263] Add pubspec files to args and dartdoc . Review URL: https://chromiumcodereview.appspot.com//10823399 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@10928 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 9 +++++++++ 1 file changed, 9 insertions(+) create mode 100644 pkgs/args/pubspec.yaml diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml new file mode 100644 index 00000000..6dbe61d4 --- /dev/null +++ b/pkgs/args/pubspec.yaml @@ -0,0 +1,9 @@ +name: args +description: > + Libraries for defining parsers for parsing raw command-line arguments into + a set of options and values using GNU and POSIX style options. + + +dependencies: + unittest: + sdk: unittest From 9776ba703bbd64211eb485a34865a53b4914ebc2 Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Tue, 21 Aug 2012 21:09:52 +0000 Subject: [PATCH 003/263] Continued deletion of frog. Review URL: https://chromiumcodereview.appspot.com//10860071 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@11098 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example.dart | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/pkgs/args/example.dart b/pkgs/args/example.dart index 58b9fb73..3a2dd083 100644 --- a/pkgs/args/example.dart +++ b/pkgs/args/example.dart @@ -22,17 +22,15 @@ main() { parser.addOption('compiler', abbr: 'c', defaultsTo: 'none', help: 'Specify any compilation step (if needed).', - allowed: ['none', 'dart2js', 'dartc', 'frog'], + allowed: ['none', 'dart2js', 'dartc'], allowedHelp: { 'none': 'Do not compile the Dart code (run native Dart code on the' ' VM).\n(only valid with the following runtimes: vm, drt)', - 'dart2js': 'Compile dart code to JavaScript by running dart2js (leg).\n' - '(only valid with the following runtimes: same as frog)', + 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' + '(only valid with the following runtimes: d8, drt, chrome\n' + 'safari, ie, firefox, opera, none (compile only))' 'dartc': 'Perform static analysis on Dart code by running dartc.\n' '(only valid with the following runtimes: none)', - 'frog': '(DEPRECATED) Compile dart code to JavaScript by running the\n' - 'frog compiler. (only valid with the following runtimes: d8,\n' - 'drt, chrome, safari, ie, firefox, opera, none (compile only))' }); parser.addOption('runtime', abbr: 'r', defaultsTo: 'vm', @@ -117,11 +115,9 @@ is 'dart file.dart' and you specify special command help: 'Print timing information after running tests', defaultsTo: false); - parser.addOption('frog', help: 'Path to frog script or executable'); parser.addOption('dart', help: 'Path to dart executable'); parser.addOption('drt', help: 'Path to DumpRenderTree executable'); parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); - parser.addOption('froglib', help: 'Path to frog library'); parser.addFlag('batch', abbr: 'b', help: 'Run browser tests in batch mode', From d2c8d78e32ca57b90460757985d017ae870d8c15 Mon Sep 17 00:00:00 2001 From: "ager@google.com" Date: Wed, 22 Aug 2012 14:33:01 +0000 Subject: [PATCH 004/263] Deprecate Math object in corelib in favor of dart:math library. Review URL: https://chromiumcodereview.appspot.com//10829459 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@11155 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/args.dart | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/args/args.dart b/pkgs/args/args.dart index f49c3bc9..5268f509 100644 --- a/pkgs/args/args.dart +++ b/pkgs/args/args.dart @@ -180,6 +180,8 @@ */ #library('args'); +#import('dart:math'); + #import('utils.dart'); /** @@ -672,15 +674,15 @@ class _Usage { var option = args._options[name]; // Make room in the first column if there are abbreviations. - abbr = Math.max(abbr, getAbbreviation(option).length); + abbr = max(abbr, getAbbreviation(option).length); // Make room for the option. - title = Math.max(title, getLongOption(option).length); + title = max(title, getLongOption(option).length); // Make room for the allowed help. if (option.allowedHelp != null) { for (var allowed in option.allowedHelp.getKeys()) { - title = Math.max(title, getAllowedTitle(allowed).length); + title = max(title, getAllowedTitle(allowed).length); } } } From b9faf00cff8ec5bfcdaa264c99a335f666ce890a Mon Sep 17 00:00:00 2001 From: "bak@google.com" Date: Wed, 29 Aug 2012 10:18:52 +0000 Subject: [PATCH 005/263] Removed syntax error from example.dart. Review URL: https://chromiumcodereview.appspot.com//10896034 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@11506 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/example.dart b/pkgs/args/example.dart index 3a2dd083..5cba8f10 100644 --- a/pkgs/args/example.dart +++ b/pkgs/args/example.dart @@ -28,7 +28,7 @@ main() { ' VM).\n(only valid with the following runtimes: vm, drt)', 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' '(only valid with the following runtimes: d8, drt, chrome\n' - 'safari, ie, firefox, opera, none (compile only))' + 'safari, ie, firefox, opera, none (compile only))', 'dartc': 'Perform static analysis on Dart code by running dartc.\n' '(only valid with the following runtimes: none)', }); From 00b7c6179624d45bc0e930795dbcf3e4f9195ef5 Mon Sep 17 00:00:00 2001 From: "iposva@google.com" Date: Fri, 31 Aug 2012 17:47:17 +0000 Subject: [PATCH 006/263] - Change "static final" to "static const" in the pkg/, language/, utils/ and lib/core/ directories. Review URL: https://chromiumcodereview.appspot.com//10919024 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@11703 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/args.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/args.dart b/pkgs/args/args.dart index 5268f509..acc557ae 100644 --- a/pkgs/args/args.dart +++ b/pkgs/args/args.dart @@ -189,9 +189,9 @@ * options and flags from them. */ class ArgParser { - static final _SOLO_OPT = const RegExp(@'^-([a-zA-Z0-9])$'); - static final _ABBR_OPT = const RegExp(@'^-([a-zA-Z0-9]+)(.*)$'); - static final _LONG_OPT = const RegExp(@'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + static const _SOLO_OPT = const RegExp(@'^-([a-zA-Z0-9])$'); + static const _ABBR_OPT = const RegExp(@'^-([a-zA-Z0-9]+)(.*)$'); + static const _LONG_OPT = const RegExp(@'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); final Map _options; @@ -565,7 +565,7 @@ class _Option { * spaces and wrapping to the next line to keep the cells correctly lined up. */ class _Usage { - static final NUM_COLUMNS = 3; // Abbreviation, long name, help. + static const NUM_COLUMNS = 3; // Abbreviation, long name, help. /** The parser this is generating usage for. */ final ArgParser args; From 3ce6c4ebbed7b65b1c2bcd410e21c606d2dedd94 Mon Sep 17 00:00:00 2001 From: "kasperl@google.com" Date: Fri, 7 Sep 2012 11:50:07 +0000 Subject: [PATCH 007/263] Get rid of a lot of () for getters. R=bak@google.com BUG= Review URL: https://chromiumcodereview.appspot.com//10919146 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12017 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/args.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/args.dart b/pkgs/args/args.dart index acc557ae..152fcc67 100644 --- a/pkgs/args/args.dart +++ b/pkgs/args/args.dart @@ -533,7 +533,7 @@ class ArgResults { } /** Get the names of the options as a [Collection]. */ - Collection get options() => _options.getKeys(); + Collection get options => _options.getKeys(); } class _Option { From 23594e7d59c63428b67d9050fde2ce51dbe8bdd6 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 12 Sep 2012 19:26:49 +0000 Subject: [PATCH 008/263] Take external patch from https://chromiumcodereview.appspot.com/10918056/. Review URL: https://codereview.chromium.org//10933052 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12278 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/args.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/args/args.dart b/pkgs/args/args.dart index 152fcc67..69c7d282 100644 --- a/pkgs/args/args.dart +++ b/pkgs/args/args.dart @@ -23,6 +23,11 @@ * * parser.addFlag('name'); * + * Flag options will, by default, accept a 'no-' prefix to negate the option. + * This can be disabled like so: + * + * parser.addFlag('name', negatable: false); + * * (From here on out "option" will refer to both "regular" options and flags. * In cases where the distinction matters, we'll use "non-flag option".) * From 40e3c513c2f407e8eba821c0ad601015873e400f Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 12 Sep 2012 21:31:42 +0000 Subject: [PATCH 009/263] Make args package follow new layout. Review URL: https://codereview.chromium.org//10919249 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12284 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/{example.dart => example/test_runner.dart} | 2 +- pkgs/args/{ => lib}/args.dart | 3 ++- pkgs/args/{ => lib/src}/utils.dart | 0 pkgs/args/test/args_test.dart | 4 +++- 4 files changed, 6 insertions(+), 3 deletions(-) rename pkgs/args/{example.dart => example/test_runner.dart} (99%) rename pkgs/args/{ => lib}/args.dart (99%) rename pkgs/args/{ => lib/src}/utils.dart (100%) diff --git a/pkgs/args/example.dart b/pkgs/args/example/test_runner.dart similarity index 99% rename from pkgs/args/example.dart rename to pkgs/args/example/test_runner.dart index 5cba8f10..f91e3855 100644 --- a/pkgs/args/example.dart +++ b/pkgs/args/example/test_runner.dart @@ -11,7 +11,7 @@ #import('dart:io'); -#import('args.dart'); +#import('package:args/args.dart'); main() { var parser = new ArgParser(); diff --git a/pkgs/args/args.dart b/pkgs/args/lib/args.dart similarity index 99% rename from pkgs/args/args.dart rename to pkgs/args/lib/args.dart index 69c7d282..b49bc1a3 100644 --- a/pkgs/args/args.dart +++ b/pkgs/args/lib/args.dart @@ -187,7 +187,8 @@ #import('dart:math'); -#import('utils.dart'); +// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. +#import('src/utils.dart'); /** * A class for taking a list of raw command line arguments and parsing out diff --git a/pkgs/args/utils.dart b/pkgs/args/lib/src/utils.dart similarity index 100% rename from pkgs/args/utils.dart rename to pkgs/args/lib/src/utils.dart diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 98bd799a..e1a4d437 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -5,7 +5,9 @@ #library('args_test'); #import('../../unittest/unittest.dart'); -#import('../args.dart'); + +// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. +#import('../lib/args.dart'); main() { group('ArgParser.addFlag()', () { From f655c0936f714d3f2da0512d122949a93faf47c0 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 14 Sep 2012 18:22:24 +0000 Subject: [PATCH 010/263] Make unittest follow the new package layout. Review URL: https://codereview.chromium.org//10918240 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12396 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index e1a4d437..cb300ab2 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,9 +4,8 @@ #library('args_test'); -#import('../../unittest/unittest.dart'); - -// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. +// TODO(rnystrom): Use "package:" URL (#4968). +#import('../../unittest/lib/unittest.dart'); #import('../lib/args.dart'); main() { From cb5264516d1ba10a47a44eb90c74cadaca8e8020 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 14 Sep 2012 19:43:10 +0000 Subject: [PATCH 011/263] Revert "Make unittest follow the new package layout." This reverts commit 38f7d16f373832a7bf889192af7845cb602aec01. Review URL: https://codereview.chromium.org//10911318 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12400 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index cb300ab2..e1a4d437 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,8 +4,9 @@ #library('args_test'); -// TODO(rnystrom): Use "package:" URL (#4968). -#import('../../unittest/lib/unittest.dart'); +#import('../../unittest/unittest.dart'); + +// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. #import('../lib/args.dart'); main() { From 05838189c908be25cbd87270170fc25d48b036d2 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 14 Sep 2012 21:12:17 +0000 Subject: [PATCH 012/263] Update unittest to new package layout. Review URL: https://codereview.chromium.org//10917275 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12404 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index e1a4d437..cb300ab2 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,9 +4,8 @@ #library('args_test'); -#import('../../unittest/unittest.dart'); - -// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. +// TODO(rnystrom): Use "package:" URL (#4968). +#import('../../unittest/lib/unittest.dart'); #import('../lib/args.dart'); main() { From d004efd4cb1a9aad69643d2969b26e4008db0503 Mon Sep 17 00:00:00 2001 From: "efortuna@google.com" Date: Sat, 15 Sep 2012 01:21:41 +0000 Subject: [PATCH 013/263] revert 1204. Will investigate when I get a chance. TBR=rnystrom Review URL: https://codereview.chromium.org//10928216 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12413 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index cb300ab2..e1a4d437 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,8 +4,9 @@ #library('args_test'); -// TODO(rnystrom): Use "package:" URL (#4968). -#import('../../unittest/lib/unittest.dart'); +#import('../../unittest/unittest.dart'); + +// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. #import('../lib/args.dart'); main() { From fc90287ea3c057bc7ca0fa12a9e28691aefada13 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 19 Sep 2012 21:17:49 +0000 Subject: [PATCH 014/263] Commit patch: https://codereview.chromium.org/10914320/ Thanks, Matt! Review URL: https://codereview.chromium.org//10947044 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12603 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- pkgs/args/test/args_test.dart | 44 +++++++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 2 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index b49bc1a3..dd77d376 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -239,14 +239,14 @@ class ArgParser { */ void addOption(String name, [String abbr, String help, List allowed, Map allowedHelp, String defaultsTo, - void callback(bool value), bool allowMultiple = false]) { + void callback(value), bool allowMultiple = false]) { _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, callback, isFlag: false, allowMultiple: allowMultiple); } void _addOption(String name, String abbr, String help, List allowed, Map allowedHelp, defaultsTo, - void callback(bool value), [bool isFlag, bool negatable = false, + void callback(value), [bool isFlag, bool negatable = false, bool allowMultiple = false]) { // Make sure the name isn't in use. if (_options.containsKey(name)) { diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index e1a4d437..1de7877a 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -189,6 +189,50 @@ main() { var args = parser.parse([]); expect(a, isNull); }); + + test('for multiple present, allowMultiple, options are invoked with ' + 'value as a list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse(['--a=v', '--a=x']); + expect(a, equals(['v', 'x'])); + }); + + test('for single present, allowMultiple, options are invoked with ' + ' value as a single element list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse(['--a=v']); + expect(a, equals(['v'])); + }); + + test('for absent, allowMultiple, options are invoked with default ' + 'value as a list.', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, defaultsTo: 'v', + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, equals(['v'])); + }); + + test('for absent, allowMultiple, options are invoked with value ' + 'as an empty list.', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isEmpty); + }); }); group('abbreviations', () { From efe55652b9bc0eead08d31bc053b75134008ce18 Mon Sep 17 00:00:00 2001 From: "danrubel@google.com" Date: Sat, 22 Sep 2012 19:41:42 +0000 Subject: [PATCH 015/263] migrate raw string to new syntax in pkg Review URL: https://codereview.chromium.org//10961061 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12754 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index dd77d376..7c51a087 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -195,9 +195,9 @@ * options and flags from them. */ class ArgParser { - static const _SOLO_OPT = const RegExp(@'^-([a-zA-Z0-9])$'); - static const _ABBR_OPT = const RegExp(@'^-([a-zA-Z0-9]+)(.*)$'); - static const _LONG_OPT = const RegExp(@'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + static const _SOLO_OPT = const RegExp(r'^-([a-zA-Z0-9])$'); + static const _ABBR_OPT = const RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); + static const _LONG_OPT = const RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); final Map _options; From a9fde3e04d95ed1b7cdff460addddc7bab896942 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Tue, 25 Sep 2012 12:27:52 +0000 Subject: [PATCH 016/263] Change IllegalArgumentException to ArgumentError. Review URL: https://codereview.chromium.org//10989013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12841 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 14 +++++++------- pkgs/args/test/args_test.dart | 26 +++++++++++++------------- 2 files changed, 20 insertions(+), 20 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 7c51a087..52e7350d 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -220,7 +220,7 @@ class ArgParser { _optionNames = []; /** - * Defines a flag. Throws an [IllegalArgumentException] if: + * Defines a flag. Throws an [ArgumentError] if: * * * There is already an option named [name]. * * There is already an option using abbreviation [abbr]. @@ -232,7 +232,7 @@ class ArgParser { } /** - * Defines a value-taking option. Throws an [IllegalArgumentException] if: + * Defines a value-taking option. Throws an [ArgumentError] if: * * * There is already an option with name [name]. * * There is already an option using abbreviation [abbr]. @@ -250,19 +250,19 @@ class ArgParser { bool allowMultiple = false]) { // Make sure the name isn't in use. if (_options.containsKey(name)) { - throw new IllegalArgumentException('Duplicate option "$name".'); + throw new ArgumentError('Duplicate option "$name".'); } // Make sure the abbreviation isn't too long or in use. if (abbr != null) { if (abbr.length > 1) { - throw new IllegalArgumentException( + throw new ArgumentError( 'Abbreviation "$abbr" is longer than one character.'); } var existing = _findByAbbr(abbr); if (existing != null) { - throw new IllegalArgumentException( + throw new ArgumentError( 'Abbreviation "$abbr" is already used by "${existing.name}".'); } } @@ -504,7 +504,7 @@ class ArgParser { */ getDefault(String option) { if (!_options.containsKey(option)) { - throw new IllegalArgumentException('No option named $option'); + throw new ArgumentError('No option named $option'); } return _options[option].defaultValue; } @@ -531,7 +531,7 @@ class ArgResults { /** Gets the parsed command-line option named [name]. */ operator [](String name) { if (!_options.containsKey(name)) { - throw new IllegalArgumentException( + throw new ArgumentError( 'Could not find an option named "$name".'); } diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 1de7877a..d3b219bf 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -11,25 +11,25 @@ main() { group('ArgParser.addFlag()', () { - test('throws IllegalArgumentException if the flag already exists', () { + test('throws ArgumentError if the flag already exists', () { var parser = new ArgParser(); parser.addFlag('foo'); throwsIllegalArg(() => parser.addFlag('foo')); }); - test('throws IllegalArgumentException if the option already exists', () { + test('throws ArgumentError if the option already exists', () { var parser = new ArgParser(); parser.addOption('foo'); throwsIllegalArg(() => parser.addFlag('foo')); }); - test('throws IllegalArgumentException if the abbreviation exists', () { + test('throws ArgumentError if the abbreviation exists', () { var parser = new ArgParser(); parser.addFlag('foo', abbr: 'f'); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'f')); }); - test('throws IllegalArgumentException if the abbreviation is longer ' + test('throws ArgumentError if the abbreviation is longer ' 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); @@ -37,25 +37,25 @@ main() { }); group('ArgParser.addOption()', () { - test('throws IllegalArgumentException if the flag already exists', () { + test('throws ArgumentError if the flag already exists', () { var parser = new ArgParser(); parser.addFlag('foo'); throwsIllegalArg(() => parser.addOption('foo')); }); - test('throws IllegalArgumentException if the option already exists', () { + test('throws ArgumentError if the option already exists', () { var parser = new ArgParser(); parser.addOption('foo'); throwsIllegalArg(() => parser.addOption('foo')); }); - test('throws IllegalArgumentException if the abbreviation exists', () { + test('throws ArgumentError if the abbreviation exists', () { var parser = new ArgParser(); parser.addFlag('foo', abbr: 'f'); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'f')); }); - test('throws IllegalArgumentException if the abbreviation is longer ' + test('throws ArgumentError if the abbreviation is longer ' 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); @@ -434,14 +434,14 @@ main() { var parser = new ArgParser(); parser.addOption('define', defaultsTo: '0'); expect(()=>parser.getDefault('undefine'), - throwsIllegalArgumentException); + throwsArgumentError); }); test('queries the default value for an unknown option', () { var parser = new ArgParser(); parser.addOption('define', defaultsTo: '0'); expect(()=>parser.getDefault('undefine'), - throwsIllegalArgumentException); + throwsArgumentError); }); }); @@ -664,7 +664,7 @@ main() { } throwsIllegalArg(function) { - expect(function, throwsIllegalArgumentException); + expect(function, throwsArgumentError); } throwsFormat(ArgParser parser, List args) { @@ -693,14 +693,14 @@ String unindentString(String text) { if (line.length <= indent) { // It's short, so it must be nothing but whitespace. if (line.trim() != '') { - throw new IllegalArgumentException( + throw new ArgumentError( 'Line "$line" does not have enough indentation.'); } lines[i] = ''; } else { if (line.substring(0, indent).trim() != '') { - throw new IllegalArgumentException( + throw new ArgumentError( 'Line "$line" does not have enough indentation.'); } From 5335d4c0847c215d83d9f6273362533f0a2f0eda Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 25 Sep 2012 23:40:24 +0000 Subject: [PATCH 017/263] Apply patch from https://codereview.chromium.org/10967042/. Review URL: https://codereview.chromium.org//10985035 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@12885 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 52e7350d..c2db6e3b 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -179,6 +179,11 @@ * * [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 + * options. * * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces From 13800feef11605d16a3fdf4884a7d9d78fdd6f91 Mon Sep 17 00:00:00 2001 From: "regis@google.com" Date: Tue, 16 Oct 2012 16:03:47 +0000 Subject: [PATCH 018/263] Change core lib, dart2js, and more for new optional parameters syntax and semantics (new semantics is not enforced yet). Review URL: https://codereview.chromium.org//11090016 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@13684 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index c2db6e3b..7ad7280b 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -230,8 +230,8 @@ class ArgParser { * * There is already an option named [name]. * * There is already an option using abbreviation [abbr]. */ - void addFlag(String name, [String abbr, String help, bool defaultsTo = false, - bool negatable = true, void callback(bool value)]) { + void addFlag(String name, {String abbr, String help, bool defaultsTo: false, + bool negatable: true, void callback(bool value)}) { _addOption(name, abbr, help, null, null, defaultsTo, callback, isFlag: true, negatable: negatable); } @@ -251,8 +251,8 @@ class ArgParser { void _addOption(String name, String abbr, String help, List allowed, Map allowedHelp, defaultsTo, - void callback(value), [bool isFlag, bool negatable = false, - bool allowMultiple = false]) { + void callback(value), {bool isFlag, bool negatable: false, + bool allowMultiple: false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { throw new ArgumentError('Duplicate option "$name".'); @@ -560,8 +560,8 @@ class _Option { final bool allowMultiple; _Option(this.name, this.abbreviation, this.help, this.allowed, - this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, - this.negatable, this.allowMultiple = false]); + this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, + this.negatable, this.allowMultiple: false}); } /** From e204704509983818a09ccf291a6cb1e2e3c26fab Mon Sep 17 00:00:00 2001 From: "regis@google.com" Date: Wed, 17 Oct 2012 20:58:19 +0000 Subject: [PATCH 019/263] Second round of cleanups for new optional parameter semantics. Review URL: https://codereview.chromium.org//11194025 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@13759 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 7ad7280b..f97c1bdd 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -242,9 +242,9 @@ class ArgParser { * * There is already an option with name [name]. * * There is already an option using abbreviation [abbr]. */ - void addOption(String name, [String abbr, String help, List allowed, + void addOption(String name, {String abbr, String help, List allowed, Map allowedHelp, String defaultsTo, - void callback(value), bool allowMultiple = false]) { + void callback(value), bool allowMultiple: false}) { _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, callback, isFlag: false, allowMultiple: allowMultiple); } From 476441d7c19ac11fc46a06ced560ec0d8a75bae9 Mon Sep 17 00:00:00 2001 From: "scheglov@google.com" Date: Tue, 23 Oct 2012 13:20:50 +0000 Subject: [PATCH 020/263] Parts must start with 'part of' git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@13961 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example/test_runner.dart | 6 +++--- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/lib/src/utils.dart | 2 +- pkgs/args/test/args_test.dart | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index f91e3855..d0a6ed8e 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -7,11 +7,11 @@ * It shows what it looks like to build an [ArgParser] and then, when the code * is run, demonstrates what the generated usage text looks like. */ -#library('example'); +library example; -#import('dart:io'); +import 'dart:io'; -#import('package:args/args.dart'); +import 'package:args/args.dart'; main() { var parser = new ArgParser(); diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index f97c1bdd..2f2cd578 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -188,12 +188,12 @@ * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces */ -#library('args'); +library args; -#import('dart:math'); +import 'dart:math'; // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -#import('src/utils.dart'); +import 'src/utils.dart'; /** * A class for taking a list of raw command line arguments and parsing out diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index 029ada30..c6997c85 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -4,7 +4,7 @@ // TODO(rnystrom): This file was copied from pub. /** Generic utility functions. Stuff that should possibly be in core. */ -#library('args_utils'); +library args_utils; /** Pads [source] to [length] by adding spaces at the end. */ String padRight(String source, int length) { diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index d3b219bf..e5341653 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -2,12 +2,12 @@ // 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. -#library('args_test'); +library args_test; -#import('../../unittest/unittest.dart'); +import '../../unittest/unittest.dart'; // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -#import('../lib/args.dart'); +import '../lib/args.dart'; main() { group('ArgParser.addFlag()', () { From 6f021b01991d3a0741f3aae096913d0acff4c18a Mon Sep 17 00:00:00 2001 From: "ahe@google.com" Date: Tue, 23 Oct 2012 14:24:04 +0000 Subject: [PATCH 021/263] Revert "Parts must start with 'part of'" and "Attempt to fix VM build" This reverts r13961 and r13963. Review URL: https://codereview.chromium.org//11233061 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@13967 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example/test_runner.dart | 6 +++--- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/lib/src/utils.dart | 2 +- pkgs/args/test/args_test.dart | 6 +++--- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index d0a6ed8e..f91e3855 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -7,11 +7,11 @@ * It shows what it looks like to build an [ArgParser] and then, when the code * is run, demonstrates what the generated usage text looks like. */ -library example; +#library('example'); -import 'dart:io'; +#import('dart:io'); -import 'package:args/args.dart'; +#import('package:args/args.dart'); main() { var parser = new ArgParser(); diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 2f2cd578..f97c1bdd 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -188,12 +188,12 @@ * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces */ -library args; +#library('args'); -import 'dart:math'; +#import('dart:math'); // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -import 'src/utils.dart'; +#import('src/utils.dart'); /** * A class for taking a list of raw command line arguments and parsing out diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index c6997c85..029ada30 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -4,7 +4,7 @@ // TODO(rnystrom): This file was copied from pub. /** Generic utility functions. Stuff that should possibly be in core. */ -library args_utils; +#library('args_utils'); /** Pads [source] to [length] by adding spaces at the end. */ String padRight(String source, int length) { diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index e5341653..d3b219bf 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -2,12 +2,12 @@ // 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. -library args_test; +#library('args_test'); -import '../../unittest/unittest.dart'; +#import('../../unittest/unittest.dart'); // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -import '../lib/args.dart'; +#import('../lib/args.dart'); main() { group('ArgParser.addFlag()', () { From d82162d4d7962e644ec14706759455562a5af78d Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Thu, 25 Oct 2012 19:22:11 +0000 Subject: [PATCH 022/263] Make getKeys, getValues getters (keys, values). Review URL: https://codereview.chromium.org//11267018 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14093 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index f97c1bdd..a74c981c 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -496,7 +496,7 @@ class ArgParser { * that abbreviation. */ _Option _findByAbbr(String abbr) { - for (var option in _options.getValues()) { + for (var option in _options.values) { if (option.abbreviation == abbr) return option; } @@ -544,7 +544,7 @@ class ArgResults { } /** Get the names of the options as a [Collection]. */ - Collection get options => _options.getKeys(); + Collection get options => _options.keys; } class _Option { @@ -630,7 +630,7 @@ class _Usage { if (option.help != null) write(2, option.help); if (option.allowedHelp != null) { - var allowedNames = option.allowedHelp.getKeys(); + var allowedNames = option.allowedHelp.keys; allowedNames.sort((a, b) => a.compareTo(b)); newline(); for (var name in allowedNames) { @@ -692,7 +692,7 @@ class _Usage { // Make room for the allowed help. if (option.allowedHelp != null) { - for (var allowed in option.allowedHelp.getKeys()) { + for (var allowed in option.allowedHelp.keys) { title = max(title, getAllowedTitle(allowed).length); } } From 60b150f72bd002b1cd93f2e55cfac7fe3a826f2a Mon Sep 17 00:00:00 2001 From: "gram@google.com" Date: Thu, 1 Nov 2012 23:09:47 +0000 Subject: [PATCH 023/263] Restructure pkg/unittest and pkg/webdriver to follow the pub conventions. This means all imports of unittest in our test code had to change to include 'lib' in the path. While doing that change I changed the library/imports to the new syntax in the affected files. Review URL: https://codereview.chromium.org//11301046 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14443 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index d3b219bf..61112969 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -2,12 +2,12 @@ // 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. -#library('args_test'); +library args_test; -#import('../../unittest/unittest.dart'); +import '../../../pkg/unittest/lib/unittest.dart'; // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -#import('../lib/args.dart'); +import '../lib/args.dart'; main() { group('ArgParser.addFlag()', () { From 1cdacbc18bd632808b4f5190eb023161a07e7bb0 Mon Sep 17 00:00:00 2001 From: "gram@google.com" Date: Wed, 7 Nov 2012 18:52:40 +0000 Subject: [PATCH 024/263] Update library syntax in pkg files. Review URL: https://codereview.chromium.org//11362139 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14646 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example/test_runner.dart | 6 +++--- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/lib/src/utils.dart | 2 +- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index f91e3855..d0a6ed8e 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -7,11 +7,11 @@ * It shows what it looks like to build an [ArgParser] and then, when the code * is run, demonstrates what the generated usage text looks like. */ -#library('example'); +library example; -#import('dart:io'); +import 'dart:io'; -#import('package:args/args.dart'); +import 'package:args/args.dart'; main() { var parser = new ArgParser(); diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index a74c981c..ad0d9071 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -188,12 +188,12 @@ * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces */ -#library('args'); +library args; -#import('dart:math'); +import 'dart:math'; // TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -#import('src/utils.dart'); +import 'src/utils.dart'; /** * A class for taking a list of raw command line arguments and parsing out diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index 029ada30..c6997c85 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -4,7 +4,7 @@ // TODO(rnystrom): This file was copied from pub. /** Generic utility functions. Stuff that should possibly be in core. */ -#library('args_utils'); +library args_utils; /** Pads [source] to [length] by adding spaces at the end. */ String padRight(String source, int length) { From 3ad4591f69358dc58e930fd47a95811c13a44f91 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Tue, 13 Nov 2012 06:48:43 +0000 Subject: [PATCH 025/263] Make RegExp's constructor non-const. BUG= Review URL: https://codereview.chromium.org//11410033 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14829 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/test/args_test.dart | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index ad0d9071..12ea7454 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -200,9 +200,9 @@ import 'src/utils.dart'; * options and flags from them. */ class ArgParser { - static const _SOLO_OPT = const RegExp(r'^-([a-zA-Z0-9])$'); - static const _ABBR_OPT = const RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); - static const _LONG_OPT = const RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + 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 _options; diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 61112969..10c68fe0 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -681,7 +681,7 @@ String unindentString(String text) { var lines = text.split('\n'); // Count the indentation of the last line. - var whitespace = const RegExp('^ *'); + var whitespace = new RegExp('^ *'); var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; // Drop the last line. It only exists for specifying indentation. From e8205fab8ca4ea0ac6a5740a5905e48ff9fb2ed6 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Tue, 13 Nov 2012 07:28:13 +0000 Subject: [PATCH 026/263] "Reverting 14829-14832" BUG= Review URL: https://codereview.chromium.org//11312203 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14833 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/test/args_test.dart | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 12ea7454..ad0d9071 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -200,9 +200,9 @@ import 'src/utils.dart'; * 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]+)(=(.*))?$'); + static const _SOLO_OPT = const RegExp(r'^-([a-zA-Z0-9])$'); + static const _ABBR_OPT = const RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); + static const _LONG_OPT = const RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); final Map _options; diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 10c68fe0..61112969 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -681,7 +681,7 @@ String unindentString(String text) { var lines = text.split('\n'); // Count the indentation of the last line. - var whitespace = new RegExp('^ *'); + var whitespace = const RegExp('^ *'); var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; // Drop the last line. It only exists for specifying indentation. From f3efcabd2f8c93389d3173363d5a47f5eccd8380 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Tue, 13 Nov 2012 10:18:06 +0000 Subject: [PATCH 027/263] Make RegExp constructor non-const. Another take of https://codereview.chromium.org/11410033/ with status changes applied for co19 tests. BUG= Review URL: https://codereview.chromium.org//11369210 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@14837 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 6 +++--- pkgs/args/test/args_test.dart | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index ad0d9071..12ea7454 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -200,9 +200,9 @@ import 'src/utils.dart'; * options and flags from them. */ class ArgParser { - static const _SOLO_OPT = const RegExp(r'^-([a-zA-Z0-9])$'); - static const _ABBR_OPT = const RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); - static const _LONG_OPT = const RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + 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 _options; diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 61112969..10c68fe0 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -681,7 +681,7 @@ String unindentString(String text) { var lines = text.split('\n'); // Count the indentation of the last line. - var whitespace = const RegExp('^ *'); + var whitespace = new RegExp('^ *'); var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; // Drop the last line. It only exists for specifying indentation. From 74464583a65d86646b7f8140cdbecc7148e927b8 Mon Sep 17 00:00:00 2001 From: "ahe@google.com" Date: Mon, 19 Nov 2012 09:51:17 +0000 Subject: [PATCH 028/263] Allow tests to specify a package root. Use a shared package root if none is specified. Review URL: https://codereview.chromium.org//11359187 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@15064 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 10c68fe0..728ff7d7 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -6,8 +6,7 @@ library args_test; import '../../../pkg/unittest/lib/unittest.dart'; -// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -import '../lib/args.dart'; +import 'package:args/args.dart'; main() { group('ArgParser.addFlag()', () { From 39d208e0a27630a240e92a5e975d9f6e3828d711 Mon Sep 17 00:00:00 2001 From: "ahe@google.com" Date: Mon, 19 Nov 2012 10:42:55 +0000 Subject: [PATCH 029/263] Revert "Allow tests to specify a package root." Revert "Add dependencies for bots that doesn't build everything." This reverts r15066 and r15064. Review URL: https://codereview.chromium.org//11414055 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@15068 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 728ff7d7..10c68fe0 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -6,7 +6,8 @@ library args_test; import '../../../pkg/unittest/lib/unittest.dart'; -import 'package:args/args.dart'; +// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. +import '../lib/args.dart'; main() { group('ArgParser.addFlag()', () { From 3868bbd5f1c9467df1b5442f68bb100a09ccaa7f Mon Sep 17 00:00:00 2001 From: "ahe@google.com" Date: Mon, 19 Nov 2012 11:35:57 +0000 Subject: [PATCH 030/263] Allow tests to specify a package root. Use a shared package root if none is specified. Committed: https://code.google.com/p/dart/source/detail?r=15064 Review URL: https://codereview.chromium.org//11359187 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@15070 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 10c68fe0..728ff7d7 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -6,8 +6,7 @@ library args_test; import '../../../pkg/unittest/lib/unittest.dart'; -// TODO(rnystrom): Use "package:" URL here when test.dart can handle pub. -import '../lib/args.dart'; +import 'package:args/args.dart'; main() { group('ArgParser.addFlag()', () { From e7af6b752a9e4551bf1d05e3381cdc06a9903ff2 Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Fri, 30 Nov 2012 21:16:01 +0000 Subject: [PATCH 031/263] Add authors and homepages to packages. Review URL: https://codereview.chromium.org//11418267 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@15601 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 6dbe61d4..232ce207 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,4 +1,6 @@ name: args +author: "Dart Team " +homepage: http://www.dartlang.org description: > Libraries for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. From aece4ff08a9b7a0633bd8594cb25e51720365c3a Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Sat, 15 Dec 2012 01:05:35 +0000 Subject: [PATCH 032/263] Convert the small packages in pkg to use "package:". Review URL: https://codereview.chromium.org//11575043 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16189 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 728ff7d7..11627f3f 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,8 +4,7 @@ library args_test; -import '../../../pkg/unittest/lib/unittest.dart'; - +import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; main() { @@ -188,47 +187,47 @@ main() { var args = parser.parse([]); expect(a, isNull); }); - + test('for multiple present, allowMultiple, options are invoked with ' 'value as a list', () { var a; var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, + parser.addOption('a', allowMultiple: true, callback: (value) => a = value); - + var args = parser.parse(['--a=v', '--a=x']); expect(a, equals(['v', 'x'])); }); - + test('for single present, allowMultiple, options are invoked with ' ' value as a single element list', () { var a; var parser = new ArgParser(); parser.addOption('a', allowMultiple: true, callback: (value) => a = value); - + var args = parser.parse(['--a=v']); expect(a, equals(['v'])); }); - + test('for absent, allowMultiple, options are invoked with default ' 'value as a list.', () { var a; var parser = new ArgParser(); parser.addOption('a', allowMultiple: true, defaultsTo: 'v', callback: (value) => a = value); - + var args = parser.parse([]); expect(a, equals(['v'])); }); - + test('for absent, allowMultiple, options are invoked with value ' 'as an empty list.', () { var a; var parser = new ArgParser(); parser.addOption('a', allowMultiple: true, callback: (value) => a = value); - + var args = parser.parse([]); expect(a, isEmpty); }); From 3bc22111e4066fdfe149a6729fbc2fa1239e88e8 Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Mon, 7 Jan 2013 11:23:16 +0000 Subject: [PATCH 033/263] Big merge from experimental to bleeding edge. Review URL: https://codereview.chromium.org//11783009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16687 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 8 ++++---- pkgs/args/test/args_test.dart | 4 ++-- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 12ea7454..0cb81961 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -353,7 +353,7 @@ class ArgParser { _setOption(Map results, _Option option, value) { // See if it's one of the allowed values. if (option.allowed != null) { - _validate(option.allowed.some((allow) => allow == value), + _validate(option.allowed.any((allow) => allow == value), '"$value" is not an allowed value for option "${option.name}".'); } @@ -544,7 +544,7 @@ class ArgResults { } /** Get the names of the options as a [Collection]. */ - Collection get options => _options.keys; + Collection get options => _options.keys.toList(); } class _Option { @@ -630,8 +630,8 @@ class _Usage { if (option.help != null) write(2, option.help); if (option.allowedHelp != null) { - var allowedNames = option.allowedHelp.keys; - allowedNames.sort((a, b) => a.compareTo(b)); + var allowedNames = option.allowedHelp.keys.toList(); + allowedNames.sort(); newline(); for (var name in allowedNames) { write(1, getAllowedTitle(name)); diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 11627f3f..6feb1dce 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -450,8 +450,8 @@ main() { parser.addOption('meow', defaultsTo: 'kitty'); var args = parser.parse([]); expect(args.options, hasLength(2)); - expect(args.options.some((o) => o == 'woof'), isTrue); - expect(args.options.some((o) => o == 'meow'), isTrue); + expect(args.options.any((o) => o == 'woof'), isTrue); + expect(args.options.any((o) => o == 'meow'), isTrue); }); }); From 114aae80d62621e839e02510edffd9c11b60b1c7 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 9 Jan 2013 21:02:21 +0000 Subject: [PATCH 034/263] Split the args tests into a couple of smaller suites. Review URL: https://codereview.chromium.org//11817027 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16882 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 643 ++------------------------------- pkgs/args/test/parse_test.dart | 421 +++++++++++++++++++++ pkgs/args/test/usage_test.dart | 215 +++++++++++ 3 files changed, 660 insertions(+), 619 deletions(-) create mode 100644 pkgs/args/test/parse_test.dart create mode 100644 pkgs/args/test/usage_test.dart diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 6feb1dce..9784bd27 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -60,595 +60,40 @@ main() { }); }); - group('ArgParser.parse()', () { - group('flags', () { - test('are true if present', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - - var args = parser.parse(['--verbose']); - expect(args['verbose'], isTrue); - }); - - test('default if missing', () { - var parser = new ArgParser(); - parser.addFlag('a', defaultsTo: true); - parser.addFlag('b', defaultsTo: false); - - var args = parser.parse([]); - expect(args['a'], isTrue); - expect(args['b'], isFalse); - }); - - test('are false if missing with no default', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - - var args = parser.parse([]); - expect(args['verbose'], isFalse); - }); - - test('throws if given a value', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - - throwsFormat(parser, ['--verbose=true']); - }); - }); - - group('flags negated with "no-"', () { - test('set the flag to false', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - - var args = parser.parse(['--no-verbose']); - expect(args['verbose'], isFalse); - }); - - test('set the flag to true if the flag actually starts with "no-"', () { - var parser = new ArgParser(); - parser.addFlag('no-body'); - - var args = parser.parse(['--no-body']); - expect(args['no-body'], isTrue); - }); - - test('are not preferred over a colliding one without', () { - var parser = new ArgParser(); - parser.addFlag('no-strum'); - parser.addFlag('strum'); - - var args = parser.parse(['--no-strum']); - expect(args['no-strum'], isTrue); - expect(args['strum'], isFalse); - }); - - test('fail for non-negatable flags', () { - var parser = new ArgParser(); - parser.addFlag('strum', negatable: false); - - throwsFormat(parser, ['--no-strum']); - }); - }); - - group('callbacks', () { - test('for present flags are invoked with the value', () { - var a; - var parser = new ArgParser(); - parser.addFlag('a', callback: (value) => a = value); - - var args = parser.parse(['--a']); - expect(a, isTrue); - }); - - test('for absent flags are invoked with the default value', () { - var a; - var parser = new ArgParser(); - parser.addFlag('a', defaultsTo: false, - callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, isFalse); - }); - - test('are invoked even if the flag is not present', () { - var a = 'not called'; - var parser = new ArgParser(); - parser.addFlag('a', callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, isFalse); - }); - - test('for present options are invoked with the value', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', callback: (value) => a = value); - - var args = parser.parse(['--a=v']); - expect(a, equals('v')); - }); - - test('for absent options are invoked with the default value', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', defaultsTo: 'v', - callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, equals('v')); - }); - - test('are invoked even if the option is not present', () { - var a = 'not called'; - var parser = new ArgParser(); - parser.addOption('a', callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, isNull); - }); - - test('for multiple present, allowMultiple, options are invoked with ' - 'value as a list', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); - - var args = parser.parse(['--a=v', '--a=x']); - expect(a, equals(['v', 'x'])); - }); - - test('for single present, allowMultiple, options are invoked with ' - ' value as a single element list', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); - - var args = parser.parse(['--a=v']); - expect(a, equals(['v'])); - }); - - test('for absent, allowMultiple, options are invoked with default ' - 'value as a list.', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, defaultsTo: 'v', - callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, equals(['v'])); - }); - - test('for absent, allowMultiple, options are invoked with value ' - 'as an empty list.', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); - - var args = parser.parse([]); - expect(a, isEmpty); - }); - }); - - group('abbreviations', () { - test('are parsed with a preceding "-"', () { - var parser = new ArgParser(); - parser.addFlag('arg', abbr: 'a'); - - var args = parser.parse(['-a']); - expect(args['arg'], isTrue); - }); - - test('can use multiple after a single "-"', () { - var parser = new ArgParser(); - parser.addFlag('first', abbr: 'f'); - parser.addFlag('second', abbr: 's'); - parser.addFlag('third', abbr: 't'); - - var args = parser.parse(['-tf']); - expect(args['first'], isTrue); - expect(args['second'], isFalse); - expect(args['third'], isTrue); - }); - - test('can have multiple "-" args', () { - var parser = new ArgParser(); - parser.addFlag('first', abbr: 'f'); - parser.addFlag('second', abbr: 's'); - parser.addFlag('third', abbr: 't'); - - var args = parser.parse(['-s', '-tf']); - expect(args['first'], isTrue); - expect(args['second'], isTrue); - expect(args['third'], isTrue); - }); - - test('can take arguments without a space separating', () { - var parser = new ArgParser(); - parser.addOption('file', abbr: 'f'); - - var args = parser.parse(['-flip']); - expect(args['file'], equals('lip')); - }); - - test('can take arguments with a space separating', () { - var parser = new ArgParser(); - parser.addOption('file', abbr: 'f'); - - var args = parser.parse(['-f', 'name']); - expect(args['file'], equals('name')); - }); - - test('allow non-option characters in the value', () { - var parser = new ArgParser(); - parser.addOption('apple', abbr: 'a'); - - var args = parser.parse(['-ab?!c']); - expect(args['apple'], equals('b?!c')); - }); - - test('throw if unknown', () { - var parser = new ArgParser(); - throwsFormat(parser, ['-f']); - }); - - test('throw if the value is missing', () { - var parser = new ArgParser(); - parser.addOption('file', abbr: 'f'); - - throwsFormat(parser, ['-f']); - }); - - test('throw if the value looks like an option', () { - var parser = new ArgParser(); - parser.addOption('file', abbr: 'f'); - parser.addOption('other'); - - throwsFormat(parser, ['-f', '--other']); - throwsFormat(parser, ['-f', '--unknown']); - throwsFormat(parser, ['-f', '-abbr']); - }); - - test('throw if the value is not allowed', () { - var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']); - - throwsFormat(parser, ['-mprofile']); - }); - - test('throw if any but the first is not a flag', () { - var parser = new ArgParser(); - parser.addFlag('apple', abbr: 'a'); - parser.addOption('banana', abbr: 'b'); // Takes an argument. - parser.addFlag('cherry', abbr: 'c'); - - throwsFormat(parser, ['-abc']); - }); - - test('throw if it has a value but the option is a flag', () { - var parser = new ArgParser(); - parser.addFlag('apple', abbr: 'a'); - parser.addFlag('banana', abbr: 'b'); - - // The '?!' means this can only be understood as '--apple b?!c'. - throwsFormat(parser, ['-ab?!c']); - }); - }); - - group('options', () { - test('are parsed if present', () { - var parser = new ArgParser(); - parser.addOption('mode'); - var args = parser.parse(['--mode=release']); - expect(args['mode'], equals('release')); - }); - - test('are null if not present', () { - var parser = new ArgParser(); - parser.addOption('mode'); - var args = parser.parse([]); - expect(args['mode'], isNull); - }); - - test('default if missing', () { - var parser = new ArgParser(); - parser.addOption('mode', defaultsTo: 'debug'); - var args = parser.parse([]); - expect(args['mode'], equals('debug')); - }); - - test('allow the value to be separated by whitespace', () { - var parser = new ArgParser(); - parser.addOption('mode'); - var args = parser.parse(['--mode', 'release']); - expect(args['mode'], equals('release')); - }); - - test('throw if unknown', () { - var parser = new ArgParser(); - throwsFormat(parser, ['--unknown']); - throwsFormat(parser, ['--nobody']); // Starts with "no". - }); - - test('throw if the arg does not include a value', () { - var parser = new ArgParser(); - parser.addOption('mode'); - throwsFormat(parser, ['--mode']); - }); - - test('throw if the value looks like an option', () { - var parser = new ArgParser(); - parser.addOption('mode'); - parser.addOption('other'); - - throwsFormat(parser, ['--mode', '--other']); - throwsFormat(parser, ['--mode', '--unknown']); - throwsFormat(parser, ['--mode', '-abbr']); - }); - - test('do not throw if the value is in the allowed set', () { - var parser = new ArgParser(); - parser.addOption('mode', allowed: ['debug', 'release']); - var args = parser.parse(['--mode=debug']); - expect(args['mode'], equals('debug')); - }); - - test('throw if the value is not in the allowed set', () { - var parser = new ArgParser(); - parser.addOption('mode', allowed: ['debug', 'release']); - throwsFormat(parser, ['--mode=profile']); - }); - - test('returns last provided value', () { - var parser = new ArgParser(); - parser.addOption('define'); - var args = parser.parse(['--define=1', '--define=2']); - expect(args['define'], equals('2')); - }); - - test('returns a List if multi-valued', () { - var parser = new ArgParser(); - parser.addOption('define', allowMultiple: true); - var args = parser.parse(['--define=1']); - expect(args['define'], equals(['1'])); - args = parser.parse(['--define=1', '--define=2']); - expect(args['define'], equals(['1','2'])); - }); - - test('returns the default value for multi-valued arguments ' - 'if not explicitly set', () { - var parser = new ArgParser(); - parser.addOption('define', defaultsTo: '0', allowMultiple: true); - var args = parser.parse(['']); - expect(args['define'], equals(['0'])); - }); - }); - - group('query default values', () { - test('queries the default value', () { - var parser = new ArgParser(); - parser.addOption('define', defaultsTo: '0'); - expect(()=>parser.getDefault('undefine'), - throwsArgumentError); - }); - - test('queries the default value for an unknown option', () { - var parser = new ArgParser(); - parser.addOption('define', defaultsTo: '0'); - expect(()=>parser.getDefault('undefine'), - throwsArgumentError); - }); - }); - - group('gets the option names from an ArgsResult', () { - test('queries the set options', () { - var parser = new ArgParser(); - parser.addFlag('woof', defaultsTo: false); - parser.addOption('meow', defaultsTo: 'kitty'); - var args = parser.parse([]); - expect(args.options, hasLength(2)); - expect(args.options.any((o) => o == 'woof'), isTrue); - expect(args.options.any((o) => o == 'meow'), isTrue); - }); - }); - - group('remaining args', () { - test('stops parsing args when a non-option-like arg is encountered', () { - var parser = new ArgParser(); - parser.addFlag('woof'); - parser.addOption('meow'); - parser.addOption('tweet', defaultsTo: 'bird'); - - var results = parser.parse(['--woof', '--meow', 'v', 'not', 'option']); - expect(results['woof'], isTrue); - expect(results['meow'], equals('v')); - expect(results['tweet'], equals('bird')); - expect(results.rest, orderedEquals(['not', 'option'])); - }); - - test('stops parsing at "--"', () { - var parser = new ArgParser(); - parser.addFlag('woof', defaultsTo: false); - parser.addOption('meow', defaultsTo: 'kitty'); - - var results = parser.parse(['--woof', '--', '--meow']); - expect(results['woof'], isTrue); - expect(results['meow'], equals('kitty')); - expect(results.rest, orderedEquals(['--meow'])); - }); - - test('handles options with case-sensitivity', () { - var parser = new ArgParser(); - parser.addFlag('recurse', defaultsTo: false, abbr:'R'); - var results = parser.parse(['-R']); - expect(results['recurse'], isTrue); - expect(results.rest, [ ]); - throwsFormat(parser, ['-r']); - }); - }); - }); - - group('ArgParser.getUsage()', () { - test('negatable flags show "no-" in title', () { + group('ArgParser.getDefault()', () { + test('returns the default value for an option', () { var parser = new ArgParser(); - parser.addFlag('mode', help: 'The mode'); - - validateUsage(parser, - ''' - --[no-]mode The mode - '''); - }); - - test('non-negatable flags don\'t show "no-" in title', () { - var parser = new ArgParser(); - parser.addFlag('mode', negatable: false, help: 'The mode'); - - validateUsage(parser, - ''' - --mode The mode - '''); - }); - - test('if there are no abbreviations, there is no column for them', () { - var parser = new ArgParser(); - parser.addFlag('mode', help: 'The mode'); - - validateUsage(parser, - ''' - --[no-]mode The mode - '''); + parser.addOption('mode', defaultsTo: 'debug'); + expect(parser.getDefault('mode'), 'debug'); }); - test('options are lined up past abbreviations', () { + test('throws if the option is unknown', () { var parser = new ArgParser(); - parser.addFlag('mode', abbr: 'm', help: 'The mode'); - parser.addOption('long', help: 'Lacks an abbreviation'); - - validateUsage(parser, - ''' - -m, --[no-]mode The mode - --long Lacks an abbreviation - '''); - }); - - test('help text is lined up past the longest option', () { - var parser = new ArgParser(); - parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); - parser.addOption('a-really-long-name', help: 'Its help text'); - - validateUsage(parser, - ''' - -m, --[no-]mode Lined up with below - --a-really-long-name Its help text - '''); - }); - - test('leading empty lines are ignored in help text', () { - var parser = new ArgParser(); - parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); - - validateUsage(parser, - ''' - --[no-]mode After newlines - '''); - }); - - test('trailing empty lines are ignored in help text', () { - var parser = new ArgParser(); - parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); - - validateUsage(parser, - ''' - --[no-]mode Before newlines - '''); - }); - - test('options are documented in the order they were added', () { - var parser = new ArgParser(); - parser.addFlag('zebra', help: 'First'); - parser.addFlag('monkey', help: 'Second'); - parser.addFlag('wombat', help: 'Third'); - - validateUsage(parser, - ''' - --[no-]zebra First - --[no-]monkey Second - --[no-]wombat Third - '''); - }); - - test('the default value for a flag is shown if on', () { - var parser = new ArgParser(); - parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); - parser.addFlag('negate', help: 'Should be off', defaultsTo: false); - - validateUsage(parser, - ''' - --[no-]affirm Should be on - (defaults to on) - - --[no-]negate Should be off - '''); - }); - - test('the default value for an option with no allowed list is shown', () { - var parser = new ArgParser(); - parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); - - validateUsage(parser, - ''' - --any Can be anything - (defaults to "whatevs") - '''); - }); - - test('the allowed list is shown', () { - var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', - allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - - validateUsage(parser, - ''' - --suit Like in cards - [spades, clubs, hearts, diamonds] - '''); + parser.addOption('mode', defaultsTo: 'debug'); + expect(()=>parser.getDefault('undefined'), + throwsArgumentError); }); + }); - test('the default is highlighted in the allowed list', () { + group('ArgResults.options', () { + test('returns the provided options', () { var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', - allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - - validateUsage(parser, - ''' - --suit Like in cards - [spades, clubs (default), hearts, diamonds] - '''); + parser.addFlag('woof'); + parser.addOption('meow'); + var args = parser.parse(['--woof', '--meow', 'kitty']); + expect(args.options, hasLength(2)); + expect(args.options.any((o) => o == 'woof'), isTrue); + expect(args.options.any((o) => o == 'meow'), isTrue); }); - test('the allowed help is shown', () { + test('includes defaulted options', () { var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', - allowed: ['spades', 'clubs', 'diamonds', 'hearts'], - allowedHelp: { - 'spades': 'Swords of a soldier', - 'clubs': 'Weapons of war', - 'diamonds': 'Money for this art', - 'hearts': 'The shape of my heart' - }); - - validateUsage(parser, - ''' - --suit Like in cards - - [clubs] Weapons of war - [diamonds] Money for this art - [hearts] The shape of my heart - [spades] Swords of a soldier - '''); + parser.addFlag('woof', defaultsTo: false); + parser.addOption('meow', defaultsTo: 'kitty'); + var args = parser.parse([]); + expect(args.options, hasLength(2)); + expect(args.options.any((o) => o == 'woof'), isTrue); + expect(args.options.any((o) => o == 'meow'), isTrue); }); }); @@ -668,43 +113,3 @@ throwsIllegalArg(function) { throwsFormat(ArgParser parser, List args) { expect(() => parser.parse(args), throwsFormatException); } - -validateUsage(ArgParser parser, String expected) { - expected = unindentString(expected); - expect(parser.getUsage(), equals(expected)); -} - -// TODO(rnystrom): Replace one in test_utils. -String unindentString(String text) { - var lines = text.split('\n'); - - // Count the indentation of the last line. - var whitespace = new RegExp('^ *'); - var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; - - // Drop the last line. It only exists for specifying indentation. - lines.removeLast(); - - // Strip indentation from the remaining lines. - for (var i = 0; i < lines.length; i++) { - var line = lines[i]; - if (line.length <= indent) { - // It's short, so it must be nothing but whitespace. - if (line.trim() != '') { - throw new ArgumentError( - 'Line "$line" does not have enough indentation.'); - } - - lines[i] = ''; - } else { - if (line.substring(0, indent).trim() != '') { - throw new ArgumentError( - 'Line "$line" does not have enough indentation.'); - } - - lines[i] = line.substring(indent); - } - } - - return Strings.join(lines, '\n'); -} diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart new file mode 100644 index 00000000..ab1d1306 --- /dev/null +++ b/pkgs/args/test/parse_test.dart @@ -0,0 +1,421 @@ +// Copyright (c) 2012, 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. + +library args_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +main() { + group('ArgParser.parse()', () { + group('flags', () { + test('are true if present', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse(['--verbose']); + expect(args['verbose'], isTrue); + }); + + test('default if missing', () { + var parser = new ArgParser(); + parser.addFlag('a', defaultsTo: true); + parser.addFlag('b', defaultsTo: false); + + var args = parser.parse([]); + expect(args['a'], isTrue); + expect(args['b'], isFalse); + }); + + test('are false if missing with no default', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse([]); + expect(args['verbose'], isFalse); + }); + + test('throws if given a value', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + throwsFormat(parser, ['--verbose=true']); + }); + }); + + group('flags negated with "no-"', () { + test('set the flag to false', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = parser.parse(['--no-verbose']); + expect(args['verbose'], isFalse); + }); + + test('set the flag to true if the flag actually starts with "no-"', () { + var parser = new ArgParser(); + parser.addFlag('no-body'); + + var args = parser.parse(['--no-body']); + expect(args['no-body'], isTrue); + }); + + test('are not preferred over a colliding one without', () { + var parser = new ArgParser(); + parser.addFlag('no-strum'); + parser.addFlag('strum'); + + var args = parser.parse(['--no-strum']); + expect(args['no-strum'], isTrue); + expect(args['strum'], isFalse); + }); + + test('fail for non-negatable flags', () { + var parser = new ArgParser(); + parser.addFlag('strum', negatable: false); + + throwsFormat(parser, ['--no-strum']); + }); + }); + + group('callbacks', () { + test('for present flags are invoked with the value', () { + var a; + var parser = new ArgParser(); + parser.addFlag('a', callback: (value) => a = value); + + var args = parser.parse(['--a']); + expect(a, isTrue); + }); + + test('for absent flags are invoked with the default value', () { + var a; + var parser = new ArgParser(); + parser.addFlag('a', defaultsTo: false, + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isFalse); + }); + + test('are invoked even if the flag is not present', () { + var a = 'not called'; + var parser = new ArgParser(); + parser.addFlag('a', callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isFalse); + }); + + test('for present options are invoked with the value', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', callback: (value) => a = value); + + var args = parser.parse(['--a=v']); + expect(a, equals('v')); + }); + + test('for absent options are invoked with the default value', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', defaultsTo: 'v', + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, equals('v')); + }); + + test('are invoked even if the option is not present', () { + var a = 'not called'; + var parser = new ArgParser(); + parser.addOption('a', callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isNull); + }); + + test('for multiple present, allowMultiple, options are invoked with ' + 'value as a list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse(['--a=v', '--a=x']); + expect(a, equals(['v', 'x'])); + }); + + test('for single present, allowMultiple, options are invoked with ' + ' value as a single element list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse(['--a=v']); + expect(a, equals(['v'])); + }); + + test('for absent, allowMultiple, options are invoked with default ' + 'value as a list.', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, defaultsTo: 'v', + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, equals(['v'])); + }); + + test('for absent, allowMultiple, options are invoked with value ' + 'as an empty list.', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', allowMultiple: true, + callback: (value) => a = value); + + var args = parser.parse([]); + expect(a, isEmpty); + }); + }); + + group('abbreviations', () { + test('are parsed with a preceding "-"', () { + var parser = new ArgParser(); + parser.addFlag('arg', abbr: 'a'); + + var args = parser.parse(['-a']); + expect(args['arg'], isTrue); + }); + + test('can use multiple after a single "-"', () { + var parser = new ArgParser(); + parser.addFlag('first', abbr: 'f'); + parser.addFlag('second', abbr: 's'); + parser.addFlag('third', abbr: 't'); + + var args = parser.parse(['-tf']); + expect(args['first'], isTrue); + expect(args['second'], isFalse); + expect(args['third'], isTrue); + }); + + test('can have multiple "-" args', () { + var parser = new ArgParser(); + parser.addFlag('first', abbr: 'f'); + parser.addFlag('second', abbr: 's'); + parser.addFlag('third', abbr: 't'); + + var args = parser.parse(['-s', '-tf']); + expect(args['first'], isTrue); + expect(args['second'], isTrue); + expect(args['third'], isTrue); + }); + + test('can take arguments without a space separating', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + var args = parser.parse(['-flip']); + expect(args['file'], equals('lip')); + }); + + test('can take arguments with a space separating', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + var args = parser.parse(['-f', 'name']); + expect(args['file'], equals('name')); + }); + + test('allow non-option characters in the value', () { + var parser = new ArgParser(); + parser.addOption('apple', abbr: 'a'); + + var args = parser.parse(['-ab?!c']); + expect(args['apple'], equals('b?!c')); + }); + + test('throw if unknown', () { + var parser = new ArgParser(); + throwsFormat(parser, ['-f']); + }); + + test('throw if the value is missing', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + + throwsFormat(parser, ['-f']); + }); + + test('throw if the value looks like an option', () { + var parser = new ArgParser(); + parser.addOption('file', abbr: 'f'); + parser.addOption('other'); + + throwsFormat(parser, ['-f', '--other']); + throwsFormat(parser, ['-f', '--unknown']); + throwsFormat(parser, ['-f', '-abbr']); + }); + + test('throw if the value is not allowed', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']); + + throwsFormat(parser, ['-mprofile']); + }); + + test('throw if any but the first is not a flag', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + parser.addOption('banana', abbr: 'b'); // Takes an argument. + parser.addFlag('cherry', abbr: 'c'); + + throwsFormat(parser, ['-abc']); + }); + + test('throw if it has a value but the option is a flag', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + parser.addFlag('banana', abbr: 'b'); + + // The '?!' means this can only be understood as '--apple b?!c'. + throwsFormat(parser, ['-ab?!c']); + }); + }); + + group('options', () { + test('are parsed if present', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse(['--mode=release']); + expect(args['mode'], equals('release')); + }); + + test('are null if not present', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse([]); + expect(args['mode'], isNull); + }); + + test('default if missing', () { + var parser = new ArgParser(); + parser.addOption('mode', defaultsTo: 'debug'); + var args = parser.parse([]); + expect(args['mode'], equals('debug')); + }); + + test('allow the value to be separated by whitespace', () { + var parser = new ArgParser(); + parser.addOption('mode'); + var args = parser.parse(['--mode', 'release']); + expect(args['mode'], equals('release')); + }); + + test('throw if unknown', () { + var parser = new ArgParser(); + throwsFormat(parser, ['--unknown']); + throwsFormat(parser, ['--nobody']); // Starts with "no". + }); + + test('throw if the arg does not include a value', () { + var parser = new ArgParser(); + parser.addOption('mode'); + throwsFormat(parser, ['--mode']); + }); + + test('throw if the value looks like an option', () { + var parser = new ArgParser(); + parser.addOption('mode'); + parser.addOption('other'); + + throwsFormat(parser, ['--mode', '--other']); + throwsFormat(parser, ['--mode', '--unknown']); + throwsFormat(parser, ['--mode', '-abbr']); + }); + + test('do not throw if the value is in the allowed set', () { + var parser = new ArgParser(); + parser.addOption('mode', allowed: ['debug', 'release']); + var args = parser.parse(['--mode=debug']); + expect(args['mode'], equals('debug')); + }); + + test('throw if the value is not in the allowed set', () { + var parser = new ArgParser(); + parser.addOption('mode', allowed: ['debug', 'release']); + throwsFormat(parser, ['--mode=profile']); + }); + + test('returns last provided value', () { + var parser = new ArgParser(); + parser.addOption('define'); + var args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals('2')); + }); + + test('returns a List if multi-valued', () { + var parser = new ArgParser(); + parser.addOption('define', allowMultiple: true); + var args = parser.parse(['--define=1']); + expect(args['define'], equals(['1'])); + args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals(['1','2'])); + }); + + test('returns the default value for multi-valued arguments ' + 'if not explicitly set', () { + var parser = new ArgParser(); + parser.addOption('define', defaultsTo: '0', allowMultiple: true); + var args = parser.parse(['']); + expect(args['define'], equals(['0'])); + }); + }); + + group('remaining args', () { + test('stops parsing args when a non-option-like arg is encountered', () { + var parser = new ArgParser(); + parser.addFlag('woof'); + parser.addOption('meow'); + parser.addOption('tweet', defaultsTo: 'bird'); + + var results = parser.parse(['--woof', '--meow', 'v', 'not', 'option']); + expect(results['woof'], isTrue); + expect(results['meow'], equals('v')); + expect(results['tweet'], equals('bird')); + expect(results.rest, orderedEquals(['not', 'option'])); + }); + + test('stops parsing at "--"', () { + var parser = new ArgParser(); + parser.addFlag('woof', defaultsTo: false); + parser.addOption('meow', defaultsTo: 'kitty'); + + var results = parser.parse(['--woof', '--', '--meow']); + expect(results['woof'], isTrue); + expect(results['meow'], equals('kitty')); + expect(results.rest, orderedEquals(['--meow'])); + }); + + test('handles options with case-sensitivity', () { + var parser = new ArgParser(); + parser.addFlag('recurse', defaultsTo: false, abbr:'R'); + var results = parser.parse(['-R']); + expect(results['recurse'], isTrue); + expect(results.rest, [ ]); + throwsFormat(parser, ['-r']); + }); + }); + }); +} + +throwsIllegalArg(function) { + expect(function, throwsArgumentError); +} + +throwsFormat(ArgParser parser, List args) { + expect(() => parser.parse(args), throwsFormatException); +} diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart new file mode 100644 index 00000000..2dffcae2 --- /dev/null +++ b/pkgs/args/test/usage_test.dart @@ -0,0 +1,215 @@ +// Copyright (c) 2012, 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. + +library args_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +main() { + group('ArgParser.getUsage()', () { + test('negatable flags show "no-" in title', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'The mode'); + + validateUsage(parser, + ''' + --[no-]mode The mode + '''); + }); + + test('non-negatable flags don\'t show "no-" in title', () { + var parser = new ArgParser(); + parser.addFlag('mode', negatable: false, help: 'The mode'); + + validateUsage(parser, + ''' + --mode The mode + '''); + }); + + test('if there are no abbreviations, there is no column for them', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'The mode'); + + validateUsage(parser, + ''' + --[no-]mode The mode + '''); + }); + + test('options are lined up past abbreviations', () { + var parser = new ArgParser(); + parser.addFlag('mode', abbr: 'm', help: 'The mode'); + parser.addOption('long', help: 'Lacks an abbreviation'); + + validateUsage(parser, + ''' + -m, --[no-]mode The mode + --long Lacks an abbreviation + '''); + }); + + test('help text is lined up past the longest option', () { + var parser = new ArgParser(); + parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); + parser.addOption('a-really-long-name', help: 'Its help text'); + + validateUsage(parser, + ''' + -m, --[no-]mode Lined up with below + --a-really-long-name Its help text + '''); + }); + + test('leading empty lines are ignored in help text', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); + + validateUsage(parser, + ''' + --[no-]mode After newlines + '''); + }); + + test('trailing empty lines are ignored in help text', () { + var parser = new ArgParser(); + parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); + + validateUsage(parser, + ''' + --[no-]mode Before newlines + '''); + }); + + test('options are documented in the order they were added', () { + var parser = new ArgParser(); + parser.addFlag('zebra', help: 'First'); + parser.addFlag('monkey', help: 'Second'); + parser.addFlag('wombat', help: 'Third'); + + validateUsage(parser, + ''' + --[no-]zebra First + --[no-]monkey Second + --[no-]wombat Third + '''); + }); + + test('the default value for a flag is shown if on', () { + var parser = new ArgParser(); + parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); + parser.addFlag('negate', help: 'Should be off', defaultsTo: false); + + validateUsage(parser, + ''' + --[no-]affirm Should be on + (defaults to on) + + --[no-]negate Should be off + '''); + }); + + test('the default value for an option with no allowed list is shown', () { + var parser = new ArgParser(); + parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); + + validateUsage(parser, + ''' + --any Can be anything + (defaults to "whatevs") + '''); + }); + + test('the allowed list is shown', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', + allowed: ['spades', 'clubs', 'hearts', 'diamonds']); + + validateUsage(parser, + ''' + --suit Like in cards + [spades, clubs, hearts, diamonds] + '''); + }); + + test('the default is highlighted in the allowed list', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + allowed: ['spades', 'clubs', 'hearts', 'diamonds']); + + validateUsage(parser, + ''' + --suit Like in cards + [spades, clubs (default), hearts, diamonds] + '''); + }); + + test('the allowed help is shown', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + allowed: ['spades', 'clubs', 'diamonds', 'hearts'], + allowedHelp: { + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage(parser, + ''' + --suit Like in cards + + [clubs] Weapons of war + [diamonds] Money for this art + [hearts] The shape of my heart + [spades] Swords of a soldier + '''); + }); + }); +} + +throwsIllegalArg(function) { + expect(function, throwsArgumentError); +} + +validateUsage(ArgParser parser, String expected) { + expected = unindentString(expected); + expect(parser.getUsage(), equals(expected)); +} + +// TODO(rnystrom): Replace one in test_utils. +String unindentString(String text) { + var lines = text.split('\n'); + + // Count the indentation of the last line. + var whitespace = new RegExp('^ *'); + var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; + + // Drop the last line. It only exists for specifying indentation. + lines.removeLast(); + + // Strip indentation from the remaining lines. + for (var i = 0; i < lines.length; i++) { + var line = lines[i]; + if (line.length <= indent) { + // It's short, so it must be nothing but whitespace. + if (line.trim() != '') { + throw new ArgumentError( + 'Line "$line" does not have enough indentation.'); + } + + lines[i] = ''; + } else { + if (line.substring(0, indent).trim() != '') { + throw new ArgumentError( + 'Line "$line" does not have enough indentation.'); + } + + lines[i] = line.substring(indent); + } + } + + return Strings.join(lines, '\n'); +} From d8ca33172fbedb311c1ccb4edbc26870f9ed6b2b Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 11 Jan 2013 22:03:21 +0000 Subject: [PATCH 035/263] Add command support to args. Review URL: https://codereview.chromium.org//11819068 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@16988 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 569 +++++-------------------------- pkgs/args/lib/src/parser.dart | 281 +++++++++++++++ pkgs/args/lib/src/usage.dart | 238 +++++++++++++ pkgs/args/lib/src/utils.dart | 19 -- pkgs/args/test/args_test.dart | 53 ++- pkgs/args/test/command_test.dart | 199 +++++++++++ pkgs/args/test/parse_test.dart | 12 +- pkgs/args/test/usage_test.dart | 2 +- 8 files changed, 874 insertions(+), 499 deletions(-) create mode 100644 pkgs/args/lib/src/parser.dart create mode 100644 pkgs/args/lib/src/usage.dart delete mode 100644 pkgs/args/lib/src/utils.dart create mode 100644 pkgs/args/test/command_test.dart diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 0cb81961..29da1e26 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/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 _options; + /** + * The options that have been defined for this parser. + */ + final Map options = {}; /** - * 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 _optionNames; + final Map commands = {}; - /** The current argument list being parsed. Set by [parse()]. */ - List _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 = {}, - _optionNames = []; + 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 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 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 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 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 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 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 get options => _options.keys.toList(); } + diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart new file mode 100644 index 00000000..d35f9ea4 --- /dev/null +++ b/pkgs/args/lib/src/parser.dart @@ -0,0 +1,281 @@ +// 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. + +library args.src.parser; + +import '../args.dart'; + +final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); +final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); +final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); + +/** + * The actual parsing class. Unlike [ArgParser] which is really more an "arg + * grammar", this is the class that does the parsing and holds the mutable + * state required during a parse. + */ +class Parser { + /** + * If parser is parsing a command's options, this will be the name of the + * command. For top-level results, this returns `null`. + */ + final String commandName; + + /** + * The parser for the supercommand of this command parser, or `null` if this + * is the top-level parser. + */ + final Parser parent; + + /** The grammar being parsed. */ + final ArgParser grammar; + + /** The arguments being parsed. */ + final List args; + + /** The accumulated parsed options. */ + final Map results = {}; + + Parser(this.commandName, this.grammar, this.args, [this.parent]); + + /** The current argument being parsed. */ + String get current => args[0]; + + /** Parses the arguments. This can only be called once. */ + ArgResults parse() { + var commandResults = null; + + // Initialize flags to their defaults. + grammar.options.forEach((name, option) { + if (option.allowMultiple) { + results[name] = []; + } else { + results[name] = option.defaultValue; + } + }); + + // Parse the args. + while (args.length > 0) { + if (current == '--') { + // Reached the argument terminator, so stop here. + args.removeAt(0); + break; + } + + // Try to parse the current argument as a command. This happens before + // options so that commands can have option-like names. + var command = grammar.commands[current]; + if (command != null) { + var commandName = args.removeAt(0); + var commandParser = new Parser(commandName, command, args, this); + commandResults = commandParser.parse(); + continue; + } + + // Try to parse the current argument as an option. Note that the order + // here matters. + if (parseSoloOption()) continue; + if (parseAbbreviation(this)) continue; + if (parseLongOption()) 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. + grammar.options.forEach((name, option) { + 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 to the innermost command. + var rest = args.toList(); + args.clear(); + return new ArgResults(results, commandName, commandResults, rest); + } + + /** + * Pulls the value for [option] from the second argument in [args]. Validates + * that there is a valid value there. + */ + void readNextArgAsValue(Option option) { + // Take the option argument from the next command line arg. + validate(args.length > 0, + 'Missing argument for "${option.name}".'); + + // Make sure it isn't an option itself. + validate(!_ABBR_OPT.hasMatch(current) && !_LONG_OPT.hasMatch(current), + 'Missing argument for "${option.name}".'); + + setOption(results, option, current); + args.removeAt(0); + } + + /** + * 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() { + var soloOpt = _SOLO_OPT.firstMatch(current); + if (soloOpt == null) return false; + + var option = grammar.findByAbbreviation(soloOpt[1]); + if (option == null) { + // Walk up to the parent command if possible. + validate(parent != null, + 'Could not find an option or flag "-${soloOpt[1]}".'); + return parent.parseSoloOption(); + } + + args.removeAt(0); + + if (option.isFlag) { + setOption(results, option, true); + } else { + readNextArgAsValue(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(Parser innermostCommand) { + var abbrOpt = _ABBR_OPT.firstMatch(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 = grammar.findByAbbreviation(c); + if (first == null) { + // Walk up to the parent command if possible. + validate(parent != null, + 'Could not find an option with short name "-$c".'); + return parent.parseAbbreviation(innermostCommand); + } 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. + // We use "innermostCommand" here so that if a parent command parses the + // *first* letter, subcommands can still be found to parse the other + // letters. + for (var i = 0; i < abbrOpt[1].length; i++) { + var c = abbrOpt[1].substring(i, i + 1); + innermostCommand.parseShortFlag(c); + } + } + + args.removeAt(0); + return true; + } + + void parseShortFlag(String c) { + var option = grammar.findByAbbreviation(c); + if (option == null) { + // Walk up to the parent command if possible. + validate(parent != null, + 'Could not find an option with short name "-$c".'); + parent.parseShortFlag(c); + return; + } + + // 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); + } + + /** + * 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() { + var longOpt = _LONG_OPT.firstMatch(current); + if (longOpt == null) return false; + + var name = longOpt[1]; + var option = grammar.options[name]; + if (option != null) { + args.removeAt(0); + 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(option); + } + } else if (name.startsWith('no-')) { + // See if it's a negated flag. + name = name.substring('no-'.length); + option = grammar.options[name]; + if (option == null) { + // Walk up to the parent command if possible. + validate(parent != null, 'Could not find an option named "$name".'); + return parent.parseLongOption(); + } + + args.removeAt(0); + validate(option.isFlag, 'Cannot negate non-flag option "$name".'); + validate(option.negatable, 'Cannot negate option "$name".'); + + setOption(results, option, false); + } else { + // Walk up to the parent command if possible. + validate(parent != null, 'Could not find an option named "$name".'); + return parent.parseLongOption(); + } + + return true; + } + + /** + * 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; + } + } +} diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart new file mode 100644 index 00000000..72525e29 --- /dev/null +++ b/pkgs/args/lib/src/usage.dart @@ -0,0 +1,238 @@ +// 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. + +library args.src.usage; + +import 'dart:math'; + +import '../args.dart'; + +/** + * 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. + */ +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 columnWidths; + + /** + * 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. + */ + int numHelpLines = 0; + + /** + * 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. + */ + int newlinesNeeded = 0; + + Usage(this.args); + + /** + * Generates a string displaying usage information for the defined options. + * This is basically the help text shown on the command line. + */ + String generate() { + buffer = new StringBuffer(); + + calculateColumnWidths(); + + args.options.forEach((name, option) { + 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; + args.options.forEach((name, option) { + // 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--; + } + + // 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; + } + + 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); + } + + // 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; + } + } + + 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(); + } +} + +/** Pads [source] to [length] by adding spaces at the end. */ +String padRight(String source, int length) { + final result = new StringBuffer(); + result.add(source); + + while (result.length < length) { + result.add(' '); + } + + return result.toString(); +} \ No newline at end of file diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart deleted file mode 100644 index c6997c85..00000000 --- a/pkgs/args/lib/src/utils.dart +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright (c) 2012, 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. - -// TODO(rnystrom): This file was copied from pub. -/** Generic utility functions. Stuff that should possibly be in core. */ -library args_utils; - -/** Pads [source] to [length] by adding spaces at the end. */ -String padRight(String source, int length) { - final result = new StringBuffer(); - result.add(source); - - while (result.length < length) { - result.add(' '); - } - - return result.toString(); -} \ No newline at end of file diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 9784bd27..d807291e 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -70,8 +70,57 @@ main() { test('throws if the option is unknown', () { var parser = new ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); - expect(()=>parser.getDefault('undefined'), - throwsArgumentError); + throwsIllegalArg(() => parser.getDefault('undefined')); + }); + }); + + group('ArgParser.commands', () { + test('returns an empty map if there are no commands', () { + var parser = new ArgParser(); + expect(parser.commands, isEmpty); + }); + + test('returns the commands that were added', () { + var parser = new ArgParser(); + parser.addCommand('hide'); + parser.addCommand('seek'); + expect(parser.commands, hasLength(2)); + expect(parser.commands['hide'], isNotNull); + expect(parser.commands['seek'], isNotNull); + }); + + test('iterates over the commands in the order they were added', () { + var parser = new ArgParser(); + parser.addCommand('a'); + parser.addCommand('d'); + parser.addCommand('b'); + parser.addCommand('c'); + expect(parser.commands.keys, equals(['a', 'd', 'b', 'c'])); + }); + }); + + group('ArgParser.options', () { + test('returns an empty map if there are no options', () { + var parser = new ArgParser(); + expect(parser.options, isEmpty); + }); + + test('returns the options that were added', () { + var parser = new ArgParser(); + parser.addFlag('hide'); + parser.addOption('seek'); + expect(parser.options, hasLength(2)); + expect(parser.options['hide'], isNotNull); + expect(parser.options['seek'], isNotNull); + }); + + test('iterates over the options in the order they were added', () { + var parser = new ArgParser(); + parser.addFlag('a'); + parser.addOption('d'); + parser.addFlag('b'); + parser.addOption('c'); + expect(parser.options.keys, equals(['a', 'd', 'b', 'c'])); }); }); diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart new file mode 100644 index 00000000..50794256 --- /dev/null +++ b/pkgs/args/test/command_test.dart @@ -0,0 +1,199 @@ +// Copyright (c) 2012, 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. + +library command_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +main() { + group('ArgParser.addCommand()', () { + test('throws on a duplicate command name', () { + var parser = new ArgParser(); + parser.addCommand('install'); + throwsIllegalArg(() => parser.addCommand('install')); + }); + }); + + group('ArgParser.parse()', () { + test('parses a command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + + var args = parser.parse(['install']); + + expect(args.command.name, equals('install')); + expect(args.rest, isEmpty); + }); + + test('parses a command option', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path'); + + var args = parser.parse(['install', '--path', 'some/path']); + expect(args.command['path'], equals('some/path')); + }); + + test('parses a parent solo option before the command', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm'); + var command = parser.addCommand('install'); + + var args = parser.parse(['-m', 'debug', 'install']); + expect(args['mode'], equals('debug')); + expect(args.command.name, equals('install')); + }); + + test('parses a parent solo option after the command', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '-m', 'debug']); + expect(args['mode'], equals('debug')); + expect(args.command.name, equals('install')); + }); + + test('parses a parent option before the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('install'); + + var args = parser.parse(['--verbose', 'install']); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent option after the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '--verbose']); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent negated option before the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose', defaultsTo: true); + var command = parser.addCommand('install'); + + var args = parser.parse(['--no-verbose', 'install']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('install')); + }); + + test('parses a parent negated option after the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose', defaultsTo: true); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '--no-verbose']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('install')); + }); + + test('parses a parent abbreviation before the command', () { + var parser = new ArgParser(); + parser.addFlag('debug', abbr: 'd'); + parser.addFlag('verbose', abbr: 'v'); + var command = parser.addCommand('install'); + + var args = parser.parse(['-dv', 'install']); + expect(args['debug'], isTrue); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent abbreviation after the command', () { + var parser = new ArgParser(); + parser.addFlag('debug', abbr: 'd'); + parser.addFlag('verbose', abbr: 'v'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '-dv']); + expect(args['debug'], isTrue); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('does not parse a solo command option before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path', abbr: 'p'); + + throwsFormat(parser, ['-p', 'foo', 'install']); + }); + + test('does not parse a command option before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path'); + + throwsFormat(parser, ['--path', 'foo', 'install']); + }); + + test('does not parse a command abbreviation before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addFlag('debug', abbr: 'd'); + command.addFlag('verbose', abbr: 'v'); + + throwsFormat(parser, ['-dv', 'install']); + }); + + test('assigns collapsed options to the proper command', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + var command = parser.addCommand('cmd'); + command.addFlag('banana', abbr: 'b'); + var subcommand = command.addCommand('subcmd'); + subcommand.addFlag('cherry', abbr: 'c'); + + var args = parser.parse(['cmd', 'subcmd', '-abc']); + expect(args['apple'], isTrue); + expect(args.command.name, equals('cmd')); + expect(args.command['banana'], isTrue); + expect(args.command.command.name, equals('subcmd')); + expect(args.command.command['cherry'], isTrue); + }); + + test('option is given to innermost command that can take it', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('cmd'); + command.addFlag('verbose'); + var subcommand = command.addCommand('subcmd'); + + var args = parser.parse(['cmd', 'subcmd', '--verbose']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('cmd')); + expect(args.command['verbose'], isTrue); + expect(args.command.command.name, equals('subcmd')); + }); + + test('remaining arguments are given to the innermost command', () { + var parser = new ArgParser(); + var command = parser.addCommand('cmd'); + var subcommand = command.addCommand('subcmd'); + + var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); + expect(args.command.name, equals('cmd')); + expect(args.rest, isEmpty); + expect(args.command.command.name, equals('subcmd')); + expect(args.command.rest, isEmpty); + expect(args.command.command.rest, equals(['other', 'stuff'])); + }); + }); +} + +throwsIllegalArg(function) { + expect(function, throwsArgumentError); +} + +throwsFormat(ArgParser parser, List args) { + expect(() => parser.parse(args), throwsFormatException); +} diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index ab1d1306..715c2269 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -2,13 +2,23 @@ // 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. -library args_test; +library parse_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; main() { group('ArgParser.parse()', () { + test('does not destructively modify the argument list', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + + var args = ['--verbose']; + var results = parser.parse(args); + expect(args, equals(['--verbose'])); + expect(results['verbose'], isTrue); + }); + group('flags', () { test('are true if present', () { var parser = new ArgParser(); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 2dffcae2..b29bf95b 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -2,7 +2,7 @@ // 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. -library args_test; +library usage_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; From 7c29884f51b756c1c46f6bab5ff6b81897254137 Mon Sep 17 00:00:00 2001 From: "amouravski@google.com" Date: Tue, 12 Feb 2013 23:38:30 +0000 Subject: [PATCH 036/263] Added documentation links to all of the pkg packages hosted on api.dartlang.org. Review URL: https://codereview.chromium.org//12218119 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18414 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 232ce207..b4b1e457 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,6 +1,7 @@ name: args author: "Dart Team " homepage: http://www.dartlang.org +documentation: http://api.dartlang.org/docs/pkg/args.html description: > Libraries for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. From 537eafd51fd0328b176ead251733eb78b509e42d Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Fri, 15 Feb 2013 01:27:10 +0000 Subject: [PATCH 037/263] Update documentation links to point to the correct URLs. Review URL: https://codereview.chromium.org//12252055 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18552 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index b4b1e457..959c189c 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,7 +1,7 @@ name: args author: "Dart Team " homepage: http://www.dartlang.org -documentation: http://api.dartlang.org/docs/pkg/args.html +documentation: http://api.dartlang.org/docs/pkg/args description: > Libraries for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. From 25195702253248c8adeb515d4666aad7d3c1fb29 Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Tue, 19 Feb 2013 13:57:03 +0000 Subject: [PATCH 038/263] Remove deprecated Strings class. Review URL: https://codereview.chromium.org//12295014 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18686 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/usage_test.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index b29bf95b..6068f321 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -211,5 +211,5 @@ String unindentString(String text) { } } - return Strings.join(lines, '\n'); + return lines.join('\n'); } From f6eda2aa98f0473166732be92ae4cd637c8f4eef Mon Sep 17 00:00:00 2001 From: "sigmund@google.com" Date: Fri, 22 Feb 2013 18:21:18 +0000 Subject: [PATCH 039/263] Fix use of stringbuffer in pkg/args Review URL: https://codereview.chromium.org//12335035 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@18902 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/src/usage.dart | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 72525e29..b83a2d51 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -170,7 +170,7 @@ class Usage { writeLine(int column, String text) { // Write any pending newlines. while (newlinesNeeded > 0) { - buffer.add('\n'); + buffer.write('\n'); newlinesNeeded--; } @@ -178,19 +178,19 @@ class Usage { // to the next line. while (currentColumn != column) { if (currentColumn < NUM_COLUMNS - 1) { - buffer.add(padRight('', columnWidths[currentColumn])); + buffer.write(padRight('', columnWidths[currentColumn])); } else { - buffer.add('\n'); + buffer.write('\n'); } currentColumn = (currentColumn + 1) % NUM_COLUMNS; } if (column < columnWidths.length) { // Fixed-size column, so pad it. - buffer.add(padRight(text, columnWidths[column])); + buffer.write(padRight(text, columnWidths[column])); } else { // The last column, so just write it. - buffer.add(text); + buffer.write(text); } // Advance to the next column. @@ -210,17 +210,17 @@ class Usage { buildAllowedList(Option option) { var allowedBuffer = new StringBuffer(); - allowedBuffer.add('['); + allowedBuffer.write('['); bool first = true; for (var allowed in option.allowed) { - if (!first) allowedBuffer.add(', '); - allowedBuffer.add(allowed); + if (!first) allowedBuffer.write(', '); + allowedBuffer.write(allowed); if (allowed == option.defaultValue) { - allowedBuffer.add(' (default)'); + allowedBuffer.write(' (default)'); } first = false; } - allowedBuffer.add(']'); + allowedBuffer.write(']'); return allowedBuffer.toString(); } } @@ -228,11 +228,11 @@ class Usage { /** Pads [source] to [length] by adding spaces at the end. */ String padRight(String source, int length) { final result = new StringBuffer(); - result.add(source); + result.write(source); while (result.length < length) { - result.add(' '); + result.write(' '); } return result.toString(); -} \ No newline at end of file +} From eeadaa7ea8d0c42c53092c0839b520ccc22a4fc0 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Wed, 27 Feb 2013 08:45:04 +0000 Subject: [PATCH 040/263] Change new List(n) to return fixed length list. Deprecate List.fixedLength, add List.filled. Make Iterable.toList and List.from take "growable" argument, defaulting to false. Review URL: https://codereview.chromium.org//12328104 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19112 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 29da1e26..7760a99f 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -307,7 +307,7 @@ class ArgParser { * flags and options defined by this parser, and returns the result. */ ArgResults parse(List args) => - new Parser(null, this, args.toList()).parse(); + new Parser(null, this, args.toList(growable: true)).parse(); /** * Generates a string displaying usage information for the defined options. From 641dc5fc9df68651d8b3b3a0a0dcbb3d32603177 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Wed, 27 Feb 2013 09:48:17 +0000 Subject: [PATCH 041/263] Make arg parser return growable list of rest arguments. This allows the user to remove them when he's done, which is what is currently being done. Review URL: https://codereview.chromium.org//12316155 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19119 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/src/parser.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index d35f9ea4..5c372ab0 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -95,7 +95,7 @@ class Parser { }); // Add in the leftover arguments we didn't parse to the innermost command. - var rest = args.toList(); + var rest = args.toList(growable: true); args.clear(); return new ArgResults(results, commandName, commandResults, rest); } From ae5319c16e5fcb12edeee5f82f69f371eefbd8d9 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Mon, 4 Mar 2013 14:55:40 +0000 Subject: [PATCH 042/263] Make List.from and Iterable.toList default to not growable. Review URL: https://codereview.chromium.org//12401002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19391 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- pkgs/args/lib/src/parser.dart | 2 +- pkgs/args/lib/src/usage.dart | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 7760a99f..6bdcb7e9 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -307,7 +307,7 @@ class ArgParser { * flags and options defined by this parser, and returns the result. */ ArgResults parse(List args) => - new Parser(null, this, args.toList(growable: true)).parse(); + new Parser(null, this, args.toList()).parse(); /** * Generates a string displaying usage information for the defined options. @@ -397,6 +397,6 @@ class ArgResults { } /** Get the names of the options as a [Collection]. */ - Collection get options => _options.keys.toList(); + Collection get options => _options.keys.toList(growable: false); } diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 5c372ab0..d35f9ea4 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -95,7 +95,7 @@ class Parser { }); // Add in the leftover arguments we didn't parse to the innermost command. - var rest = args.toList(growable: true); + var rest = args.toList(); args.clear(); return new ArgResults(results, commandName, commandResults, rest); } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index b83a2d51..45893a5e 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -73,7 +73,7 @@ class Usage { if (option.help != null) write(2, option.help); if (option.allowedHelp != null) { - var allowedNames = option.allowedHelp.keys.toList(); + var allowedNames = option.allowedHelp.keys.toList(growable: false); allowedNames.sort(); newline(); for (var name in allowedNames) { From fc9e547bbe76f91b2a3d9479abd465690140317f Mon Sep 17 00:00:00 2001 From: "floitsch@google.com" Date: Tue, 12 Mar 2013 17:34:49 +0000 Subject: [PATCH 043/263] Rename XMatching to XWhere. For example firstMatching -> firstWhere. BUG= http://dartbug.com/8664 BUG= http://dartbug.com/8337 Review URL: https://codereview.chromium.org//12537009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@19880 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 6bdcb7e9..304827b4 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -331,7 +331,7 @@ class ArgParser { * that abbreviation. */ Option findByAbbreviation(String abbr) { - return options.values.firstMatching((option) => option.abbreviation == abbr, + return options.values.firstWhere((option) => option.abbreviation == abbr, orElse: () => null); } } From 9a6a650f0b0fe7c0bd5dd96b5e171e6b67eeccf3 Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Thu, 14 Mar 2013 18:00:21 +0000 Subject: [PATCH 044/263] pkg/args Option should be more strict about names and abbreviations Should not allow: space, \t, \r, \n, single- and double-quote, slashes Should not allow empty string Should not allow '-' as the beginning character name should not be allowed to be null (although it's fine for abbreviation) BUG=https://code.google.com/p/dart/issues/detail?id=8965 Review URL: https://codereview.chromium.org//12472019 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@20040 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 35 ++++++++++++--- pkgs/args/test/args_test.dart | 85 ++++++++++++++++++++++++++++++++++- 2 files changed, 111 insertions(+), 9 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 304827b4..48cef164 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -285,11 +285,6 @@ class ArgParser { // Make sure the abbreviation isn't too long or in use. if (abbr != null) { - if (abbr.length > 1) { - throw new ArgumentError( - 'Abbreviation "$abbr" is longer than one character.'); - } - var existing = findByAbbreviation(abbr); if (existing != null) { throw new ArgumentError( @@ -342,7 +337,7 @@ class ArgParser { class Option { final String name; final String abbreviation; - final List allowed; + final List allowed; final defaultValue; final Function callback; final String help; @@ -353,7 +348,33 @@ class Option { Option(this.name, this.abbreviation, this.help, this.allowed, this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, - this.negatable, this.allowMultiple: false}); + this.negatable, this.allowMultiple: false}) { + + if (name.isEmpty) { + throw new ArgumentError('Name cannot be empty.'); + } else if (name.startsWith('-')) { + throw new ArgumentError('Name $name cannot start with "-".'); + } + + // Ensure name does not contain any invalid characters. + if (_invalidChars.hasMatch(name)) { + throw new ArgumentError('Name "$name" contains invalid characters.'); + } + + if (abbreviation != null) { + if (abbreviation.length != 1) { + throw new ArgumentError('Abbreviation must be null or have length 1.'); + } else if(abbreviation == '-') { + throw new ArgumentError('Abbreviation cannot be "-".'); + } + + if (_invalidChars.hasMatch(abbreviation)) { + throw new ArgumentError('Abbreviation is an invalid character.'); + } + } + } + + static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); } /** diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index d807291e..cf31cc26 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -32,6 +32,24 @@ main() { var parser = new ArgParser(); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); }); + + test('throws ArgumentError if a flag name is invalid', () { + var parser = new ArgParser(); + + for(var name in _INVALID_OPTIONS) { + var reason = '${Error.safeToString(name)} is not valid'; + throwsIllegalArg(() => parser.addFlag(name), reason: reason); + } + }); + + test('accepts valid flag names', () { + var parser = new ArgParser(); + + for(var name in _VALID_OPTIONS) { + var reason = '${Error.safeToString(name)} is valid'; + expect(() => parser.addFlag(name), returnsNormally, reason: reason); + } + }); }); group('ArgParser.addOption()', () { @@ -58,6 +76,46 @@ main() { var parser = new ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); }); + + test('throws ArgumentError if the abbreviation is empty', () { + var parser = new ArgParser(); + throwsIllegalArg(() => parser.addOption('flummox', abbr: '')); + }); + + test('throws ArgumentError if the abbreviation is an invalid value', () { + var parser = new ArgParser(); + for(var name in _INVALID_OPTIONS.where((v) => v != null)) { + throwsIllegalArg(() => parser.addOption('flummox', abbr: name)); + } + }); + + test('throws ArgumentError if the abbreviation is a dash', () { + var parser = new ArgParser(); + throwsIllegalArg(() => parser.addOption('flummox', abbr: '-')); + }); + + test('allows explict null value for "abbr"', () { + var parser = new ArgParser(); + expect(() => parser.addOption('flummox', abbr: null), returnsNormally); + }); + + test('throws ArgumentError if an option name is invalid', () { + var parser = new ArgParser(); + + for(var name in _INVALID_OPTIONS) { + var reason = '${Error.safeToString(name)} is not valid'; + throwsIllegalArg(() => parser.addOption(name), reason: reason); + } + }); + + test('accepts valid option names', () { + var parser = new ArgParser(); + + for(var name in _VALID_OPTIONS) { + var reason = '${Error.safeToString(name)} is valid'; + expect(() => parser.addOption(name), returnsNormally, reason: reason); + } + }); }); group('ArgParser.getDefault()', () { @@ -155,10 +213,33 @@ main() { }); } -throwsIllegalArg(function) { - expect(function, throwsArgumentError); +throwsIllegalArg(function, {String reason: null}) { + expect(function, throwsArgumentError, reason: reason); } throwsFormat(ArgParser parser, List args) { expect(() => parser.parse(args), throwsFormatException); } + +const _INVALID_OPTIONS = const [ + ' ', '', '-', '--', '--foo', + ' with space', + 'with\ttab', + 'with\rcarriage\rreturn', + 'with\nline\nfeed', + "'singlequotes'", + '"doublequotes"', + 'back\\slash', + 'forward/slash' +]; + +const _VALID_OPTIONS = const [ + 'a' // one char + 'contains-dash', + 'contains_underscore', + 'ends-with-dash-', + 'contains--doubledash--', + '1starts-with-number', + 'contains-a-1number', + 'ends-with-a-number8' +]; From 3303a3e33946aa459c79625e6784f1099f99d0ee Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Wed, 20 Mar 2013 19:41:10 +0000 Subject: [PATCH 045/263] Moved pkg pubspecs to use dev_dependencies where appropriate BUG=https://code.google.com/p/dart/issues/detail?id=9309 Review URL: https://codereview.chromium.org//12962003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@20285 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 959c189c..14802e70 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -6,7 +6,5 @@ description: > Libraries for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. - -dependencies: - unittest: - sdk: unittest +dev_dependencies: + unittest: any From 28ddb14a8ab054d6630326afe19e5bde3fdeb104 Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Fri, 12 Apr 2013 09:34:52 +0000 Subject: [PATCH 046/263] Remove Collection, Collections and clean up List/Set/Queue implementations of retain/remove. BUG= Review URL: https://codereview.chromium.org//14173003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@21338 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 48cef164..1337b46a 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -417,7 +417,7 @@ class ArgResults { return _options[name]; } - /** Get the names of the options as a [Collection]. */ - Collection get options => _options.keys.toList(growable: false); + /** Get the names of the options as an [Iterable]. */ + Iterable get options => _options.keys; } From 0a2d0bb8ba19173a57b550ab050caed144039ca6 Mon Sep 17 00:00:00 2001 From: "sethladd@google.com" Date: Fri, 19 Apr 2013 20:51:10 +0000 Subject: [PATCH 047/263] add installation instructions to pkg packages BUG= Review URL: https://codereview.chromium.org//14188048 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@21770 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 1337b46a..be1a6efa 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -6,6 +6,19 @@ * This library lets you define parsers for parsing raw command-line arguments * into a set of options and values using [GNU][] and [POSIX][] style options. * + * ## Installing ## + * + * Use [pub][] to install this package. Add the following to your `pubspec.yaml` + * file. + * + * dependencies: + * args: any + * + * Then run `pub install`. + * + * For more information, see the + * [args package on pub.dartlang.org](http://pub.dartlang.org/packages/args). + * * ## Defining options ## * * To use this library, you create an [ArgParser] object which will contain @@ -209,6 +222,7 @@ * * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces + * [pub]: http://pub.dartlang.org */ library args; From 7eb338964657a0c8db50af8d66580ad145368088 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 21 May 2013 23:20:27 +0000 Subject: [PATCH 048/263] Allow passing in an existing ArgParser for a command. R=gram@google.com Review URL: https://codereview.chromium.org//15621002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@22973 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 53 +++++++++++++++++++++++++------- pkgs/args/test/command_test.dart | 15 +++++++++ 2 files changed, 57 insertions(+), 11 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index be1a6efa..3898b9fd 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -169,17 +169,46 @@ * option passed to the command. You can add a command like so: * * var parser = new ArgParser(); - * var command = parser.addCommand("commit"); + * var command = parser.addCommand('commit'); + * + * It returns another [ArgParser] which you can then use to define options + * specific to that command. If you already have an [ArgParser] for the + * command's options, you can pass it to [addCommand]: + * + * var parser = new ArgParser(); + * var command = new ArgParser(); + * parser.addCommand('commit', command); + * + * The [ArgParser] for a command can then define whatever options or flags: + * * 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. + * You can add multiple commands to the same parser so that a user can select + * one from a range of possible commands. 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 * + * Options for a command must appear after the command in the argument list. + * For example, given the above parser, "git -a commit" is *not* valid. The + * parser will try to find the right-most command that accepts an option. For + * example: + * + * var parser = new ArgParser(); + * parser.addFlag('all', abbr: 'a'); + * var command = new ArgParser().addCommand('commit'); + * parser.addFlag('all', abbr: 'a'); + * var results = parser.parse(['commit', '-a']); + * print(results.command['a']); // true + * + * Here, both the top-level parser and the "commit" command can accept a "-a" + * (which is probably a bad command line interface, admittedly). In that case, + * when "-a" appears after "commit", it will be applied to that command. If it + * appears to the left of "commit", it will be given to the top-level parser. + * * ## Displaying usage ## * * This library can also be used to automatically generate nice usage help @@ -248,19 +277,21 @@ class ArgParser { ArgParser(); /** - * 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. + * Defines a command. + * + * A command is a named argument which may in turn define its own options and + * subcommands using the given parser. If [parser] is omitted, implicitly + * creates a new one. Returns the parser for the command. */ - ArgParser addCommand(String name) { + ArgParser addCommand(String name, [ArgParser parser]) { // Make sure the name isn't in use. if (commands.containsKey(name)) { throw new ArgumentError('Duplicate command "$name".'); } - var command = new ArgParser(); - commands[name] = command; - return command; + if (parser == null) parser = new ArgParser(); + commands[name] = parser; + return parser; } /** diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 50794256..6115ab1c 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -9,6 +9,21 @@ import 'package:args/args.dart'; main() { group('ArgParser.addCommand()', () { + test('creates a new ArgParser if none is given', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + expect(parser.commands['install'], equals(command)); + expect(command is ArgParser, isTrue); + }); + + test('uses the command parser if given one', () { + var parser = new ArgParser(); + var command = new ArgParser(); + var result = parser.addCommand('install', command); + expect(parser.commands['install'], equals(command)); + expect(result, equals(command)); + }); + test('throws on a duplicate command name', () { var parser = new ArgParser(); parser.addCommand('install'); From 3522e9e26f8aaae0d3e366b47f886bed746c5967 Mon Sep 17 00:00:00 2001 From: "antonm@google.com" Date: Wed, 29 May 2013 08:34:52 +0000 Subject: [PATCH 049/263] Switch from DRT to content shell. R=kustermann@google.com, ricow@google.com Review URL: https://codereview.chromium.org//15755017 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@23328 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/example/test_runner.dart | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index d0a6ed8e..74750ae9 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -40,8 +40,9 @@ main() { allowedHelp: { 'vm': 'Run Dart code on the standalone dart vm.', 'd8': 'Run JavaScript from the command line using v8.', + // TODO(antonm): rename flag. 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' - 'DumpRenderTree.', + 'content shell.', 'dartium': 'Run Dart or JavaScript in Dartium.', 'ff': 'Run JavaScript in Firefox', 'chrome': 'Run JavaScript in Chrome', @@ -116,7 +117,8 @@ is 'dart file.dart' and you specify special command defaultsTo: false); parser.addOption('dart', help: 'Path to dart executable'); - parser.addOption('drt', help: 'Path to DumpRenderTree executable'); + // TODO(antonm): rename the option. + parser.addOption('drt', help: 'Path to content shell executable'); parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); parser.addFlag('batch', abbr: 'b', From 1cd6f73ba5e12fc59e60f805cf7f500cbb7cca8c Mon Sep 17 00:00:00 2001 From: "amouravski@google.com" Date: Mon, 17 Jun 2013 19:15:15 +0000 Subject: [PATCH 050/263] Moved the Option class to its own file in anticipation of working on it. R=rnystrom@google.com Review URL: https://codereview.chromium.org//16802002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24107 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 48 ++-------------------------------- pkgs/args/lib/src/options.dart | 47 +++++++++++++++++++++++++++++++++ 2 files changed, 49 insertions(+), 46 deletions(-) create mode 100644 pkgs/args/lib/src/options.dart diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 3898b9fd..9e6252a5 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -257,6 +257,8 @@ library args; import 'src/parser.dart'; import 'src/usage.dart'; +import 'src/options.dart'; +export 'src/options.dart'; /** * A class for taking a list of raw command line arguments and parsing out @@ -376,52 +378,6 @@ class ArgParser { } } -/** - * A command-line option. Includes both flags and options which take a value. - */ -class Option { - final String name; - final String abbreviation; - final List allowed; - final defaultValue; - final Function callback; - final String help; - final Map allowedHelp; - final bool isFlag; - final bool negatable; - final bool allowMultiple; - - Option(this.name, this.abbreviation, this.help, this.allowed, - this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, - this.negatable, this.allowMultiple: false}) { - - if (name.isEmpty) { - throw new ArgumentError('Name cannot be empty.'); - } else if (name.startsWith('-')) { - throw new ArgumentError('Name $name cannot start with "-".'); - } - - // Ensure name does not contain any invalid characters. - if (_invalidChars.hasMatch(name)) { - throw new ArgumentError('Name "$name" contains invalid characters.'); - } - - if (abbreviation != null) { - if (abbreviation.length != 1) { - throw new ArgumentError('Abbreviation must be null or have length 1.'); - } else if(abbreviation == '-') { - throw new ArgumentError('Abbreviation cannot be "-".'); - } - - if (_invalidChars.hasMatch(abbreviation)) { - throw new ArgumentError('Abbreviation is an invalid character.'); - } - } - } - - static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); -} - /** * The results of parsing a series of command line arguments using * [ArgParser.parse()]. Includes the parsed options and any remaining unparsed diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart new file mode 100644 index 00000000..00c0ad8d --- /dev/null +++ b/pkgs/args/lib/src/options.dart @@ -0,0 +1,47 @@ +library options; + +/** + * A command-line option. Includes both flags and options which take a value. + */ +class Option { + final String name; + final String abbreviation; + final List allowed; + final defaultValue; + final Function callback; + final String help; + final Map allowedHelp; + final bool isFlag; + final bool negatable; + final bool allowMultiple; + + Option(this.name, this.abbreviation, this.help, this.allowed, + this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, + this.negatable, this.allowMultiple: false}) { + + if (name.isEmpty) { + throw new ArgumentError('Name cannot be empty.'); + } else if (name.startsWith('-')) { + throw new ArgumentError('Name $name cannot start with "-".'); + } + + // Ensure name does not contain any invalid characters. + if (_invalidChars.hasMatch(name)) { + throw new ArgumentError('Name "$name" contains invalid characters.'); + } + + if (abbreviation != null) { + if (abbreviation.length != 1) { + throw new ArgumentError('Abbreviation must be null or have length 1.'); + } else if(abbreviation == '-') { + throw new ArgumentError('Abbreviation cannot be "-".'); + } + + if (_invalidChars.hasMatch(abbreviation)) { + throw new ArgumentError('Abbreviation is an invalid character.'); + } + } + } + + static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); +} From f048085e8cc71d007c26360ff79de87fd5f152f5 Mon Sep 17 00:00:00 2001 From: "amouravski@google.com" Date: Wed, 19 Jun 2013 23:05:42 +0000 Subject: [PATCH 051/263] Added new style of tests for parser. R=rnystrom@google.com Review URL: https://codereview.chromium.org//17333004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24207 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/parse_all_test.dart | 62 ++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 pkgs/args/test/parse_all_test.dart diff --git a/pkgs/args/test/parse_all_test.dart b/pkgs/args/test/parse_all_test.dart new file mode 100644 index 00000000..a9210464 --- /dev/null +++ b/pkgs/args/test/parse_all_test.dart @@ -0,0 +1,62 @@ +// 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. + +library parse_all_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +main() { + group('ArgParser.parse() starting with a non-option', () { + test('followed by flag', () { + var parser = new ArgParser()..addFlag('flag'); + var args = ['A', '--flag']; + + var results = parser.parse(args); + expect(results['flag'], isFalse); + expect(results.rest, orderedEquals(args)); + }); + + test('followed by option', () { + var parser = new ArgParser()..addOption('opt'); + var args = ['A', '--opt']; + + var results = parser.parse(args); + expect(results['opt'], isNull); + expect(results.rest, orderedEquals(args)); + }); + + test('followed by option and value', () { + var parser = new ArgParser()..addOption('opt'); + var args = ['A', '--opt', 'V']; + + var results = parser.parse(args); + expect(results['opt'], isNull); + expect(results.rest, orderedEquals(args)); + }); + + test('followed by unknown flag', () { + var parser = new ArgParser(); + var args = ['A', '--xflag']; + var results = parser.parse(args); + expect(results.rest, orderedEquals(args)); + }); + + test('followed by unknown option and value', () { + var parser = new ArgParser(); + var args = ['A', '--xopt', 'V']; + var results = parser.parse(args); + expect(results.rest, orderedEquals(args)); + }); + + test('followed by command', () { + var parser = new ArgParser()..addCommand('com'); + var args = ['A', 'com']; + + var results = parser.parse(args); + expect(results.command, isNull); + expect(results.rest, orderedEquals(args)); + }); + }); +} From 036bc57fc2bdfb091c80674a4cb833923fc00b5b Mon Sep 17 00:00:00 2001 From: "amouravski@google.com" Date: Mon, 24 Jun 2013 20:55:13 +0000 Subject: [PATCH 052/263] Added the continueParsing option to ArgParser. R=rnystrom@google.com Review URL: https://codereview.chromium.org//12545013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@24366 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 15 +++++++++++-- pkgs/args/lib/src/parser.dart | 29 ++++++++++++++++++++----- pkgs/args/test/parse_all_test.dart | 35 +++++++++++++++--------------- 3 files changed, 54 insertions(+), 25 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 9e6252a5..668577cc 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -347,9 +347,20 @@ class ArgParser { /** * Parses [args], a list of command-line arguments, matches them against the * flags and options defined by this parser, and returns the result. + * + * If [allowTrailingOptions] is set, the parser will continue parsing even + * after it finds an argument that is neither an option nor a command. + * This allows options to be specified after regular arguments. + * + * [allowTrailingOptions] is false by default, so when a non-option, + * non-command argument is encountered, it and all remaining arguments, + * even those that look like options are passed to the innermost command. */ - ArgResults parse(List args) => - new Parser(null, this, args.toList()).parse(); + ArgResults parse(List args, {bool allowTrailingOptions}) { + if (allowTrailingOptions == null) allowTrailingOptions = false; + return new Parser(null, this, args.toList(), null, null, + allowTrailingOptions: allowTrailingOptions).parse(); + } /** * Generates a string displaying usage information for the defined options. diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index d35f9ea4..37a8e41e 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -28,16 +28,26 @@ class Parser { */ final Parser parent; + /** If `true`, parsing will continue after a non-option argument. */ + final bool allowTrailingOptions; + /** The grammar being parsed. */ final ArgParser grammar; /** The arguments being parsed. */ final List args; + /** The remaining non-option, non-command arguments. */ + final rest = []; + /** The accumulated parsed options. */ final Map results = {}; - Parser(this.commandName, this.grammar, this.args, [this.parent]); + Parser(this.commandName, this.grammar, this.args, this.parent, rest, + {this.allowTrailingOptions: false}) { + if (rest != null) this.rest.addAll(rest); + } + /** The current argument being parsed. */ String get current => args[0]; @@ -67,10 +77,15 @@ class Parser { // options so that commands can have option-like names. var command = grammar.commands[current]; if (command != null) { + validate(rest.isEmpty, 'Cannot specify arguments before a command.'); var commandName = args.removeAt(0); - var commandParser = new Parser(commandName, command, args, this); + var commandParser = new Parser(commandName, command, args, this, rest, + allowTrailingOptions: allowTrailingOptions); commandResults = commandParser.parse(); - continue; + + // All remaining arguments were passed to command so clear them here. + rest.clear(); + break; } // Try to parse the current argument as an option. Note that the order @@ -79,8 +94,10 @@ class Parser { if (parseAbbreviation(this)) continue; if (parseLongOption()) continue; - // If we got here, the argument doesn't look like an option, so stop. - break; + // This argument is neither option nor command, so stop parsing unless + // the [allowTrailingOptions] option is set. + if (!allowTrailingOptions) break; + rest.add(args.removeAt(0)); } // Set unspecified multivalued arguments to their default value, @@ -95,7 +112,7 @@ class Parser { }); // Add in the leftover arguments we didn't parse to the innermost command. - var rest = args.toList(); + rest.addAll(args); args.clear(); return new ArgResults(results, commandName, commandResults, rest); } diff --git a/pkgs/args/test/parse_all_test.dart b/pkgs/args/test/parse_all_test.dart index a9210464..8498ae67 100644 --- a/pkgs/args/test/parse_all_test.dart +++ b/pkgs/args/test/parse_all_test.dart @@ -8,55 +8,56 @@ import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; main() { - group('ArgParser.parse() starting with a non-option', () { + group('ArgParser.parse(allowTrailingOptions: true) ' + 'starting with a non-option', () { test('followed by flag', () { var parser = new ArgParser()..addFlag('flag'); var args = ['A', '--flag']; - var results = parser.parse(args); - expect(results['flag'], isFalse); - expect(results.rest, orderedEquals(args)); + var resultsAll = parser.parse(args, allowTrailingOptions: true); + expect(resultsAll['flag'], isTrue); + expect(resultsAll.rest, equals(['A'])); }); test('followed by option', () { var parser = new ArgParser()..addOption('opt'); var args = ['A', '--opt']; - var results = parser.parse(args); - expect(results['opt'], isNull); - expect(results.rest, orderedEquals(args)); + expectThrows(parser, args); }); test('followed by option and value', () { var parser = new ArgParser()..addOption('opt'); var args = ['A', '--opt', 'V']; - var results = parser.parse(args); - expect(results['opt'], isNull); - expect(results.rest, orderedEquals(args)); + var resultsAll = parser.parse(args, allowTrailingOptions: true); + expect(resultsAll['opt'], equals('V')); + expect(resultsAll.rest, equals(['A'])); }); test('followed by unknown flag', () { var parser = new ArgParser(); var args = ['A', '--xflag']; - var results = parser.parse(args); - expect(results.rest, orderedEquals(args)); + + expectThrows(parser, args); }); test('followed by unknown option and value', () { var parser = new ArgParser(); var args = ['A', '--xopt', 'V']; - var results = parser.parse(args); - expect(results.rest, orderedEquals(args)); + + expectThrows(parser, args); }); test('followed by command', () { var parser = new ArgParser()..addCommand('com'); var args = ['A', 'com']; - var results = parser.parse(args); - expect(results.command, isNull); - expect(results.rest, orderedEquals(args)); + expectThrows(parser, args); }); }); } +expectThrows(ArgParser parser, List args) => + expect(() => parser.parse(args, allowTrailingOptions: true), + throwsFormatException, + reason: "with allowTrailingOptions: true"); From 691f77606a16812ed1eba8333d05c85a0f09f942 Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Tue, 23 Jul 2013 16:56:13 +0000 Subject: [PATCH 053/263] pkg/args: expose immutable collections BUG= https://code.google.com/p/dart/issues/detail?id=10127 R=rnystrom@google.com Review URL: https://codereview.chromium.org//19810004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@25347 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 31 ++++++++++++++++++++++--------- pkgs/args/lib/src/options.dart | 12 +++++++++--- pkgs/args/lib/src/parser.dart | 6 +++--- pkgs/args/lib/src/usage.dart | 8 ++++---- pkgs/args/pubspec.yaml | 2 ++ 5 files changed, 40 insertions(+), 19 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 668577cc..09506546 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -255,6 +255,8 @@ */ library args; +import 'package:unmodifiable_collection/unmodifiable_collection.dart'; + import 'src/parser.dart'; import 'src/usage.dart'; import 'src/options.dart'; @@ -265,18 +267,28 @@ export 'src/options.dart'; * options and flags from them. */ class ArgParser { + final Map _options; + final Map _commands; + /** * The options that have been defined for this parser. */ - final Map options = {}; + final Map options; /** * The commands that have been defined for this parser. */ - final Map commands = {}; + final Map commands; /** Creates a new ArgParser. */ - ArgParser(); + factory ArgParser() => + new ArgParser._({}, {}); + + ArgParser._(Map options, Map commands) : + this._options = options, + this.options = new UnmodifiableMapView(options), + this._commands = commands, + this.commands = new UnmodifiableMapView(commands); /** * Defines a command. @@ -287,12 +299,12 @@ class ArgParser { */ ArgParser addCommand(String name, [ArgParser parser]) { // Make sure the name isn't in use. - if (commands.containsKey(name)) { + if (_commands.containsKey(name)) { throw new ArgumentError('Duplicate command "$name".'); } if (parser == null) parser = new ArgParser(); - commands[name] = parser; + _commands[name] = parser; return parser; } @@ -326,7 +338,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".'); } @@ -339,7 +351,7 @@ class ArgParser { } } - 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); } @@ -395,7 +407,7 @@ class ArgParser { * command line arguments. */ class ArgResults { - final Map _options; + final Map _options; /** * If these are the results for parsing a command's options, this will be @@ -417,7 +429,8 @@ class ArgResults { final List rest; /** Creates a new [ArgResults]. */ - ArgResults(this._options, this.name, this.command, this.rest); + ArgResults(this._options, this.name, this.command, List rest) + : this.rest = new UnmodifiableListView(rest); /** Gets the parsed command-line option named [name]. */ operator [](String name) { diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index 00c0ad8d..5f1f4840 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -1,5 +1,7 @@ library options; +import 'package:unmodifiable_collection/unmodifiable_collection.dart'; + /** * A command-line option. Includes both flags and options which take a value. */ @@ -15,9 +17,13 @@ class Option { final bool negatable; final bool allowMultiple; - Option(this.name, this.abbreviation, this.help, this.allowed, - this.allowedHelp, this.defaultValue, this.callback, {this.isFlag, - this.negatable, this.allowMultiple: false}) { + Option(this.name, this.abbreviation, this.help, List allowed, + Map allowedHelp, this.defaultValue, this.callback, + {this.isFlag, this.negatable, this.allowMultiple: false}) : + this.allowed = allowed == null ? + null : new UnmodifiableListView(allowed), + this.allowedHelp = allowedHelp == null ? + null : new UnmodifiableMapView(allowedHelp) { if (name.isEmpty) { throw new ArgumentError('Name cannot be empty.'); diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 37a8e41e..550fe63e 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -41,7 +41,7 @@ class Parser { final rest = []; /** The accumulated parsed options. */ - final Map results = {}; + final Map results = {}; Parser(this.commandName, this.grammar, this.args, this.parent, rest, {this.allowTrailingOptions: false}) { @@ -277,12 +277,12 @@ class Parser { * Called during parsing to validate the arguments. Throws a * [FormatException] if [condition] is `false`. */ - validate(bool condition, String message) { + void 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) { + void 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), diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 45893a5e..c3dd68ea 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -144,13 +144,13 @@ class Usage { columnWidths = [abbr, title]; } - newline() { + void newline() { newlinesNeeded++; currentColumn = 0; numHelpLines = 0; } - write(int column, String text) { + void write(int column, String text) { var lines = text.split('\n'); // Strip leading and trailing empty lines. @@ -167,7 +167,7 @@ class Usage { } } - writeLine(int column, String text) { + void writeLine(int column, String text) { // Write any pending newlines. while (newlinesNeeded > 0) { buffer.write('\n'); @@ -208,7 +208,7 @@ class Usage { } } - buildAllowedList(Option option) { + String buildAllowedList(Option option) { var allowedBuffer = new StringBuffer(); allowedBuffer.write('['); bool first = true; diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 14802e70..5e460f37 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -6,5 +6,7 @@ description: > Libraries for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. +dependencies: + unmodifiable_collection: any dev_dependencies: unittest: any From d121d7766720186352dea1bd28b8e2f5972f575c Mon Sep 17 00:00:00 2001 From: "kathyw@google.com" Date: Wed, 11 Sep 2013 15:35:01 +0000 Subject: [PATCH 054/263] Copyedit pass over args library description; fixed some example code R=mcampione@google.com, rnystrom@google.com Review URL: https://codereview.chromium.org//23603002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@27388 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 194 +++++++++++++++++++++------------------- 1 file changed, 100 insertions(+), 94 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 09506546..0b877cd8 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -3,141 +3,144 @@ // BSD-style license that can be found in the LICENSE file. /** - * This library lets you define parsers for parsing raw command-line arguments - * into a set of options and values using [GNU][] and [POSIX][] style options. + * Parser support for transforming raw command-line arguments into a set + * of options and values. * - * ## Installing ## + * This library supports [GNU][] and [POSIX][] style options, and it works + * in both server-side and client-side apps. * - * Use [pub][] to install this package. Add the following to your `pubspec.yaml` - * file. - * - * dependencies: - * args: any - * - * Then run `pub install`. - * - * For more information, see the + * For information on installing this library, see the * [args package on pub.dartlang.org](http://pub.dartlang.org/packages/args). + * Here's an example of importing this library: * - * ## Defining options ## + * import 'package:args/args.dart'; * - * To use this library, you create an [ArgParser] object which will contain - * the set of options you support: + * ## Defining options + * + * To use this library, first create an [ArgParser]: * * var parser = new ArgParser(); * - * Then you define a set of options on that parser using [addOption()] and - * [addFlag()]. The minimal way to create an option is: + * Then define a set of options on that parser using [addOption()] and + * [addFlag()]. Here's the minimal way to create an option named "name": * * parser.addOption('name'); * - * This creates an option named "name". Options must be given a value on the - * command line. If you have a simple on/off flag, you can instead use: + * When an option can only be set or unset (as opposed to taking a string + * value), use a flag: * * parser.addFlag('name'); * - * Flag options will, by default, accept a 'no-' prefix to negate the option. - * This can be disabled like so: + * Flag options, by default, accept a 'no-' prefix to negate the option. + * You can disable the 'no-' prefix using the `negatable` parameter: * * parser.addFlag('name', negatable: false); * - * (From here on out "option" will refer to both "regular" options and flags. - * In cases where the distinction matters, we'll use "non-flag option".) + * **Terminology note:** + * From here on out, the term _option_ refers to both regular options and + * flags. In cases where the distinction matters, this documentation uses + * the term _non-flag option._ * - * Options may have an optional single-character abbreviation: + * Options can have an optional single-character abbreviation, specified + * with the `abbr` parameter: * * parser.addOption('mode', abbr: 'm'); * parser.addFlag('verbose', abbr: 'v'); * - * They may also specify a default value. The default value will be used if the - * option isn't provided: + * Options can also have a default value, specified with the `defaultsTo` + * parameter. The default value is used when arguments don't specify the + * option. * * parser.addOption('mode', defaultsTo: 'debug'); * parser.addFlag('verbose', defaultsTo: false); * - * The default value for non-flag options can be any [String]. For flags, it - * must be a [bool]. + * The default value for non-flag options can be any [String]. For flags, + * it must be a [bool]. * - * To validate non-flag options, you may provide an allowed set of values. When - * you do, it will throw a [FormatException] when you parse the arguments if - * the value for an option is not in the allowed set: + * To validate a non-flag option, you can use the `allowed` parameter to + * provide an allowed set of values. When you do, the parser throws a + * [FormatException] if the value for an option is not in the allowed set. + * Here's an example of specifying allowed values: * * parser.addOption('mode', allowed: ['debug', 'release']); * - * You can provide a callback when you define an option. When you later parse - * a set of arguments, the callback for that option will be invoked with the - * value provided for it: + * You can use the `callback` parameter to associate a function with an + * option. Later, when parsing occurs, the callback function is invoked + * with the value of the option: * * parser.addOption('mode', callback: (mode) => print('Got mode $mode)); * parser.addFlag('verbose', callback: (verbose) { * if (verbose) print('Verbose'); * }); * - * The callback for each option will *always* be called when you parse a set of - * arguments. If the option isn't provided in the args, the callback will be - * passed the default value, or `null` if there is none set. + * The callbacks for all options are called whenever a set of arguments + * is parsed. If an option isn't provided in the args, its callback is + * passed the default value, or `null` if no default value is set. * - * ## Parsing arguments ## + * ## Parsing arguments * - * Once you have an [ArgParser] set up with some options and flags, you use it - * by calling [ArgParser.parse()] with a set of arguments: + * Once you have an [ArgParser] set up with some options and flags, you + * use it by calling [ArgParser.parse()] with a set of arguments: * * var results = parser.parse(['some', 'command', 'line', 'args']); * - * These will usually come from `new Options().arguments`, but you can pass in - * any list of strings. It returns an instance of [ArgResults]. This is a - * map-like object that will return the value of any parsed option. + * These arguments usually come from dart:io's Options class + * (`new Options().arguments`), but you can pass in any list of strings. + * The parse() method returns an instance of [ArgResults], a map-like + * object that contains the values of the parsed options. * * var parser = new ArgParser(); * parser.addOption('mode'); * parser.addFlag('verbose', defaultsTo: true); - * var results = parser.parse('['--mode', 'debug', 'something', 'else']); + * var results = parser.parse(['--mode', 'debug', 'something', 'else']); * * print(results['mode']); // debug * print(results['verbose']); // true * - * The [parse()] method will stop as soon as it reaches `--` or anything that - * it doesn't recognize as an option, flag, or option value. If there are still - * arguments left, they will be provided to you in - * [ArgResults.rest]. + * By default, the parse() method stops as soon as it reaches `--` by itself + * or anything that the parser doesn't recognize as an option, flag, or + * option value. If arguments still remain, they go into [ArgResults.rest]. * * print(results.rest); // ['something', 'else'] * - * ## Specifying options ## + * To continue to parse options found after non-option arguments, call + * parse() with `allowTrailingOptions: true`. + * + * ## Specifying options * - * To actually pass in options and flags on the command line, use GNU or POSIX - * style. If you define an option like: + * To actually pass in options and flags on the command line, use GNU or + * POSIX style. Consider this option: * * parser.addOption('name', abbr: 'n'); * - * Then a value for it can be specified on the command line using any of: + * You can specify its value on the command line using any of the following: * * --name=somevalue * --name somevalue * -nsomevalue * -n somevalue * - * Given this flag: + * Consider this flag: * * parser.addFlag('name', abbr: 'n'); * - * You can set it on using one of: + * You can set it to true using one of the following: * * --name * -n * - * Or set it off using: + * You can set it to false using the following: * * --no-name * - * Multiple flag abbreviation can also be collapsed into a single argument. If - * you define: + * Multiple flag abbreviations can be collapsed into a single argument. Say + * you define these flags: * * parser.addFlag('verbose', abbr: 'v'); * parser.addFlag('french', abbr: 'f'); * parser.addFlag('iambic-pentameter', abbr: 'i'); * - * Then all three flags could be set using: + * You can set all three flags at once: * * -vfi * @@ -149,9 +152,9 @@ * var results = parser.parse(['--mode', 'on', '--mode', 'off']); * print(results['mode']); // prints 'off' * - * If you need multiple values, set the [allowMultiple] flag. In that - * case the option can occur multiple times and when parsing arguments a - * List of values will be returned: + * If you need multiple values, set the `allowMultiple` parameter. In that + * case the option can occur multiple times, and the parse() method returns + * a list of values: * * var parser = new ArgParser(); * parser.addOption('mode', allowMultiple: true); @@ -160,68 +163,73 @@ * * ## 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: + * In addition to *options*, you can also define *commands*. A command is + * a named argument that has its own set of options. For example, consider + * this shell command: * * $ 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: + * The executable is `git`, the command is `commit`, and the `-a` option is + * an option passed to the command. You can add a command using the + * [addCommand] method: * * var parser = new ArgParser(); * var command = parser.addCommand('commit'); * - * It returns another [ArgParser] which you can then use to define options - * specific to that command. If you already have an [ArgParser] for the - * command's options, you can pass it to [addCommand]: + * The addCommand() method returns another [ArgParser], which you can then + * use to define options specific to that command. If you already have an + * [ArgParser] for the command's options, you can pass it to addCommand: * * var parser = new ArgParser(); * var command = new ArgParser(); * parser.addCommand('commit', command); * - * The [ArgParser] for a command can then define whatever options or flags: + * The [ArgParser] for a command can then define options or flags: * * command.addFlag('all', abbr: 'a'); * * You can add multiple commands to the same parser so that a user can select - * one from a range of possible commands. When an argument list is parsed, + * one from a range of possible commands. When parsing an argument list, * 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 + * print(results.command.name); // "commit" + * print(results.command['all']); // true * * Options for a command must appear after the command in the argument list. * For example, given the above parser, "git -a commit" is *not* valid. The - * parser will try to find the right-most command that accepts an option. For + * parser tries to find the right-most command that accepts an option. For * example: * * var parser = new ArgParser(); * parser.addFlag('all', abbr: 'a'); - * var command = new ArgParser().addCommand('commit'); - * parser.addFlag('all', abbr: 'a'); + * var command = parser.addCommand('commit'); + * command.addFlag('all', abbr: 'a'); + * * var results = parser.parse(['commit', '-a']); - * print(results.command['a']); // true + * print(results.command['all']); // true * - * Here, both the top-level parser and the "commit" command can accept a "-a" - * (which is probably a bad command line interface, admittedly). In that case, - * when "-a" appears after "commit", it will be applied to that command. If it - * appears to the left of "commit", it will be given to the top-level parser. + * Here, both the top-level parser and the "commit" command can accept a + * "-a" (which is probably a bad command line interface, admittedly). In + * that case, when "-a" appears after "commit", it is applied to that + * command. If it appears to the left of "commit", it is given to the + * top-level parser. * - * ## Displaying usage ## + * ## 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 - * will also want to provide some help text when you create your options. To - * define help text for the entire option, do: + * You can automatically generate nice help text, suitable for use as the + * output of `--help`. To display good usage information, you should + * provide some help text when you create your options. + * + * To define help text for an entire option, use the `help` parameter: * * parser.addOption('mode', help: 'The compiler configuration', * allowed: ['debug', 'release']); * parser.addFlag('verbose', help: 'Show additional diagnostic info'); * * For non-flag options, you can also provide detailed help for each expected - * value using a map: + * value by using the `allowedHelp` parameter: * * parser.addOption('arch', help: 'The architecture to compile for', * allowedHelp: { @@ -229,11 +237,11 @@ * 'arm': 'ARM Holding 32-bit chip' * }); * - * If you define a set of options like the above, then calling this: + * To display the help, use the ArgParser getUsage() method: * * print(parser.getUsage()); - * - * Will display something like: + * + * The resulting string looks something like this: * * --mode The compiler configuration * [debug, release] @@ -244,14 +252,12 @@ * [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 - * options. + * To assist the formatting of the usage help, single-line help text is + * followed by a single new line. Options with multi-line help text are + * followed by two new lines. This provides spatial diversity between options. * * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces - * [pub]: http://pub.dartlang.org */ library args; From 08ecc33c8af0a4a74996fd18d601f8df3e45328b Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 24 Sep 2013 21:01:36 +0000 Subject: [PATCH 055/263] Add support for hidden options to pkg/args. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//23913009 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@27853 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 9 +++++---- pkgs/args/lib/src/options.dart | 4 +++- pkgs/args/lib/src/usage.dart | 2 ++ pkgs/args/test/usage_test.dart | 14 ++++++++++++++ 4 files changed, 24 insertions(+), 5 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 0b877cd8..a4db2046 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -334,15 +334,16 @@ class ArgParser { */ void addOption(String name, {String abbr, String help, List allowed, Map allowedHelp, String defaultsTo, - void callback(value), bool allowMultiple: false}) { + void callback(value), bool allowMultiple: false, bool hide: false}) { _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, - callback, isFlag: false, allowMultiple: allowMultiple); + callback, isFlag: false, allowMultiple: allowMultiple, + hide: hide); } void _addOption(String name, String abbr, String help, List allowed, Map allowedHelp, defaultsTo, void callback(value), {bool isFlag, bool negatable: false, - bool allowMultiple: false}) { + bool allowMultiple: false, bool hide: false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { throw new ArgumentError('Duplicate option "$name".'); @@ -359,7 +360,7 @@ class ArgParser { _options[name] = new Option(name, abbr, help, allowed, allowedHelp, defaultsTo, callback, isFlag: isFlag, negatable: negatable, - allowMultiple: allowMultiple); + allowMultiple: allowMultiple, hide: hide); } /** diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index 5f1f4840..f47bd61a 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -16,10 +16,12 @@ class Option { final bool isFlag; final bool negatable; final bool allowMultiple; + final bool hide; Option(this.name, this.abbreviation, this.help, List allowed, Map allowedHelp, this.defaultValue, this.callback, - {this.isFlag, this.negatable, this.allowMultiple: false}) : + {this.isFlag, this.negatable, this.allowMultiple: false, + this.hide: false}) : this.allowed = allowed == null ? null : new UnmodifiableListView(allowed), this.allowedHelp = allowedHelp == null ? diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index c3dd68ea..3244c4e5 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -67,6 +67,8 @@ class Usage { calculateColumnWidths(); args.options.forEach((name, option) { + if (option.hide) return; + write(0, getAbbreviation(option)); write(1, getLongOption(option)); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 6068f321..27b03894 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -167,6 +167,20 @@ main() { [spades] Swords of a soldier '''); }); + + test("hidden flags don't appear in the help", () { + var parser = new ArgParser(); + parser.addOption('first', help: 'The first option'); + parser.addOption('second', hide: true); + parser.addOption('third', help: 'The third option'); + + + validateUsage(parser, + ''' + --first The first option + --third The third option + '''); + }); }); } From fd0e7308f61fc858ad45122ed8f700d87119e81b Mon Sep 17 00:00:00 2001 From: "whesse@google.com" Date: Wed, 30 Oct 2013 15:17:01 +0000 Subject: [PATCH 056/263] Remove uses of Options from pkg, samples, tests, and third_party directories. BUG= R=sgjesse@google.com Review URL: https://codereview.chromium.org//52573002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29552 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index a4db2046..ea7f2318 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -84,8 +84,8 @@ * * var results = parser.parse(['some', 'command', 'line', 'args']); * - * These arguments usually come from dart:io's Options class - * (`new Options().arguments`), but you can pass in any list of strings. + * These arguments usually come from the arguments to main + * (`main(List arguments`), but you can pass in any list of strings. * The parse() method returns an instance of [ArgResults], a map-like * object that contains the values of the parsed options. * From e0e769d94eef8b3cb4ac6551466a4dd156ae0159 Mon Sep 17 00:00:00 2001 From: "jmesserly@google.com" Date: Wed, 6 Nov 2013 03:27:58 +0000 Subject: [PATCH 057/263] add versions and constraints for packages and samples - all packages at 0.9.0, except "analyzer" which had a version already - dependencies at ">=0.9.0 <0.10.0" except analyzer is ">=0.10.0 <0.11.0" - sdk constraint ">=1.0.0 <2.0.0" R=sigmund@google.com Review URL: https://codereview.chromium.org//59763006 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29957 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 5e460f37..bd36d6db 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,4 +1,5 @@ name: args +version: 0.9.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args @@ -7,6 +8,8 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - unmodifiable_collection: any + unmodifiable_collection: ">=0.9.0 <0.10.0" dev_dependencies: - unittest: any + unittest: ">=0.9.0 <0.10.0" +environment: + sdk: ">=1.0.0 <2.0.0" From f5d6a32ba24856f939eb43f3e19e716eb5e6478d Mon Sep 17 00:00:00 2001 From: "ajohnsen@google.com" Date: Wed, 6 Nov 2013 09:09:18 +0000 Subject: [PATCH 058/263] Revert "add versions and constraints for packages and samples" This is currently blocking us from testing samples. BUG= R=kasperl@google.com Review URL: https://codereview.chromium.org//59513007 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29960 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index bd36d6db..5e460f37 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,4 @@ name: args -version: 0.9.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args @@ -8,8 +7,6 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - unmodifiable_collection: ">=0.9.0 <0.10.0" + unmodifiable_collection: any dev_dependencies: - unittest: ">=0.9.0 <0.10.0" -environment: - sdk: ">=1.0.0 <2.0.0" + unittest: any From 6d5eb7c04ff275b8730dbc66dbb75eb9d961879f Mon Sep 17 00:00:00 2001 From: "dgrove@google.com" Date: Wed, 6 Nov 2013 18:28:22 +0000 Subject: [PATCH 059/263] Re-land r29957 (add versions and constraints for packages and samples), with SDK constraints bumped from 1.0.0 to 0.8.10+6 . R=ricow@google.com, sigmund@google.com Review URL: https://codereview.chromium.org//62473002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@29986 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 5e460f37..2d7a0861 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,4 +1,5 @@ name: args +version: 0.9.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args @@ -7,6 +8,8 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - unmodifiable_collection: any + unmodifiable_collection: ">=0.9.0 <0.10.0" dev_dependencies: - unittest: any + unittest: ">=0.9.0 <0.10.0" +environment: + sdk: ">=0.8.10+6 <2.0.0" From a47b50d3acbb55fa667ac6c1265fdcfd14cf8b5f Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Tue, 19 Nov 2013 12:11:24 +0000 Subject: [PATCH 060/263] Combine unmodifiable_collection package into collection_helpers. Avoids some duplicated code, and makes everything much simpler. R=floitsch@google.com Review URL: https://codereview.chromium.org//70073003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30402 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- pkgs/args/lib/src/options.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index ea7f2318..7f524dfb 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -240,7 +240,7 @@ * To display the help, use the ArgParser getUsage() method: * * print(parser.getUsage()); - * + * * The resulting string looks something like this: * * --mode The compiler configuration @@ -261,7 +261,7 @@ */ library args; -import 'package:unmodifiable_collection/unmodifiable_collection.dart'; +import 'package:collection_helpers/wrappers.dart'; import 'src/parser.dart'; import 'src/usage.dart'; diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index f47bd61a..0b8b404b 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -1,6 +1,6 @@ library options; -import 'package:unmodifiable_collection/unmodifiable_collection.dart'; +import 'package:collection_helpers/wrappers.dart'; /** * A command-line option. Includes both flags and options which take a value. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 2d7a0861..0ff36a86 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -8,7 +8,7 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - unmodifiable_collection: ">=0.9.0 <0.10.0" + collection_helpers: ">=0.9.0 <0.10.0" dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: From 778cbe2aa1e771b05eb4f1e31fe1558b41243537 Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Tue, 26 Nov 2013 19:35:44 +0000 Subject: [PATCH 061/263] pkg/args: a bunch of test cleanup R=rnystrom@google.com Review URL: https://codereview.chromium.org//88553003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30675 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/test/args_test.dart | 64 +++++++++++++++--------------- pkgs/args/test/command_test.dart | 11 +---- pkgs/args/test/parse_all_test.dart | 5 ++- pkgs/args/test/parse_test.dart | 11 +---- pkgs/args/test/usage_test.dart | 8 +--- pkgs/args/test/utils.dart | 16 ++++++++ 6 files changed, 56 insertions(+), 59 deletions(-) create mode 100644 pkgs/args/test/utils.dart diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index cf31cc26..075a3e83 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -6,8 +6,9 @@ library args_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; +import 'utils.dart'; -main() { +void main() { group('ArgParser.addFlag()', () { test('throws ArgumentError if the flag already exists', () { var parser = new ArgParser(); @@ -182,43 +183,40 @@ main() { }); }); - group('ArgResults.options', () { - test('returns the provided options', () { - var parser = new ArgParser(); - parser.addFlag('woof'); - parser.addOption('meow'); - var args = parser.parse(['--woof', '--meow', 'kitty']); - expect(args.options, hasLength(2)); - expect(args.options.any((o) => o == 'woof'), isTrue); - expect(args.options.any((o) => o == 'meow'), isTrue); - }); - - test('includes defaulted options', () { - var parser = new ArgParser(); - parser.addFlag('woof', defaultsTo: false); - parser.addOption('meow', defaultsTo: 'kitty'); - var args = parser.parse([]); - expect(args.options, hasLength(2)); - expect(args.options.any((o) => o == 'woof'), isTrue); - expect(args.options.any((o) => o == 'meow'), isTrue); - }); - }); - - group('ArgResults[]', () { - test('throws if the name is not an option', () { + group('ArgResults', () { + group('options', () { + test('returns the provided options', () { + var parser = new ArgParser(); + parser.addFlag('woof'); + parser.addOption('meow'); + var args = parser.parse(['--woof', '--meow', 'kitty']); + expect(args.options, hasLength(2)); + expect(args.options, contains('woof')); + expect(args.options, contains('meow')); + }); + + test('includes defaulted options', () { + var parser = new ArgParser(); + parser.addFlag('woof', defaultsTo: false); + parser.addOption('meow', defaultsTo: 'kitty'); + var args = parser.parse([]); + expect(args.options, hasLength(2)); + expect(args.options, contains('woof')); + expect(args.options, contains('meow')); + }); + }); + + test('[] throws if the name is not an option', () { var parser = new ArgParser(); var results = parser.parse([]); throwsIllegalArg(() => results['unknown']); }); - }); -} - -throwsIllegalArg(function, {String reason: null}) { - expect(function, throwsArgumentError, reason: reason); -} -throwsFormat(ArgParser parser, List args) { - expect(() => parser.parse(args), throwsFormatException); + test('rest cannot be modified', () { + var results = new ArgResults({}, '', null, []); + expect(() => results.rest.add('oops'), throwsUnsupportedError); + }); + }); } const _INVALID_OPTIONS = const [ diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 6115ab1c..1380e160 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -6,8 +6,9 @@ library command_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; +import 'utils.dart'; -main() { +void main() { group('ArgParser.addCommand()', () { test('creates a new ArgParser if none is given', () { var parser = new ArgParser(); @@ -204,11 +205,3 @@ main() { }); }); } - -throwsIllegalArg(function) { - expect(function, throwsArgumentError); -} - -throwsFormat(ArgParser parser, List args) { - expect(() => parser.parse(args), throwsFormatException); -} diff --git a/pkgs/args/test/parse_all_test.dart b/pkgs/args/test/parse_all_test.dart index 8498ae67..d404c2f9 100644 --- a/pkgs/args/test/parse_all_test.dart +++ b/pkgs/args/test/parse_all_test.dart @@ -7,7 +7,7 @@ library parse_all_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; -main() { +void main() { group('ArgParser.parse(allowTrailingOptions: true) ' 'starting with a non-option', () { test('followed by flag', () { @@ -57,7 +57,8 @@ main() { }); }); } -expectThrows(ArgParser parser, List args) => + +void expectThrows(ArgParser parser, List args) => expect(() => parser.parse(args, allowTrailingOptions: true), throwsFormatException, reason: "with allowTrailingOptions: true"); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 715c2269..659c08e2 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -6,8 +6,9 @@ library parse_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; +import 'utils.dart'; -main() { +void main() { group('ArgParser.parse()', () { test('does not destructively modify the argument list', () { var parser = new ArgParser(); @@ -421,11 +422,3 @@ main() { }); }); } - -throwsIllegalArg(function) { - expect(function, throwsArgumentError); -} - -throwsFormat(ArgParser parser, List args) { - expect(() => parser.parse(args), throwsFormatException); -} diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 27b03894..9f5e53f6 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -7,7 +7,7 @@ library usage_test; import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; -main() { +void main() { group('ArgParser.getUsage()', () { test('negatable flags show "no-" in title', () { var parser = new ArgParser(); @@ -184,11 +184,7 @@ main() { }); } -throwsIllegalArg(function) { - expect(function, throwsArgumentError); -} - -validateUsage(ArgParser parser, String expected) { +void validateUsage(ArgParser parser, String expected) { expected = unindentString(expected); expect(parser.getUsage(), equals(expected)); } diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart new file mode 100644 index 00000000..4586f573 --- /dev/null +++ b/pkgs/args/test/utils.dart @@ -0,0 +1,16 @@ +// 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. + +library utils; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +void throwsIllegalArg(function, {String reason: null}) { + expect(function, throwsArgumentError, reason: reason); +} + +void throwsFormat(ArgParser parser, List args) { + expect(() => parser.parse(args), throwsFormatException); +} From c06d30c9ac0b9eed1f9a006e37d43f8fd4ab98e4 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 26 Nov 2013 21:55:39 +0000 Subject: [PATCH 062/263] Add a [hide] argument to [ArgParser.addFlag]. Also bump the args version to 0.10.0 in preperation for a release. R=rnystrom@google.com BUG= Review URL: https://codereview.chromium.org//82213002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30678 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 4 ++-- pkgs/args/pubspec.yaml | 4 ++-- pkgs/args/test/usage_test.dart | 16 +++++++++++++++- 3 files changed, 19 insertions(+), 5 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 7f524dfb..a76e7264 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -321,9 +321,9 @@ class ArgParser { * * There is already an option using abbreviation [abbr]. */ void addFlag(String name, {String abbr, String help, bool defaultsTo: false, - bool negatable: true, void callback(bool value)}) { + bool negatable: true, void callback(bool value), bool hide: false}) { _addOption(name, abbr, help, null, null, defaultsTo, callback, - isFlag: true, negatable: negatable); + isFlag: true, negatable: negatable, hide: hide); } /** diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 0ff36a86..06e201f9 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.9.0 +version: 0.10.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args @@ -12,4 +12,4 @@ dependencies: dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: - sdk: ">=0.8.10+6 <2.0.0" + sdk: ">=1.0.0 <2.0.0" diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 9f5e53f6..ddd223c9 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -168,7 +168,7 @@ void main() { '''); }); - test("hidden flags don't appear in the help", () { + test("hidden options don't appear in the help", () { var parser = new ArgParser(); parser.addOption('first', help: 'The first option'); parser.addOption('second', hide: true); @@ -181,6 +181,20 @@ void main() { --third The third option '''); }); + + test("hidden flags don't appear in the help", () { + var parser = new ArgParser(); + parser.addFlag('first', help: 'The first flag'); + parser.addFlag('second', hide: true); + parser.addFlag('third', help: 'The third flag'); + + + validateUsage(parser, + ''' + --[no-]first The first flag + --[no-]third The third flag + '''); + }); }); } From e9b1ccdadad140fd17673d8b27a1da1e0febed4a Mon Sep 17 00:00:00 2001 From: "kevmoo@j832.com" Date: Thu, 5 Dec 2013 23:39:26 +0000 Subject: [PATCH 063/263] pkg/args: updated args dependency on collection_helpers to >= 0.9.1 R=rnystrom@google.com Review URL: https://codereview.chromium.org//89173002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@30924 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 06e201f9..f907300f 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -8,7 +8,7 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - collection_helpers: ">=0.9.0 <0.10.0" + collection_helpers: ">=0.9.1 <0.10.0" dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: From 0749dc4bc02dd17a52522d01a8bdfbdee39f42c1 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Thu, 19 Dec 2013 11:57:56 +0000 Subject: [PATCH 064/263] Create associated packages for the dart:collection and dart:async libs. R=sgjesse@google.com Review URL: https://codereview.chromium.org//113883002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31260 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/lib/args.dart | 2 +- pkgs/args/lib/src/options.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index a76e7264..4b9f857f 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -261,7 +261,7 @@ */ library args; -import 'package:collection_helpers/wrappers.dart'; +import 'package:collection/wrappers.dart'; import 'src/parser.dart'; import 'src/usage.dart'; diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index 0b8b404b..a4091df4 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -1,6 +1,6 @@ library options; -import 'package:collection_helpers/wrappers.dart'; +import 'package:collection/wrappers.dart'; /** * A command-line option. Includes both flags and options which take a value. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index f907300f..771fdb1f 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -8,7 +8,7 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - collection_helpers: ">=0.9.1 <0.10.0" + collection: ">=0.9.0 <0.10.0" dev_dependencies: unittest: ">=0.9.0 <0.10.0" environment: From e78fcf36d21fe476ffc0f832f9df04f37f0bfb29 Mon Sep 17 00:00:00 2001 From: "kevmoo@google.com" Date: Fri, 27 Dec 2013 17:20:00 +0000 Subject: [PATCH 065/263] flagging pkg/args and pkg/logging as dev+1 should be publishing with bumped version due to package constraint changes R=rnystrom@google.com Review URL: https://codereview.chromium.org//102833012 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31388 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 771fdb1f..ab3bbdd7 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.10.0 +version: 0.10.0+1 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args From 189e021dbad1f7e751f130fcbb2ca3f3bae1f50a Mon Sep 17 00:00:00 2001 From: "kevmoo@google.com" Date: Fri, 27 Dec 2013 19:03:22 +0000 Subject: [PATCH 066/263] pkg/args: adding LICENSE file R=rnystrom@google.com Review URL: https://codereview.chromium.org//121753002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@31391 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/LICENSE | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 pkgs/args/LICENSE diff --git a/pkgs/args/LICENSE b/pkgs/args/LICENSE new file mode 100644 index 00000000..ee999303 --- /dev/null +++ b/pkgs/args/LICENSE @@ -0,0 +1,26 @@ +Copyright 2013, the Dart project authors. All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials provided + with the distribution. + * Neither the name of Google Inc. nor the names of its + contributors may be used to endorse or promote products derived + from this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. From 9b2fc9aa3bba1dd79fc58835cf26cce866d77307 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 19 Mar 2014 21:13:55 +0000 Subject: [PATCH 067/263] Ignore hidden options when calculating usage columns. R=nweiz@google.com Review URL: https://codereview.chromium.org//203273013 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@34140 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 3 +++ pkgs/args/lib/src/usage.dart | 2 ++ pkgs/args/pubspec.yaml | 4 ++-- pkgs/args/test/usage_test.dart | 22 ++++++++++++++++++---- 4 files changed, 25 insertions(+), 6 deletions(-) create mode 100644 pkgs/args/CHANGELOG.md diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md new file mode 100644 index 00000000..aadaddcf --- /dev/null +++ b/pkgs/args/CHANGELOG.md @@ -0,0 +1,3 @@ +## 0.10.0+2 + +* Usage ignores hidden options when determining column widths. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 3244c4e5..177a0f90 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -127,6 +127,8 @@ class Usage { int abbr = 0; int title = 0; args.options.forEach((name, option) { + if (option.hide) return; + // Make room in the first column if there are abbreviations. abbr = max(abbr, getAbbreviation(option).length); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index ab3bbdd7..ce264e18 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,10 +1,10 @@ name: args -version: 0.10.0+1 +version: 0.10.0+2 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args description: > - Libraries for defining parsers for parsing raw command-line arguments into + Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. dependencies: diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index ddd223c9..77d66a83 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -177,8 +177,8 @@ void main() { validateUsage(parser, ''' - --first The first option - --third The third option + --first The first option + --third The third option '''); }); @@ -191,8 +191,22 @@ void main() { validateUsage(parser, ''' - --[no-]first The first flag - --[no-]third The third flag + --[no-]first The first flag + --[no-]third The third flag + '''); + }); + + test("hidden options don't affect spacing", () { + var parser = new ArgParser(); + parser.addFlag('first', help: 'The first flag'); + parser.addFlag('second-very-long-option', hide: true); + parser.addFlag('third', help: 'The third flag'); + + + validateUsage(parser, + ''' + --[no-]first The first flag + --[no-]third The third flag '''); }); }); From 0ffd97749bf617b06e321b5b32ca29afa31504aa Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Tue, 6 May 2014 23:25:59 +0000 Subject: [PATCH 068/263] Move allowTrailingOptions into ArgParser. R=brianwilkerson@google.com, jmesserly@google.com, nweiz@google.com Review URL: https://codereview.chromium.org//260963007 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@35837 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 6 ++ pkgs/args/lib/args.dart | 42 +++++---- pkgs/args/lib/src/parser.dart | 11 +-- pkgs/args/pubspec.yaml | 4 +- pkgs/args/test/parse_all_test.dart | 64 -------------- pkgs/args/test/parse_test.dart | 45 ++++++++-- pkgs/args/test/trailing_options_test.dart | 100 ++++++++++++++++++++++ 7 files changed, 171 insertions(+), 101 deletions(-) delete mode 100644 pkgs/args/test/parse_all_test.dart create mode 100644 pkgs/args/test/trailing_options_test.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index aadaddcf..cb8acb94 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.11.0 + +* Move handling trailing options from `ArgParser.parse()` into `ArgParser` + itself. This lets subcommands have different behavior for how they handle + trailing options. + ## 0.10.0+2 * Usage ignores hidden options when determining column widths. diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 4b9f857f..bdd61a58 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -286,15 +286,32 @@ class ArgParser { */ final Map commands; - /** Creates a new ArgParser. */ - factory ArgParser() => - new ArgParser._({}, {}); + /** + * Whether or not this parser parses options that appear after non-option + * arguments. + */ + final bool allowTrailingOptions; - ArgParser._(Map options, Map commands) : + /** + * Creates a new ArgParser. + * + * If [allowTrailingOptions] is set, the parser will continue parsing even + * after it finds an argument that is neither an option nor a command. + * This allows options to be specified after regular arguments. Defaults to + * `false`. + */ + factory ArgParser({bool allowTrailingOptions}) => + new ArgParser._({}, {}, + allowTrailingOptions: allowTrailingOptions); + + ArgParser._(Map options, Map commands, + {bool allowTrailingOptions}) : this._options = options, this.options = new UnmodifiableMapView(options), this._commands = commands, - this.commands = new UnmodifiableMapView(commands); + this.commands = new UnmodifiableMapView(commands), + this.allowTrailingOptions = allowTrailingOptions != null ? + allowTrailingOptions : false; /** * Defines a command. @@ -366,20 +383,9 @@ class ArgParser { /** * Parses [args], a list of command-line arguments, matches them against the * flags and options defined by this parser, and returns the result. - * - * If [allowTrailingOptions] is set, the parser will continue parsing even - * after it finds an argument that is neither an option nor a command. - * This allows options to be specified after regular arguments. - * - * [allowTrailingOptions] is false by default, so when a non-option, - * non-command argument is encountered, it and all remaining arguments, - * even those that look like options are passed to the innermost command. */ - ArgResults parse(List args, {bool allowTrailingOptions}) { - if (allowTrailingOptions == null) allowTrailingOptions = false; - return new Parser(null, this, args.toList(), null, null, - allowTrailingOptions: allowTrailingOptions).parse(); - } + ArgResults parse(List args) => + new Parser(null, this, args.toList(), null, null).parse(); /** * Generates a string displaying usage information for the defined options. diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 550fe63e..bccce64d 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -28,9 +28,6 @@ class Parser { */ final Parser parent; - /** If `true`, parsing will continue after a non-option argument. */ - final bool allowTrailingOptions; - /** The grammar being parsed. */ final ArgParser grammar; @@ -43,8 +40,7 @@ class Parser { /** The accumulated parsed options. */ final Map results = {}; - Parser(this.commandName, this.grammar, this.args, this.parent, rest, - {this.allowTrailingOptions: false}) { + Parser(this.commandName, this.grammar, this.args, this.parent, rest) { if (rest != null) this.rest.addAll(rest); } @@ -79,8 +75,7 @@ class Parser { if (command != null) { validate(rest.isEmpty, 'Cannot specify arguments before a command.'); var commandName = args.removeAt(0); - var commandParser = new Parser(commandName, command, args, this, rest, - allowTrailingOptions: allowTrailingOptions); + var commandParser = new Parser(commandName, command, args, this, rest); commandResults = commandParser.parse(); // All remaining arguments were passed to command so clear them here. @@ -96,7 +91,7 @@ class Parser { // This argument is neither option nor command, so stop parsing unless // the [allowTrailingOptions] option is set. - if (!allowTrailingOptions) break; + if (!grammar.allowTrailingOptions) break; rest.add(args.removeAt(0)); } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index ce264e18..a5c1a181 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.10.0+2 +version: 0.11.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args @@ -10,6 +10,6 @@ description: > dependencies: collection: ">=0.9.0 <0.10.0" dev_dependencies: - unittest: ">=0.9.0 <0.10.0" + unittest: ">=0.9.0 <0.11.0" environment: sdk: ">=1.0.0 <2.0.0" diff --git a/pkgs/args/test/parse_all_test.dart b/pkgs/args/test/parse_all_test.dart deleted file mode 100644 index d404c2f9..00000000 --- a/pkgs/args/test/parse_all_test.dart +++ /dev/null @@ -1,64 +0,0 @@ -// 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. - -library parse_all_test; - -import 'package:unittest/unittest.dart'; -import 'package:args/args.dart'; - -void main() { - group('ArgParser.parse(allowTrailingOptions: true) ' - 'starting with a non-option', () { - test('followed by flag', () { - var parser = new ArgParser()..addFlag('flag'); - var args = ['A', '--flag']; - - var resultsAll = parser.parse(args, allowTrailingOptions: true); - expect(resultsAll['flag'], isTrue); - expect(resultsAll.rest, equals(['A'])); - }); - - test('followed by option', () { - var parser = new ArgParser()..addOption('opt'); - var args = ['A', '--opt']; - - expectThrows(parser, args); - }); - - test('followed by option and value', () { - var parser = new ArgParser()..addOption('opt'); - var args = ['A', '--opt', 'V']; - - var resultsAll = parser.parse(args, allowTrailingOptions: true); - expect(resultsAll['opt'], equals('V')); - expect(resultsAll.rest, equals(['A'])); - }); - - test('followed by unknown flag', () { - var parser = new ArgParser(); - var args = ['A', '--xflag']; - - expectThrows(parser, args); - }); - - test('followed by unknown option and value', () { - var parser = new ArgParser(); - var args = ['A', '--xopt', 'V']; - - expectThrows(parser, args); - }); - - test('followed by command', () { - var parser = new ArgParser()..addCommand('com'); - var args = ['A', 'com']; - - expectThrows(parser, args); - }); - }); -} - -void expectThrows(ArgParser parser, List args) => - expect(() => parser.parse(args, allowTrailingOptions: true), - throwsFormatException, - reason: "with allowTrailingOptions: true"); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 659c08e2..56320d60 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -53,6 +53,15 @@ void main() { throwsFormat(parser, ['--verbose=true']); }); + + test('are case-sensitive', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + parser.addFlag('Verbose'); + var results = parser.parse(['--verbose']); + expect(results['verbose'], isTrue); + expect(results['Verbose'], isFalse); + }); }); group('flags negated with "no-"', () { @@ -295,6 +304,15 @@ void main() { // The '?!' means this can only be understood as '--apple b?!c'. throwsFormat(parser, ['-ab?!c']); }); + + test('are case-sensitive', () { + var parser = new ArgParser(); + parser.addFlag('file', abbr: 'f'); + parser.addFlag('force', abbr: 'F'); + var results = parser.parse(['-f']); + expect(results['file'], isTrue); + expect(results['force'], isFalse); + }); }); group('options', () { @@ -384,6 +402,15 @@ void main() { var args = parser.parse(['']); expect(args['define'], equals(['0'])); }); + + test('are case-sensitive', () { + var parser = new ArgParser(); + parser.addOption('verbose', defaultsTo: 'no'); + parser.addOption('Verbose', defaultsTo: 'no'); + var results = parser.parse(['--verbose', 'chatty']); + expect(results['verbose'], equals('chatty')); + expect(results['Verbose'], equals('no')); + }); }); group('remaining args', () { @@ -397,10 +424,10 @@ void main() { expect(results['woof'], isTrue); expect(results['meow'], equals('v')); expect(results['tweet'], equals('bird')); - expect(results.rest, orderedEquals(['not', 'option'])); + expect(results.rest, equals(['not', 'option'])); }); - test('stops parsing at "--"', () { + test('consumes "--" and stops', () { var parser = new ArgParser(); parser.addFlag('woof', defaultsTo: false); parser.addOption('meow', defaultsTo: 'kitty'); @@ -408,16 +435,16 @@ void main() { var results = parser.parse(['--woof', '--', '--meow']); expect(results['woof'], isTrue); expect(results['meow'], equals('kitty')); - expect(results.rest, orderedEquals(['--meow'])); + expect(results.rest, equals(['--meow'])); }); - test('handles options with case-sensitivity', () { + test('leaves "--" if not the first non-option', () { var parser = new ArgParser(); - parser.addFlag('recurse', defaultsTo: false, abbr:'R'); - var results = parser.parse(['-R']); - expect(results['recurse'], isTrue); - expect(results.rest, [ ]); - throwsFormat(parser, ['-r']); + parser.addFlag('woof'); + + var results = parser.parse(['--woof', 'stop', '--', 'arg']); + expect(results['woof'], isTrue); + expect(results.rest, equals(['stop', '--', 'arg'])); }); }); }); diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart new file mode 100644 index 00000000..2ee860ca --- /dev/null +++ b/pkgs/args/test/trailing_options_test.dart @@ -0,0 +1,100 @@ +// 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. + +library trailing_options_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; + +void main() { + test('allowTrailingOptions defaults to false', () { + var parser = new ArgParser(); + expect(parser.allowTrailingOptions, isFalse); + }); + + group('when trailing options are allowed', () { + var parser; + setUp(() { + parser = new ArgParser(allowTrailingOptions: true); + }); + + void expectThrows(List args) => + expect(() => parser.parse(args), throwsFormatException, + reason: "with allowTrailingOptions: true"); + + test('collects non-options in rest', () { + parser.addFlag('flag'); + parser.addOption('opt', abbr: 'o'); + var results = parser.parse(['a', '--flag', 'b', '-o', 'value', 'c']); + expect(results['flag'], isTrue); + expect(results['opt'], equals('value')); + expect(results.rest, equals(['a', 'b', 'c'])); + }); + + test('stops parsing options at "--"', () { + parser.addFlag('flag'); + parser.addOption('opt', abbr: 'o'); + var results = parser.parse(['a', '--flag', '--', '-ovalue', 'c']); + expect(results['flag'], isTrue); + expect(results.rest, equals(['a', '-ovalue', 'c'])); + }); + + test('only consumes first "--"', () { + parser.addFlag('flag', abbr: 'f'); + parser.addOption('opt', abbr: 'o'); + var results = parser.parse(['a', '--', 'b', '--', 'c']); + expect(results.rest, equals(['a', 'b', '--', 'c'])); + }); + + test('parses a trailing flag', () { + parser.addFlag('flag'); + var results = parser.parse(['arg', '--flag']); + + expect(results['flag'], isTrue); + expect(results.rest, equals(['arg'])); + }); + + test('throws on a trailing option missing its value', () { + parser.addOption('opt'); + expectThrows(['arg', '--opt']); + }); + + test('parses a trailing option', () { + parser.addOption('opt'); + var results = parser.parse(['arg', '--opt', 'v']); + expect(results['opt'], equals('v')); + expect(results.rest, equals(['arg'])); + }); + + test('throws on a trailing unknown flag', () { + expectThrows(['arg', '--xflag']); + }); + + test('throws on a trailing unknown option and value', () { + expectThrows(['arg', '--xopt', 'v']); + }); + + test('throws on a command', () { + parser.addCommand('com'); + expectThrows(['arg', 'com']); + }); + }); + + test("uses the innermost command's trailing options behavior", () { + var parser = new ArgParser(allowTrailingOptions: true); + parser.addFlag('flag', abbr: 'f'); + var command = parser.addCommand('cmd', + new ArgParser(allowTrailingOptions: false)); + command.addFlag('verbose', abbr: 'v'); + + var results = parser.parse(['a', '-f', 'b']); + expect(results['flag'], isTrue); + expect(results.rest, equals(['a', 'b'])); + + results = parser.parse(['cmd', '-f', 'a', '-v', '--unknown']); + expect(results['flag'], isTrue); // Not trailing. + expect(results.command['verbose'], isFalse); + expect(results.command.rest, equals(['a', '-v', '--unknown'])); + }); +} From 964052138de505b93bf799a12eb96412dde2d500 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Mon, 12 May 2014 21:28:45 +0000 Subject: [PATCH 069/263] Modernize the docs for args. R=kevmoo@google.com Review URL: https://codereview.chromium.org//271183002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@36072 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/README.md | 249 ++++++++++++++++++ pkgs/args/example/test_runner.dart | 9 +- pkgs/args/lib/args.dart | 388 ++++------------------------- pkgs/args/lib/src/options.dart | 4 +- pkgs/args/lib/src/parser.dart | 75 +++--- pkgs/args/lib/src/usage.dart | 70 +++--- pkgs/args/pubspec.yaml | 2 +- 7 files changed, 371 insertions(+), 426 deletions(-) create mode 100644 pkgs/args/README.md diff --git a/pkgs/args/README.md b/pkgs/args/README.md new file mode 100644 index 00000000..fe31554c --- /dev/null +++ b/pkgs/args/README.md @@ -0,0 +1,249 @@ +Parses raw command-line arguments into a set of options and values. + +This library supports [GNU][] and [POSIX][] style options, and it works +in both server-side and client-side apps. + +## Defining options + +First create an [ArgParser][]: + + var parser = new ArgParser(); + +Then define a set of options on that parser using [addOption()][addOption] and +[addFlag()][addFlag]. Here's the minimal way to create an option named "name": + + parser.addOption('name'); + +When an option can only be set or unset (as opposed to taking a string value), +use a flag: + + parser.addFlag('name'); + +Flag options, by default, accept a 'no-' prefix to negate the option. You can +disable the 'no-' prefix using the `negatable` parameter: + + parser.addFlag('name', negatable: false); + +*Note:* From here on out, "option" refers to both regular options and flags. In +cases where the distinction matters, we'll use "non-flag option." + +Options can have an optional single-character abbreviation, specified with the +`abbr` parameter: + + parser.addOption('mode', abbr: 'm'); + parser.addFlag('verbose', abbr: 'v'); + +Options can also have a default value, specified with the `defaultsTo` +parameter. The default value is used when arguments don't specify the option. + + parser.addOption('mode', defaultsTo: 'debug'); + parser.addFlag('verbose', defaultsTo: false); + +The default value for non-flag options can be any string. For flags, it must +be a `bool`. + +To validate a non-flag option, you can use the `allowed` parameter to provide an +allowed set of values. When you do, the parser throws a [FormatException] if the +value for an option is not in the allowed set. Here's an example of specifying +allowed values: + + parser.addOption('mode', allowed: ['debug', 'release']); + +You can use the `callback` parameter to associate a function with an option. +Later, when parsing occurs, the callback function is invoked with the value of +the option: + + parser.addOption('mode', callback: (mode) => print('Got mode $mode)); + parser.addFlag('verbose', callback: (verbose) { + if (verbose) print('Verbose'); + }); + +The callbacks for all options are called whenever a set of arguments is parsed. +If an option isn't provided in the args, its callback is passed the default +value, or `null` if no default value is set. + +## Parsing arguments + +Once you have an [ArgParser][] set up with some options and flags, you use it by +calling [ArgParser.parse()][parse] with a set of arguments: + + var results = parser.parse(['some', 'command', 'line', 'args']); + +These arguments usually come from the arguments to `main()`, but you can pass in +any list of strings. The `parse()` method returns an instance of [ArgResults][], +a map-like object that contains the values of the parsed options. + + var parser = new ArgParser(); + parser.addOption('mode'); + parser.addFlag('verbose', defaultsTo: true); + var results = parser.parse(['--mode', 'debug', 'something', 'else']); + + print(results['mode']); // debug + print(results['verbose']); // true + +By default, the `parse()` method stops as soon as it reaches `--` by itself or +anything that the parser doesn't recognize as an option, flag, or option value. +If arguments still remain, they go into [ArgResults.rest][rest]. + + print(results.rest); // ['something', 'else'] + +To continue to parse options found after non-option arguments, pass +`allowTrailingOptions: true` when creating the [ArgParser][]. + +## Specifying options + +To actually pass in options and flags on the command line, use GNU or POSIX +style. Consider this option: + + parser.addOption('name', abbr: 'n'); + +You can specify its value on the command line using any of the following: + + --name=somevalue + --name somevalue + -nsomevalue + -n somevalue + +Consider this flag: + + parser.addFlag('name', abbr: 'n'); + +You can set it to true using one of the following: + + --name + -n + +You can set it to false using the following: + + --no-name + +Multiple flag abbreviations can be collapsed into a single argument. Say you +define these flags: + + parser.addFlag('verbose', abbr: 'v'); + parser.addFlag('french', abbr: 'f'); + parser.addFlag('iambic-pentameter', abbr: 'i'); + +You can set all three flags at once: + + -vfi + +By default, an option has only a single value, with later option values +overriding earlier ones; for example: + + var parser = new ArgParser(); + parser.addOption('mode'); + var results = parser.parse(['--mode', 'on', '--mode', 'off']); + print(results['mode']); // prints 'off' + +If you need multiple values, set the `allowMultiple` parameter. In that case the +option can occur multiple times, and the `parse()` method returns a list of +values: + + var parser = new ArgParser(); + parser.addOption('mode', allowMultiple: true); + var results = parser.parse(['--mode', 'on', '--mode', 'off']); + print(results['mode']); // prints '[on, off]' + +## 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, consider this shell +command: + + $ 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 using the [addCommand][] +method: + + var parser = new ArgParser(); + var command = parser.addCommand('commit'); + +It returns another [ArgParser][], which you can then use to define options +specific to that command. If you already have an [ArgParser][] for the command's +options, you can pass it in: + + var parser = new ArgParser(); + var command = new ArgParser(); + parser.addCommand('commit', command); + +The [ArgParser][] for a command can then define options or flags: + + command.addFlag('all', abbr: 'a'); + +You can add multiple commands to the same parser so that a user can select one +from a range of possible commands. When parsing an argument list, 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['all']); // true + +Options for a command must appear after the command in the argument list. For +example, given the above parser, `"git -a commit"` is *not* valid. The parser +tries to find the right-most command that accepts an option. For example: + + var parser = new ArgParser(); + parser.addFlag('all', abbr: 'a'); + var command = parser.addCommand('commit'); + command.addFlag('all', abbr: 'a'); + + var results = parser.parse(['commit', '-a']); + print(results.command['all']); // true + +Here, both the top-level parser and the `"commit"` command can accept a `"-a"` +(which is probably a bad command line interface, admittedly). In that case, when +`"-a"` appears after `"commit"`, it is applied to that command. If it appears to +the left of `"commit"`, it is given to the top-level parser. + +## Displaying usage + +You can automatically generate nice help text, suitable for use as the output of +`--help`. To display good usage information, you should provide some help text +when you create your options. + +To define help text for an entire option, use the `help:` parameter: + + parser.addOption('mode', help: 'The compiler configuration', + allowed: ['debug', 'release']); + parser.addFlag('verbose', help: 'Show additional diagnostic info'); + +For non-flag options, you can also provide detailed help for each expected value +by using the `allowedHelp:` parameter: + + parser.addOption('arch', help: 'The architecture to compile for', + allowedHelp: { + 'ia32': 'Intel x86', + 'arm': 'ARM Holding 32-bit chip' + }); + +To display the help, use the [getUsage()][getUsage] method: + + print(parser.getUsage()); + +The resulting string looks something like this: + + --mode The compiler configuration + [debug, release] + + --[no-]verbose Show additional diagnostic info + --arch The architecture to compile for + + [arm] ARM Holding 32-bit chip + [ia32] Intel x86 + +To assist the formatting of the usage help, single-line help text is followed by +a single new line. Options with multi-line help text are followed by two new +lines. This provides spatial diversity between options. + +[posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 +[gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces +[ArgParser]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser +[ArgResults]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgResults +[addOption]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addOption +[addFlag]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addFlag +[parse]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_parse +[rest]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgResults#id_rest +[addCommand]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addCommand +[getUsage]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_getUsage diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index 74750ae9..fac1690b 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -2,11 +2,9 @@ // 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. -/** - * This is an example of converting the args in test.dart to use this API. - * It shows what it looks like to build an [ArgParser] and then, when the code - * is run, demonstrates what the generated usage text looks like. - */ +/// This is an example of converting the args in test.dart to use this API. +/// It shows what it looks like to build an [ArgParser] and then, when the code +/// is run, demonstrates what the generated usage text looks like. library example; import 'dart:io'; @@ -117,7 +115,6 @@ is 'dart file.dart' and you specify special command defaultsTo: false); parser.addOption('dart', help: 'Path to dart executable'); - // TODO(antonm): rename the option. parser.addOption('drt', help: 'Path to content shell executable'); parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index bdd61a58..4eb23c0e 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -2,263 +2,6 @@ // 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. -/** - * Parser support for transforming raw command-line arguments into a set - * of options and values. - * - * This library supports [GNU][] and [POSIX][] style options, and it works - * in both server-side and client-side apps. - * - * For information on installing this library, see the - * [args package on pub.dartlang.org](http://pub.dartlang.org/packages/args). - * Here's an example of importing this library: - * - * import 'package:args/args.dart'; - * - * ## Defining options - * - * To use this library, first create an [ArgParser]: - * - * var parser = new ArgParser(); - * - * Then define a set of options on that parser using [addOption()] and - * [addFlag()]. Here's the minimal way to create an option named "name": - * - * parser.addOption('name'); - * - * When an option can only be set or unset (as opposed to taking a string - * value), use a flag: - * - * parser.addFlag('name'); - * - * Flag options, by default, accept a 'no-' prefix to negate the option. - * You can disable the 'no-' prefix using the `negatable` parameter: - * - * parser.addFlag('name', negatable: false); - * - * **Terminology note:** - * From here on out, the term _option_ refers to both regular options and - * flags. In cases where the distinction matters, this documentation uses - * the term _non-flag option._ - * - * Options can have an optional single-character abbreviation, specified - * with the `abbr` parameter: - * - * parser.addOption('mode', abbr: 'm'); - * parser.addFlag('verbose', abbr: 'v'); - * - * Options can also have a default value, specified with the `defaultsTo` - * parameter. The default value is used when arguments don't specify the - * option. - * - * parser.addOption('mode', defaultsTo: 'debug'); - * parser.addFlag('verbose', defaultsTo: false); - * - * The default value for non-flag options can be any [String]. For flags, - * it must be a [bool]. - * - * To validate a non-flag option, you can use the `allowed` parameter to - * provide an allowed set of values. When you do, the parser throws a - * [FormatException] if the value for an option is not in the allowed set. - * Here's an example of specifying allowed values: - * - * parser.addOption('mode', allowed: ['debug', 'release']); - * - * You can use the `callback` parameter to associate a function with an - * option. Later, when parsing occurs, the callback function is invoked - * with the value of the option: - * - * parser.addOption('mode', callback: (mode) => print('Got mode $mode)); - * parser.addFlag('verbose', callback: (verbose) { - * if (verbose) print('Verbose'); - * }); - * - * The callbacks for all options are called whenever a set of arguments - * is parsed. If an option isn't provided in the args, its callback is - * passed the default value, or `null` if no default value is set. - * - * ## Parsing arguments - * - * Once you have an [ArgParser] set up with some options and flags, you - * use it by calling [ArgParser.parse()] with a set of arguments: - * - * var results = parser.parse(['some', 'command', 'line', 'args']); - * - * These arguments usually come from the arguments to main - * (`main(List arguments`), but you can pass in any list of strings. - * The parse() method returns an instance of [ArgResults], a map-like - * object that contains the values of the parsed options. - * - * var parser = new ArgParser(); - * parser.addOption('mode'); - * parser.addFlag('verbose', defaultsTo: true); - * var results = parser.parse(['--mode', 'debug', 'something', 'else']); - * - * print(results['mode']); // debug - * print(results['verbose']); // true - * - * By default, the parse() method stops as soon as it reaches `--` by itself - * or anything that the parser doesn't recognize as an option, flag, or - * option value. If arguments still remain, they go into [ArgResults.rest]. - * - * print(results.rest); // ['something', 'else'] - * - * To continue to parse options found after non-option arguments, call - * parse() with `allowTrailingOptions: true`. - * - * ## Specifying options - * - * To actually pass in options and flags on the command line, use GNU or - * POSIX style. Consider this option: - * - * parser.addOption('name', abbr: 'n'); - * - * You can specify its value on the command line using any of the following: - * - * --name=somevalue - * --name somevalue - * -nsomevalue - * -n somevalue - * - * Consider this flag: - * - * parser.addFlag('name', abbr: 'n'); - * - * You can set it to true using one of the following: - * - * --name - * -n - * - * You can set it to false using the following: - * - * --no-name - * - * Multiple flag abbreviations can be collapsed into a single argument. Say - * you define these flags: - * - * parser.addFlag('verbose', abbr: 'v'); - * parser.addFlag('french', abbr: 'f'); - * parser.addFlag('iambic-pentameter', abbr: 'i'); - * - * You can set all three flags at once: - * - * -vfi - * - * By default, an option has only a single value, with later option values - * overriding earlier ones; for example: - * - * var parser = new ArgParser(); - * parser.addOption('mode'); - * var results = parser.parse(['--mode', 'on', '--mode', 'off']); - * print(results['mode']); // prints 'off' - * - * If you need multiple values, set the `allowMultiple` parameter. In that - * case the option can occur multiple times, and the parse() method returns - * a list of values: - * - * var parser = new ArgParser(); - * parser.addOption('mode', allowMultiple: true); - * var results = parser.parse(['--mode', 'on', '--mode', 'off']); - * print(results['mode']); // prints '[on, off]' - * - * ## 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, consider - * this shell command: - * - * $ 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 using the - * [addCommand] method: - * - * var parser = new ArgParser(); - * var command = parser.addCommand('commit'); - * - * The addCommand() method returns another [ArgParser], which you can then - * use to define options specific to that command. If you already have an - * [ArgParser] for the command's options, you can pass it to addCommand: - * - * var parser = new ArgParser(); - * var command = new ArgParser(); - * parser.addCommand('commit', command); - * - * The [ArgParser] for a command can then define options or flags: - * - * command.addFlag('all', abbr: 'a'); - * - * You can add multiple commands to the same parser so that a user can select - * one from a range of possible commands. When parsing an argument list, - * 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['all']); // true - * - * Options for a command must appear after the command in the argument list. - * For example, given the above parser, "git -a commit" is *not* valid. The - * parser tries to find the right-most command that accepts an option. For - * example: - * - * var parser = new ArgParser(); - * parser.addFlag('all', abbr: 'a'); - * var command = parser.addCommand('commit'); - * command.addFlag('all', abbr: 'a'); - * - * var results = parser.parse(['commit', '-a']); - * print(results.command['all']); // true - * - * Here, both the top-level parser and the "commit" command can accept a - * "-a" (which is probably a bad command line interface, admittedly). In - * that case, when "-a" appears after "commit", it is applied to that - * command. If it appears to the left of "commit", it is given to the - * top-level parser. - * - * ## Displaying usage - * - * You can automatically generate nice help text, suitable for use as the - * output of `--help`. To display good usage information, you should - * provide some help text when you create your options. - * - * To define help text for an entire option, use the `help` parameter: - * - * parser.addOption('mode', help: 'The compiler configuration', - * allowed: ['debug', 'release']); - * parser.addFlag('verbose', help: 'Show additional diagnostic info'); - * - * For non-flag options, you can also provide detailed help for each expected - * value by using the `allowedHelp` parameter: - * - * parser.addOption('arch', help: 'The architecture to compile for', - * allowedHelp: { - * 'ia32': 'Intel x86', - * 'arm': 'ARM Holding 32-bit chip' - * }); - * - * To display the help, use the ArgParser getUsage() method: - * - * print(parser.getUsage()); - * - * The resulting string looks something like this: - * - * --mode The compiler configuration - * [debug, release] - * - * --[no-]verbose Show additional diagnostic info - * --arch The architecture to compile for - * - * [arm] ARM Holding 32-bit chip - * [ia32] Intel x86 - * - * To assist the formatting of the usage help, single-line help text is - * followed by a single new line. Options with multi-line help text are - * followed by two new lines. This provides spatial diversity between options. - * - * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 - * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces - */ library args; import 'package:collection/wrappers.dart'; @@ -268,38 +11,28 @@ import 'src/usage.dart'; import 'src/options.dart'; export 'src/options.dart'; -/** - * A class for taking a list of raw command line arguments and parsing out - * options and flags from them. - */ +/// A class for taking a list of raw command line arguments and parsing out +/// options and flags from them. class ArgParser { final Map _options; final Map _commands; - /** - * The options that have been defined for this parser. - */ + /// The options that have been defined for this parser. final Map options; - /** - * The commands that have been defined for this parser. - */ + /// The commands that have been defined for this parser. final Map commands; - /** - * Whether or not this parser parses options that appear after non-option - * arguments. - */ + /// Whether or not this parser parses options that appear after non-option + /// arguments. final bool allowTrailingOptions; - /** - * Creates a new ArgParser. - * - * If [allowTrailingOptions] is set, the parser will continue parsing even - * after it finds an argument that is neither an option nor a command. - * This allows options to be specified after regular arguments. Defaults to - * `false`. - */ + /// Creates a new ArgParser. + /// + /// If [allowTrailingOptions] is set, the parser will continue parsing even + /// after it finds an argument that is neither an option nor a command. + /// This allows options to be specified after regular arguments. Defaults to + /// `false`. factory ArgParser({bool allowTrailingOptions}) => new ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions); @@ -313,13 +46,11 @@ class ArgParser { this.allowTrailingOptions = allowTrailingOptions != null ? allowTrailingOptions : false; - /** - * Defines a command. - * - * A command is a named argument which may in turn define its own options and - * subcommands using the given parser. If [parser] is omitted, implicitly - * creates a new one. Returns the parser for the command. - */ + /// Defines a command. + /// + /// A command is a named argument which may in turn define its own options and + /// subcommands using the given parser. If [parser] is omitted, implicitly + /// creates a new one. Returns the parser for the command. ArgParser addCommand(String name, [ArgParser parser]) { // Make sure the name isn't in use. if (_commands.containsKey(name)) { @@ -331,24 +62,20 @@ class ArgParser { return parser; } - /** - * Defines a flag. Throws an [ArgumentError] if: - * - * * There is already an option named [name]. - * * There is already an option using abbreviation [abbr]. - */ + /// Defines a flag. Throws an [ArgumentError] if: + /// + /// * There is already an option named [name]. + /// * There is already an option using abbreviation [abbr]. void addFlag(String name, {String abbr, String help, bool defaultsTo: false, bool negatable: true, void callback(bool value), bool hide: false}) { _addOption(name, abbr, help, null, null, defaultsTo, callback, isFlag: true, negatable: negatable, hide: hide); } - /** - * Defines a value-taking option. Throws an [ArgumentError] if: - * - * * There is already an option with name [name]. - * * There is already an option using abbreviation [abbr]. - */ + /// Defines a value-taking option. Throws an [ArgumentError] if: + /// + /// * There is already an option with name [name]. + /// * There is already an option using abbreviation [abbr]. void addOption(String name, {String abbr, String help, List allowed, Map allowedHelp, String defaultsTo, void callback(value), bool allowMultiple: false, bool hide: false}) { @@ -380,23 +107,18 @@ class ArgParser { allowMultiple: allowMultiple, hide: hide); } - /** - * Parses [args], a list of command-line arguments, matches them against the - * flags and options defined by this parser, and returns the result. - */ + /// 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 args) => new Parser(null, this, args.toList(), null, null).parse(); - /** - * Generates a string displaying usage information for the defined options. - * This is basically the help text shown on the command line. - */ + /// Generates a string displaying usage information for the defined options. + /// + /// This is basically the help text shown on the command line. 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. - */ + /// 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)) { throw new ArgumentError('No option named $option'); @@ -404,48 +126,44 @@ class ArgParser { return options[option].defaultValue; } - /** - * Finds the option whose abbreviation is [abbr], or `null` if no option has - * that abbreviation. - */ + /// Finds the option whose abbreviation is [abbr], or `null` if no option has + /// that abbreviation. Option findByAbbreviation(String abbr) { return options.values.firstWhere((option) => option.abbreviation == abbr, orElse: () => null); } } -/** - * The results of parsing a series of command line arguments using - * [ArgParser.parse()]. Includes the parsed options and any remaining unparsed - * command line arguments. - */ +/// 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; - /** - * 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`. - */ + /// 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`. final String name; - /** - * The command that was selected, or `null` if none was. This will contain - * the options that were selected for that command. - */ + /// The command that was selected, or `null` if none was. + /// + /// This will contain the options that were selected for that command. final ArgResults command; - /** - * 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. - */ + /// 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 unless parsing stopped before the + /// `--` was reached. final List rest; - /** Creates a new [ArgResults]. */ + /// Creates a new [ArgResults]. ArgResults(this._options, this.name, this.command, List rest) : this.rest = new UnmodifiableListView(rest); - /** Gets the parsed command-line option named [name]. */ + /// Gets the parsed command-line option named [name]. operator [](String name) { if (!_options.containsKey(name)) { throw new ArgumentError( @@ -455,7 +173,7 @@ class ArgResults { return _options[name]; } - /** Get the names of the options as an [Iterable]. */ + /// Get the names of the options as an [Iterable]. Iterable get options => _options.keys; } diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index a4091df4..d78459e9 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -2,9 +2,7 @@ library options; import 'package:collection/wrappers.dart'; -/** - * A command-line option. Includes both flags and options which take a value. - */ +/// A command-line option. Includes both flags and options which take a value. class Option { final String name; final String abbreviation; diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index bccce64d..3f2e79dc 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -10,45 +10,39 @@ final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); -/** - * The actual parsing class. Unlike [ArgParser] which is really more an "arg - * grammar", this is the class that does the parsing and holds the mutable - * state required during a parse. - */ +/// The actual argument parsing class. +/// +/// Unlike [ArgParser] which is really more an "arg grammar", this is the class +/// that does the parsing and holds the mutable state required during a parse. class Parser { - /** - * If parser is parsing a command's options, this will be the name of the - * command. For top-level results, this returns `null`. - */ + /// If parser is parsing a command's options, this will be the name of the + /// command. For top-level results, this returns `null`. final String commandName; - /** - * The parser for the supercommand of this command parser, or `null` if this - * is the top-level parser. - */ + /// The parser for the supercommand of this command parser, or `null` if this + /// is the top-level parser. final Parser parent; - /** The grammar being parsed. */ + /// The grammar being parsed. final ArgParser grammar; - /** The arguments being parsed. */ + /// The arguments being parsed. final List args; - /** The remaining non-option, non-command arguments. */ + /// The remaining non-option, non-command arguments. final rest = []; - /** The accumulated parsed options. */ + /// The accumulated parsed options. final Map results = {}; Parser(this.commandName, this.grammar, this.args, this.parent, rest) { if (rest != null) this.rest.addAll(rest); } - - /** The current argument being parsed. */ + /// The current argument being parsed. String get current => args[0]; - /** Parses the arguments. This can only be called once. */ + /// Parses the arguments. This can only be called once. ArgResults parse() { var commandResults = null; @@ -112,10 +106,9 @@ class Parser { return new ArgResults(results, commandName, commandResults, rest); } - /** - * Pulls the value for [option] from the second argument in [args]. Validates - * that there is a valid value there. - */ + /// Pulls the value for [option] from the second argument in [args]. + /// + /// Validates that there is a valid value there. void readNextArgAsValue(Option option) { // Take the option argument from the next command line arg. validate(args.length > 0, @@ -129,12 +122,11 @@ class Parser { args.removeAt(0); } - /** - * 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. - */ + /// 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() { var soloOpt = _SOLO_OPT.firstMatch(current); if (soloOpt == null) return false; @@ -158,11 +150,9 @@ class Parser { 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"). - */ + /// 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(Parser innermostCommand) { var abbrOpt = _ABBR_OPT.firstMatch(current); if (abbrOpt == null) return false; @@ -220,10 +210,8 @@ class Parser { setOption(results, option, true); } - /** - * Tries to parse the current argument as a long-form named option, which - * may include a value like "--mode=release" or "--mode release". - */ + /// 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() { var longOpt = _LONG_OPT.firstMatch(current); if (longOpt == null) return false; @@ -268,15 +256,14 @@ class Parser { return true; } - /** - * Called during parsing to validate the arguments. Throws a - * [FormatException] if [condition] is `false`. - */ + /// Called during parsing to validate the arguments. + /// + /// Throws a [FormatException] if [condition] is `false`. void validate(bool condition, String message) { if (!condition) throw new FormatException(message); } - /** Validates and stores [value] as the value for [option]. */ + /// Validates and stores [value] as the value for [option]. void setOption(Map results, Option option, value) { // See if it's one of the allowed values. if (option.allowed != null) { diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 177a0f90..a431395f 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -8,59 +8,55 @@ import 'dart:math'; import '../args.dart'; -/** - * 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. - */ +/// 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. class Usage { static const NUM_COLUMNS = 3; // Abbreviation, long name, help. - /** The parser this is generating usage for. */ + /// The parser this is generating usage for. final ArgParser args; - /** The working buffer for the generated usage text. */ + /// 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). - */ + /// 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. */ + /// The width in characters of each column. List columnWidths; - /** - * 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. - */ + /// 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. int numHelpLines = 0; - /** - * 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. - */ + /// 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. int newlinesNeeded = 0; Usage(this.args); - /** - * Generates a string displaying usage information for the defined options. - * This is basically the help text shown on the command line. - */ + /// Generates a string displaying usage information for the defined options. + /// This is basically the help text shown on the command line. String generate() { buffer = new StringBuffer(); @@ -229,7 +225,7 @@ class Usage { } } -/** Pads [source] to [length] by adding spaces at the end. */ +/// Pads [source] to [length] by adding spaces at the end. String padRight(String source, int length) { final result = new StringBuffer(); result.write(source); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index a5c1a181..238df349 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.11.0 +version: 0.11.0+1 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args From 18c2457d54e91e0ca14b0c24fd10f6d060b45b98 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Wed, 2 Jul 2014 23:07:13 +0000 Subject: [PATCH 070/263] Fix typo in readme. BUG=https://code.google.com/p/dart/issues/detail?id=19106 R=nweiz@google.com Review URL: https://codereview.chromium.org//362383002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@37962 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/README.md | 2 +- pkgs/args/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index fe31554c..6e36435e 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -53,7 +53,7 @@ You can use the `callback` parameter to associate a function with an option. Later, when parsing occurs, the callback function is invoked with the value of the option: - parser.addOption('mode', callback: (mode) => print('Got mode $mode)); + parser.addOption('mode', callback: (mode) => print('Got mode $mode')); parser.addFlag('verbose', callback: (verbose) { if (verbose) print('Verbose'); }); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 238df349..ba0dc572 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.11.0+1 +version: 0.11.0+2 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args From 880aac1fa8fa05b5325bef5718d59e3bb89920af Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Mon, 7 Jul 2014 23:16:17 +0000 Subject: [PATCH 071/263] Allow defining a help string for an option's parameter value. BUG= https://code.google.com/p/dart/issues/detail?id=5201 R=nweiz@google.com Review URL: https://codereview.chromium.org//363083002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38047 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/README.md | 11 ++++++----- pkgs/args/lib/args.dart | 16 ++++++++-------- pkgs/args/lib/src/options.dart | 7 ++++--- pkgs/args/lib/src/usage.dart | 9 +++++++-- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 11 +++++++++++ 7 files changed, 41 insertions(+), 19 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index cb8acb94..5349ae2f 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.11.1 + +* Options may define `valueHelp` which will then be shown in the usage. + ## 0.11.0 * Move handling trailing options from `ArgParser.parse()` into `ArgParser` diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 6e36435e..bee1ea29 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -209,6 +209,11 @@ To define help text for an entire option, use the `help:` parameter: allowed: ['debug', 'release']); parser.addFlag('verbose', help: 'Show additional diagnostic info'); +For non-flag options, you can also provide a help string for the parameter: + + parser.addOption('out', help: 'The output path', helpValue: 'path', + allowed: ['debug', 'release']); + For non-flag options, you can also provide detailed help for each expected value by using the `allowedHelp:` parameter: @@ -227,16 +232,12 @@ The resulting string looks something like this: --mode The compiler configuration [debug, release] + --out= The output path --[no-]verbose Show additional diagnostic info --arch The architecture to compile for - [arm] ARM Holding 32-bit chip [ia32] Intel x86 -To assist the formatting of the usage help, single-line help text is followed by -a single new line. Options with multi-line help text are followed by two new -lines. This provides spatial diversity between options. - [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces [ArgParser]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 4eb23c0e..ec409589 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -68,7 +68,7 @@ class ArgParser { /// * There is already an option using abbreviation [abbr]. void addFlag(String name, {String abbr, String help, bool defaultsTo: false, bool negatable: true, void callback(bool value), bool hide: false}) { - _addOption(name, abbr, help, null, null, defaultsTo, callback, + _addOption(name, abbr, help, null, null, null, defaultsTo, callback, isFlag: true, negatable: negatable, hide: hide); } @@ -76,16 +76,16 @@ class ArgParser { /// /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. - void addOption(String name, {String abbr, String help, List allowed, - Map allowedHelp, String defaultsTo, + void addOption(String name, {String abbr, String help, String valueHelp, + List allowed, Map allowedHelp, String defaultsTo, void callback(value), bool allowMultiple: false, bool hide: false}) { - _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, + _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, isFlag: false, allowMultiple: allowMultiple, hide: hide); } - void _addOption(String name, String abbr, String help, List allowed, - Map allowedHelp, defaultsTo, + void _addOption(String name, String abbr, String help, String valueHelp, + List allowed, Map allowedHelp, defaultsTo, void callback(value), {bool isFlag, bool negatable: false, bool allowMultiple: false, bool hide: false}) { // Make sure the name isn't in use. @@ -102,8 +102,8 @@ class ArgParser { } } - _options[name] = new Option(name, abbr, help, allowed, allowedHelp, - defaultsTo, callback, isFlag: isFlag, negatable: negatable, + _options[name] = new Option(name, abbr, help, valueHelp, allowed, + allowedHelp, defaultsTo, callback, isFlag: isFlag, negatable: negatable, allowMultiple: allowMultiple, hide: hide); } diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart index d78459e9..34dc8d30 100644 --- a/pkgs/args/lib/src/options.dart +++ b/pkgs/args/lib/src/options.dart @@ -10,15 +10,16 @@ class Option { final defaultValue; final Function callback; final String help; + final String valueHelp; final Map allowedHelp; final bool isFlag; final bool negatable; final bool allowMultiple; final bool hide; - Option(this.name, this.abbreviation, this.help, List allowed, - Map allowedHelp, this.defaultValue, this.callback, - {this.isFlag, this.negatable, this.allowMultiple: false, + Option(this.name, this.abbreviation, this.help, this.valueHelp, + List allowed, Map allowedHelp, this.defaultValue, + this.callback, {this.isFlag, this.negatable, this.allowMultiple: false, this.hide: false}) : this.allowed = allowed == null ? null : new UnmodifiableListView(allowed), diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index a431395f..11be54b3 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -108,11 +108,16 @@ class Usage { } String getLongOption(Option option) { + var result; if (option.negatable) { - return '--[no-]${option.name}'; + result = '--[no-]${option.name}'; } else { - return '--${option.name}'; + result = '--${option.name}'; } + + if (option.valueHelp != null) result += "=<${option.valueHelp}>"; + + return result; } String getAllowedTitle(String allowed) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index ba0dc572..c5548163 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.11.0+2 +version: 0.11.1-dev author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 77d66a83..b6b860d4 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -122,6 +122,17 @@ void main() { '''); }); + test('the value help is shown', () { + var parser = new ArgParser(); + parser.addOption('out', abbr: 'o', help: 'Where to write file', + valueHelp: 'path'); + + validateUsage(parser, + ''' + -o, --out= Where to write file + '''); + }); + test('the allowed list is shown', () { var parser = new ArgParser(); parser.addOption('suit', help: 'Like in cards', From 2751f59d426e21ba8219a39aafee0973b11f2db8 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Thu, 17 Jul 2014 21:35:45 +0000 Subject: [PATCH 072/263] Add .wasParsed() to ArgResults. BUG=https://code.google.com/p/dart/issues/detail?id=16227 R=alanknight@google.com, nweiz@google.com, sigmund@google.com Review URL: https://codereview.chromium.org//383913003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38348 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 10 +- pkgs/args/lib/args.dart | 176 +---------------------------- pkgs/args/lib/src/arg_parser.dart | 135 ++++++++++++++++++++++ pkgs/args/lib/src/arg_results.dart | 91 +++++++++++++++ pkgs/args/lib/src/option.dart | 124 ++++++++++++++++++++ pkgs/args/lib/src/options.dart | 54 --------- pkgs/args/lib/src/parser.dart | 31 ++--- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 68 ++++++++++- 9 files changed, 436 insertions(+), 255 deletions(-) create mode 100644 pkgs/args/lib/src/arg_parser.dart create mode 100644 pkgs/args/lib/src/arg_results.dart create mode 100644 pkgs/args/lib/src/option.dart delete mode 100644 pkgs/args/lib/src/options.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 5349ae2f..36b8208e 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,5 +1,13 @@ -## 0.11.1 +## 0.12.0 +* Removed public constructors for `ArgResults` and `Option`. + +* `ArgResults.wasParsed()` can be used to determine if an option was actually + parsed or the default value is being returned. + +* Replaced `isFlag` and `allowMultiple` fields in the `Option` class with a + three-value `OptionType` enum. + * Options may define `valueHelp` which will then be shown in the usage. ## 0.11.0 diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index ec409589..562df446 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -4,176 +4,6 @@ library args; -import 'package:collection/wrappers.dart'; - -import 'src/parser.dart'; -import 'src/usage.dart'; -import 'src/options.dart'; -export 'src/options.dart'; - -/// A class for taking a list of raw command line arguments and parsing out -/// options and flags from them. -class ArgParser { - final Map _options; - final Map _commands; - - /// The options that have been defined for this parser. - final Map options; - - /// The commands that have been defined for this parser. - final Map commands; - - /// Whether or not this parser parses options that appear after non-option - /// arguments. - final bool allowTrailingOptions; - - /// Creates a new ArgParser. - /// - /// If [allowTrailingOptions] is set, the parser will continue parsing even - /// after it finds an argument that is neither an option nor a command. - /// This allows options to be specified after regular arguments. Defaults to - /// `false`. - factory ArgParser({bool allowTrailingOptions}) => - new ArgParser._({}, {}, - allowTrailingOptions: allowTrailingOptions); - - ArgParser._(Map options, Map commands, - {bool allowTrailingOptions}) : - this._options = options, - this.options = new UnmodifiableMapView(options), - this._commands = commands, - this.commands = new UnmodifiableMapView(commands), - this.allowTrailingOptions = allowTrailingOptions != null ? - allowTrailingOptions : false; - - /// Defines a command. - /// - /// A command is a named argument which may in turn define its own options and - /// subcommands using the given parser. If [parser] is omitted, implicitly - /// creates a new one. Returns the parser for the command. - ArgParser addCommand(String name, [ArgParser parser]) { - // Make sure the name isn't in use. - if (_commands.containsKey(name)) { - throw new ArgumentError('Duplicate command "$name".'); - } - - if (parser == null) parser = new ArgParser(); - _commands[name] = parser; - return parser; - } - - /// Defines a flag. Throws an [ArgumentError] if: - /// - /// * There is already an option named [name]. - /// * There is already an option using abbreviation [abbr]. - void addFlag(String name, {String abbr, String help, bool defaultsTo: false, - bool negatable: true, void callback(bool value), bool hide: false}) { - _addOption(name, abbr, help, null, null, null, defaultsTo, callback, - isFlag: true, negatable: negatable, hide: hide); - } - - /// Defines a value-taking option. Throws an [ArgumentError] if: - /// - /// * There is already an option with name [name]. - /// * There is already an option using abbreviation [abbr]. - void addOption(String name, {String abbr, String help, String valueHelp, - List allowed, Map allowedHelp, String defaultsTo, - void callback(value), bool allowMultiple: false, bool hide: false}) { - _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, - callback, isFlag: false, allowMultiple: allowMultiple, - hide: hide); - } - - void _addOption(String name, String abbr, String help, String valueHelp, - List allowed, Map allowedHelp, defaultsTo, - void callback(value), {bool isFlag, bool negatable: false, - bool allowMultiple: false, bool hide: false}) { - // Make sure the name isn't in use. - if (_options.containsKey(name)) { - throw new ArgumentError('Duplicate option "$name".'); - } - - // Make sure the abbreviation isn't too long or in use. - if (abbr != null) { - 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, valueHelp, allowed, - allowedHelp, defaultsTo, callback, isFlag: isFlag, negatable: negatable, - allowMultiple: allowMultiple, hide: hide); - } - - /// 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 args) => - new Parser(null, this, args.toList(), null, null).parse(); - - /// Generates a string displaying usage information for the defined options. - /// - /// This is basically the help text shown on the command line. - 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)) { - throw new ArgumentError('No option named $option'); - } - return options[option].defaultValue; - } - - /// Finds the option whose abbreviation is [abbr], or `null` if no option has - /// that abbreviation. - Option findByAbbreviation(String abbr) { - return options.values.firstWhere((option) => option.abbreviation == abbr, - orElse: () => null); - } -} - -/// 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; - - /// 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`. - final String name; - - /// The command that was selected, or `null` if none was. - /// - /// This will contain the options that were selected for that command. - final ArgResults command; - - /// 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 unless parsing stopped before the - /// `--` was reached. - final List rest; - - /// Creates a new [ArgResults]. - ArgResults(this._options, this.name, this.command, List rest) - : this.rest = new UnmodifiableListView(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]; - } - - /// Get the names of the options as an [Iterable]. - Iterable get options => _options.keys; -} - +export 'src/arg_parser.dart'; +export 'src/arg_results.dart' hide newArgResults; +export 'src/option.dart' hide newOption; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart new file mode 100644 index 00000000..b1ee6280 --- /dev/null +++ b/pkgs/args/lib/src/arg_parser.dart @@ -0,0 +1,135 @@ +// Copyright (c) 2014, 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. + +library args.src.arg_parser; + +import 'package:collection/wrappers.dart'; + +import 'arg_results.dart'; +import 'option.dart'; +import 'parser.dart'; +import 'usage.dart'; + +/// A class for taking a list of raw command line arguments and parsing out +/// options and flags from them. +class ArgParser { + final Map _options; + final Map _commands; + + /// The options that have been defined for this parser. + final Map options; + + /// The commands that have been defined for this parser. + final Map commands; + + /// Whether or not this parser parses options that appear after non-option + /// arguments. + final bool allowTrailingOptions; + + /// Creates a new ArgParser. + /// + /// If [allowTrailingOptions] is set, the parser will continue parsing even + /// after it finds an argument that is neither an option nor a command. + /// This allows options to be specified after regular arguments. Defaults to + /// `false`. + factory ArgParser({bool allowTrailingOptions}) => + new ArgParser._({}, {}, + allowTrailingOptions: allowTrailingOptions); + + ArgParser._(Map options, Map commands, + {bool allowTrailingOptions}) : + this._options = options, + this.options = new UnmodifiableMapView(options), + this._commands = commands, + this.commands = new UnmodifiableMapView(commands), + this.allowTrailingOptions = allowTrailingOptions != null ? + allowTrailingOptions : false; + + /// Defines a command. + /// + /// A command is a named argument which may in turn define its own options and + /// subcommands using the given parser. If [parser] is omitted, implicitly + /// creates a new one. Returns the parser for the command. + ArgParser addCommand(String name, [ArgParser parser]) { + // Make sure the name isn't in use. + if (_commands.containsKey(name)) { + throw new ArgumentError('Duplicate command "$name".'); + } + + if (parser == null) parser = new ArgParser(); + _commands[name] = parser; + return parser; + } + + /// Defines a flag. Throws an [ArgumentError] if: + /// + /// * There is already an option named [name]. + /// * There is already an option using abbreviation [abbr]. + void addFlag(String name, {String abbr, String help, bool defaultsTo: false, + bool negatable: true, void callback(bool value), bool hide: false}) { + _addOption(name, abbr, help, null, null, null, defaultsTo, callback, + OptionType.FLAG, negatable: negatable, hide: hide); + } + + /// Defines a value-taking option. Throws an [ArgumentError] if: + /// + /// * There is already an option with name [name]. + /// * There is already an option using abbreviation [abbr]. + void addOption(String name, {String abbr, String help, String valueHelp, + List allowed, Map allowedHelp, String defaultsTo, + void callback(value), bool allowMultiple: false, bool hide: false}) { + _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, + callback, allowMultiple ? OptionType.MULTIPLE : OptionType.SINGLE, + hide: hide); + } + + void _addOption(String name, String abbr, String help, String valueHelp, + List allowed, Map allowedHelp, defaultsTo, + void callback(value), OptionType type, {bool negatable: false, + bool hide: false}) { + // Make sure the name isn't in use. + if (_options.containsKey(name)) { + throw new ArgumentError('Duplicate option "$name".'); + } + + // Make sure the abbreviation isn't too long or in use. + if (abbr != null) { + var existing = findByAbbreviation(abbr); + if (existing != null) { + throw new ArgumentError( + 'Abbreviation "$abbr" is already used by "${existing.name}".'); + } + } + + _options[name] = newOption(name, abbr, help, valueHelp, allowed, + allowedHelp, defaultsTo, callback, type, negatable: negatable, + hide: hide); + } + + /// 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 args) => + new Parser(null, this, args.toList(), null, null).parse(); + + /// Generates a string displaying usage information for the defined options. + /// + /// This is basically the help text shown on the command line. + 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)) { + throw new ArgumentError('No option named $option'); + } + return options[option].defaultValue; + } + + /// Finds the option whose abbreviation is [abbr], or `null` if no option has + /// that abbreviation. + Option findByAbbreviation(String abbr) { + return options.values.firstWhere((option) => option.abbreviation == abbr, + orElse: () => null); + } +} diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart new file mode 100644 index 00000000..2e47a8e9 --- /dev/null +++ b/pkgs/args/lib/src/arg_results.dart @@ -0,0 +1,91 @@ +// Copyright (c) 2014, 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. + +library args.src.arg_results; + +import 'package:collection/wrappers.dart'; + +import 'arg_parser.dart'; + +/// Creates a new [ArgResults]. +/// +/// Since [ArgResults] doesn't have a public constructor, this lets [Parser] +/// get to it. This function isn't exported to the public API of the package. +ArgResults newArgResults(ArgParser parser, Map parsed, + String name, ArgResults command, List rest) { + return new ArgResults._(parser, parsed, name, command, rest); +} + +/// 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 { + /// The [ArgParser] whose options were parsed for these results. + final ArgParser _parser; + + /// The option values that were parsed from arguments. + final Map _parsed; + + /// 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`. + final String name; + + /// The command that was selected, or `null` if none was. + /// + /// This will contain the options that were selected for that command. + final ArgResults command; + + /// 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 unless parsing stopped before the + /// `--` was reached. + final List rest; + + /// Creates a new [ArgResults]. + ArgResults._(this._parser, this._parsed, this.name, this.command, + List rest) + : this.rest = new UnmodifiableListView(rest); + + /// Gets the parsed command-line option named [name]. + operator [](String name) { + if (!_parser.options.containsKey(name)) { + throw new ArgumentError('Could not find an option named "$name".'); + } + + return _parser.options[name].getOrDefault(_parsed[name]); + } + + /// Get the names of the available options as an [Iterable]. + /// + /// This includes the options whose values were parsed or that have defaults. + /// Options that weren't present and have no default will be omitted. + Iterable get options { + var result = new Set.from(_parsed.keys); + + // Include the options that have defaults. + _parser.options.forEach((name, option) { + if (option.defaultValue != null) result.add(name); + }); + + return result; + } + + /// Returns `true` if the option with [name] was parsed from an actual + /// argument. + /// + /// Returns `false` if it wasn't provided and the default value or no default + /// value would be used instead. + bool wasParsed(String name) { + var option = _parser.options[name]; + if (option == null) { + throw new ArgumentError('Could not find an option named "$name".'); + } + + return _parsed.containsKey(name); + } +} diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart new file mode 100644 index 00000000..6edc8abc --- /dev/null +++ b/pkgs/args/lib/src/option.dart @@ -0,0 +1,124 @@ +// Copyright (c) 2014, 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. + +library args.src.option; + +import 'package:collection/wrappers.dart'; + +/// Creates a new [Option]. +/// +/// Since [Option] doesn't have a public constructor, this lets [ArgParser] +/// get to it. This function isn't exported to the public API of the package. +Option newOption(String name, String abbreviation, String help, + String valueHelp, List allowed, Map allowedHelp, + defaultValue, Function callback, OptionType type, + {bool negatable, bool hide: false}) { + return new Option._(name, abbreviation, help, valueHelp, allowed, allowedHelp, + defaultValue, callback, type, negatable: negatable, hide: hide); +} + +/// A command-line option. Includes both flags and options which take a value. +class Option { + final String name; + final String abbreviation; + final List allowed; + final defaultValue; + final Function callback; + final String help; + final String valueHelp; + final Map allowedHelp; + final OptionType type; + final bool negatable; + final bool hide; + + /// Whether the option is boolean-valued flag. + bool get isFlag => type == OptionType.FLAG; + + /// Whether the option takes a single value. + bool get isSingle => type == OptionType.SINGLE; + + /// Whether the option allows multiple values. + bool get isMultiple => type == OptionType.MULTIPLE; + + Option._(this.name, this.abbreviation, this.help, this.valueHelp, + List allowed, Map allowedHelp, this.defaultValue, + this.callback, this.type, {this.negatable, this.hide: false}) : + this.allowed = allowed == null ? + null : new UnmodifiableListView(allowed), + this.allowedHelp = allowedHelp == null ? + null : new UnmodifiableMapView(allowedHelp) { + + if (name.isEmpty) { + throw new ArgumentError('Name cannot be empty.'); + } else if (name.startsWith('-')) { + throw new ArgumentError('Name $name cannot start with "-".'); + } + + // Ensure name does not contain any invalid characters. + if (_invalidChars.hasMatch(name)) { + throw new ArgumentError('Name "$name" contains invalid characters.'); + } + + if (abbreviation != null) { + if (abbreviation.length != 1) { + throw new ArgumentError('Abbreviation must be null or have length 1.'); + } else if(abbreviation == '-') { + throw new ArgumentError('Abbreviation cannot be "-".'); + } + + if (_invalidChars.hasMatch(abbreviation)) { + throw new ArgumentError('Abbreviation is an invalid character.'); + } + } + } + + /// Returns [value] if non-`null`, otherwise returns the default value for + /// this option. + /// + /// For single-valued options, it will be [defaultValue] if set or `null` + /// otherwise. For multiple-valued options, it will be an empty list or a + /// list containing [defaultValue] if set. + dynamic getOrDefault(value) { + if (value != null) return value; + + if (!isMultiple) return defaultValue; + if (defaultValue != null) return [defaultValue]; + return []; + } + + static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); +} + +/// What kinds of values an option accepts. +class OptionType { + /// An option that can only be `true` or `false`. + /// + /// The presence of the option name itself in the argument list means `true`. + static const FLAG = const OptionType._("OptionType.FLAG"); + + /// An option that takes a single value. + /// + /// Examples: + /// + /// --mode debug + /// -mdebug + /// --mode=debug + /// + /// If the option is passed more than once, the last one wins. + static const SINGLE = const OptionType._("OptionType.SINGLE"); + + /// An option that allows multiple values. + /// + /// Example: + /// + /// --output text --output xml + /// + /// In the parsed [ArgResults], a multiple-valued option will always return + /// a list, even if one or no values were passed. + static const MULTIPLE = const OptionType._("OptionType.MULTIPLE"); + + final String name; + + const OptionType._(this.name); +} \ No newline at end of file diff --git a/pkgs/args/lib/src/options.dart b/pkgs/args/lib/src/options.dart deleted file mode 100644 index 34dc8d30..00000000 --- a/pkgs/args/lib/src/options.dart +++ /dev/null @@ -1,54 +0,0 @@ -library options; - -import 'package:collection/wrappers.dart'; - -/// A command-line option. Includes both flags and options which take a value. -class Option { - final String name; - final String abbreviation; - final List allowed; - final defaultValue; - final Function callback; - final String help; - final String valueHelp; - final Map allowedHelp; - final bool isFlag; - final bool negatable; - final bool allowMultiple; - final bool hide; - - Option(this.name, this.abbreviation, this.help, this.valueHelp, - List allowed, Map allowedHelp, this.defaultValue, - this.callback, {this.isFlag, this.negatable, this.allowMultiple: false, - this.hide: false}) : - this.allowed = allowed == null ? - null : new UnmodifiableListView(allowed), - this.allowedHelp = allowedHelp == null ? - null : new UnmodifiableMapView(allowedHelp) { - - if (name.isEmpty) { - throw new ArgumentError('Name cannot be empty.'); - } else if (name.startsWith('-')) { - throw new ArgumentError('Name $name cannot start with "-".'); - } - - // Ensure name does not contain any invalid characters. - if (_invalidChars.hasMatch(name)) { - throw new ArgumentError('Name "$name" contains invalid characters.'); - } - - if (abbreviation != null) { - if (abbreviation.length != 1) { - throw new ArgumentError('Abbreviation must be null or have length 1.'); - } else if(abbreviation == '-') { - throw new ArgumentError('Abbreviation cannot be "-".'); - } - - if (_invalidChars.hasMatch(abbreviation)) { - throw new ArgumentError('Abbreviation is an invalid character.'); - } - } - } - - static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); -} diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 3f2e79dc..4d3417a1 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -4,7 +4,9 @@ library args.src.parser; -import '../args.dart'; +import 'arg_parser.dart'; +import 'arg_results.dart'; +import 'option.dart'; final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); @@ -46,15 +48,6 @@ class Parser { ArgResults parse() { var commandResults = null; - // Initialize flags to their defaults. - grammar.options.forEach((name, option) { - if (option.allowMultiple) { - results[name] = []; - } else { - results[name] = option.defaultValue; - } - }); - // Parse the args. while (args.length > 0) { if (current == '--') { @@ -89,21 +82,16 @@ class Parser { rest.add(args.removeAt(0)); } - // Set unspecified multivalued arguments to their default value, - // if any, and invoke the callbacks. + // Invoke the callbacks. grammar.options.forEach((name, option) { - if (option.allowMultiple && - results[name].length == 0 && - option.defaultValue != null) { - results[name].add(option.defaultValue); - } - if (option.callback != null) option.callback(results[name]); + if (option.callback == null) return; + option.callback(option.getOrDefault(results[name])); }); // Add in the leftover arguments we didn't parse to the innermost command. rest.addAll(args); args.clear(); - return new ArgResults(results, commandName, commandResults, rest); + return newArgResults(grammar, results, commandName, commandResults, rest); } /// Pulls the value for [option] from the second argument in [args]. @@ -271,8 +259,9 @@ class Parser { '"$value" is not an allowed value for option "${option.name}".'); } - if (option.allowMultiple) { - results[option.name].add(value); + if (option.isMultiple) { + var list = results.putIfAbsent(option.name, () => []); + list.add(value); } else { results[option.name] = value; } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index c5548163..32e84edc 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.11.1-dev +version: 0.12.0 author: "Dart Team " homepage: http://www.dartlang.org documentation: http://api.dartlang.org/docs/pkg/args diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 075a3e83..6792c95e 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -189,6 +189,10 @@ void main() { var parser = new ArgParser(); parser.addFlag('woof'); parser.addOption('meow'); + + parser.addOption('missing-option'); + parser.addFlag('missing-flag', defaultsTo: null); + var args = parser.parse(['--woof', '--meow', 'kitty']); expect(args.options, hasLength(2)); expect(args.options, contains('woof')); @@ -199,23 +203,77 @@ void main() { var parser = new ArgParser(); parser.addFlag('woof', defaultsTo: false); parser.addOption('meow', defaultsTo: 'kitty'); + + // Flags normally have a default value. + parser.addFlag('moo'); + + parser.addOption('missing-option'); + parser.addFlag('missing-flag', defaultsTo: null); + var args = parser.parse([]); - expect(args.options, hasLength(2)); + expect(args.options, hasLength(3)); expect(args.options, contains('woof')); expect(args.options, contains('meow')); + expect(args.options, contains('moo')); }); }); test('[] throws if the name is not an option', () { - var parser = new ArgParser(); - var results = parser.parse([]); + var results = new ArgParser().parse([]); throwsIllegalArg(() => results['unknown']); }); test('rest cannot be modified', () { - var results = new ArgResults({}, '', null, []); + var results = new ArgParser().parse([]); expect(() => results.rest.add('oops'), throwsUnsupportedError); }); + + group('.wasParsed()', () { + test('throws if the name is not an option', () { + var results = new ArgParser().parse([]); + throwsIllegalArg(() => results.wasParsed('unknown')); + }); + + test('returns true for parsed options', () { + var parser = new ArgParser(); + parser.addFlag('fast'); + parser.addFlag('verbose'); + parser.addOption('mode'); + parser.addOption('output'); + + var results = parser.parse(['--fast', '--mode=debug']); + + expect(results.wasParsed('fast'), isTrue); + expect(results.wasParsed('verbose'), isFalse); + expect(results.wasParsed('mode'), isTrue); + expect(results.wasParsed('output'), isFalse); + }); + }); + }); + + group('Option', () { + test('.getOrDefault() returns a type-specific default value', () { + var parser = new ArgParser(); + parser.addFlag('flag-no', defaultsTo: null); + parser.addFlag('flag-def', defaultsTo: true); + parser.addOption('single-no'); + parser.addOption('single-def', defaultsTo: 'def'); + parser.addOption('multi-no', allowMultiple: true); + parser.addOption('multi-def', allowMultiple: true, defaultsTo: 'def'); + + expect(parser.options['flag-no'].getOrDefault(null), equals(null)); + expect(parser.options['flag-no'].getOrDefault(false), equals(false)); + expect(parser.options['flag-def'].getOrDefault(null), equals(true)); + expect(parser.options['flag-def'].getOrDefault(false), equals(false)); + expect(parser.options['single-no'].getOrDefault(null), equals(null)); + expect(parser.options['single-no'].getOrDefault('v'), equals('v')); + expect(parser.options['single-def'].getOrDefault(null), equals('def')); + expect(parser.options['single-def'].getOrDefault('v'), equals('v')); + expect(parser.options['multi-no'].getOrDefault(null), equals([])); + expect(parser.options['multi-no'].getOrDefault(['v']), equals(['v'])); + expect(parser.options['multi-def'].getOrDefault(null), equals(['def'])); + expect(parser.options['multi-def'].getOrDefault(['v']), equals(['v'])); + }); }); } @@ -232,7 +290,7 @@ const _INVALID_OPTIONS = const [ ]; const _VALID_OPTIONS = const [ - 'a' // one char + 'a' // One character. 'contains-dash', 'contains_underscore', 'ends-with-dash-', From 21bad913a2d1fd063cceb04fec91105c90e0e032 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 24 Jul 2014 20:23:52 +0000 Subject: [PATCH 073/263] Remove documentation links for repo packages. pub.dartlang.org will now automatically link to dartdocs.org for packages with no documentation link. BUG= http://dartbug.com/20177, http://dartbug.com/20178, http://dartbug.com/20186 R=sigmund@google.com Review URL: https://codereview.chromium.org//416973003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@38551 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 5 +++++ pkgs/args/pubspec.yaml | 3 +-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 36b8208e..ffe21096 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.0+1 + +* Remove the documentation link from the pubspec so this is linked to + pub.dartlang.org by default. + ## 0.12.0 * Removed public constructors for `ArgResults` and `Option`. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 32e84edc..0f620105 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,8 +1,7 @@ name: args -version: 0.12.0 +version: 0.12.0+1 author: "Dart Team " homepage: http://www.dartlang.org -documentation: http://api.dartlang.org/docs/pkg/args description: > Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. From 957576a5f56fe0236759401b2e29c3c91ef50bd7 Mon Sep 17 00:00:00 2001 From: "lrn@google.com" Date: Fri, 22 Aug 2014 08:52:09 +0000 Subject: [PATCH 074/263] Update dart:collection to version 1.0.0 R=sgjesse@google.com Review URL: https://codereview.chromium.org//463333004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@39484 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 0f620105..9542666f 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.0+1 +version: 0.12.0+2 author: "Dart Team " homepage: http://www.dartlang.org description: > @@ -7,7 +7,7 @@ description: > a set of options and values using GNU and POSIX style options. dependencies: - collection: ">=0.9.0 <0.10.0" + collection: ">=0.9.0 <2.0.0" dev_dependencies: unittest: ">=0.9.0 <0.11.0" environment: From 10f96f28fdd6665b8d3599ef44e5eb8d66c89ddb Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Tue, 26 Aug 2014 19:49:44 +0000 Subject: [PATCH 075/263] Clean up after r39484 and r39486. In particular, this adds CHANGELOG entries for packages that have been modified and fixes pub's barback dependency so it doesn't declare compatibility on an uncut release. This releases args, barback, http_parser, and shelf. R=rnystrom@google.com Review URL: https://codereview.chromium.org//506973003 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@39563 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index ffe21096..ea32d897 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.0+2 + +* Widen the version constraint on the `collection` package. + ## 0.12.0+1 * Remove the documentation link from the pubspec so this is linked to From cde27ce573cc5c1957e93411e536ff9eac8c4408 Mon Sep 17 00:00:00 2001 From: "srawlins@google.com" Date: Tue, 7 Oct 2014 17:35:49 +0000 Subject: [PATCH 076/263] Fix args README BUG= https://code.google.com/p/dart/issues/detail?id=20224 R=rnystrom@google.com Review URL: https://codereview.chromium.org//634853002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@40965 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index bee1ea29..9baf1fb5 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -211,7 +211,7 @@ To define help text for an entire option, use the `help:` parameter: For non-flag options, you can also provide a help string for the parameter: - parser.addOption('out', help: 'The output path', helpValue: 'path', + parser.addOption('out', help: 'The output path', valueHelp: 'path', allowed: ['debug', 'release']); For non-flag options, you can also provide detailed help for each expected value From fe1607cd751f7892ce7e51b65057f3016cd9074b Mon Sep 17 00:00:00 2001 From: "srawlins@google.com" Date: Tue, 7 Oct 2014 17:56:16 +0000 Subject: [PATCH 077/263] Swap getUsage() for get usage in args package BUG= https://code.google.com/p/dart/issues/detail?id=12536 R=rnystrom@google.com Review URL: https://codereview.chromium.org//631123002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@40966 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 5 +++++ pkgs/args/lib/src/arg_parser.dart | 6 ++++++ pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 4 ++-- 4 files changed, 14 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index ea32d897..3759dc95 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.1 + +* Replace `ArgParser.getUsage()` with `ArgParser.usage`, a getter. + `ArgParser.getUsage()` is now deprecated, to be removed in args version 1.0.0. + ## 0.12.0+2 * Widen the version constraint on the `collection` package. diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index b1ee6280..03d8d3f5 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -115,8 +115,14 @@ class ArgParser { /// Generates a string displaying usage information for the defined options. /// /// This is basically the help text shown on the command line. + @Deprecated("Replaced with get usage. getUsage() will be removed in args 1.0") String getUsage() => new Usage(this).generate(); + /// Generates a string displaying usage information for the defined options. + /// + /// This is basically the help text shown on the command line. + String get usage => 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) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 9542666f..a19084c3 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.0+2 +version: 0.12.1-dev author: "Dart Team " homepage: http://www.dartlang.org description: > diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index b6b860d4..2c44ec36 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -8,7 +8,7 @@ import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; void main() { - group('ArgParser.getUsage()', () { + group('ArgParser.usage', () { test('negatable flags show "no-" in title', () { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); @@ -225,7 +225,7 @@ void main() { void validateUsage(ArgParser parser, String expected) { expected = unindentString(expected); - expect(parser.getUsage(), equals(expected)); + expect(parser.usage, equals(expected)); } // TODO(rnystrom): Replace one in test_utils. From 4ec07b44d40753cc648548cef28607d897f9c307 Mon Sep 17 00:00:00 2001 From: "rnystrom@google.com" Date: Fri, 31 Oct 2014 20:29:04 +0000 Subject: [PATCH 078/263] Remove "-dev" to publish args package. R=nweiz@google.com, paulberry@google.com Review URL: https://codereview.chromium.org//698463004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@41448 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index a19084c3..4dd081d3 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.1-dev +version: 0.12.1 author: "Dart Team " homepage: http://www.dartlang.org description: > From 2fd3a03ae83163f1cdb10afeb05aca7719679b89 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Thu, 11 Dec 2014 21:38:13 +0000 Subject: [PATCH 079/263] Add an ArgResults.original getter. R=rnystrom@google.com Review URL: https://codereview.chromium.org//796593004 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42301 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/src/arg_results.dart | 13 +++++++++---- pkgs/args/lib/src/parser.dart | 4 +++- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 8 ++++++++ 5 files changed, 25 insertions(+), 6 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 3759dc95..f5aaafe6 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.2 + +* Add an `ArgResults.arguments` field, which contains the original argument list. + ## 0.12.1 * Replace `ArgParser.getUsage()` with `ArgParser.usage`, a getter. diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 2e47a8e9..815d8dd7 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -13,8 +13,9 @@ import 'arg_parser.dart'; /// Since [ArgResults] doesn't have a public constructor, this lets [Parser] /// get to it. This function isn't exported to the public API of the package. ArgResults newArgResults(ArgParser parser, Map parsed, - String name, ArgResults command, List rest) { - return new ArgResults._(parser, parsed, name, command, rest); + String name, ArgResults command, List rest, + List arguments) { + return new ArgResults._(parser, parsed, name, command, rest, arguments); } /// The results of parsing a series of command line arguments using @@ -46,10 +47,14 @@ class ArgResults { /// `--` was reached. final List rest; + /// The original list of arguments that were parsed. + final List arguments; + /// Creates a new [ArgResults]. ArgResults._(this._parser, this._parsed, this.name, this.command, - List rest) - : this.rest = new UnmodifiableListView(rest); + List rest, List arguments) + : this.rest = new UnmodifiableListView(rest), + this.arguments = new UnmodifiableListView(arguments); /// Gets the parsed command-line option named [name]. operator [](String name) { diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 4d3417a1..9cd141c0 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -46,6 +46,7 @@ class Parser { /// Parses the arguments. This can only be called once. ArgResults parse() { + var arguments = args.toList(); var commandResults = null; // Parse the args. @@ -91,7 +92,8 @@ class Parser { // Add in the leftover arguments we didn't parse to the innermost command. rest.addAll(args); args.clear(); - return newArgResults(grammar, results, commandName, commandResults, rest); + return newArgResults(grammar, results, commandName, commandResults, rest, + arguments); } /// Pulls the value for [option] from the second argument in [args]. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4dd081d3..76d41b93 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.1 +version: 0.12.2-dev author: "Dart Team " homepage: http://www.dartlang.org description: > diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 6792c95e..77c23c32 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -228,6 +228,14 @@ void main() { expect(() => results.rest.add('oops'), throwsUnsupportedError); }); + test('.arguments returns the original argument list', () { + var parser = new ArgParser(); + parser.addFlag('foo'); + + var results = parser.parse(['--foo']); + expect(results.arguments, equals(['--foo'])); + }); + group('.wasParsed()', () { test('throws if the name is not an option', () { var results = new ArgParser().parse([]); From a9f632bfdf73de72c6bde38027ceb87d33254240 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 17 Dec 2014 00:05:27 +0000 Subject: [PATCH 080/263] Add a CommandRunner class for dispatching commands to args. This was extracted from pub's dispatching logic. R=rnystrom@google.com Review URL: https://codereview.chromium.org//797473002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42418 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 3 + pkgs/args/README.md | 80 ++++- pkgs/args/lib/command_runner.dart | 385 ++++++++++++++++++++++++ pkgs/args/lib/src/help_command.dart | 53 ++++ pkgs/args/lib/src/usage_exception.dart | 14 + pkgs/args/lib/src/utils.dart | 9 + pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_parse_test.dart | 207 +++++++++++++ pkgs/args/test/command_runner_test.dart | 260 ++++++++++++++++ pkgs/args/test/command_test.dart | 229 ++++---------- pkgs/args/test/utils.dart | 69 ++++- 11 files changed, 1138 insertions(+), 173 deletions(-) create mode 100644 pkgs/args/lib/command_runner.dart create mode 100644 pkgs/args/lib/src/help_command.dart create mode 100644 pkgs/args/lib/src/usage_exception.dart create mode 100644 pkgs/args/lib/src/utils.dart create mode 100644 pkgs/args/test/command_parse_test.dart create mode 100644 pkgs/args/test/command_runner_test.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index f5aaafe6..bbdc5237 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,5 +1,8 @@ ## 0.12.2 +* Add `CommandRunner` and `Command` classes which make it easy to build a + command-based command-line application. + * Add an `ArgResults.arguments` field, which contains the original argument list. ## 0.12.1 diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 9baf1fb5..d51f7cb3 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -197,6 +197,65 @@ Here, both the top-level parser and the `"commit"` command can accept a `"-a"` `"-a"` appears after `"commit"`, it is applied to that command. If it appears to the left of `"commit"`, it is given to the top-level parser. +## Dispatching Commands + +If you're writing a command-based application, you can use the [CommandRunner][] +and [Command][] classes to help structure it. [CommandRunner][] has built-in +support for dispatching to [Command][]s based on command-line arguments, as well +as handling `--help` flags and invalid arguments. For example: + + var runner = new CommandRunner("git", "Distributed version control."); + runner.addCommand(new CommitCommand()); + runner.addCommand(new StashCommand()); + runner.run(['commit', '-a']); // Calls [CommitCommand.run()] + +Custom commands are defined by extending the [Command][] class. For example: + + class CommitCommand extends Command { + // The [name] and [description] properties must be defined by every + // subclass. + final name = "commit"; + final description = "Record changes to the repository."; + + CommitCommand() { + // [argParser] is automatically created by the parent class. + argParser.addFlag('all', abbr: 'a'); + } + + // [run] may also return a Future. + void run() { + // [options] is set before [run()] is called and contains the options + // passed to this command. + print(options['all']); + } + } + +Commands can also have subcommands, which are added with [addSubcommand][]. A +command with subcommands can't run its own code, so [run][] doesn't need to be +implemented. For example: + + class StashCommand extends Command { + final String name = "stash"; + final String description = "Stash changes in the working directory."; + + StashCommand() { + addSubcommand(new StashSaveCommand()); + addSubcommand(new StashListCommand()); + } + } + +[CommandRunner][] automatically adds a `help` command that displays usage +information for commands, as well as support for the `--help` flag for all +commands. If it encounters an error parsing the arguments or processing a +command, it throws a [UsageError][]; your `main()` method should catch these and +print them appropriately. For example: + + runner.run(arguments).catchError((error) { + if (error is! UsageError) throw error; + print(error); + exit(64); // Exit code 64 indicates a usage error. + }); + ## Displaying usage You can automatically generate nice help text, suitable for use as the output of @@ -240,11 +299,16 @@ The resulting string looks something like this: [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces -[ArgParser]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser -[ArgResults]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgResults -[addOption]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addOption -[addFlag]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addFlag -[parse]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_parse -[rest]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgResults#id_rest -[addCommand]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_addCommand -[getUsage]: https://api.dartlang.org/apidocs/channels/stable/dartdoc-viewer/args/args.ArgParser#id_getUsage +[ArgParser]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser +[ArgResults]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults +[CommandRunner]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.CommandRunner +[Command]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command +[UsageError]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.UsageError +[addOption]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addOption +[addFlag]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addFlag +[parse]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_parse +[rest]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults@id_rest +[addCommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addCommand +[getUsage]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_getUsage +[addSubcommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command@id_addSubcommand +[run]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command@id_run diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart new file mode 100644 index 00000000..f2c22c13 --- /dev/null +++ b/pkgs/args/lib/command_runner.dart @@ -0,0 +1,385 @@ +// Copyright (c) 2014, 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. + +library args.command_runner; + +import 'dart:async'; +import 'dart:collection'; +import 'dart:math' as math; + +import 'src/arg_parser.dart'; +import 'src/arg_results.dart'; +import 'src/help_command.dart'; +import 'src/usage_exception.dart'; +import 'src/utils.dart'; + +export 'src/usage_exception.dart'; + +/// A class for invoking [Commands] based on raw command-line arguments. +class CommandRunner { + /// The name of the executable being run. + /// + /// Used for error reporting and [usage]. + final String executableName; + + /// A short description of this executable. + final String description; + + /// A single-line template for how to invoke this executable. + /// + /// Defaults to "$executableName [arguments]". Subclasses can + /// override this for a more specific template. + String get invocation => "$executableName [arguments]"; + + /// Generates a string displaying usage information for the executable. + /// + /// This includes usage for the global arguments as well as a list of + /// top-level commands. + String get usage => "$description\n\n$_usageWithoutDescription"; + + /// An optional footer for [usage]. + /// + /// If a subclass overrides this to return a string, it will automatically be + /// added to the end of [usage]. + final String usageFooter = null; + + /// Returns [usage] with [description] removed from the beginning. + String get _usageWithoutDescription { + var usage = ''' +Usage: $invocation + +Global options: +${argParser.usage} + +${_getCommandUsage(_commands)} + +Run "$executableName help " for more information about a command.'''; + + if (usageFooter != null) usage += "\n$usageFooter"; + return usage; + } + + /// An unmodifiable view of all top-level commands defined for this runner. + Map get commands => + new UnmodifiableMapView(_commands); + final _commands = new Map(); + + /// The top-level argument parser. + /// + /// Global options should be registered with this parser; they'll end up + /// available via [Command.globalResults]. Commands should be registered with + /// [addCommand] rather than directly on the parser. + final argParser = new ArgParser(); + + CommandRunner(this.executableName, this.description) { + argParser.addFlag('help', abbr: 'h', negatable: false, + help: 'Print this usage information.'); + addCommand(new HelpCommand()); + } + + /// Prints the usage information for this runner. + /// + /// This is called internally by [run] and can be overridden by subclasses to + /// control how output is displayed or integrate with a logging system. + void printUsage() => print(usage); + + /// Throws a [UsageException] with [message]. + void usageException(String message) => + throw new UsageException(message, _usageWithoutDescription); + + /// Adds [Command] as a top-level command to this runner. + void addCommand(Command command) { + var names = [command.name]..addAll(command.aliases); + for (var name in names) { + _commands[name] = command; + argParser.addCommand(name, command.argParser); + } + command._runner = this; + } + + /// Parses [args] and invokes [Command.run] on the chosen command. + /// + /// This always returns a [Future] in case the command is asynchronous. The + /// [Future] will throw a [UsageError] if [args] was invalid. + Future run(Iterable args) => + new Future.sync(() => runCommand(parse(args))); + + /// Parses [args] and returns the result, converting a [FormatException] to a + /// [UsageException]. + /// + /// This is notionally a protected method. It may be overridden or called from + /// subclasses, but it shouldn't be called externally. + ArgResults parse(Iterable args) { + try { + // TODO(nweiz): if arg parsing fails for a command, print that command's + // usage, not the global usage. + return argParser.parse(args); + } on FormatException catch (error) { + usageException(error.message); + } + } + + /// Runs the command specified by [topLevelResults]. + /// + /// This is notionally a protected method. It may be overridden or called from + /// subclasses, but it shouldn't be called externally. + /// + /// It's useful to override this to handle global flags and/or wrap the entire + /// command in a block. For example, you might handle the `--verbose` flag + /// here to enable verbose logging before running the command. + Future runCommand(ArgResults topLevelResults) { + return new Future.sync(() { + var argResults = topLevelResults; + var commands = _commands; + var command; + var commandString = executableName; + + while (commands.isNotEmpty) { + if (argResults.command == null) { + if (argResults.rest.isEmpty) { + if (command == null) { + // No top-level command was chosen. + printUsage(); + return new Future.value(); + } + + command.usageException('Missing subcommand for "$commandString".'); + } else { + if (command == null) { + usageException( + 'Could not find a command named "${argResults.rest[0]}".'); + } + + command.usageException('Could not find a subcommand named ' + '"${argResults.rest[0]}" for "$commandString".'); + } + } + + // Step into the command. + var parent = command; + argResults = argResults.command; + command = commands[argResults.name]; + command._globalResults = topLevelResults; + command._argResults = argResults; + commands = command._subcommands; + commandString += " ${argResults.name}"; + + if (argResults['help']) { + command.printUsage(); + return new Future.value(); + } + } + + // Make sure there aren't unexpected arguments. + if (!command.takesArguments && argResults.rest.isNotEmpty) { + command.usageException( + 'Command "${argResults.name}" does not take any arguments.'); + } + + return command.run(); + }); + } +} + +/// A single command. +/// +/// A command is known as a "leaf command" if it has no subcommands and is meant +/// to be run. Leaf commands must override [run]. +/// +/// A command with subcommands is known as a "branch command" and cannot be run +/// itself. It should call [addSubcommand] (often from the constructor) to +/// register subcommands. +abstract class Command { + /// The name of this command. + String get name; + + /// A short description of this command. + String get description; + + /// A single-line template for how to invoke this command (e.g. `"pub get + /// [package]"`). + String get invocation { + var parents = [name]; + for (var command = parent; command != null; command = command.parent) { + parents.add(command.name); + } + parents.add(runner.executableName); + + var invocation = parents.reversed.join(" "); + return _subcommands.isNotEmpty ? + "$invocation [arguments]" : + "$invocation [arguments]"; + } + + /// The command's parent command, if this is a subcommand. + /// + /// This will be `null` until [Command.addSubcommmand] has been called with + /// this command. + Command get parent => _parent; + Command _parent; + + /// The command runner for this command. + /// + /// This will be `null` until [CommandRunner.addCommand] has been called with + /// this command or one of its parents. + CommandRunner get runner { + if (parent == null) return _runner; + return parent.runner; + } + CommandRunner _runner; + + /// The parsed global argument results. + /// + /// This will be `null` until just before [Command.run] is called. + ArgResults get globalResults => _globalResults; + ArgResults _globalResults; + + /// The parsed argument results for this command. + /// + /// This will be `null` until just before [Command.run] is called. + ArgResults get argResults => _argResults; + ArgResults _argResults; + + /// The argument parser for this command. + /// + /// Options for this command should be registered with this parser (often in + /// the constructor); they'll end up available via [argResults]. Subcommands + /// should be registered with [addSubcommand] rather than directly on the + /// parser. + final argParser = new ArgParser(); + + /// Generates a string displaying usage information for this command. + /// + /// This includes usage for the command's arguments as well as a list of + /// subcommands, if there are any. + String get usage => "$description\n\n$_usageWithoutDescription"; + + /// An optional footer for [usage]. + /// + /// If a subclass overrides this to return a string, it will automatically be + /// added to the end of [usage]. + final String usageFooter = null; + + /// Returns [usage] with [description] removed from the beginning. + String get _usageWithoutDescription { + var buffer = new StringBuffer() + ..writeln('Usage: $invocation') + ..writeln(argParser.usage); + + if (_subcommands.isNotEmpty) { + buffer.writeln(); + buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true)); + } + + buffer.writeln(); + buffer.write('Run "${runner.executableName} help" to see global options.'); + + if (usageFooter != null) { + buffer.writeln(); + buffer.write(usageFooter); + } + + return buffer.toString(); + } + + /// An unmodifiable view of all sublevel commands of this command. + Map get subcommands => + new UnmodifiableMapView(_subcommands); + final _subcommands = new Map(); + + /// Whether or not this command should be hidden from help listings. + /// + /// This is intended to be overridden by commands that want to mark themselves + /// hidden. + /// + /// By default, leaf commands are always visible. Branch commands are visible + /// as long as any of their leaf commands are visible. + bool get hidden { + // Leaf commands are visible by default. + if (_subcommands.isEmpty) return false; + + // Otherwise, a command is hidden if all of its subcommands are. + return _subcommands.values.every((subcommand) => subcommand.hidden); + } + + /// Whether or not this command takes positional arguments in addition to + /// options. + /// + /// If false, [CommandRunner.run] will throw a [UsageException] if arguments + /// are provided. Defaults to true. + /// + /// This is intended to be overridden by commands that don't want to receive + /// arguments. It has no effect for branch commands. + final takesArguments = true; + + /// Alternate names for this command. + /// + /// These names won't be used in the documentation, but they will work when + /// invoked on the command line. + /// + /// This is intended to be overridden. + final aliases = const []; + + Command() { + argParser.addFlag('help', abbr: 'h', negatable: false, + help: 'Print this usage information.'); + } + + /// Runs this command. + /// + /// If this returns a [Future], [CommandRunner.run] won't complete until the + /// returned [Future] does. Otherwise, the return value is ignored. + run() { + throw new UnimplementedError("Leaf command $this must implement run()."); + } + + /// Adds [Command] as a subcommand of this. + void addSubcommand(Command command) { + var names = [command.name]..addAll(command.aliases); + for (var name in names) { + _subcommands[name] = command; + argParser.addCommand(name, command.argParser); + } + command._parent = this; + } + + /// Prints the usage information for this command. + /// + /// This is called internally by [run] and can be overridden by subclasses to + /// control how output is displayed or integrate with a logging system. + void printUsage() => print(usage); + + /// Throws a [UsageException] with [message]. + void usageException(String message) => + throw new UsageException(message, _usageWithoutDescription); +} + +/// Returns a string representation of [commands] fit for use in a usage string. +/// +/// [isSubcommand] indicates whether the commands should be called "commands" or +/// "subcommands". +String _getCommandUsage(Map commands, + {bool isSubcommand: false}) { + // Don't include aliases. + var names = commands.keys + .where((name) => !commands[name].aliases.contains(name)); + + // Filter out hidden ones, unless they are all hidden. + var visible = names.where((name) => !commands[name].hidden); + if (visible.isNotEmpty) names = visible; + + // Show the commands alphabetically. + names = names.toList()..sort(); + var length = names.map((name) => name.length).reduce(math.max); + + var buffer = new StringBuffer( + 'Available ${isSubcommand ? "sub" : ""}commands:'); + for (var name in names) { + buffer.writeln(); + buffer.write(' ${padRight(name, length)} ' + '${commands[name].description.split("\n").first}'); + } + + return buffer.toString(); +} diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart new file mode 100644 index 00000000..283f3074 --- /dev/null +++ b/pkgs/args/lib/src/help_command.dart @@ -0,0 +1,53 @@ +// Copyright (c) 2014, 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. + +library args.help_command; + +import '../command_runner.dart'; + +/// The built-in help command that's added to every [CommandRunner]. +/// +/// This command displays help information for the various subcommands. +class HelpCommand extends Command { + final name = "help"; + String get description => + "Display help information for ${runner.executableName}."; + String get usage => "${runner.executableName} help [command]"; + + void run() { + // Show the default help if no command was specified. + if (options.rest.isEmpty) { + runner.printUsage(); + return; + } + + // Walk the command tree to show help for the selected command or + // subcommand. + var commands = runner.commands; + var command = null; + var commandString = runner.executableName; + + for (var name in options.rest) { + if (commands.isEmpty) { + command.usageException( + 'Command "$commandString" does not expect a subcommand.'); + } + + if (commands[name] == null) { + if (command == null) { + runner.usageException('Could not find a command named "$name".'); + } + + command.usageException( + 'Could not find a subcommand named "$name" for "$commandString".'); + } + + command = commands[name]; + commands = command.subcommands; + commandString += " $name"; + } + + command.printUsage(); + } +} diff --git a/pkgs/args/lib/src/usage_exception.dart b/pkgs/args/lib/src/usage_exception.dart new file mode 100644 index 00000000..35cc7442 --- /dev/null +++ b/pkgs/args/lib/src/usage_exception.dart @@ -0,0 +1,14 @@ +// Copyright (c) 2014, 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. + +library args.usage_exception; + +class UsageException implements Exception { + final String message; + final String usage; + + UsageException(this.message, this.usage); + + String toString() => "$message\n\n$usage"; +} diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart new file mode 100644 index 00000000..dc355dfb --- /dev/null +++ b/pkgs/args/lib/src/utils.dart @@ -0,0 +1,9 @@ +// Copyright (c) 2014, 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. + +library args.utils; + +/// Pads [source] to [length] by adding spaces at the end. +String padRight(String source, int length) => + source + ' ' * (length - source.length); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 76d41b93..9cc9d577 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2-dev +version: 0.12.2 author: "Dart Team " homepage: http://www.dartlang.org description: > diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart new file mode 100644 index 00000000..ac530564 --- /dev/null +++ b/pkgs/args/test/command_parse_test.dart @@ -0,0 +1,207 @@ +// Copyright (c) 2012, 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. + +library command_parse_test; + +import 'package:unittest/unittest.dart'; +import 'package:args/args.dart'; +import 'utils.dart'; + +void main() { + group('ArgParser.addCommand()', () { + test('creates a new ArgParser if none is given', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + expect(parser.commands['install'], equals(command)); + expect(command is ArgParser, isTrue); + }); + + test('uses the command parser if given one', () { + var parser = new ArgParser(); + var command = new ArgParser(); + var result = parser.addCommand('install', command); + expect(parser.commands['install'], equals(command)); + expect(result, equals(command)); + }); + + test('throws on a duplicate command name', () { + var parser = new ArgParser(); + parser.addCommand('install'); + throwsIllegalArg(() => parser.addCommand('install')); + }); + }); + + group('ArgParser.parse()', () { + test('parses a command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + + var args = parser.parse(['install']); + + expect(args.command.name, equals('install')); + expect(args.rest, isEmpty); + }); + + test('parses a command option', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path'); + + var args = parser.parse(['install', '--path', 'some/path']); + expect(args.command['path'], equals('some/path')); + }); + + test('parses a parent solo option before the command', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm'); + var command = parser.addCommand('install'); + + var args = parser.parse(['-m', 'debug', 'install']); + expect(args['mode'], equals('debug')); + expect(args.command.name, equals('install')); + }); + + test('parses a parent solo option after the command', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '-m', 'debug']); + expect(args['mode'], equals('debug')); + expect(args.command.name, equals('install')); + }); + + test('parses a parent option before the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('install'); + + var args = parser.parse(['--verbose', 'install']); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent option after the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '--verbose']); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent negated option before the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose', defaultsTo: true); + var command = parser.addCommand('install'); + + var args = parser.parse(['--no-verbose', 'install']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('install')); + }); + + test('parses a parent negated option after the command', () { + var parser = new ArgParser(); + parser.addFlag('verbose', defaultsTo: true); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '--no-verbose']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('install')); + }); + + test('parses a parent abbreviation before the command', () { + var parser = new ArgParser(); + parser.addFlag('debug', abbr: 'd'); + parser.addFlag('verbose', abbr: 'v'); + var command = parser.addCommand('install'); + + var args = parser.parse(['-dv', 'install']); + expect(args['debug'], isTrue); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('parses a parent abbreviation after the command', () { + var parser = new ArgParser(); + parser.addFlag('debug', abbr: 'd'); + parser.addFlag('verbose', abbr: 'v'); + var command = parser.addCommand('install'); + + var args = parser.parse(['install', '-dv']); + expect(args['debug'], isTrue); + expect(args['verbose'], isTrue); + expect(args.command.name, equals('install')); + }); + + test('does not parse a solo command option before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path', abbr: 'p'); + + throwsFormat(parser, ['-p', 'foo', 'install']); + }); + + test('does not parse a command option before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addOption('path'); + + throwsFormat(parser, ['--path', 'foo', 'install']); + }); + + test('does not parse a command abbreviation before the command', () { + var parser = new ArgParser(); + var command = parser.addCommand('install'); + command.addFlag('debug', abbr: 'd'); + command.addFlag('verbose', abbr: 'v'); + + throwsFormat(parser, ['-dv', 'install']); + }); + + test('assigns collapsed options to the proper command', () { + var parser = new ArgParser(); + parser.addFlag('apple', abbr: 'a'); + var command = parser.addCommand('cmd'); + command.addFlag('banana', abbr: 'b'); + var subcommand = command.addCommand('subcmd'); + subcommand.addFlag('cherry', abbr: 'c'); + + var args = parser.parse(['cmd', 'subcmd', '-abc']); + expect(args['apple'], isTrue); + expect(args.command.name, equals('cmd')); + expect(args.command['banana'], isTrue); + expect(args.command.command.name, equals('subcmd')); + expect(args.command.command['cherry'], isTrue); + }); + + test('option is given to innermost command that can take it', () { + var parser = new ArgParser(); + parser.addFlag('verbose'); + var command = parser.addCommand('cmd'); + command.addFlag('verbose'); + var subcommand = command.addCommand('subcmd'); + + var args = parser.parse(['cmd', 'subcmd', '--verbose']); + expect(args['verbose'], isFalse); + expect(args.command.name, equals('cmd')); + expect(args.command['verbose'], isTrue); + expect(args.command.command.name, equals('subcmd')); + }); + + test('remaining arguments are given to the innermost command', () { + var parser = new ArgParser(); + var command = parser.addCommand('cmd'); + var subcommand = command.addCommand('subcmd'); + + var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); + expect(args.command.name, equals('cmd')); + expect(args.rest, isEmpty); + expect(args.command.command.name, equals('subcmd')); + expect(args.command.rest, isEmpty); + expect(args.command.command.rest, equals(['other', 'stuff'])); + }); + }); +} diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart new file mode 100644 index 00000000..750e4e1d --- /dev/null +++ b/pkgs/args/test/command_runner_test.dart @@ -0,0 +1,260 @@ +// Copyright (c) 2014, 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. + +library command_runner_test; + +import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:unittest/unittest.dart'; + +import 'utils.dart'; + +const _DEFAULT_USAGE = """ +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + help Display help information for test. + +Run "test help " for more information about a command."""; + +void main() { + var runner; + setUp(() { + runner = new CommandRunner("test", "A test command runner."); + }); + + test(".invocation has a sane default", () { + expect(runner.invocation, + equals("test [arguments]")); + }); + + group(".usage", () { + test("returns the usage string", () { + expect(runner.usage, equals(""" +A test command runner. + +$_DEFAULT_USAGE""")); + }); + + test("contains custom commands", () { + runner.addCommand(new FooCommand()); + + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + foo Set a value. + help Display help information for test. + +Run "test help " for more information about a command.""")); + }); + + test("contains custom options", () { + runner.argParser.addFlag("foo", help: "Do something."); + + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + --[no-]foo Do something. + +Available commands: + help Display help information for test. + +Run "test help " for more information about a command.""")); + }); + + test("doesn't print hidden commands", () { + runner.addCommand(new HiddenCommand()); + + expect(runner.usage, equals(""" +A test command runner. + +$_DEFAULT_USAGE""")); + }); + + test("doesn't print aliases", () { + runner.addCommand(new AliasedCommand()); + + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + aliased Set a value. + help Display help information for test. + +Run "test help " for more information about a command.""")); + }); + }); + + test("usageException splits up the message and usage", () { + expect(() => runner.usageException("message"), + throwsUsageError("message", _DEFAULT_USAGE)); + }); + + group("run()", () { + test("runs a command", () { + var command = new FooCommand(); + runner.addCommand(command); + + expect(runner.run(["foo"]).then((_) { + expect(command.hasRun, isTrue); + }), completes); + }); + + test("runs an asynchronous command", () { + var command = new AsyncCommand(); + runner.addCommand(command); + + expect(runner.run(["async"]).then((_) { + expect(command.hasRun, isTrue); + }), completes); + }); + + test("runs a hidden comand", () { + var command = new HiddenCommand(); + runner.addCommand(command); + + expect(runner.run(["hidden"]).then((_) { + expect(command.hasRun, isTrue); + }), completes); + }); + + test("runs an aliased comand", () { + var command = new AliasedCommand(); + runner.addCommand(command); + + expect(runner.run(["als"]).then((_) { + expect(command.hasRun, isTrue); + }), completes); + }); + + test("runs a subcommand", () { + var command = new AsyncCommand(); + runner.addCommand(new FooCommand()..addSubcommand(command)); + + expect(runner.run(["foo", "async"]).then((_) { + expect(command.hasRun, isTrue); + }), completes); + }); + + group("with --help", () { + test("with no command prints the usage", () { + expect(() => runner.run(["--help"]), prints(""" +A test command runner. + +$_DEFAULT_USAGE +""")); + }); + + test("with a command prints the usage for that command", () { + var command = new FooCommand(); + runner.addCommand(command); + + expect(() => runner.run(["foo", "--help"]), prints(""" +Set a value. + +Usage: test foo [arguments] +-h, --help Print this usage information. + +Run "test help" to see global options. +""")); + }); + }); + }); + + group("with a footer", () { + setUp(() { + runner = new CommandRunnerWithFooter("test", "A test command runner."); + }); + + test("includes the footer in the usage string", () { + expect(runner.usage, equals(""" +A test command runner. + +$_DEFAULT_USAGE +Also, footer!""")); + }); + + test("includes the footer in usage errors", () { + expect(runner.run(["--bad"]), + throwsUsageError('Could not find an option named "bad".', + "$_DEFAULT_USAGE\nAlso, footer!")); + }); + }); + + group("throws a useful error when", () { + test("arg parsing fails", () { + expect(runner.run(["--bad"]), + throwsUsageError('Could not find an option named "bad".', + _DEFAULT_USAGE)); + }); + + test("a top-level command doesn't exist", () { + expect(runner.run(["bad"]), + throwsUsageError('Could not find a command named "bad".', + _DEFAULT_USAGE)); + }); + + test("a subcommand doesn't exist", () { + runner.addCommand( + new FooCommand()..addSubcommand(new AsyncCommand())); + + expect(runner.run(["foo bad"]), + throwsUsageError('Could not find a command named "foo bad".', """ +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + foo Set a value. + help Display help information for test. + +Run "test help " for more information about a command.""")); + }); + + test("a subcommand wasn't passed", () { + runner.addCommand( + new FooCommand()..addSubcommand(new AsyncCommand())); + + expect(runner.run(["foo"]), + throwsUsageError('Missing subcommand for "test foo".', """ +Usage: test foo [arguments] +-h, --help Print this usage information. + +Available subcommands: + async Set a value asynchronously. + +Run "test help" to see global options.""")); + }); + + test("a command that doesn't take arguments was given them", () { + runner.addCommand(new FooCommand()); + + expect(runner.run(["foo", "bar"]), + throwsUsageError('Command "foo" does not take any arguments.', """ +Usage: test foo [arguments] +-h, --help Print this usage information. + +Run "test help" to see global options.""")); + }); + }); +} diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 1380e160..ae3594b1 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -1,207 +1,110 @@ -// Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file +// Copyright (c) 2014, 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. library command_test; -import 'package:unittest/unittest.dart'; import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:unittest/unittest.dart'; import 'utils.dart'; void main() { - group('ArgParser.addCommand()', () { - test('creates a new ArgParser if none is given', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - expect(parser.commands['install'], equals(command)); - expect(command is ArgParser, isTrue); - }); + var foo; + setUp(() { + foo = new FooCommand(); - test('uses the command parser if given one', () { - var parser = new ArgParser(); - var command = new ArgParser(); - var result = parser.addCommand('install', command); - expect(parser.commands['install'], equals(command)); - expect(result, equals(command)); - }); - - test('throws on a duplicate command name', () { - var parser = new ArgParser(); - parser.addCommand('install'); - throwsIllegalArg(() => parser.addCommand('install')); - }); + // Make sure [Command.runner] is set up. + new CommandRunner("test", "A test command runner.").addCommand(foo); }); - group('ArgParser.parse()', () { - test('parses a command', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - - var args = parser.parse(['install']); - - expect(args.command.name, equals('install')); - expect(args.rest, isEmpty); + group(".invocation has a sane default", () { + test("without subcommands", () { + expect(foo.invocation, + equals("test foo [arguments]")); }); - test('parses a command option', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - command.addOption('path'); - - var args = parser.parse(['install', '--path', 'some/path']); - expect(args.command['path'], equals('some/path')); + test("with subcommands", () { + foo.addSubcommand(new AsyncCommand()); + expect(foo.invocation, + equals("test foo [arguments]")); }); - test('parses a parent solo option before the command', () { - var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm'); - var command = parser.addCommand('install'); + test("for a subcommand", () { + var async = new AsyncCommand(); + foo.addSubcommand(async); - var args = parser.parse(['-m', 'debug', 'install']); - expect(args['mode'], equals('debug')); - expect(args.command.name, equals('install')); + expect(async.invocation, + equals("test foo async [arguments]")); }); + }); - test('parses a parent solo option after the command', () { - var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm'); - var command = parser.addCommand('install'); - - var args = parser.parse(['install', '-m', 'debug']); - expect(args['mode'], equals('debug')); - expect(args.command.name, equals('install')); - }); + group(".usage", () { + test("returns the usage string", () { + expect(foo.usage, equals(""" +Set a value. - test('parses a parent option before the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - var command = parser.addCommand('install'); +Usage: test foo [arguments] +-h, --help Print this usage information. - var args = parser.parse(['--verbose', 'install']); - expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); +Run "test help" to see global options.""")); }); - test('parses a parent option after the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - var command = parser.addCommand('install'); + test("contains custom options", () { + foo.argParser.addFlag("flag", help: "Do something."); - var args = parser.parse(['install', '--verbose']); - expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); - }); + expect(foo.usage, equals(""" +Set a value. - test('parses a parent negated option before the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose', defaultsTo: true); - var command = parser.addCommand('install'); +Usage: test foo [arguments] +-h, --help Print this usage information. + --[no-]flag Do something. - var args = parser.parse(['--no-verbose', 'install']); - expect(args['verbose'], isFalse); - expect(args.command.name, equals('install')); +Run "test help" to see global options.""")); }); - test('parses a parent negated option after the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose', defaultsTo: true); - var command = parser.addCommand('install'); - - var args = parser.parse(['install', '--no-verbose']); - expect(args['verbose'], isFalse); - expect(args.command.name, equals('install')); - }); + test("doesn't print hidden subcommands", () { + foo.addSubcommand(new AsyncCommand()); + foo.addSubcommand(new HiddenCommand()); - test('parses a parent abbreviation before the command', () { - var parser = new ArgParser(); - parser.addFlag('debug', abbr: 'd'); - parser.addFlag('verbose', abbr: 'v'); - var command = parser.addCommand('install'); + expect(foo.usage, equals(""" +Set a value. - var args = parser.parse(['-dv', 'install']); - expect(args['debug'], isTrue); - expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); - }); +Usage: test foo [arguments] +-h, --help Print this usage information. - test('parses a parent abbreviation after the command', () { - var parser = new ArgParser(); - parser.addFlag('debug', abbr: 'd'); - parser.addFlag('verbose', abbr: 'v'); - var command = parser.addCommand('install'); +Available subcommands: + async Set a value asynchronously. - var args = parser.parse(['install', '-dv']); - expect(args['debug'], isTrue); - expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); +Run "test help" to see global options.""")); }); - test('does not parse a solo command option before the command', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - command.addOption('path', abbr: 'p'); - - throwsFormat(parser, ['-p', 'foo', 'install']); - }); + test("doesn't print subcommand aliases", () { + foo.addSubcommand(new AliasedCommand()); - test('does not parse a command option before the command', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - command.addOption('path'); + expect(foo.usage, equals(""" +Set a value. - throwsFormat(parser, ['--path', 'foo', 'install']); - }); +Usage: test foo [arguments] +-h, --help Print this usage information. - test('does not parse a command abbreviation before the command', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); - command.addFlag('debug', abbr: 'd'); - command.addFlag('verbose', abbr: 'v'); +Available subcommands: + aliased Set a value. - throwsFormat(parser, ['-dv', 'install']); +Run "test help" to see global options.""")); }); + }); - test('assigns collapsed options to the proper command', () { - var parser = new ArgParser(); - parser.addFlag('apple', abbr: 'a'); - var command = parser.addCommand('cmd'); - command.addFlag('banana', abbr: 'b'); - var subcommand = command.addCommand('subcmd'); - subcommand.addFlag('cherry', abbr: 'c'); - - var args = parser.parse(['cmd', 'subcmd', '-abc']); - expect(args['apple'], isTrue); - expect(args.command.name, equals('cmd')); - expect(args.command['banana'], isTrue); - expect(args.command.command.name, equals('subcmd')); - expect(args.command.command['cherry'], isTrue); - }); + test("usageException splits up the message and usage", () { + expect(() => foo.usageException("message"), throwsUsageError("message", """ +Usage: test foo [arguments] +-h, --help Print this usage information. - test('option is given to innermost command that can take it', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - var command = parser.addCommand('cmd'); - command.addFlag('verbose'); - var subcommand = command.addCommand('subcmd'); - - var args = parser.parse(['cmd', 'subcmd', '--verbose']); - expect(args['verbose'], isFalse); - expect(args.command.name, equals('cmd')); - expect(args.command['verbose'], isTrue); - expect(args.command.command.name, equals('subcmd')); - }); +Run "test help" to see global options.""")); + }); - test('remaining arguments are given to the innermost command', () { - var parser = new ArgParser(); - var command = parser.addCommand('cmd'); - var subcommand = command.addCommand('subcmd'); - - var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); - expect(args.command.name, equals('cmd')); - expect(args.rest, isEmpty); - expect(args.command.command.name, equals('subcmd')); - expect(args.command.rest, isEmpty); - expect(args.command.command.rest, equals(['other', 'stuff'])); - }); + test("considers a command hidden if all its subcommands are hidden", () { + foo.addSubcommand(new HiddenCommand()); + expect(foo.hidden, isTrue); }); } diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 4586f573..1267a53c 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -4,8 +4,66 @@ library utils; -import 'package:unittest/unittest.dart'; +import 'dart:async'; + import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; +import 'package:unittest/unittest.dart'; + +class CommandRunnerWithFooter extends CommandRunner { + final usageFooter = "Also, footer!"; + + CommandRunnerWithFooter(String executableName, String description) + : super(executableName, description); +} + +class FooCommand extends Command { + var hasRun = false; + + final name = "foo"; + final description = "Set a value."; + final takesArguments = false; + + void run() { + hasRun = true; + } +} + +class HiddenCommand extends Command { + var hasRun = false; + + final name = "hidden"; + final description = "Set a value."; + final hidden = true; + final takesArguments = false; + + void run() { + hasRun = true; + } +} + +class AliasedCommand extends Command { + var hasRun = false; + + final name = "aliased"; + final description = "Set a value."; + final takesArguments = false; + final aliases = const ["alias", "als"]; + + void run() { + hasRun = true; + } +} + +class AsyncCommand extends Command { + var hasRun = false; + + final name = "async"; + final description = "Set a value asynchronously."; + final takesArguments = false; + + Future run() => new Future.value().then((_) => hasRun = true); +} void throwsIllegalArg(function, {String reason: null}) { expect(function, throwsArgumentError, reason: reason); @@ -14,3 +72,12 @@ void throwsIllegalArg(function, {String reason: null}) { void throwsFormat(ArgParser parser, List args) { expect(() => parser.parse(args), throwsFormatException); } + +Matcher throwsUsageError(message, usage) { + return throwsA(predicate((error) { + expect(error, new isInstanceOf()); + expect(error.message, message); + expect(error.usage, usage); + return true; + })); +} From 80098b1acf5ae0c6eb99d100ff71f8e2f4639927 Mon Sep 17 00:00:00 2001 From: "nweiz@google.com" Date: Wed, 17 Dec 2014 02:07:51 +0000 Subject: [PATCH 081/263] Fix the built-in help command in args. R=rnystrom@google.com TBR Review URL: https://codereview.chromium.org//812743002 git-svn-id: https://dart.googlecode.com/svn/branches/bleeding_edge/dart/pkg/args@42421 260f80e4-7a28-3924-810f-c04153c831b5 --- pkgs/args/CHANGELOG.md | 4 +++ pkgs/args/lib/command_runner.dart | 1 - pkgs/args/lib/src/help_command.dart | 6 ++--- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 35 +++++++++++++++++++++++++ pkgs/args/test/utils.dart | 2 +- 6 files changed, 44 insertions(+), 6 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index bbdc5237..2e503b70 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.2+1 + +* Fix the built-in `help` command for `CommandRunner`. + ## 0.12.2 * Add `CommandRunner` and `Command` classes which make it easy to build a diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index f2c22c13..bbc19b2e 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -157,7 +157,6 @@ Run "$executableName help " for more information about a command.'''; } // Step into the command. - var parent = command; argResults = argResults.command; command = commands[argResults.name]; command._globalResults = topLevelResults; diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index 283f3074..f477b474 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -13,11 +13,11 @@ class HelpCommand extends Command { final name = "help"; String get description => "Display help information for ${runner.executableName}."; - String get usage => "${runner.executableName} help [command]"; + String get invocation => "${runner.executableName} help [command]"; void run() { // Show the default help if no command was specified. - if (options.rest.isEmpty) { + if (argResults.rest.isEmpty) { runner.printUsage(); return; } @@ -28,7 +28,7 @@ class HelpCommand extends Command { var command = null; var commandString = runner.executableName; - for (var name in options.rest) { + for (var name in argResults.rest) { if (commands.isEmpty) { command.usageException( 'Command "$commandString" does not expect a subcommand.'); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 9cc9d577..275af81c 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2 +version: 0.12.2+1 author: "Dart Team " homepage: http://www.dartlang.org description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 750e4e1d..ba7faeb1 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -174,6 +174,41 @@ Set a value. Usage: test foo [arguments] -h, --help Print this usage information. +Run "test help" to see global options. +""")); + }); + }); + + group("with help command", () { + test("with no command prints the usage", () { + expect(() => runner.run(["help"]), prints(""" +A test command runner. + +$_DEFAULT_USAGE +""")); + }); + + test("with a command prints the usage for that command", () { + var command = new FooCommand(); + runner.addCommand(command); + + expect(() => runner.run(["help", "foo"]), prints(""" +Set a value. + +Usage: test foo [arguments] +-h, --help Print this usage information. + +Run "test help" to see global options. +""")); + }); + + test("prints its own usage", () { + expect(() => runner.run(["help", "help"]), prints(""" +Display help information for test. + +Usage: test help [command] +-h, --help Print this usage information. + Run "test help" to see global options. """)); }); diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 1267a53c..9d85fb07 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -75,7 +75,7 @@ void throwsFormat(ArgParser parser, List args) { Matcher throwsUsageError(message, usage) { return throwsA(predicate((error) { - expect(error, new isInstanceOf()); + expect(error, new isInstanceOf()); expect(error.message, message); expect(error.usage, usage); return true; From e4fedb5e4415b3736ff0c31420c182780b1a34cd Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 19 Dec 2014 13:41:41 -0800 Subject: [PATCH 082/263] Add gitignore, status, and codereview files. --- pkgs/args/.gitignore | 14 ++++++++++++++ pkgs/args/.status | 14 ++++++++++++++ pkgs/args/codereview.settings | 3 +++ 3 files changed, 31 insertions(+) create mode 100644 pkgs/args/.gitignore create mode 100644 pkgs/args/.status create mode 100644 pkgs/args/codereview.settings diff --git a/pkgs/args/.gitignore b/pkgs/args/.gitignore new file mode 100644 index 00000000..388eff0b --- /dev/null +++ b/pkgs/args/.gitignore @@ -0,0 +1,14 @@ +# Don’t commit the following directories created by pub. +.buildlog +.pub/ +build/ +packages + +# Or the files created by dart2js. +*.dart.js +*.js_ +*.js.deps +*.js.map + +# Include when developing application packages. +pubspec.lock \ No newline at end of file diff --git a/pkgs/args/.status b/pkgs/args/.status new file mode 100644 index 00000000..0a222dbd --- /dev/null +++ b/pkgs/args/.status @@ -0,0 +1,14 @@ +# Copyright (c) 2014, 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. + +# Skip non-test files ending with "_test". +packages/*: Skip +*/packages/*: Skip +*/*/packages/*: Skip +*/*/*/packages/*: Skip +*/*/*/*packages/*: Skip +*/*/*/*/*packages/*: Skip + +[ $browser ] +*: Fail, OK # Uses dart:io. diff --git a/pkgs/args/codereview.settings b/pkgs/args/codereview.settings new file mode 100644 index 00000000..da6054d3 --- /dev/null +++ b/pkgs/args/codereview.settings @@ -0,0 +1,3 @@ +CODE_REVIEW_SERVER: http://codereview.chromium.org/ +VIEW_VC: https://github.com/dart-lang/args/commit/ +CC_LIST: reviews@dartlang.org \ No newline at end of file From adc77f6e7db17af04415a32f440d986e21b53f16 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 19 Dec 2014 15:15:13 -0800 Subject: [PATCH 083/263] Don't mark tests as failing on the browser. --- pkgs/args/.status | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/args/.status b/pkgs/args/.status index 0a222dbd..ff095a2f 100644 --- a/pkgs/args/.status +++ b/pkgs/args/.status @@ -10,5 +10,6 @@ packages/*: Skip */*/*/*packages/*: Skip */*/*/*/*packages/*: Skip -[ $browser ] -*: Fail, OK # Uses dart:io. +# Only run tests from the build directory, since we don't care about the +# difference between transformed an untransformed code. +test/*: Skip From 6b899ed2996f8f6b3b269bfd993138b0e165d272 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 13 Jan 2015 17:36:31 -0800 Subject: [PATCH 084/263] fix dependency on unittest R=sigmund@google.com Review URL: https://codereview.chromium.org//849983002 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 2e503b70..916bca9d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.3-dev + +* Updated dependency constraint on `unittest`. + ## 0.12.2+1 * Fix the built-in `help` command for `CommandRunner`. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 275af81c..deb8c15c 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,7 +1,7 @@ name: args -version: 0.12.2+1 +version: 0.12.3-dev author: "Dart Team " -homepage: http://www.dartlang.org +homepage: https://github.com/dart-lang/args description: > Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. @@ -9,6 +9,6 @@ description: > dependencies: collection: ">=0.9.0 <2.0.0" dev_dependencies: - unittest: ">=0.9.0 <0.11.0" + unittest: ">=0.11.5 <0.12.0" environment: sdk: ">=1.0.0 <2.0.0" From 75d2837bdbf6a37a6fd614a559ff1049c8a766f6 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 13 Jan 2015 17:39:55 -0800 Subject: [PATCH 085/263] format code, removed unused variables and deprecated usage R=sigmund@google.com Review URL: https://codereview.chromium.org//849023002 --- pkgs/args/CHANGELOG.md | 6 +- pkgs/args/example/test_runner.dart | 155 +++++++++++++--------- pkgs/args/lib/command_runner.dart | 32 +++-- pkgs/args/lib/src/arg_parser.dart | 29 ++-- pkgs/args/lib/src/arg_results.dart | 4 +- pkgs/args/lib/src/option.dart | 17 +-- pkgs/args/lib/src/parser.dart | 27 ++-- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 52 ++++---- pkgs/args/test/command_parse_test.dart | 64 +++++---- pkgs/args/test/command_runner_test.dart | 40 +++--- pkgs/args/test/command_test.dart | 12 +- pkgs/args/test/parse_test.dart | 54 ++++---- pkgs/args/test/trailing_options_test.dart | 10 +- pkgs/args/test/usage_test.dart | 81 +++++------ 15 files changed, 298 insertions(+), 287 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 916bca9d..771bcd05 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,7 +1,11 @@ -## 0.12.3-dev +## 0.12.2+2 * Updated dependency constraint on `unittest`. +* Formatted source code. + +* Fixed use of deprecated API in example. + ## 0.12.2+1 * Fix the built-in `help` command for `CommandRunner`. diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index fac1690b..9480f417 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -14,92 +14,125 @@ import 'package:args/args.dart'; main() { var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm', defaultsTo: 'debug', + parser.addOption('mode', + abbr: 'm', + defaultsTo: 'debug', help: 'Mode in which to run the tests', allowed: ['all', 'debug', 'release']); - parser.addOption('compiler', abbr: 'c', defaultsTo: 'none', + parser.addOption('compiler', + abbr: 'c', + defaultsTo: 'none', help: 'Specify any compilation step (if needed).', allowed: ['none', 'dart2js', 'dartc'], allowedHelp: { - 'none': 'Do not compile the Dart code (run native Dart code on the' - ' VM).\n(only valid with the following runtimes: vm, drt)', - 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' - '(only valid with the following runtimes: d8, drt, chrome\n' - 'safari, ie, firefox, opera, none (compile only))', - 'dartc': 'Perform static analysis on Dart code by running dartc.\n' - '(only valid with the following runtimes: none)', - }); - - parser.addOption('runtime', abbr: 'r', defaultsTo: 'vm', + 'none': 'Do not compile the Dart code (run native Dart code on the' + ' VM).\n(only valid with the following runtimes: vm, drt)', + 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' + '(only valid with the following runtimes: d8, drt, chrome\n' + 'safari, ie, firefox, opera, none (compile only))', + 'dartc': 'Perform static analysis on Dart code by running dartc.\n' + '(only valid with the following runtimes: none)', + }); + + parser.addOption('runtime', + abbr: 'r', + defaultsTo: 'vm', help: 'Where the tests should be run.', - allowed: ['vm', 'd8', 'drt', 'dartium', 'ff', 'firefox', 'chrome', - 'safari', 'ie', 'opera', 'none'], + allowed: [ + 'vm', + 'd8', + 'drt', + 'dartium', + 'ff', + 'firefox', + 'chrome', + 'safari', + 'ie', + 'opera', + 'none' + ], allowedHelp: { - 'vm': 'Run Dart code on the standalone dart vm.', - 'd8': 'Run JavaScript from the command line using v8.', - // TODO(antonm): rename flag. - 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' - 'content shell.', - 'dartium': 'Run Dart or JavaScript in Dartium.', - 'ff': 'Run JavaScript in Firefox', - 'chrome': 'Run JavaScript in Chrome', - 'safari': 'Run JavaScript in Safari', - 'ie': 'Run JavaScript in Internet Explorer', - 'opera': 'Run JavaScript in Opera', - 'none': 'No runtime, compile only (for example, used for dartc static\n' - 'analysis tests).', - }); - - parser.addOption('arch', abbr: 'a', defaultsTo: 'ia32', + 'vm': 'Run Dart code on the standalone dart vm.', + 'd8': 'Run JavaScript from the command line using v8.', + // TODO(antonm): rename flag. + 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' + 'content shell.', + 'dartium': 'Run Dart or JavaScript in Dartium.', + 'ff': 'Run JavaScript in Firefox', + 'chrome': 'Run JavaScript in Chrome', + 'safari': 'Run JavaScript in Safari', + 'ie': 'Run JavaScript in Internet Explorer', + 'opera': 'Run JavaScript in Opera', + 'none': 'No runtime, compile only (for example, used for dartc static\n' + 'analysis tests).', + }); + + parser.addOption('arch', + abbr: 'a', + defaultsTo: 'ia32', help: 'The architecture to run tests for', allowed: ['all', 'ia32', 'x64', 'simarm']); - parser.addOption('system', abbr: 's', defaultsTo: Platform.operatingSystem, + parser.addOption('system', + abbr: 's', + defaultsTo: Platform.operatingSystem, help: 'The operating system to run tests on', allowed: ['linux', 'macos', 'windows']); - parser.addFlag('checked', defaultsTo: false, - help: 'Run tests in checked mode'); + parser.addFlag('checked', + defaultsTo: false, help: 'Run tests in checked mode'); - parser.addFlag('host-checked', defaultsTo: false, - help: 'Run compiler in checked mode'); + parser.addFlag('host-checked', + defaultsTo: false, help: 'Run compiler in checked mode'); - parser.addOption('timeout', abbr: 't', - help: 'Timeout in seconds'); + parser.addOption('timeout', abbr: 't', help: 'Timeout in seconds'); - parser.addOption('progress', abbr: 'p', defaultsTo: 'compact', + parser.addOption('progress', + abbr: 'p', + defaultsTo: 'compact', help: 'Progress indication mode', - allowed: ['compact', 'color', 'line', 'verbose', 'silent', 'status', - 'buildbot']); - - parser.addFlag('report', defaultsTo: false, + allowed: [ + 'compact', + 'color', + 'line', + 'verbose', + 'silent', + 'status', + 'buildbot' + ]); + + parser.addFlag('report', + defaultsTo: false, help: 'Print a summary report of the number of tests, by expectation'); - parser.addOption('tasks', abbr: 'j', + parser.addOption('tasks', + abbr: 'j', defaultsTo: Platform.numberOfProcessors.toString(), help: 'The number of parallel tasks to run'); - parser.addOption('shards', defaultsTo: '1', + parser.addOption('shards', + defaultsTo: '1', help: 'The number of instances that the tests will be sharded over'); - parser.addOption('shard', defaultsTo: '1', + parser.addOption('shard', + defaultsTo: '1', help: 'The index of this instance when running in sharded mode'); - parser.addFlag('verbose', abbr: 'v', defaultsTo: false, - help: 'Verbose output'); + parser.addFlag('verbose', + abbr: 'v', defaultsTo: false, help: 'Verbose output'); - parser.addFlag('list', defaultsTo: false, - help: 'List tests only, do not run them'); + parser.addFlag('list', + defaultsTo: false, help: 'List tests only, do not run them'); - parser.addFlag('keep-generated-tests', defaultsTo: false, + parser.addFlag('keep-generated-tests', + defaultsTo: false, help: 'Keep the generated files in the temporary directory'); - parser.addFlag('valgrind', defaultsTo: false, - help: 'Run tests through valgrind'); + parser.addFlag('valgrind', + defaultsTo: false, help: 'Run tests through valgrind'); - parser.addOption('special-command', - help: """ + parser.addOption('special-command', help: """ Special command support. Wraps the command line in a special command. The special command should contain an '@' character which will be replaced by the normal @@ -111,16 +144,14 @@ is 'dart file.dart' and you specify special command 'python -u valgrind.py dart file.dart suffix'"""); parser.addFlag('time', - help: 'Print timing information after running tests', - defaultsTo: false); + help: 'Print timing information after running tests', defaultsTo: false); - parser.addOption('dart', help: 'Path to dart executable'); - parser.addOption('drt', help: 'Path to content shell executable'); + parser.addOption('dart', help: 'Path to dart executable'); + parser.addOption('drt', help: 'Path to content shell executable'); parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); - parser.addFlag('batch', abbr: 'b', - help: 'Run browser tests in batch mode', - defaultsTo: true); + parser.addFlag('batch', + abbr: 'b', help: 'Run browser tests in batch mode', defaultsTo: true); - print(parser.getUsage()); + print(parser.usage); } diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index bbc19b2e..d728cb02 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -61,8 +61,7 @@ Run "$executableName help " for more information about a command.'''; } /// An unmodifiable view of all top-level commands defined for this runner. - Map get commands => - new UnmodifiableMapView(_commands); + Map get commands => new UnmodifiableMapView(_commands); final _commands = new Map(); /// The top-level argument parser. @@ -73,8 +72,8 @@ Run "$executableName help " for more information about a command.'''; final argParser = new ArgParser(); CommandRunner(this.executableName, this.description) { - argParser.addFlag('help', abbr: 'h', negatable: false, - help: 'Print this usage information.'); + argParser.addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); addCommand(new HelpCommand()); } @@ -206,9 +205,9 @@ abstract class Command { parents.add(runner.executableName); var invocation = parents.reversed.join(" "); - return _subcommands.isNotEmpty ? - "$invocation [arguments]" : - "$invocation [arguments]"; + return _subcommands.isNotEmpty + ? "$invocation [arguments]" + : "$invocation [arguments]"; } /// The command's parent command, if this is a subcommand. @@ -263,8 +262,8 @@ abstract class Command { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { var buffer = new StringBuffer() - ..writeln('Usage: $invocation') - ..writeln(argParser.usage); + ..writeln('Usage: $invocation') + ..writeln(argParser.usage); if (_subcommands.isNotEmpty) { buffer.writeln(); @@ -283,8 +282,7 @@ abstract class Command { } /// An unmodifiable view of all sublevel commands of this command. - Map get subcommands => - new UnmodifiableMapView(_subcommands); + Map get subcommands => new UnmodifiableMapView(_subcommands); final _subcommands = new Map(); /// Whether or not this command should be hidden from help listings. @@ -321,8 +319,8 @@ abstract class Command { final aliases = const []; Command() { - argParser.addFlag('help', abbr: 'h', negatable: false, - help: 'Print this usage information.'); + argParser.addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); } /// Runs this command. @@ -361,8 +359,8 @@ abstract class Command { String _getCommandUsage(Map commands, {bool isSubcommand: false}) { // Don't include aliases. - var names = commands.keys - .where((name) => !commands[name].aliases.contains(name)); + var names = + commands.keys.where((name) => !commands[name].aliases.contains(name)); // Filter out hidden ones, unless they are all hidden. var visible = names.where((name) => !commands[name].hidden); @@ -372,8 +370,8 @@ String _getCommandUsage(Map commands, names = names.toList()..sort(); var length = names.map((name) => name.length).reduce(math.max); - var buffer = new StringBuffer( - 'Available ${isSubcommand ? "sub" : ""}commands:'); + var buffer = + new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { buffer.writeln(); buffer.write(' ${padRight(name, length)} ' diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 03d8d3f5..8337c42f 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -33,18 +33,19 @@ class ArgParser { /// after it finds an argument that is neither an option nor a command. /// This allows options to be specified after regular arguments. Defaults to /// `false`. - factory ArgParser({bool allowTrailingOptions}) => - new ArgParser._({}, {}, - allowTrailingOptions: allowTrailingOptions); + factory ArgParser({bool allowTrailingOptions}) => new ArgParser._( + {}, {}, + allowTrailingOptions: allowTrailingOptions); ArgParser._(Map options, Map commands, - {bool allowTrailingOptions}) : - this._options = options, - this.options = new UnmodifiableMapView(options), - this._commands = commands, - this.commands = new UnmodifiableMapView(commands), - this.allowTrailingOptions = allowTrailingOptions != null ? - allowTrailingOptions : false; + {bool allowTrailingOptions}) + : this._options = options, + this.options = new UnmodifiableMapView(options), + this._commands = commands, + this.commands = new UnmodifiableMapView(commands), + this.allowTrailingOptions = allowTrailingOptions != null + ? allowTrailingOptions + : false; /// Defines a command. /// @@ -86,8 +87,8 @@ class ArgParser { void _addOption(String name, String abbr, String help, String valueHelp, List allowed, Map allowedHelp, defaultsTo, - void callback(value), OptionType type, {bool negatable: false, - bool hide: false}) { + void callback(value), OptionType type, + {bool negatable: false, bool hide: false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { throw new ArgumentError('Duplicate option "$name".'); @@ -103,8 +104,8 @@ class ArgParser { } _options[name] = newOption(name, abbr, help, valueHelp, allowed, - allowedHelp, defaultsTo, callback, type, negatable: negatable, - hide: hide); + allowedHelp, defaultsTo, callback, type, + negatable: negatable, hide: hide); } /// Parses [args], a list of command-line arguments, matches them against the diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 815d8dd7..de9cca70 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -13,8 +13,8 @@ import 'arg_parser.dart'; /// Since [ArgResults] doesn't have a public constructor, this lets [Parser] /// get to it. This function isn't exported to the public API of the package. ArgResults newArgResults(ArgParser parser, Map parsed, - String name, ArgResults command, List rest, - List arguments) { + String name, ArgResults command, List rest, + List arguments) { return new ArgResults._(parser, parsed, name, command, rest, arguments); } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 6edc8abc..b2292dbb 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -43,12 +43,13 @@ class Option { Option._(this.name, this.abbreviation, this.help, this.valueHelp, List allowed, Map allowedHelp, this.defaultValue, - this.callback, this.type, {this.negatable, this.hide: false}) : - this.allowed = allowed == null ? - null : new UnmodifiableListView(allowed), - this.allowedHelp = allowedHelp == null ? - null : new UnmodifiableMapView(allowedHelp) { - + this.callback, this.type, {this.negatable, this.hide: false}) + : this.allowed = allowed == null + ? null + : new UnmodifiableListView(allowed), + this.allowedHelp = allowedHelp == null + ? null + : new UnmodifiableMapView(allowedHelp) { if (name.isEmpty) { throw new ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { @@ -63,7 +64,7 @@ class Option { if (abbreviation != null) { if (abbreviation.length != 1) { throw new ArgumentError('Abbreviation must be null or have length 1.'); - } else if(abbreviation == '-') { + } else if (abbreviation == '-') { throw new ArgumentError('Abbreviation cannot be "-".'); } @@ -121,4 +122,4 @@ class OptionType { final String name; const OptionType._(this.name); -} \ No newline at end of file +} diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 9cd141c0..1e297b0e 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -92,8 +92,8 @@ class Parser { // Add in the leftover arguments we didn't parse to the innermost command. rest.addAll(args); args.clear(); - return newArgResults(grammar, results, commandName, commandResults, rest, - arguments); + return newArgResults( + grammar, results, commandName, commandResults, rest, arguments); } /// Pulls the value for [option] from the second argument in [args]. @@ -101,8 +101,7 @@ class Parser { /// Validates that there is a valid value there. void readNextArgAsValue(Option option) { // Take the option argument from the next command line arg. - validate(args.length > 0, - 'Missing argument for "${option.name}".'); + validate(args.length > 0, 'Missing argument for "${option.name}".'); // Make sure it isn't an option itself. validate(!_ABBR_OPT.hasMatch(current) && !_LONG_OPT.hasMatch(current), @@ -124,8 +123,8 @@ class Parser { var option = grammar.findByAbbreviation(soloOpt[1]); if (option == null) { // Walk up to the parent command if possible. - validate(parent != null, - 'Could not find an option or flag "-${soloOpt[1]}".'); + validate( + parent != null, 'Could not find an option or flag "-${soloOpt[1]}".'); return parent.parseSoloOption(); } @@ -153,8 +152,8 @@ class Parser { var first = grammar.findByAbbreviation(c); if (first == null) { // Walk up to the parent command if possible. - validate(parent != null, - 'Could not find an option with short name "-$c".'); + validate( + parent != null, 'Could not find an option with short name "-$c".'); return parent.parseAbbreviation(innermostCommand); } else if (!first.isFlag) { // The first character is a non-flag option, so the rest must be the @@ -165,8 +164,8 @@ class Parser { // 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]}".'); + 'Option "-$c" is a flag and cannot handle value ' + '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".'); // Not an option, so all characters should be flags. // We use "innermostCommand" here so that if a parent command parses the @@ -186,16 +185,16 @@ class Parser { var option = grammar.findByAbbreviation(c); if (option == null) { // Walk up to the parent command if possible. - validate(parent != null, - 'Could not find an option with short name "-$c".'); + validate( + parent != null, 'Could not find an option with short name "-$c".'); parent.parseShortFlag(c); return; } // 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 "-".'); + validate( + option.isFlag, 'Option "-$c" must be a flag to be in a collapsed "-".'); setOption(results, option, true); } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index deb8c15c..c625ad0e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.3-dev +version: 0.12.2+2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 77c23c32..dd1634d1 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -29,7 +29,7 @@ void main() { }); test('throws ArgumentError if the abbreviation is longer ' - 'than one character', () { + 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); }); @@ -37,7 +37,7 @@ void main() { test('throws ArgumentError if a flag name is invalid', () { var parser = new ArgParser(); - for(var name in _INVALID_OPTIONS) { + for (var name in _INVALID_OPTIONS) { var reason = '${Error.safeToString(name)} is not valid'; throwsIllegalArg(() => parser.addFlag(name), reason: reason); } @@ -46,7 +46,7 @@ void main() { test('accepts valid flag names', () { var parser = new ArgParser(); - for(var name in _VALID_OPTIONS) { + for (var name in _VALID_OPTIONS) { var reason = '${Error.safeToString(name)} is valid'; expect(() => parser.addFlag(name), returnsNormally, reason: reason); } @@ -73,7 +73,7 @@ void main() { }); test('throws ArgumentError if the abbreviation is longer ' - 'than one character', () { + 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); }); @@ -85,7 +85,7 @@ void main() { test('throws ArgumentError if the abbreviation is an invalid value', () { var parser = new ArgParser(); - for(var name in _INVALID_OPTIONS.where((v) => v != null)) { + for (var name in _INVALID_OPTIONS.where((v) => v != null)) { throwsIllegalArg(() => parser.addOption('flummox', abbr: name)); } }); @@ -103,7 +103,7 @@ void main() { test('throws ArgumentError if an option name is invalid', () { var parser = new ArgParser(); - for(var name in _INVALID_OPTIONS) { + for (var name in _INVALID_OPTIONS) { var reason = '${Error.safeToString(name)} is not valid'; throwsIllegalArg(() => parser.addOption(name), reason: reason); } @@ -112,7 +112,7 @@ void main() { test('accepts valid option names', () { var parser = new ArgParser(); - for(var name in _VALID_OPTIONS) { + for (var name in _VALID_OPTIONS) { var reason = '${Error.safeToString(name)} is valid'; expect(() => parser.addOption(name), returnsNormally, reason: reason); } @@ -286,24 +286,28 @@ void main() { } const _INVALID_OPTIONS = const [ - ' ', '', '-', '--', '--foo', - ' with space', - 'with\ttab', - 'with\rcarriage\rreturn', - 'with\nline\nfeed', - "'singlequotes'", - '"doublequotes"', - 'back\\slash', - 'forward/slash' + ' ', + '', + '-', + '--', + '--foo', + ' with space', + 'with\ttab', + 'with\rcarriage\rreturn', + 'with\nline\nfeed', + "'singlequotes'", + '"doublequotes"', + 'back\\slash', + 'forward/slash' ]; const _VALID_OPTIONS = const [ - 'a' // One character. - 'contains-dash', - 'contains_underscore', - 'ends-with-dash-', - 'contains--doubledash--', - '1starts-with-number', - 'contains-a-1number', - 'ends-with-a-number8' + 'a' // One character. + 'contains-dash', + 'contains_underscore', + 'ends-with-dash-', + 'contains--doubledash--', + '1starts-with-number', + 'contains-a-1number', + 'ends-with-a-number8' ]; diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index ac530564..e336e715 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -34,8 +34,7 @@ void main() { group('ArgParser.parse()', () { test('parses a command', () { - var parser = new ArgParser(); - var command = parser.addCommand('install'); + var parser = new ArgParser()..addCommand('install'); var args = parser.parse(['install']); @@ -53,9 +52,9 @@ void main() { }); test('parses a parent solo option before the command', () { - var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addOption('mode', abbr: 'm') + ..addCommand('install'); var args = parser.parse(['-m', 'debug', 'install']); expect(args['mode'], equals('debug')); @@ -63,9 +62,9 @@ void main() { }); test('parses a parent solo option after the command', () { - var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addOption('mode', abbr: 'm') + ..addCommand('install'); var args = parser.parse(['install', '-m', 'debug']); expect(args['mode'], equals('debug')); @@ -73,9 +72,9 @@ void main() { }); test('parses a parent option before the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('verbose') + ..addCommand('install'); var args = parser.parse(['--verbose', 'install']); expect(args['verbose'], isTrue); @@ -83,9 +82,9 @@ void main() { }); test('parses a parent option after the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('verbose') + ..addCommand('install'); var args = parser.parse(['install', '--verbose']); expect(args['verbose'], isTrue); @@ -93,9 +92,9 @@ void main() { }); test('parses a parent negated option before the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose', defaultsTo: true); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('verbose', defaultsTo: true) + ..addCommand('install'); var args = parser.parse(['--no-verbose', 'install']); expect(args['verbose'], isFalse); @@ -103,9 +102,9 @@ void main() { }); test('parses a parent negated option after the command', () { - var parser = new ArgParser(); - parser.addFlag('verbose', defaultsTo: true); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('verbose', defaultsTo: true) + ..addCommand('install'); var args = parser.parse(['install', '--no-verbose']); expect(args['verbose'], isFalse); @@ -113,10 +112,10 @@ void main() { }); test('parses a parent abbreviation before the command', () { - var parser = new ArgParser(); - parser.addFlag('debug', abbr: 'd'); - parser.addFlag('verbose', abbr: 'v'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('debug', abbr: 'd') + ..addFlag('verbose', abbr: 'v') + ..addCommand('install'); var args = parser.parse(['-dv', 'install']); expect(args['debug'], isTrue); @@ -125,10 +124,10 @@ void main() { }); test('parses a parent abbreviation after the command', () { - var parser = new ArgParser(); - parser.addFlag('debug', abbr: 'd'); - parser.addFlag('verbose', abbr: 'v'); - var command = parser.addCommand('install'); + var parser = new ArgParser() + ..addFlag('debug', abbr: 'd') + ..addFlag('verbose', abbr: 'v') + ..addCommand('install'); var args = parser.parse(['install', '-dv']); expect(args['debug'], isTrue); @@ -180,9 +179,9 @@ void main() { test('option is given to innermost command that can take it', () { var parser = new ArgParser(); parser.addFlag('verbose'); - var command = parser.addCommand('cmd'); - command.addFlag('verbose'); - var subcommand = command.addCommand('subcmd'); + parser.addCommand('cmd') + ..addFlag('verbose') + ..addCommand('subcmd'); var args = parser.parse(['cmd', 'subcmd', '--verbose']); expect(args['verbose'], isFalse); @@ -193,8 +192,7 @@ void main() { test('remaining arguments are given to the innermost command', () { var parser = new ArgParser(); - var command = parser.addCommand('cmd'); - var subcommand = command.addCommand('subcmd'); + parser.addCommand('cmd')..addCommand('subcmd'); var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); expect(args.command.name, equals('cmd')); diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index ba7faeb1..904a0380 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -4,7 +4,6 @@ library command_runner_test; -import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:unittest/unittest.dart'; @@ -28,8 +27,7 @@ void main() { }); test(".invocation has a sane default", () { - expect(runner.invocation, - equals("test [arguments]")); + expect(runner.invocation, equals("test [arguments]")); }); group(".usage", () { @@ -200,7 +198,7 @@ Usage: test foo [arguments] Run "test help" to see global options. """)); - }); + }); test("prints its own usage", () { expect(() => runner.run(["help", "help"]), prints(""" @@ -229,31 +227,28 @@ Also, footer!""")); }); test("includes the footer in usage errors", () { - expect(runner.run(["--bad"]), - throwsUsageError('Could not find an option named "bad".', - "$_DEFAULT_USAGE\nAlso, footer!")); + expect(runner.run(["--bad"]), throwsUsageError( + 'Could not find an option named "bad".', + "$_DEFAULT_USAGE\nAlso, footer!")); }); }); group("throws a useful error when", () { test("arg parsing fails", () { - expect(runner.run(["--bad"]), - throwsUsageError('Could not find an option named "bad".', - _DEFAULT_USAGE)); + expect(runner.run(["--bad"]), throwsUsageError( + 'Could not find an option named "bad".', _DEFAULT_USAGE)); }); test("a top-level command doesn't exist", () { - expect(runner.run(["bad"]), - throwsUsageError('Could not find a command named "bad".', - _DEFAULT_USAGE)); + expect(runner.run(["bad"]), throwsUsageError( + 'Could not find a command named "bad".', _DEFAULT_USAGE)); }); test("a subcommand doesn't exist", () { - runner.addCommand( - new FooCommand()..addSubcommand(new AsyncCommand())); + runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo bad"]), - throwsUsageError('Could not find a command named "foo bad".', """ + expect(runner.run(["foo bad"]), throwsUsageError( + 'Could not find a command named "foo bad".', """ Usage: test [arguments] Global options: @@ -267,11 +262,10 @@ Run "test help " for more information about a command.""")); }); test("a subcommand wasn't passed", () { - runner.addCommand( - new FooCommand()..addSubcommand(new AsyncCommand())); + runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo"]), - throwsUsageError('Missing subcommand for "test foo".', """ + expect(runner.run(["foo"]), throwsUsageError( + 'Missing subcommand for "test foo".', """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -284,8 +278,8 @@ Run "test help" to see global options.""")); test("a command that doesn't take arguments was given them", () { runner.addCommand(new FooCommand()); - expect(runner.run(["foo", "bar"]), - throwsUsageError('Command "foo" does not take any arguments.', """ + expect(runner.run(["foo", "bar"]), throwsUsageError( + 'Command "foo" does not take any arguments.', """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index ae3594b1..fe3446c0 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -4,7 +4,6 @@ library command_test; -import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:unittest/unittest.dart'; import 'utils.dart'; @@ -14,28 +13,25 @@ void main() { setUp(() { foo = new FooCommand(); - // Make sure [Command.runner] is set up. + // Make sure [Command.runner] is set up. new CommandRunner("test", "A test command runner.").addCommand(foo); }); group(".invocation has a sane default", () { test("without subcommands", () { - expect(foo.invocation, - equals("test foo [arguments]")); + expect(foo.invocation, equals("test foo [arguments]")); }); test("with subcommands", () { foo.addSubcommand(new AsyncCommand()); - expect(foo.invocation, - equals("test foo [arguments]")); + expect(foo.invocation, equals("test foo [arguments]")); }); test("for a subcommand", () { var async = new AsyncCommand(); foo.addSubcommand(async); - expect(async.invocation, - equals("test foo async [arguments]")); + expect(async.invocation, equals("test foo async [arguments]")); }); }); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 56320d60..0dd00ba4 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -105,17 +105,16 @@ void main() { var parser = new ArgParser(); parser.addFlag('a', callback: (value) => a = value); - var args = parser.parse(['--a']); + parser.parse(['--a']); expect(a, isTrue); }); test('for absent flags are invoked with the default value', () { var a; var parser = new ArgParser(); - parser.addFlag('a', defaultsTo: false, - callback: (value) => a = value); + parser.addFlag('a', defaultsTo: false, callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, isFalse); }); @@ -124,7 +123,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('a', callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, isFalse); }); @@ -133,17 +132,16 @@ void main() { var parser = new ArgParser(); parser.addOption('a', callback: (value) => a = value); - var args = parser.parse(['--a=v']); + parser.parse(['--a=v']); expect(a, equals('v')); }); test('for absent options are invoked with the default value', () { var a; var parser = new ArgParser(); - parser.addOption('a', defaultsTo: 'v', - callback: (value) => a = value); + parser.addOption('a', defaultsTo: 'v', callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, equals('v')); }); @@ -152,51 +150,53 @@ void main() { var parser = new ArgParser(); parser.addOption('a', callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, isNull); }); test('for multiple present, allowMultiple, options are invoked with ' - 'value as a list', () { + 'value as a list', () { var a; var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); - var args = parser.parse(['--a=v', '--a=x']); + parser.parse(['--a=v', '--a=x']); expect(a, equals(['v', 'x'])); }); test('for single present, allowMultiple, options are invoked with ' - ' value as a single element list', () { + ' value as a single element list', () { var a; var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); - var args = parser.parse(['--a=v']); + parser.parse(['--a=v']); expect(a, equals(['v'])); }); test('for absent, allowMultiple, options are invoked with default ' - 'value as a list.', () { + 'value as a list.', () { var a; var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, defaultsTo: 'v', + parser.addOption('a', + allowMultiple: true, + defaultsTo: 'v', callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, equals(['v'])); }); test('for absent, allowMultiple, options are invoked with value ' - 'as an empty list.', () { + 'as an empty list.', () { var a; var parser = new ArgParser(); - parser.addOption('a', allowMultiple: true, - callback: (value) => a = value); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); - var args = parser.parse([]); + parser.parse([]); expect(a, isEmpty); }); }); @@ -392,11 +392,11 @@ void main() { var args = parser.parse(['--define=1']); expect(args['define'], equals(['1'])); args = parser.parse(['--define=1', '--define=2']); - expect(args['define'], equals(['1','2'])); + expect(args['define'], equals(['1', '2'])); }); test('returns the default value for multi-valued arguments ' - 'if not explicitly set', () { + 'if not explicitly set', () { var parser = new ArgParser(); parser.addOption('define', defaultsTo: '0', allowMultiple: true); var args = parser.parse(['']); diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index 2ee860ca..24f6ce73 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -19,9 +19,9 @@ void main() { parser = new ArgParser(allowTrailingOptions: true); }); - void expectThrows(List args) => - expect(() => parser.parse(args), throwsFormatException, - reason: "with allowTrailingOptions: true"); + void expectThrows(List args) => expect( + () => parser.parse(args), throwsFormatException, + reason: "with allowTrailingOptions: true"); test('collects non-options in rest', () { parser.addFlag('flag'); @@ -84,8 +84,8 @@ void main() { test("uses the innermost command's trailing options behavior", () { var parser = new ArgParser(allowTrailingOptions: true); parser.addFlag('flag', abbr: 'f'); - var command = parser.addCommand('cmd', - new ArgParser(allowTrailingOptions: false)); + var command = + parser.addCommand('cmd', new ArgParser(allowTrailingOptions: false)); command.addFlag('verbose', abbr: 'v'); var results = parser.parse(['a', '-f', 'b']); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 2c44ec36..e7f3e477 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -13,8 +13,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]mode The mode '''); }); @@ -23,8 +22,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', negatable: false, help: 'The mode'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --mode The mode '''); }); @@ -33,8 +31,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]mode The mode '''); }); @@ -44,8 +41,7 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'The mode'); parser.addOption('long', help: 'Lacks an abbreviation'); - validateUsage(parser, - ''' + validateUsage(parser, ''' -m, --[no-]mode The mode --long Lacks an abbreviation '''); @@ -56,8 +52,7 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); parser.addOption('a-really-long-name', help: 'Its help text'); - validateUsage(parser, - ''' + validateUsage(parser, ''' -m, --[no-]mode Lined up with below --a-really-long-name Its help text '''); @@ -67,8 +62,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]mode After newlines '''); }); @@ -77,8 +71,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]mode Before newlines '''); }); @@ -89,8 +82,7 @@ void main() { parser.addFlag('monkey', help: 'Second'); parser.addFlag('wombat', help: 'Third'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]zebra First --[no-]monkey Second --[no-]wombat Third @@ -102,8 +94,7 @@ void main() { parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); parser.addFlag('negate', help: 'Should be off', defaultsTo: false); - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]affirm Should be on (defaults to on) @@ -115,8 +106,7 @@ void main() { var parser = new ArgParser(); parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); - validateUsage(parser, - ''' + validateUsage(parser, ''' --any Can be anything (defaults to "whatevs") '''); @@ -124,22 +114,21 @@ void main() { test('the value help is shown', () { var parser = new ArgParser(); - parser.addOption('out', abbr: 'o', help: 'Where to write file', - valueHelp: 'path'); + parser.addOption('out', + abbr: 'o', help: 'Where to write file', valueHelp: 'path'); - validateUsage(parser, - ''' + validateUsage(parser, ''' -o, --out= Where to write file '''); }); test('the allowed list is shown', () { var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', + parser.addOption('suit', + help: 'Like in cards', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage(parser, - ''' + validateUsage(parser, ''' --suit Like in cards [spades, clubs, hearts, diamonds] '''); @@ -147,11 +136,12 @@ void main() { test('the default is highlighted in the allowed list', () { var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + parser.addOption('suit', + help: 'Like in cards', + defaultsTo: 'clubs', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage(parser, - ''' + validateUsage(parser, ''' --suit Like in cards [spades, clubs (default), hearts, diamonds] '''); @@ -159,17 +149,18 @@ void main() { test('the allowed help is shown', () { var parser = new ArgParser(); - parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', + parser.addOption('suit', + help: 'Like in cards', + defaultsTo: 'clubs', allowed: ['spades', 'clubs', 'diamonds', 'hearts'], allowedHelp: { - 'spades': 'Swords of a soldier', - 'clubs': 'Weapons of war', - 'diamonds': 'Money for this art', - 'hearts': 'The shape of my heart' - }); - - validateUsage(parser, - ''' + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage(parser, ''' --suit Like in cards [clubs] Weapons of war @@ -185,9 +176,7 @@ void main() { parser.addOption('second', hide: true); parser.addOption('third', help: 'The third option'); - - validateUsage(parser, - ''' + validateUsage(parser, ''' --first The first option --third The third option '''); @@ -199,9 +188,7 @@ void main() { parser.addFlag('second', hide: true); parser.addFlag('third', help: 'The third flag'); - - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]first The first flag --[no-]third The third flag '''); @@ -213,9 +200,7 @@ void main() { parser.addFlag('second-very-long-option', hide: true); parser.addFlag('third', help: 'The third flag'); - - validateUsage(parser, - ''' + validateUsage(parser, ''' --[no-]first The first flag --[no-]third The third flag '''); From 672a8c2470e8beccb223ea9c27fda1a2318bff8d Mon Sep 17 00:00:00 2001 From: Jacob Richman Date: Fri, 23 Jan 2015 13:22:55 -0800 Subject: [PATCH 086/263] Fixed implementation of ArgResults.options to really use Iterable instead of Iterable cast to Iterable. BUG= R=sigmund@google.com Review URL: https://codereview.chromium.org//870883006 --- pkgs/args/CHANGELOG.md | 5 +++++ pkgs/args/lib/src/arg_results.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 7 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 771bcd05..319e000a 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.12.2+3 + +* Fixed implementation of ArgResults.options to really use Iterable + instead of Iterable cast to Iterable. + ## 0.12.2+2 * Updated dependency constraint on `unittest`. diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index de9cca70..15fb5bbc 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -70,7 +70,7 @@ class ArgResults { /// This includes the options whose values were parsed or that have defaults. /// Options that weren't present and have no default will be omitted. Iterable get options { - var result = new Set.from(_parsed.keys); + var result = new Set.from(_parsed.keys); // Include the options that have defaults. _parser.options.forEach((name, option) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index c625ad0e..07af8ec5 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2+2 +version: 0.12.2+3 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 21f87c3481e7e9a42f060309f26ed87e7d6c5394 Mon Sep 17 00:00:00 2001 From: Seth Ladd Date: Tue, 10 Feb 2015 12:27:16 -0800 Subject: [PATCH 087/263] Explicit example of parsing args from main --- pkgs/args/README.md | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index d51f7cb3..211c5dc6 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -69,7 +69,14 @@ calling [ArgParser.parse()][parse] with a set of arguments: var results = parser.parse(['some', 'command', 'line', 'args']); -These arguments usually come from the arguments to `main()`, but you can pass in +These arguments usually come from the arguments to `main()`. For example: + + main(List args) { + // ... + var results = parser.parse(args); + } + +However, you can pass in any list of strings. The `parse()` method returns an instance of [ArgResults][], a map-like object that contains the values of the parsed options. From 819da54401ef3bd785355a7c4ab4b364c86eda84 Mon Sep 17 00:00:00 2001 From: Seth Ladd Date: Tue, 10 Feb 2015 12:32:07 -0800 Subject: [PATCH 088/263] reflowing --- pkgs/args/README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 211c5dc6..e665a1f5 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -76,9 +76,9 @@ These arguments usually come from the arguments to `main()`. For example: var results = parser.parse(args); } -However, you can pass in -any list of strings. The `parse()` method returns an instance of [ArgResults][], -a map-like object that contains the values of the parsed options. +However, you can pass in any list of strings. The `parse()` method returns an +instance of [ArgResults][], a map-like object that contains the values of the +parsed options. var parser = new ArgParser(); parser.addOption('mode'); From cff4fda5e6b02807374329b0e71bd6fbe1618801 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 11 Feb 2015 10:44:59 -0800 Subject: [PATCH 089/263] Update CHANGELOG and pubspec version. --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 319e000a..f9724613 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.2+4 + +* Add an example of using command-line arguments to the README. + ## 0.12.2+3 * Fixed implementation of ArgResults.options to really use Iterable diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 07af8ec5..dc77d992 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2+3 +version: 0.12.2+4 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 7b24b42788c8ba1bfe27cf9044e59648eab2419f Mon Sep 17 00:00:00 2001 From: Seth Ladd Date: Fri, 13 Feb 2015 13:56:37 -0800 Subject: [PATCH 090/263] Are you a fan of method cascades? --- pkgs/args/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index e665a1f5..bee2f969 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -127,9 +127,9 @@ You can set it to false using the following: Multiple flag abbreviations can be collapsed into a single argument. Say you define these flags: - parser.addFlag('verbose', abbr: 'v'); - parser.addFlag('french', abbr: 'f'); - parser.addFlag('iambic-pentameter', abbr: 'i'); + parser.addFlag('verbose', abbr: 'v') + ..addFlag('french', abbr: 'f') + ..addFlag('iambic-pentameter', abbr: 'i'); You can set all three flags at once: @@ -211,10 +211,10 @@ and [Command][] classes to help structure it. [CommandRunner][] has built-in support for dispatching to [Command][]s based on command-line arguments, as well as handling `--help` flags and invalid arguments. For example: - var runner = new CommandRunner("git", "Distributed version control."); - runner.addCommand(new CommitCommand()); - runner.addCommand(new StashCommand()); - runner.run(['commit', '-a']); // Calls [CommitCommand.run()] + var runner = new CommandRunner("git", "Distributed version control.") + ..addCommand(new CommitCommand()) + ..addCommand(new StashCommand()) + ..run(['commit', '-a']); // Calls [CommitCommand.run()] Custom commands are defined by extending the [Command][] class. For example: From fc6dd115ada78c141fdd3042a5d9e17de593f3ce Mon Sep 17 00:00:00 2001 From: Oliver Date: Sun, 15 Feb 2015 10:27:17 +0100 Subject: [PATCH 091/263] Fix example addFlag doesn't return an instance. --- pkgs/args/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index bee2f969..fe25d89e 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -127,7 +127,8 @@ You can set it to false using the following: Multiple flag abbreviations can be collapsed into a single argument. Say you define these flags: - parser.addFlag('verbose', abbr: 'v') + parser + ..addFlag('verbose', abbr: 'v') ..addFlag('french', abbr: 'f') ..addFlag('iambic-pentameter', abbr: 'i'); From 06b40382473a4e259db88ea2273257e576668bf4 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 17 Feb 2015 10:01:24 -0800 Subject: [PATCH 092/263] Fixed missing comma in test and updated README syntax Closes dart-lang/args#1 R=rnystrom@google.com Review URL: https://codereview.chromium.org//925003002 --- pkgs/args/README.md | 286 +++++++++++++++++++++------------- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 4 +- 3 files changed, 179 insertions(+), 113 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index fe25d89e..fa635cb4 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -17,12 +17,16 @@ Then define a set of options on that parser using [addOption()][addOption] and When an option can only be set or unset (as opposed to taking a string value), use a flag: - parser.addFlag('name'); +```dart +parser.addFlag('name'); +``` Flag options, by default, accept a 'no-' prefix to negate the option. You can disable the 'no-' prefix using the `negatable` parameter: - parser.addFlag('name', negatable: false); +```dart +parser.addFlag('name', negatable: false); +``` *Note:* From here on out, "option" refers to both regular options and flags. In cases where the distinction matters, we'll use "non-flag option." @@ -30,14 +34,18 @@ cases where the distinction matters, we'll use "non-flag option." Options can have an optional single-character abbreviation, specified with the `abbr` parameter: - parser.addOption('mode', abbr: 'm'); - parser.addFlag('verbose', abbr: 'v'); +```dart +parser.addOption('mode', abbr: 'm'); +parser.addFlag('verbose', abbr: 'v'); +``` Options can also have a default value, specified with the `defaultsTo` parameter. The default value is used when arguments don't specify the option. - parser.addOption('mode', defaultsTo: 'debug'); - parser.addFlag('verbose', defaultsTo: false); +```dart +parser.addOption('mode', defaultsTo: 'debug'); +parser.addFlag('verbose', defaultsTo: false); +``` The default value for non-flag options can be any string. For flags, it must be a `bool`. @@ -47,16 +55,20 @@ allowed set of values. When you do, the parser throws a [FormatException] if the value for an option is not in the allowed set. Here's an example of specifying allowed values: - parser.addOption('mode', allowed: ['debug', 'release']); +```dart +parser.addOption('mode', allowed: ['debug', 'release']); +``` You can use the `callback` parameter to associate a function with an option. Later, when parsing occurs, the callback function is invoked with the value of the option: - parser.addOption('mode', callback: (mode) => print('Got mode $mode')); - parser.addFlag('verbose', callback: (verbose) { - if (verbose) print('Verbose'); - }); +```dart +parser.addOption('mode', callback: (mode) => print('Got mode $mode')); +parser.addFlag('verbose', callback: (verbose) { + if (verbose) print('Verbose'); +}); +``` The callbacks for all options are called whenever a set of arguments is parsed. If an option isn't provided in the args, its callback is passed the default @@ -67,7 +79,9 @@ value, or `null` if no default value is set. Once you have an [ArgParser][] set up with some options and flags, you use it by calling [ArgParser.parse()][parse] with a set of arguments: - var results = parser.parse(['some', 'command', 'line', 'args']); +```dart +var results = parser.parse(['some', 'command', 'line', 'args']); +``` These arguments usually come from the arguments to `main()`. For example: @@ -80,19 +94,23 @@ However, you can pass in any list of strings. The `parse()` method returns an instance of [ArgResults][], a map-like object that contains the values of the parsed options. - var parser = new ArgParser(); - parser.addOption('mode'); - parser.addFlag('verbose', defaultsTo: true); - var results = parser.parse(['--mode', 'debug', 'something', 'else']); +```dart +var parser = new ArgParser(); +parser.addOption('mode'); +parser.addFlag('verbose', defaultsTo: true); +var results = parser.parse(['--mode', 'debug', 'something', 'else']); - print(results['mode']); // debug - print(results['verbose']); // true +print(results['mode']); // debug +print(results['verbose']); // true +``` By default, the `parse()` method stops as soon as it reaches `--` by itself or anything that the parser doesn't recognize as an option, flag, or option value. If arguments still remain, they go into [ArgResults.rest][rest]. - print(results.rest); // ['something', 'else'] +```dart +print(results.rest); // ['something', 'else'] +``` To continue to parse options found after non-option arguments, pass `allowTrailingOptions: true` when creating the [ArgParser][]. @@ -102,56 +120,74 @@ To continue to parse options found after non-option arguments, pass To actually pass in options and flags on the command line, use GNU or POSIX style. Consider this option: - parser.addOption('name', abbr: 'n'); +```dart +parser.addOption('name', abbr: 'n'); +``` You can specify its value on the command line using any of the following: - --name=somevalue - --name somevalue - -nsomevalue - -n somevalue +``` +--name=somevalue +--name somevalue +-nsomevalue +-n somevalue +``` Consider this flag: - parser.addFlag('name', abbr: 'n'); +```dart +parser.addFlag('name', abbr: 'n'); +``` You can set it to true using one of the following: - --name - -n +``` +--name +-n +``` You can set it to false using the following: - --no-name +``` +--no-name +``` Multiple flag abbreviations can be collapsed into a single argument. Say you define these flags: - parser - ..addFlag('verbose', abbr: 'v') - ..addFlag('french', abbr: 'f') - ..addFlag('iambic-pentameter', abbr: 'i'); +```dart +parser + ..addFlag('verbose', abbr: 'v') + ..addFlag('french', abbr: 'f') + ..addFlag('iambic-pentameter', abbr: 'i'); +``` You can set all three flags at once: - -vfi +``` +-vfi +``` By default, an option has only a single value, with later option values overriding earlier ones; for example: - var parser = new ArgParser(); - parser.addOption('mode'); - var results = parser.parse(['--mode', 'on', '--mode', 'off']); - print(results['mode']); // prints 'off' +```dart +var parser = new ArgParser(); +parser.addOption('mode'); +var results = parser.parse(['--mode', 'on', '--mode', 'off']); +print(results['mode']); // prints 'off' +``` If you need multiple values, set the `allowMultiple` parameter. In that case the option can occur multiple times, and the `parse()` method returns a list of values: - var parser = new ArgParser(); - parser.addOption('mode', allowMultiple: true); - var results = parser.parse(['--mode', 'on', '--mode', 'off']); - print(results['mode']); // prints '[on, off]' +```dart +var parser = new ArgParser(); +parser.addOption('mode', allowMultiple: true); +var results = parser.parse(['--mode', 'on', '--mode', 'off']); +print(results['mode']); // prints '[on, off]' +``` ## Defining commands ## @@ -159,46 +195,58 @@ In addition to *options*, you can also define *commands*. A command is a named argument that has its own set of options. For example, consider this shell command: - $ git commit -a +``` +$ 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 using the [addCommand][] method: - var parser = new ArgParser(); - var command = parser.addCommand('commit'); +```dart +var parser = new ArgParser(); +var command = parser.addCommand('commit'); +``` It returns another [ArgParser][], which you can then use to define options specific to that command. If you already have an [ArgParser][] for the command's options, you can pass it in: - var parser = new ArgParser(); - var command = new ArgParser(); - parser.addCommand('commit', command); +```dart +var parser = new ArgParser(); +var command = new ArgParser(); +parser.addCommand('commit', command); +``` The [ArgParser][] for a command can then define options or flags: - command.addFlag('all', abbr: 'a'); +```dart +command.addFlag('all', abbr: 'a'); +``` You can add multiple commands to the same parser so that a user can select one from a range of possible commands. When parsing an argument list, 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['all']); // true +```dart +var results = parser.parse(['commit', '-a']); +print(results.command.name); // "commit" +print(results.command['all']); // true +``` Options for a command must appear after the command in the argument list. For example, given the above parser, `"git -a commit"` is *not* valid. The parser tries to find the right-most command that accepts an option. For example: - var parser = new ArgParser(); - parser.addFlag('all', abbr: 'a'); - var command = parser.addCommand('commit'); - command.addFlag('all', abbr: 'a'); +```dart +var parser = new ArgParser(); +parser.addFlag('all', abbr: 'a'); +var command = parser.addCommand('commit'); +command.addFlag('all', abbr: 'a'); - var results = parser.parse(['commit', '-a']); - print(results.command['all']); // true +var results = parser.parse(['commit', '-a']); +print(results.command['all']); // true +``` Here, both the top-level parser and the `"commit"` command can accept a `"-a"` (which is probably a bad command line interface, admittedly). In that case, when @@ -212,45 +260,51 @@ and [Command][] classes to help structure it. [CommandRunner][] has built-in support for dispatching to [Command][]s based on command-line arguments, as well as handling `--help` flags and invalid arguments. For example: - var runner = new CommandRunner("git", "Distributed version control.") - ..addCommand(new CommitCommand()) - ..addCommand(new StashCommand()) - ..run(['commit', '-a']); // Calls [CommitCommand.run()] +```dart +var runner = new CommandRunner("git", "Distributed version control.") + ..addCommand(new CommitCommand()) + ..addCommand(new StashCommand()) + ..run(['commit', '-a']); // Calls [CommitCommand.run()] +``` Custom commands are defined by extending the [Command][] class. For example: - class CommitCommand extends Command { - // The [name] and [description] properties must be defined by every - // subclass. - final name = "commit"; - final description = "Record changes to the repository."; - - CommitCommand() { - // [argParser] is automatically created by the parent class. - argParser.addFlag('all', abbr: 'a'); - } - - // [run] may also return a Future. - void run() { - // [options] is set before [run()] is called and contains the options - // passed to this command. - print(options['all']); - } - } +```dart +class CommitCommand extends Command { + // The [name] and [description] properties must be defined by every + // subclass. + final name = "commit"; + final description = "Record changes to the repository."; + + CommitCommand() { + // [argParser] is automatically created by the parent class. + argParser.addFlag('all', abbr: 'a'); + } + + // [run] may also return a Future. + void run() { + // [options] is set before [run()] is called and contains the options + // passed to this command. + print(options['all']); + } +} +``` Commands can also have subcommands, which are added with [addSubcommand][]. A command with subcommands can't run its own code, so [run][] doesn't need to be implemented. For example: - class StashCommand extends Command { - final String name = "stash"; - final String description = "Stash changes in the working directory."; +```dart +class StashCommand extends Command { + final String name = "stash"; + final String description = "Stash changes in the working directory."; - StashCommand() { - addSubcommand(new StashSaveCommand()); - addSubcommand(new StashListCommand()); - } - } + StashCommand() { + addSubcommand(new StashSaveCommand()); + addSubcommand(new StashListCommand()); + } +} +``` [CommandRunner][] automatically adds a `help` command that displays usage information for commands, as well as support for the `--help` flag for all @@ -258,11 +312,13 @@ commands. If it encounters an error parsing the arguments or processing a command, it throws a [UsageError][]; your `main()` method should catch these and print them appropriately. For example: - runner.run(arguments).catchError((error) { - if (error is! UsageError) throw error; - print(error); - exit(64); // Exit code 64 indicates a usage error. - }); +```dart +runner.run(arguments).catchError((error) { + if (error is! UsageError) throw error; + print(error); + exit(64); // Exit code 64 indicates a usage error. +}); +``` ## Displaying usage @@ -272,38 +328,48 @@ when you create your options. To define help text for an entire option, use the `help:` parameter: - parser.addOption('mode', help: 'The compiler configuration', - allowed: ['debug', 'release']); - parser.addFlag('verbose', help: 'Show additional diagnostic info'); +```dart +parser.addOption('mode', help: 'The compiler configuration', + allowed: ['debug', 'release']); +parser.addFlag('verbose', help: 'Show additional diagnostic info'); +``` For non-flag options, you can also provide a help string for the parameter: - parser.addOption('out', help: 'The output path', valueHelp: 'path', - allowed: ['debug', 'release']); +```dart +parser.addOption('out', help: 'The output path', valueHelp: 'path', + allowed: ['debug', 'release']); +``` For non-flag options, you can also provide detailed help for each expected value by using the `allowedHelp:` parameter: - parser.addOption('arch', help: 'The architecture to compile for', - allowedHelp: { - 'ia32': 'Intel x86', - 'arm': 'ARM Holding 32-bit chip' - }); +```dart +parser.addOption('arch', help: 'The architecture to compile for', + allowedHelp: { + 'ia32': 'Intel x86', + 'arm': 'ARM Holding 32-bit chip' + }); +``` To display the help, use the [getUsage()][getUsage] method: - print(parser.getUsage()); +```dart +print(parser.getUsage()); +``` The resulting string looks something like this: - --mode The compiler configuration - [debug, release] +``` +--mode The compiler configuration + [debug, release] - --out= The output path - --[no-]verbose Show additional diagnostic info - --arch The architecture to compile for - [arm] ARM Holding 32-bit chip - [ia32] Intel x86 +--out= The output path +--[no-]verbose Show additional diagnostic info +--arch The architecture to compile for + [arm] ARM Holding 32-bit chip + [ia32] Intel x86 +``` [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index dc77d992..fdf99c01 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2+4 +version: 0.12.2+5 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index dd1634d1..4c23ba53 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -302,8 +302,8 @@ const _INVALID_OPTIONS = const [ ]; const _VALID_OPTIONS = const [ - 'a' // One character. - 'contains-dash', + 'a', // One character. + 'contains-dash', 'contains_underscore', 'ends-with-dash-', 'contains--doubledash--', From dc95a41d033d04664ab79eb2afa6b4665b7447d1 Mon Sep 17 00:00:00 2001 From: Kenneth Endfinger Date: Wed, 18 Feb 2015 19:33:22 +0000 Subject: [PATCH 093/263] Remove dependency on 'collection' --- pkgs/args/lib/src/arg_parser.dart | 2 +- pkgs/args/lib/src/arg_results.dart | 2 +- pkgs/args/lib/src/option.dart | 2 +- pkgs/args/pubspec.yaml | 2 -- 4 files changed, 3 insertions(+), 5 deletions(-) diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 8337c42f..e7f728c0 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -4,7 +4,7 @@ library args.src.arg_parser; -import 'package:collection/wrappers.dart'; +import 'dart:collection'; import 'arg_results.dart'; import 'option.dart'; diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 15fb5bbc..6be2de45 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -4,7 +4,7 @@ library args.src.arg_results; -import 'package:collection/wrappers.dart'; +import 'dart:collection'; import 'arg_parser.dart'; diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index b2292dbb..4045bce1 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -4,7 +4,7 @@ library args.src.option; -import 'package:collection/wrappers.dart'; +import 'dart:collection'; /// Creates a new [Option]. /// diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index fdf99c01..7ce1981a 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -6,8 +6,6 @@ description: > Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. -dependencies: - collection: ">=0.9.0 <2.0.0" dev_dependencies: unittest: ">=0.11.5 <0.12.0" environment: From cc6c84ff8c69de3df7c0f631ea18dfab7bfdff09 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 18 Feb 2015 12:35:19 -0800 Subject: [PATCH 094/263] Update the CHANGELOG and pubspec. --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index f9724613..29f0fbbe 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.12.2+5 + +* Remove the dependency on the `collection` package. + ## 0.12.2+4 * Add an example of using command-line arguments to the README. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 7ce1981a..0af927dc 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2+5 +version: 0.12.2+6 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -9,4 +9,4 @@ description: > dev_dependencies: unittest: ">=0.11.5 <0.12.0" environment: - sdk: ">=1.0.0 <2.0.0" + sdk: ">=1.4.0 <2.0.0" From b8840b07417b69d82145fe7551942aeca7d94d8f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 18 Feb 2015 12:39:44 -0800 Subject: [PATCH 095/263] Fix the CHANGELOG. --- pkgs/args/CHANGELOG.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 29f0fbbe..1fb1bb76 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,7 +1,11 @@ -## 0.12.2+5 +## 0.12.2+6 * Remove the dependency on the `collection` package. +## 0.12.2+5 + +* Add syntax highlighting to the README. + ## 0.12.2+4 * Add an example of using command-line arguments to the README. From d2f5dce48708a877a46f2229cd352efb8a8bf5e6 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 4 Mar 2015 17:54:31 -0800 Subject: [PATCH 096/263] Parse comma-separated multiple values. R=rnystrom@google.com Review URL: https://codereview.chromium.org//975463004 --- pkgs/args/CHANGELOG.md | 6 ++++ pkgs/args/README.md | 11 +++++++ pkgs/args/lib/src/arg_parser.dart | 15 ++++++--- pkgs/args/lib/src/option.dart | 17 +++++++--- pkgs/args/lib/src/parser.dart | 51 +++++++++++++++++++++-------- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 7 ++++ pkgs/args/test/parse_test.dart | 53 +++++++++++++++++++++++++++++++ 8 files changed, 139 insertions(+), 23 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 1fb1bb76..8c416d69 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.13.0 + +* **Breaking change**: An option that allows multiple values will now + automatically split apart comma-separated values. This can be controlled with + the `splitCommas` option. + ## 0.12.2+6 * Remove the dependency on the `collection` package. diff --git a/pkgs/args/README.md b/pkgs/args/README.md index fa635cb4..96df5e6a 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -189,6 +189,17 @@ var results = parser.parse(['--mode', 'on', '--mode', 'off']); print(results['mode']); // prints '[on, off]' ``` +By default, values for a multi-valued option may also be separated with commas: + +```dart +var parser = new ArgParser(); +parser.addOption('mode', allowMultiple: true); +var results = parser.parse(['--mode', 'on,off']); +print(results['mode']); // prints '[on, off]' +``` + +This can be disabled by passing `splitCommas: false`. + ## Defining commands ## In addition to *options*, you can also define *commands*. A command is a named diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index e7f728c0..49d6aa6b 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -77,18 +77,25 @@ class ArgParser { /// /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. + /// * [splitCommas] is passed but [allowMultiple] is `false`. void addOption(String name, {String abbr, String help, String valueHelp, List allowed, Map allowedHelp, String defaultsTo, - void callback(value), bool allowMultiple: false, bool hide: false}) { + void callback(value), bool allowMultiple: false, bool splitCommas, + bool hide: false}) { + if (!allowMultiple && splitCommas != null) { + throw new ArgumentError( + 'splitCommas may not be set if allowMultiple is false.'); + } + _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, allowMultiple ? OptionType.MULTIPLE : OptionType.SINGLE, - hide: hide); + splitCommas: splitCommas, hide: hide); } void _addOption(String name, String abbr, String help, String valueHelp, List allowed, Map allowedHelp, defaultsTo, void callback(value), OptionType type, - {bool negatable: false, bool hide: false}) { + {bool negatable: false, bool splitCommas, bool hide: false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { throw new ArgumentError('Duplicate option "$name".'); @@ -105,7 +112,7 @@ class ArgParser { _options[name] = newOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, type, - negatable: negatable, hide: hide); + negatable: negatable, splitCommas: splitCommas, hide: hide); } /// Parses [args], a list of command-line arguments, matches them against the diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 4045bce1..07e87f68 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -13,9 +13,10 @@ import 'dart:collection'; Option newOption(String name, String abbreviation, String help, String valueHelp, List allowed, Map allowedHelp, defaultValue, Function callback, OptionType type, - {bool negatable, bool hide: false}) { + {bool negatable, bool splitCommas, bool hide: false}) { return new Option._(name, abbreviation, help, valueHelp, allowed, allowedHelp, - defaultValue, callback, type, negatable: negatable, hide: hide); + defaultValue, callback, type, negatable: negatable, + splitCommas: splitCommas, hide: hide); } /// A command-line option. Includes both flags and options which take a value. @@ -30,6 +31,7 @@ class Option { final Map allowedHelp; final OptionType type; final bool negatable; + final bool splitCommas; final bool hide; /// Whether the option is boolean-valued flag. @@ -43,13 +45,20 @@ class Option { Option._(this.name, this.abbreviation, this.help, this.valueHelp, List allowed, Map allowedHelp, this.defaultValue, - this.callback, this.type, {this.negatable, this.hide: false}) + this.callback, OptionType type, {this.negatable, bool splitCommas, + this.hide: false}) : this.allowed = allowed == null ? null : new UnmodifiableListView(allowed), this.allowedHelp = allowedHelp == null ? null - : new UnmodifiableMapView(allowedHelp) { + : new UnmodifiableMapView(allowedHelp), + this.type = type, + // If the user doesn't specify [splitCommas], it defaults to true for + // multiple options. + this.splitCommas = splitCommas == null + ? type == OptionType.MULTIPLE + : splitCommas { if (name.isEmpty) { throw new ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 1e297b0e..848bb9c2 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -131,7 +131,7 @@ class Parser { args.removeAt(0); if (option.isFlag) { - setOption(results, option, true); + setFlag(results, option, true); } else { readNextArgAsValue(option); } @@ -196,7 +196,7 @@ class Parser { validate( option.isFlag, 'Option "-$c" must be a flag to be in a collapsed "-".'); - setOption(results, option, true); + setFlag(results, option, true); } /// Tries to parse the current argument as a long-form named option, which @@ -213,7 +213,7 @@ class Parser { validate(longOpt[3] == null, 'Flag option "$name" should not be given a value.'); - setOption(results, option, true); + setFlag(results, option, true); } else if (longOpt[3] != null) { // We have a value like --foo=bar. setOption(results, option, longOpt[3]); @@ -235,7 +235,7 @@ class Parser { validate(option.isFlag, 'Cannot negate non-flag option "$name".'); validate(option.negatable, 'Cannot negate option "$name".'); - setOption(results, option, false); + setFlag(results, option, false); } else { // Walk up to the parent command if possible. validate(parent != null, 'Could not find an option named "$name".'); @@ -252,19 +252,42 @@ class Parser { if (!condition) throw new FormatException(message); } - /// Validates and stores [value] as the value for [option]. - void 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}".'); + /// Validates and stores [value] as the value for [option], which must not be + /// a flag. + void setOption(Map results, Option option, String value) { + assert(!option.isFlag); + + if (!option.isMultiple) { + _validateAllowed(option, value); + results[option.name] = value; + return; } - if (option.isMultiple) { - var list = results.putIfAbsent(option.name, () => []); - list.add(value); + var list = results.putIfAbsent(option.name, () => []); + + if (option.splitCommas) { + for (var element in value.split(",")) { + _validateAllowed(option, element); + list.add(element); + } } else { - results[option.name] = value; + _validateAllowed(option, value); + list.add(value); } } + + /// Validates and stores [value] as the value for [option], which must be a + /// flag. + void setFlag(Map results, Option option, bool value) { + assert(option.isFlag); + results[option.name] = value; + } + + /// Validates that [value] is allowed as a value of [option]. + void _validateAllowed(Option option, String value) { + if (option.allowed == null) return; + + validate(option.allowed.contains(value), + '"$value" is not an allowed value for option "${option.name}".'); + } } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 0af927dc..4a3b1f6d 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.12.2+6 +version: 0.13.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 4c23ba53..b22d0c95 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -109,6 +109,13 @@ void main() { } }); + test('throws ArgumentError if splitCommas is passed with allowMultiple: ' + 'false', () { + var parser = new ArgParser(); + throwsIllegalArg(() => parser.addOption('flummox', splitCommas: true)); + throwsIllegalArg(() => parser.addOption('flummox', splitCommas: false)); + }); + test('accepts valid option names', () { var parser = new ArgParser(); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 0dd00ba4..502210ab 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -199,6 +199,51 @@ void main() { parser.parse([]); expect(a, isEmpty); }); + + test('allowMultiple parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); + + test("allowMultiple doesn't parses comma-separated strings with " + "splitCommas: false", () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, + splitCommas: false, + callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v,w', 'x'])); + }); + + test('allowMultiple parses empty strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); + expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); + }); + + test('allowMultiple with allowed parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, + allowed: ['v', 'w', 'x'], + callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); }); group('abbreviations', () { @@ -287,6 +332,14 @@ void main() { throwsFormat(parser, ['-mprofile']); }); + test('throw if a comma-separated value is not allowed', () { + var parser = new ArgParser(); + parser.addOption('mode', abbr: 'm', allowMultiple: true, + allowed: ['debug', 'release']); + + throwsFormat(parser, ['-mdebug,profile']); + }); + test('throw if any but the first is not a flag', () { var parser = new ArgParser(); parser.addFlag('apple', abbr: 'a'); From 5bbc74f71250deb09a86e6436aa2b6a327f2c385 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 21 May 2015 13:28:59 -0700 Subject: [PATCH 097/263] Upgrade to test 0.12.0. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1145403002 --- pkgs/args/pubspec.yaml | 4 ++-- pkgs/args/test/args_test.dart | 2 +- pkgs/args/test/command_parse_test.dart | 2 +- pkgs/args/test/command_runner_test.dart | 2 +- pkgs/args/test/command_test.dart | 2 +- pkgs/args/test/parse_test.dart | 2 +- pkgs/args/test/trailing_options_test.dart | 2 +- pkgs/args/test/usage_test.dart | 2 +- pkgs/args/test/utils.dart | 2 +- 9 files changed, 10 insertions(+), 10 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4a3b1f6d..cb373be9 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.0 +version: 0.13.1-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -7,6 +7,6 @@ description: > a set of options and values using GNU and POSIX style options. dev_dependencies: - unittest: ">=0.11.5 <0.12.0" + test: ">=0.12.0 <0.13.0" environment: sdk: ">=1.4.0 <2.0.0" diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index b22d0c95..ee37b45f 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,7 +4,7 @@ library args_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index e336e715..54b30325 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -4,7 +4,7 @@ library command_parse_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 904a0380..3a796f10 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -5,7 +5,7 @@ library command_runner_test; import 'package:args/command_runner.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index fe3446c0..888016a4 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -5,7 +5,7 @@ library command_test; import 'package:args/command_runner.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'utils.dart'; void main() { diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 502210ab..ba7453cb 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -4,7 +4,7 @@ library parse_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index 24f6ce73..efc53b25 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -4,7 +4,7 @@ library trailing_options_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:args/args.dart'; void main() { diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index e7f3e477..51efc054 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -4,7 +4,7 @@ library usage_test; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; import 'package:args/args.dart'; void main() { diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 9d85fb07..1cdd803a 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -8,7 +8,7 @@ import 'dart:async'; import 'package:args/args.dart'; import 'package:args/command_runner.dart'; -import 'package:unittest/unittest.dart'; +import 'package:test/test.dart'; class CommandRunnerWithFooter extends CommandRunner { final usageFooter = "Also, footer!"; From 6d8723495223db2e2ac3bcc432fa7e70ff4fef18 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 21 May 2015 13:29:48 -0700 Subject: [PATCH 098/263] Add support for separators. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1145413002 --- pkgs/args/CHANGELOG.md | 5 +++ pkgs/args/example/test_runner.dart | 62 ++++++++++++++------------ pkgs/args/lib/src/arg_parser.dart | 20 +++++++-- pkgs/args/lib/src/usage.dart | 28 ++++++++---- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 71 ++++++++++++++++++++++++++++++ 6 files changed, 148 insertions(+), 40 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 8c416d69..74a6d1ef 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.1 + +* Add `ArgParser.addSeparator()`. Separators allow users to group their options + in the usage text. + ## 0.13.0 * **Breaking change**: An option that allows multiple values will now diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index 9480f417..0d15e839 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -14,11 +14,7 @@ import 'package:args/args.dart'; main() { var parser = new ArgParser(); - parser.addOption('mode', - abbr: 'm', - defaultsTo: 'debug', - help: 'Mode in which to run the tests', - allowed: ['all', 'debug', 'release']); + parser.addSeparator('===== Platform'); parser.addOption('compiler', abbr: 'c', @@ -80,6 +76,14 @@ main() { help: 'The operating system to run tests on', allowed: ['linux', 'macos', 'windows']); + parser.addSeparator('===== Runtime'); + + parser.addOption('mode', + abbr: 'm', + defaultsTo: 'debug', + help: 'Mode in which to run the tests', + allowed: ['all', 'debug', 'release']); + parser.addFlag('checked', defaultsTo: false, help: 'Run tests in checked mode'); @@ -88,6 +92,24 @@ main() { parser.addOption('timeout', abbr: 't', help: 'Timeout in seconds'); + parser.addOption('tasks', + abbr: 'j', + defaultsTo: Platform.numberOfProcessors.toString(), + help: 'The number of parallel tasks to run'); + + parser.addOption('shards', + defaultsTo: '1', + help: 'The number of instances that the tests will be sharded over'); + + parser.addOption('shard', + defaultsTo: '1', + help: 'The index of this instance when running in sharded mode'); + + parser.addFlag('valgrind', + defaultsTo: false, help: 'Run tests through valgrind'); + + parser.addSeparator('===== Output'); + parser.addOption('progress', abbr: 'p', defaultsTo: 'compact', @@ -106,32 +128,24 @@ main() { defaultsTo: false, help: 'Print a summary report of the number of tests, by expectation'); - parser.addOption('tasks', - abbr: 'j', - defaultsTo: Platform.numberOfProcessors.toString(), - help: 'The number of parallel tasks to run'); - - parser.addOption('shards', - defaultsTo: '1', - help: 'The number of instances that the tests will be sharded over'); - - parser.addOption('shard', - defaultsTo: '1', - help: 'The index of this instance when running in sharded mode'); - parser.addFlag('verbose', abbr: 'v', defaultsTo: false, help: 'Verbose output'); parser.addFlag('list', defaultsTo: false, help: 'List tests only, do not run them'); + parser.addFlag('time', + help: 'Print timing information after running tests', defaultsTo: false); + + parser.addFlag('batch', + abbr: 'b', help: 'Run browser tests in batch mode', defaultsTo: true); + + parser.addSeparator('===== Miscellaneous'); + parser.addFlag('keep-generated-tests', defaultsTo: false, help: 'Keep the generated files in the temporary directory'); - parser.addFlag('valgrind', - defaultsTo: false, help: 'Run tests through valgrind'); - parser.addOption('special-command', help: """ Special command support. Wraps the command line in a special command. The special command should contain @@ -143,15 +157,9 @@ is 'dart file.dart' and you specify special command 'python -u valgrind.py @ suffix' the final command will be 'python -u valgrind.py dart file.dart suffix'"""); - parser.addFlag('time', - help: 'Print timing information after running tests', defaultsTo: false); - parser.addOption('dart', help: 'Path to dart executable'); parser.addOption('drt', help: 'Path to content shell executable'); parser.addOption('dartium', help: 'Path to Dartium Chrome executable'); - parser.addFlag('batch', - abbr: 'b', help: 'Run browser tests in batch mode', defaultsTo: true); - print(parser.usage); } diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 49d6aa6b..c32741df 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -23,6 +23,10 @@ class ArgParser { /// The commands that have been defined for this parser. final Map commands; + /// A list of the [Option]s in [options] intermingled with [String] + /// separators. + final _optionsAndSeparators = []; + /// Whether or not this parser parses options that appear after non-option /// arguments. final bool allowTrailingOptions; @@ -110,9 +114,19 @@ class ArgParser { } } - _options[name] = newOption(name, abbr, help, valueHelp, allowed, + var option = newOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, type, negatable: negatable, splitCommas: splitCommas, hide: hide); + _options[name] = option; + _optionsAndSeparators.add(option); + } + + /// Adds a separator line to the usage. + /// + /// In the usage text for the parser, this will appear between any options + /// added before this call and ones added after it. + void addSeparator(String text) { + _optionsAndSeparators.add(text); } /// Parses [args], a list of command-line arguments, matches them against the @@ -124,12 +138,12 @@ class ArgParser { /// /// This is basically the help text shown on the command line. @Deprecated("Replaced with get usage. getUsage() will be removed in args 1.0") - String getUsage() => new Usage(this).generate(); + String getUsage() => usage; /// Generates a string displaying usage information for the defined options. /// /// This is basically the help text shown on the command line. - String get usage => new Usage(this).generate(); + String get usage => new Usage(_optionsAndSeparators).generate(); /// Get the default value for an option. Useful after parsing to test if the /// user specified something other than the default. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 11be54b3..177a810b 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -22,8 +22,8 @@ import '../args.dart'; class Usage { static const NUM_COLUMNS = 3; // Abbreviation, long name, help. - /// The parser this is generating usage for. - final ArgParser args; + /// A list of the [Option]s intermingled with [String] separators. + final List optionsAndSeparators; /// The working buffer for the generated usage text. StringBuffer buffer; @@ -53,7 +53,7 @@ class Usage { /// content. int newlinesNeeded = 0; - Usage(this.args); + Usage(this.optionsAndSeparators); /// Generates a string displaying usage information for the defined options. /// This is basically the help text shown on the command line. @@ -62,8 +62,17 @@ class Usage { calculateColumnWidths(); - args.options.forEach((name, option) { - if (option.hide) return; + for (var optionOrSeparator in optionsAndSeparators) { + if (optionOrSeparator is String) { + // Ensure that there's always a blank line before a separator. + if (buffer.isNotEmpty) buffer.write("\n\n"); + buffer.write(optionOrSeparator); + newlinesNeeded = 1; + continue; + } + + var option = optionOrSeparator as Option; + if (option.hide) continue; write(0, getAbbreviation(option)); write(1, getLongOption(option)); @@ -94,7 +103,7 @@ class Usage { // 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(); } @@ -127,8 +136,9 @@ class Usage { void calculateColumnWidths() { int abbr = 0; int title = 0; - args.options.forEach((name, option) { - if (option.hide) return; + for (var option in optionsAndSeparators) { + if (option is! Option) continue; + if (option.hide) continue; // Make room in the first column if there are abbreviations. abbr = max(abbr, getAbbreviation(option).length); @@ -142,7 +152,7 @@ class Usage { title = max(title, getAllowedTitle(allowed).length); } } - }); + } // Leave a gutter between the columns. title += 4; diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index cb373be9..3b71ea32 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.1-dev +version: 0.13.1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 51efc054..492c517c 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -205,6 +205,77 @@ void main() { --[no-]third The third flag '''); }); + + group("separators", () { + test("separates options where it's placed", () { + var parser = new ArgParser(); + parser.addFlag('zebra', help: 'First'); + parser.addSeparator('Primate:'); + parser.addFlag('monkey', help: 'Second'); + parser.addSeparator('Marsupial:'); + parser.addFlag('wombat', help: 'Third'); + + validateUsage(parser, ''' + --[no-]zebra First + + Primate: + --[no-]monkey Second + + Marsupial: + --[no-]wombat Third + '''); + }); + + test("doesn't add extra newlines after a multiline option", () { + var parser = new ArgParser(); + parser.addFlag('zebra', help: 'Multi\nline'); + parser.addSeparator('Primate:'); + parser.addFlag('monkey', help: 'Second'); + + validateUsage(parser, ''' + --[no-]zebra Multi + line + + Primate: + --[no-]monkey Second + '''); + }); + + test("doesn't add newlines if it's the first component", () { + var parser = new ArgParser(); + parser.addSeparator('Equine:'); + parser.addFlag('zebra', help: 'First'); + + validateUsage(parser, ''' + Equine: + --[no-]zebra First + '''); + }); + + test("doesn't add trailing newlines if it's the last component", () { + var parser = new ArgParser(); + parser.addFlag('zebra', help: 'First'); + parser.addSeparator('Primate:'); + + validateUsage(parser, ''' + --[no-]zebra First + + Primate: + '''); + }); + + test("adds a newline after another separator", () { + var parser = new ArgParser(); + parser.addSeparator('First'); + parser.addSeparator('Second'); + + validateUsage(parser, ''' + First + + Second + '''); + }); + }); }); } From d3f680a25e89335e36fa85d485275173fa67e26f Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 8 Jun 2015 16:06:15 -0700 Subject: [PATCH 099/263] Follow getopt in parsing option-like values. The getopt function (and the GNU getopt_long extension) are the de facto standards for option parsing, so we should follow their behavior to make Dart programs consistent with other languages. Closes dart-lang/args#36 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1165263003 --- pkgs/args/CHANGELOG.md | 7 +++++++ pkgs/args/lib/src/parser.dart | 4 ---- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/parse_test.dart | 19 +++++++++++-------- 4 files changed, 19 insertions(+), 13 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 74a6d1ef..96df1dda 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,10 @@ +## 0.13.1+1 + +* Allow option values that look like options. This more closely matches the + behavior of [`getopt`][getopt], the *de facto* standard for option parsing. + +[getopt]: http://man7.org/linux/man-pages/man3/getopt.3.html + ## 0.13.1 * Add `ArgParser.addSeparator()`. Separators allow users to group their options diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 848bb9c2..a10692d4 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -103,10 +103,6 @@ class Parser { // Take the option argument from the next command line arg. validate(args.length > 0, 'Missing argument for "${option.name}".'); - // Make sure it isn't an option itself. - validate(!_ABBR_OPT.hasMatch(current) && !_LONG_OPT.hasMatch(current), - 'Missing argument for "${option.name}".'); - setOption(results, option, current); args.removeAt(0); } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 3b71ea32..3ea1a11c 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.1 +version: 0.13.2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index ba7453cb..39027c2c 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -315,14 +315,15 @@ void main() { throwsFormat(parser, ['-f']); }); - test('throw if the value looks like an option', () { + test('does not throw if the value looks like an option', () { var parser = new ArgParser(); parser.addOption('file', abbr: 'f'); parser.addOption('other'); - throwsFormat(parser, ['-f', '--other']); - throwsFormat(parser, ['-f', '--unknown']); - throwsFormat(parser, ['-f', '-abbr']); + expect(parser.parse(['-f', '--other'])['file'], equals('--other')); + expect(parser.parse(['-f', '--unknown'])['file'], equals('--unknown')); + expect(parser.parse(['-f', '-abbr'])['file'], equals('-abbr')); + expect(parser.parse(['-f', '--'])['file'], equals('--')); }); test('throw if the value is not allowed', () { @@ -409,14 +410,16 @@ void main() { throwsFormat(parser, ['--mode']); }); - test('throw if the value looks like an option', () { + test('do not throw if the value looks like an option', () { var parser = new ArgParser(); parser.addOption('mode'); parser.addOption('other'); - throwsFormat(parser, ['--mode', '--other']); - throwsFormat(parser, ['--mode', '--unknown']); - throwsFormat(parser, ['--mode', '-abbr']); + expect(parser.parse(['--mode', '--other'])['mode'], equals('--other')); + expect(parser.parse(['--mode', '--unknown'])['mode'], + equals('--unknown')); + expect(parser.parse(['--mode', '-abbr'])['mode'], equals('-abbr')); + expect(parser.parse(['--mode', '--'])['mode'], equals('--')); }); test('do not throw if the value is in the allowed set', () { From 32e56756fdd5087cd7497888760686c5df34f4fd Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 8 Jun 2015 17:34:31 -0700 Subject: [PATCH 100/263] Fix the version number in the changelog. I changed the pubspec but I forgot this. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1168923002. --- pkgs/args/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 96df1dda..123c5646 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.13.1+1 +## 0.13.2 * Allow option values that look like options. This more closely matches the behavior of [`getopt`][getopt], the *de facto* standard for option parsing. From f46d26f7bed935c37b7f1ecc180dea8f93145e9b Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 15 Jul 2015 17:19:51 -0700 Subject: [PATCH 101/263] Use the new test runner on the bots. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1235363003 . --- pkgs/args/.status | 15 --------------- pkgs/args/.test_config | 3 +++ pkgs/args/pubspec.yaml | 2 +- 3 files changed, 4 insertions(+), 16 deletions(-) delete mode 100644 pkgs/args/.status create mode 100644 pkgs/args/.test_config diff --git a/pkgs/args/.status b/pkgs/args/.status deleted file mode 100644 index ff095a2f..00000000 --- a/pkgs/args/.status +++ /dev/null @@ -1,15 +0,0 @@ -# Copyright (c) 2014, 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. - -# Skip non-test files ending with "_test". -packages/*: Skip -*/packages/*: Skip -*/*/packages/*: Skip -*/*/*/packages/*: Skip -*/*/*/*packages/*: Skip -*/*/*/*/*packages/*: Skip - -# Only run tests from the build directory, since we don't care about the -# difference between transformed an untransformed code. -test/*: Skip diff --git a/pkgs/args/.test_config b/pkgs/args/.test_config new file mode 100644 index 00000000..412fc5c5 --- /dev/null +++ b/pkgs/args/.test_config @@ -0,0 +1,3 @@ +{ + "test_package": true +} \ No newline at end of file diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 3ea1a11c..a4eb70cc 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.2 +version: 0.13.3-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 744fbc17cf03456fadf25da3215155d4ed201283 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 12 Jan 2016 17:18:19 -0800 Subject: [PATCH 102/263] Get rid of all the library tags. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1575233006 . --- pkgs/args/lib/args.dart | 2 -- pkgs/args/lib/command_runner.dart | 2 -- pkgs/args/lib/src/arg_parser.dart | 2 -- pkgs/args/lib/src/arg_results.dart | 2 -- pkgs/args/lib/src/help_command.dart | 2 -- pkgs/args/lib/src/option.dart | 2 -- pkgs/args/lib/src/parser.dart | 2 -- pkgs/args/lib/src/usage.dart | 2 -- pkgs/args/lib/src/usage_exception.dart | 2 -- pkgs/args/lib/src/utils.dart | 2 -- pkgs/args/test/args_test.dart | 2 -- pkgs/args/test/command_parse_test.dart | 2 -- pkgs/args/test/command_runner_test.dart | 2 -- pkgs/args/test/command_test.dart | 2 -- pkgs/args/test/parse_test.dart | 2 -- pkgs/args/test/trailing_options_test.dart | 2 -- pkgs/args/test/usage_test.dart | 2 -- pkgs/args/test/utils.dart | 2 -- 18 files changed, 36 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 562df446..2534082d 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -2,8 +2,6 @@ // 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. -library args; - export 'src/arg_parser.dart'; export 'src/arg_results.dart' hide newArgResults; export 'src/option.dart' hide newOption; diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index d728cb02..10b87ce0 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -2,8 +2,6 @@ // 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. -library args.command_runner; - import 'dart:async'; import 'dart:collection'; import 'dart:math' as math; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index c32741df..0b81fed4 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -2,8 +2,6 @@ // 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. -library args.src.arg_parser; - import 'dart:collection'; import 'arg_results.dart'; diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 6be2de45..50e85fa7 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -2,8 +2,6 @@ // 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. -library args.src.arg_results; - import 'dart:collection'; import 'arg_parser.dart'; diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index f477b474..9ffcbe17 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -2,8 +2,6 @@ // 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. -library args.help_command; - import '../command_runner.dart'; /// The built-in help command that's added to every [CommandRunner]. diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 07e87f68..a37193f6 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -2,8 +2,6 @@ // 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. -library args.src.option; - import 'dart:collection'; /// Creates a new [Option]. diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index a10692d4..c36035ce 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -2,8 +2,6 @@ // 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. -library args.src.parser; - import 'arg_parser.dart'; import 'arg_results.dart'; import 'option.dart'; diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 177a810b..2b40332e 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -2,8 +2,6 @@ // 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. -library args.src.usage; - import 'dart:math'; import '../args.dart'; diff --git a/pkgs/args/lib/src/usage_exception.dart b/pkgs/args/lib/src/usage_exception.dart index 35cc7442..2d8e1125 100644 --- a/pkgs/args/lib/src/usage_exception.dart +++ b/pkgs/args/lib/src/usage_exception.dart @@ -2,8 +2,6 @@ // 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. -library args.usage_exception; - class UsageException implements Exception { final String message; final String usage; diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index dc355dfb..2469faca 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -2,8 +2,6 @@ // 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. -library args.utils; - /// Pads [source] to [length] by adding spaces at the end. String padRight(String source, int length) => source + ' ' * (length - source.length); diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index ee37b45f..4553b308 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -2,8 +2,6 @@ // 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. -library args_test; - import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index 54b30325..269ed017 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -2,8 +2,6 @@ // 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. -library command_parse_test; - import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 3a796f10..f3a74a0b 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -2,8 +2,6 @@ // 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. -library command_runner_test; - import 'package:args/command_runner.dart'; import 'package:test/test.dart'; diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 888016a4..127cfc91 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -2,8 +2,6 @@ // 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. -library command_test; - import 'package:args/command_runner.dart'; import 'package:test/test.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 39027c2c..187f735e 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -2,8 +2,6 @@ // 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. -library parse_test; - import 'package:test/test.dart'; import 'package:args/args.dart'; import 'utils.dart'; diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index efc53b25..cdc4227f 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -2,8 +2,6 @@ // 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. -library trailing_options_test; - import 'package:test/test.dart'; import 'package:args/args.dart'; diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 492c517c..31c19d26 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -2,8 +2,6 @@ // 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. -library usage_test; - import 'package:test/test.dart'; import 'package:args/args.dart'; diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 1cdd803a..b6c6c717 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -2,8 +2,6 @@ // 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. -library utils; - import 'dart:async'; import 'package:args/args.dart'; From 6e6d9f282c364b321127be70d522be64d47cc902 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 19 Jan 2016 16:44:24 -0800 Subject: [PATCH 103/263] Don't truncate multi-line command descriptions. Closes dart-lang/args#42 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1603063004 . --- pkgs/args/.gitignore | 1 + pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/command_runner.dart | 10 ++++++++-- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 19 +++++++++++++++++++ pkgs/args/test/utils.dart | 12 ++++++++++++ 6 files changed, 45 insertions(+), 3 deletions(-) diff --git a/pkgs/args/.gitignore b/pkgs/args/.gitignore index 388eff0b..7dbf0350 100644 --- a/pkgs/args/.gitignore +++ b/pkgs/args/.gitignore @@ -3,6 +3,7 @@ .pub/ build/ packages +.packages # Or the files created by dart2js. *.dart.js diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 123c5646..250b6013 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.2+1 + +* Print all lines of multi-line command descriptions. + ## 0.13.2 * Allow option values that look like options. This more closely matches the diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 10b87ce0..df1dbf86 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -371,9 +371,15 @@ String _getCommandUsage(Map commands, var buffer = new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { + var lines = commands[name].description.split("\n"); buffer.writeln(); - buffer.write(' ${padRight(name, length)} ' - '${commands[name].description.split("\n").first}'); + buffer.write(' ${padRight(name, length)} ${lines.first}'); + + for (var line in lines.skip(1)) { + buffer.writeln(); + buffer.write(' ' * (length + 5)); + buffer.write(line); + } } return buffer.toString(); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index a4eb70cc..702f0085 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3-dev +version: 0.13.3+1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index f3a74a0b..bccc7b31 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -51,6 +51,25 @@ Available commands: foo Set a value. help Display help information for test. +Run "test help " for more information about a command.""")); + }); + + test("supports newlines in command descriptions", () { + runner.addCommand(new MultilineCommand()); + + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + help Display help information for test. + multiline Multi + line. + Run "test help " for more information about a command.""")); }); diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index b6c6c717..3ef68873 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -27,6 +27,18 @@ class FooCommand extends Command { } } +class MultilineCommand extends Command { + var hasRun = false; + + final name = "multiline"; + final description = "Multi\nline."; + final takesArguments = false; + + void run() { + hasRun = true; + } +} + class HiddenCommand extends Command { var hasRun = false; From d9a675568a4832e50a0bf86674fee707a63f0916 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Feb 2016 08:01:20 -0800 Subject: [PATCH 104/263] Fix 0.13.3+1 version in CHANGELOG --- pkgs/args/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 250b6013..9de260cc 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 0.13.2+1 +## 0.13.3+1 * Print all lines of multi-line command descriptions. From 50033995d036a6b88eeb47c60ebed2c381743825 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Tue, 2 Feb 2016 08:12:51 -0800 Subject: [PATCH 105/263] Fix dart-lang/args#39 with docs change --- pkgs/args/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 96df5e6a..764e0080 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -294,9 +294,9 @@ class CommitCommand extends Command { // [run] may also return a Future. void run() { - // [options] is set before [run()] is called and contains the options + // [argResults] is set before [run()] is called and contains the options // passed to this command. - print(options['all']); + print(argResults['all']); } } ``` From b35f574753df33200f990e125fd1a1ecd18df563 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 2 Feb 2016 11:23:45 -0800 Subject: [PATCH 106/263] Add a CHANGELOG entry and bump the version. --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 9de260cc..8f9c01a4 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.3+2 + +* Documentation fixes. + ## 0.13.3+1 * Print all lines of multi-line command descriptions. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 702f0085..c51fad74 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+1 +version: 0.13.3+2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 774de6e8211b190f20cff3c489ca39ace950519f Mon Sep 17 00:00:00 2001 From: Kasper Peulen Date: Sat, 6 Feb 2016 14:13:08 +0100 Subject: [PATCH 107/263] Document allowTrailingOptions in code --- pkgs/args/lib/src/arg_parser.dart | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 0b81fed4..f5ce41a3 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -35,12 +35,12 @@ class ArgParser { /// after it finds an argument that is neither an option nor a command. /// This allows options to be specified after regular arguments. Defaults to /// `false`. - factory ArgParser({bool allowTrailingOptions}) => new ArgParser._( + factory ArgParser({bool allowTrailingOptions: false}) => new ArgParser._( {}, {}, allowTrailingOptions: allowTrailingOptions); ArgParser._(Map options, Map commands, - {bool allowTrailingOptions}) + {bool allowTrailingOptions: false}) : this._options = options, this.options = new UnmodifiableMapView(options), this._commands = commands, From 36c320d87cb779d2de7d5817a3443488608e6ac7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 8 Feb 2016 12:56:10 -0800 Subject: [PATCH 108/263] Update the pubspec and changelog. --- pkgs/args/CHANGELOG.md | 6 ++++++ pkgs/args/pubspec.yaml | 2 +- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 8f9c01a4..15112fd8 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,9 @@ +## 0.13.3+3 + +* Add an explicit default value for the `allowTrailingOptions` parameter to `new + ArgParser()`. This doesn't change the behavior at all; the option already + defaulted to `false`, and passing in `null` still works. + ## 0.13.3+2 * Documentation fixes. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index c51fad74..326ef0ee 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+2 +version: 0.13.3+3 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From b9049a8610b28c4b93e1b02111112e5be76848a3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Luis=20Cruz?= Date: Sun, 28 Feb 2016 20:30:53 +0100 Subject: [PATCH 109/263] replace deprecated getUsage() method --- pkgs/args/README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 764e0080..9e3b3ca9 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -363,10 +363,10 @@ parser.addOption('arch', help: 'The architecture to compile for', }); ``` -To display the help, use the [getUsage()][getUsage] method: +To display the help, use the [usage][usage] getter: ```dart -print(parser.getUsage()); +print(parser.usage); ``` The resulting string looks something like this: From 575c880b4910a74ea847938a04309ee54fc2308d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 29 Feb 2016 12:37:58 -0800 Subject: [PATCH 110/263] Cut a release for the fix in b9049a8. See dart-lang/args#46 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 15112fd8..d8e4cf88 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.3+4 + +* Use the proper `usage` getter in the README. + ## 0.13.3+3 * Add an explicit default value for the `allowTrailingOptions` parameter to `new diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 326ef0ee..34688a23 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+3 +version: 0.13.3+4 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 4b7a45b8a489256c82b6e4e1ebf703ebe98ae951 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Wed, 2 Mar 2016 16:30:00 -0800 Subject: [PATCH 111/263] Make it strong mode clean. R=nweiz@google.com Review URL: https://codereview.chromium.org//1748333005 . --- pkgs/args/.analysis_options | 2 ++ pkgs/args/CHANGELOG.md | 8 ++++++-- pkgs/args/lib/command_runner.dart | 14 +++++++------- pkgs/args/lib/src/arg_parser.dart | 2 +- pkgs/args/lib/src/help_command.dart | 2 +- pkgs/args/lib/src/parser.dart | 3 ++- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/parse_test.dart | 2 +- pkgs/args/test/utils.dart | 2 +- 9 files changed, 22 insertions(+), 15 deletions(-) create mode 100644 pkgs/args/.analysis_options diff --git a/pkgs/args/.analysis_options b/pkgs/args/.analysis_options new file mode 100644 index 00000000..a10d4c5a --- /dev/null +++ b/pkgs/args/.analysis_options @@ -0,0 +1,2 @@ +analyzer: + strong-mode: true diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index d8e4cf88..0a1adc49 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.3+5 + +* Make strong mode clean. + ## 0.13.3+4 * Use the proper `usage` getter in the README. @@ -87,13 +91,13 @@ ## 0.12.0 * Removed public constructors for `ArgResults` and `Option`. - + * `ArgResults.wasParsed()` can be used to determine if an option was actually parsed or the default value is being returned. * Replaced `isFlag` and `allowMultiple` fields in the `Option` class with a three-value `OptionType` enum. - + * Options may define `valueHelp` which will then be shown in the usage. ## 0.11.0 diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index df1dbf86..3cc952ec 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -40,7 +40,7 @@ class CommandRunner { /// /// If a subclass overrides this to return a string, it will automatically be /// added to the end of [usage]. - final String usageFooter = null; + String get usageFooter => null; /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { @@ -60,7 +60,7 @@ Run "$executableName help " for more information about a command.'''; /// An unmodifiable view of all top-level commands defined for this runner. Map get commands => new UnmodifiableMapView(_commands); - final _commands = new Map(); + final _commands = {}; /// The top-level argument parser. /// @@ -129,7 +129,7 @@ Run "$executableName help " for more information about a command.'''; return new Future.sync(() { var argResults = topLevelResults; var commands = _commands; - var command; + Command command; var commandString = executableName; while (commands.isNotEmpty) { @@ -255,7 +255,7 @@ abstract class Command { /// /// If a subclass overrides this to return a string, it will automatically be /// added to the end of [usage]. - final String usageFooter = null; + String get usageFooter => null; /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { @@ -281,7 +281,7 @@ abstract class Command { /// An unmodifiable view of all sublevel commands of this command. Map get subcommands => new UnmodifiableMapView(_subcommands); - final _subcommands = new Map(); + final _subcommands = {}; /// Whether or not this command should be hidden from help listings. /// @@ -306,7 +306,7 @@ abstract class Command { /// /// This is intended to be overridden by commands that don't want to receive /// arguments. It has no effect for branch commands. - final takesArguments = true; + bool get takesArguments => true; /// Alternate names for this command. /// @@ -314,7 +314,7 @@ abstract class Command { /// invoked on the command line. /// /// This is intended to be overridden. - final aliases = const []; + List get aliases => const []; Command() { argParser.addFlag('help', diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index f5ce41a3..fa80031a 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -130,7 +130,7 @@ class ArgParser { /// 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 args) => - new Parser(null, this, args.toList(), null, null).parse(); + new Parser(null, this, args.toList()).parse(); /// Generates a string displaying usage information for the defined options. /// diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index 9ffcbe17..085e444c 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -23,7 +23,7 @@ class HelpCommand extends Command { // Walk the command tree to show help for the selected command or // subcommand. var commands = runner.commands; - var command = null; + Command command; var commandString = runner.executableName; for (var name in argResults.rest) { diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index c36035ce..55678542 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -35,7 +35,8 @@ class Parser { /// The accumulated parsed options. final Map results = {}; - Parser(this.commandName, this.grammar, this.args, this.parent, rest) { + Parser(this.commandName, this.grammar, this.args, + [this.parent, List rest]) { if (rest != null) this.rest.addAll(rest); } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 34688a23..abfd6aa8 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+4 +version: 0.13.3+5 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 187f735e..cdc6d5e6 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -117,7 +117,7 @@ void main() { }); test('are invoked even if the flag is not present', () { - var a = 'not called'; + var a = true; var parser = new ArgParser(); parser.addFlag('a', callback: (value) => a = value); diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 3ef68873..4ecc5026 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -9,7 +9,7 @@ import 'package:args/command_runner.dart'; import 'package:test/test.dart'; class CommandRunnerWithFooter extends CommandRunner { - final usageFooter = "Also, footer!"; + String get usageFooter => "Also, footer!"; CommandRunnerWithFooter(String executableName, String description) : super(executableName, description); From 8aed5d9e5c252b542e996a0b68b865c1fb581dc0 Mon Sep 17 00:00:00 2001 From: ArgentiApparatus Date: Fri, 4 Mar 2016 19:24:29 -0500 Subject: [PATCH 112/263] README fixes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit UsageError → UsageException Fixed links to classes, methods etc. in command_runner library --- pkgs/args/README.md | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 9e3b3ca9..461ebc69 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -320,12 +320,12 @@ class StashCommand extends Command { [CommandRunner][] automatically adds a `help` command that displays usage information for commands, as well as support for the `--help` flag for all commands. If it encounters an error parsing the arguments or processing a -command, it throws a [UsageError][]; your `main()` method should catch these and +command, it throws a [UsageException][]; your `main()` method should catch these and print them appropriately. For example: ```dart runner.run(arguments).catchError((error) { - if (error is! UsageError) throw error; + if (error is! UsageException) throw error; print(error); exit(64); // Exit code 64 indicates a usage error. }); @@ -386,14 +386,14 @@ The resulting string looks something like this: [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces [ArgParser]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser [ArgResults]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults -[CommandRunner]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.CommandRunner -[Command]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command -[UsageError]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.UsageError +[CommandRunner]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.CommandRunner +[Command]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command +[UsageException]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.UsageException [addOption]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addOption [addFlag]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addFlag [parse]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_parse [rest]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults@id_rest [addCommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addCommand [getUsage]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_getUsage -[addSubcommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command@id_addSubcommand -[run]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.Command@id_run +[addSubcommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command@id_addSubcommand +[run]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command@id_run From 975c67b41e45b67cbd80e74760230c8db4ef7115 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 4 Mar 2016 17:47:32 -0800 Subject: [PATCH 113/263] Release the changes in dart-lang/args#47. --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 0a1adc49..47c0cb5a 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.3+6 + +* README fixes. + ## 0.13.3+5 * Make strong mode clean. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index abfd6aa8..128dd7fa 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+5 +version: 0.13.3+6 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From cc05e9cc10e6e61fad21fcc7c8aecf4033824938 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 17 Mar 2016 15:53:04 -0700 Subject: [PATCH 114/263] Add an explicit distinction between a command's description and summary. 6e6d9f282c364b321127be70d522be64d47cc902 broke some users who were relying on only the first line of a command's description being included in its runner's usage as a summary. That is once again the default behavior. To support the use case described in dart-lang/args#42, there's a new Command.summary getter that explicitly controls the summary included in the parent runner's usage. This defaults to the first line of the description, but may be overridden to declare a multi-line summary. Closes dart-lang/args#42 R=rnystrom@google.com Review URL: https://codereview.chromium.org//1812923003 . --- pkgs/args/CHANGELOG.md | 11 +++++++++++ pkgs/args/lib/command_runner.dart | 10 ++++++++-- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 20 +++++++++++++++++++- pkgs/args/test/utils.dart | 4 ++++ 5 files changed, 43 insertions(+), 4 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 47c0cb5a..ae90612d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,14 @@ +## 0.13.4 + +* By default, only the first line of a command's description is included in its + parent runner's usage string. This returns to the default behavior from + before 0.13.3+1. + +* A `Command.summary` getter has been added to explicitly control the summary + that appears in the parent runner's usage string. This getter defaults to the + first line of the description, but can be overridden if the user wants a + multi-line summary. + ## 0.13.3+6 * README fixes. diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 3cc952ec..d0cbf261 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -190,9 +190,15 @@ abstract class Command { /// The name of this command. String get name; - /// A short description of this command. + /// A description of this command, included in [usage]. String get description; + /// A short description of this command, included in [parent]'s + /// [CommandRunner.usage]. + /// + /// This defaults to the first line of [description]. + String get summary => description.split("\n").first; + /// A single-line template for how to invoke this command (e.g. `"pub get /// [package]"`). String get invocation { @@ -371,7 +377,7 @@ String _getCommandUsage(Map commands, var buffer = new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { - var lines = commands[name].description.split("\n"); + var lines = commands[name].summary.split("\n"); buffer.writeln(); buffer.write(' ${padRight(name, length)} ${lines.first}'); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 128dd7fa..38b5654c 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.3+6 +version: 0.13.4 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index bccc7b31..9961e7dc 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -54,7 +54,7 @@ Available commands: Run "test help " for more information about a command.""")); }); - test("supports newlines in command descriptions", () { + test("truncates newlines in command descriptions by default", () { runner.addCommand(new MultilineCommand()); expect(runner.usage, equals(""" @@ -65,6 +65,24 @@ Usage: test [arguments] Global options: -h, --help Print this usage information. +Available commands: + help Display help information for test. + multiline Multi + +Run "test help " for more information about a command.""")); + }); + + test("supports newlines in command summaries", () { + runner.addCommand(new MultilineSummaryCommand()); + + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + Available commands: help Display help information for test. multiline Multi diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 4ecc5026..1fd84689 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -39,6 +39,10 @@ class MultilineCommand extends Command { } } +class MultilineSummaryCommand extends MultilineCommand { + String get summary => description; +} + class HiddenCommand extends Command { var hasRun = false; From 18fee1314757bbb23fdd0fc38ecc2fce6a3eac8c Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 5 May 2016 16:07:10 -0700 Subject: [PATCH 115/263] Ensure that lists are reified. This is important for strong mode, since "as List" will fail for a "List". R=rnystrom@google.com Review URL: https://codereview.chromium.org//1954923002 . --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/src/parser.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/parse_test.dart | 4 ++++ 4 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index ae90612d..1c9f5fc3 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.4+1 + +* Ensure that multiple-value arguments produce reified `List`s. + ## 0.13.4 * By default, only the first line of a command's description is included in its diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 55678542..22579fb2 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -258,7 +258,7 @@ class Parser { return; } - var list = results.putIfAbsent(option.name, () => []); + var list = results.putIfAbsent(option.name, () => []); if (option.splitCommas) { for (var element in value.split(",")) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 38b5654c..78a8a72f 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.4 +version: 0.13.4+1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index cdc6d5e6..6ce8a199 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -161,6 +161,10 @@ void main() { parser.parse(['--a=v', '--a=x']); expect(a, equals(['v', 'x'])); + + // This reified type is important in strong mode so that people can + // safely write "as List". + expect(a, new isInstanceOf>()); }); test('for single present, allowMultiple, options are invoked with ' From b6a5600c977311652221c1580d85d04e3d3dd823 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 16 May 2016 15:17:25 -0700 Subject: [PATCH 116/263] UsageException isn't called UsageError. Closes dart-lang/args#50. R=rnystrom@google.com Review URL: https://codereview.chromium.org//1985883002 . --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/command_runner.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 14 +++++++------- pkgs/args/test/command_test.dart | 3 ++- pkgs/args/test/utils.dart | 2 +- 6 files changed, 16 insertions(+), 11 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 1c9f5fc3..cbbe4639 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 0.13.4+2 + +* Fix a minor documentation error. + ## 0.13.4+1 * Ensure that multiple-value arguments produce reified `List`s. diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index d0cbf261..1e33965c 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -98,7 +98,7 @@ Run "$executableName help " for more information about a command.'''; /// Parses [args] and invokes [Command.run] on the chosen command. /// /// This always returns a [Future] in case the command is asynchronous. The - /// [Future] will throw a [UsageError] if [args] was invalid. + /// [Future] will throw a [UsageException] if [args] was invalid. Future run(Iterable args) => new Future.sync(() => runCommand(parse(args))); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 78a8a72f..4f5fb7f3 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.4+1 +version: 0.13.4+2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 9961e7dc..d6605a4d 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -139,7 +139,7 @@ Run "test help " for more information about a command.""")); test("usageException splits up the message and usage", () { expect(() => runner.usageException("message"), - throwsUsageError("message", _DEFAULT_USAGE)); + throwsUsageException("message", _DEFAULT_USAGE)); }); group("run()", () { @@ -262,7 +262,7 @@ Also, footer!""")); }); test("includes the footer in usage errors", () { - expect(runner.run(["--bad"]), throwsUsageError( + expect(runner.run(["--bad"]), throwsUsageException( 'Could not find an option named "bad".', "$_DEFAULT_USAGE\nAlso, footer!")); }); @@ -270,19 +270,19 @@ Also, footer!""")); group("throws a useful error when", () { test("arg parsing fails", () { - expect(runner.run(["--bad"]), throwsUsageError( + expect(runner.run(["--bad"]), throwsUsageException( 'Could not find an option named "bad".', _DEFAULT_USAGE)); }); test("a top-level command doesn't exist", () { - expect(runner.run(["bad"]), throwsUsageError( + expect(runner.run(["bad"]), throwsUsageException( 'Could not find a command named "bad".', _DEFAULT_USAGE)); }); test("a subcommand doesn't exist", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo bad"]), throwsUsageError( + expect(runner.run(["foo bad"]), throwsUsageException( 'Could not find a command named "foo bad".', """ Usage: test [arguments] @@ -299,7 +299,7 @@ Run "test help " for more information about a command.""")); test("a subcommand wasn't passed", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo"]), throwsUsageError( + expect(runner.run(["foo"]), throwsUsageException( 'Missing subcommand for "test foo".', """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -313,7 +313,7 @@ Run "test help" to see global options.""")); test("a command that doesn't take arguments was given them", () { runner.addCommand(new FooCommand()); - expect(runner.run(["foo", "bar"]), throwsUsageError( + expect(runner.run(["foo", "bar"]), throwsUsageException( 'Command "foo" does not take any arguments.', """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 127cfc91..cd32c63d 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -90,7 +90,8 @@ Run "test help" to see global options.""")); }); test("usageException splits up the message and usage", () { - expect(() => foo.usageException("message"), throwsUsageError("message", """ + expect(() => foo.usageException("message"), + throwsUsageException("message", """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index 1fd84689..e07f8f3b 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -87,7 +87,7 @@ void throwsFormat(ArgParser parser, List args) { expect(() => parser.parse(args), throwsFormatException); } -Matcher throwsUsageError(message, usage) { +Matcher throwsUsageException(message, usage) { return throwsA(predicate((error) { expect(error, new isInstanceOf()); expect(error.message, message); From e612d155c741db90c7ec2137d17191e3a1df483d Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 22 Jul 2016 11:41:04 -0700 Subject: [PATCH 117/263] Allow command ArgParsers to be overridden. This didn't work in strong mode, since they were declared as fields. R=rnystrom@google.com Review URL: https://codereview.chromium.org//2173623002 . --- pkgs/args/CHANGELOG.md | 5 +++++ pkgs/args/lib/command_runner.dart | 6 ++++-- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index cbbe4639..0861e198 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.5 + +* Allow `CommandRunner.argParser` and `Command.argParser` to be overridden in + strong mode. + ## 0.13.4+2 * Fix a minor documentation error. diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 1e33965c..21dfa32d 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -67,7 +67,8 @@ Run "$executableName help " for more information about a command.'''; /// Global options should be registered with this parser; they'll end up /// available via [Command.globalResults]. Commands should be registered with /// [addCommand] rather than directly on the parser. - final argParser = new ArgParser(); + ArgParser get argParser => _argParser; + final _argParser = new ArgParser(); CommandRunner(this.executableName, this.description) { argParser.addFlag('help', @@ -249,7 +250,8 @@ abstract class Command { /// the constructor); they'll end up available via [argResults]. Subcommands /// should be registered with [addSubcommand] rather than directly on the /// parser. - final argParser = new ArgParser(); + ArgParser get argParser => _argParser; + final _argParser = new ArgParser(); /// Generates a string displaying usage information for this command. /// diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4f5fb7f3..e52bf292 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.4+2 +version: 0.13.5 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 9a107d1cc207208f365251a65289d52779a8ed97 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 30 Sep 2016 15:58:48 -0700 Subject: [PATCH 118/263] Print the proper usage in CommandRunner. (dart-lang/args#53) This now prints the usage for the command that failed to parse, rather than always printing the root usage. Closes dart-lang/args#52 --- pkgs/args/CHANGELOG.md | 8 ++++++++ pkgs/args/README.md | 8 +++++--- pkgs/args/lib/args.dart | 1 + pkgs/args/lib/command_runner.dart | 18 ++++++++++++------ pkgs/args/lib/src/arg_parser_exception.dart | 17 +++++++++++++++++ pkgs/args/lib/src/parser.dart | 15 ++++++++++++--- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 21 +++++++++++++++++++++ 8 files changed, 77 insertions(+), 13 deletions(-) create mode 100644 pkgs/args/lib/src/arg_parser_exception.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 0861e198..108d60e0 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.13.6 + +* `ArgParser.parse()` now throws an `ArgParserException`, which implements + `FormatException` and has a field that lists the commands that were parsed. + +* If `CommandRunner.run()` encounters a parse error for a subcommand, it now + prints the subcommand's usage rather than the global usage. + ## 0.13.5 * Allow `CommandRunner.argParser` and `Command.argParser` to be overridden in diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 461ebc69..36e931c5 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -51,9 +51,11 @@ The default value for non-flag options can be any string. For flags, it must be a `bool`. To validate a non-flag option, you can use the `allowed` parameter to provide an -allowed set of values. When you do, the parser throws a [FormatException] if the -value for an option is not in the allowed set. Here's an example of specifying -allowed values: +allowed set of values. When you do, the parser throws an +[`ArgParserException`][ArgParserException] if the value for an option is not in +the allowed set. Here's an example of specifying allowed values: + +[ArgParserException]: https://www.dartdocs.org/documentation/args/latest/args/ArgParserException-class.html ```dart parser.addOption('mode', allowed: ['debug', 'release']); diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 2534082d..97b47b54 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -3,5 +3,6 @@ // BSD-style license that can be found in the LICENSE file. export 'src/arg_parser.dart'; +export 'src/arg_parser_exception.dart'; export 'src/arg_results.dart' hide newArgResults; export 'src/option.dart' hide newOption; diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 21dfa32d..5780fae5 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -7,6 +7,7 @@ import 'dart:collection'; import 'dart:math' as math; import 'src/arg_parser.dart'; +import 'src/arg_parser_exception.dart'; import 'src/arg_results.dart'; import 'src/help_command.dart'; import 'src/usage_exception.dart'; @@ -103,18 +104,23 @@ Run "$executableName help " for more information about a command.'''; Future run(Iterable args) => new Future.sync(() => runCommand(parse(args))); - /// Parses [args] and returns the result, converting a [FormatException] to a - /// [UsageException]. + /// Parses [args] and returns the result, converting an [ArgParserException] + /// to a [UsageException]. /// /// This is notionally a protected method. It may be overridden or called from /// subclasses, but it shouldn't be called externally. ArgResults parse(Iterable args) { try { - // TODO(nweiz): if arg parsing fails for a command, print that command's - // usage, not the global usage. return argParser.parse(args); - } on FormatException catch (error) { - usageException(error.message); + } on ArgParserException catch (error) { + if (error.commands.isEmpty) usageException(error.message); + + var command = commands[error.commands.first]; + for (var commandName in error.commands.skip(1)) { + command = command.subcommands[commandName]; + } + + command.usageException(error.message); } } diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart new file mode 100644 index 00000000..20eddb9d --- /dev/null +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -0,0 +1,17 @@ +// Copyright (c) 2016, 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. + +/// An exception thrown by [ArgParser]. +class ArgParserException extends FormatException { + /// The command(s) that were parsed before discovering the error. + /// + /// This will be empty if the error was on the root parser. + final List commands; + + ArgParserException(String message, [Iterable commands]) + : commands = commands == null + ? const [] + : new List.unmodifiable(commands), + super(message); +} diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 22579fb2..64374fcb 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -3,6 +3,7 @@ // BSD-style license that can be found in the LICENSE file. import 'arg_parser.dart'; +import 'arg_parser_exception.dart'; import 'arg_results.dart'; import 'option.dart'; @@ -63,7 +64,15 @@ class Parser { validate(rest.isEmpty, 'Cannot specify arguments before a command.'); var commandName = args.removeAt(0); var commandParser = new Parser(commandName, command, args, this, rest); - commandResults = commandParser.parse(); + + try { + commandResults = commandParser.parse(); + } on ArgParserException catch (error) { + if (commandName == null) rethrow; + throw new ArgParserException( + error.message, + [commandName]..addAll(error.commands)); + } // All remaining arguments were passed to command so clear them here. rest.clear(); @@ -242,9 +251,9 @@ class Parser { /// Called during parsing to validate the arguments. /// - /// Throws a [FormatException] if [condition] is `false`. + /// Throws an [ArgParserException] if [condition] is `false`. void validate(bool condition, String message) { - if (!condition) throw new FormatException(message); + if (!condition) throw new ArgParserException(message); } /// Validates and stores [value] as the value for [option], which must not be diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index e52bf292..ab921a50 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.5 +version: 0.13.6 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index d6605a4d..6b4e30e5 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -246,6 +246,27 @@ Run "test help" to see global options. """)); }); }); + + group("with an invalid argument", () { + test("at the root throws the root usage", () { + expect(runner.run(["--asdf"]), throwsUsageException( + 'Could not find an option named "asdf".', + '$_DEFAULT_USAGE')); + }); + + test("for a command throws the command usage", () { + var command = new FooCommand(); + runner.addCommand(command); + + expect(runner.run(["foo", "--asdf"]), throwsUsageException( + 'Could not find an option named "asdf".', + """ +Usage: test foo [arguments] +-h, --help Print this usage information. + +Run "test help" to see global options.""")); + }); + }); }); group("with a footer", () { From 6bfe39e48383ca3a13c25d8f36f242f211812659 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 26 Oct 2016 12:25:28 -0700 Subject: [PATCH 119/263] Fix an analysis hint. (dart-lang/args#56) --- pkgs/args/lib/command_runner.dart | 1 + 1 file changed, 1 insertion(+) diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 5780fae5..fa22c236 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -121,6 +121,7 @@ Run "$executableName help " for more information about a command.'''; } command.usageException(error.message); + return null; } } From 946a28cdbeea7204ebfc4575bc4fa67b4571112e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 2 Nov 2016 15:11:36 -0700 Subject: [PATCH 120/263] Print command help for "bin --help command" (dart-lang/args#55) Closes dart-lang/args#54 --- pkgs/args/CHANGELOG.md | 5 ++ pkgs/args/lib/command_runner.dart | 99 +++++++++++++------------ pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 16 +++- 4 files changed, 72 insertions(+), 50 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 108d60e0..91273322 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.6+1 + +* When a `CommandRunner` is passed `--help` before any commands, it now prints + the usage of the chosen command. + ## 0.13.6 * `ArgParser.parse()` now throws an `ArgParserException`, which implements diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index fa22c236..338ad947 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -133,56 +133,59 @@ Run "$executableName help " for more information about a command.'''; /// It's useful to override this to handle global flags and/or wrap the entire /// command in a block. For example, you might handle the `--verbose` flag /// here to enable verbose logging before running the command. - Future runCommand(ArgResults topLevelResults) { - return new Future.sync(() { - var argResults = topLevelResults; - var commands = _commands; - Command command; - var commandString = executableName; - - while (commands.isNotEmpty) { - if (argResults.command == null) { - if (argResults.rest.isEmpty) { - if (command == null) { - // No top-level command was chosen. - printUsage(); - return new Future.value(); - } - - command.usageException('Missing subcommand for "$commandString".'); - } else { - if (command == null) { - usageException( - 'Could not find a command named "${argResults.rest[0]}".'); - } - - command.usageException('Could not find a subcommand named ' - '"${argResults.rest[0]}" for "$commandString".'); + Future runCommand(ArgResults topLevelResults) async { + var argResults = topLevelResults; + var commands = _commands; + Command command; + var commandString = executableName; + + while (commands.isNotEmpty) { + if (argResults.command == null) { + if (argResults.rest.isEmpty) { + if (command == null) { + // No top-level command was chosen. + printUsage(); + return; + } + + command.usageException('Missing subcommand for "$commandString".'); + } else { + if (command == null) { + usageException( + 'Could not find a command named "${argResults.rest[0]}".'); } - } - // Step into the command. - argResults = argResults.command; - command = commands[argResults.name]; - command._globalResults = topLevelResults; - command._argResults = argResults; - commands = command._subcommands; - commandString += " ${argResults.name}"; - - if (argResults['help']) { - command.printUsage(); - return new Future.value(); + command.usageException('Could not find a subcommand named ' + '"${argResults.rest[0]}" for "$commandString".'); } } - // Make sure there aren't unexpected arguments. - if (!command.takesArguments && argResults.rest.isNotEmpty) { - command.usageException( - 'Command "${argResults.name}" does not take any arguments.'); + // Step into the command. + argResults = argResults.command; + command = commands[argResults.name]; + command._globalResults = topLevelResults; + command._argResults = argResults; + commands = command._subcommands; + commandString += " ${argResults.name}"; + + if (argResults['help']) { + command.printUsage(); + return; } + } - return command.run(); - }); + if (topLevelResults['help']) { + command.printUsage(); + return; + } + + // Make sure there aren't unexpected arguments. + if (!command.takesArguments && argResults.rest.isNotEmpty) { + command.usageException( + 'Command "${argResults.name}" does not take any arguments.'); + } + + await command.run(); } } @@ -275,8 +278,8 @@ abstract class Command { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { var buffer = new StringBuffer() - ..writeln('Usage: $invocation') - ..writeln(argParser.usage); + ..writeln('Usage: $invocation') + ..writeln(argParser.usage); if (_subcommands.isNotEmpty) { buffer.writeln(); @@ -362,7 +365,7 @@ abstract class Command { /// Throws a [UsageException] with [message]. void usageException(String message) => - throw new UsageException(message, _usageWithoutDescription); + throw new UsageException(message, _usageWithoutDescription); } /// Returns a string representation of [commands] fit for use in a usage string. @@ -373,7 +376,7 @@ String _getCommandUsage(Map commands, {bool isSubcommand: false}) { // Don't include aliases. var names = - commands.keys.where((name) => !commands[name].aliases.contains(name)); + commands.keys.where((name) => !commands[name].aliases.contains(name)); // Filter out hidden ones, unless they are all hidden. var visible = names.where((name) => !commands[name].hidden); @@ -384,7 +387,7 @@ String _getCommandUsage(Map commands, var length = names.map((name) => name.length).reduce(math.max); var buffer = - new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); + new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { var lines = commands[name].summary.split("\n"); buffer.writeln(); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index ab921a50..4156049b 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.6 +version: 0.13.6+1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 6b4e30e5..8875d8c3 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -197,7 +197,7 @@ $_DEFAULT_USAGE """)); }); - test("with a command prints the usage for that command", () { + test("with a preceding command prints the usage for that command", () { var command = new FooCommand(); runner.addCommand(command); @@ -207,6 +207,20 @@ Set a value. Usage: test foo [arguments] -h, --help Print this usage information. +Run "test help" to see global options. +""")); + }); + + test("with a following command prints the usage for that command", () { + var command = new FooCommand(); + runner.addCommand(command); + + expect(() => runner.run(["--help", "foo"]), prints(""" +Set a value. + +Usage: test foo [arguments] +-h, --help Print this usage information. + Run "test help" to see global options. """)); }); From 971a4976c4d43b7160055a30f2e2824f15679582 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 11 Nov 2016 14:42:13 -0800 Subject: [PATCH 121/263] Support command return value forwarding. (dart-lang/args#59) This was unintentionally supported prior to 0.13.6+1. This re-adds support, documents and tests it, and adds type annotations to make it type-safe. Closes dart-lang/args#57 Closes dart-lang/args#58 --- pkgs/args/CHANGELOG.md | 8 ++++ pkgs/args/lib/command_runner.dart | 50 ++++++++++++++----------- pkgs/args/lib/src/help_command.dart | 7 ++-- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/command_runner_test.dart | 16 ++++++++ pkgs/args/test/utils.dart | 16 ++++++++ 6 files changed, 74 insertions(+), 25 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 91273322..5f5c8c6c 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,11 @@ +## 0.13.7 + +* Add explicit support for forwarding the value returned by `Command.run()` to + `CommandRunner.run()`. This worked unintentionally prior to 0.13.6+1. + +* Add type arguments to `CommandRunner` and `Command` to indicate the return + values of the `run()` functions. + ## 0.13.6+1 * When a `CommandRunner` is passed `--help` before any commands, it now prints diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 338ad947..6a6d2826 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -16,7 +16,11 @@ import 'src/utils.dart'; export 'src/usage_exception.dart'; /// A class for invoking [Commands] based on raw command-line arguments. -class CommandRunner { +/// +/// The type argument `T` represents the type returned by [Command.run] and +/// [CommandRunner.run]; it can be ommitted if you're not using the return +/// values. +class CommandRunner { /// The name of the executable being run. /// /// Used for error reporting and [usage]. @@ -60,8 +64,8 @@ Run "$executableName help " for more information about a command.'''; } /// An unmodifiable view of all top-level commands defined for this runner. - Map get commands => new UnmodifiableMapView(_commands); - final _commands = {}; + Map> get commands => new UnmodifiableMapView(_commands); + final _commands = >{}; /// The top-level argument parser. /// @@ -74,7 +78,7 @@ Run "$executableName help " for more information about a command.'''; CommandRunner(this.executableName, this.description) { argParser.addFlag('help', abbr: 'h', negatable: false, help: 'Print this usage information.'); - addCommand(new HelpCommand()); + addCommand(new HelpCommand()); } /// Prints the usage information for this runner. @@ -88,7 +92,7 @@ Run "$executableName help " for more information about a command.'''; throw new UsageException(message, _usageWithoutDescription); /// Adds [Command] as a top-level command to this runner. - void addCommand(Command command) { + void addCommand(Command command) { var names = [command.name]..addAll(command.aliases); for (var name in names) { _commands[name] = command; @@ -101,7 +105,7 @@ Run "$executableName help " for more information about a command.'''; /// /// This always returns a [Future] in case the command is asynchronous. The /// [Future] will throw a [UsageException] if [args] was invalid. - Future run(Iterable args) => + Future run(Iterable args) => new Future.sync(() => runCommand(parse(args))); /// Parses [args] and returns the result, converting an [ArgParserException] @@ -133,7 +137,9 @@ Run "$executableName help " for more information about a command.'''; /// It's useful to override this to handle global flags and/or wrap the entire /// command in a block. For example, you might handle the `--verbose` flag /// here to enable verbose logging before running the command. - Future runCommand(ArgResults topLevelResults) async { + /// + /// This returns the return value of [Command.run]. + Future runCommand(ArgResults topLevelResults) async { var argResults = topLevelResults; var commands = _commands; Command command; @@ -145,7 +151,7 @@ Run "$executableName help " for more information about a command.'''; if (command == null) { // No top-level command was chosen. printUsage(); - return; + return null; } command.usageException('Missing subcommand for "$commandString".'); @@ -170,13 +176,13 @@ Run "$executableName help " for more information about a command.'''; if (argResults['help']) { command.printUsage(); - return; + return null; } } if (topLevelResults['help']) { command.printUsage(); - return; + return null; } // Make sure there aren't unexpected arguments. @@ -185,7 +191,7 @@ Run "$executableName help " for more information about a command.'''; 'Command "${argResults.name}" does not take any arguments.'); } - await command.run(); + return (await command.run()) as T; } } @@ -197,7 +203,7 @@ Run "$executableName help " for more information about a command.'''; /// A command with subcommands is known as a "branch command" and cannot be run /// itself. It should call [addSubcommand] (often from the constructor) to /// register subcommands. -abstract class Command { +abstract class Command { /// The name of this command. String get name; @@ -229,18 +235,18 @@ abstract class Command { /// /// This will be `null` until [Command.addSubcommmand] has been called with /// this command. - Command get parent => _parent; - Command _parent; + Command get parent => _parent; + Command _parent; /// The command runner for this command. /// /// This will be `null` until [CommandRunner.addCommand] has been called with /// this command or one of its parents. - CommandRunner get runner { + CommandRunner get runner { if (parent == null) return _runner; return parent.runner; } - CommandRunner _runner; + CommandRunner _runner; /// The parsed global argument results. /// @@ -298,8 +304,9 @@ abstract class Command { } /// An unmodifiable view of all sublevel commands of this command. - Map get subcommands => new UnmodifiableMapView(_subcommands); - final _subcommands = {}; + Map> get subcommands => + new UnmodifiableMapView(_subcommands); + final _subcommands = >{}; /// Whether or not this command should be hidden from help listings. /// @@ -341,14 +348,15 @@ abstract class Command { /// Runs this command. /// - /// If this returns a [Future], [CommandRunner.run] won't complete until the - /// returned [Future] does. Otherwise, the return value is ignored. + /// This must return a `T`, a `Future`, or `null`. The value is returned by + /// [CommandRunner.runCommand]. Subclasses must explicitly declare a return + /// type for `run()`, and may not use `void` if `T` is defined. run() { throw new UnimplementedError("Leaf command $this must implement run()."); } /// Adds [Command] as a subcommand of this. - void addSubcommand(Command command) { + void addSubcommand(Command command) { var names = [command.name]..addAll(command.aliases); for (var name in names) { _subcommands[name] = command; diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index 085e444c..104327c9 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -7,17 +7,17 @@ import '../command_runner.dart'; /// The built-in help command that's added to every [CommandRunner]. /// /// This command displays help information for the various subcommands. -class HelpCommand extends Command { +class HelpCommand extends Command { final name = "help"; String get description => "Display help information for ${runner.executableName}."; String get invocation => "${runner.executableName} help [command]"; - void run() { + T run() { // Show the default help if no command was specified. if (argResults.rest.isEmpty) { runner.printUsage(); - return; + return null; } // Walk the command tree to show help for the selected command or @@ -47,5 +47,6 @@ class HelpCommand extends Command { } command.printUsage(); + return null; } } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4156049b..e1a6b05e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.6+1 +version: 0.13.7 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 8875d8c3..9b0713a3 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -161,6 +161,22 @@ Run "test help " for more information about a command.""")); }), completes); }); + test("runs a command with a return value", () { + var runner = new CommandRunner("test", ""); + var command = new ValueCommand(); + runner.addCommand(command); + + expect(runner.run(["foo"]), completion(equals(12))); + }); + + test("runs a command with an asynchronous return value", () { + var runner = new CommandRunner("test", ""); + var command = new AsyncValueCommand(); + runner.addCommand(command); + + expect(runner.run(["foo"]), completion(equals("hi"))); + }); + test("runs a hidden comand", () { var command = new HiddenCommand(); runner.addCommand(command); diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index e07f8f3b..a874eeec 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -27,6 +27,22 @@ class FooCommand extends Command { } } +class ValueCommand extends Command { + final name = "foo"; + final description = "Return a value."; + final takesArguments = false; + + int run() => 12; +} + +class AsyncValueCommand extends Command { + final name = "foo"; + final description = "Return a future."; + final takesArguments = false; + + Future run() async => "hi"; +} + class MultilineCommand extends Command { var hasRun = false; From db73ee0c8685e742eda2b79daeb3e73fddc8a67a Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 10 Feb 2017 13:34:32 -0800 Subject: [PATCH 122/263] Fix return value with latest change in pkg/test (dart-lang/args#63) --- pkgs/args/test/trailing_options_test.dart | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index cdc4227f..ce22ad4f 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -17,9 +17,10 @@ void main() { parser = new ArgParser(allowTrailingOptions: true); }); - void expectThrows(List args) => expect( - () => parser.parse(args), throwsFormatException, - reason: "with allowTrailingOptions: true"); + void expectThrows(List args) { + expect(() => parser.parse(args), throwsFormatException, + reason: "with allowTrailingOptions: true"); + } test('collects non-options in rest', () { parser.addFlag('flag'); From 62f052568007e8839d72ffc82f0ee070d489d763 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 6 Apr 2017 16:56:26 -0700 Subject: [PATCH 123/263] Add Travic-CI support Follow up on https://github.com/dart-lang/args/pull/64 --- pkgs/args/.travis.yml | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 pkgs/args/.travis.yml diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml new file mode 100644 index 00000000..abf77dd8 --- /dev/null +++ b/pkgs/args/.travis.yml @@ -0,0 +1,16 @@ +language: dart +sudo: false +dart: + - stable + - dev + - 1.22.1 + - 1.21.1 + - 1.20.1 + - 1.19.1 +dart_task: + - test: -p vm + - dartfmt + - dartanalyzer +cache: + directories: + - $HOME/.pub-cache From b647b8cfa84f595b0d7f1d290e14e47f66eb566f Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 6 Apr 2017 16:59:18 -0700 Subject: [PATCH 124/263] dartfmt --- pkgs/args/example/test_runner.dart | 93 +++++++++-------- pkgs/args/lib/command_runner.dart | 13 +-- pkgs/args/lib/src/arg_parser.dart | 57 +++++++---- pkgs/args/lib/src/arg_parser_exception.dart | 5 +- pkgs/args/lib/src/arg_results.dart | 8 +- pkgs/args/lib/src/option.dart | 51 ++++++---- pkgs/args/lib/src/parser.dart | 6 +- pkgs/args/test/args_test.dart | 9 +- pkgs/args/test/command_runner_test.dart | 95 ++++++++++------- pkgs/args/test/command_test.dart | 7 +- pkgs/args/test/parse_test.dart | 26 +++-- pkgs/args/test/usage_test.dart | 107 ++++++++++++++------ 12 files changed, 305 insertions(+), 172 deletions(-) diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index 0d15e839..d9a62971 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -20,49 +20,53 @@ main() { abbr: 'c', defaultsTo: 'none', help: 'Specify any compilation step (if needed).', - allowed: ['none', 'dart2js', 'dartc'], + allowed: [ + 'none', + 'dart2js', + 'dartc' + ], allowedHelp: { - 'none': 'Do not compile the Dart code (run native Dart code on the' - ' VM).\n(only valid with the following runtimes: vm, drt)', - 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' - '(only valid with the following runtimes: d8, drt, chrome\n' - 'safari, ie, firefox, opera, none (compile only))', - 'dartc': 'Perform static analysis on Dart code by running dartc.\n' - '(only valid with the following runtimes: none)', - }); + 'none': 'Do not compile the Dart code (run native Dart code on the' + ' VM).\n(only valid with the following runtimes: vm, drt)', + 'dart2js': 'Compile dart code to JavaScript by running dart2js.\n' + '(only valid with the following runtimes: d8, drt, chrome\n' + 'safari, ie, firefox, opera, none (compile only))', + 'dartc': 'Perform static analysis on Dart code by running dartc.\n' + '(only valid with the following runtimes: none)', + }); parser.addOption('runtime', abbr: 'r', defaultsTo: 'vm', help: 'Where the tests should be run.', allowed: [ - 'vm', - 'd8', - 'drt', - 'dartium', - 'ff', - 'firefox', - 'chrome', - 'safari', - 'ie', - 'opera', - 'none' - ], + 'vm', + 'd8', + 'drt', + 'dartium', + 'ff', + 'firefox', + 'chrome', + 'safari', + 'ie', + 'opera', + 'none' + ], allowedHelp: { - 'vm': 'Run Dart code on the standalone dart vm.', - 'd8': 'Run JavaScript from the command line using v8.', - // TODO(antonm): rename flag. - 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' - 'content shell.', - 'dartium': 'Run Dart or JavaScript in Dartium.', - 'ff': 'Run JavaScript in Firefox', - 'chrome': 'Run JavaScript in Chrome', - 'safari': 'Run JavaScript in Safari', - 'ie': 'Run JavaScript in Internet Explorer', - 'opera': 'Run JavaScript in Opera', - 'none': 'No runtime, compile only (for example, used for dartc static\n' - 'analysis tests).', - }); + 'vm': 'Run Dart code on the standalone dart vm.', + 'd8': 'Run JavaScript from the command line using v8.', + // TODO(antonm): rename flag. + 'drt': 'Run Dart or JavaScript in the headless version of Chrome,\n' + 'content shell.', + 'dartium': 'Run Dart or JavaScript in Dartium.', + 'ff': 'Run JavaScript in Firefox', + 'chrome': 'Run JavaScript in Chrome', + 'safari': 'Run JavaScript in Safari', + 'ie': 'Run JavaScript in Internet Explorer', + 'opera': 'Run JavaScript in Opera', + 'none': 'No runtime, compile only (for example, used for dartc static\n' + 'analysis tests).', + }); parser.addOption('arch', abbr: 'a', @@ -115,14 +119,14 @@ main() { defaultsTo: 'compact', help: 'Progress indication mode', allowed: [ - 'compact', - 'color', - 'line', - 'verbose', - 'silent', - 'status', - 'buildbot' - ]); + 'compact', + 'color', + 'line', + 'verbose', + 'silent', + 'status', + 'buildbot' + ]); parser.addFlag('report', defaultsTo: false, @@ -146,7 +150,8 @@ main() { defaultsTo: false, help: 'Keep the generated files in the temporary directory'); - parser.addOption('special-command', help: """ + parser.addOption('special-command', + help: """ Special command support. Wraps the command line in a special command. The special command should contain an '@' character which will be replaced by the normal diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 6a6d2826..bbf08ae3 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -138,7 +138,7 @@ Run "$executableName help " for more information about a command.'''; /// command in a block. For example, you might handle the `--verbose` flag /// here to enable verbose logging before running the command. /// - /// This returns the return value of [Command.run]. + /// This returns the return value of [Command.run]. Future runCommand(ArgResults topLevelResults) async { var argResults = topLevelResults; var commands = _commands; @@ -246,6 +246,7 @@ abstract class Command { if (parent == null) return _runner; return parent.runner; } + CommandRunner _runner; /// The parsed global argument results. @@ -284,8 +285,8 @@ abstract class Command { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { var buffer = new StringBuffer() - ..writeln('Usage: $invocation') - ..writeln(argParser.usage); + ..writeln('Usage: $invocation') + ..writeln(argParser.usage); if (_subcommands.isNotEmpty) { buffer.writeln(); @@ -373,7 +374,7 @@ abstract class Command { /// Throws a [UsageException] with [message]. void usageException(String message) => - throw new UsageException(message, _usageWithoutDescription); + throw new UsageException(message, _usageWithoutDescription); } /// Returns a string representation of [commands] fit for use in a usage string. @@ -384,7 +385,7 @@ String _getCommandUsage(Map commands, {bool isSubcommand: false}) { // Don't include aliases. var names = - commands.keys.where((name) => !commands[name].aliases.contains(name)); + commands.keys.where((name) => !commands[name].aliases.contains(name)); // Filter out hidden ones, unless they are all hidden. var visible = names.where((name) => !commands[name].hidden); @@ -395,7 +396,7 @@ String _getCommandUsage(Map commands, var length = names.map((name) => name.length).reduce(math.max); var buffer = - new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); + new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { var lines = commands[name].summary.split("\n"); buffer.writeln(); diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index fa80031a..44ceb64e 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -35,9 +35,9 @@ class ArgParser { /// after it finds an argument that is neither an option nor a command. /// This allows options to be specified after regular arguments. Defaults to /// `false`. - factory ArgParser({bool allowTrailingOptions: false}) => new ArgParser._( - {}, {}, - allowTrailingOptions: allowTrailingOptions); + factory ArgParser({bool allowTrailingOptions: false}) => + new ArgParser._({}, {}, + allowTrailingOptions: allowTrailingOptions); ArgParser._(Map options, Map commands, {bool allowTrailingOptions: false}) @@ -45,9 +45,8 @@ class ArgParser { this.options = new UnmodifiableMapView(options), this._commands = commands, this.commands = new UnmodifiableMapView(commands), - this.allowTrailingOptions = allowTrailingOptions != null - ? allowTrailingOptions - : false; + this.allowTrailingOptions = + allowTrailingOptions != null ? allowTrailingOptions : false; /// Defines a command. /// @@ -69,10 +68,16 @@ class ArgParser { /// /// * There is already an option named [name]. /// * There is already an option using abbreviation [abbr]. - void addFlag(String name, {String abbr, String help, bool defaultsTo: false, - bool negatable: true, void callback(bool value), bool hide: false}) { + void addFlag(String name, + {String abbr, + String help, + bool defaultsTo: false, + bool negatable: true, + void callback(bool value), + bool hide: false}) { _addOption(name, abbr, help, null, null, null, defaultsTo, callback, - OptionType.FLAG, negatable: negatable, hide: hide); + OptionType.FLAG, + negatable: negatable, hide: hide); } /// Defines a value-taking option. Throws an [ArgumentError] if: @@ -80,9 +85,16 @@ class ArgParser { /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. /// * [splitCommas] is passed but [allowMultiple] is `false`. - void addOption(String name, {String abbr, String help, String valueHelp, - List allowed, Map allowedHelp, String defaultsTo, - void callback(value), bool allowMultiple: false, bool splitCommas, + void addOption(String name, + {String abbr, + String help, + String valueHelp, + List allowed, + Map allowedHelp, + String defaultsTo, + void callback(value), + bool allowMultiple: false, + bool splitCommas, bool hide: false}) { if (!allowMultiple && splitCommas != null) { throw new ArgumentError( @@ -94,10 +106,19 @@ class ArgParser { splitCommas: splitCommas, hide: hide); } - void _addOption(String name, String abbr, String help, String valueHelp, - List allowed, Map allowedHelp, defaultsTo, - void callback(value), OptionType type, - {bool negatable: false, bool splitCommas, bool hide: false}) { + void _addOption( + String name, + String abbr, + String help, + String valueHelp, + List allowed, + Map allowedHelp, + defaultsTo, + void callback(value), + OptionType type, + {bool negatable: false, + bool splitCommas, + bool hide: false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { throw new ArgumentError('Duplicate option "$name".'); @@ -112,8 +133,8 @@ class ArgParser { } } - var option = newOption(name, abbr, help, valueHelp, allowed, - allowedHelp, defaultsTo, callback, type, + var option = newOption(name, abbr, help, valueHelp, allowed, allowedHelp, + defaultsTo, callback, type, negatable: negatable, splitCommas: splitCommas, hide: hide); _options[name] = option; _optionsAndSeparators.add(option); diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart index 20eddb9d..f4129d68 100644 --- a/pkgs/args/lib/src/arg_parser_exception.dart +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -10,8 +10,7 @@ class ArgParserException extends FormatException { final List commands; ArgParserException(String message, [Iterable commands]) - : commands = commands == null - ? const [] - : new List.unmodifiable(commands), + : commands = + commands == null ? const [] : new List.unmodifiable(commands), super(message); } diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 50e85fa7..20349da1 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -10,8 +10,12 @@ import 'arg_parser.dart'; /// /// Since [ArgResults] doesn't have a public constructor, this lets [Parser] /// get to it. This function isn't exported to the public API of the package. -ArgResults newArgResults(ArgParser parser, Map parsed, - String name, ArgResults command, List rest, +ArgResults newArgResults( + ArgParser parser, + Map parsed, + String name, + ArgResults command, + List rest, List arguments) { return new ArgResults._(parser, parsed, name, command, rest, arguments); } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index a37193f6..5eb0efb7 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -8,13 +8,22 @@ import 'dart:collection'; /// /// Since [Option] doesn't have a public constructor, this lets [ArgParser] /// get to it. This function isn't exported to the public API of the package. -Option newOption(String name, String abbreviation, String help, - String valueHelp, List allowed, Map allowedHelp, - defaultValue, Function callback, OptionType type, - {bool negatable, bool splitCommas, bool hide: false}) { +Option newOption( + String name, + String abbreviation, + String help, + String valueHelp, + List allowed, + Map allowedHelp, + defaultValue, + Function callback, + OptionType type, + {bool negatable, + bool splitCommas, + bool hide: false}) { return new Option._(name, abbreviation, help, valueHelp, allowed, allowedHelp, - defaultValue, callback, type, negatable: negatable, - splitCommas: splitCommas, hide: hide); + defaultValue, callback, type, + negatable: negatable, splitCommas: splitCommas, hide: hide); } /// A command-line option. Includes both flags and options which take a value. @@ -41,22 +50,28 @@ class Option { /// Whether the option allows multiple values. bool get isMultiple => type == OptionType.MULTIPLE; - Option._(this.name, this.abbreviation, this.help, this.valueHelp, - List allowed, Map allowedHelp, this.defaultValue, - this.callback, OptionType type, {this.negatable, bool splitCommas, + Option._( + this.name, + this.abbreviation, + this.help, + this.valueHelp, + List allowed, + Map allowedHelp, + this.defaultValue, + this.callback, + OptionType type, + {this.negatable, + bool splitCommas, this.hide: false}) - : this.allowed = allowed == null - ? null - : new UnmodifiableListView(allowed), - this.allowedHelp = allowedHelp == null - ? null - : new UnmodifiableMapView(allowedHelp), + : this.allowed = + allowed == null ? null : new UnmodifiableListView(allowed), + this.allowedHelp = + allowedHelp == null ? null : new UnmodifiableMapView(allowedHelp), this.type = type, // If the user doesn't specify [splitCommas], it defaults to true for // multiple options. - this.splitCommas = splitCommas == null - ? type == OptionType.MULTIPLE - : splitCommas { + this.splitCommas = + splitCommas == null ? type == OptionType.MULTIPLE : splitCommas { if (name.isEmpty) { throw new ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 64374fcb..6c2ee40e 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -70,8 +70,7 @@ class Parser { } on ArgParserException catch (error) { if (commandName == null) rethrow; throw new ArgParserException( - error.message, - [commandName]..addAll(error.commands)); + error.message, [commandName]..addAll(error.commands)); } // All remaining arguments were passed to command so clear them here. @@ -167,7 +166,8 @@ class Parser { } 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] == '', + validate( + abbrOpt[2] == '', 'Option "-$c" is a flag and cannot handle value ' '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".'); diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 4553b308..ff968adc 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -26,7 +26,8 @@ void main() { throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'f')); }); - test('throws ArgumentError if the abbreviation is longer ' + test( + 'throws ArgumentError if the abbreviation is longer ' 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); @@ -70,7 +71,8 @@ void main() { throwsIllegalArg(() => parser.addOption('flummox', abbr: 'f')); }); - test('throws ArgumentError if the abbreviation is longer ' + test( + 'throws ArgumentError if the abbreviation is longer ' 'than one character', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); @@ -107,7 +109,8 @@ void main() { } }); - test('throws ArgumentError if splitCommas is passed with allowMultiple: ' + test( + 'throws ArgumentError if splitCommas is passed with allowMultiple: ' 'false', () { var parser = new ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', splitCommas: true)); diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 9b0713a3..5a765b13 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -147,18 +147,22 @@ Run "test help " for more information about a command.""")); var command = new FooCommand(); runner.addCommand(command); - expect(runner.run(["foo"]).then((_) { - expect(command.hasRun, isTrue); - }), completes); + expect( + runner.run(["foo"]).then((_) { + expect(command.hasRun, isTrue); + }), + completes); }); test("runs an asynchronous command", () { var command = new AsyncCommand(); runner.addCommand(command); - expect(runner.run(["async"]).then((_) { - expect(command.hasRun, isTrue); - }), completes); + expect( + runner.run(["async"]).then((_) { + expect(command.hasRun, isTrue); + }), + completes); }); test("runs a command with a return value", () { @@ -181,27 +185,33 @@ Run "test help " for more information about a command.""")); var command = new HiddenCommand(); runner.addCommand(command); - expect(runner.run(["hidden"]).then((_) { - expect(command.hasRun, isTrue); - }), completes); + expect( + runner.run(["hidden"]).then((_) { + expect(command.hasRun, isTrue); + }), + completes); }); test("runs an aliased comand", () { var command = new AliasedCommand(); runner.addCommand(command); - expect(runner.run(["als"]).then((_) { - expect(command.hasRun, isTrue); - }), completes); + expect( + runner.run(["als"]).then((_) { + expect(command.hasRun, isTrue); + }), + completes); }); test("runs a subcommand", () { var command = new AsyncCommand(); runner.addCommand(new FooCommand()..addSubcommand(command)); - expect(runner.run(["foo", "async"]).then((_) { - expect(command.hasRun, isTrue); - }), completes); + expect( + runner.run(["foo", "async"]).then((_) { + expect(command.hasRun, isTrue); + }), + completes); }); group("with --help", () { @@ -279,18 +289,21 @@ Run "test help" to see global options. group("with an invalid argument", () { test("at the root throws the root usage", () { - expect(runner.run(["--asdf"]), throwsUsageException( - 'Could not find an option named "asdf".', - '$_DEFAULT_USAGE')); + expect( + runner.run(["--asdf"]), + throwsUsageException( + 'Could not find an option named "asdf".', '$_DEFAULT_USAGE')); }); test("for a command throws the command usage", () { var command = new FooCommand(); runner.addCommand(command); - expect(runner.run(["foo", "--asdf"]), throwsUsageException( - 'Could not find an option named "asdf".', - """ + expect( + runner.run(["foo", "--asdf"]), + throwsUsageException( + 'Could not find an option named "asdf".', + """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -313,28 +326,36 @@ Also, footer!""")); }); test("includes the footer in usage errors", () { - expect(runner.run(["--bad"]), throwsUsageException( - 'Could not find an option named "bad".', - "$_DEFAULT_USAGE\nAlso, footer!")); + expect( + runner.run(["--bad"]), + throwsUsageException('Could not find an option named "bad".', + "$_DEFAULT_USAGE\nAlso, footer!")); }); }); group("throws a useful error when", () { test("arg parsing fails", () { - expect(runner.run(["--bad"]), throwsUsageException( - 'Could not find an option named "bad".', _DEFAULT_USAGE)); + expect( + runner.run(["--bad"]), + throwsUsageException( + 'Could not find an option named "bad".', _DEFAULT_USAGE)); }); test("a top-level command doesn't exist", () { - expect(runner.run(["bad"]), throwsUsageException( - 'Could not find a command named "bad".', _DEFAULT_USAGE)); + expect( + runner.run(["bad"]), + throwsUsageException( + 'Could not find a command named "bad".', _DEFAULT_USAGE)); }); test("a subcommand doesn't exist", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo bad"]), throwsUsageException( - 'Could not find a command named "foo bad".', """ + expect( + runner.run(["foo bad"]), + throwsUsageException( + 'Could not find a command named "foo bad".', + """ Usage: test [arguments] Global options: @@ -350,8 +371,11 @@ Run "test help " for more information about a command.""")); test("a subcommand wasn't passed", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect(runner.run(["foo"]), throwsUsageException( - 'Missing subcommand for "test foo".', """ + expect( + runner.run(["foo"]), + throwsUsageException( + 'Missing subcommand for "test foo".', + """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -364,8 +388,11 @@ Run "test help" to see global options.""")); test("a command that doesn't take arguments was given them", () { runner.addCommand(new FooCommand()); - expect(runner.run(["foo", "bar"]), throwsUsageException( - 'Command "foo" does not take any arguments.', """ + expect( + runner.run(["foo", "bar"]), + throwsUsageException( + 'Command "foo" does not take any arguments.', + """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index cd32c63d..c0c08d34 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -90,8 +90,11 @@ Run "test help" to see global options.""")); }); test("usageException splits up the message and usage", () { - expect(() => foo.usageException("message"), - throwsUsageException("message", """ + expect( + () => foo.usageException("message"), + throwsUsageException( + "message", + """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 6ce8a199..b1638629 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -152,7 +152,8 @@ void main() { expect(a, isNull); }); - test('for multiple present, allowMultiple, options are invoked with ' + test( + 'for multiple present, allowMultiple, options are invoked with ' 'value as a list', () { var a; var parser = new ArgParser(); @@ -167,7 +168,8 @@ void main() { expect(a, new isInstanceOf>()); }); - test('for single present, allowMultiple, options are invoked with ' + test( + 'for single present, allowMultiple, options are invoked with ' ' value as a single element list', () { var a; var parser = new ArgParser(); @@ -178,7 +180,8 @@ void main() { expect(a, equals(['v'])); }); - test('for absent, allowMultiple, options are invoked with default ' + test( + 'for absent, allowMultiple, options are invoked with default ' 'value as a list.', () { var a; var parser = new ArgParser(); @@ -191,7 +194,8 @@ void main() { expect(a, equals(['v'])); }); - test('for absent, allowMultiple, options are invoked with value ' + test( + 'for absent, allowMultiple, options are invoked with value ' 'as an empty list.', () { var a; var parser = new ArgParser(); @@ -212,7 +216,8 @@ void main() { expect(a, equals(['v', 'w', 'x'])); }); - test("allowMultiple doesn't parses comma-separated strings with " + test( + "allowMultiple doesn't parses comma-separated strings with " "splitCommas: false", () { var a; var parser = new ArgParser(); @@ -337,8 +342,8 @@ void main() { test('throw if a comma-separated value is not allowed', () { var parser = new ArgParser(); - parser.addOption('mode', abbr: 'm', allowMultiple: true, - allowed: ['debug', 'release']); + parser.addOption('mode', + abbr: 'm', allowMultiple: true, allowed: ['debug', 'release']); throwsFormat(parser, ['-mdebug,profile']); }); @@ -418,8 +423,8 @@ void main() { parser.addOption('other'); expect(parser.parse(['--mode', '--other'])['mode'], equals('--other')); - expect(parser.parse(['--mode', '--unknown'])['mode'], - equals('--unknown')); + expect( + parser.parse(['--mode', '--unknown'])['mode'], equals('--unknown')); expect(parser.parse(['--mode', '-abbr'])['mode'], equals('-abbr')); expect(parser.parse(['--mode', '--'])['mode'], equals('--')); }); @@ -453,7 +458,8 @@ void main() { expect(args['define'], equals(['1', '2'])); }); - test('returns the default value for multi-valued arguments ' + test( + 'returns the default value for multi-valued arguments ' 'if not explicitly set', () { var parser = new ArgParser(); parser.addOption('define', defaultsTo: '0', allowMultiple: true); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 31c19d26..6c591bf0 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -11,7 +11,9 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]mode The mode '''); }); @@ -20,7 +22,9 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', negatable: false, help: 'The mode'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --mode The mode '''); }); @@ -29,7 +33,9 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]mode The mode '''); }); @@ -39,7 +45,9 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'The mode'); parser.addOption('long', help: 'Lacks an abbreviation'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' -m, --[no-]mode The mode --long Lacks an abbreviation '''); @@ -50,7 +58,9 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); parser.addOption('a-really-long-name', help: 'Its help text'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' -m, --[no-]mode Lined up with below --a-really-long-name Its help text '''); @@ -60,7 +70,9 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]mode After newlines '''); }); @@ -69,7 +81,9 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]mode Before newlines '''); }); @@ -80,7 +94,9 @@ void main() { parser.addFlag('monkey', help: 'Second'); parser.addFlag('wombat', help: 'Third'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]zebra First --[no-]monkey Second --[no-]wombat Third @@ -92,7 +108,9 @@ void main() { parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); parser.addFlag('negate', help: 'Should be off', defaultsTo: false); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]affirm Should be on (defaults to on) @@ -104,7 +122,9 @@ void main() { var parser = new ArgParser(); parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --any Can be anything (defaults to "whatevs") '''); @@ -115,7 +135,9 @@ void main() { parser.addOption('out', abbr: 'o', help: 'Where to write file', valueHelp: 'path'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' -o, --out= Where to write file '''); }); @@ -126,7 +148,9 @@ void main() { help: 'Like in cards', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --suit Like in cards [spades, clubs, hearts, diamonds] '''); @@ -139,7 +163,9 @@ void main() { defaultsTo: 'clubs', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --suit Like in cards [spades, clubs (default), hearts, diamonds] '''); @@ -150,15 +176,22 @@ void main() { parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', - allowed: ['spades', 'clubs', 'diamonds', 'hearts'], + allowed: [ + 'spades', + 'clubs', + 'diamonds', + 'hearts' + ], allowedHelp: { - 'spades': 'Swords of a soldier', - 'clubs': 'Weapons of war', - 'diamonds': 'Money for this art', - 'hearts': 'The shape of my heart' - }); - - validateUsage(parser, ''' + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage( + parser, + ''' --suit Like in cards [clubs] Weapons of war @@ -174,7 +207,9 @@ void main() { parser.addOption('second', hide: true); parser.addOption('third', help: 'The third option'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --first The first option --third The third option '''); @@ -186,7 +221,9 @@ void main() { parser.addFlag('second', hide: true); parser.addFlag('third', help: 'The third flag'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]first The first flag --[no-]third The third flag '''); @@ -198,7 +235,9 @@ void main() { parser.addFlag('second-very-long-option', hide: true); parser.addFlag('third', help: 'The third flag'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]first The first flag --[no-]third The third flag '''); @@ -213,7 +252,9 @@ void main() { parser.addSeparator('Marsupial:'); parser.addFlag('wombat', help: 'Third'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]zebra First Primate: @@ -230,7 +271,9 @@ void main() { parser.addSeparator('Primate:'); parser.addFlag('monkey', help: 'Second'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]zebra Multi line @@ -244,7 +287,9 @@ void main() { parser.addSeparator('Equine:'); parser.addFlag('zebra', help: 'First'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' Equine: --[no-]zebra First '''); @@ -255,7 +300,9 @@ void main() { parser.addFlag('zebra', help: 'First'); parser.addSeparator('Primate:'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' --[no-]zebra First Primate: @@ -267,7 +314,9 @@ void main() { parser.addSeparator('First'); parser.addSeparator('Second'); - validateUsage(parser, ''' + validateUsage( + parser, + ''' First Second From f43d9676828f83c029889faa364828c6eeaf0c78 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 7 Apr 2017 08:16:11 -0700 Subject: [PATCH 125/263] Use the recommended name for analysis_options And add/fix a number of standard lints --- pkgs/args/.analysis_options | 2 - pkgs/args/analysis_options.yaml | 29 +++++++++++++ pkgs/args/lib/command_runner.dart | 8 ++-- pkgs/args/lib/src/arg_parser_exception.dart | 2 +- pkgs/args/lib/src/arg_results.dart | 2 +- pkgs/args/lib/src/help_command.dart | 6 +++ pkgs/args/lib/src/option.dart | 4 +- pkgs/args/lib/src/parser.dart | 14 +++--- pkgs/args/lib/src/usage_exception.dart | 1 + pkgs/args/test/utils.dart | 48 +++++++++++++++++++++ 10 files changed, 99 insertions(+), 17 deletions(-) delete mode 100644 pkgs/args/.analysis_options create mode 100644 pkgs/args/analysis_options.yaml diff --git a/pkgs/args/.analysis_options b/pkgs/args/.analysis_options deleted file mode 100644 index a10d4c5a..00000000 --- a/pkgs/args/.analysis_options +++ /dev/null @@ -1,2 +0,0 @@ -analyzer: - strong-mode: true diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml new file mode 100644 index 00000000..092773be --- /dev/null +++ b/pkgs/args/analysis_options.yaml @@ -0,0 +1,29 @@ +analyzer: + strong-mode: true +linter: + rules: + # Errors + - avoid_empty_else + - comment_references + - control_flow_in_finally + - empty_statements + - hash_and_equals + - test_types_in_equals + - throw_in_finally + - unrelated_type_equality_checks + - valid_regexps + + # Style + - annotate_overrides + - avoid_init_to_null + - avoid_return_types_on_setters + - await_only_futures + - camel_case_types + - empty_catches + - empty_constructor_bodies + - library_names + - library_prefixes + - non_constant_identifier_names + - prefer_is_not_empty + - slash_for_doc_comments + - type_init_formals diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index bbf08ae3..2fe15177 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -15,7 +15,7 @@ import 'src/utils.dart'; export 'src/usage_exception.dart'; -/// A class for invoking [Commands] based on raw command-line arguments. +/// A class for invoking [Command]s based on raw command-line arguments. /// /// The type argument `T` represents the type returned by [Command.run] and /// [CommandRunner.run]; it can be ommitted if you're not using the return @@ -31,7 +31,7 @@ class CommandRunner { /// A single-line template for how to invoke this executable. /// - /// Defaults to "$executableName [arguments]". Subclasses can + /// Defaults to "$executableName `arguments`". Subclasses can /// override this for a more specific template. String get invocation => "$executableName [arguments]"; @@ -217,7 +217,7 @@ abstract class Command { String get summary => description.split("\n").first; /// A single-line template for how to invoke this command (e.g. `"pub get - /// [package]"`). + /// `package`"`). String get invocation { var parents = [name]; for (var command = parent; command != null; command = command.parent) { @@ -233,7 +233,7 @@ abstract class Command { /// The command's parent command, if this is a subcommand. /// - /// This will be `null` until [Command.addSubcommmand] has been called with + /// This will be `null` until [addSubcommand] has been called with /// this command. Command get parent => _parent; Command _parent; diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart index f4129d68..5ce0eabf 100644 --- a/pkgs/args/lib/src/arg_parser_exception.dart +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -2,7 +2,7 @@ // 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. -/// An exception thrown by [ArgParser]. +/// An exception thrown by `ArgParser`. class ArgParserException extends FormatException { /// The command(s) that were parsed before discovering the error. /// diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 20349da1..744686dc 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -8,7 +8,7 @@ import 'arg_parser.dart'; /// Creates a new [ArgResults]. /// -/// Since [ArgResults] doesn't have a public constructor, this lets [Parser] +/// Since [ArgResults] doesn't have a public constructor, this lets [ArgParser] /// get to it. This function isn't exported to the public API of the package. ArgResults newArgResults( ArgParser parser, diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index 104327c9..b119431a 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -8,11 +8,17 @@ import '../command_runner.dart'; /// /// This command displays help information for the various subcommands. class HelpCommand extends Command { + @override final name = "help"; + + @override String get description => "Display help information for ${runner.executableName}."; + + @override String get invocation => "${runner.executableName} help [command]"; + @override T run() { // Show the default help if no command was specified. if (argResults.rest.isEmpty) { diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 5eb0efb7..3ff7fe85 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -6,7 +6,7 @@ import 'dart:collection'; /// Creates a new [Option]. /// -/// Since [Option] doesn't have a public constructor, this lets [ArgParser] +/// Since [Option] doesn't have a public constructor, this lets `ArgParser` /// get to it. This function isn't exported to the public API of the package. Option newOption( String name, @@ -137,7 +137,7 @@ class OptionType { /// /// --output text --output xml /// - /// In the parsed [ArgResults], a multiple-valued option will always return + /// In the parsed `ArgResults`, a multiple-valued option will always return /// a list, even if one or no values were passed. static const MULTIPLE = const OptionType._("OptionType.MULTIPLE"); diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 6c2ee40e..82b74cef 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -7,9 +7,9 @@ import 'arg_parser_exception.dart'; import 'arg_results.dart'; import 'option.dart'; -final _SOLO_OPT = new RegExp(r'^-([a-zA-Z0-9])$'); -final _ABBR_OPT = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); -final _LONG_OPT = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); +final _soloOpt = new RegExp(r'^-([a-zA-Z0-9])$'); +final _abbrOpt = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); +final _longOpt = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); /// The actual argument parsing class. /// @@ -47,7 +47,7 @@ class Parser { /// Parses the arguments. This can only be called once. ArgResults parse() { var arguments = args.toList(); - var commandResults = null; + ArgResults commandResults; // Parse the args. while (args.length > 0) { @@ -120,7 +120,7 @@ class Parser { /// We treat this differently than collapsed abbreviations (like "-abc") to /// handle the possible value that may follow it. bool parseSoloOption() { - var soloOpt = _SOLO_OPT.firstMatch(current); + var soloOpt = _soloOpt.firstMatch(current); if (soloOpt == null) return false; var option = grammar.findByAbbreviation(soloOpt[1]); @@ -146,7 +146,7 @@ class Parser { /// (like "-abc") or a single abbreviation with the value directly attached /// to it (like "-mrelease"). bool parseAbbreviation(Parser innermostCommand) { - var abbrOpt = _ABBR_OPT.firstMatch(current); + var abbrOpt = _abbrOpt.firstMatch(current); if (abbrOpt == null) return false; // If the first character is the abbreviation for a non-flag option, then @@ -206,7 +206,7 @@ class Parser { /// 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() { - var longOpt = _LONG_OPT.firstMatch(current); + var longOpt = _longOpt.firstMatch(current); if (longOpt == null) return false; var name = longOpt[1]; diff --git a/pkgs/args/lib/src/usage_exception.dart b/pkgs/args/lib/src/usage_exception.dart index 2d8e1125..7d783dbc 100644 --- a/pkgs/args/lib/src/usage_exception.dart +++ b/pkgs/args/lib/src/usage_exception.dart @@ -8,5 +8,6 @@ class UsageException implements Exception { UsageException(this.message, this.usage); + @override String toString() => "$message\n\n$usage"; } diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/utils.dart index a874eeec..d9e4f700 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/utils.dart @@ -9,6 +9,7 @@ import 'package:args/command_runner.dart'; import 'package:test/test.dart'; class CommandRunnerWithFooter extends CommandRunner { + @override String get usageFooter => "Also, footer!"; CommandRunnerWithFooter(String executableName, String description) @@ -18,55 +19,88 @@ class CommandRunnerWithFooter extends CommandRunner { class FooCommand extends Command { var hasRun = false; + @override final name = "foo"; + + @override final description = "Set a value."; + + @override final takesArguments = false; + @override void run() { hasRun = true; } } class ValueCommand extends Command { + @override final name = "foo"; + + @override final description = "Return a value."; + + @override final takesArguments = false; + @override int run() => 12; } class AsyncValueCommand extends Command { + @override final name = "foo"; + + @override final description = "Return a future."; + + @override final takesArguments = false; + @override Future run() async => "hi"; } class MultilineCommand extends Command { var hasRun = false; + @override final name = "multiline"; + + @override final description = "Multi\nline."; + + @override final takesArguments = false; + @override void run() { hasRun = true; } } class MultilineSummaryCommand extends MultilineCommand { + @override String get summary => description; } class HiddenCommand extends Command { var hasRun = false; + @override final name = "hidden"; + + @override final description = "Set a value."; + + @override final hidden = true; + + @override final takesArguments = false; + @override void run() { hasRun = true; } @@ -75,11 +109,19 @@ class HiddenCommand extends Command { class AliasedCommand extends Command { var hasRun = false; + @override final name = "aliased"; + + @override final description = "Set a value."; + + @override final takesArguments = false; + + @override final aliases = const ["alias", "als"]; + @override void run() { hasRun = true; } @@ -88,10 +130,16 @@ class AliasedCommand extends Command { class AsyncCommand extends Command { var hasRun = false; + @override final name = "async"; + + @override final description = "Set a value asynchronously."; + + @override final takesArguments = false; + @override Future run() => new Future.value().then((_) => hasRun = true); } From 891ad3c81103500b95b9a6f3eb8a0fd7bbfbab96 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Thu, 8 Jun 2017 15:20:10 -0700 Subject: [PATCH 126/263] Document that Command.argParser can be overridden. (dart-lang/args#72) See dart-lang/args#68 --- pkgs/args/lib/command_runner.dart | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 2fe15177..13b0a6ee 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -267,6 +267,9 @@ abstract class Command { /// the constructor); they'll end up available via [argResults]. Subcommands /// should be registered with [addSubcommand] rather than directly on the /// parser. + /// + /// This can be overridden to change the arguments passed to the `ArgParser` + /// constructor. ArgParser get argParser => _argParser; final _argParser = new ArgParser(); From 6907fde0bf190ee91049a0e8a95a45f1f000d18a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Jun 2017 13:49:43 -0700 Subject: [PATCH 127/263] Add ArgParser.allowAnything(). (dart-lang/args#71) Closes dart-lang/args#70 --- pkgs/args/CHANGELOG.md | 5 ++ pkgs/args/lib/src/allow_anything_parser.dart | 65 ++++++++++++++++++++ pkgs/args/lib/src/arg_parser.dart | 16 ++++- pkgs/args/lib/src/parser.dart | 5 ++ pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/allow_anything_test.dart | 58 +++++++++++++++++ 6 files changed, 148 insertions(+), 3 deletions(-) create mode 100644 pkgs/args/lib/src/allow_anything_parser.dart create mode 100644 pkgs/args/test/allow_anything_test.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 5f5c8c6c..2a0f6140 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 0.13.8 + +* Add `new ArgParser.allowAnything()`. This allows any input, without parsing + any options. + ## 0.13.7 * Add explicit support for forwarding the value returned by `Command.run()` to diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart new file mode 100644 index 00000000..450920c5 --- /dev/null +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -0,0 +1,65 @@ +// Copyright (c) 2017, 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. + +import 'arg_parser.dart'; +import 'arg_results.dart'; +import 'option.dart'; +import 'parser.dart'; + +/// An ArgParser that treats *all input* as non-option arguments. +class AllowAnythingParser implements ArgParser { + Map get options => const {}; + Map get commands => const {}; + bool get allowTrailingOptions => false; + bool get allowsAnything => true; + + ArgParser addCommand(String name, [ArgParser parser]) { + throw new UnsupportedError( + "ArgParser.allowAnything().addCommands() isn't supported."); + } + + void addFlag(String name, + {String abbr, + String help, + bool defaultsTo: false, + bool negatable: true, + void callback(bool value), + bool hide: false}) { + throw new UnsupportedError( + "ArgParser.allowAnything().addFlag() isn't supported."); + } + + void addOption(String name, + {String abbr, + String help, + String valueHelp, + List allowed, + Map allowedHelp, + String defaultsTo, + void callback(value), + bool allowMultiple: false, + bool splitCommas, + bool hide: false}) { + throw new UnsupportedError( + "ArgParser.allowAnything().addOption() isn't supported."); + } + + void addSeparator(String text) { + throw new UnsupportedError( + "ArgParser.allowAnything().addSeparator() isn't supported."); + } + + ArgResults parse(List args) => + new Parser(null, this, args.toList()).parse(); + + String getUsage() => usage; + + String get usage => ""; + + getDefault(String option) { + throw new ArgumentError('No option named $option'); + } + + Option findByAbbreviation(String abbr) => null; +} diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 44ceb64e..69de95ad 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -4,6 +4,7 @@ import 'dart:collection'; +import 'allow_anything_parser.dart'; import 'arg_results.dart'; import 'option.dart'; import 'parser.dart'; @@ -29,6 +30,10 @@ class ArgParser { /// arguments. final bool allowTrailingOptions; + /// Whether or not this parser treats unrecognized options as non-option + /// arguments. + bool get allowsAnything => false; + /// Creates a new ArgParser. /// /// If [allowTrailingOptions] is set, the parser will continue parsing even @@ -36,8 +41,15 @@ class ArgParser { /// This allows options to be specified after regular arguments. Defaults to /// `false`. factory ArgParser({bool allowTrailingOptions: false}) => - new ArgParser._({}, {}, - allowTrailingOptions: allowTrailingOptions); + new ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions); + + /// Creates a new ArgParser that treats *all input* as non-option arguments. + /// + /// This is intended to allow arguments to be passed through to child + /// processes without needing to be redefined in the parent. + /// + /// Options may not be defined for this parser. + factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, {bool allowTrailingOptions: false}) diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 82b74cef..c1bcc842 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -47,6 +47,11 @@ class Parser { /// Parses the arguments. This can only be called once. ArgResults parse() { var arguments = args.toList(); + if (grammar.allowsAnything) { + return newArgResults( + grammar, const {}, commandName, null, arguments, arguments); + } + ArgResults commandResults; // Parse the args. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index e1a6b05e..10531e49 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.7 +version: 0.13.8-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart new file mode 100644 index 00000000..7e229af1 --- /dev/null +++ b/pkgs/args/test/allow_anything_test.dart @@ -0,0 +1,58 @@ +// Copyright (c) 2017, 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. + +import 'package:test/test.dart'; + +import 'package:args/args.dart'; + +import 'utils.dart'; + +void main() { + group('new ArgParser.allowAnything()', () { + ArgParser parser; + setUp(() { + parser = new ArgParser.allowAnything(); + }); + + test('exposes empty values', () { + expect(parser.options, isEmpty); + expect(parser.commands, isEmpty); + expect(parser.allowTrailingOptions, isFalse); + expect(parser.allowsAnything, isTrue); + expect(parser.usage, isEmpty); + expect(parser.getUsage(), isEmpty); + expect(parser.findByAbbreviation("a"), isNull); + }); + + test('mutation methods throw errors', () { + expect(() => parser.addCommand("command"), throwsUnsupportedError); + expect(() => parser.addFlag("flag"), throwsUnsupportedError); + expect(() => parser.addOption("option"), throwsUnsupportedError); + expect(() => parser.addSeparator("==="), throwsUnsupportedError); + }); + + test('getDefault() throws an error', () { + expect(() => parser.getDefault('option'), throwsArgumentError); + }); + + test('parses all values as rest arguments', () { + var results = parser.parse(['--foo', '-abc', '--', 'bar']); + expect(results.options, isEmpty); + expect(results.rest, equals(['--foo', '-abc', '--', 'bar'])); + expect(results.arguments, equals(['--foo', '-abc', '--', 'bar'])); + expect(results.command, isNull); + expect(results.name, isNull); + }); + + test('works as a subcommand', () { + var commandParser = new ArgParser()..addCommand('command', parser); + var results = + commandParser.parse(['command', '--foo', '-abc', '--', 'bar']); + expect(results.command.options, isEmpty); + expect(results.command.rest, equals(['--foo', '-abc', '--', 'bar'])); + expect(results.command.arguments, equals(['--foo', '-abc', '--', 'bar'])); + expect(results.command.name, equals('command')); + }); + }); +} From d6f6061d3eaf2da4ac6886fdc75b55c0098ea162 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Wed, 14 Jun 2017 15:25:45 -0700 Subject: [PATCH 128/263] Make allowTrailingOptions default to `false`. (dart-lang/args#66) Make allowTrailingOptions default to `false`. Also release 1.0.0. --- pkgs/args/.travis.yml | 4 ---- pkgs/args/CHANGELOG.md | 5 ++++- pkgs/args/README.md | 11 ++++++----- pkgs/args/lib/src/arg_parser.dart | 15 ++++++++------- pkgs/args/pubspec.yaml | 6 +++++- pkgs/args/test/parse_test.dart | 6 ++++-- pkgs/args/test/trailing_options_test.dart | 4 ++-- 7 files changed, 29 insertions(+), 22 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index abf77dd8..992ce871 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -3,10 +3,6 @@ sudo: false dart: - stable - dev - - 1.22.1 - - 1.21.1 - - 1.20.1 - - 1.19.1 dart_task: - test: -p vm - dartfmt diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 2a0f6140..d893d5cb 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,7 @@ -## 0.13.8 +## 1.0.0 + +* **Breaking change**: The `allowTrailingOptions` argument to `new + ArgumentParser()` defaults to `true` instead of `false`. * Add `new ArgParser.allowAnything()`. This allows any input, without parsing any options. diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 36e931c5..a23fe14c 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -106,16 +106,17 @@ print(results['mode']); // debug print(results['verbose']); // true ``` -By default, the `parse()` method stops as soon as it reaches `--` by itself or -anything that the parser doesn't recognize as an option, flag, or option value. -If arguments still remain, they go into [ArgResults.rest][rest]. +By default, the `parse()` method allows additional flags and options to be +passed after positional parameters unless `--` is used to indicate that all +further parameters will be positional. The positional arguments go into +[ArgResults.rest][rest]. ```dart print(results.rest); // ['something', 'else'] ``` -To continue to parse options found after non-option arguments, pass -`allowTrailingOptions: true` when creating the [ArgParser][]. +To stop parsing options as soon as a positional argument is found, +`allowTrailingOptions: false` when creating the [ArgParser][]. ## Specifying options diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 69de95ad..fd6cb9a6 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -36,12 +36,13 @@ class ArgParser { /// Creates a new ArgParser. /// - /// If [allowTrailingOptions] is set, the parser will continue parsing even - /// after it finds an argument that is neither an option nor a command. - /// This allows options to be specified after regular arguments. Defaults to - /// `false`. - factory ArgParser({bool allowTrailingOptions: false}) => - new ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions); + /// If [allowTrailingOptions] is `true` (the default), the parser will parse + /// flags and options that appear after positional arguments. If it's `false`, + /// the parser stops parsing as soon as it finds an argument that is neither + /// an option nor a command. + factory ArgParser({bool allowTrailingOptions: true}) => + new ArgParser._({}, {}, + allowTrailingOptions: allowTrailingOptions); /// Creates a new ArgParser that treats *all input* as non-option arguments. /// @@ -52,7 +53,7 @@ class ArgParser { factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, - {bool allowTrailingOptions: false}) + {bool allowTrailingOptions: true}) : this._options = options, this.options = new UnmodifiableMapView(options), this._commands = commands, diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 10531e49..78b714d9 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 0.13.8-dev +version: 1.0.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -10,3 +10,7 @@ dev_dependencies: test: ">=0.12.0 <0.13.0" environment: sdk: ">=1.4.0 <2.0.0" + +# Test currently depends on args 0.13.x. +dependency_overrides: + test: ">=0.12.0 <0.13.0" diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index b1638629..87f3d81e 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -502,8 +502,10 @@ void main() { expect(results.rest, equals(['--meow'])); }); - test('leaves "--" if not the first non-option', () { - var parser = new ArgParser(); + test( + 'with allowTrailingOptions: false, leaves "--" if not the first ' + 'non-option', () { + var parser = new ArgParser(allowTrailingOptions: false); parser.addFlag('woof'); var results = parser.parse(['--woof', 'stop', '--', 'arg']); diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index ce22ad4f..6a69818b 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -6,9 +6,9 @@ import 'package:test/test.dart'; import 'package:args/args.dart'; void main() { - test('allowTrailingOptions defaults to false', () { + test('allowTrailingOptions defaults to true', () { var parser = new ArgParser(); - expect(parser.allowTrailingOptions, isFalse); + expect(parser.allowTrailingOptions, isTrue); }); group('when trailing options are allowed', () { From cc02ee6fc124634bfc53f0eee8e081a5db479013 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 18 Jul 2017 07:54:22 -0700 Subject: [PATCH 129/263] Update .travis.yml: allows failures in dartfmt dev (dart-lang/args#75) --- pkgs/args/.travis.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index 992ce871..ac48a956 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -5,8 +5,17 @@ dart: - dev dart_task: - test: -p vm - - dartfmt - dartanalyzer + +matrix: + include: + - dart: stable + dart_task: dartfmt + +# Only building master means that we don't run two builds for each pull request. +branches: + only: [master] + cache: directories: - $HOME/.pub-cache From 81b9bd8e3b7bd8788e97b824aeed2e81ece8e19e Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Thu, 21 Sep 2017 13:09:28 -0700 Subject: [PATCH 130/263] Remove pkg/test override --- pkgs/args/pubspec.yaml | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 78b714d9..f5ead67d 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.0.0 +version: 1.0.1-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -10,7 +10,3 @@ dev_dependencies: test: ">=0.12.0 <0.13.0" environment: sdk: ">=1.4.0 <2.0.0" - -# Test currently depends on args 0.13.x. -dependency_overrides: - test: ">=0.12.0 <0.13.0" From dc68fad83d82059b38173aa4b6cf2b45b27c66e0 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 3 Oct 2017 15:30:34 -0700 Subject: [PATCH 131/263] Fix a fuzzy arrow warning (dart-lang/args#79) Closes dart-lang/args#78 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/src/arg_parser.dart | 13 +++++++++++-- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index d893d5cb..13b020db 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.1 + +* Fix a fuzzy arrow type warning. + ## 1.0.0 * **Breaking change**: The `allowTrailingOptions` argument to `new diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index fd6cb9a6..62c8276e 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -88,9 +88,18 @@ class ArgParser { bool negatable: true, void callback(bool value), bool hide: false}) { - _addOption(name, abbr, help, null, null, null, defaultsTo, callback, + _addOption( + name, + abbr, + help, + null, + null, + null, + defaultsTo, + callback == null ? null : (value) => callback(value as bool), OptionType.FLAG, - negatable: negatable, hide: hide); + negatable: negatable, + hide: hide); } /// Defines a value-taking option. Throws an [ArgumentError] if: diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index f5ead67d..00fc576e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.0.1-dev +version: 1.0.1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 8602d0a84949d67ba5224fb70abc0bd27f67db09 Mon Sep 17 00:00:00 2001 From: Leaf Petersen Date: Thu, 19 Oct 2017 15:00:24 -0700 Subject: [PATCH 132/263] Fix unused import warning in tests. (dart-lang/args#80) --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/allow_anything_test.dart | 2 -- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 13b020db..c6526302 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.0.2 + +* Fix analyzer warning + ## 1.0.1 * Fix a fuzzy arrow type warning. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 00fc576e..46924280 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.0.1 +version: 1.0.2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index 7e229af1..ac185f83 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -6,8 +6,6 @@ import 'package:test/test.dart'; import 'package:args/args.dart'; -import 'utils.dart'; - void main() { group('new ArgParser.allowAnything()', () { ArgParser parser; From 0142dd2daaa24ae64c94e6ac5ecf2bfe149442c7 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 27 Nov 2017 15:34:39 -0800 Subject: [PATCH 133/263] Take Iterables rather than Lists where possible (dart-lang/args#83) Take Iterables rather than Lists where possible Closes dart-lang/args#82 --- pkgs/args/CHANGELOG.md | 8 ++++++++ pkgs/args/lib/src/allow_anything_parser.dart | 4 ++-- pkgs/args/lib/src/arg_parser.dart | 6 +++--- pkgs/args/lib/src/option.dart | 11 ++++------- pkgs/args/pubspec.yaml | 2 +- 5 files changed, 18 insertions(+), 13 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index c6526302..1f38fad2 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,11 @@ +## 1.1.0 + +* `ArgParser.parse()` now takes an `Iterable` rather than a + `List`. + +* `ArgParser.addOption()`'s `allowed` option now takes an `Iterable` + rather than a `List`. + ## 1.0.2 * Fix analyzer warning diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 450920c5..38daea53 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -34,7 +34,7 @@ class AllowAnythingParser implements ArgParser { {String abbr, String help, String valueHelp, - List allowed, + Iterable allowed, Map allowedHelp, String defaultsTo, void callback(value), @@ -50,7 +50,7 @@ class AllowAnythingParser implements ArgParser { "ArgParser.allowAnything().addSeparator() isn't supported."); } - ArgResults parse(List args) => + ArgResults parse(Iterable args) => new Parser(null, this, args.toList()).parse(); String getUsage() => usage; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 62c8276e..e91276d6 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -111,7 +111,7 @@ class ArgParser { {String abbr, String help, String valueHelp, - List allowed, + Iterable allowed, Map allowedHelp, String defaultsTo, void callback(value), @@ -133,7 +133,7 @@ class ArgParser { String abbr, String help, String valueHelp, - List allowed, + Iterable allowed, Map allowedHelp, defaultsTo, void callback(value), @@ -172,7 +172,7 @@ class ArgParser { /// 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 args) => + ArgResults parse(Iterable args) => new Parser(null, this, args.toList()).parse(); /// Generates a string displaying usage information for the defined options. diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 3ff7fe85..55cc5668 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -2,8 +2,6 @@ // 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. -import 'dart:collection'; - /// Creates a new [Option]. /// /// Since [Option] doesn't have a public constructor, this lets `ArgParser` @@ -13,7 +11,7 @@ Option newOption( String abbreviation, String help, String valueHelp, - List allowed, + Iterable allowed, Map allowedHelp, defaultValue, Function callback, @@ -55,7 +53,7 @@ class Option { this.abbreviation, this.help, this.valueHelp, - List allowed, + Iterable allowed, Map allowedHelp, this.defaultValue, this.callback, @@ -63,10 +61,9 @@ class Option { {this.negatable, bool splitCommas, this.hide: false}) - : this.allowed = - allowed == null ? null : new UnmodifiableListView(allowed), + : this.allowed = allowed == null ? null : new List.unmodifiable(allowed), this.allowedHelp = - allowedHelp == null ? null : new UnmodifiableMapView(allowedHelp), + allowedHelp == null ? null : new Map.unmodifiable(allowedHelp), this.type = type, // If the user doesn't specify [splitCommas], it defaults to true for // multiple options. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 46924280..4e3311c7 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.0.2 +version: 1.1.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From ed835576bd32f64af17d230ca3dbef3017dd3c1a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 5 Dec 2017 16:50:26 -0800 Subject: [PATCH 134/263] Type the callback parameter to addOption as Function (dart-lang/args#84) See flutter/flutterdart-lang/args#13223 --- pkgs/args/CHANGELOG.md | 7 +++++++ pkgs/args/lib/src/allow_anything_parser.dart | 2 +- pkgs/args/lib/src/arg_parser.dart | 4 ++-- pkgs/args/pubspec.yaml | 2 +- 4 files changed, 11 insertions(+), 4 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 1f38fad2..c026ae6e 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,10 @@ +## 1.2.0 + +* Type the `callback` parameter to `ArgParser.addOption()` as `Function` rather + than `void Function(value)`. This allows strong-mode users to write `callback: + (String value) { ... }` rather than having to manually cast `value` to a + `String` (or a `List` with `allowMultiple: true`). + ## 1.1.0 * `ArgParser.parse()` now takes an `Iterable` rather than a diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 38daea53..58e48127 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -37,7 +37,7 @@ class AllowAnythingParser implements ArgParser { Iterable allowed, Map allowedHelp, String defaultsTo, - void callback(value), + Function callback, bool allowMultiple: false, bool splitCommas, bool hide: false}) { diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index e91276d6..98312ec4 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -114,7 +114,7 @@ class ArgParser { Iterable allowed, Map allowedHelp, String defaultsTo, - void callback(value), + Function callback, bool allowMultiple: false, bool splitCommas, bool hide: false}) { @@ -136,7 +136,7 @@ class ArgParser { Iterable allowed, Map allowedHelp, defaultsTo, - void callback(value), + Function callback, OptionType type, {bool negatable: false, bool splitCommas, diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4e3311c7..42a4718e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.1.0 +version: 1.2.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From de4290095d2515fd62076c048ba26723f505f214 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 26 Jan 2018 14:05:07 -0800 Subject: [PATCH 135/263] Type Command.run()'s return value as FutureOr (dart-lang/args#87) Closes dart-lang/args#85 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/command_runner.dart | 7 +++---- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index c026ae6e..8771808b 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.3.0 + +* Type `Command.run()`'s return value as `FutureOr`. + ## 1.2.0 * Type the `callback` parameter to `ArgParser.addOption()` as `Function` rather diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 13b0a6ee..3135dbf5 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -352,10 +352,9 @@ abstract class Command { /// Runs this command. /// - /// This must return a `T`, a `Future`, or `null`. The value is returned by - /// [CommandRunner.runCommand]. Subclasses must explicitly declare a return - /// type for `run()`, and may not use `void` if `T` is defined. - run() { + /// The return value is wrapped in a `Future` if necessary and returned by + /// [CommandRunner.runCommand]. + FutureOr run() { throw new UnimplementedError("Leaf command $this must implement run()."); } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 42a4718e..2c5fa596 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.2.0 +version: 1.3.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From d53daf04328222cb95946894f4bf1d858b744ee1 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Feb 2018 13:54:04 -0800 Subject: [PATCH 136/263] Improve the documentation option values Closes dart-lang/args#73 --- pkgs/args/lib/src/arg_parser.dart | 69 ++++++++++++++++++++++++++++++- pkgs/args/lib/src/option.dart | 44 +++++++++++++++++--- 2 files changed, 106 insertions(+), 7 deletions(-) diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 98312ec4..ea8ede6a 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -77,7 +77,31 @@ class ArgParser { return parser; } - /// Defines a flag. Throws an [ArgumentError] if: + /// Defines a boolean flag. + /// + /// This adds an [Option] with the given properties to [options]. + /// + /// The [abbr] argument is a single-character string that can be used as a + /// shorthand for this flag. For example, `abbr: "a"` will allow the user to + /// pass `-a` to enable the flag. + /// + /// The [help] argument is used by [usage] to describe this flag. + /// + /// The [defaultsTo] argument indicates the value this flag will have if the + /// user doesn't explicitly pass it in. + /// + /// The [negatable] argument indicates whether this flag's value can be set to + /// `false`. For example, if [name] is `flag`, the user can pass `--no-flag` + /// to set its value to `false`. + /// + /// The [callback] argument is invoked with the flag's value when the flag + /// is parsed. Note that this makes argument parsing order-dependent in ways + /// that are often surprising, and its use is discouraged in favor of reading + /// values from the [ArgResult]. + /// + /// If [hide] is `true`, this option won't be included in [usage]. + /// + /// Throws an [ArgumentError] if: /// /// * There is already an option named [name]. /// * There is already an option using abbreviation [abbr]. @@ -102,7 +126,48 @@ class ArgParser { hide: hide); } - /// Defines a value-taking option. Throws an [ArgumentError] if: + /// Defines an option that takes a value. + /// + /// This adds an [Option] with the given properties to [options]. + /// + /// The [abbr] argument is a single-character string that can be used as a + /// shorthand for this option. For example, `abbr: "a"` will allow the user to + /// pass `-a value` or `-avalue`. + /// + /// The [help] argument is used by [usage] to describe this option. + /// + /// The [valueHelp] argument is used by [usage] as a name for the value this + /// option takes. For example, `valueHelp: "FOO"` will include + /// `--option=` rather than just `--option` in the usage string. + /// + /// The [allowed] argument is a list of valid values for this option. If + /// it's non-`null` and the user passes a value that's not included in the + /// list, [parse] will throw a [FormatException]. The allowed values will also + /// be included in [usage]. + /// + /// The [allowedHelp] argument is a map from values in [allowed] to + /// documentation for those values that will be included in [usage]. + /// + /// The [defaultsTo] argument indicates the value this option will have if the + /// user doesn't explicitly pass it in (or `null` by default). + /// + /// The [callback] argument is invoked with the option's value when the option + /// is parsed. Note that this makes argument parsing order-dependent in ways + /// that are often surprising, and its use is discouraged in favor of reading + /// values from the [ArgResult]. + /// + /// If [allowMultiple] is `true`, the user may pass this option multiple times + /// and its value will be a `List` rather than a `String`. The default + /// value will be `[]` rather than `null`, or `[defaultsTo]` if [defaultsTo] + /// is passed. + /// + /// If [splitCommas] is `true`, multiple options may be passed by writing + /// `--option a,b` in addition to `--option a --option b`. It defaults to + /// `true` if [allowMultiple] is `true` and `false` otherwise. + /// + /// If [hide] is `true`, this option won't be included in [usage]. + /// + /// Throws an [ArgumentError] if: /// /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 55cc5668..b7bb62bc 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -24,19 +24,53 @@ Option newOption( negatable: negatable, splitCommas: splitCommas, hide: hide); } -/// A command-line option. Includes both flags and options which take a value. +/// A command-line option. +/// +/// This represents both boolean flags and options which take a value. class Option { + /// The name of the option that the user passes as an argument. final String name; + + /// A single-character string that can be used as a shorthand for this option. + /// + /// For example, `abbr: "a"` will allow the user to pass `-a value` or + /// `-avalue`. final String abbreviation; - final List allowed; - final defaultValue; - final Function callback; + + /// A description of this option. final String help; + + /// A name for the value this option takes. final String valueHelp; + + /// A list of valid values for this option. + final List allowed; + + /// A map from values in [allowed] to documentation for those values. final Map allowedHelp; - final OptionType type; + + /// The value this option will have if the user doesn't explicitly pass it in + final defaultValue; + + /// Whether this flag's value can be set to `false`. + /// + /// For example, if [name] is `flag`, the user can pass `--no-flag` to set its + /// value to `false`. + /// + /// This is `null` unless [type] is [OptionType.FLAG]. final bool negatable; + + /// The callback to invoke with the option's value when the option is parsed. + final Function callback; + + /// Whether this is a flag, a single value option, or a multi-value option. + final OptionType type; + + /// Whether multiple values may be passed by writing `--option a,b` in + /// addition to `--option a --option b`. final bool splitCommas; + + /// Whether this option should be hidden from usage documentation. final bool hide; /// Whether the option is boolean-valued flag. From 476ddcaecb807925b821595703a055e4341e50a3 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Feb 2018 14:21:46 -0800 Subject: [PATCH 137/263] Deprecate upper-case constants in favor of lowercase --- pkgs/args/CHANGELOG.md | 6 ++++++ pkgs/args/lib/src/arg_parser.dart | 6 +++--- pkgs/args/lib/src/option.dart | 25 +++++++++++++++++-------- pkgs/args/pubspec.yaml | 2 +- 4 files changed, 27 insertions(+), 12 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 8771808b..e3ff6732 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,9 @@ +## 1.4.0 + +* Deprecated `OptionType.FLAG`, `OptionType.SINGLE`, and `OptionType.MULTIPLE` + in favor of `OptionType.flag`, `OptionType.single`, and `OptionType.multiple` + which follow the style guide. + ## 1.3.0 * Type `Command.run()`'s return value as `FutureOr`. diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index ea8ede6a..08234b95 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -121,7 +121,7 @@ class ArgParser { null, defaultsTo, callback == null ? null : (value) => callback(value as bool), - OptionType.FLAG, + OptionType.flag, negatable: negatable, hide: hide); } @@ -161,7 +161,7 @@ class ArgParser { /// value will be `[]` rather than `null`, or `[defaultsTo]` if [defaultsTo] /// is passed. /// - /// If [splitCommas] is `true`, multiple options may be passed by writing + /// If [splitCommas] is `true`, multiple values may be passed by writing /// `--option a,b` in addition to `--option a --option b`. It defaults to /// `true` if [allowMultiple] is `true` and `false` otherwise. /// @@ -189,7 +189,7 @@ class ArgParser { } _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, - callback, allowMultiple ? OptionType.MULTIPLE : OptionType.SINGLE, + callback, allowMultiple ? OptionType.multiple : OptionType.single, splitCommas: splitCommas, hide: hide); } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index b7bb62bc..8d4a9172 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -57,7 +57,7 @@ class Option { /// For example, if [name] is `flag`, the user can pass `--no-flag` to set its /// value to `false`. /// - /// This is `null` unless [type] is [OptionType.FLAG]. + /// This is `null` unless [type] is [OptionType.flag]. final bool negatable; /// The callback to invoke with the option's value when the option is parsed. @@ -74,13 +74,13 @@ class Option { final bool hide; /// Whether the option is boolean-valued flag. - bool get isFlag => type == OptionType.FLAG; + bool get isFlag => type == OptionType.flag; /// Whether the option takes a single value. - bool get isSingle => type == OptionType.SINGLE; + bool get isSingle => type == OptionType.single; /// Whether the option allows multiple values. - bool get isMultiple => type == OptionType.MULTIPLE; + bool get isMultiple => type == OptionType.multiple; Option._( this.name, @@ -102,7 +102,7 @@ class Option { // If the user doesn't specify [splitCommas], it defaults to true for // multiple options. this.splitCommas = - splitCommas == null ? type == OptionType.MULTIPLE : splitCommas { + splitCommas == null ? type == OptionType.multiple : splitCommas { if (name.isEmpty) { throw new ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { @@ -149,7 +149,10 @@ class OptionType { /// An option that can only be `true` or `false`. /// /// The presence of the option name itself in the argument list means `true`. - static const FLAG = const OptionType._("OptionType.FLAG"); + static const flag = const OptionType._("OptionType.flag"); + + @Deprecated("Use OptionType.flag instead.") + static const FLAG = flag; /// An option that takes a single value. /// @@ -160,7 +163,10 @@ class OptionType { /// --mode=debug /// /// If the option is passed more than once, the last one wins. - static const SINGLE = const OptionType._("OptionType.SINGLE"); + static const single = const OptionType._("OptionType.single"); + + @Deprecated("Use OptionType.single instead.") + static const SINGLE = single; /// An option that allows multiple values. /// @@ -170,7 +176,10 @@ class OptionType { /// /// In the parsed `ArgResults`, a multiple-valued option will always return /// a list, even if one or no values were passed. - static const MULTIPLE = const OptionType._("OptionType.MULTIPLE"); + static const multiple = const OptionType._("OptionType.multiple"); + + @Deprecated("Use OptionType.multiple instead.") + static const MULTIPLE = multiple; final String name; diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 2c5fa596..4985ea2e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.3.0 +version: 1.4.0-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 5fdcdb5f7a7a516d28929e3060b2b1571bcfb57a Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Feb 2018 14:26:18 -0800 Subject: [PATCH 138/263] Make Option's field names match addOption's parameters --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/src/arg_parser.dart | 6 ++--- pkgs/args/lib/src/arg_results.dart | 2 +- pkgs/args/lib/src/option.dart | 38 +++++++++++++++++------------- pkgs/args/lib/src/usage.dart | 17 +++++-------- 5 files changed, 36 insertions(+), 31 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index e3ff6732..7cc69c00 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -4,6 +4,10 @@ in favor of `OptionType.flag`, `OptionType.single`, and `OptionType.multiple` which follow the style guide. +* Deprecated `Option.abbreviation` and `Option.defaultValue` in favor of + `Option.abbr` and `Option.defaultsTo`. This makes all of `Option`'s fields + match the corresponding parameters to `ArgParser.addOption()`. + ## 1.3.0 * Type `Command.run()`'s return value as `FutureOr`. diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 08234b95..c1151971 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -257,13 +257,13 @@ class ArgParser { if (!options.containsKey(option)) { throw new ArgumentError('No option named $option'); } - return options[option].defaultValue; + return options[option].defaultsTo; } /// Finds the option whose abbreviation is [abbr], or `null` if no option has /// that abbreviation. Option findByAbbreviation(String abbr) { - return options.values.firstWhere((option) => option.abbreviation == abbr, - orElse: () => null); + return options.values + .firstWhere((option) => option.abbr == abbr, orElse: () => null); } } diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 744686dc..78daceab 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -76,7 +76,7 @@ class ArgResults { // Include the options that have defaults. _parser.options.forEach((name, option) { - if (option.defaultValue != null) result.add(name); + if (option.defaultsTo != null) result.add(name); }); return result; diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 8d4a9172..0902fe3c 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -8,19 +8,19 @@ /// get to it. This function isn't exported to the public API of the package. Option newOption( String name, - String abbreviation, + String abbr, String help, String valueHelp, Iterable allowed, Map allowedHelp, - defaultValue, + defaultsTo, Function callback, OptionType type, {bool negatable, bool splitCommas, bool hide: false}) { - return new Option._(name, abbreviation, help, valueHelp, allowed, allowedHelp, - defaultValue, callback, type, + return new Option._(name, abbr, help, valueHelp, allowed, allowedHelp, + defaultsTo, callback, type, negatable: negatable, splitCommas: splitCommas, hide: hide); } @@ -35,7 +35,10 @@ class Option { /// /// For example, `abbr: "a"` will allow the user to pass `-a value` or /// `-avalue`. - final String abbreviation; + final String abbr; + + @Deprecated("Use abbr instead.") + String get abbreviation => abbr; /// A description of this option. final String help; @@ -50,7 +53,10 @@ class Option { final Map allowedHelp; /// The value this option will have if the user doesn't explicitly pass it in - final defaultValue; + final defaultsTo; + + @Deprecated("Use defaultsTo instead.") + get defaultValue => defaultsTo; /// Whether this flag's value can be set to `false`. /// @@ -84,12 +90,12 @@ class Option { Option._( this.name, - this.abbreviation, + this.abbr, this.help, this.valueHelp, Iterable allowed, Map allowedHelp, - this.defaultValue, + this.defaultsTo, this.callback, OptionType type, {this.negatable, @@ -114,14 +120,14 @@ class Option { throw new ArgumentError('Name "$name" contains invalid characters.'); } - if (abbreviation != null) { - if (abbreviation.length != 1) { + if (abbr != null) { + if (abbr.length != 1) { throw new ArgumentError('Abbreviation must be null or have length 1.'); - } else if (abbreviation == '-') { + } else if (abbr == '-') { throw new ArgumentError('Abbreviation cannot be "-".'); } - if (_invalidChars.hasMatch(abbreviation)) { + if (_invalidChars.hasMatch(abbr)) { throw new ArgumentError('Abbreviation is an invalid character.'); } } @@ -130,14 +136,14 @@ class Option { /// Returns [value] if non-`null`, otherwise returns the default value for /// this option. /// - /// For single-valued options, it will be [defaultValue] if set or `null` + /// For single-valued options, it will be [defaultsTo] if set or `null` /// otherwise. For multiple-valued options, it will be an empty list or a - /// list containing [defaultValue] if set. + /// list containing [defaultsTo] if set. dynamic getOrDefault(value) { if (value != null) return value; - if (!isMultiple) return defaultValue; - if (defaultValue != null) return [defaultValue]; + if (!isMultiple) return defaultsTo; + if (defaultsTo != null) return [defaultsTo]; return []; } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 2b40332e..85edcac2 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -88,11 +88,11 @@ class Usage { newline(); } else if (option.allowed != null) { write(2, buildAllowedList(option)); - } else if (option.defaultValue != null) { - if (option.isFlag && option.defaultValue == true) { + } else if (option.defaultsTo != null) { + if (option.isFlag && option.defaultsTo == true) { write(2, '(defaults to on)'); } else if (!option.isFlag) { - write(2, '(defaults to "${option.defaultValue}")'); + write(2, '(defaults to "${option.defaultsTo}")'); } } @@ -106,13 +106,8 @@ class Usage { return buffer.toString(); } - String getAbbreviation(Option option) { - if (option.abbreviation != null) { - return '-${option.abbreviation}, '; - } else { - return ''; - } - } + String getAbbreviation(Option option) => + option.abbr == null ? '' : '-${option.abbr}, '; String getLongOption(Option option) { var result; @@ -228,7 +223,7 @@ class Usage { for (var allowed in option.allowed) { if (!first) allowedBuffer.write(', '); allowedBuffer.write(allowed); - if (allowed == option.defaultValue) { + if (allowed == option.defaultsTo) { allowedBuffer.write(' (default)'); } first = false; From 2963e038d80dbcd04fddd7ecc888fec8253cefc9 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Feb 2018 16:40:37 -0800 Subject: [PATCH 139/263] Reformat tests with the latest dartfmt --- pkgs/args/.travis.yml | 2 +- pkgs/args/example/test_runner.dart | 3 +- pkgs/args/test/command_runner_test.dart | 28 +++----- pkgs/args/test/command_test.dart | 5 +- pkgs/args/test/usage_test.dart | 88 +++++++------------------ 5 files changed, 33 insertions(+), 93 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index ac48a956..3b94716e 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -9,7 +9,7 @@ dart_task: matrix: include: - - dart: stable + - dart: dev dart_task: dartfmt # Only building master means that we don't run two builds for each pull request. diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index d9a62971..64f0fff0 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -150,8 +150,7 @@ main() { defaultsTo: false, help: 'Keep the generated files in the temporary directory'); - parser.addOption('special-command', - help: """ + parser.addOption('special-command', help: """ Special command support. Wraps the command line in a special command. The special command should contain an '@' character which will be replaced by the normal diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 5a765b13..ebb2f616 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -299,11 +299,8 @@ Run "test help" to see global options. var command = new FooCommand(); runner.addCommand(command); - expect( - runner.run(["foo", "--asdf"]), - throwsUsageException( - 'Could not find an option named "asdf".', - """ + expect(runner.run(["foo", "--asdf"]), + throwsUsageException('Could not find an option named "asdf".', """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -351,11 +348,8 @@ Also, footer!""")); test("a subcommand doesn't exist", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect( - runner.run(["foo bad"]), - throwsUsageException( - 'Could not find a command named "foo bad".', - """ + expect(runner.run(["foo bad"]), + throwsUsageException('Could not find a command named "foo bad".', """ Usage: test [arguments] Global options: @@ -371,11 +365,8 @@ Run "test help " for more information about a command.""")); test("a subcommand wasn't passed", () { runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); - expect( - runner.run(["foo"]), - throwsUsageException( - 'Missing subcommand for "test foo".', - """ + expect(runner.run(["foo"]), + throwsUsageException('Missing subcommand for "test foo".', """ Usage: test foo [arguments] -h, --help Print this usage information. @@ -388,11 +379,8 @@ Run "test help" to see global options.""")); test("a command that doesn't take arguments was given them", () { runner.addCommand(new FooCommand()); - expect( - runner.run(["foo", "bar"]), - throwsUsageException( - 'Command "foo" does not take any arguments.', - """ + expect(runner.run(["foo", "bar"]), + throwsUsageException('Command "foo" does not take any arguments.', """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index c0c08d34..c4f29d43 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -91,10 +91,7 @@ Run "test help" to see global options.""")); test("usageException splits up the message and usage", () { expect( - () => foo.usageException("message"), - throwsUsageException( - "message", - """ + () => foo.usageException("message"), throwsUsageException("message", """ Usage: test foo [arguments] -h, --help Print this usage information. diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 6c591bf0..cef3a16a 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -11,9 +11,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]mode The mode '''); }); @@ -22,9 +20,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', negatable: false, help: 'The mode'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --mode The mode '''); }); @@ -33,9 +29,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'The mode'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]mode The mode '''); }); @@ -45,9 +39,7 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'The mode'); parser.addOption('long', help: 'Lacks an abbreviation'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' -m, --[no-]mode The mode --long Lacks an abbreviation '''); @@ -58,9 +50,7 @@ void main() { parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); parser.addOption('a-really-long-name', help: 'Its help text'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' -m, --[no-]mode Lined up with below --a-really-long-name Its help text '''); @@ -70,9 +60,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]mode After newlines '''); }); @@ -81,9 +69,7 @@ void main() { var parser = new ArgParser(); parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]mode Before newlines '''); }); @@ -94,9 +80,7 @@ void main() { parser.addFlag('monkey', help: 'Second'); parser.addFlag('wombat', help: 'Third'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]zebra First --[no-]monkey Second --[no-]wombat Third @@ -108,9 +92,7 @@ void main() { parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); parser.addFlag('negate', help: 'Should be off', defaultsTo: false); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]affirm Should be on (defaults to on) @@ -122,9 +104,7 @@ void main() { var parser = new ArgParser(); parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --any Can be anything (defaults to "whatevs") '''); @@ -135,9 +115,7 @@ void main() { parser.addOption('out', abbr: 'o', help: 'Where to write file', valueHelp: 'path'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' -o, --out= Where to write file '''); }); @@ -148,9 +126,7 @@ void main() { help: 'Like in cards', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --suit Like in cards [spades, clubs, hearts, diamonds] '''); @@ -163,9 +139,7 @@ void main() { defaultsTo: 'clubs', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --suit Like in cards [spades, clubs (default), hearts, diamonds] '''); @@ -189,9 +163,7 @@ void main() { 'hearts': 'The shape of my heart' }); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --suit Like in cards [clubs] Weapons of war @@ -207,9 +179,7 @@ void main() { parser.addOption('second', hide: true); parser.addOption('third', help: 'The third option'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --first The first option --third The third option '''); @@ -221,9 +191,7 @@ void main() { parser.addFlag('second', hide: true); parser.addFlag('third', help: 'The third flag'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]first The first flag --[no-]third The third flag '''); @@ -235,9 +203,7 @@ void main() { parser.addFlag('second-very-long-option', hide: true); parser.addFlag('third', help: 'The third flag'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]first The first flag --[no-]third The third flag '''); @@ -252,9 +218,7 @@ void main() { parser.addSeparator('Marsupial:'); parser.addFlag('wombat', help: 'Third'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]zebra First Primate: @@ -271,9 +235,7 @@ void main() { parser.addSeparator('Primate:'); parser.addFlag('monkey', help: 'Second'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]zebra Multi line @@ -287,9 +249,7 @@ void main() { parser.addSeparator('Equine:'); parser.addFlag('zebra', help: 'First'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' Equine: --[no-]zebra First '''); @@ -300,9 +260,7 @@ void main() { parser.addFlag('zebra', help: 'First'); parser.addSeparator('Primate:'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' --[no-]zebra First Primate: @@ -314,9 +272,7 @@ void main() { parser.addSeparator('First'); parser.addSeparator('Second'); - validateUsage( - parser, - ''' + validateUsage(parser, ''' First Second From 301afab46a4f77ac7beab8a5f2e768bbea230d4e Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 20 Feb 2018 16:45:42 -0800 Subject: [PATCH 140/263] Add a separate addMultiOption() method This allows us to be more specific in our types and to keep flags that are specific to multi-options in the place where they make sense. Closes dart-lang/args#90 --- pkgs/args/CHANGELOG.md | 6 + pkgs/args/analysis_options.yaml | 8 +- pkgs/args/lib/src/allow_anything_parser.dart | 14 + pkgs/args/lib/src/arg_parser.dart | 92 ++++- pkgs/args/lib/src/option.dart | 6 +- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 14 +- pkgs/args/test/parse_test.dart | 348 ++++++++++++------- 8 files changed, 350 insertions(+), 140 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 7cc69c00..20eba1ff 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -8,6 +8,12 @@ `Option.abbr` and `Option.defaultsTo`. This makes all of `Option`'s fields match the corresponding parameters to `ArgParser.addOption()`. +* Deprecated the `allowMultiple` and `splitCommas` arguments to + `ArgParser.addOption()` in favor of a separate `ArgParser.addMultiOption()` + method. This allows us to provide more accurate type information, and to avoid + adding flags that only make sense for multi-options in places where they might + be usable for single-value options. + ## 1.3.0 * Type `Command.run()`'s return value as `FutureOr`. diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 092773be..32747eea 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -1,5 +1,11 @@ analyzer: - strong-mode: true + strong-mode: true + + errors: + # TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no + # longer refers to its own deprecated members. + deprecated_member_use: ignore + linter: rules: # Errors diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 58e48127..248a39a9 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -45,6 +45,20 @@ class AllowAnythingParser implements ArgParser { "ArgParser.allowAnything().addOption() isn't supported."); } + void addMultiOption(String name, + {String abbr, + String help, + String valueHelp, + Iterable allowed, + Map allowedHelp, + Iterable defaultsTo, + void callback(List values), + bool splitCommas: true, + bool hide: false}) { + throw new UnsupportedError( + "ArgParser.allowAnything().addMultiOption() isn't supported."); + } + void addSeparator(String text) { throw new UnsupportedError( "ArgParser.allowAnything().addSeparator() isn't supported."); diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index c1151971..bcdb8df9 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -156,14 +156,8 @@ class ArgParser { /// that are often surprising, and its use is discouraged in favor of reading /// values from the [ArgResult]. /// - /// If [allowMultiple] is `true`, the user may pass this option multiple times - /// and its value will be a `List` rather than a `String`. The default - /// value will be `[]` rather than `null`, or `[defaultsTo]` if [defaultsTo] - /// is passed. - /// - /// If [splitCommas] is `true`, multiple values may be passed by writing - /// `--option a,b` in addition to `--option a --option b`. It defaults to - /// `true` if [allowMultiple] is `true` and `false` otherwise. + /// The [allowMultiple] and [splitCommas] options are deprecated; the + /// [addMultiOption] method should be used instead. /// /// If [hide] is `true`, this option won't be included in [usage]. /// @@ -180,17 +174,89 @@ class ArgParser { Map allowedHelp, String defaultsTo, Function callback, - bool allowMultiple: false, - bool splitCommas, + @Deprecated("Use addMultiOption() instead.") bool allowMultiple: false, + @Deprecated("Use addMultiOption() instead.") bool splitCommas, bool hide: false}) { if (!allowMultiple && splitCommas != null) { throw new ArgumentError( 'splitCommas may not be set if allowMultiple is false.'); } - _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, - callback, allowMultiple ? OptionType.multiple : OptionType.single, - splitCommas: splitCommas, hide: hide); + _addOption( + name, + abbr, + help, + valueHelp, + allowed, + allowedHelp, + allowMultiple + ? (defaultsTo == null ? [] : [defaultsTo]) + : defaultsTo, + callback, + allowMultiple ? OptionType.multiple : OptionType.single, + splitCommas: splitCommas, + hide: hide); + } + + /// Defines an option that takes multiple values. + /// + /// The [abbr] argument is a single-character string that can be used as a + /// shorthand for this option. For example, `abbr: "a"` will allow the user to + /// pass `-a value` or `-avalue`. + /// + /// The [help] argument is used by [usage] to describe this option. + /// + /// The [valueHelp] argument is used by [usage] as a name for the value this + /// argument takes. For example, `valueHelp: "FOO"` will include + /// `--option=` rather than just `--option` in the usage string. + /// + /// The [allowed] argument is a list of valid values for this argument. If + /// it's non-`null` and the user passes a value that's not included in the + /// list, [parse] will throw a [FormatException]. The allowed values will also + /// be included in [usage]. + /// + /// The [allowedHelp] argument is a map from values in [allowed] to + /// documentation for those values that will be included in [usage]. + /// + /// The [defaultsTo] argument indicates the values this option will have if + /// the user doesn't explicitly pass it in (or `[]` by default). + /// + /// The [callback] argument is invoked with the option's value when the option + /// is parsed. Note that this makes argument parsing order-dependent in ways + /// that are often surprising, and its use is discouraged in favor of reading + /// values from the [ArgResult]. + /// + /// If [splitCommas] is `true` (the default), multiple options may be passed + /// by writing `--option a,b` in addition to `--option a --option b`. + /// + /// If [hide] is `true`, this option won't be included in [usage]. + /// + /// Throws an [ArgumentError] if: + /// + /// * There is already an option with name [name]. + /// * There is already an option using abbreviation [abbr]. + void addMultiOption(String name, + {String abbr, + String help, + String valueHelp, + Iterable allowed, + Map allowedHelp, + Iterable defaultsTo, + void callback(List values), + bool splitCommas: true, + bool hide: false}) { + _addOption( + name, + abbr, + help, + valueHelp, + allowed, + allowedHelp, + defaultsTo?.toList() ?? [], + callback == null ? null : (value) => callback(value as List), + OptionType.multiple, + splitCommas: splitCommas, + hide: hide); } void _addOption( diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 0902fe3c..727ea52f 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -141,10 +141,8 @@ class Option { /// list containing [defaultsTo] if set. dynamic getOrDefault(value) { if (value != null) return value; - - if (!isMultiple) return defaultsTo; - if (defaultsTo != null) return [defaultsTo]; - return []; + if (isMultiple) return defaultsTo ?? []; + return defaultsTo; } static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4985ea2e..c476bc38 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.0-dev +version: 1.4.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index ff968adc..8741ad87 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -274,8 +274,11 @@ void main() { parser.addFlag('flag-def', defaultsTo: true); parser.addOption('single-no'); parser.addOption('single-def', defaultsTo: 'def'); - parser.addOption('multi-no', allowMultiple: true); - parser.addOption('multi-def', allowMultiple: true, defaultsTo: 'def'); + parser.addOption('allow-multi-no', allowMultiple: true); + parser.addOption('allow-multi-def', + allowMultiple: true, defaultsTo: 'def'); + parser.addMultiOption('multi-no'); + parser.addMultiOption('multi-def', defaultsTo: ['def']); expect(parser.options['flag-no'].getOrDefault(null), equals(null)); expect(parser.options['flag-no'].getOrDefault(false), equals(false)); @@ -285,6 +288,13 @@ void main() { expect(parser.options['single-no'].getOrDefault('v'), equals('v')); expect(parser.options['single-def'].getOrDefault(null), equals('def')); expect(parser.options['single-def'].getOrDefault('v'), equals('v')); + expect(parser.options['allow-multi-no'].getOrDefault(null), equals([])); + expect( + parser.options['allow-multi-no'].getOrDefault(['v']), equals(['v'])); + expect(parser.options['allow-multi-def'].getOrDefault(null), + equals(['def'])); + expect( + parser.options['allow-multi-def'].getOrDefault(['v']), equals(['v'])); expect(parser.options['multi-no'].getOrDefault(null), equals([])); expect(parser.options['multi-no'].getOrDefault(['v']), equals(['v'])); expect(parser.options['multi-def'].getOrDefault(null), equals(['def'])); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 87f3d81e..d212cf02 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -152,104 +152,186 @@ void main() { expect(a, isNull); }); - test( - 'for multiple present, allowMultiple, options are invoked with ' - 'value as a list', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); - - parser.parse(['--a=v', '--a=x']); - expect(a, equals(['v', 'x'])); - - // This reified type is important in strong mode so that people can - // safely write "as List". - expect(a, new isInstanceOf>()); - }); - - test( - 'for single present, allowMultiple, options are invoked with ' - ' value as a single element list', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); - - parser.parse(['--a=v']); - expect(a, equals(['v'])); - }); - - test( - 'for absent, allowMultiple, options are invoked with default ' - 'value as a list.', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, - defaultsTo: 'v', - callback: (value) => a = value); - - parser.parse([]); - expect(a, equals(['v'])); - }); - - test( - 'for absent, allowMultiple, options are invoked with value ' - 'as an empty list.', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); - - parser.parse([]); - expect(a, isEmpty); - }); - - test('allowMultiple parses comma-separated strings', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v', 'w', 'x'])); - }); - - test( - "allowMultiple doesn't parses comma-separated strings with " - "splitCommas: false", () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, - splitCommas: false, - callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v,w', 'x'])); - }); - - test('allowMultiple parses empty strings', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); - - parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); - expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); - }); - - test('allowMultiple with allowed parses comma-separated strings', () { - var a; - var parser = new ArgParser(); - parser.addOption('a', - allowMultiple: true, - allowed: ['v', 'w', 'x'], - callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v', 'w', 'x'])); + group("with allowMultiple", () { + test('for multiple present, options are invoked with value as a list', + () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=v', '--a=x']); + expect(a, equals(['v', 'x'])); + + // This reified type is important in strong mode so that people can + // safely write "as List". + expect(a, new isInstanceOf>()); + }); + + test( + 'for single present, options are invoked with value as a single ' + 'element list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=v']); + expect(a, equals(['v'])); + }); + + test('for absent, options are invoked with default value as a list', + () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, + defaultsTo: 'v', + callback: (value) => a = value); + + parser.parse([]); + expect(a, equals(['v'])); + }); + + test('for absent, options are invoked with value as an empty list', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse([]); + expect(a, isEmpty); + }); + + test('parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); + + test("doesn't parse comma-separated strings with splitCommas: false", + () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, + splitCommas: false, + callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v,w', 'x'])); + }); + + test('parses empty strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, callback: (value) => a = value); + + parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); + expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); + }); + + test('with allowed parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addOption('a', + allowMultiple: true, + allowed: ['v', 'w', 'x'], + callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); + }); + + group("with addMultiOption", () { + test('for multiple present, options are invoked with value as a list', + () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', callback: (value) => a = value); + + parser.parse(['--a=v', '--a=x']); + expect(a, equals(['v', 'x'])); + + // This reified type is important in strong mode so that people can + // safely write "as List". + expect(a, new isInstanceOf>()); + }); + + test( + 'for single present, options are invoked with value as a single ' + 'element list', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', callback: (value) => a = value); + + parser.parse(['--a=v']); + expect(a, equals(['v'])); + }); + + test('for absent, options are invoked with default value', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', + defaultsTo: ['v', 'w'], callback: (value) => a = value); + + parser.parse([]); + expect(a, equals(['v', 'w'])); + }); + + test('for absent, options are invoked with value as an empty list', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', callback: (value) => a = value); + + parser.parse([]); + expect(a, isEmpty); + }); + + test('parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); + + test("doesn't parse comma-separated strings with splitCommas: false", + () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', + splitCommas: false, callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v,w', 'x'])); + }); + + test('parses empty strings', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', callback: (value) => a = value); + + parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); + expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); + }); + + test('with allowed parses comma-separated strings', () { + var a; + var parser = new ArgParser(); + parser.addMultiOption('a', + allowed: ['v', 'w', 'x'], callback: (value) => a = value); + + parser.parse(['--a=v,w', '--a=x']); + expect(a, equals(['v', 'w', 'x'])); + }); }); }); @@ -340,12 +422,22 @@ void main() { throwsFormat(parser, ['-mprofile']); }); - test('throw if a comma-separated value is not allowed', () { - var parser = new ArgParser(); - parser.addOption('mode', - abbr: 'm', allowMultiple: true, allowed: ['debug', 'release']); + group('throw if a comma-separated value is not allowed', () { + test("with allowMultiple", () { + var parser = new ArgParser(); + parser.addOption('mode', + abbr: 'm', allowMultiple: true, allowed: ['debug', 'release']); + + throwsFormat(parser, ['-mdebug,profile']); + }); - throwsFormat(parser, ['-mdebug,profile']); + test("with addMultiOption", () { + var parser = new ArgParser(); + parser + .addMultiOption('mode', abbr: 'm', allowed: ['debug', 'release']); + + throwsFormat(parser, ['-mdebug,profile']); + }); }); test('throw if any but the first is not a flag', () { @@ -449,22 +541,40 @@ void main() { expect(args['define'], equals('2')); }); - test('returns a List if multi-valued', () { - var parser = new ArgParser(); - parser.addOption('define', allowMultiple: true); - var args = parser.parse(['--define=1']); - expect(args['define'], equals(['1'])); - args = parser.parse(['--define=1', '--define=2']); - expect(args['define'], equals(['1', '2'])); - }); - - test( - 'returns the default value for multi-valued arguments ' - 'if not explicitly set', () { - var parser = new ArgParser(); - parser.addOption('define', defaultsTo: '0', allowMultiple: true); - var args = parser.parse(['']); - expect(args['define'], equals(['0'])); + group('returns a List', () { + test('with allowMultiple', () { + var parser = new ArgParser(); + parser.addOption('define', allowMultiple: true); + var args = parser.parse(['--define=1']); + expect(args['define'], equals(['1'])); + args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals(['1', '2'])); + }); + + test('with addMultiOption', () { + var parser = new ArgParser(); + parser.addMultiOption('define'); + var args = parser.parse(['--define=1']); + expect(args['define'], equals(['1'])); + args = parser.parse(['--define=1', '--define=2']); + expect(args['define'], equals(['1', '2'])); + }); + }); + + group('returns the default value if not explicitly set', () { + test('with allowMultiple', () { + var parser = new ArgParser(); + parser.addOption('define', defaultsTo: '0', allowMultiple: true); + var args = parser.parse(['']); + expect(args['define'], equals(['0'])); + }); + + test('with addMultiOption', () { + var parser = new ArgParser(); + parser.addMultiOption('define', defaultsTo: ['0']); + var args = parser.parse(['']); + expect(args['define'], equals(['0'])); + }); }); test('are case-sensitive', () { From 21951028c3a228c2d3f1a9c93e6a00e560deabb8 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Fri, 23 Feb 2018 14:38:47 -0800 Subject: [PATCH 141/263] Fix addMultiOption() and addOption(allowMultiple) usage (dart-lang/args#94) We were printing the option list, with square brackets, even for options that didn't have a default. Fixes dart-lang/testdart-lang/args#774. --- pkgs/args/CHANGELOG.md | 5 +++ pkgs/args/lib/src/usage.dart | 24 +++++++++++--- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 58 ++++++++++++++++++++++++++++++++-- 4 files changed, 81 insertions(+), 8 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 20eba1ff..0ab3399d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.4.1 + +* Fix the way default values for multi-valued options are printed in argument + usage. + ## 1.4.0 * Deprecated `OptionType.FLAG`, `OptionType.SINGLE`, and `OptionType.MULTIPLE` diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 85edcac2..3ce6d6b5 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -88,10 +88,20 @@ class Usage { newline(); } else if (option.allowed != null) { write(2, buildAllowedList(option)); - } else if (option.defaultsTo != null) { - if (option.isFlag && option.defaultsTo == true) { + } else if (option.isFlag) { + if (option.defaultsTo == true) { write(2, '(defaults to on)'); - } else if (!option.isFlag) { + } + } else if (option.isMultiple) { + if (option.defaultsTo != null && option.defaultsTo.isNotEmpty) { + write( + 2, + '(defaults to ' + + option.defaultsTo.map((value) => '"$value"').join(', ') + + ')'); + } + } else { + if (option.defaultsTo != null) { write(2, '(defaults to "${option.defaultsTo}")'); } } @@ -217,13 +227,17 @@ class Usage { } String buildAllowedList(Option option) { + var isDefault = option.defaultsTo is List + ? option.defaultsTo.contains + : (value) => value == option.defaultsTo; + var allowedBuffer = new StringBuffer(); allowedBuffer.write('['); - bool first = true; + var first = true; for (var allowed in option.allowed) { if (!first) allowedBuffer.write(', '); allowedBuffer.write(allowed); - if (allowed == option.defaultsTo) { + if (isDefault(allowed)) { allowedBuffer.write(' (default)'); } first = false; diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index c476bc38..b7331520 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.0 +version: 1.4.1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index cef3a16a..ef0b01a9 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -91,22 +91,63 @@ void main() { var parser = new ArgParser(); parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); parser.addFlag('negate', help: 'Should be off', defaultsTo: false); + parser.addFlag('null', help: 'Should be null', defaultsTo: null); validateUsage(parser, ''' --[no-]affirm Should be on (defaults to on) --[no-]negate Should be off + --[no-]null Should be null '''); }); test('the default value for an option with no allowed list is shown', () { var parser = new ArgParser(); - parser.addOption('any', help: 'Can be anything', defaultsTo: 'whatevs'); + parser.addOption('single', + help: 'Can be anything', defaultsTo: 'whatevs'); + parser.addMultiOption('multiple', + help: 'Can be anything', defaultsTo: ['whatevs']); + parser.addOption('allow-multi', + help: 'Can be anything', defaultsTo: 'whatevs', allowMultiple: true); + + validateUsage(parser, ''' + --single Can be anything + (defaults to "whatevs") + + --multiple Can be anything + (defaults to "whatevs") + + --allow-multi Can be anything + (defaults to "whatevs") + '''); + }); + + test('multiple default values for an option with no allowed list are shown', + () { + var parser = new ArgParser(); + parser.addMultiOption('any', + help: 'Can be anything', defaultsTo: ['some', 'stuff']); validateUsage(parser, ''' --any Can be anything - (defaults to "whatevs") + (defaults to "some", "stuff") + '''); + }); + + test('no default values are shown for a multi option with an empty default', + () { + var parser = new ArgParser(); + parser.addMultiOption('implicit', help: 'Implicit default'); + parser + .addMultiOption('explicit', help: 'Explicit default', defaultsTo: []); + parser.addOption('allow-multi', + help: 'Implicit with allowMultiple', allowMultiple: true); + + validateUsage(parser, ''' + --implicit Implicit default + --explicit Explicit default + --allow-multi Implicit with allowMultiple '''); }); @@ -145,6 +186,19 @@ void main() { '''); }); + test('multiple defaults are highlighted in the allowed list', () { + var parser = new ArgParser(); + parser.addMultiOption('suit', + help: 'Like in cards', + defaultsTo: ['clubs', 'diamonds'], + allowed: ['spades', 'clubs', 'hearts', 'diamonds']); + + validateUsage(parser, ''' + --suit Like in cards + [spades, clubs (default), hearts, diamonds (default)] + '''); + }); + test('the allowed help is shown', () { var parser = new ArgParser(); parser.addOption('suit', From 984a57c4e1c5ddd9aadc8d64dce6fc306554dabb Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 9 Apr 2018 11:40:10 -0700 Subject: [PATCH 142/263] Narrow the SDK constraint to require FutureOr (dart-lang/args#89) Closes dart-lang/args#88 --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 4 ++-- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 0ab3399d..1299d58d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.2 + +* Narrow the SDK constraint to only allow SDK versions that support `FutureOr`. + ## 1.4.1 * Fix the way default values for multi-valued options are printed in argument diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index b7331520..91e184e7 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.1 +version: 1.4.2 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -9,4 +9,4 @@ description: > dev_dependencies: test: ">=0.12.0 <0.13.0" environment: - sdk: ">=1.4.0 <2.0.0" + sdk: ">=1.22.0 <2.0.0" From cca0f82036d995adebbc51bd3431cf4f99561bac Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Mon, 9 Apr 2018 16:24:58 -0700 Subject: [PATCH 143/263] Remove lints (dart-lang/args#98) I don't believe in supporting lints that are generally unmaintained and non-canonical. --- pkgs/args/analysis_options.yaml | 28 ---------------------------- 1 file changed, 28 deletions(-) diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 32747eea..4aa709ee 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -5,31 +5,3 @@ analyzer: # TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no # longer refers to its own deprecated members. deprecated_member_use: ignore - -linter: - rules: - # Errors - - avoid_empty_else - - comment_references - - control_flow_in_finally - - empty_statements - - hash_and_equals - - test_types_in_equals - - throw_in_finally - - unrelated_type_equality_checks - - valid_regexps - - # Style - - annotate_overrides - - avoid_init_to_null - - avoid_return_types_on_setters - - await_only_futures - - camel_case_types - - empty_catches - - empty_constructor_bodies - - library_names - - library_prefixes - - non_constant_identifier_names - - prefer_is_not_empty - - slash_for_doc_comments - - type_init_formals From 90c1e0ba6d7b11e995d984184ced3f3cc576bc84 Mon Sep 17 00:00:00 2001 From: Natalie Weizenbaum Date: Tue, 24 Apr 2018 13:30:02 -0700 Subject: [PATCH 144/263] Display the default values for options with allowedHelp specified (dart-lang/args#100) Closes dart-lang/args#99 --- pkgs/args/CHANGELOG.md | 4 +++ pkgs/args/lib/src/usage.dart | 15 +++++---- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 61 +++++++++++++++++++++++++++++++--- 4 files changed, 70 insertions(+), 12 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 1299d58d..3a3630dc 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.3 + +* Display the default values for options with `allowedHelp` specified. + ## 1.4.2 * Narrow the SDK constraint to only allow SDK versions that support `FutureOr`. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 3ce6d6b5..aa3ed07b 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -82,7 +82,7 @@ class Usage { allowedNames.sort(); newline(); for (var name in allowedNames) { - write(1, getAllowedTitle(name)); + write(1, getAllowedTitle(option, name)); write(2, option.allowedHelp[name]); } newline(); @@ -132,13 +132,16 @@ class Usage { return result; } - String getAllowedTitle(String allowed) { - return ' [$allowed]'; + String getAllowedTitle(Option option, String allowed) { + var isDefault = option.defaultsTo is List + ? option.defaultsTo.contains(allowed) + : option.defaultsTo == allowed; + return ' [$allowed]' + (isDefault ? ' (default)' : ''); } void calculateColumnWidths() { - int abbr = 0; - int title = 0; + var abbr = 0; + var title = 0; for (var option in optionsAndSeparators) { if (option is! Option) continue; if (option.hide) continue; @@ -152,7 +155,7 @@ class Usage { // Make room for the allowed help. if (option.allowedHelp != null) { for (var allowed in option.allowedHelp.keys) { - title = max(title, getAllowedTitle(allowed).length); + title = max(title, getAllowedTitle(option, allowed).length); } } } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 91e184e7..23dd5255 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.2 +version: 1.4.3 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index ef0b01a9..48a33ddd 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -200,6 +200,30 @@ void main() { }); test('the allowed help is shown', () { + var parser = new ArgParser(); + parser.addOption('suit', help: 'Like in cards', allowed: [ + 'spades', + 'clubs', + 'diamonds', + 'hearts' + ], allowedHelp: { + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage(parser, ''' + --suit Like in cards + + [clubs] Weapons of war + [diamonds] Money for this art + [hearts] The shape of my heart + [spades] Swords of a soldier + '''); + }); + + test('the default is highlighted in the allowed help', () { var parser = new ArgParser(); parser.addOption('suit', help: 'Like in cards', @@ -218,12 +242,39 @@ void main() { }); validateUsage(parser, ''' - --suit Like in cards + --suit Like in cards - [clubs] Weapons of war - [diamonds] Money for this art - [hearts] The shape of my heart - [spades] Swords of a soldier + [clubs] (default) Weapons of war + [diamonds] Money for this art + [hearts] The shape of my heart + [spades] Swords of a soldier + '''); + }); + + test('multiple defaults are highlighted in the allowed help', () { + var parser = new ArgParser(); + parser.addMultiOption('suit', help: 'Like in cards', defaultsTo: [ + 'clubs', + 'hearts' + ], allowed: [ + 'spades', + 'clubs', + 'diamonds', + 'hearts' + ], allowedHelp: { + 'spades': 'Swords of a soldier', + 'clubs': 'Weapons of war', + 'diamonds': 'Money for this art', + 'hearts': 'The shape of my heart' + }); + + validateUsage(parser, ''' + --suit Like in cards + + [clubs] (default) Weapons of war + [diamonds] Money for this art + [hearts] (default) The shape of my heart + [spades] Swords of a soldier '''); }); From a9c46a0bf68b554f897dc7c5d0ba51eed69c554a Mon Sep 17 00:00:00 2001 From: BC Ko Date: Thu, 24 May 2018 01:59:09 -0700 Subject: [PATCH 145/263] pubspec conventions dart-lang/site-wwwdart-lang/args#825 --- pkgs/args/pubspec.yaml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 23dd5255..5d8bbf7d 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -7,6 +7,7 @@ description: > a set of options and values using GNU and POSIX style options. dev_dependencies: - test: ">=0.12.0 <0.13.0" + test: '>=0.12.0 <0.13.0' + environment: - sdk: ">=1.22.0 <2.0.0" + sdk: '>=1.22.0 <2.0.0' From 491683ab9bfbc8fa31ffd087ecec02d43957f04e Mon Sep 17 00:00:00 2001 From: BC Ko Date: Thu, 24 May 2018 02:06:16 -0700 Subject: [PATCH 146/263] Update .gitignore to new `dart_tool` pub cache dart-lang/sdkdart-lang/args#32030 --- pkgs/args/.gitignore | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/pkgs/args/.gitignore b/pkgs/args/.gitignore index 7dbf0350..813a31ec 100644 --- a/pkgs/args/.gitignore +++ b/pkgs/args/.gitignore @@ -1,6 +1,7 @@ # Don’t commit the following directories created by pub. .buildlog .pub/ +.dart_tool/ build/ packages .packages @@ -12,4 +13,4 @@ packages *.js.map # Include when developing application packages. -pubspec.lock \ No newline at end of file +pubspec.lock From d5dbb9791012b8b3795836cedf57e6f074275d2d Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Wed, 18 Jul 2018 10:02:42 -0700 Subject: [PATCH 147/263] Update README to use addMultiOption (dart-lang/args#104) --- pkgs/args/README.md | 4 ++-- pkgs/args/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index a23fe14c..df7591a1 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -181,13 +181,13 @@ var results = parser.parse(['--mode', 'on', '--mode', 'off']); print(results['mode']); // prints 'off' ``` -If you need multiple values, set the `allowMultiple` parameter. In that case the +Multiple values can be parsed with `addMultiOption()`. With this method, an option can occur multiple times, and the `parse()` method returns a list of values: ```dart var parser = new ArgParser(); -parser.addOption('mode', allowMultiple: true); +parser.addMultiOption('mode'); var results = parser.parse(['--mode', 'on', '--mode', 'off']); print(results['mode']); // prints '[on, off]' ``` diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 5d8bbf7d..09ad6094 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.3 +version: 1.4.4-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From cf6ff32e40b1a4a0fb41d16822575a8fd95ce78b Mon Sep 17 00:00:00 2001 From: Patrice Chalin Date: Wed, 18 Jul 2018 13:05:32 -0400 Subject: [PATCH 148/263] chore: set max SDK version to <3.0.0 (dart-lang/args#106) --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/analysis_options.yaml | 2 -- pkgs/args/pubspec.yaml | 10 +++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 3a3630dc..d418df97 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.4 + +* Set max SDK version to `<3.0.0`, and adjust other dependencies. + ## 1.4.3 * Display the default values for options with `allowedHelp` specified. diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 4aa709ee..c2bf9bfb 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -1,6 +1,4 @@ analyzer: - strong-mode: true - errors: # TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no # longer refers to its own deprecated members. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 09ad6094..80b6033a 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,13 +1,13 @@ name: args -version: 1.4.4-dev +version: 1.4.4 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. -dev_dependencies: - test: '>=0.12.0 <0.13.0' - environment: - sdk: '>=1.22.0 <2.0.0' + sdk: '>=1.22.0 <3.0.0' + +dev_dependencies: + test: '>=0.12.0 <2.0.0' From a47ad830267600408e1216a79730c2b774a39641 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 11 Jul 2018 13:13:10 -0700 Subject: [PATCH 149/263] Add optional wrapping of text for usage messages. --- pkgs/args/lib/src/allow_anything_parser.dart | 1 + pkgs/args/lib/src/arg_parser.dart | 24 ++++- pkgs/args/lib/src/usage.dart | 68 +++++++++++++- pkgs/args/test/usage_test.dart | 96 ++++++++++++++++++++ 4 files changed, 184 insertions(+), 5 deletions(-) diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 248a39a9..34a0b191 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -13,6 +13,7 @@ class AllowAnythingParser implements ArgParser { Map get commands => const {}; bool get allowTrailingOptions => false; bool get allowsAnything => true; + int get maxLineLength => null; ArgParser addCommand(String name, [ArgParser parser]) { throw new UnsupportedError( diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index bcdb8df9..a25a8b81 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -30,6 +30,18 @@ class ArgParser { /// arguments. final bool allowTrailingOptions; + /// An optional maximum line length for [usage] messages. + /// + /// If specified, then help messages in the usage are wrapped at the given + /// column, after taking into account the width of the options. Will refuse to + /// wrap help text to less than 10 characters of help text per line if there + /// isn't enough space on the line. It preserves embedded newlines, and + /// attempts to wrap at whitespace breaks (although it will split words if + /// there is no whitespace at which to split). + /// + /// If null (the default), help messages are not wrapped. + final int maxLineLength; + /// Whether or not this parser treats unrecognized options as non-option /// arguments. bool get allowsAnything => false; @@ -40,9 +52,10 @@ class ArgParser { /// flags and options that appear after positional arguments. If it's `false`, /// the parser stops parsing as soon as it finds an argument that is neither /// an option nor a command. - factory ArgParser({bool allowTrailingOptions: true}) => + factory ArgParser({bool allowTrailingOptions: true, int maxLineLength}) => new ArgParser._({}, {}, - allowTrailingOptions: allowTrailingOptions); + allowTrailingOptions: allowTrailingOptions, + maxLineLength: maxLineLength); /// Creates a new ArgParser that treats *all input* as non-option arguments. /// @@ -53,7 +66,7 @@ class ArgParser { factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, - {bool allowTrailingOptions: true}) + {bool allowTrailingOptions: true, this.maxLineLength}) : this._options = options, this.options = new UnmodifiableMapView(options), this._commands = commands, @@ -315,7 +328,10 @@ class ArgParser { /// Generates a string displaying usage information for the defined options. /// /// This is basically the help text shown on the command line. - String get usage => new Usage(_optionsAndSeparators).generate(); + String get usage { + return new Usage(_optionsAndSeparators, maxLineLength: maxLineLength) + .generate(); + } /// Get the default value for an option. Useful after parsing to test if the /// user specified something other than the default. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index aa3ed07b..9898b44d 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -51,7 +51,12 @@ class Usage { /// content. int newlinesNeeded = 0; - Usage(this.optionsAndSeparators); + /// The horizontal character position at which help text is wrapped. Help that + /// extends past this column will be wrapped at the nearest whitespace (or + /// truncated if there is no available whitespace). + final int maxLineLength; + + Usage(this.optionsAndSeparators, {this.maxLineLength}); /// Generates a string displaying usage information for the defined options. /// This is basically the help text shown on the command line. @@ -171,8 +176,52 @@ class Usage { numHelpLines = 0; } + /// Wrap a single line of text into lines no longer than length. + /// Try to split at whitespace, but if that's not good enough to keep it + /// under the length, then split in the middle of a word. + List wrap(String text, int length) { + assert(length > 0, 'Wrap length must be larger than zero.'); + text = text.trim(); + if (text.length <= length) { + return [text]; + } + var result = []; + int currentLineStart = 0; + int lastWhitespace; + for (int i = 0; i < text.length; ++i) { + if (isWhitespace(text, i)) { + lastWhitespace = i; + } + if ((i - currentLineStart) >= length) { + // Back up to the last whitespace, unless there wasn't any, in which + // case we just split where we are. + if (lastWhitespace != null) { + i = lastWhitespace; + } + result.add(text.substring(currentLineStart, i)); + // Skip any intervening whitespace. + while (isWhitespace(text, i) && i < text.length) i++; + currentLineStart = i; + lastWhitespace = null; + } + } + result.add(text.substring(currentLineStart)); + return result; + } + void write(int column, String text) { + const int minColumnWidth = 10; var lines = text.split('\n'); + if (column == columnWidths.length && maxLineLength != null) { + var wrappedLines = []; + var start = 0; + columnWidths.sublist(0, column).forEach((int width) => start += width); + for (var line in lines) { + wrappedLines + .addAll(wrap(line, max(maxLineLength - start, minColumnWidth))); + } + lines = wrappedLines; + } // Strip leading and trailing empty lines. while (lines.length > 0 && lines[0].trim() == '') { @@ -261,3 +310,20 @@ String padRight(String source, int length) { return result.toString(); } + +bool isWhitespace(String text, int index) { + final int rune = text.codeUnitAt(index); + return ((rune >= 0x0009 && rune <= 0x000D) || + rune == 0x0020 || + rune == 0x0085 || + rune == 0x00A0 || + rune == 0x1680 || + rune == 0x180E || + (rune >= 0x2000 && rune <= 0x200A) || + rune == 0x2028 || + rune == 0x2029 || + rune == 0x202F || + rune == 0x205F || + rune == 0x3000 || + rune == 0xFEFF); +} diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 48a33ddd..3bd228c1 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -314,6 +314,102 @@ void main() { '''); }); + test("help strings are not wrapped if maxLineLength is null", () { + var parser = new ArgParser(maxLineLength: null); + parser.addFlag('long', + help: 'The flag with a really long help text that will not ' + 'be wrapped.'); + validateUsage(parser, ''' + --[no-]long The flag with a really long help text that will not be wrapped. + '''); + }); + + test("help strings are wrapped properly when maxLineLength is specified", + () { + var parser = new ArgParser(maxLineLength: 60); + parser.addFlag('long', + help: 'The flag with a really long help text that will be wrapped.'); + parser.addFlag('longNewline', + help: 'The flag with a really long help text and newlines\n\nthat ' + 'will still be wrapped because it is really long.'); + parser.addFlag('solid', + help: + 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); + parser.addFlag('small1', help: ' a '); + parser.addFlag('small2', help: ' a'); + parser.addFlag('small3', help: 'a '); + validateUsage(parser, ''' + --[no-]long The flag with a really long help text + that will be wrapped. + + --[no-]longNewline The flag with a really long help text + and newlines + + that will still be wrapped because it + is really long. + + --[no-]solid The-flag-with-no-whitespace-that-will- + be-wrapped-by-splitting-a-word. + + --[no-]small1 a + --[no-]small2 a + --[no-]small3 a + '''); + }); + + test( + "help strings are wrapped with at 10 chars when maxHelpLineLength is " + "smaller than available space", () { + var parser = new ArgParser(maxLineLength: 1); + parser.addFlag('long', + help: 'The flag with a really long help text that will be wrapped.'); + parser.addFlag('longNewline', + help: + 'The flag with a really long help text and newlines\n\nthat will ' + 'still be wrapped because it is really long.'); + parser.addFlag('solid', + help: + 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); + parser.addFlag('small1', help: ' a '); + parser.addFlag('small2', help: ' a'); + parser.addFlag('small3', help: 'a '); + validateUsage(parser, ''' + --[no-]long The flag + with a + really + long help + text that + will be + wrapped. + + --[no-]longNewline The flag + with a + really + long help + text and + newlines + + that will + still be + wrapped + because it + is really + long. + + --[no-]solid The-flag-w + ith-no-whi + tespace-th + at-will-be + -wrapped-b + y-splittin + g-a-word. + + --[no-]small1 a + --[no-]small2 a + --[no-]small3 a + '''); + }); + group("separators", () { test("separates options where it's placed", () { var parser = new ArgParser(); From 22995a51540fe0dddeaea4571f8d6c43336dcdba Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Thu, 2 Aug 2018 11:27:51 -0700 Subject: [PATCH 150/263] Review changes --- pkgs/args/lib/src/usage.dart | 29 ++++++------ pkgs/args/test/usage_test.dart | 82 +++++++++++++++++++++------------- 2 files changed, 68 insertions(+), 43 deletions(-) diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 9898b44d..cc84e6cb 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -176,11 +176,14 @@ class Usage { numHelpLines = 0; } - /// Wrap a single line of text into lines no longer than length. - /// Try to split at whitespace, but if that's not good enough to keep it - /// under the length, then split in the middle of a word. - List wrap(String text, int length) { - assert(length > 0, 'Wrap length must be larger than zero.'); + // Wrap a single line of text into lines no longer than maxLineLength, + // starting at the "start" column. + // Try to split at whitespace, but if that's not good enough to keep it + // under the maxLineLength, then split in the middle of a word. + List _wrap(String text, int start) { + assert(start >= 0); + const int minColumnWidth = 10; + final int length = max(maxLineLength - start, minColumnWidth); text = text.trim(); if (text.length <= length) { return [text]; @@ -210,15 +213,15 @@ class Usage { } void write(int column, String text) { - const int minColumnWidth = 10; var lines = text.split('\n'); + // If we are writing the last column, word wrap it to fit. if (column == columnWidths.length && maxLineLength != null) { var wrappedLines = []; - var start = 0; - columnWidths.sublist(0, column).forEach((int width) => start += width); + int start = columnWidths + .sublist(0, column) + .reduce((int start, int width) => start += width); for (var line in lines) { - wrappedLines - .addAll(wrap(line, max(maxLineLength - start, minColumnWidth))); + wrappedLines.addAll(_wrap(line, start)); } lines = wrappedLines; } @@ -313,17 +316,17 @@ String padRight(String source, int length) { bool isWhitespace(String text, int index) { final int rune = text.codeUnitAt(index); - return ((rune >= 0x0009 && rune <= 0x000D) || + return rune >= 0x0009 && rune <= 0x000D || rune == 0x0020 || rune == 0x0085 || rune == 0x00A0 || rune == 0x1680 || rune == 0x180E || - (rune >= 0x2000 && rune <= 0x200A) || + rune >= 0x2000 && rune <= 0x200A || rune == 0x2028 || rune == 0x2029 || rune == 0x202F || rune == 0x205F || rune == 0x3000 || - rune == 0xFEFF); + rune == 0xFEFF; } diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 3bd228c1..d7ee3dc1 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -370,43 +370,65 @@ void main() { parser.addFlag('solid', help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); + parser.addFlag('longWhitespace', + help: ' The flag with a really long help text and whitespace at the start.'); + parser.addFlag('longTrailspace', + help: 'The flag with a really long help text and whitespace at the end. '); parser.addFlag('small1', help: ' a '); parser.addFlag('small2', help: ' a'); parser.addFlag('small3', help: 'a '); validateUsage(parser, ''' - --[no-]long The flag - with a - really - long help - text that - will be - wrapped. + --[no-]long The flag + with a + really + long help + text that + will be + wrapped. - --[no-]longNewline The flag - with a - really - long help - text and - newlines - - that will - still be - wrapped - because it - is really - long. + --[no-]longNewline The flag + with a + really + long help + text and + newlines + + that will + still be + wrapped + because it + is really + long. - --[no-]solid The-flag-w - ith-no-whi - tespace-th - at-will-be - -wrapped-b - y-splittin - g-a-word. + --[no-]solid The-flag-w + ith-no-whi + tespace-th + at-will-be + -wrapped-b + y-splittin + g-a-word. - --[no-]small1 a - --[no-]small2 a - --[no-]small3 a + --[no-]longWhitespace The flag + with a + really + long help + text and + whitespace + at the + start. + + --[no-]longTrailspace The flag + with a + really + long help + text and + whitespace + at the + end. + + --[no-]small1 a + --[no-]small2 a + --[no-]small3 a '''); }); From 99be7b09e755c79dbc8577b4a93863b68c75d210 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 2 Aug 2018 14:47:04 -0700 Subject: [PATCH 151/263] Tweak the new word wrapping and get ready to release it. - Renamed "maxLineLength" to "usageLineLength". In the context of ArgParser, the former makes it sound like it's the argument line itself that it applies to. - Some style tweaks. - Remove some ooooold workarounds. Strings support padRight() now. - Run dartfmt on it. - Bump the version number and update CHANGELOG. --- pkgs/args/CHANGELOG.md | 4 + pkgs/args/lib/src/allow_anything_parser.dart | 2 +- pkgs/args/lib/src/arg_parser.dart | 10 +-- pkgs/args/lib/src/usage.dart | 87 +++++++++----------- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 18 ++-- 6 files changed, 61 insertions(+), 62 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index d418df97..f1e947ce 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.4.5 + +* Add `usageLineLength` to control word wrapping usage text. + ## 1.4.4 * Set max SDK version to `<3.0.0`, and adjust other dependencies. diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 34a0b191..31215ba0 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -13,7 +13,7 @@ class AllowAnythingParser implements ArgParser { Map get commands => const {}; bool get allowTrailingOptions => false; bool get allowsAnything => true; - int get maxLineLength => null; + int get usageLineLength => null; ArgParser addCommand(String name, [ArgParser parser]) { throw new UnsupportedError( diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index a25a8b81..cdc54e60 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -40,7 +40,7 @@ class ArgParser { /// there is no whitespace at which to split). /// /// If null (the default), help messages are not wrapped. - final int maxLineLength; + final int usageLineLength; /// Whether or not this parser treats unrecognized options as non-option /// arguments. @@ -52,10 +52,10 @@ class ArgParser { /// flags and options that appear after positional arguments. If it's `false`, /// the parser stops parsing as soon as it finds an argument that is neither /// an option nor a command. - factory ArgParser({bool allowTrailingOptions: true, int maxLineLength}) => + factory ArgParser({bool allowTrailingOptions: true, int usageLineLength}) => new ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions, - maxLineLength: maxLineLength); + usageLineLength: usageLineLength); /// Creates a new ArgParser that treats *all input* as non-option arguments. /// @@ -66,7 +66,7 @@ class ArgParser { factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, - {bool allowTrailingOptions: true, this.maxLineLength}) + {bool allowTrailingOptions: true, this.usageLineLength}) : this._options = options, this.options = new UnmodifiableMapView(options), this._commands = commands, @@ -329,7 +329,7 @@ class ArgParser { /// /// This is basically the help text shown on the command line. String get usage { - return new Usage(_optionsAndSeparators, maxLineLength: maxLineLength) + return new Usage(_optionsAndSeparators, lineLength: usageLineLength) .generate(); } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index cc84e6cb..1135bb52 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -2,7 +2,7 @@ // 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. -import 'dart:math'; +import 'dart:math' as math; import '../args.dart'; @@ -18,7 +18,8 @@ import '../args.dart'; /// 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. class Usage { - static const NUM_COLUMNS = 3; // Abbreviation, long name, help. + /// Abbreviation, long name, help. + static const _columnCount = 3; /// A list of the [Option]s intermingled with [String] separators. final List optionsAndSeparators; @@ -54,9 +55,9 @@ class Usage { /// The horizontal character position at which help text is wrapped. Help that /// extends past this column will be wrapped at the nearest whitespace (or /// truncated if there is no available whitespace). - final int maxLineLength; + final int lineLength; - Usage(this.optionsAndSeparators, {this.maxLineLength}); + Usage(this.optionsAndSeparators, {this.lineLength}); /// Generates a string displaying usage information for the defined options. /// This is basically the help text shown on the command line. @@ -152,15 +153,15 @@ class Usage { if (option.hide) continue; // Make room in the first column if there are abbreviations. - abbr = max(abbr, getAbbreviation(option).length); + abbr = math.max(abbr, getAbbreviation(option).length); // Make room for the option. - title = max(title, getLongOption(option).length); + title = math.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(option, allowed).length); + title = math.max(title, getAllowedTitle(option, allowed).length); } } } @@ -176,38 +177,40 @@ class Usage { numHelpLines = 0; } - // Wrap a single line of text into lines no longer than maxLineLength, - // starting at the "start" column. - // Try to split at whitespace, but if that's not good enough to keep it - // under the maxLineLength, then split in the middle of a word. + /// Wraps a single line of text into lines no longer than [lineLength], + /// starting at the [start] column. + /// + /// Tries to split at whitespace, but if that's not good enough to keep it + /// under the limit, then splits in the middle of a word. List _wrap(String text, int start) { assert(start >= 0); - const int minColumnWidth = 10; - final int length = max(maxLineLength - start, minColumnWidth); + text = text.trim(); - if (text.length <= length) { - return [text]; - } + + var length = math.max(lineLength - start, 10); + if (text.length <= length) return [text]; + var result = []; - int currentLineStart = 0; + var currentLineStart = 0; int lastWhitespace; - for (int i = 0; i < text.length; ++i) { - if (isWhitespace(text, i)) { - lastWhitespace = i; - } - if ((i - currentLineStart) >= length) { + for (var i = 0; i < text.length; ++i) { + if (isWhitespace(text, i)) lastWhitespace = i; + + if (i - currentLineStart >= length) { // Back up to the last whitespace, unless there wasn't any, in which // case we just split where we are. - if (lastWhitespace != null) { - i = lastWhitespace; - } + if (lastWhitespace != null) i = lastWhitespace; + result.add(text.substring(currentLineStart, i)); + // Skip any intervening whitespace. while (isWhitespace(text, i) && i < text.length) i++; + currentLineStart = i; lastWhitespace = null; } } + result.add(text.substring(currentLineStart)); return result; } @@ -215,14 +218,16 @@ class Usage { void write(int column, String text) { var lines = text.split('\n'); // If we are writing the last column, word wrap it to fit. - if (column == columnWidths.length && maxLineLength != null) { + if (column == columnWidths.length && lineLength != null) { var wrappedLines = []; - int start = columnWidths + var start = columnWidths .sublist(0, column) - .reduce((int start, int width) => start += width); + .reduce((start, width) => start += width); + for (var line in lines) { wrappedLines.addAll(_wrap(line, start)); } + lines = wrappedLines; } @@ -250,31 +255,31 @@ class Usage { // 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.write(padRight('', columnWidths[currentColumn])); + if (currentColumn < _columnCount - 1) { + buffer.write(' ' * columnWidths[currentColumn]); } else { buffer.write('\n'); } - currentColumn = (currentColumn + 1) % NUM_COLUMNS; + currentColumn = (currentColumn + 1) % _columnCount; } if (column < columnWidths.length) { // Fixed-size column, so pad it. - buffer.write(padRight(text, columnWidths[column])); + buffer.write(text.padRight(columnWidths[column])); } else { // The last column, so just write it. buffer.write(text); } // Advance to the next column. - currentColumn = (currentColumn + 1) % NUM_COLUMNS; + currentColumn = (currentColumn + 1) % _columnCount; // If we reached the last column, we need to wrap to the next line. - if (column == NUM_COLUMNS - 1) newlinesNeeded++; + if (column == _columnCount - 1) newlinesNeeded++; // Keep track of how many consecutive lines we've written in the last // column. - if (column == NUM_COLUMNS - 1) { + if (column == _columnCount - 1) { numHelpLines++; } else { numHelpLines = 0; @@ -302,18 +307,6 @@ class Usage { } } -/// Pads [source] to [length] by adding spaces at the end. -String padRight(String source, int length) { - final result = new StringBuffer(); - result.write(source); - - while (result.length < length) { - result.write(' '); - } - - return result.toString(); -} - bool isWhitespace(String text, int index) { final int rune = text.codeUnitAt(index); return rune >= 0x0009 && rune <= 0x000D || diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 80b6033a..27583bf8 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.4 +version: 1.4.5 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index d7ee3dc1..c556b6d5 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -314,8 +314,8 @@ void main() { '''); }); - test("help strings are not wrapped if maxLineLength is null", () { - var parser = new ArgParser(maxLineLength: null); + test("help strings are not wrapped if usageLineLength is null", () { + var parser = new ArgParser(usageLineLength: null); parser.addFlag('long', help: 'The flag with a really long help text that will not ' 'be wrapped.'); @@ -324,9 +324,9 @@ void main() { '''); }); - test("help strings are wrapped properly when maxLineLength is specified", + test("help strings are wrapped properly when usageLineLength is specified", () { - var parser = new ArgParser(maxLineLength: 60); + var parser = new ArgParser(usageLineLength: 60); parser.addFlag('long', help: 'The flag with a really long help text that will be wrapped.'); parser.addFlag('longNewline', @@ -358,9 +358,9 @@ void main() { }); test( - "help strings are wrapped with at 10 chars when maxHelpLineLength is " + "help strings are wrapped with at 10 chars when usageLineLength is " "smaller than available space", () { - var parser = new ArgParser(maxLineLength: 1); + var parser = new ArgParser(usageLineLength: 1); parser.addFlag('long', help: 'The flag with a really long help text that will be wrapped.'); parser.addFlag('longNewline', @@ -371,9 +371,11 @@ void main() { help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); parser.addFlag('longWhitespace', - help: ' The flag with a really long help text and whitespace at the start.'); + help: + ' The flag with a really long help text and whitespace at the start.'); parser.addFlag('longTrailspace', - help: 'The flag with a really long help text and whitespace at the end. '); + help: + 'The flag with a really long help text and whitespace at the end. '); parser.addFlag('small1', help: ' a '); parser.addFlag('small2', help: ' a'); parser.addFlag('small3', help: 'a '); From be4b4fba6ed497c114c0596c100370f85ad1a885 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 2 Aug 2018 14:54:18 -0700 Subject: [PATCH 152/263] Document isWhitespace(). --- pkgs/args/lib/src/usage.dart | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 1135bb52..a9ca72fa 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -194,7 +194,7 @@ class Usage { var currentLineStart = 0; int lastWhitespace; for (var i = 0; i < text.length; ++i) { - if (isWhitespace(text, i)) lastWhitespace = i; + if (_isWhitespace(text, i)) lastWhitespace = i; if (i - currentLineStart >= length) { // Back up to the last whitespace, unless there wasn't any, in which @@ -204,7 +204,7 @@ class Usage { result.add(text.substring(currentLineStart, i)); // Skip any intervening whitespace. - while (isWhitespace(text, i) && i < text.length) i++; + while (_isWhitespace(text, i) && i < text.length) i++; currentLineStart = i; lastWhitespace = null; @@ -307,8 +307,12 @@ class Usage { } } -bool isWhitespace(String text, int index) { - final int rune = text.codeUnitAt(index); +/// Returns true if the code unit at [index] in [text] is a whitespace +/// character. +/// +/// Based on: https://en.wikipedia.org/wiki/Whitespace_character#Unicode +bool _isWhitespace(String text, int index) { + var rune = text.codeUnitAt(index); return rune >= 0x0009 && rune <= 0x000D || rune == 0x0020 || rune == 0x0085 || From 2364fe7225ee68aa765fc9a17fd7fba1872add44 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 2 Aug 2018 15:18:25 -0700 Subject: [PATCH 153/263] Incorporate review feedback. --- pkgs/args/CHANGELOG.md | 2 +- pkgs/args/lib/src/usage.dart | 2 +- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index f1e947ce..2a335e7d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.4.5 +## 1.5.0 * Add `usageLineLength` to control word wrapping usage text. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index a9ca72fa..8a1e764f 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -183,6 +183,7 @@ class Usage { /// Tries to split at whitespace, but if that's not good enough to keep it /// under the limit, then splits in the middle of a word. List _wrap(String text, int start) { + assert(lineLength != null, "Should wrap when given a length."); assert(start >= 0); text = text.trim(); @@ -316,7 +317,6 @@ bool _isWhitespace(String text, int index) { return rune >= 0x0009 && rune <= 0x000D || rune == 0x0020 || rune == 0x0085 || - rune == 0x00A0 || rune == 0x1680 || rune == 0x180E || rune >= 0x2000 && rune <= 0x200A || diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 27583bf8..3f52f942 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.4.5 +version: 1.5.0 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 559f5cb8677f9debe34454c47ea8d8de89ae7dcc Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Fri, 3 Aug 2018 14:47:53 -0700 Subject: [PATCH 154/263] Add support for wrapping command descriptions and other help text. --- pkgs/args/lib/command_runner.dart | 54 +++-- pkgs/args/lib/src/usage.dart | 63 +---- pkgs/args/lib/src/utils.dart | 140 +++++++++++ pkgs/args/test/args_test.dart | 2 +- pkgs/args/test/command_parse_test.dart | 2 +- pkgs/args/test/command_runner_test.dart | 55 ++++- pkgs/args/test/command_test.dart | 54 ++++- pkgs/args/test/parse_test.dart | 2 +- .../args/test/{utils.dart => test_utils.dart} | 63 +++++ pkgs/args/test/usage_test.dart | 126 +++++----- pkgs/args/test/utils_test.dart | 217 ++++++++++++++++++ 11 files changed, 628 insertions(+), 150 deletions(-) rename pkgs/args/test/{utils.dart => test_utils.dart} (66%) create mode 100644 pkgs/args/test/utils_test.dart diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 3135dbf5..62f7fe7a 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -39,7 +39,7 @@ class CommandRunner { /// /// This includes usage for the global arguments as well as a list of /// top-level commands. - String get usage => "$description\n\n$_usageWithoutDescription"; + String get usage => _wrap("$description\n\n") + _usageWithoutDescription; /// An optional footer for [usage]. /// @@ -49,17 +49,20 @@ class CommandRunner { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { + const usagePrefix = "Usage:"; var usage = ''' -Usage: $invocation +$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)} -Global options: +${_wrap('Global options:')} ${argParser.usage} -${_getCommandUsage(_commands)} +${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)} -Run "$executableName help " for more information about a command.'''; +${_wrap('Run "$executableName help " for more information about a command.')}'''; - if (usageFooter != null) usage += "\n$usageFooter"; + if (usageFooter != null) { + usage += "\n${_wrap(usageFooter)}"; + } return usage; } @@ -193,6 +196,9 @@ Run "$executableName help " for more information about a command.'''; return (await command.run()) as T; } + + String _wrap(String text, {int hangingIndent}) => wrapText(text, + length: argParser.usageLineLength, hangingIndent: hangingIndent); } /// A single command. @@ -277,7 +283,7 @@ abstract class Command { /// /// This includes usage for the command's arguments as well as a list of /// subcommands, if there are any. - String get usage => "$description\n\n$_usageWithoutDescription"; + String get usage => _wrap("$description\n\n") + _usageWithoutDescription; /// An optional footer for [usage]. /// @@ -285,23 +291,36 @@ abstract class Command { /// added to the end of [usage]. String get usageFooter => null; + String _wrap(String text, {int hangingIndent}) { + return wrapText(text, + length: argParser.usageLineLength, hangingIndent: hangingIndent); + } + /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { + final length = argParser.usageLineLength; + const usagePrefix = "Usage: "; var buffer = new StringBuffer() - ..writeln('Usage: $invocation') + ..writeln( + usagePrefix + _wrap(invocation, hangingIndent: usagePrefix.length)) ..writeln(argParser.usage); if (_subcommands.isNotEmpty) { buffer.writeln(); - buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true)); + buffer.writeln(_getCommandUsage( + _subcommands, + isSubcommand: true, + lineLength: length, + )); } buffer.writeln(); - buffer.write('Run "${runner.executableName} help" to see global options.'); + buffer.write( + _wrap('Run "${runner.executableName} help" to see global options.')); if (usageFooter != null) { buffer.writeln(); - buffer.write(usageFooter); + buffer.write(_wrap(usageFooter)); } return buffer.toString(); @@ -355,7 +374,8 @@ abstract class Command { /// The return value is wrapped in a `Future` if necessary and returned by /// [CommandRunner.runCommand]. FutureOr run() { - throw new UnimplementedError("Leaf command $this must implement run()."); + throw new UnimplementedError( + _wrap("Leaf command $this must implement run().")); } /// Adds [Command] as a subcommand of this. @@ -376,7 +396,7 @@ abstract class Command { /// Throws a [UsageException] with [message]. void usageException(String message) => - throw new UsageException(message, _usageWithoutDescription); + throw new UsageException(_wrap(message), _usageWithoutDescription); } /// Returns a string representation of [commands] fit for use in a usage string. @@ -384,7 +404,7 @@ abstract class Command { /// [isSubcommand] indicates whether the commands should be called "commands" or /// "subcommands". String _getCommandUsage(Map commands, - {bool isSubcommand: false}) { + {bool isSubcommand: false, int lineLength}) { // Don't include aliases. var names = commands.keys.where((name) => !commands[name].aliases.contains(name)); @@ -400,13 +420,15 @@ String _getCommandUsage(Map commands, var buffer = new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); for (var name in names) { - var lines = commands[name].summary.split("\n"); + var columnStart = length + 5; + var lines = wrapTextAsLines(commands[name].summary, + start: columnStart, length: lineLength); buffer.writeln(); buffer.write(' ${padRight(name, length)} ${lines.first}'); for (var line in lines.skip(1)) { buffer.writeln(); - buffer.write(' ' * (length + 5)); + buffer.write(' ' * columnStart); buffer.write(line); } } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 8a1e764f..01775ffc 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -5,6 +5,7 @@ import 'dart:math' as math; import '../args.dart'; +import 'utils.dart'; /// Takes an [ArgParser] and generates a string of usage (i.e. help) text for /// its defined options. @@ -177,45 +178,6 @@ class Usage { numHelpLines = 0; } - /// Wraps a single line of text into lines no longer than [lineLength], - /// starting at the [start] column. - /// - /// Tries to split at whitespace, but if that's not good enough to keep it - /// under the limit, then splits in the middle of a word. - List _wrap(String text, int start) { - assert(lineLength != null, "Should wrap when given a length."); - assert(start >= 0); - - text = text.trim(); - - var length = math.max(lineLength - start, 10); - if (text.length <= length) return [text]; - - var result = []; - var currentLineStart = 0; - int lastWhitespace; - for (var i = 0; i < text.length; ++i) { - if (_isWhitespace(text, i)) lastWhitespace = i; - - if (i - currentLineStart >= length) { - // Back up to the last whitespace, unless there wasn't any, in which - // case we just split where we are. - if (lastWhitespace != null) i = lastWhitespace; - - result.add(text.substring(currentLineStart, i)); - - // Skip any intervening whitespace. - while (_isWhitespace(text, i) && i < text.length) i++; - - currentLineStart = i; - lastWhitespace = null; - } - } - - result.add(text.substring(currentLineStart)); - return result; - } - void write(int column, String text) { var lines = text.split('\n'); // If we are writing the last column, word wrap it to fit. @@ -226,7 +188,8 @@ class Usage { .reduce((start, width) => start += width); for (var line in lines) { - wrappedLines.addAll(_wrap(line, start)); + wrappedLines + .addAll(wrapTextAsLines(line, start: start, length: lineLength)); } lines = wrappedLines; @@ -307,23 +270,3 @@ class Usage { return allowedBuffer.toString(); } } - -/// Returns true if the code unit at [index] in [text] is a whitespace -/// character. -/// -/// Based on: https://en.wikipedia.org/wiki/Whitespace_character#Unicode -bool _isWhitespace(String text, int index) { - var rune = text.codeUnitAt(index); - return rune >= 0x0009 && rune <= 0x000D || - rune == 0x0020 || - rune == 0x0085 || - rune == 0x1680 || - rune == 0x180E || - rune >= 0x2000 && rune <= 0x200A || - rune == 0x2028 || - rune == 0x2029 || - rune == 0x202F || - rune == 0x205F || - rune == 0x3000 || - rune == 0xFEFF; -} diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index 2469faca..9c3972f2 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -1,7 +1,147 @@ // Copyright (c) 2014, 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. +import 'dart:math' as math; /// Pads [source] to [length] by adding spaces at the end. String padRight(String source, int length) => source + ' ' * (length - source.length); + +/// Wraps a block of text into lines no longer than [length]. +/// +/// Tries to split at whitespace, but if that's not good enough to keep it +/// under the limit, then it splits in the middle of a word. +/// +/// Preserves indentation (leading whitespace) for each line (delimited by '\n') +/// in the input, and will indent wrapped lines the same amount. +/// +/// If [hangingIndent] is supplied, then that many spaces will be added to each +/// line, except for the first line. This is useful for flowing text with a +/// heading prefix (e.g. "Usage: "): +/// +/// ```dart +/// var prefix = "Usage: "; +/// print(prefix + wrapText(invocation, hangingIndent: prefix.length, length: 40)); +/// ``` +/// +/// yields: +/// ``` +/// Usage: app main_command +/// [arguments] +/// ``` +/// +/// If [length] is not specified, then no wrapping occurs, and the original +/// [text] is returned unchanged. +String wrapText(String text, {int length, int hangingIndent}) { + if (length == null) return text; + hangingIndent ??= 0; + var splitText = text.split('\n'); + var result = []; + for (var line in splitText) { + var trimmedText = line.trimLeft(); + final leadingWhitespace = + line.substring(0, line.length - trimmedText.length); + var notIndented; + if (hangingIndent != 0) { + // When we have a hanging indent, we want to wrap the first line at one + // width, and the rest at another (offset by hangingIndent), so we wrap + // them twice and recombine. + final firstLineWrap = wrapTextAsLines( + trimmedText, + length: length - leadingWhitespace.length, + ); + notIndented = [firstLineWrap.removeAt(0)]; + trimmedText = trimmedText.substring(notIndented[0].length).trimLeft(); + if (firstLineWrap.isNotEmpty) { + notIndented.addAll(wrapTextAsLines( + trimmedText, + length: length - leadingWhitespace.length - hangingIndent, + )); + } + } else { + notIndented = wrapTextAsLines( + trimmedText, + length: length - leadingWhitespace.length, + ); + } + String hangingIndentString; + result.addAll(notIndented.map( + (String line) { + // Don't return any lines with just whitespace on them. + if (line.isEmpty) return ""; + var result = "${hangingIndentString ?? ""}$leadingWhitespace$line"; + hangingIndentString ??= " " * hangingIndent; + return result; + }, + )); + } + return result.join('\n'); +} + +/// Wraps a block of text into lines no longer than [length], +/// starting at the [start] column, and returns the result as a list of strings. +/// +/// Tries to split at whitespace, but if that's not good enough to keep it +/// under the limit, then splits in the middle of a word. Preserves embedded +/// newlines, but not indentation (it trims whitespace from each line). +/// +/// If [length] is not specified, then no wrapping occurs, and the original +/// [text] is returned after splitting it on newlines. Whitespace is not trimmed +/// in this case. +List wrapTextAsLines(String text, {int start = 0, int length}) { + assert(start >= 0); + + /// Returns true if the code unit at [index] in [text] is a whitespace + /// character. + /// + /// Based on: https://en.wikipedia.org/wiki/Whitespace_character#Unicode + bool isWhitespace(String text, int index) { + final rune = text.codeUnitAt(index); + return rune >= 0x0009 && rune <= 0x000D || + rune == 0x0020 || + rune == 0x0085 || + rune == 0x1680 || + rune == 0x180E || + rune >= 0x2000 && rune <= 0x200A || + rune == 0x2028 || + rune == 0x2029 || + rune == 0x202F || + rune == 0x205F || + rune == 0x3000 || + rune == 0xFEFF; + } + + if (length == null) return text.split('\n'); + + final result = []; + final effectiveLength = math.max(length - start, 10); + for (var line in text.split('\n')) { + line = line.trim(); + if (line.length <= effectiveLength) { + result.add(line); + continue; + } + + var currentLineStart = 0; + var lastWhitespace; + for (var i = 0; i < line.length; ++i) { + if (isWhitespace(line, i)) lastWhitespace = i; + + if (i - currentLineStart >= effectiveLength) { + // Back up to the last whitespace, unless there wasn't any, in which + // case we just split where we are. + if (lastWhitespace != null) i = lastWhitespace; + + result.add(line.substring(currentLineStart, i).trim()); + + // Skip any intervening whitespace. + while (isWhitespace(line, i) && i < line.length) i++; + + currentLineStart = i; + lastWhitespace = null; + } + } + result.add(line.substring(currentLineStart).trim()); + } + return result; +} diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 8741ad87..d738f008 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; import 'package:args/args.dart'; -import 'utils.dart'; +import 'test_utils.dart'; void main() { group('ArgParser.addFlag()', () { diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index 269ed017..a5a1f568 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; import 'package:args/args.dart'; -import 'utils.dart'; +import 'test_utils.dart'; void main() { group('ArgParser.addCommand()', () { diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index ebb2f616..07999278 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -5,7 +5,7 @@ import 'package:args/command_runner.dart'; import 'package:test/test.dart'; -import 'utils.dart'; +import 'test_utils.dart'; const _DEFAULT_USAGE = """ Usage: test [arguments] @@ -330,6 +330,59 @@ Also, footer!""")); }); }); + group("with a footer and wrapping", () { + setUp(() { + runner = new CommandRunnerWithFooterAndWrapping( + "test", "A test command runner."); + }); + test("includes the footer in the usage string", () { + expect(runner.usage, equals(""" +A test command runner. + +Usage: test [arguments] + +Global options: +-h, --help Print this usage + information. + +Available commands: + help Display help information for + test. + +Run "test help " for more +information about a command. +LONG footer! This is a long footer, so +we can check wrapping on long footer +messages. + +And make sure that they preserve +newlines properly.""")); + }); + + test("includes the footer in usage errors", () { + expect(runner.run(["--bad"]), + throwsUsageException('Could not find an option named "bad".', """ +Usage: test [arguments] + +Global options: +-h, --help Print this usage + information. + +Available commands: + help Display help information for + test. + +Run "test help " for more +information about a command. +LONG footer! This is a long footer, so +we can check wrapping on long footer +messages. + +And make sure that they preserve +newlines properly.""")); + }); + }); + group("throws a useful error when", () { test("arg parsing fails", () { expect( diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index c4f29d43..4c99fa2e 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -4,7 +4,7 @@ import 'package:args/command_runner.dart'; import 'package:test/test.dart'; -import 'utils.dart'; +import 'test_utils.dart'; void main() { var foo; @@ -87,6 +87,58 @@ Available subcommands: Run "test help" to see global options.""")); }); + + test("wraps long command descriptions with subcommands", () { + var wrapping = new WrappingCommand(); + + // Make sure [Command.runner] is set up. + new CommandRunner("longtest", "A long-lined test command runner.") + .addCommand(wrapping); + + wrapping.addSubcommand(new LongCommand()); + expect(wrapping.usage, equals(""" +This command overrides the argParser so +that it will wrap long lines. + +Usage: longtest wrapping + [arguments] +-h, --help Print this usage + information. + +Available subcommands: + long This command has a long + description that needs to be + wrapped sometimes. + +Run "longtest help" to see global +options.""")); + }); + + test("wraps long command descriptions", () { + var longCommand = new LongCommand(); + + // Make sure [Command.runner] is set up. + new CommandRunner("longtest", "A long-lined test command runner.") + .addCommand(longCommand); + + expect(longCommand.usage, equals(""" +This command has a long description that +needs to be wrapped sometimes. +It has embedded newlines, + and indented lines that also need + to be wrapped and have their + indentation preserved. +0123456789012345678901234567890123456789 +0123456789012345678901234567890123456789 +01234567890123456789 + +Usage: longtest long [arguments] +-h, --help Print this usage + information. + +Run "longtest help" to see global +options.""")); + }); }); test("usageException splits up the message and usage", () { diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index d212cf02..3fa20653 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -4,7 +4,7 @@ import 'package:test/test.dart'; import 'package:args/args.dart'; -import 'utils.dart'; +import 'test_utils.dart'; void main() { group('ArgParser.parse()', () { diff --git a/pkgs/args/test/utils.dart b/pkgs/args/test/test_utils.dart similarity index 66% rename from pkgs/args/test/utils.dart rename to pkgs/args/test/test_utils.dart index d9e4f700..99a6ba0e 100644 --- a/pkgs/args/test/utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -16,6 +16,20 @@ class CommandRunnerWithFooter extends CommandRunner { : super(executableName, description); } +class CommandRunnerWithFooterAndWrapping extends CommandRunner { + @override + String get usageFooter => "LONG footer! " + "This is a long footer, so we can check wrapping on long footer messages.\n\n" + "And make sure that they preserve newlines properly."; + + @override + ArgParser get argParser => _argParser; + final _argParser = new ArgParser(usageLineLength: 40); + + CommandRunnerWithFooterAndWrapping(String executableName, String description) + : super(executableName, description); +} + class FooCommand extends Command { var hasRun = false; @@ -80,6 +94,55 @@ class MultilineCommand extends Command { } } +class WrappingCommand extends Command { + var hasRun = false; + + @override + ArgParser get argParser => _argParser; + final _argParser = new ArgParser(usageLineLength: 40); + + @override + final name = "wrapping"; + + @override + final description = + "This command overrides the argParser so that it will wrap long lines."; + + @override + final takesArguments = false; + + @override + void run() { + hasRun = true; + } +} + +class LongCommand extends Command { + var hasRun = false; + + @override + ArgParser get argParser => _argParser; + final _argParser = new ArgParser(usageLineLength: 40); + + @override + final name = "long"; + + @override + final description = "This command has a long description that needs to be " + "wrapped sometimes.\nIt has embedded newlines,\n" + " and indented lines that also need to be wrapped and have their " + "indentation preserved.\n" + + ("0123456789" * 10); + + @override + final takesArguments = false; + + @override + void run() { + hasRun = true; + } +} + class MultilineSummaryCommand extends MultilineCommand { @override String get summary => description; diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index c556b6d5..11b0f348 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -335,25 +335,37 @@ void main() { parser.addFlag('solid', help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); + parser.addFlag('longWhitespace', + help: + ' The flag with a really long help text and whitespace at the start.'); + parser.addFlag('longTrailspace', + help: + 'The flag with a really long help text and whitespace at the end. '); parser.addFlag('small1', help: ' a '); parser.addFlag('small2', help: ' a'); parser.addFlag('small3', help: 'a '); validateUsage(parser, ''' - --[no-]long The flag with a really long help text - that will be wrapped. - - --[no-]longNewline The flag with a really long help text - and newlines - - that will still be wrapped because it - is really long. - - --[no-]solid The-flag-with-no-whitespace-that-will- - be-wrapped-by-splitting-a-word. - - --[no-]small1 a - --[no-]small2 a - --[no-]small3 a + --[no-]long The flag with a really long help + text that will be wrapped. + + --[no-]longNewline The flag with a really long help + text and newlines + + that will still be wrapped because + it is really long. + + --[no-]solid The-flag-with-no-whitespace-that-wi + ll-be-wrapped-by-splitting-a-word. + + --[no-]longWhitespace The flag with a really long help + text and whitespace at the start. + + --[no-]longTrailspace The flag with a really long help + text and whitespace at the end. + + --[no-]small1 a + --[no-]small2 a + --[no-]small3 a '''); }); @@ -370,67 +382,43 @@ void main() { parser.addFlag('solid', help: 'The-flag-with-no-whitespace-that-will-be-wrapped-by-splitting-a-word.'); - parser.addFlag('longWhitespace', - help: - ' The flag with a really long help text and whitespace at the start.'); - parser.addFlag('longTrailspace', - help: - 'The flag with a really long help text and whitespace at the end. '); parser.addFlag('small1', help: ' a '); parser.addFlag('small2', help: ' a'); parser.addFlag('small3', help: 'a '); validateUsage(parser, ''' - --[no-]long The flag - with a - really - long help - text that - will be - wrapped. + --[no-]long The flag + with a + really + long help + text that + will be + wrapped. - --[no-]longNewline The flag - with a - really - long help - text and - newlines - - that will - still be - wrapped - because it - is really - long. - - --[no-]solid The-flag-w - ith-no-whi - tespace-th - at-will-be - -wrapped-b - y-splittin - g-a-word. - - --[no-]longWhitespace The flag - with a - really - long help - text and - whitespace - at the - start. + --[no-]longNewline The flag + with a + really + long help + text and + newlines + + that will + still be + wrapped + because it + is really + long. - --[no-]longTrailspace The flag - with a - really - long help - text and - whitespace - at the - end. + --[no-]solid The-flag-w + ith-no-whi + tespace-th + at-will-be + -wrapped-b + y-splittin + g-a-word. - --[no-]small1 a - --[no-]small2 a - --[no-]small3 a + --[no-]small1 a + --[no-]small2 a + --[no-]small3 a '''); }); diff --git a/pkgs/args/test/utils_test.dart b/pkgs/args/test/utils_test.dart new file mode 100644 index 00000000..7c6463ae --- /dev/null +++ b/pkgs/args/test/utils_test.dart @@ -0,0 +1,217 @@ +// Copyright (c) 2018, 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. + +import 'package:test/test.dart'; + +import '../lib/src/utils.dart'; + +const _lineLength = 40; +const _longLine = "This is a long line that needs to be wrapped."; +final _longLineWithNewlines = "This is a long line with newlines that\n" + "needs to be wrapped.\n\n" + + "0123456789" * 5; +final _indentedLongLineWithNewlines = + " This is an indented long line with newlines that\n" + "needs to be wrapped.\n\tAnd preserves tabs.\n \n " + + "0123456789" * 5; +const _shortLine = "Short line."; +const _indentedLongLine = " This is an indented long line that needs to be " + "wrapped and indentation preserved."; + +void main() { + group("padding", () { + test("can pad on the right.", () { + expect(padRight("foo", 6), equals("foo ")); + }); + }); + group("text wrapping", () { + test("doesn't wrap short lines.", () { + expect(wrapText(_shortLine, length: _lineLength), equals(_shortLine)); + }); + test("doesn't wrap at all if not given a length", () { + expect(wrapText(_longLine), equals(_longLine)); + }); + test("able to wrap long lines", () { + expect(wrapText(_longLine, length: _lineLength), equals(""" +This is a long line that needs to be +wrapped.""")); + }); + test("wrap long lines with no whitespace", () { + expect(wrapText("0123456789" * 5, length: _lineLength), equals(""" +0123456789012345678901234567890123456789 +0123456789""")); + }); + test("refuses to wrap to a column smaller than 10 characters", () { + expect(wrapText("$_longLine " + "0123456789" * 4, length: 1), equals(""" +This is a +long line +that needs +to be +wrapped. +0123456789 +0123456789 +0123456789 +0123456789""")); + }); + test("preserves indentation", () { + expect(wrapText(_indentedLongLine, length: _lineLength), equals(""" + This is an indented long line that + needs to be wrapped and indentation + preserved.""")); + }); + test("preserves indentation and stripping trailing whitespace", () { + expect(wrapText("$_indentedLongLine ", length: _lineLength), equals(""" + This is an indented long line that + needs to be wrapped and indentation + preserved.""")); + }); + test("wraps text with newlines", () { + expect(wrapText(_longLineWithNewlines, length: _lineLength), equals(""" +This is a long line with newlines that +needs to be wrapped. + +0123456789012345678901234567890123456789 +0123456789""")); + }); + test("preserves indentation in the presence of newlines", () { + expect(wrapText(_indentedLongLineWithNewlines, length: _lineLength), + equals(""" + This is an indented long line with + newlines that +needs to be wrapped. +\tAnd preserves tabs. + + 01234567890123456789012345678901234567 + 890123456789""")); + }); + test("removes trailing whitespace when wrapping", () { + expect(wrapText("$_longLine \t", length: _lineLength), equals(""" +This is a long line that needs to be +wrapped.""")); + }); + test("preserves trailing whitespace when not wrapping", () { + expect(wrapText("$_longLine \t"), equals("$_longLine \t")); + }); + test("honors hangingIndent parameter", () { + expect( + wrapText(_longLine, length: _lineLength, hangingIndent: 6), equals(""" +This is a long line that needs to be + wrapped.""")); + }); + test("handles hangingIndent with a single unwrapped line.", () { + expect(wrapText(_shortLine, length: _lineLength, hangingIndent: 6), + equals(""" +Short line.""")); + }); + test( + "handles hangingIndent with two unwrapped lines and the second is empty.", + () { + expect(wrapText("$_shortLine\n", length: _lineLength, hangingIndent: 6), + equals(""" +Short line. +""")); + }); + test("honors hangingIndent parameter on already indented line.", () { + expect(wrapText(_indentedLongLine, length: _lineLength, hangingIndent: 6), + equals(""" + This is an indented long line that + needs to be wrapped and + indentation preserved.""")); + }); + test("honors hangingIndent parameter on already indented line.", () { + expect( + wrapText(_indentedLongLineWithNewlines, + length: _lineLength, hangingIndent: 6), + equals(""" + This is an indented long line with + newlines that +needs to be wrapped. + And preserves tabs. + + 01234567890123456789012345678901234567 + 890123456789""")); + }); + }); + group("text wrapping as lines", () { + test("doesn't wrap short lines.", () { + expect(wrapTextAsLines(_shortLine, length: _lineLength), + equals([_shortLine])); + }); + test("doesn't wrap at all if not given a length", () { + expect(wrapTextAsLines(_longLine), equals([_longLine])); + }); + test("able to wrap long lines", () { + expect(wrapTextAsLines(_longLine, length: _lineLength), + equals(["This is a long line that needs to be", "wrapped."])); + }); + test("wrap long lines with no whitespace", () { + expect(wrapTextAsLines("0123456789" * 5, length: _lineLength), + equals(["0123456789012345678901234567890123456789", "0123456789"])); + }); + + test("refuses to wrap to a column smaller than 10 characters", () { + expect( + wrapTextAsLines("$_longLine " + "0123456789" * 4, length: 1), + equals([ + "This is a", + "long line", + "that needs", + "to be", + "wrapped.", + "0123456789", + "0123456789", + "0123456789", + "0123456789" + ])); + }); + test("doesn't preserve indentation", () { + expect( + wrapTextAsLines(_indentedLongLine, length: _lineLength), + equals([ + "This is an indented long line that needs", + "to be wrapped and indentation preserved." + ])); + }); + test("strips trailing whitespace", () { + expect( + wrapTextAsLines("$_indentedLongLine ", length: _lineLength), + equals([ + "This is an indented long line that needs", + "to be wrapped and indentation preserved." + ])); + }); + test("splits text with newlines properly", () { + expect( + wrapTextAsLines(_longLineWithNewlines, length: _lineLength), + equals([ + "This is a long line with newlines that", + "needs to be wrapped.", + "", + "0123456789012345678901234567890123456789", + "0123456789" + ])); + }); + test("does not preserves indentation in the presence of newlines", () { + expect( + wrapTextAsLines(_indentedLongLineWithNewlines, length: _lineLength), + equals([ + "This is an indented long line with", + "newlines that", + "needs to be wrapped.", + "And preserves tabs.", + "", + "0123456789012345678901234567890123456789", + "0123456789" + ])); + }); + test("removes trailing whitespace when wrapping", () { + expect(wrapTextAsLines("$_longLine \t", length: _lineLength), + equals(["This is a long line that needs to be", "wrapped."])); + }); + test("preserves trailing whitespace when not wrapping", () { + expect( + wrapTextAsLines("$_longLine \t"), equals(["$_longLine \t"])); + }); + }); +} From 1fc2d0a853b288d6e669812febbbb53b26db25c4 Mon Sep 17 00:00:00 2001 From: Greg Spencer Date: Wed, 12 Sep 2018 13:14:49 -0700 Subject: [PATCH 155/263] Review changes. --- pkgs/args/CHANGELOG.md | 4 +++ pkgs/args/lib/command_runner.dart | 31 ++++++++++----------- pkgs/args/lib/src/utils.dart | 46 +++++++++++++------------------ pkgs/args/pubspec.yaml | 2 +- 4 files changed, 39 insertions(+), 44 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 2a335e7d..1fbb54c0 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.1-dev + +* Added more comprehensive word wrapping when `usageLineLength` is set. + ## 1.5.0 * Add `usageLineLength` to control word wrapping usage text. diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 62f7fe7a..fa2babd8 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -49,21 +49,20 @@ class CommandRunner { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { - const usagePrefix = "Usage:"; - var usage = ''' -$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)} - -${_wrap('Global options:')} -${argParser.usage} - -${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)} - -${_wrap('Run "$executableName help " for more information about a command.')}'''; - + var usagePrefix = "Usage:"; + var buffer = new StringBuffer(); + buffer.writeln( + '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n'); + buffer.writeln(_wrap('Global options:')); + buffer.writeln('${argParser.usage}\n'); + buffer.writeln( + '${_getCommandUsage(_commands, lineLength: argParser.usageLineLength)}\n'); + buffer.write(_wrap( + 'Run "$executableName help " for more information about a command.')); if (usageFooter != null) { - usage += "\n${_wrap(usageFooter)}"; + buffer.write('\n${_wrap(usageFooter)}'); } - return usage; + return buffer.toString(); } /// An unmodifiable view of all top-level commands defined for this runner. @@ -298,8 +297,8 @@ abstract class Command { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { - final length = argParser.usageLineLength; - const usagePrefix = "Usage: "; + var length = argParser.usageLineLength; + var usagePrefix = "Usage: "; var buffer = new StringBuffer() ..writeln( usagePrefix + _wrap(invocation, hangingIndent: usagePrefix.length)) @@ -419,8 +418,8 @@ String _getCommandUsage(Map commands, var buffer = new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); + var columnStart = length + 5; for (var name in names) { - var columnStart = length + 5; var lines = wrapTextAsLines(commands[name].summary, start: columnStart, length: lineLength); buffer.writeln(); diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index 9c3972f2..da705b17 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -13,9 +13,9 @@ String padRight(String source, int length) => /// under the limit, then it splits in the middle of a word. /// /// Preserves indentation (leading whitespace) for each line (delimited by '\n') -/// in the input, and will indent wrapped lines the same amount. +/// in the input, and indents wrapped lines the same amount. /// -/// If [hangingIndent] is supplied, then that many spaces will be added to each +/// If [hangingIndent] is supplied, then that many spaces are added to each /// line, except for the first line. This is useful for flowing text with a /// heading prefix (e.g. "Usage: "): /// @@ -46,34 +46,26 @@ String wrapText(String text, {int length, int hangingIndent}) { // When we have a hanging indent, we want to wrap the first line at one // width, and the rest at another (offset by hangingIndent), so we wrap // them twice and recombine. - final firstLineWrap = wrapTextAsLines( - trimmedText, - length: length - leadingWhitespace.length, - ); - notIndented = [firstLineWrap.removeAt(0)]; + var firstLineWrap = wrapTextAsLines(trimmedText, + length: length - leadingWhitespace.length); + notIndented = [firstLineWrap.removeAt(0)]; trimmedText = trimmedText.substring(notIndented[0].length).trimLeft(); if (firstLineWrap.isNotEmpty) { - notIndented.addAll(wrapTextAsLines( - trimmedText, - length: length - leadingWhitespace.length - hangingIndent, - )); + notIndented.addAll(wrapTextAsLines(trimmedText, + length: length - leadingWhitespace.length - hangingIndent)); } } else { - notIndented = wrapTextAsLines( - trimmedText, - length: length - leadingWhitespace.length, - ); + notIndented = wrapTextAsLines(trimmedText, + length: length - leadingWhitespace.length); } String hangingIndentString; - result.addAll(notIndented.map( - (String line) { - // Don't return any lines with just whitespace on them. - if (line.isEmpty) return ""; - var result = "${hangingIndentString ?? ""}$leadingWhitespace$line"; - hangingIndentString ??= " " * hangingIndent; - return result; - }, - )); + result.addAll(notIndented.map((String line) { + // Don't return any lines with just whitespace on them. + if (line.isEmpty) return ''; + var result = '${hangingIndentString ?? ''}$leadingWhitespace$line'; + hangingIndentString ??= ' ' * hangingIndent; + return result; + })); } return result.join('\n'); } @@ -96,7 +88,7 @@ List wrapTextAsLines(String text, {int start = 0, int length}) { /// /// Based on: https://en.wikipedia.org/wiki/Whitespace_character#Unicode bool isWhitespace(String text, int index) { - final rune = text.codeUnitAt(index); + var rune = text.codeUnitAt(index); return rune >= 0x0009 && rune <= 0x000D || rune == 0x0020 || rune == 0x0085 || @@ -113,8 +105,8 @@ List wrapTextAsLines(String text, {int start = 0, int length}) { if (length == null) return text.split('\n'); - final result = []; - final effectiveLength = math.max(length - start, 10); + var result = []; + var effectiveLength = math.max(length - start, 10); for (var line in text.split('\n')) { line = line.trim(); if (line.length <= effectiveLength) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 3f52f942..f68421b4 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.0 +version: 1.5.1-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > From 1c1149b7e42cd3b95e358a6f566c6ed1db78e3a4 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Thu, 8 Nov 2018 10:34:49 -0800 Subject: [PATCH 156/263] Bump version to publish. --- pkgs/args/CHANGELOG.md | 2 +- pkgs/args/pubspec.yaml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 1fbb54c0..4707541f 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.5.1-dev +## 1.5.1 * Added more comprehensive word wrapping when `usageLineLength` is set. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index f68421b4..296b8390 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.1-dev +version: 1.5.1 author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -10,4 +10,4 @@ environment: sdk: '>=1.22.0 <3.0.0' dev_dependencies: - test: '>=0.12.0 <2.0.0' + test: '^1.5.1' From c913257fd4c7d80186eb7d023ae3a398d1722035 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Tue, 18 Dec 2018 12:08:28 -0800 Subject: [PATCH 157/263] Enable and fix a number of standard lints and deprecated member use (dart-lang/args#111) Improves the package health when analyzed on the pub site --- pkgs/args/.travis.yml | 6 +- pkgs/args/analysis_options.yaml | 46 ++++- pkgs/args/example/test_runner.dart | 2 +- pkgs/args/lib/command_runner.dart | 29 ++- pkgs/args/lib/src/allow_anything_parser.dart | 28 +-- pkgs/args/lib/src/arg_parser.dart | 49 ++--- pkgs/args/lib/src/arg_parser_exception.dart | 3 +- pkgs/args/lib/src/arg_results.dart | 12 +- pkgs/args/lib/src/option.dart | 38 ++-- pkgs/args/lib/src/parser.dart | 16 +- pkgs/args/lib/src/usage.dart | 8 +- pkgs/args/pubspec.yaml | 5 +- pkgs/args/test/allow_anything_test.dart | 9 +- pkgs/args/test/args_test.dart | 108 ++++++----- pkgs/args/test/command_parse_test.dart | 40 ++--- pkgs/args/test/command_runner_test.dart | 74 ++++---- pkgs/args/test/command_test.dart | 26 +-- pkgs/args/test/parse_test.dart | 177 ++++++++++--------- pkgs/args/test/test_utils.dart | 12 +- pkgs/args/test/trailing_options_test.dart | 8 +- pkgs/args/test/usage_test.dart | 85 ++++----- pkgs/args/test/utils_test.dart | 3 +- 22 files changed, 428 insertions(+), 356 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index 3b94716e..85cef181 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -1,11 +1,13 @@ language: dart -sudo: false + dart: + - 2.0.0 - stable - dev + dart_task: - test: -p vm - - dartanalyzer + - dartanalyzer: --fatal-infos --fatal-warnings . matrix: include: diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index c2bf9bfb..6473c166 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -1,5 +1,41 @@ -analyzer: - errors: - # TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no - # longer refers to its own deprecated members. - deprecated_member_use: ignore +include: package:pedantic/analysis_options.yaml +linter: + rules: + - avoid_empty_else + - avoid_init_to_null + - avoid_null_checks_in_equality_operators + - avoid_unused_constructor_parameters + - await_only_futures + - camel_case_types + - cancel_subscriptions + - constant_identifier_names + - control_flow_in_finally + - directives_ordering + - empty_catches + - empty_constructor_bodies + - empty_statements + - hash_and_equals + - implementation_imports + - iterable_contains_unrelated_type + - library_names + - library_prefixes + - list_remove_unrelated_type + - non_constant_identifier_names + - overridden_fields + - package_api_docs + - package_names + - package_prefixed_library_names + - prefer_equal_for_default_values + - prefer_final_fields + - prefer_generic_function_type_aliases + - prefer_is_not_empty + - slash_for_doc_comments + - super_goes_last + - test_types_in_equals + - throw_in_finally + - type_init_formals + - unnecessary_brace_in_string_interps + - unnecessary_const + - unnecessary_new + - unrelated_type_equality_checks + - valid_regexps diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index 64f0fff0..d983c867 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -12,7 +12,7 @@ import 'dart:io'; import 'package:args/args.dart'; main() { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addSeparator('===== Platform'); diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index fa2babd8..5ca40840 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -50,7 +50,7 @@ class CommandRunner { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { var usagePrefix = "Usage:"; - var buffer = new StringBuffer(); + var buffer = StringBuffer(); buffer.writeln( '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n'); buffer.writeln(_wrap('Global options:')); @@ -66,7 +66,7 @@ class CommandRunner { } /// An unmodifiable view of all top-level commands defined for this runner. - Map> get commands => new UnmodifiableMapView(_commands); + Map> get commands => UnmodifiableMapView(_commands); final _commands = >{}; /// The top-level argument parser. @@ -75,12 +75,12 @@ class CommandRunner { /// available via [Command.globalResults]. Commands should be registered with /// [addCommand] rather than directly on the parser. ArgParser get argParser => _argParser; - final _argParser = new ArgParser(); + final _argParser = ArgParser(); CommandRunner(this.executableName, this.description) { argParser.addFlag('help', abbr: 'h', negatable: false, help: 'Print this usage information.'); - addCommand(new HelpCommand()); + addCommand(HelpCommand()); } /// Prints the usage information for this runner. @@ -91,7 +91,7 @@ class CommandRunner { /// Throws a [UsageException] with [message]. void usageException(String message) => - throw new UsageException(message, _usageWithoutDescription); + throw UsageException(message, _usageWithoutDescription); /// Adds [Command] as a top-level command to this runner. void addCommand(Command command) { @@ -108,7 +108,7 @@ class CommandRunner { /// This always returns a [Future] in case the command is asynchronous. The /// [Future] will throw a [UsageException] if [args] was invalid. Future run(Iterable args) => - new Future.sync(() => runCommand(parse(args))); + Future.sync(() => runCommand(parse(args))); /// Parses [args] and returns the result, converting an [ArgParserException] /// to a [UsageException]. @@ -276,7 +276,7 @@ abstract class Command { /// This can be overridden to change the arguments passed to the `ArgParser` /// constructor. ArgParser get argParser => _argParser; - final _argParser = new ArgParser(); + final _argParser = ArgParser(); /// Generates a string displaying usage information for this command. /// @@ -299,7 +299,7 @@ abstract class Command { String get _usageWithoutDescription { var length = argParser.usageLineLength; var usagePrefix = "Usage: "; - var buffer = new StringBuffer() + var buffer = StringBuffer() ..writeln( usagePrefix + _wrap(invocation, hangingIndent: usagePrefix.length)) ..writeln(argParser.usage); @@ -326,8 +326,7 @@ abstract class Command { } /// An unmodifiable view of all sublevel commands of this command. - Map> get subcommands => - new UnmodifiableMapView(_subcommands); + Map> get subcommands => UnmodifiableMapView(_subcommands); final _subcommands = >{}; /// Whether or not this command should be hidden from help listings. @@ -373,8 +372,7 @@ abstract class Command { /// The return value is wrapped in a `Future` if necessary and returned by /// [CommandRunner.runCommand]. FutureOr run() { - throw new UnimplementedError( - _wrap("Leaf command $this must implement run().")); + throw UnimplementedError(_wrap("Leaf command $this must implement run().")); } /// Adds [Command] as a subcommand of this. @@ -395,7 +393,7 @@ abstract class Command { /// Throws a [UsageException] with [message]. void usageException(String message) => - throw new UsageException(_wrap(message), _usageWithoutDescription); + throw UsageException(_wrap(message), _usageWithoutDescription); } /// Returns a string representation of [commands] fit for use in a usage string. @@ -403,7 +401,7 @@ abstract class Command { /// [isSubcommand] indicates whether the commands should be called "commands" or /// "subcommands". String _getCommandUsage(Map commands, - {bool isSubcommand: false, int lineLength}) { + {bool isSubcommand = false, int lineLength}) { // Don't include aliases. var names = commands.keys.where((name) => !commands[name].aliases.contains(name)); @@ -416,8 +414,7 @@ String _getCommandUsage(Map commands, names = names.toList()..sort(); var length = names.map((name) => name.length).reduce(math.max); - var buffer = - new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); + var buffer = StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); var columnStart = length + 5; for (var name in names) { var lines = wrapTextAsLines(commands[name].summary, diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 31215ba0..d8937e7e 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -16,18 +16,18 @@ class AllowAnythingParser implements ArgParser { int get usageLineLength => null; ArgParser addCommand(String name, [ArgParser parser]) { - throw new UnsupportedError( + throw UnsupportedError( "ArgParser.allowAnything().addCommands() isn't supported."); } void addFlag(String name, {String abbr, String help, - bool defaultsTo: false, - bool negatable: true, + bool defaultsTo = false, + bool negatable = true, void callback(bool value), - bool hide: false}) { - throw new UnsupportedError( + bool hide = false}) { + throw UnsupportedError( "ArgParser.allowAnything().addFlag() isn't supported."); } @@ -39,10 +39,10 @@ class AllowAnythingParser implements ArgParser { Map allowedHelp, String defaultsTo, Function callback, - bool allowMultiple: false, + bool allowMultiple = false, bool splitCommas, - bool hide: false}) { - throw new UnsupportedError( + bool hide = false}) { + throw UnsupportedError( "ArgParser.allowAnything().addOption() isn't supported."); } @@ -54,26 +54,26 @@ class AllowAnythingParser implements ArgParser { Map allowedHelp, Iterable defaultsTo, void callback(List values), - bool splitCommas: true, - bool hide: false}) { - throw new UnsupportedError( + bool splitCommas = true, + bool hide = false}) { + throw UnsupportedError( "ArgParser.allowAnything().addMultiOption() isn't supported."); } void addSeparator(String text) { - throw new UnsupportedError( + throw UnsupportedError( "ArgParser.allowAnything().addSeparator() isn't supported."); } ArgResults parse(Iterable args) => - new Parser(null, this, args.toList()).parse(); + Parser(null, this, args.toList()).parse(); String getUsage() => usage; String get usage => ""; getDefault(String option) { - throw new ArgumentError('No option named $option'); + throw ArgumentError('No option named $option'); } Option findByAbbreviation(String abbr) => null; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index cdc54e60..24ef2b44 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -2,6 +2,10 @@ // 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. +// TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no +// longer refers to its own deprecated members. +// ignore_for_file: deprecated_member_use + import 'dart:collection'; import 'allow_anything_parser.dart'; @@ -52,8 +56,8 @@ class ArgParser { /// flags and options that appear after positional arguments. If it's `false`, /// the parser stops parsing as soon as it finds an argument that is neither /// an option nor a command. - factory ArgParser({bool allowTrailingOptions: true, int usageLineLength}) => - new ArgParser._({}, {}, + factory ArgParser({bool allowTrailingOptions = true, int usageLineLength}) => + ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions, usageLineLength: usageLineLength); @@ -66,11 +70,11 @@ class ArgParser { factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, - {bool allowTrailingOptions: true, this.usageLineLength}) + {bool allowTrailingOptions = true, this.usageLineLength}) : this._options = options, - this.options = new UnmodifiableMapView(options), + this.options = UnmodifiableMapView(options), this._commands = commands, - this.commands = new UnmodifiableMapView(commands), + this.commands = UnmodifiableMapView(commands), this.allowTrailingOptions = allowTrailingOptions != null ? allowTrailingOptions : false; @@ -82,10 +86,10 @@ class ArgParser { ArgParser addCommand(String name, [ArgParser parser]) { // Make sure the name isn't in use. if (_commands.containsKey(name)) { - throw new ArgumentError('Duplicate command "$name".'); + throw ArgumentError('Duplicate command "$name".'); } - if (parser == null) parser = new ArgParser(); + if (parser == null) parser = ArgParser(); _commands[name] = parser; return parser; } @@ -121,10 +125,10 @@ class ArgParser { void addFlag(String name, {String abbr, String help, - bool defaultsTo: false, - bool negatable: true, + bool defaultsTo = false, + bool negatable = true, void callback(bool value), - bool hide: false}) { + bool hide = false}) { _addOption( name, abbr, @@ -187,11 +191,11 @@ class ArgParser { Map allowedHelp, String defaultsTo, Function callback, - @Deprecated("Use addMultiOption() instead.") bool allowMultiple: false, + @Deprecated("Use addMultiOption() instead.") bool allowMultiple = false, @Deprecated("Use addMultiOption() instead.") bool splitCommas, - bool hide: false}) { + bool hide = false}) { if (!allowMultiple && splitCommas != null) { - throw new ArgumentError( + throw ArgumentError( 'splitCommas may not be set if allowMultiple is false.'); } @@ -256,8 +260,8 @@ class ArgParser { Map allowedHelp, Iterable defaultsTo, void callback(List values), - bool splitCommas: true, - bool hide: false}) { + bool splitCommas = true, + bool hide = false}) { _addOption( name, abbr, @@ -282,19 +286,19 @@ class ArgParser { defaultsTo, Function callback, OptionType type, - {bool negatable: false, + {bool negatable = false, bool splitCommas, - bool hide: false}) { + bool hide = false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { - throw new ArgumentError('Duplicate option "$name".'); + throw ArgumentError('Duplicate option "$name".'); } // Make sure the abbreviation isn't too long or in use. if (abbr != null) { var existing = findByAbbreviation(abbr); if (existing != null) { - throw new ArgumentError( + throw ArgumentError( 'Abbreviation "$abbr" is already used by "${existing.name}".'); } } @@ -317,7 +321,7 @@ class ArgParser { /// 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(Iterable args) => - new Parser(null, this, args.toList()).parse(); + Parser(null, this, args.toList()).parse(); /// Generates a string displaying usage information for the defined options. /// @@ -329,15 +333,14 @@ class ArgParser { /// /// This is basically the help text shown on the command line. String get usage { - return new Usage(_optionsAndSeparators, lineLength: usageLineLength) - .generate(); + return Usage(_optionsAndSeparators, lineLength: usageLineLength).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)) { - throw new ArgumentError('No option named $option'); + throw ArgumentError('No option named $option'); } return options[option].defaultsTo; } diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart index 5ce0eabf..f0d57d5b 100644 --- a/pkgs/args/lib/src/arg_parser_exception.dart +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -10,7 +10,6 @@ class ArgParserException extends FormatException { final List commands; ArgParserException(String message, [Iterable commands]) - : commands = - commands == null ? const [] : new List.unmodifiable(commands), + : commands = commands == null ? const [] : List.unmodifiable(commands), super(message); } diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 78daceab..8e7205f0 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -17,7 +17,7 @@ ArgResults newArgResults( ArgResults command, List rest, List arguments) { - return new ArgResults._(parser, parsed, name, command, rest, arguments); + return ArgResults._(parser, parsed, name, command, rest, arguments); } /// The results of parsing a series of command line arguments using @@ -55,13 +55,13 @@ class ArgResults { /// Creates a new [ArgResults]. ArgResults._(this._parser, this._parsed, this.name, this.command, List rest, List arguments) - : this.rest = new UnmodifiableListView(rest), - this.arguments = new UnmodifiableListView(arguments); + : this.rest = UnmodifiableListView(rest), + this.arguments = UnmodifiableListView(arguments); /// Gets the parsed command-line option named [name]. operator [](String name) { if (!_parser.options.containsKey(name)) { - throw new ArgumentError('Could not find an option named "$name".'); + throw ArgumentError('Could not find an option named "$name".'); } return _parser.options[name].getOrDefault(_parsed[name]); @@ -72,7 +72,7 @@ class ArgResults { /// This includes the options whose values were parsed or that have defaults. /// Options that weren't present and have no default will be omitted. Iterable get options { - var result = new Set.from(_parsed.keys); + var result = Set.from(_parsed.keys); // Include the options that have defaults. _parser.options.forEach((name, option) { @@ -90,7 +90,7 @@ class ArgResults { bool wasParsed(String name) { var option = _parser.options[name]; if (option == null) { - throw new ArgumentError('Could not find an option named "$name".'); + throw ArgumentError('Could not find an option named "$name".'); } return _parsed.containsKey(name); diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 727ea52f..a8496c38 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -18,9 +18,9 @@ Option newOption( OptionType type, {bool negatable, bool splitCommas, - bool hide: false}) { - return new Option._(name, abbr, help, valueHelp, allowed, allowedHelp, - defaultsTo, callback, type, + bool hide = false}) { + return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, + callback, type, negatable: negatable, splitCommas: splitCommas, hide: hide); } @@ -100,35 +100,35 @@ class Option { OptionType type, {this.negatable, bool splitCommas, - this.hide: false}) - : this.allowed = allowed == null ? null : new List.unmodifiable(allowed), + this.hide = false}) + : this.allowed = allowed == null ? null : List.unmodifiable(allowed), this.allowedHelp = - allowedHelp == null ? null : new Map.unmodifiable(allowedHelp), + allowedHelp == null ? null : Map.unmodifiable(allowedHelp), this.type = type, // If the user doesn't specify [splitCommas], it defaults to true for // multiple options. this.splitCommas = splitCommas == null ? type == OptionType.multiple : splitCommas { if (name.isEmpty) { - throw new ArgumentError('Name cannot be empty.'); + throw ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { - throw new ArgumentError('Name $name cannot start with "-".'); + throw ArgumentError('Name $name cannot start with "-".'); } // Ensure name does not contain any invalid characters. if (_invalidChars.hasMatch(name)) { - throw new ArgumentError('Name "$name" contains invalid characters.'); + throw ArgumentError('Name "$name" contains invalid characters.'); } if (abbr != null) { if (abbr.length != 1) { - throw new ArgumentError('Abbreviation must be null or have length 1.'); + throw ArgumentError('Abbreviation must be null or have length 1.'); } else if (abbr == '-') { - throw new ArgumentError('Abbreviation cannot be "-".'); + throw ArgumentError('Abbreviation cannot be "-".'); } if (_invalidChars.hasMatch(abbr)) { - throw new ArgumentError('Abbreviation is an invalid character.'); + throw ArgumentError('Abbreviation is an invalid character.'); } } } @@ -145,7 +145,7 @@ class Option { return defaultsTo; } - static final _invalidChars = new RegExp(r'''[ \t\r\n"'\\/]'''); + static final _invalidChars = RegExp(r'''[ \t\r\n"'\\/]'''); } /// What kinds of values an option accepts. @@ -153,10 +153,10 @@ class OptionType { /// An option that can only be `true` or `false`. /// /// The presence of the option name itself in the argument list means `true`. - static const flag = const OptionType._("OptionType.flag"); + static const flag = OptionType._("OptionType.flag"); @Deprecated("Use OptionType.flag instead.") - static const FLAG = flag; + static const FLAG = flag; // ignore: constant_identifier_names /// An option that takes a single value. /// @@ -167,10 +167,10 @@ class OptionType { /// --mode=debug /// /// If the option is passed more than once, the last one wins. - static const single = const OptionType._("OptionType.single"); + static const single = OptionType._("OptionType.single"); @Deprecated("Use OptionType.single instead.") - static const SINGLE = single; + static const SINGLE = single; // ignore: constant_identifier_names /// An option that allows multiple values. /// @@ -180,10 +180,10 @@ class OptionType { /// /// In the parsed `ArgResults`, a multiple-valued option will always return /// a list, even if one or no values were passed. - static const multiple = const OptionType._("OptionType.multiple"); + static const multiple = OptionType._("OptionType.multiple"); @Deprecated("Use OptionType.multiple instead.") - static const MULTIPLE = multiple; + static const MULTIPLE = multiple; // ignore: constant_identifier_names final String name; diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index c1bcc842..6895300b 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -7,9 +7,9 @@ import 'arg_parser_exception.dart'; import 'arg_results.dart'; import 'option.dart'; -final _soloOpt = new RegExp(r'^-([a-zA-Z0-9])$'); -final _abbrOpt = new RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); -final _longOpt = new RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); +final _soloOpt = RegExp(r'^-([a-zA-Z0-9])$'); +final _abbrOpt = RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); +final _longOpt = RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); /// The actual argument parsing class. /// @@ -55,7 +55,7 @@ class Parser { ArgResults commandResults; // Parse the args. - while (args.length > 0) { + while (args.isNotEmpty) { if (current == '--') { // Reached the argument terminator, so stop here. args.removeAt(0); @@ -68,13 +68,13 @@ class Parser { if (command != null) { validate(rest.isEmpty, 'Cannot specify arguments before a command.'); var commandName = args.removeAt(0); - var commandParser = new Parser(commandName, command, args, this, rest); + var commandParser = Parser(commandName, command, args, this, rest); try { commandResults = commandParser.parse(); } on ArgParserException catch (error) { if (commandName == null) rethrow; - throw new ArgParserException( + throw ArgParserException( error.message, [commandName]..addAll(error.commands)); } @@ -113,7 +113,7 @@ class Parser { /// Validates that there is a valid value there. void readNextArgAsValue(Option option) { // Take the option argument from the next command line arg. - validate(args.length > 0, 'Missing argument for "${option.name}".'); + validate(args.isNotEmpty, 'Missing argument for "${option.name}".'); setOption(results, option, current); args.removeAt(0); @@ -258,7 +258,7 @@ class Parser { /// /// Throws an [ArgParserException] if [condition] is `false`. void validate(bool condition, String message) { - if (!condition) throw new ArgParserException(message); + if (!condition) throw ArgParserException(message); } /// Validates and stores [value] as the value for [option], which must not be diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 01775ffc..cdac6d3d 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -63,7 +63,7 @@ class Usage { /// Generates a string displaying usage information for the defined options. /// This is basically the help text shown on the command line. String generate() { - buffer = new StringBuffer(); + buffer = StringBuffer(); calculateColumnWidths(); @@ -196,11 +196,11 @@ class Usage { } // Strip leading and trailing empty lines. - while (lines.length > 0 && lines[0].trim() == '') { + while (lines.isNotEmpty && lines[0].trim() == '') { lines.removeRange(0, 1); } - while (lines.length > 0 && lines[lines.length - 1].trim() == '') { + while (lines.isNotEmpty && lines[lines.length - 1].trim() == '') { lines.removeLast(); } @@ -255,7 +255,7 @@ class Usage { ? option.defaultsTo.contains : (value) => value == option.defaultsTo; - var allowedBuffer = new StringBuffer(); + var allowedBuffer = StringBuffer(); allowedBuffer.write('['); var first = true; for (var allowed in option.allowed) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 296b8390..e400bd4d 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.1 +version: 1.5.2-dev author: "Dart Team " homepage: https://github.com/dart-lang/args description: > @@ -7,7 +7,8 @@ description: > a set of options and values using GNU and POSIX style options. environment: - sdk: '>=1.22.0 <3.0.0' + sdk: '>=2.0.0 <3.0.0' dev_dependencies: + pedantic: ^1.4.0 test: '^1.5.1' diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index ac185f83..ccf11e8c 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -2,15 +2,14 @@ // 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. -import 'package:test/test.dart'; - import 'package:args/args.dart'; +import 'package:test/test.dart'; void main() { group('new ArgParser.allowAnything()', () { ArgParser parser; setUp(() { - parser = new ArgParser.allowAnything(); + parser = ArgParser.allowAnything(); }); test('exposes empty values', () { @@ -19,7 +18,7 @@ void main() { expect(parser.allowTrailingOptions, isFalse); expect(parser.allowsAnything, isTrue); expect(parser.usage, isEmpty); - expect(parser.getUsage(), isEmpty); + expect(parser.getUsage(), isEmpty); // ignore: deprecated_member_use expect(parser.findByAbbreviation("a"), isNull); }); @@ -44,7 +43,7 @@ void main() { }); test('works as a subcommand', () { - var commandParser = new ArgParser()..addCommand('command', parser); + var commandParser = ArgParser()..addCommand('command', parser); var results = commandParser.parse(['command', '--foo', '-abc', '--', 'bar']); expect(results.command.options, isEmpty); diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index d738f008..b5dd3ee5 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -2,26 +2,27 @@ // 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. -import 'package:test/test.dart'; import 'package:args/args.dart'; +import 'package:test/test.dart'; + import 'test_utils.dart'; void main() { group('ArgParser.addFlag()', () { test('throws ArgumentError if the flag already exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('foo'); throwsIllegalArg(() => parser.addFlag('foo')); }); test('throws ArgumentError if the option already exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('foo'); throwsIllegalArg(() => parser.addFlag('foo')); }); test('throws ArgumentError if the abbreviation exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('foo', abbr: 'f'); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'f')); }); @@ -29,23 +30,23 @@ void main() { test( 'throws ArgumentError if the abbreviation is longer ' 'than one character', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsIllegalArg(() => parser.addFlag('flummox', abbr: 'flu')); }); test('throws ArgumentError if a flag name is invalid', () { - var parser = new ArgParser(); + var parser = ArgParser(); - for (var name in _INVALID_OPTIONS) { + for (var name in _invalidOptions) { var reason = '${Error.safeToString(name)} is not valid'; throwsIllegalArg(() => parser.addFlag(name), reason: reason); } }); test('accepts valid flag names', () { - var parser = new ArgParser(); + var parser = ArgParser(); - for (var name in _VALID_OPTIONS) { + for (var name in _validOptions) { var reason = '${Error.safeToString(name)} is valid'; expect(() => parser.addFlag(name), returnsNormally, reason: reason); } @@ -54,19 +55,19 @@ void main() { group('ArgParser.addOption()', () { test('throws ArgumentError if the flag already exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('foo'); throwsIllegalArg(() => parser.addOption('foo')); }); test('throws ArgumentError if the option already exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('foo'); throwsIllegalArg(() => parser.addOption('foo')); }); test('throws ArgumentError if the abbreviation exists', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('foo', abbr: 'f'); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'f')); }); @@ -74,36 +75,36 @@ void main() { test( 'throws ArgumentError if the abbreviation is longer ' 'than one character', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: 'flu')); }); test('throws ArgumentError if the abbreviation is empty', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: '')); }); test('throws ArgumentError if the abbreviation is an invalid value', () { - var parser = new ArgParser(); - for (var name in _INVALID_OPTIONS.where((v) => v != null)) { + var parser = ArgParser(); + for (var name in _invalidOptions.where((v) => v != null)) { throwsIllegalArg(() => parser.addOption('flummox', abbr: name)); } }); test('throws ArgumentError if the abbreviation is a dash', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsIllegalArg(() => parser.addOption('flummox', abbr: '-')); }); test('allows explict null value for "abbr"', () { - var parser = new ArgParser(); + var parser = ArgParser(); expect(() => parser.addOption('flummox', abbr: null), returnsNormally); }); test('throws ArgumentError if an option name is invalid', () { - var parser = new ArgParser(); + var parser = ArgParser(); - for (var name in _INVALID_OPTIONS) { + for (var name in _invalidOptions) { var reason = '${Error.safeToString(name)} is not valid'; throwsIllegalArg(() => parser.addOption(name), reason: reason); } @@ -112,15 +113,24 @@ void main() { test( 'throws ArgumentError if splitCommas is passed with allowMultiple: ' 'false', () { - var parser = new ArgParser(); - throwsIllegalArg(() => parser.addOption('flummox', splitCommas: true)); - throwsIllegalArg(() => parser.addOption('flummox', splitCommas: false)); + var parser = ArgParser(); + throwsIllegalArg(() { + parser.addOption( + 'flummox', splitCommas: true, // ignore: deprecated_member_use + ); + }); + throwsIllegalArg(() { + parser.addOption( + 'flummox', + splitCommas: false, // ignore: deprecated_member_use + ); + }); }); test('accepts valid option names', () { - var parser = new ArgParser(); + var parser = ArgParser(); - for (var name in _VALID_OPTIONS) { + for (var name in _validOptions) { var reason = '${Error.safeToString(name)} is valid'; expect(() => parser.addOption(name), returnsNormally, reason: reason); } @@ -129,13 +139,13 @@ void main() { group('ArgParser.getDefault()', () { test('returns the default value for an option', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); expect(parser.getDefault('mode'), 'debug'); }); test('throws if the option is unknown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); throwsIllegalArg(() => parser.getDefault('undefined')); }); @@ -143,12 +153,12 @@ void main() { group('ArgParser.commands', () { test('returns an empty map if there are no commands', () { - var parser = new ArgParser(); + var parser = ArgParser(); expect(parser.commands, isEmpty); }); test('returns the commands that were added', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addCommand('hide'); parser.addCommand('seek'); expect(parser.commands, hasLength(2)); @@ -157,7 +167,7 @@ void main() { }); test('iterates over the commands in the order they were added', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addCommand('a'); parser.addCommand('d'); parser.addCommand('b'); @@ -168,12 +178,12 @@ void main() { group('ArgParser.options', () { test('returns an empty map if there are no options', () { - var parser = new ArgParser(); + var parser = ArgParser(); expect(parser.options, isEmpty); }); test('returns the options that were added', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('hide'); parser.addOption('seek'); expect(parser.options, hasLength(2)); @@ -182,7 +192,7 @@ void main() { }); test('iterates over the options in the order they were added', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('a'); parser.addOption('d'); parser.addFlag('b'); @@ -194,7 +204,7 @@ void main() { group('ArgResults', () { group('options', () { test('returns the provided options', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('woof'); parser.addOption('meow'); @@ -208,7 +218,7 @@ void main() { }); test('includes defaulted options', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('woof', defaultsTo: false); parser.addOption('meow', defaultsTo: 'kitty'); @@ -227,17 +237,17 @@ void main() { }); test('[] throws if the name is not an option', () { - var results = new ArgParser().parse([]); + var results = ArgParser().parse([]); throwsIllegalArg(() => results['unknown']); }); test('rest cannot be modified', () { - var results = new ArgParser().parse([]); + var results = ArgParser().parse([]); expect(() => results.rest.add('oops'), throwsUnsupportedError); }); test('.arguments returns the original argument list', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('foo'); var results = parser.parse(['--foo']); @@ -246,12 +256,12 @@ void main() { group('.wasParsed()', () { test('throws if the name is not an option', () { - var results = new ArgParser().parse([]); + var results = ArgParser().parse([]); throwsIllegalArg(() => results.wasParsed('unknown')); }); test('returns true for parsed options', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('fast'); parser.addFlag('verbose'); parser.addOption('mode'); @@ -269,14 +279,20 @@ void main() { group('Option', () { test('.getOrDefault() returns a type-specific default value', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('flag-no', defaultsTo: null); parser.addFlag('flag-def', defaultsTo: true); parser.addOption('single-no'); parser.addOption('single-def', defaultsTo: 'def'); - parser.addOption('allow-multi-no', allowMultiple: true); - parser.addOption('allow-multi-def', - allowMultiple: true, defaultsTo: 'def'); + parser.addOption( + 'allow-multi-no', + allowMultiple: true, // ignore: deprecated_member_use + ); + parser.addOption( + 'allow-multi-def', + allowMultiple: true, // ignore: deprecated_member_use + defaultsTo: 'def', + ); parser.addMultiOption('multi-no'); parser.addMultiOption('multi-def', defaultsTo: ['def']); @@ -303,7 +319,7 @@ void main() { }); } -const _INVALID_OPTIONS = const [ +const _invalidOptions = [ ' ', '', '-', @@ -319,7 +335,7 @@ const _INVALID_OPTIONS = const [ 'forward/slash' ]; -const _VALID_OPTIONS = const [ +const _validOptions = [ 'a', // One character. 'contains-dash', 'contains_underscore', diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index a5a1f568..afa8ebe9 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -9,22 +9,22 @@ import 'test_utils.dart'; void main() { group('ArgParser.addCommand()', () { test('creates a new ArgParser if none is given', () { - var parser = new ArgParser(); + var parser = ArgParser(); var command = parser.addCommand('install'); expect(parser.commands['install'], equals(command)); expect(command is ArgParser, isTrue); }); test('uses the command parser if given one', () { - var parser = new ArgParser(); - var command = new ArgParser(); + var parser = ArgParser(); + var command = ArgParser(); var result = parser.addCommand('install', command); expect(parser.commands['install'], equals(command)); expect(result, equals(command)); }); test('throws on a duplicate command name', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addCommand('install'); throwsIllegalArg(() => parser.addCommand('install')); }); @@ -32,7 +32,7 @@ void main() { group('ArgParser.parse()', () { test('parses a command', () { - var parser = new ArgParser()..addCommand('install'); + var parser = ArgParser()..addCommand('install'); var args = parser.parse(['install']); @@ -41,7 +41,7 @@ void main() { }); test('parses a command option', () { - var parser = new ArgParser(); + var parser = ArgParser(); var command = parser.addCommand('install'); command.addOption('path'); @@ -50,7 +50,7 @@ void main() { }); test('parses a parent solo option before the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addOption('mode', abbr: 'm') ..addCommand('install'); @@ -60,7 +60,7 @@ void main() { }); test('parses a parent solo option after the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addOption('mode', abbr: 'm') ..addCommand('install'); @@ -70,7 +70,7 @@ void main() { }); test('parses a parent option before the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('verbose') ..addCommand('install'); @@ -80,7 +80,7 @@ void main() { }); test('parses a parent option after the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('verbose') ..addCommand('install'); @@ -90,7 +90,7 @@ void main() { }); test('parses a parent negated option before the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('verbose', defaultsTo: true) ..addCommand('install'); @@ -100,7 +100,7 @@ void main() { }); test('parses a parent negated option after the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('verbose', defaultsTo: true) ..addCommand('install'); @@ -110,7 +110,7 @@ void main() { }); test('parses a parent abbreviation before the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('debug', abbr: 'd') ..addFlag('verbose', abbr: 'v') ..addCommand('install'); @@ -122,7 +122,7 @@ void main() { }); test('parses a parent abbreviation after the command', () { - var parser = new ArgParser() + var parser = ArgParser() ..addFlag('debug', abbr: 'd') ..addFlag('verbose', abbr: 'v') ..addCommand('install'); @@ -134,7 +134,7 @@ void main() { }); test('does not parse a solo command option before the command', () { - var parser = new ArgParser(); + var parser = ArgParser(); var command = parser.addCommand('install'); command.addOption('path', abbr: 'p'); @@ -142,7 +142,7 @@ void main() { }); test('does not parse a command option before the command', () { - var parser = new ArgParser(); + var parser = ArgParser(); var command = parser.addCommand('install'); command.addOption('path'); @@ -150,7 +150,7 @@ void main() { }); test('does not parse a command abbreviation before the command', () { - var parser = new ArgParser(); + var parser = ArgParser(); var command = parser.addCommand('install'); command.addFlag('debug', abbr: 'd'); command.addFlag('verbose', abbr: 'v'); @@ -159,7 +159,7 @@ void main() { }); test('assigns collapsed options to the proper command', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('apple', abbr: 'a'); var command = parser.addCommand('cmd'); command.addFlag('banana', abbr: 'b'); @@ -175,7 +175,7 @@ void main() { }); test('option is given to innermost command that can take it', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); parser.addCommand('cmd') ..addFlag('verbose') @@ -189,7 +189,7 @@ void main() { }); test('remaining arguments are given to the innermost command', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addCommand('cmd')..addCommand('subcmd'); var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 07999278..e9a3cfbf 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'test_utils.dart'; -const _DEFAULT_USAGE = """ +const _defaultUsage = """ Usage: test [arguments] Global options: @@ -21,7 +21,7 @@ Run "test help " for more information about a command."""; void main() { var runner; setUp(() { - runner = new CommandRunner("test", "A test command runner."); + runner = CommandRunner("test", "A test command runner."); }); test(".invocation has a sane default", () { @@ -33,11 +33,11 @@ void main() { expect(runner.usage, equals(""" A test command runner. -$_DEFAULT_USAGE""")); +$_defaultUsage""")); }); test("contains custom commands", () { - runner.addCommand(new FooCommand()); + runner.addCommand(FooCommand()); expect(runner.usage, equals(""" A test command runner. @@ -55,7 +55,7 @@ Run "test help " for more information about a command.""")); }); test("truncates newlines in command descriptions by default", () { - runner.addCommand(new MultilineCommand()); + runner.addCommand(MultilineCommand()); expect(runner.usage, equals(""" A test command runner. @@ -73,7 +73,7 @@ Run "test help " for more information about a command.""")); }); test("supports newlines in command summaries", () { - runner.addCommand(new MultilineSummaryCommand()); + runner.addCommand(MultilineSummaryCommand()); expect(runner.usage, equals(""" A test command runner. @@ -110,16 +110,16 @@ Run "test help " for more information about a command.""")); }); test("doesn't print hidden commands", () { - runner.addCommand(new HiddenCommand()); + runner.addCommand(HiddenCommand()); expect(runner.usage, equals(""" A test command runner. -$_DEFAULT_USAGE""")); +$_defaultUsage""")); }); test("doesn't print aliases", () { - runner.addCommand(new AliasedCommand()); + runner.addCommand(AliasedCommand()); expect(runner.usage, equals(""" A test command runner. @@ -139,12 +139,12 @@ Run "test help " for more information about a command.""")); test("usageException splits up the message and usage", () { expect(() => runner.usageException("message"), - throwsUsageException("message", _DEFAULT_USAGE)); + throwsUsageException("message", _defaultUsage)); }); group("run()", () { test("runs a command", () { - var command = new FooCommand(); + var command = FooCommand(); runner.addCommand(command); expect( @@ -155,7 +155,7 @@ Run "test help " for more information about a command.""")); }); test("runs an asynchronous command", () { - var command = new AsyncCommand(); + var command = AsyncCommand(); runner.addCommand(command); expect( @@ -166,23 +166,23 @@ Run "test help " for more information about a command.""")); }); test("runs a command with a return value", () { - var runner = new CommandRunner("test", ""); - var command = new ValueCommand(); + var runner = CommandRunner("test", ""); + var command = ValueCommand(); runner.addCommand(command); expect(runner.run(["foo"]), completion(equals(12))); }); test("runs a command with an asynchronous return value", () { - var runner = new CommandRunner("test", ""); - var command = new AsyncValueCommand(); + var runner = CommandRunner("test", ""); + var command = AsyncValueCommand(); runner.addCommand(command); expect(runner.run(["foo"]), completion(equals("hi"))); }); test("runs a hidden comand", () { - var command = new HiddenCommand(); + var command = HiddenCommand(); runner.addCommand(command); expect( @@ -193,7 +193,7 @@ Run "test help " for more information about a command.""")); }); test("runs an aliased comand", () { - var command = new AliasedCommand(); + var command = AliasedCommand(); runner.addCommand(command); expect( @@ -204,8 +204,8 @@ Run "test help " for more information about a command.""")); }); test("runs a subcommand", () { - var command = new AsyncCommand(); - runner.addCommand(new FooCommand()..addSubcommand(command)); + var command = AsyncCommand(); + runner.addCommand(FooCommand()..addSubcommand(command)); expect( runner.run(["foo", "async"]).then((_) { @@ -219,12 +219,12 @@ Run "test help " for more information about a command.""")); expect(() => runner.run(["--help"]), prints(""" A test command runner. -$_DEFAULT_USAGE +$_defaultUsage """)); }); test("with a preceding command prints the usage for that command", () { - var command = new FooCommand(); + var command = FooCommand(); runner.addCommand(command); expect(() => runner.run(["foo", "--help"]), prints(""" @@ -238,7 +238,7 @@ Run "test help" to see global options. }); test("with a following command prints the usage for that command", () { - var command = new FooCommand(); + var command = FooCommand(); runner.addCommand(command); expect(() => runner.run(["--help", "foo"]), prints(""" @@ -257,12 +257,12 @@ Run "test help" to see global options. expect(() => runner.run(["help"]), prints(""" A test command runner. -$_DEFAULT_USAGE +$_defaultUsage """)); }); test("with a command prints the usage for that command", () { - var command = new FooCommand(); + var command = FooCommand(); runner.addCommand(command); expect(() => runner.run(["help", "foo"]), prints(""" @@ -292,11 +292,11 @@ Run "test help" to see global options. expect( runner.run(["--asdf"]), throwsUsageException( - 'Could not find an option named "asdf".', '$_DEFAULT_USAGE')); + 'Could not find an option named "asdf".', '$_defaultUsage')); }); test("for a command throws the command usage", () { - var command = new FooCommand(); + var command = FooCommand(); runner.addCommand(command); expect(runner.run(["foo", "--asdf"]), @@ -311,14 +311,14 @@ Run "test help" to see global options.""")); group("with a footer", () { setUp(() { - runner = new CommandRunnerWithFooter("test", "A test command runner."); + runner = CommandRunnerWithFooter("test", "A test command runner."); }); test("includes the footer in the usage string", () { expect(runner.usage, equals(""" A test command runner. -$_DEFAULT_USAGE +$_defaultUsage Also, footer!""")); }); @@ -326,14 +326,14 @@ Also, footer!""")); expect( runner.run(["--bad"]), throwsUsageException('Could not find an option named "bad".', - "$_DEFAULT_USAGE\nAlso, footer!")); + "$_defaultUsage\nAlso, footer!")); }); }); group("with a footer and wrapping", () { setUp(() { - runner = new CommandRunnerWithFooterAndWrapping( - "test", "A test command runner."); + runner = + CommandRunnerWithFooterAndWrapping("test", "A test command runner."); }); test("includes the footer in the usage string", () { expect(runner.usage, equals(""" @@ -388,18 +388,18 @@ newlines properly.""")); expect( runner.run(["--bad"]), throwsUsageException( - 'Could not find an option named "bad".', _DEFAULT_USAGE)); + 'Could not find an option named "bad".', _defaultUsage)); }); test("a top-level command doesn't exist", () { expect( runner.run(["bad"]), throwsUsageException( - 'Could not find a command named "bad".', _DEFAULT_USAGE)); + 'Could not find a command named "bad".', _defaultUsage)); }); test("a subcommand doesn't exist", () { - runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); + runner.addCommand(FooCommand()..addSubcommand(AsyncCommand())); expect(runner.run(["foo bad"]), throwsUsageException('Could not find a command named "foo bad".', """ @@ -416,7 +416,7 @@ Run "test help " for more information about a command.""")); }); test("a subcommand wasn't passed", () { - runner.addCommand(new FooCommand()..addSubcommand(new AsyncCommand())); + runner.addCommand(FooCommand()..addSubcommand(AsyncCommand())); expect(runner.run(["foo"]), throwsUsageException('Missing subcommand for "test foo".', """ @@ -430,7 +430,7 @@ Run "test help" to see global options.""")); }); test("a command that doesn't take arguments was given them", () { - runner.addCommand(new FooCommand()); + runner.addCommand(FooCommand()); expect(runner.run(["foo", "bar"]), throwsUsageException('Command "foo" does not take any arguments.', """ diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index 4c99fa2e..b78a7399 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -9,10 +9,10 @@ import 'test_utils.dart'; void main() { var foo; setUp(() { - foo = new FooCommand(); + foo = FooCommand(); // Make sure [Command.runner] is set up. - new CommandRunner("test", "A test command runner.").addCommand(foo); + CommandRunner("test", "A test command runner.").addCommand(foo); }); group(".invocation has a sane default", () { @@ -21,12 +21,12 @@ void main() { }); test("with subcommands", () { - foo.addSubcommand(new AsyncCommand()); + foo.addSubcommand(AsyncCommand()); expect(foo.invocation, equals("test foo [arguments]")); }); test("for a subcommand", () { - var async = new AsyncCommand(); + var async = AsyncCommand(); foo.addSubcommand(async); expect(async.invocation, equals("test foo async [arguments]")); @@ -58,8 +58,8 @@ Run "test help" to see global options.""")); }); test("doesn't print hidden subcommands", () { - foo.addSubcommand(new AsyncCommand()); - foo.addSubcommand(new HiddenCommand()); + foo.addSubcommand(AsyncCommand()); + foo.addSubcommand(HiddenCommand()); expect(foo.usage, equals(""" Set a value. @@ -74,7 +74,7 @@ Run "test help" to see global options.""")); }); test("doesn't print subcommand aliases", () { - foo.addSubcommand(new AliasedCommand()); + foo.addSubcommand(AliasedCommand()); expect(foo.usage, equals(""" Set a value. @@ -89,13 +89,13 @@ Run "test help" to see global options.""")); }); test("wraps long command descriptions with subcommands", () { - var wrapping = new WrappingCommand(); + var wrapping = WrappingCommand(); // Make sure [Command.runner] is set up. - new CommandRunner("longtest", "A long-lined test command runner.") + CommandRunner("longtest", "A long-lined test command runner.") .addCommand(wrapping); - wrapping.addSubcommand(new LongCommand()); + wrapping.addSubcommand(LongCommand()); expect(wrapping.usage, equals(""" This command overrides the argParser so that it will wrap long lines. @@ -115,10 +115,10 @@ options.""")); }); test("wraps long command descriptions", () { - var longCommand = new LongCommand(); + var longCommand = LongCommand(); // Make sure [Command.runner] is set up. - new CommandRunner("longtest", "A long-lined test command runner.") + CommandRunner("longtest", "A long-lined test command runner.") .addCommand(longCommand); expect(longCommand.usage, equals(""" @@ -151,7 +151,7 @@ Run "test help" to see global options.""")); }); test("considers a command hidden if all its subcommands are hidden", () { - foo.addSubcommand(new HiddenCommand()); + foo.addSubcommand(HiddenCommand()); expect(foo.hidden, isTrue); }); } diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 3fa20653..d8b0e484 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -2,14 +2,15 @@ // 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. -import 'package:test/test.dart'; import 'package:args/args.dart'; +import 'package:test/test.dart'; + import 'test_utils.dart'; void main() { group('ArgParser.parse()', () { test('does not destructively modify the argument list', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); var args = ['--verbose']; @@ -20,7 +21,7 @@ void main() { group('flags', () { test('are true if present', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); var args = parser.parse(['--verbose']); @@ -28,7 +29,7 @@ void main() { }); test('default if missing', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('a', defaultsTo: true); parser.addFlag('b', defaultsTo: false); @@ -38,7 +39,7 @@ void main() { }); test('are false if missing with no default', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); var args = parser.parse([]); @@ -46,14 +47,14 @@ void main() { }); test('throws if given a value', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); throwsFormat(parser, ['--verbose=true']); }); test('are case-sensitive', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); parser.addFlag('Verbose'); var results = parser.parse(['--verbose']); @@ -64,7 +65,7 @@ void main() { group('flags negated with "no-"', () { test('set the flag to false', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('verbose'); var args = parser.parse(['--no-verbose']); @@ -72,7 +73,7 @@ void main() { }); test('set the flag to true if the flag actually starts with "no-"', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('no-body'); var args = parser.parse(['--no-body']); @@ -80,7 +81,7 @@ void main() { }); test('are not preferred over a colliding one without', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('no-strum'); parser.addFlag('strum'); @@ -90,7 +91,7 @@ void main() { }); test('fail for non-negatable flags', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('strum', negatable: false); throwsFormat(parser, ['--no-strum']); @@ -100,7 +101,7 @@ void main() { group('callbacks', () { test('for present flags are invoked with the value', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('a', callback: (value) => a = value); parser.parse(['--a']); @@ -109,7 +110,7 @@ void main() { test('for absent flags are invoked with the default value', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('a', defaultsTo: false, callback: (value) => a = value); parser.parse([]); @@ -118,7 +119,7 @@ void main() { test('are invoked even if the flag is not present', () { var a = true; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('a', callback: (value) => a = value); parser.parse([]); @@ -127,7 +128,7 @@ void main() { test('for present options are invoked with the value', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', callback: (value) => a = value); parser.parse(['--a=v']); @@ -136,7 +137,7 @@ void main() { test('for absent options are invoked with the default value', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', defaultsTo: 'v', callback: (value) => a = value); parser.parse([]); @@ -145,7 +146,7 @@ void main() { test('are invoked even if the option is not present', () { var a = 'not called'; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', callback: (value) => a = value); parser.parse([]); @@ -156,25 +157,27 @@ void main() { test('for multiple present, options are invoked with value as a list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); + allowMultiple: true, // ignore: deprecated_member_use + callback: (value) => a = value); parser.parse(['--a=v', '--a=x']); expect(a, equals(['v', 'x'])); // This reified type is important in strong mode so that people can // safely write "as List". - expect(a, new isInstanceOf>()); + expect(a, TypeMatcher>()); }); test( 'for single present, options are invoked with value as a single ' 'element list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); + allowMultiple: true, // ignore: deprecated_member_use + callback: (value) => a = value); parser.parse(['--a=v']); expect(a, equals(['v'])); @@ -183,9 +186,9 @@ void main() { test('for absent, options are invoked with default value as a list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, + allowMultiple: true, // ignore: deprecated_member_use defaultsTo: 'v', callback: (value) => a = value); @@ -195,9 +198,10 @@ void main() { test('for absent, options are invoked with value as an empty list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); + allowMultiple: true, // ignore: deprecated_member_use + callback: (value) => a = value); parser.parse([]); expect(a, isEmpty); @@ -205,9 +209,10 @@ void main() { test('parses comma-separated strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); + allowMultiple: true, // ignore: deprecated_member_use + callback: (value) => a = value); parser.parse(['--a=v,w', '--a=x']); expect(a, equals(['v', 'w', 'x'])); @@ -216,10 +221,10 @@ void main() { test("doesn't parse comma-separated strings with splitCommas: false", () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, - splitCommas: false, + allowMultiple: true, // ignore: deprecated_member_use + splitCommas: false, // ignore: deprecated_member_use callback: (value) => a = value); parser.parse(['--a=v,w', '--a=x']); @@ -228,9 +233,10 @@ void main() { test('parses empty strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, callback: (value) => a = value); + allowMultiple: true, // ignore: deprecated_member_use + callback: (value) => a = value); parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); @@ -238,9 +244,9 @@ void main() { test('with allowed parses comma-separated strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('a', - allowMultiple: true, + allowMultiple: true, // ignore: deprecated_member_use allowed: ['v', 'w', 'x'], callback: (value) => a = value); @@ -253,7 +259,7 @@ void main() { test('for multiple present, options are invoked with value as a list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', callback: (value) => a = value); parser.parse(['--a=v', '--a=x']); @@ -261,14 +267,14 @@ void main() { // This reified type is important in strong mode so that people can // safely write "as List". - expect(a, new isInstanceOf>()); + expect(a, TypeMatcher>()); }); test( 'for single present, options are invoked with value as a single ' 'element list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', callback: (value) => a = value); parser.parse(['--a=v']); @@ -277,7 +283,7 @@ void main() { test('for absent, options are invoked with default value', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', defaultsTo: ['v', 'w'], callback: (value) => a = value); @@ -287,7 +293,7 @@ void main() { test('for absent, options are invoked with value as an empty list', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', callback: (value) => a = value); parser.parse([]); @@ -296,7 +302,7 @@ void main() { test('parses comma-separated strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', callback: (value) => a = value); parser.parse(['--a=v,w', '--a=x']); @@ -306,7 +312,7 @@ void main() { test("doesn't parse comma-separated strings with splitCommas: false", () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', splitCommas: false, callback: (value) => a = value); @@ -316,7 +322,7 @@ void main() { test('parses empty strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', callback: (value) => a = value); parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); @@ -325,7 +331,7 @@ void main() { test('with allowed parses comma-separated strings', () { var a; - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('a', allowed: ['v', 'w', 'x'], callback: (value) => a = value); @@ -337,7 +343,7 @@ void main() { group('abbreviations', () { test('are parsed with a preceding "-"', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('arg', abbr: 'a'); var args = parser.parse(['-a']); @@ -345,7 +351,7 @@ void main() { }); test('can use multiple after a single "-"', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('first', abbr: 'f'); parser.addFlag('second', abbr: 's'); parser.addFlag('third', abbr: 't'); @@ -357,7 +363,7 @@ void main() { }); test('can have multiple "-" args', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('first', abbr: 'f'); parser.addFlag('second', abbr: 's'); parser.addFlag('third', abbr: 't'); @@ -369,7 +375,7 @@ void main() { }); test('can take arguments without a space separating', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('file', abbr: 'f'); var args = parser.parse(['-flip']); @@ -377,7 +383,7 @@ void main() { }); test('can take arguments with a space separating', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('file', abbr: 'f'); var args = parser.parse(['-f', 'name']); @@ -385,7 +391,7 @@ void main() { }); test('allow non-option characters in the value', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('apple', abbr: 'a'); var args = parser.parse(['-ab?!c']); @@ -393,19 +399,19 @@ void main() { }); test('throw if unknown', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsFormat(parser, ['-f']); }); test('throw if the value is missing', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('file', abbr: 'f'); throwsFormat(parser, ['-f']); }); test('does not throw if the value looks like an option', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('file', abbr: 'f'); parser.addOption('other'); @@ -416,7 +422,7 @@ void main() { }); test('throw if the value is not allowed', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', abbr: 'm', allowed: ['debug', 'release']); throwsFormat(parser, ['-mprofile']); @@ -424,15 +430,18 @@ void main() { group('throw if a comma-separated value is not allowed', () { test("with allowMultiple", () { - var parser = new ArgParser(); - parser.addOption('mode', - abbr: 'm', allowMultiple: true, allowed: ['debug', 'release']); + var parser = ArgParser(); + parser.addOption( + 'mode', + abbr: 'm', allowMultiple: true, // ignore: deprecated_member_use + allowed: ['debug', 'release'], + ); throwsFormat(parser, ['-mdebug,profile']); }); test("with addMultiOption", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser .addMultiOption('mode', abbr: 'm', allowed: ['debug', 'release']); @@ -441,7 +450,7 @@ void main() { }); test('throw if any but the first is not a flag', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('apple', abbr: 'a'); parser.addOption('banana', abbr: 'b'); // Takes an argument. parser.addFlag('cherry', abbr: 'c'); @@ -450,7 +459,7 @@ void main() { }); test('throw if it has a value but the option is a flag', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('apple', abbr: 'a'); parser.addFlag('banana', abbr: 'b'); @@ -459,7 +468,7 @@ void main() { }); test('are case-sensitive', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('file', abbr: 'f'); parser.addFlag('force', abbr: 'F'); var results = parser.parse(['-f']); @@ -470,47 +479,47 @@ void main() { group('options', () { test('are parsed if present', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode'); var args = parser.parse(['--mode=release']); expect(args['mode'], equals('release')); }); test('are null if not present', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode'); var args = parser.parse([]); expect(args['mode'], isNull); }); test('default if missing', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); var args = parser.parse([]); expect(args['mode'], equals('debug')); }); test('allow the value to be separated by whitespace', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode'); var args = parser.parse(['--mode', 'release']); expect(args['mode'], equals('release')); }); test('throw if unknown', () { - var parser = new ArgParser(); + var parser = ArgParser(); throwsFormat(parser, ['--unknown']); throwsFormat(parser, ['--nobody']); // Starts with "no". }); test('throw if the arg does not include a value', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode'); throwsFormat(parser, ['--mode']); }); test('do not throw if the value looks like an option', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode'); parser.addOption('other'); @@ -522,20 +531,20 @@ void main() { }); test('do not throw if the value is in the allowed set', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', allowed: ['debug', 'release']); var args = parser.parse(['--mode=debug']); expect(args['mode'], equals('debug')); }); test('throw if the value is not in the allowed set', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('mode', allowed: ['debug', 'release']); throwsFormat(parser, ['--mode=profile']); }); test('returns last provided value', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('define'); var args = parser.parse(['--define=1', '--define=2']); expect(args['define'], equals('2')); @@ -543,8 +552,10 @@ void main() { group('returns a List', () { test('with allowMultiple', () { - var parser = new ArgParser(); - parser.addOption('define', allowMultiple: true); + var parser = ArgParser(); + parser.addOption( + 'define', allowMultiple: true, // ignore: deprecated_member_use + ); var args = parser.parse(['--define=1']); expect(args['define'], equals(['1'])); args = parser.parse(['--define=1', '--define=2']); @@ -552,7 +563,7 @@ void main() { }); test('with addMultiOption', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('define'); var args = parser.parse(['--define=1']); expect(args['define'], equals(['1'])); @@ -563,14 +574,18 @@ void main() { group('returns the default value if not explicitly set', () { test('with allowMultiple', () { - var parser = new ArgParser(); - parser.addOption('define', defaultsTo: '0', allowMultiple: true); + var parser = ArgParser(); + parser.addOption( + 'define', + defaultsTo: '0', + allowMultiple: true, // ignore: deprecated_member_use + ); var args = parser.parse(['']); expect(args['define'], equals(['0'])); }); test('with addMultiOption', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('define', defaultsTo: ['0']); var args = parser.parse(['']); expect(args['define'], equals(['0'])); @@ -578,7 +593,7 @@ void main() { }); test('are case-sensitive', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('verbose', defaultsTo: 'no'); parser.addOption('Verbose', defaultsTo: 'no'); var results = parser.parse(['--verbose', 'chatty']); @@ -589,7 +604,7 @@ void main() { group('remaining args', () { test('stops parsing args when a non-option-like arg is encountered', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('woof'); parser.addOption('meow'); parser.addOption('tweet', defaultsTo: 'bird'); @@ -602,7 +617,7 @@ void main() { }); test('consumes "--" and stops', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('woof', defaultsTo: false); parser.addOption('meow', defaultsTo: 'kitty'); @@ -615,7 +630,7 @@ void main() { test( 'with allowTrailingOptions: false, leaves "--" if not the first ' 'non-option', () { - var parser = new ArgParser(allowTrailingOptions: false); + var parser = ArgParser(allowTrailingOptions: false); parser.addFlag('woof'); var results = parser.parse(['--woof', 'stop', '--', 'arg']); diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index 99a6ba0e..e5236a78 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -24,7 +24,7 @@ class CommandRunnerWithFooterAndWrapping extends CommandRunner { @override ArgParser get argParser => _argParser; - final _argParser = new ArgParser(usageLineLength: 40); + final _argParser = ArgParser(usageLineLength: 40); CommandRunnerWithFooterAndWrapping(String executableName, String description) : super(executableName, description); @@ -99,7 +99,7 @@ class WrappingCommand extends Command { @override ArgParser get argParser => _argParser; - final _argParser = new ArgParser(usageLineLength: 40); + final _argParser = ArgParser(usageLineLength: 40); @override final name = "wrapping"; @@ -122,7 +122,7 @@ class LongCommand extends Command { @override ArgParser get argParser => _argParser; - final _argParser = new ArgParser(usageLineLength: 40); + final _argParser = ArgParser(usageLineLength: 40); @override final name = "long"; @@ -203,10 +203,10 @@ class AsyncCommand extends Command { final takesArguments = false; @override - Future run() => new Future.value().then((_) => hasRun = true); + Future run() => Future.value().then((_) => hasRun = true); } -void throwsIllegalArg(function, {String reason: null}) { +void throwsIllegalArg(function, {String reason}) { expect(function, throwsArgumentError, reason: reason); } @@ -216,7 +216,7 @@ void throwsFormat(ArgParser parser, List args) { Matcher throwsUsageException(message, usage) { return throwsA(predicate((error) { - expect(error, new isInstanceOf()); + expect(error, TypeMatcher()); expect(error.message, message); expect(error.usage, usage); return true; diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index 6a69818b..c4f2b724 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -7,14 +7,14 @@ import 'package:args/args.dart'; void main() { test('allowTrailingOptions defaults to true', () { - var parser = new ArgParser(); + var parser = ArgParser(); expect(parser.allowTrailingOptions, isTrue); }); group('when trailing options are allowed', () { var parser; setUp(() { - parser = new ArgParser(allowTrailingOptions: true); + parser = ArgParser(allowTrailingOptions: true); }); void expectThrows(List args) { @@ -81,10 +81,10 @@ void main() { }); test("uses the innermost command's trailing options behavior", () { - var parser = new ArgParser(allowTrailingOptions: true); + var parser = ArgParser(allowTrailingOptions: true); parser.addFlag('flag', abbr: 'f'); var command = - parser.addCommand('cmd', new ArgParser(allowTrailingOptions: false)); + parser.addCommand('cmd', ArgParser(allowTrailingOptions: false)); command.addFlag('verbose', abbr: 'v'); var results = parser.parse(['a', '-f', 'b']); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 11b0f348..1e4c75cf 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -2,13 +2,13 @@ // 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. -import 'package:test/test.dart'; import 'package:args/args.dart'; +import 'package:test/test.dart'; void main() { group('ArgParser.usage', () { test('negatable flags show "no-" in title', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', help: 'The mode'); validateUsage(parser, ''' @@ -17,7 +17,7 @@ void main() { }); test('non-negatable flags don\'t show "no-" in title', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', negatable: false, help: 'The mode'); validateUsage(parser, ''' @@ -26,7 +26,7 @@ void main() { }); test('if there are no abbreviations, there is no column for them', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', help: 'The mode'); validateUsage(parser, ''' @@ -35,7 +35,7 @@ void main() { }); test('options are lined up past abbreviations', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', abbr: 'm', help: 'The mode'); parser.addOption('long', help: 'Lacks an abbreviation'); @@ -46,7 +46,7 @@ void main() { }); test('help text is lined up past the longest option', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', abbr: 'm', help: 'Lined up with below'); parser.addOption('a-really-long-name', help: 'Its help text'); @@ -57,7 +57,7 @@ void main() { }); test('leading empty lines are ignored in help text', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', help: '\n\n\n\nAfter newlines'); validateUsage(parser, ''' @@ -66,7 +66,7 @@ void main() { }); test('trailing empty lines are ignored in help text', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('mode', help: 'Before newlines\n\n\n\n'); validateUsage(parser, ''' @@ -75,7 +75,7 @@ void main() { }); test('options are documented in the order they were added', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('zebra', help: 'First'); parser.addFlag('monkey', help: 'Second'); parser.addFlag('wombat', help: 'Third'); @@ -88,7 +88,7 @@ void main() { }); test('the default value for a flag is shown if on', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('affirm', help: 'Should be on', defaultsTo: true); parser.addFlag('negate', help: 'Should be off', defaultsTo: false); parser.addFlag('null', help: 'Should be null', defaultsTo: null); @@ -103,13 +103,17 @@ void main() { }); test('the default value for an option with no allowed list is shown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('single', help: 'Can be anything', defaultsTo: 'whatevs'); parser.addMultiOption('multiple', help: 'Can be anything', defaultsTo: ['whatevs']); - parser.addOption('allow-multi', - help: 'Can be anything', defaultsTo: 'whatevs', allowMultiple: true); + parser.addOption( + 'allow-multi', + help: 'Can be anything', + defaultsTo: 'whatevs', + allowMultiple: true, // ignore: deprecated_member_use + ); validateUsage(parser, ''' --single Can be anything @@ -125,7 +129,7 @@ void main() { test('multiple default values for an option with no allowed list are shown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('any', help: 'Can be anything', defaultsTo: ['some', 'stuff']); @@ -137,12 +141,15 @@ void main() { test('no default values are shown for a multi option with an empty default', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('implicit', help: 'Implicit default'); parser .addMultiOption('explicit', help: 'Explicit default', defaultsTo: []); - parser.addOption('allow-multi', - help: 'Implicit with allowMultiple', allowMultiple: true); + parser.addOption( + 'allow-multi', + help: 'Implicit with allowMultiple', + allowMultiple: true, // ignore: deprecated_member_use + ); validateUsage(parser, ''' --implicit Implicit default @@ -152,7 +159,7 @@ void main() { }); test('the value help is shown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('out', abbr: 'o', help: 'Where to write file', valueHelp: 'path'); @@ -162,7 +169,7 @@ void main() { }); test('the allowed list is shown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('suit', help: 'Like in cards', allowed: ['spades', 'clubs', 'hearts', 'diamonds']); @@ -174,7 +181,7 @@ void main() { }); test('the default is highlighted in the allowed list', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', @@ -187,7 +194,7 @@ void main() { }); test('multiple defaults are highlighted in the allowed list', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('suit', help: 'Like in cards', defaultsTo: ['clubs', 'diamonds'], @@ -200,7 +207,7 @@ void main() { }); test('the allowed help is shown', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('suit', help: 'Like in cards', allowed: [ 'spades', 'clubs', @@ -224,7 +231,7 @@ void main() { }); test('the default is highlighted in the allowed help', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('suit', help: 'Like in cards', defaultsTo: 'clubs', @@ -252,7 +259,7 @@ void main() { }); test('multiple defaults are highlighted in the allowed help', () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addMultiOption('suit', help: 'Like in cards', defaultsTo: [ 'clubs', 'hearts' @@ -279,7 +286,7 @@ void main() { }); test("hidden options don't appear in the help", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addOption('first', help: 'The first option'); parser.addOption('second', hide: true); parser.addOption('third', help: 'The third option'); @@ -291,7 +298,7 @@ void main() { }); test("hidden flags don't appear in the help", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('first', help: 'The first flag'); parser.addFlag('second', hide: true); parser.addFlag('third', help: 'The third flag'); @@ -303,7 +310,7 @@ void main() { }); test("hidden options don't affect spacing", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('first', help: 'The first flag'); parser.addFlag('second-very-long-option', hide: true); parser.addFlag('third', help: 'The third flag'); @@ -315,7 +322,7 @@ void main() { }); test("help strings are not wrapped if usageLineLength is null", () { - var parser = new ArgParser(usageLineLength: null); + var parser = ArgParser(usageLineLength: null); parser.addFlag('long', help: 'The flag with a really long help text that will not ' 'be wrapped.'); @@ -326,7 +333,7 @@ void main() { test("help strings are wrapped properly when usageLineLength is specified", () { - var parser = new ArgParser(usageLineLength: 60); + var parser = ArgParser(usageLineLength: 60); parser.addFlag('long', help: 'The flag with a really long help text that will be wrapped.'); parser.addFlag('longNewline', @@ -372,7 +379,7 @@ void main() { test( "help strings are wrapped with at 10 chars when usageLineLength is " "smaller than available space", () { - var parser = new ArgParser(usageLineLength: 1); + var parser = ArgParser(usageLineLength: 1); parser.addFlag('long', help: 'The flag with a really long help text that will be wrapped.'); parser.addFlag('longNewline', @@ -424,7 +431,7 @@ void main() { group("separators", () { test("separates options where it's placed", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('zebra', help: 'First'); parser.addSeparator('Primate:'); parser.addFlag('monkey', help: 'Second'); @@ -443,7 +450,7 @@ void main() { }); test("doesn't add extra newlines after a multiline option", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('zebra', help: 'Multi\nline'); parser.addSeparator('Primate:'); parser.addFlag('monkey', help: 'Second'); @@ -458,7 +465,7 @@ void main() { }); test("doesn't add newlines if it's the first component", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addSeparator('Equine:'); parser.addFlag('zebra', help: 'First'); @@ -469,7 +476,7 @@ void main() { }); test("doesn't add trailing newlines if it's the last component", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addFlag('zebra', help: 'First'); parser.addSeparator('Primate:'); @@ -481,7 +488,7 @@ void main() { }); test("adds a newline after another separator", () { - var parser = new ArgParser(); + var parser = ArgParser(); parser.addSeparator('First'); parser.addSeparator('Second'); @@ -505,7 +512,7 @@ String unindentString(String text) { var lines = text.split('\n'); // Count the indentation of the last line. - var whitespace = new RegExp('^ *'); + var whitespace = RegExp('^ *'); var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; // Drop the last line. It only exists for specifying indentation. @@ -517,15 +524,13 @@ String unindentString(String text) { if (line.length <= indent) { // It's short, so it must be nothing but whitespace. if (line.trim() != '') { - throw new ArgumentError( - 'Line "$line" does not have enough indentation.'); + throw ArgumentError('Line "$line" does not have enough indentation.'); } lines[i] = ''; } else { if (line.substring(0, indent).trim() != '') { - throw new ArgumentError( - 'Line "$line" does not have enough indentation.'); + throw ArgumentError('Line "$line" does not have enough indentation.'); } lines[i] = line.substring(indent); diff --git a/pkgs/args/test/utils_test.dart b/pkgs/args/test/utils_test.dart index 7c6463ae..71f28fe1 100644 --- a/pkgs/args/test/utils_test.dart +++ b/pkgs/args/test/utils_test.dart @@ -2,10 +2,9 @@ // 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. +import 'package:args/src/utils.dart'; import 'package:test/test.dart'; -import '../lib/src/utils.dart'; - const _lineLength = 40; const _longLine = "This is a long line that needs to be wrapped."; final _longLineWithNewlines = "This is a long line with newlines that\n" From a598954361cec23573e605701f1dc8df28948ef5 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 25 Jan 2019 11:09:53 -0800 Subject: [PATCH 158/263] All deprecated usage from this package (dart-lang/args#112) --- pkgs/args/analysis_options.yaml | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 6473c166..b4877475 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -1,4 +1,7 @@ include: package:pedantic/analysis_options.yaml +analyzer: + errors: + deprecated_member_use_from_same_package: ignore linter: rules: - avoid_empty_else From 8b7b4d75f1df220e39580b468234a988dd02a511 Mon Sep 17 00:00:00 2001 From: Todd Volkert Date: Thu, 14 Mar 2019 08:30:12 -0700 Subject: [PATCH 159/263] Support usageLineLength in CommandRunner (dart-lang/args#115) --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/lib/command_runner.dart | 5 +++-- pkgs/args/test/command_runner_test.dart | 19 +++++++++++++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 4707541f..e0cefde1 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 1.5.2 + +* Added support for `usageLineLength` in `CommandRunner` + ## 1.5.1 * Added more comprehensive word wrapping when `usageLineLength` is set. diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 5ca40840..4962a176 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -75,9 +75,10 @@ class CommandRunner { /// available via [Command.globalResults]. Commands should be registered with /// [addCommand] rather than directly on the parser. ArgParser get argParser => _argParser; - final _argParser = ArgParser(); + final ArgParser _argParser; - CommandRunner(this.executableName, this.description) { + CommandRunner(this.executableName, this.description, {int usageLineLength}) + : _argParser = ArgParser(usageLineLength: usageLineLength) { argParser.addFlag('help', abbr: 'h', negatable: false, help: 'Print this usage information.'); addCommand(HelpCommand()); diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index e9a3cfbf..553271a4 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -135,6 +135,25 @@ Available commands: Run "test help " for more information about a command.""")); }); + + test("respects usageLineLength", () { + runner = CommandRunner("name", "desc", usageLineLength: 35); + expect(runner.usage, equals(""" +desc + +Usage: name [arguments] + +Global options: +-h, --help Print this usage + information. + +Available commands: + help Display help information + for name. + +Run "name help " for more +information about a command.""")); + }); }); test("usageException splits up the message and usage", () { From 8ed2321b74ecd76b13a8cf8f13922a429968a028 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 12 Apr 2019 09:41:37 -0700 Subject: [PATCH 160/263] dartfmt --- pkgs/args/.travis.yml | 11 +++++------ pkgs/args/analysis_options.yaml | 1 - pkgs/args/test/test_utils.dart | 6 +++--- pkgs/args/test/utils_test.dart | 4 ++-- 4 files changed, 10 insertions(+), 12 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index 85cef181..5eab673d 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -1,13 +1,12 @@ language: dart dart: - - 2.0.0 - - stable - - dev +- 2.0.0 +- dev dart_task: - - test: -p vm - - dartanalyzer: --fatal-infos --fatal-warnings . +- test: -p vm +- dartanalyzer: --fatal-infos --fatal-warnings . matrix: include: @@ -20,4 +19,4 @@ branches: cache: directories: - - $HOME/.pub-cache + - $HOME/.pub-cache diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index b4877475..c356c2b0 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -33,7 +33,6 @@ linter: - prefer_generic_function_type_aliases - prefer_is_not_empty - slash_for_doc_comments - - super_goes_last - test_types_in_equals - throw_in_finally - type_init_formals diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index e5236a78..cd6c1e5a 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -129,9 +129,9 @@ class LongCommand extends Command { @override final description = "This command has a long description that needs to be " - "wrapped sometimes.\nIt has embedded newlines,\n" - " and indented lines that also need to be wrapped and have their " - "indentation preserved.\n" + + "wrapped sometimes.\nIt has embedded newlines,\n" + " and indented lines that also need to be wrapped and have their " + "indentation preserved.\n" + ("0123456789" * 10); @override diff --git a/pkgs/args/test/utils_test.dart b/pkgs/args/test/utils_test.dart index 71f28fe1..035289e8 100644 --- a/pkgs/args/test/utils_test.dart +++ b/pkgs/args/test/utils_test.dart @@ -8,11 +8,11 @@ import 'package:test/test.dart'; const _lineLength = 40; const _longLine = "This is a long line that needs to be wrapped."; final _longLineWithNewlines = "This is a long line with newlines that\n" - "needs to be wrapped.\n\n" + + "needs to be wrapped.\n\n" + "0123456789" * 5; final _indentedLongLineWithNewlines = " This is an indented long line with newlines that\n" - "needs to be wrapped.\n\tAnd preserves tabs.\n \n " + + "needs to be wrapped.\n\tAnd preserves tabs.\n \n " + "0123456789" * 5; const _shortLine = "Short line."; const _indentedLongLine = " This is an indented long line that needs to be " From f09e5d9826f05eafc0bad8a4aa1ed0bd8e756ecc Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 10 May 2019 08:35:47 -0700 Subject: [PATCH 161/263] fix latest pedantic lints --- pkgs/args/lib/src/utils.dart | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index da705b17..09319ede 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -127,7 +127,9 @@ List wrapTextAsLines(String text, {int start = 0, int length}) { result.add(line.substring(currentLineStart, i).trim()); // Skip any intervening whitespace. - while (isWhitespace(line, i) && i < line.length) i++; + while (isWhitespace(line, i) && i < line.length) { + i++; + } currentLineStart = i; lastWhitespace = null; From 764f494226abd6fe8e810cc32875fc8e19d2e6cb Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 31 May 2019 08:37:38 -0700 Subject: [PATCH 162/263] Fix deprecated URLs (dart-lang/args#117) Remove superfluous codereview.settings --- pkgs/args/CHANGELOG.md | 2 +- pkgs/args/README.md | 29 ++++++++++++++--------------- pkgs/args/codereview.settings | 3 --- 3 files changed, 15 insertions(+), 19 deletions(-) delete mode 100644 pkgs/args/codereview.settings diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index e0cefde1..98d13e37 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -215,7 +215,7 @@ ## 0.12.0+1 * Remove the documentation link from the pubspec so this is linked to - pub.dartlang.org by default. + pub.dev by default. ## 0.12.0 diff --git a/pkgs/args/README.md b/pkgs/args/README.md index df7591a1..8dbef191 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -55,8 +55,6 @@ allowed set of values. When you do, the parser throws an [`ArgParserException`][ArgParserException] if the value for an option is not in the allowed set. Here's an example of specifying allowed values: -[ArgParserException]: https://www.dartdocs.org/documentation/args/latest/args/ArgParserException-class.html - ```dart parser.addOption('mode', allowed: ['debug', 'release']); ``` @@ -387,16 +385,17 @@ The resulting string looks something like this: [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces -[ArgParser]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser -[ArgResults]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults -[CommandRunner]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.CommandRunner -[Command]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command -[UsageException]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.UsageException -[addOption]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addOption -[addFlag]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addFlag -[parse]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_parse -[rest]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgResults@id_rest -[addCommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_addCommand -[getUsage]: http://www.dartdocs.org/documentation/args/latest/index.html#args/args.ArgParser@id_getUsage -[addSubcommand]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command@id_addSubcommand -[run]: http://www.dartdocs.org/documentation/args/latest/index.html#args/command_runner.Command@id_run +[ArgParser]: https://pub.dev/documentation/args/latest/args/ArgParser/ArgParser.html +[ArgParserException]: https://pub.dev/documentation/args/latest/args/ArgParserException-class.html +[ArgResults]: https://pub.dev/documentation/args/latest/args/ArgResults-class.html +[CommandRunner]: https://pub.dev/documentation/args/latest/command_runner/CommandRunner-class.html +[Command]: https://pub.dev/documentation/args/latest/command_runner/Command-class.html +[UsageException]: https://pub.dev/documentation/args/latest/command_runner/UsageException-class.html +[addOption]: https://pub.dev/documentation/args/latest/args/ArgParser/addOption.html +[addFlag]: https://pub.dev/documentation/args/latest/args/ArgParser/addFlag.html +[parse]: https://pub.dev/documentation/args/latest/args/ArgParser/parse.html +[rest]: https://pub.dev/documentation/args/latest/args/ArgResults/rest.html +[addCommand]: https://pub.dev/documentation/args/latest/args/ArgParser/addCommand.html +[usage]: https://pub.dev/documentation/args/latest/args/ArgParser/usage.html +[addSubcommand]: https://pub.dev/documentation/args/latest/command_runner/Command/addSubcommand.html +[run]: https://pub.dev/documentation/args/latest/command_runner/CommandRunner/run.html diff --git a/pkgs/args/codereview.settings b/pkgs/args/codereview.settings deleted file mode 100644 index da6054d3..00000000 --- a/pkgs/args/codereview.settings +++ /dev/null @@ -1,3 +0,0 @@ -CODE_REVIEW_SERVER: http://codereview.chromium.org/ -VIEW_VC: https://github.com/dart-lang/args/commit/ -CC_LIST: reviews@dartlang.org \ No newline at end of file From 14d191824cf389d6614faf98357245d85bdd1b97 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 31 May 2019 10:21:06 -0700 Subject: [PATCH 163/263] Prepare to release 1.5.2 (dart-lang/args#118) --- pkgs/args/pubspec.yaml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index e400bd4d..9924b24d 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,10 +1,10 @@ name: args -version: 1.5.2-dev +version: 1.5.2 author: "Dart Team " homepage: https://github.com/dart-lang/args -description: > - Library for defining parsers for parsing raw command-line arguments into - a set of options and values using GNU and POSIX style options. +description: >- + Library for defining parsers for parsing raw command-line arguments into a set + of options and values using GNU and POSIX style options. environment: sdk: '>=2.0.0 <3.0.0' From 33550f65942c0a1410864d760ff9a01451cb6ac5 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 29 Jul 2019 16:06:55 -0700 Subject: [PATCH 164/263] Remove linter rules included in pedantic 1.8.0 --- pkgs/args/analysis_options.yaml | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index c356c2b0..090b8b22 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -4,8 +4,6 @@ analyzer: deprecated_member_use_from_same_package: ignore linter: rules: - - avoid_empty_else - - avoid_init_to_null - avoid_null_checks_in_equality_operators - avoid_unused_constructor_parameters - await_only_futures @@ -14,8 +12,6 @@ linter: - constant_identifier_names - control_flow_in_finally - directives_ordering - - empty_catches - - empty_constructor_bodies - empty_statements - hash_and_equals - implementation_imports @@ -28,16 +24,9 @@ linter: - package_api_docs - package_names - package_prefixed_library_names - - prefer_equal_for_default_values - prefer_final_fields - prefer_generic_function_type_aliases - - prefer_is_not_empty - - slash_for_doc_comments - test_types_in_equals - throw_in_finally - - type_init_formals - unnecessary_brace_in_string_interps - - unnecessary_const - - unnecessary_new - unrelated_type_equality_checks - - valid_regexps From b2f4225d86c22ee1f0ae4864b87a0e3dec189bc0 Mon Sep 17 00:00:00 2001 From: Phil Quitslund Date: Thu, 12 Sep 2019 05:17:39 -0700 Subject: [PATCH 165/263] remove optional `new`s --- pkgs/args/README.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index 8dbef191..b483c3f0 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -7,7 +7,7 @@ in both server-side and client-side apps. First create an [ArgParser][]: - var parser = new ArgParser(); + var parser = ArgParser(); Then define a set of options on that parser using [addOption()][addOption] and [addFlag()][addFlag]. Here's the minimal way to create an option named "name": @@ -95,7 +95,7 @@ instance of [ArgResults][], a map-like object that contains the values of the parsed options. ```dart -var parser = new ArgParser(); +var parser = ArgParser(); parser.addOption('mode'); parser.addFlag('verbose', defaultsTo: true); var results = parser.parse(['--mode', 'debug', 'something', 'else']); @@ -173,7 +173,7 @@ By default, an option has only a single value, with later option values overriding earlier ones; for example: ```dart -var parser = new ArgParser(); +var parser = ArgParser(); parser.addOption('mode'); var results = parser.parse(['--mode', 'on', '--mode', 'off']); print(results['mode']); // prints 'off' @@ -184,7 +184,7 @@ option can occur multiple times, and the `parse()` method returns a list of values: ```dart -var parser = new ArgParser(); +var parser = ArgParser(); parser.addMultiOption('mode'); var results = parser.parse(['--mode', 'on', '--mode', 'off']); print(results['mode']); // prints '[on, off]' @@ -193,7 +193,7 @@ print(results['mode']); // prints '[on, off]' By default, values for a multi-valued option may also be separated with commas: ```dart -var parser = new ArgParser(); +var parser = ArgParser(); parser.addOption('mode', allowMultiple: true); var results = parser.parse(['--mode', 'on,off']); print(results['mode']); // prints '[on, off]' @@ -216,7 +216,7 @@ option passed to the command. You can add a command using the [addCommand][] method: ```dart -var parser = new ArgParser(); +var parser = ArgParser(); var command = parser.addCommand('commit'); ``` @@ -225,8 +225,8 @@ specific to that command. If you already have an [ArgParser][] for the command's options, you can pass it in: ```dart -var parser = new ArgParser(); -var command = new ArgParser(); +var parser = ArgParser(); +var command = ArgParser(); parser.addCommand('commit', command); ``` @@ -251,7 +251,7 @@ example, given the above parser, `"git -a commit"` is *not* valid. The parser tries to find the right-most command that accepts an option. For example: ```dart -var parser = new ArgParser(); +var parser = ArgParser(); parser.addFlag('all', abbr: 'a'); var command = parser.addCommand('commit'); command.addFlag('all', abbr: 'a'); @@ -273,9 +273,9 @@ support for dispatching to [Command][]s based on command-line arguments, as well as handling `--help` flags and invalid arguments. For example: ```dart -var runner = new CommandRunner("git", "Distributed version control.") - ..addCommand(new CommitCommand()) - ..addCommand(new StashCommand()) +var runner = CommandRunner("git", "Distributed version control.") + ..addCommand(CommitCommand()) + ..addCommand(StashCommand()) ..run(['commit', '-a']); // Calls [CommitCommand.run()] ``` @@ -312,8 +312,8 @@ class StashCommand extends Command { final String description = "Stash changes in the working directory."; StashCommand() { - addSubcommand(new StashSaveCommand()); - addSubcommand(new StashListCommand()); + addSubcommand(StashSaveCommand()); + addSubcommand(StashListCommand()); } } ``` From 57068b583c5fedb46099b32e0648680424542e57 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 6 Dec 2019 14:15:34 -0800 Subject: [PATCH 166/263] Fix newly enforced package:pedantic lints (dart-lang/args#126) - always_declare_return_types - annotate_overrides - prefer_conditional_assignment - prefer_if_null_operators - prefer_single_quotes - prefer_spread_collections - unnecessary_this - use_function_type_syntax_for_parameters Bump min SDK to 2.3.0 to allow spreads in collection literals. Drop unused author field from pubspec. Restructure a string to avoid a line over 80 characters. --- pkgs/args/.travis.yml | 2 +- pkgs/args/example/test_runner.dart | 2 +- pkgs/args/lib/command_runner.dart | 26 +-- pkgs/args/lib/src/allow_anything_parser.dart | 23 +- pkgs/args/lib/src/arg_parser.dart | 25 ++- pkgs/args/lib/src/arg_results.dart | 6 +- pkgs/args/lib/src/help_command.dart | 8 +- pkgs/args/lib/src/option.dart | 29 ++- pkgs/args/lib/src/parser.dart | 4 +- pkgs/args/lib/src/usage.dart | 4 +- pkgs/args/lib/src/usage_exception.dart | 2 +- pkgs/args/pubspec.yaml | 5 +- pkgs/args/test/allow_anything_test.dart | 10 +- pkgs/args/test/command_runner_test.dart | 214 +++++++++---------- pkgs/args/test/command_test.dart | 64 +++--- pkgs/args/test/parse_test.dart | 8 +- pkgs/args/test/test_utils.dart | 57 ++--- pkgs/args/test/trailing_options_test.dart | 2 +- pkgs/args/test/usage_test.dart | 12 +- pkgs/args/test/utils_test.dart | 196 ++++++++--------- 20 files changed, 356 insertions(+), 343 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index 5eab673d..a80f7c7b 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -1,7 +1,7 @@ language: dart dart: -- 2.0.0 +- 2.3.0 - dev dart_task: diff --git a/pkgs/args/example/test_runner.dart b/pkgs/args/example/test_runner.dart index d983c867..f6009156 100644 --- a/pkgs/args/example/test_runner.dart +++ b/pkgs/args/example/test_runner.dart @@ -11,7 +11,7 @@ import 'dart:io'; import 'package:args/args.dart'; -main() { +void main() { var parser = ArgParser(); parser.addSeparator('===== Platform'); diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 4962a176..c314abed 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -33,13 +33,13 @@ class CommandRunner { /// /// Defaults to "$executableName `arguments`". Subclasses can /// override this for a more specific template. - String get invocation => "$executableName [arguments]"; + String get invocation => '$executableName [arguments]'; /// Generates a string displaying usage information for the executable. /// /// This includes usage for the global arguments as well as a list of /// top-level commands. - String get usage => _wrap("$description\n\n") + _usageWithoutDescription; + String get usage => _wrap('$description\n\n') + _usageWithoutDescription; /// An optional footer for [usage]. /// @@ -49,7 +49,7 @@ class CommandRunner { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { - var usagePrefix = "Usage:"; + var usagePrefix = 'Usage:'; var buffer = StringBuffer(); buffer.writeln( '$usagePrefix ${_wrap(invocation, hangingIndent: usagePrefix.length)}\n'); @@ -96,7 +96,7 @@ class CommandRunner { /// Adds [Command] as a top-level command to this runner. void addCommand(Command command) { - var names = [command.name]..addAll(command.aliases); + var names = [command.name, ...command.aliases]; for (var name in names) { _commands[name] = command; argParser.addCommand(name, command.argParser); @@ -175,7 +175,7 @@ class CommandRunner { command._globalResults = topLevelResults; command._argResults = argResults; commands = command._subcommands; - commandString += " ${argResults.name}"; + commandString += ' ${argResults.name}'; if (argResults['help']) { command.printUsage(); @@ -220,7 +220,7 @@ abstract class Command { /// [CommandRunner.usage]. /// /// This defaults to the first line of [description]. - String get summary => description.split("\n").first; + String get summary => description.split('\n').first; /// A single-line template for how to invoke this command (e.g. `"pub get /// `package`"`). @@ -231,10 +231,10 @@ abstract class Command { } parents.add(runner.executableName); - var invocation = parents.reversed.join(" "); + var invocation = parents.reversed.join(' '); return _subcommands.isNotEmpty - ? "$invocation [arguments]" - : "$invocation [arguments]"; + ? '$invocation [arguments]' + : '$invocation [arguments]'; } /// The command's parent command, if this is a subcommand. @@ -283,7 +283,7 @@ abstract class Command { /// /// This includes usage for the command's arguments as well as a list of /// subcommands, if there are any. - String get usage => _wrap("$description\n\n") + _usageWithoutDescription; + String get usage => _wrap('$description\n\n') + _usageWithoutDescription; /// An optional footer for [usage]. /// @@ -299,7 +299,7 @@ abstract class Command { /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { var length = argParser.usageLineLength; - var usagePrefix = "Usage: "; + var usagePrefix = 'Usage: '; var buffer = StringBuffer() ..writeln( usagePrefix + _wrap(invocation, hangingIndent: usagePrefix.length)) @@ -373,12 +373,12 @@ abstract class Command { /// The return value is wrapped in a `Future` if necessary and returned by /// [CommandRunner.runCommand]. FutureOr run() { - throw UnimplementedError(_wrap("Leaf command $this must implement run().")); + throw UnimplementedError(_wrap('Leaf command $this must implement run().')); } /// Adds [Command] as a subcommand of this. void addSubcommand(Command command) { - var names = [command.name]..addAll(command.aliases); + var names = [command.name, ...command.aliases]; for (var name in names) { _subcommands[name] = command; argParser.addCommand(name, command.argParser); diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index d8937e7e..46f7e128 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -9,28 +9,36 @@ import 'parser.dart'; /// An ArgParser that treats *all input* as non-option arguments. class AllowAnythingParser implements ArgParser { + @override Map get options => const {}; + @override Map get commands => const {}; + @override bool get allowTrailingOptions => false; + @override bool get allowsAnything => true; + @override int get usageLineLength => null; + @override ArgParser addCommand(String name, [ArgParser parser]) { throw UnsupportedError( "ArgParser.allowAnything().addCommands() isn't supported."); } + @override void addFlag(String name, {String abbr, String help, bool defaultsTo = false, bool negatable = true, - void callback(bool value), + void Function(bool) callback, bool hide = false}) { throw UnsupportedError( "ArgParser.allowAnything().addFlag() isn't supported."); } + @override void addOption(String name, {String abbr, String help, @@ -46,6 +54,7 @@ class AllowAnythingParser implements ArgParser { "ArgParser.allowAnything().addOption() isn't supported."); } + @override void addMultiOption(String name, {String abbr, String help, @@ -53,28 +62,34 @@ class AllowAnythingParser implements ArgParser { Iterable allowed, Map allowedHelp, Iterable defaultsTo, - void callback(List values), + void Function(List) callback, bool splitCommas = true, bool hide = false}) { throw UnsupportedError( "ArgParser.allowAnything().addMultiOption() isn't supported."); } + @override void addSeparator(String text) { throw UnsupportedError( "ArgParser.allowAnything().addSeparator() isn't supported."); } + @override ArgResults parse(Iterable args) => Parser(null, this, args.toList()).parse(); + @override String getUsage() => usage; - String get usage => ""; + @override + String get usage => ''; - getDefault(String option) { + @override + dynamic getDefault(String option) { throw ArgumentError('No option named $option'); } + @override Option findByAbbreviation(String abbr) => null; } diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 24ef2b44..eda76ed4 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -71,12 +71,11 @@ class ArgParser { ArgParser._(Map options, Map commands, {bool allowTrailingOptions = true, this.usageLineLength}) - : this._options = options, - this.options = UnmodifiableMapView(options), - this._commands = commands, - this.commands = UnmodifiableMapView(commands), - this.allowTrailingOptions = - allowTrailingOptions != null ? allowTrailingOptions : false; + : _options = options, + options = UnmodifiableMapView(options), + _commands = commands, + commands = UnmodifiableMapView(commands), + allowTrailingOptions = allowTrailingOptions ?? false; /// Defines a command. /// @@ -89,7 +88,7 @@ class ArgParser { throw ArgumentError('Duplicate command "$name".'); } - if (parser == null) parser = ArgParser(); + parser ??= ArgParser(); _commands[name] = parser; return parser; } @@ -127,7 +126,7 @@ class ArgParser { String help, bool defaultsTo = false, bool negatable = true, - void callback(bool value), + void Function(bool) callback, bool hide = false}) { _addOption( name, @@ -191,8 +190,8 @@ class ArgParser { Map allowedHelp, String defaultsTo, Function callback, - @Deprecated("Use addMultiOption() instead.") bool allowMultiple = false, - @Deprecated("Use addMultiOption() instead.") bool splitCommas, + @Deprecated('Use addMultiOption() instead.') bool allowMultiple = false, + @Deprecated('Use addMultiOption() instead.') bool splitCommas, bool hide = false}) { if (!allowMultiple && splitCommas != null) { throw ArgumentError( @@ -259,7 +258,7 @@ class ArgParser { Iterable allowed, Map allowedHelp, Iterable defaultsTo, - void callback(List values), + void Function(List) callback, bool splitCommas = true, bool hide = false}) { _addOption( @@ -326,7 +325,7 @@ class ArgParser { /// Generates a string displaying usage information for the defined options. /// /// This is basically the help text shown on the command line. - @Deprecated("Replaced with get usage. getUsage() will be removed in args 1.0") + @Deprecated('Replaced with get usage. getUsage() will be removed in args 1.0') String getUsage() => usage; /// Generates a string displaying usage information for the defined options. @@ -338,7 +337,7 @@ class ArgParser { /// Get the default value for an option. Useful after parsing to test if the /// user specified something other than the default. - getDefault(String option) { + dynamic getDefault(String option) { if (!options.containsKey(option)) { throw ArgumentError('No option named $option'); } diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 8e7205f0..ca0f493e 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -55,11 +55,11 @@ class ArgResults { /// Creates a new [ArgResults]. ArgResults._(this._parser, this._parsed, this.name, this.command, List rest, List arguments) - : this.rest = UnmodifiableListView(rest), - this.arguments = UnmodifiableListView(arguments); + : rest = UnmodifiableListView(rest), + arguments = UnmodifiableListView(arguments); /// Gets the parsed command-line option named [name]. - operator [](String name) { + dynamic operator [](String name) { if (!_parser.options.containsKey(name)) { throw ArgumentError('Could not find an option named "$name".'); } diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index b119431a..cf68c3f9 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -9,14 +9,14 @@ import '../command_runner.dart'; /// This command displays help information for the various subcommands. class HelpCommand extends Command { @override - final name = "help"; + final name = 'help'; @override String get description => - "Display help information for ${runner.executableName}."; + 'Display help information for ${runner.executableName}.'; @override - String get invocation => "${runner.executableName} help [command]"; + String get invocation => '${runner.executableName} help [command]'; @override T run() { @@ -49,7 +49,7 @@ class HelpCommand extends Command { command = commands[name]; commands = command.subcommands; - commandString += " $name"; + commandString += ' $name'; } command.printUsage(); diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index a8496c38..897f9683 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -37,7 +37,7 @@ class Option { /// `-avalue`. final String abbr; - @Deprecated("Use abbr instead.") + @Deprecated('Use abbr instead.') String get abbreviation => abbr; /// A description of this option. @@ -53,10 +53,10 @@ class Option { final Map allowedHelp; /// The value this option will have if the user doesn't explicitly pass it in - final defaultsTo; + final dynamic defaultsTo; - @Deprecated("Use defaultsTo instead.") - get defaultValue => defaultsTo; + @Deprecated('Use defaultsTo instead.') + dynamic get defaultValue => defaultsTo; /// Whether this flag's value can be set to `false`. /// @@ -101,14 +101,13 @@ class Option { {this.negatable, bool splitCommas, this.hide = false}) - : this.allowed = allowed == null ? null : List.unmodifiable(allowed), - this.allowedHelp = + : allowed = allowed == null ? null : List.unmodifiable(allowed), + allowedHelp = allowedHelp == null ? null : Map.unmodifiable(allowedHelp), - this.type = type, + type = type, // If the user doesn't specify [splitCommas], it defaults to true for // multiple options. - this.splitCommas = - splitCommas == null ? type == OptionType.multiple : splitCommas { + splitCommas = splitCommas ?? type == OptionType.multiple { if (name.isEmpty) { throw ArgumentError('Name cannot be empty.'); } else if (name.startsWith('-')) { @@ -153,9 +152,9 @@ class OptionType { /// An option that can only be `true` or `false`. /// /// The presence of the option name itself in the argument list means `true`. - static const flag = OptionType._("OptionType.flag"); + static const flag = OptionType._('OptionType.flag'); - @Deprecated("Use OptionType.flag instead.") + @Deprecated('Use OptionType.flag instead.') static const FLAG = flag; // ignore: constant_identifier_names /// An option that takes a single value. @@ -167,9 +166,9 @@ class OptionType { /// --mode=debug /// /// If the option is passed more than once, the last one wins. - static const single = OptionType._("OptionType.single"); + static const single = OptionType._('OptionType.single'); - @Deprecated("Use OptionType.single instead.") + @Deprecated('Use OptionType.single instead.') static const SINGLE = single; // ignore: constant_identifier_names /// An option that allows multiple values. @@ -180,9 +179,9 @@ class OptionType { /// /// In the parsed `ArgResults`, a multiple-valued option will always return /// a list, even if one or no values were passed. - static const multiple = OptionType._("OptionType.multiple"); + static const multiple = OptionType._('OptionType.multiple'); - @Deprecated("Use OptionType.multiple instead.") + @Deprecated('Use OptionType.multiple instead.') static const MULTIPLE = multiple; // ignore: constant_identifier_names final String name; diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 6895300b..8df25350 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -75,7 +75,7 @@ class Parser { } on ArgParserException catch (error) { if (commandName == null) rethrow; throw ArgParserException( - error.message, [commandName]..addAll(error.commands)); + error.message, [commandName, ...error.commands]); } // All remaining arguments were passed to command so clear them here. @@ -275,7 +275,7 @@ class Parser { var list = results.putIfAbsent(option.name, () => []); if (option.splitCommas) { - for (var element in value.split(",")) { + for (var element in value.split(',')) { _validateAllowed(option, element); list.add(element); } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index cdac6d3d..932935d6 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -70,7 +70,7 @@ class Usage { for (var optionOrSeparator in optionsAndSeparators) { if (optionOrSeparator is String) { // Ensure that there's always a blank line before a separator. - if (buffer.isNotEmpty) buffer.write("\n\n"); + if (buffer.isNotEmpty) buffer.write('\n\n'); buffer.write(optionOrSeparator); newlinesNeeded = 1; continue; @@ -134,7 +134,7 @@ class Usage { result = '--${option.name}'; } - if (option.valueHelp != null) result += "=<${option.valueHelp}>"; + if (option.valueHelp != null) result += '=<${option.valueHelp}>'; return result; } diff --git a/pkgs/args/lib/src/usage_exception.dart b/pkgs/args/lib/src/usage_exception.dart index 7d783dbc..fc8910ef 100644 --- a/pkgs/args/lib/src/usage_exception.dart +++ b/pkgs/args/lib/src/usage_exception.dart @@ -9,5 +9,5 @@ class UsageException implements Exception { UsageException(this.message, this.usage); @override - String toString() => "$message\n\n$usage"; + String toString() => '$message\n\n$usage'; } diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 9924b24d..9d4301e3 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,13 +1,12 @@ name: args -version: 1.5.2 -author: "Dart Team " +version: 1.5.3-dev homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. environment: - sdk: '>=2.0.0 <3.0.0' + sdk: '>=2.3.0 <3.0.0' dev_dependencies: pedantic: ^1.4.0 diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index ccf11e8c..efaf0935 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -19,14 +19,14 @@ void main() { expect(parser.allowsAnything, isTrue); expect(parser.usage, isEmpty); expect(parser.getUsage(), isEmpty); // ignore: deprecated_member_use - expect(parser.findByAbbreviation("a"), isNull); + expect(parser.findByAbbreviation('a'), isNull); }); test('mutation methods throw errors', () { - expect(() => parser.addCommand("command"), throwsUnsupportedError); - expect(() => parser.addFlag("flag"), throwsUnsupportedError); - expect(() => parser.addOption("option"), throwsUnsupportedError); - expect(() => parser.addSeparator("==="), throwsUnsupportedError); + expect(() => parser.addCommand('command'), throwsUnsupportedError); + expect(() => parser.addFlag('flag'), throwsUnsupportedError); + expect(() => parser.addOption('option'), throwsUnsupportedError); + expect(() => parser.addSeparator('==='), throwsUnsupportedError); }); test('getDefault() throws an error', () { diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 553271a4..6961945e 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'test_utils.dart'; -const _defaultUsage = """ +const _defaultUsage = ''' Usage: test [arguments] Global options: @@ -16,30 +16,30 @@ Global options: Available commands: help Display help information for test. -Run "test help " for more information about a command."""; +Run "test help " for more information about a command.'''; void main() { var runner; setUp(() { - runner = CommandRunner("test", "A test command runner."); + runner = CommandRunner('test', 'A test command runner.'); }); - test(".invocation has a sane default", () { - expect(runner.invocation, equals("test [arguments]")); + test('.invocation has a sane default', () { + expect(runner.invocation, equals('test [arguments]')); }); - group(".usage", () { - test("returns the usage string", () { - expect(runner.usage, equals(""" + group('.usage', () { + test('returns the usage string', () { + expect(runner.usage, equals(''' A test command runner. -$_defaultUsage""")); +$_defaultUsage''')); }); - test("contains custom commands", () { + test('contains custom commands', () { runner.addCommand(FooCommand()); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -51,13 +51,13 @@ Available commands: foo Set a value. help Display help information for test. -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); - test("truncates newlines in command descriptions by default", () { + test('truncates newlines in command descriptions by default', () { runner.addCommand(MultilineCommand()); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -69,13 +69,13 @@ Available commands: help Display help information for test. multiline Multi -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); - test("supports newlines in command summaries", () { + test('supports newlines in command summaries', () { runner.addCommand(MultilineSummaryCommand()); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -88,13 +88,13 @@ Available commands: multiline Multi line. -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); - test("contains custom options", () { - runner.argParser.addFlag("foo", help: "Do something."); + test('contains custom options', () { + runner.argParser.addFlag('foo', help: 'Do something.'); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -106,22 +106,22 @@ Global options: Available commands: help Display help information for test. -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); test("doesn't print hidden commands", () { runner.addCommand(HiddenCommand()); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. -$_defaultUsage""")); +$_defaultUsage''')); }); test("doesn't print aliases", () { runner.addCommand(AliasedCommand()); - expect(runner.usage, equals(""" + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -133,12 +133,12 @@ Available commands: aliased Set a value. help Display help information for test. -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); - test("respects usageLineLength", () { - runner = CommandRunner("name", "desc", usageLineLength: 35); - expect(runner.usage, equals(""" + test('respects usageLineLength', () { + runner = CommandRunner('name', 'desc', usageLineLength: 35); + expect(runner.usage, equals(''' desc Usage: name [arguments] @@ -152,210 +152,210 @@ Available commands: for name. Run "name help " for more -information about a command.""")); +information about a command.''')); }); }); - test("usageException splits up the message and usage", () { - expect(() => runner.usageException("message"), - throwsUsageException("message", _defaultUsage)); + test('usageException splits up the message and usage', () { + expect(() => runner.usageException('message'), + throwsUsageException('message', _defaultUsage)); }); - group("run()", () { - test("runs a command", () { + group('run()', () { + test('runs a command', () { var command = FooCommand(); runner.addCommand(command); expect( - runner.run(["foo"]).then((_) { + runner.run(['foo']).then((_) { expect(command.hasRun, isTrue); }), completes); }); - test("runs an asynchronous command", () { + test('runs an asynchronous command', () { var command = AsyncCommand(); runner.addCommand(command); expect( - runner.run(["async"]).then((_) { + runner.run(['async']).then((_) { expect(command.hasRun, isTrue); }), completes); }); - test("runs a command with a return value", () { - var runner = CommandRunner("test", ""); + test('runs a command with a return value', () { + var runner = CommandRunner('test', ''); var command = ValueCommand(); runner.addCommand(command); - expect(runner.run(["foo"]), completion(equals(12))); + expect(runner.run(['foo']), completion(equals(12))); }); - test("runs a command with an asynchronous return value", () { - var runner = CommandRunner("test", ""); + test('runs a command with an asynchronous return value', () { + var runner = CommandRunner('test', ''); var command = AsyncValueCommand(); runner.addCommand(command); - expect(runner.run(["foo"]), completion(equals("hi"))); + expect(runner.run(['foo']), completion(equals('hi'))); }); - test("runs a hidden comand", () { + test('runs a hidden comand', () { var command = HiddenCommand(); runner.addCommand(command); expect( - runner.run(["hidden"]).then((_) { + runner.run(['hidden']).then((_) { expect(command.hasRun, isTrue); }), completes); }); - test("runs an aliased comand", () { + test('runs an aliased comand', () { var command = AliasedCommand(); runner.addCommand(command); expect( - runner.run(["als"]).then((_) { + runner.run(['als']).then((_) { expect(command.hasRun, isTrue); }), completes); }); - test("runs a subcommand", () { + test('runs a subcommand', () { var command = AsyncCommand(); runner.addCommand(FooCommand()..addSubcommand(command)); expect( - runner.run(["foo", "async"]).then((_) { + runner.run(['foo', 'async']).then((_) { expect(command.hasRun, isTrue); }), completes); }); - group("with --help", () { - test("with no command prints the usage", () { - expect(() => runner.run(["--help"]), prints(""" + group('with --help', () { + test('with no command prints the usage', () { + expect(() => runner.run(['--help']), prints(''' A test command runner. $_defaultUsage -""")); +''')); }); - test("with a preceding command prints the usage for that command", () { + test('with a preceding command prints the usage for that command', () { var command = FooCommand(); runner.addCommand(command); - expect(() => runner.run(["foo", "--help"]), prints(""" + expect(() => runner.run(['foo', '--help']), prints(''' Set a value. Usage: test foo [arguments] -h, --help Print this usage information. Run "test help" to see global options. -""")); +''')); }); - test("with a following command prints the usage for that command", () { + test('with a following command prints the usage for that command', () { var command = FooCommand(); runner.addCommand(command); - expect(() => runner.run(["--help", "foo"]), prints(""" + expect(() => runner.run(['--help', 'foo']), prints(''' Set a value. Usage: test foo [arguments] -h, --help Print this usage information. Run "test help" to see global options. -""")); +''')); }); }); - group("with help command", () { - test("with no command prints the usage", () { - expect(() => runner.run(["help"]), prints(""" + group('with help command', () { + test('with no command prints the usage', () { + expect(() => runner.run(['help']), prints(''' A test command runner. $_defaultUsage -""")); +''')); }); - test("with a command prints the usage for that command", () { + test('with a command prints the usage for that command', () { var command = FooCommand(); runner.addCommand(command); - expect(() => runner.run(["help", "foo"]), prints(""" + expect(() => runner.run(['help', 'foo']), prints(''' Set a value. Usage: test foo [arguments] -h, --help Print this usage information. Run "test help" to see global options. -""")); +''')); }); - test("prints its own usage", () { - expect(() => runner.run(["help", "help"]), prints(""" + test('prints its own usage', () { + expect(() => runner.run(['help', 'help']), prints(''' Display help information for test. Usage: test help [command] -h, --help Print this usage information. Run "test help" to see global options. -""")); +''')); }); }); - group("with an invalid argument", () { - test("at the root throws the root usage", () { + group('with an invalid argument', () { + test('at the root throws the root usage', () { expect( - runner.run(["--asdf"]), + runner.run(['--asdf']), throwsUsageException( 'Could not find an option named "asdf".', '$_defaultUsage')); }); - test("for a command throws the command usage", () { + test('for a command throws the command usage', () { var command = FooCommand(); runner.addCommand(command); - expect(runner.run(["foo", "--asdf"]), - throwsUsageException('Could not find an option named "asdf".', """ + expect(runner.run(['foo', '--asdf']), + throwsUsageException('Could not find an option named "asdf".', ''' Usage: test foo [arguments] -h, --help Print this usage information. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); }); }); - group("with a footer", () { + group('with a footer', () { setUp(() { - runner = CommandRunnerWithFooter("test", "A test command runner."); + runner = CommandRunnerWithFooter('test', 'A test command runner.'); }); - test("includes the footer in the usage string", () { - expect(runner.usage, equals(""" + test('includes the footer in the usage string', () { + expect(runner.usage, equals(''' A test command runner. $_defaultUsage -Also, footer!""")); +Also, footer!''')); }); - test("includes the footer in usage errors", () { + test('includes the footer in usage errors', () { expect( - runner.run(["--bad"]), + runner.run(['--bad']), throwsUsageException('Could not find an option named "bad".', - "$_defaultUsage\nAlso, footer!")); + '$_defaultUsage\nAlso, footer!')); }); }); - group("with a footer and wrapping", () { + group('with a footer and wrapping', () { setUp(() { runner = - CommandRunnerWithFooterAndWrapping("test", "A test command runner."); + CommandRunnerWithFooterAndWrapping('test', 'A test command runner.'); }); - test("includes the footer in the usage string", () { - expect(runner.usage, equals(""" + test('includes the footer in the usage string', () { + expect(runner.usage, equals(''' A test command runner. Usage: test [arguments] @@ -375,12 +375,12 @@ we can check wrapping on long footer messages. And make sure that they preserve -newlines properly.""")); +newlines properly.''')); }); - test("includes the footer in usage errors", () { - expect(runner.run(["--bad"]), - throwsUsageException('Could not find an option named "bad".', """ + test('includes the footer in usage errors', () { + expect(runner.run(['--bad']), + throwsUsageException('Could not find an option named "bad".', ''' Usage: test [arguments] Global options: @@ -398,21 +398,21 @@ we can check wrapping on long footer messages. And make sure that they preserve -newlines properly.""")); +newlines properly.''')); }); }); - group("throws a useful error when", () { - test("arg parsing fails", () { + group('throws a useful error when', () { + test('arg parsing fails', () { expect( - runner.run(["--bad"]), + runner.run(['--bad']), throwsUsageException( 'Could not find an option named "bad".', _defaultUsage)); }); test("a top-level command doesn't exist", () { expect( - runner.run(["bad"]), + runner.run(['bad']), throwsUsageException( 'Could not find a command named "bad".', _defaultUsage)); }); @@ -420,8 +420,8 @@ newlines properly.""")); test("a subcommand doesn't exist", () { runner.addCommand(FooCommand()..addSubcommand(AsyncCommand())); - expect(runner.run(["foo bad"]), - throwsUsageException('Could not find a command named "foo bad".', """ + expect(runner.run(['foo bad']), + throwsUsageException('Could not find a command named "foo bad".', ''' Usage: test [arguments] Global options: @@ -431,32 +431,32 @@ Available commands: foo Set a value. help Display help information for test. -Run "test help " for more information about a command.""")); +Run "test help " for more information about a command.''')); }); test("a subcommand wasn't passed", () { runner.addCommand(FooCommand()..addSubcommand(AsyncCommand())); - expect(runner.run(["foo"]), - throwsUsageException('Missing subcommand for "test foo".', """ + expect(runner.run(['foo']), + throwsUsageException('Missing subcommand for "test foo".', ''' Usage: test foo [arguments] -h, --help Print this usage information. Available subcommands: async Set a value asynchronously. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); test("a command that doesn't take arguments was given them", () { runner.addCommand(FooCommand()); - expect(runner.run(["foo", "bar"]), - throwsUsageException('Command "foo" does not take any arguments.', """ + expect(runner.run(['foo', 'bar']), + throwsUsageException('Command "foo" does not take any arguments.', ''' Usage: test foo [arguments] -h, --help Print this usage information. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); }); } diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index b78a7399..a5c44947 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -12,56 +12,56 @@ void main() { foo = FooCommand(); // Make sure [Command.runner] is set up. - CommandRunner("test", "A test command runner.").addCommand(foo); + CommandRunner('test', 'A test command runner.').addCommand(foo); }); - group(".invocation has a sane default", () { - test("without subcommands", () { - expect(foo.invocation, equals("test foo [arguments]")); + group('.invocation has a sane default', () { + test('without subcommands', () { + expect(foo.invocation, equals('test foo [arguments]')); }); - test("with subcommands", () { + test('with subcommands', () { foo.addSubcommand(AsyncCommand()); - expect(foo.invocation, equals("test foo [arguments]")); + expect(foo.invocation, equals('test foo [arguments]')); }); - test("for a subcommand", () { + test('for a subcommand', () { var async = AsyncCommand(); foo.addSubcommand(async); - expect(async.invocation, equals("test foo async [arguments]")); + expect(async.invocation, equals('test foo async [arguments]')); }); }); - group(".usage", () { - test("returns the usage string", () { - expect(foo.usage, equals(""" + group('.usage', () { + test('returns the usage string', () { + expect(foo.usage, equals(''' Set a value. Usage: test foo [arguments] -h, --help Print this usage information. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); - test("contains custom options", () { - foo.argParser.addFlag("flag", help: "Do something."); + test('contains custom options', () { + foo.argParser.addFlag('flag', help: 'Do something.'); - expect(foo.usage, equals(""" + expect(foo.usage, equals(''' Set a value. Usage: test foo [arguments] -h, --help Print this usage information. --[no-]flag Do something. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); test("doesn't print hidden subcommands", () { foo.addSubcommand(AsyncCommand()); foo.addSubcommand(HiddenCommand()); - expect(foo.usage, equals(""" + expect(foo.usage, equals(''' Set a value. Usage: test foo [arguments] @@ -70,13 +70,13 @@ Usage: test foo [arguments] Available subcommands: async Set a value asynchronously. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); test("doesn't print subcommand aliases", () { foo.addSubcommand(AliasedCommand()); - expect(foo.usage, equals(""" + expect(foo.usage, equals(''' Set a value. Usage: test foo [arguments] @@ -85,18 +85,18 @@ Usage: test foo [arguments] Available subcommands: aliased Set a value. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); - test("wraps long command descriptions with subcommands", () { + test('wraps long command descriptions with subcommands', () { var wrapping = WrappingCommand(); // Make sure [Command.runner] is set up. - CommandRunner("longtest", "A long-lined test command runner.") + CommandRunner('longtest', 'A long-lined test command runner.') .addCommand(wrapping); wrapping.addSubcommand(LongCommand()); - expect(wrapping.usage, equals(""" + expect(wrapping.usage, equals(''' This command overrides the argParser so that it will wrap long lines. @@ -111,17 +111,17 @@ Available subcommands: wrapped sometimes. Run "longtest help" to see global -options.""")); +options.''')); }); - test("wraps long command descriptions", () { + test('wraps long command descriptions', () { var longCommand = LongCommand(); // Make sure [Command.runner] is set up. - CommandRunner("longtest", "A long-lined test command runner.") + CommandRunner('longtest', 'A long-lined test command runner.') .addCommand(longCommand); - expect(longCommand.usage, equals(""" + expect(longCommand.usage, equals(''' This command has a long description that needs to be wrapped sometimes. It has embedded newlines, @@ -137,20 +137,20 @@ Usage: longtest long [arguments] information. Run "longtest help" to see global -options.""")); +options.''')); }); }); - test("usageException splits up the message and usage", () { + test('usageException splits up the message and usage', () { expect( - () => foo.usageException("message"), throwsUsageException("message", """ + () => foo.usageException('message'), throwsUsageException('message', ''' Usage: test foo [arguments] -h, --help Print this usage information. -Run "test help" to see global options.""")); +Run "test help" to see global options.''')); }); - test("considers a command hidden if all its subcommands are hidden", () { + test('considers a command hidden if all its subcommands are hidden', () { foo.addSubcommand(HiddenCommand()); expect(foo.hidden, isTrue); }); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index d8b0e484..9c874879 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -153,7 +153,7 @@ void main() { expect(a, isNull); }); - group("with allowMultiple", () { + group('with allowMultiple', () { test('for multiple present, options are invoked with value as a list', () { var a; @@ -255,7 +255,7 @@ void main() { }); }); - group("with addMultiOption", () { + group('with addMultiOption', () { test('for multiple present, options are invoked with value as a list', () { var a; @@ -429,7 +429,7 @@ void main() { }); group('throw if a comma-separated value is not allowed', () { - test("with allowMultiple", () { + test('with allowMultiple', () { var parser = ArgParser(); parser.addOption( 'mode', @@ -440,7 +440,7 @@ void main() { throwsFormat(parser, ['-mdebug,profile']); }); - test("with addMultiOption", () { + test('with addMultiOption', () { var parser = ArgParser(); parser .addMultiOption('mode', abbr: 'm', allowed: ['debug', 'release']); diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index cd6c1e5a..0aa566ff 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -10,7 +10,7 @@ import 'package:test/test.dart'; class CommandRunnerWithFooter extends CommandRunner { @override - String get usageFooter => "Also, footer!"; + String get usageFooter => 'Also, footer!'; CommandRunnerWithFooter(String executableName, String description) : super(executableName, description); @@ -18,9 +18,10 @@ class CommandRunnerWithFooter extends CommandRunner { class CommandRunnerWithFooterAndWrapping extends CommandRunner { @override - String get usageFooter => "LONG footer! " - "This is a long footer, so we can check wrapping on long footer messages.\n\n" - "And make sure that they preserve newlines properly."; + String get usageFooter => 'LONG footer! ' + 'This is a long footer, so we can check wrapping on long footer messages.' + '\n\n' + 'And make sure that they preserve newlines properly.'; @override ArgParser get argParser => _argParser; @@ -34,10 +35,10 @@ class FooCommand extends Command { var hasRun = false; @override - final name = "foo"; + final name = 'foo'; @override - final description = "Set a value."; + final description = 'Set a value.'; @override final takesArguments = false; @@ -50,10 +51,10 @@ class FooCommand extends Command { class ValueCommand extends Command { @override - final name = "foo"; + final name = 'foo'; @override - final description = "Return a value."; + final description = 'Return a value.'; @override final takesArguments = false; @@ -64,26 +65,26 @@ class ValueCommand extends Command { class AsyncValueCommand extends Command { @override - final name = "foo"; + final name = 'foo'; @override - final description = "Return a future."; + final description = 'Return a future.'; @override final takesArguments = false; @override - Future run() async => "hi"; + Future run() async => 'hi'; } class MultilineCommand extends Command { var hasRun = false; @override - final name = "multiline"; + final name = 'multiline'; @override - final description = "Multi\nline."; + final description = 'Multi\nline.'; @override final takesArguments = false; @@ -102,11 +103,11 @@ class WrappingCommand extends Command { final _argParser = ArgParser(usageLineLength: 40); @override - final name = "wrapping"; + final name = 'wrapping'; @override final description = - "This command overrides the argParser so that it will wrap long lines."; + 'This command overrides the argParser so that it will wrap long lines.'; @override final takesArguments = false; @@ -125,14 +126,14 @@ class LongCommand extends Command { final _argParser = ArgParser(usageLineLength: 40); @override - final name = "long"; + final name = 'long'; @override - final description = "This command has a long description that needs to be " - "wrapped sometimes.\nIt has embedded newlines,\n" - " and indented lines that also need to be wrapped and have their " - "indentation preserved.\n" + - ("0123456789" * 10); + final description = 'This command has a long description that needs to be ' + 'wrapped sometimes.\nIt has embedded newlines,\n' + ' and indented lines that also need to be wrapped and have their ' + 'indentation preserved.\n' + + ('0123456789' * 10); @override final takesArguments = false; @@ -152,10 +153,10 @@ class HiddenCommand extends Command { var hasRun = false; @override - final name = "hidden"; + final name = 'hidden'; @override - final description = "Set a value."; + final description = 'Set a value.'; @override final hidden = true; @@ -173,16 +174,16 @@ class AliasedCommand extends Command { var hasRun = false; @override - final name = "aliased"; + final name = 'aliased'; @override - final description = "Set a value."; + final description = 'Set a value.'; @override final takesArguments = false; @override - final aliases = const ["alias", "als"]; + final aliases = const ['alias', 'als']; @override void run() { @@ -194,10 +195,10 @@ class AsyncCommand extends Command { var hasRun = false; @override - final name = "async"; + final name = 'async'; @override - final description = "Set a value asynchronously."; + final description = 'Set a value asynchronously.'; @override final takesArguments = false; diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index c4f2b724..174a1d03 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -19,7 +19,7 @@ void main() { void expectThrows(List args) { expect(() => parser.parse(args), throwsFormatException, - reason: "with allowTrailingOptions: true"); + reason: 'with allowTrailingOptions: true'); } test('collects non-options in rest', () { diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 1e4c75cf..64d3afd3 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -321,7 +321,7 @@ void main() { '''); }); - test("help strings are not wrapped if usageLineLength is null", () { + test('help strings are not wrapped if usageLineLength is null', () { var parser = ArgParser(usageLineLength: null); parser.addFlag('long', help: 'The flag with a really long help text that will not ' @@ -331,7 +331,7 @@ void main() { '''); }); - test("help strings are wrapped properly when usageLineLength is specified", + test('help strings are wrapped properly when usageLineLength is specified', () { var parser = ArgParser(usageLineLength: 60); parser.addFlag('long', @@ -377,8 +377,8 @@ void main() { }); test( - "help strings are wrapped with at 10 chars when usageLineLength is " - "smaller than available space", () { + 'help strings are wrapped with at 10 chars when usageLineLength is ' + 'smaller than available space', () { var parser = ArgParser(usageLineLength: 1); parser.addFlag('long', help: 'The flag with a really long help text that will be wrapped.'); @@ -429,7 +429,7 @@ void main() { '''); }); - group("separators", () { + group('separators', () { test("separates options where it's placed", () { var parser = ArgParser(); parser.addFlag('zebra', help: 'First'); @@ -487,7 +487,7 @@ void main() { '''); }); - test("adds a newline after another separator", () { + test('adds a newline after another separator', () { var parser = ArgParser(); parser.addSeparator('First'); parser.addSeparator('Second'); diff --git a/pkgs/args/test/utils_test.dart b/pkgs/args/test/utils_test.dart index 035289e8..50a4f9fe 100644 --- a/pkgs/args/test/utils_test.dart +++ b/pkgs/args/test/utils_test.dart @@ -6,43 +6,43 @@ import 'package:args/src/utils.dart'; import 'package:test/test.dart'; const _lineLength = 40; -const _longLine = "This is a long line that needs to be wrapped."; -final _longLineWithNewlines = "This is a long line with newlines that\n" - "needs to be wrapped.\n\n" + - "0123456789" * 5; +const _longLine = 'This is a long line that needs to be wrapped.'; +final _longLineWithNewlines = 'This is a long line with newlines that\n' + 'needs to be wrapped.\n\n' + + '0123456789' * 5; final _indentedLongLineWithNewlines = - " This is an indented long line with newlines that\n" - "needs to be wrapped.\n\tAnd preserves tabs.\n \n " + - "0123456789" * 5; -const _shortLine = "Short line."; -const _indentedLongLine = " This is an indented long line that needs to be " - "wrapped and indentation preserved."; + ' This is an indented long line with newlines that\n' + 'needs to be wrapped.\n\tAnd preserves tabs.\n \n ' + + '0123456789' * 5; +const _shortLine = 'Short line.'; +const _indentedLongLine = ' This is an indented long line that needs to be ' + 'wrapped and indentation preserved.'; void main() { - group("padding", () { - test("can pad on the right.", () { - expect(padRight("foo", 6), equals("foo ")); + group('padding', () { + test('can pad on the right.', () { + expect(padRight('foo', 6), equals('foo ')); }); }); - group("text wrapping", () { + group('text wrapping', () { test("doesn't wrap short lines.", () { expect(wrapText(_shortLine, length: _lineLength), equals(_shortLine)); }); test("doesn't wrap at all if not given a length", () { expect(wrapText(_longLine), equals(_longLine)); }); - test("able to wrap long lines", () { - expect(wrapText(_longLine, length: _lineLength), equals(""" + test('able to wrap long lines', () { + expect(wrapText(_longLine, length: _lineLength), equals(''' This is a long line that needs to be -wrapped.""")); +wrapped.''')); }); - test("wrap long lines with no whitespace", () { - expect(wrapText("0123456789" * 5, length: _lineLength), equals(""" + test('wrap long lines with no whitespace', () { + expect(wrapText('0123456789' * 5, length: _lineLength), equals(''' 0123456789012345678901234567890123456789 -0123456789""")); +0123456789''')); }); - test("refuses to wrap to a column smaller than 10 characters", () { - expect(wrapText("$_longLine " + "0123456789" * 4, length: 1), equals(""" + test('refuses to wrap to a column smaller than 10 characters', () { + expect(wrapText('$_longLine ' + '0123456789' * 4, length: 1), equals(''' This is a long line that needs @@ -51,88 +51,88 @@ wrapped. 0123456789 0123456789 0123456789 -0123456789""")); +0123456789''')); }); - test("preserves indentation", () { - expect(wrapText(_indentedLongLine, length: _lineLength), equals(""" + test('preserves indentation', () { + expect(wrapText(_indentedLongLine, length: _lineLength), equals(''' This is an indented long line that needs to be wrapped and indentation - preserved.""")); + preserved.''')); }); - test("preserves indentation and stripping trailing whitespace", () { - expect(wrapText("$_indentedLongLine ", length: _lineLength), equals(""" + test('preserves indentation and stripping trailing whitespace', () { + expect(wrapText('$_indentedLongLine ', length: _lineLength), equals(''' This is an indented long line that needs to be wrapped and indentation - preserved.""")); + preserved.''')); }); - test("wraps text with newlines", () { - expect(wrapText(_longLineWithNewlines, length: _lineLength), equals(""" + test('wraps text with newlines', () { + expect(wrapText(_longLineWithNewlines, length: _lineLength), equals(''' This is a long line with newlines that needs to be wrapped. 0123456789012345678901234567890123456789 -0123456789""")); +0123456789''')); }); - test("preserves indentation in the presence of newlines", () { + test('preserves indentation in the presence of newlines', () { expect(wrapText(_indentedLongLineWithNewlines, length: _lineLength), - equals(""" + equals(''' This is an indented long line with newlines that needs to be wrapped. \tAnd preserves tabs. 01234567890123456789012345678901234567 - 890123456789""")); + 890123456789''')); }); - test("removes trailing whitespace when wrapping", () { - expect(wrapText("$_longLine \t", length: _lineLength), equals(""" + test('removes trailing whitespace when wrapping', () { + expect(wrapText('$_longLine \t', length: _lineLength), equals(''' This is a long line that needs to be -wrapped.""")); +wrapped.''')); }); - test("preserves trailing whitespace when not wrapping", () { - expect(wrapText("$_longLine \t"), equals("$_longLine \t")); + test('preserves trailing whitespace when not wrapping', () { + expect(wrapText('$_longLine \t'), equals('$_longLine \t')); }); - test("honors hangingIndent parameter", () { + test('honors hangingIndent parameter', () { expect( - wrapText(_longLine, length: _lineLength, hangingIndent: 6), equals(""" + wrapText(_longLine, length: _lineLength, hangingIndent: 6), equals(''' This is a long line that needs to be - wrapped.""")); + wrapped.''')); }); - test("handles hangingIndent with a single unwrapped line.", () { + test('handles hangingIndent with a single unwrapped line.', () { expect(wrapText(_shortLine, length: _lineLength, hangingIndent: 6), - equals(""" -Short line.""")); + equals(''' +Short line.''')); }); test( - "handles hangingIndent with two unwrapped lines and the second is empty.", + 'handles hangingIndent with two unwrapped lines and the second is empty.', () { - expect(wrapText("$_shortLine\n", length: _lineLength, hangingIndent: 6), - equals(""" + expect(wrapText('$_shortLine\n', length: _lineLength, hangingIndent: 6), + equals(''' Short line. -""")); +''')); }); - test("honors hangingIndent parameter on already indented line.", () { + test('honors hangingIndent parameter on already indented line.', () { expect(wrapText(_indentedLongLine, length: _lineLength, hangingIndent: 6), - equals(""" + equals(''' This is an indented long line that needs to be wrapped and - indentation preserved.""")); + indentation preserved.''')); }); - test("honors hangingIndent parameter on already indented line.", () { + test('honors hangingIndent parameter on already indented line.', () { expect( wrapText(_indentedLongLineWithNewlines, length: _lineLength, hangingIndent: 6), - equals(""" + equals(''' This is an indented long line with newlines that needs to be wrapped. And preserves tabs. 01234567890123456789012345678901234567 - 890123456789""")); + 890123456789''')); }); }); - group("text wrapping as lines", () { + group('text wrapping as lines', () { test("doesn't wrap short lines.", () { expect(wrapTextAsLines(_shortLine, length: _lineLength), equals([_shortLine])); @@ -140,77 +140,77 @@ needs to be wrapped. test("doesn't wrap at all if not given a length", () { expect(wrapTextAsLines(_longLine), equals([_longLine])); }); - test("able to wrap long lines", () { + test('able to wrap long lines', () { expect(wrapTextAsLines(_longLine, length: _lineLength), - equals(["This is a long line that needs to be", "wrapped."])); + equals(['This is a long line that needs to be', 'wrapped.'])); }); - test("wrap long lines with no whitespace", () { - expect(wrapTextAsLines("0123456789" * 5, length: _lineLength), - equals(["0123456789012345678901234567890123456789", "0123456789"])); + test('wrap long lines with no whitespace', () { + expect(wrapTextAsLines('0123456789' * 5, length: _lineLength), + equals(['0123456789012345678901234567890123456789', '0123456789'])); }); - test("refuses to wrap to a column smaller than 10 characters", () { + test('refuses to wrap to a column smaller than 10 characters', () { expect( - wrapTextAsLines("$_longLine " + "0123456789" * 4, length: 1), + wrapTextAsLines('$_longLine ' + '0123456789' * 4, length: 1), equals([ - "This is a", - "long line", - "that needs", - "to be", - "wrapped.", - "0123456789", - "0123456789", - "0123456789", - "0123456789" + 'This is a', + 'long line', + 'that needs', + 'to be', + 'wrapped.', + '0123456789', + '0123456789', + '0123456789', + '0123456789' ])); }); test("doesn't preserve indentation", () { expect( wrapTextAsLines(_indentedLongLine, length: _lineLength), equals([ - "This is an indented long line that needs", - "to be wrapped and indentation preserved." + 'This is an indented long line that needs', + 'to be wrapped and indentation preserved.' ])); }); - test("strips trailing whitespace", () { + test('strips trailing whitespace', () { expect( - wrapTextAsLines("$_indentedLongLine ", length: _lineLength), + wrapTextAsLines('$_indentedLongLine ', length: _lineLength), equals([ - "This is an indented long line that needs", - "to be wrapped and indentation preserved." + 'This is an indented long line that needs', + 'to be wrapped and indentation preserved.' ])); }); - test("splits text with newlines properly", () { + test('splits text with newlines properly', () { expect( wrapTextAsLines(_longLineWithNewlines, length: _lineLength), equals([ - "This is a long line with newlines that", - "needs to be wrapped.", - "", - "0123456789012345678901234567890123456789", - "0123456789" + 'This is a long line with newlines that', + 'needs to be wrapped.', + '', + '0123456789012345678901234567890123456789', + '0123456789' ])); }); - test("does not preserves indentation in the presence of newlines", () { + test('does not preserves indentation in the presence of newlines', () { expect( wrapTextAsLines(_indentedLongLineWithNewlines, length: _lineLength), equals([ - "This is an indented long line with", - "newlines that", - "needs to be wrapped.", - "And preserves tabs.", - "", - "0123456789012345678901234567890123456789", - "0123456789" + 'This is an indented long line with', + 'newlines that', + 'needs to be wrapped.', + 'And preserves tabs.', + '', + '0123456789012345678901234567890123456789', + '0123456789' ])); }); - test("removes trailing whitespace when wrapping", () { - expect(wrapTextAsLines("$_longLine \t", length: _lineLength), - equals(["This is a long line that needs to be", "wrapped."])); + test('removes trailing whitespace when wrapping', () { + expect(wrapTextAsLines('$_longLine \t', length: _lineLength), + equals(['This is a long line that needs to be', 'wrapped.'])); }); - test("preserves trailing whitespace when not wrapping", () { + test('preserves trailing whitespace when not wrapping', () { expect( - wrapTextAsLines("$_longLine \t"), equals(["$_longLine \t"])); + wrapTextAsLines('$_longLine \t'), equals(['$_longLine \t'])); }); }); } From 3fba62e08ee1a1e78819c7edcb97722e3c8b7659 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Fri, 31 Jan 2020 15:46:24 +0100 Subject: [PATCH 167/263] Fix performance issues due to list.removeAt(0), which makes parsing O(n^2). Add a test that checks that parse time does not increase more than 30x when input size grows from 10k to 100k. The current increase is about x2, so this is a very loose check that should be reliably green. It will only trigger if we hit O(n^2) behaviour again. --- pkgs/args/CHANGELOG.md | 5 +++ pkgs/args/lib/src/allow_anything_parser.dart | 4 ++- pkgs/args/lib/src/arg_parser.dart | 2 +- pkgs/args/lib/src/parser.dart | 22 ++++++------ pkgs/args/test/parse_performance_test.dart | 35 ++++++++++++++++++++ 5 files changed, 56 insertions(+), 12 deletions(-) create mode 100644 pkgs/args/test/parse_performance_test.dart diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 98d13e37..cb2404d9 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.5.3 + +* Improve performance for large numbers of args. Parsing 10k args fell from + 250ms to 50ms; parsing 100k args fell from 15s to 100ms. + ## 1.5.2 * Added support for `usageLineLength` in `CommandRunner` diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 46f7e128..e6f13d09 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -2,6 +2,8 @@ // 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. +import 'dart:collection'; + import 'arg_parser.dart'; import 'arg_results.dart'; import 'option.dart'; @@ -77,7 +79,7 @@ class AllowAnythingParser implements ArgParser { @override ArgResults parse(Iterable args) => - Parser(null, this, args.toList()).parse(); + Parser(null, this, Queue.of(args)).parse(); @override String getUsage() => usage; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index eda76ed4..72211bac 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -320,7 +320,7 @@ class ArgParser { /// 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(Iterable args) => - Parser(null, this, args.toList()).parse(); + Parser(null, this, Queue.of(args)).parse(); /// Generates a string displaying usage information for the defined options. /// diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 8df25350..2a41f17b 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -2,6 +2,8 @@ // 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. +import 'dart:collection'; + import 'arg_parser.dart'; import 'arg_parser_exception.dart'; import 'arg_results.dart'; @@ -28,7 +30,7 @@ class Parser { final ArgParser grammar; /// The arguments being parsed. - final List args; + final Queue args; /// The remaining non-option, non-command arguments. final rest = []; @@ -42,7 +44,7 @@ class Parser { } /// The current argument being parsed. - String get current => args[0]; + String get current => args.first; /// Parses the arguments. This can only be called once. ArgResults parse() { @@ -58,7 +60,7 @@ class Parser { while (args.isNotEmpty) { if (current == '--') { // Reached the argument terminator, so stop here. - args.removeAt(0); + args.removeFirst(); break; } @@ -67,7 +69,7 @@ class Parser { var command = grammar.commands[current]; if (command != null) { validate(rest.isEmpty, 'Cannot specify arguments before a command.'); - var commandName = args.removeAt(0); + var commandName = args.removeFirst(); var commandParser = Parser(commandName, command, args, this, rest); try { @@ -92,7 +94,7 @@ class Parser { // This argument is neither option nor command, so stop parsing unless // the [allowTrailingOptions] option is set. if (!grammar.allowTrailingOptions) break; - rest.add(args.removeAt(0)); + rest.add(args.removeFirst()); } // Invoke the callbacks. @@ -116,7 +118,7 @@ class Parser { validate(args.isNotEmpty, 'Missing argument for "${option.name}".'); setOption(results, option, current); - args.removeAt(0); + args.removeFirst(); } /// Tries to parse the current argument as a "solo" option, which is a single @@ -136,7 +138,7 @@ class Parser { return parent.parseSoloOption(); } - args.removeAt(0); + args.removeFirst(); if (option.isFlag) { setFlag(results, option, true); @@ -186,7 +188,7 @@ class Parser { } } - args.removeAt(0); + args.removeFirst(); return true; } @@ -217,7 +219,7 @@ class Parser { var name = longOpt[1]; var option = grammar.options[name]; if (option != null) { - args.removeAt(0); + args.removeFirst(); if (option.isFlag) { validate(longOpt[3] == null, 'Flag option "$name" should not be given a value.'); @@ -240,7 +242,7 @@ class Parser { return parent.parseLongOption(); } - args.removeAt(0); + args.removeFirst(); validate(option.isFlag, 'Cannot negate non-flag option "$name".'); validate(option.negatable, 'Cannot negate option "$name".'); diff --git a/pkgs/args/test/parse_performance_test.dart b/pkgs/args/test/parse_performance_test.dart new file mode 100644 index 00000000..660e4d36 --- /dev/null +++ b/pkgs/args/test/parse_performance_test.dart @@ -0,0 +1,35 @@ +// Copyright (c) 2020, 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. + +import 'package:args/args.dart'; +import 'package:test/test.dart'; + +void main() { + group('ArgParser.parse()', () { + test('is fast', () { + var parser = ArgParser()..addFlag('flag'); + + var baseSize = 10000; + var baseList = List.generate(baseSize, (_) => '--flag'); + + var multiplier = 10; + var largeList = + List.generate(baseSize * multiplier, (_) => '--flag'); + var baseTime = _time(() => parser.parse(baseList)); + var largeTime = _time(() => parser.parse(largeList)); + expect(largeTime, lessThan(baseTime * multiplier * 3), + reason: + 'Comparing large data set time ${largeTime}ms to small data set time ' + '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to ' + 'increase up to ${multiplier * 3}x, but it increased ' + '${largeTime ~/ baseTime}x.'); + }); + }); +} + +int _time(void Function() function) { + var stopwatch = Stopwatch()..start(); + function(); + return stopwatch.elapsedMilliseconds; +} From 4bbe9c3b53f2561ba72772e40cf8f0becbfc34f6 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Thu, 6 Feb 2020 17:27:51 +0100 Subject: [PATCH 168/263] Address review comments. --- pkgs/args/CHANGELOG.md | 3 +-- pkgs/args/test/parse_performance_test.dart | 18 +++++++++++++++--- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index cb2404d9..7eac77dd 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,7 +1,6 @@ ## 1.5.3 -* Improve performance for large numbers of args. Parsing 10k args fell from - 250ms to 50ms; parsing 100k args fell from 15s to 100ms. +* Improve performance for large numbers of args. ## 1.5.2 diff --git a/pkgs/args/test/parse_performance_test.dart b/pkgs/args/test/parse_performance_test.dart index 660e4d36..16e412ff 100644 --- a/pkgs/args/test/parse_performance_test.dart +++ b/pkgs/args/test/parse_performance_test.dart @@ -10,14 +10,26 @@ void main() { test('is fast', () { var parser = ArgParser()..addFlag('flag'); - var baseSize = 10000; + var baseSize = 200000; var baseList = List.generate(baseSize, (_) => '--flag'); var multiplier = 10; var largeList = List.generate(baseSize * multiplier, (_) => '--flag'); - var baseTime = _time(() => parser.parse(baseList)); - var largeTime = _time(() => parser.parse(largeList)); + + var baseAction = () => parser.parse(baseList); + var largeAction = () => parser.parse(largeList); + + // Warm up JIT. + baseAction(); + largeAction(); + + var baseTime = _time(baseAction); + var largeTime = _time(largeAction); + + print('Parsed $baseSize elements in ${baseTime}ms, ' + '${baseSize * multiplier} elements in ${largeTime}ms.'); + expect(largeTime, lessThan(baseTime * multiplier * 3), reason: 'Comparing large data set time ${largeTime}ms to small data set time ' From eaf1d75a0c69a7d93c94636e9a41c15be6d5e3dd Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Wed, 26 Feb 2020 12:48:59 -0500 Subject: [PATCH 169/263] address an issue when using ArgParser.allowAnything() with CommandRunner (dart-lang/args#132) address an issue when using allowAnything --- pkgs/args/CHANGELOG.md | 4 +++- pkgs/args/lib/command_runner.dart | 8 +++++--- pkgs/args/test/allow_anything_test.dart | 11 +++++++++++ pkgs/args/test/test_utils.dart | 18 ++++++++++++++++++ 4 files changed, 37 insertions(+), 4 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 7eac77dd..b2f4c3ed 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,6 +1,8 @@ ## 1.5.3 -* Improve performance for large numbers of args. +* Improve arg parsing performance for large numbers of args. +* No longer automatically add a 'help' option to commands that don't validate + their arguments (fix #123). ## 1.5.2 diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index c314abed..0fbf6972 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -177,7 +177,7 @@ class CommandRunner { commands = command._subcommands; commandString += ' ${argResults.name}'; - if (argResults['help']) { + if (argResults.options.contains('help') && argResults['help']) { command.printUsage(); return null; } @@ -364,8 +364,10 @@ abstract class Command { List get aliases => const []; Command() { - argParser.addFlag('help', - abbr: 'h', negatable: false, help: 'Print this usage information.'); + if (!argParser.allowsAnything) { + argParser.addFlag('help', + abbr: 'h', negatable: false, help: 'Print this usage information.'); + } } /// Runs this command. diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index efaf0935..29038a79 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -3,8 +3,11 @@ // BSD-style license that can be found in the LICENSE file. import 'package:args/args.dart'; +import 'package:args/command_runner.dart'; import 'package:test/test.dart'; +import 'test_utils.dart'; + void main() { group('new ArgParser.allowAnything()', () { ArgParser parser; @@ -51,5 +54,13 @@ void main() { expect(results.command.arguments, equals(['--foo', '-abc', '--', 'bar'])); expect(results.command.name, equals('command')); }); + + test('works as a subcommand in a CommandRunner', () async { + var commandRunner = CommandRunner('command', 'Description of command'); + var command = AllowAnythingCommand(); + commandRunner..addCommand(command); + + await commandRunner.run([command.name, '--foo', '--bar', '-b', 'qux']); + }); }); } diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index 0aa566ff..8c6373fa 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -207,6 +207,24 @@ class AsyncCommand extends Command { Future run() => Future.value().then((_) => hasRun = true); } +class AllowAnythingCommand extends Command { + var hasRun = false; + + @override + final name = 'allowAnything'; + + @override + final description = 'A command using allowAnything.'; + + @override + final argParser = ArgParser.allowAnything(); + + @override + void run() { + hasRun = true; + } +} + void throwsIllegalArg(function, {String reason}) { expect(function, throwsArgumentError, reason: reason); } From 8345064febc8112ef0ec5120214d6d4b90ec61e4 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Fri, 28 Feb 2020 17:31:22 +0100 Subject: [PATCH 170/263] With benchmark. --- pkgs/args/test/parse_performance_test.dart | 70 ++++++++++++++-------- 1 file changed, 45 insertions(+), 25 deletions(-) diff --git a/pkgs/args/test/parse_performance_test.dart b/pkgs/args/test/parse_performance_test.dart index 16e412ff..e0c5313f 100644 --- a/pkgs/args/test/parse_performance_test.dart +++ b/pkgs/args/test/parse_performance_test.dart @@ -6,38 +6,58 @@ import 'package:args/args.dart'; import 'package:test/test.dart'; void main() { - group('ArgParser.parse()', () { - test('is fast', () { - var parser = ArgParser()..addFlag('flag'); + group('ArgParser.parse() is fast', () { + test('for short flags', () { + _test(ArgParser()..addFlag('short', abbr: 's'), '-s'); + }); - var baseSize = 200000; - var baseList = List.generate(baseSize, (_) => '--flag'); + test('for abbreviations', () { + _test( + ArgParser() + ..addFlag('short', abbr: 's') + ..addFlag('short2', abbr: 't') + ..addFlag('short3', abbr: 'u') + ..addFlag('short4', abbr: 'v'), + '-stuv'); + }); - var multiplier = 10; - var largeList = - List.generate(baseSize * multiplier, (_) => '--flag'); + test('for long flags', () { + _test(ArgParser()..addFlag('long-flag'), '--long-flag'); + }); - var baseAction = () => parser.parse(baseList); - var largeAction = () => parser.parse(largeList); + test('for long options with =', () { + _test(ArgParser()..addOption('long-option-name'), + '--long-option-name=long-option-value'); + }); + }); +} - // Warm up JIT. - baseAction(); - largeAction(); +void _test(ArgParser parser, String string) { + var baseSize = 50000; + var baseList = List.generate(baseSize, (_) => string); - var baseTime = _time(baseAction); - var largeTime = _time(largeAction); + var multiplier = 10; + var largeList = List.generate(baseSize * multiplier, (_) => string); - print('Parsed $baseSize elements in ${baseTime}ms, ' - '${baseSize * multiplier} elements in ${largeTime}ms.'); + var baseAction = () => parser.parse(baseList); + var largeAction = () => parser.parse(largeList); - expect(largeTime, lessThan(baseTime * multiplier * 3), - reason: - 'Comparing large data set time ${largeTime}ms to small data set time ' - '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to ' - 'increase up to ${multiplier * 3}x, but it increased ' - '${largeTime ~/ baseTime}x.'); - }); - }); + // Warm up JIT. + baseAction(); + largeAction(); + + var baseTime = _time(baseAction); + var largeTime = _time(largeAction); + + print('Parsed $baseSize elements in ${baseTime}ms, ' + '${baseSize * multiplier} elements in ${largeTime}ms.'); + + expect(largeTime, lessThan(baseTime * multiplier * 3), + reason: + 'Comparing large data set time ${largeTime}ms to small data set time ' + '${baseTime}ms. Data set increased ${multiplier}x, time is allowed to ' + 'increase up to ${multiplier * 3}x, but it increased ' + '${largeTime ~/ baseTime}x.'); } int _time(void Function() function) { From efc652f5ed3874a4cb0ff85baec9a17d6ca6ad87 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Fri, 28 Feb 2020 17:31:22 +0100 Subject: [PATCH 171/263] Improve parse speed by switching from regexps to simple string operations. 50k short flags before: 11ms, 11ms, 13ms, 11ms, 17ms after: 6ms, 5ms, 6ms, 5ms, 8ms 50k abbreviations before: 97ms, 114ms, 120ms, 111ms, 107ms after: 39ms, 37ms, 51ms, 36ms, 36ms 50k long flags before: 27ms, 26ms, 24ms, 24ms, 28ms after: 15ms, 19ms, 19ms, 15ms, 15ms 50k long options with = before: 27ms, 26ms, 27ms, 24ms, 26ms after: 15ms, 19ms, 19ms, 15ms, 15ms --- pkgs/args/lib/src/parser.dart | 88 +++++++++++++++++++++++++---------- 1 file changed, 63 insertions(+), 25 deletions(-) diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 2a41f17b..fe886870 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -9,10 +9,6 @@ import 'arg_parser_exception.dart'; import 'arg_results.dart'; import 'option.dart'; -final _soloOpt = RegExp(r'^-([a-zA-Z0-9])$'); -final _abbrOpt = RegExp(r'^-([a-zA-Z0-9]+)(.*)$'); -final _longOpt = RegExp(r'^--([a-zA-Z\-_0-9]+)(=(.*))?$'); - /// The actual argument parsing class. /// /// Unlike [ArgParser] which is really more an "arg grammar", this is the class @@ -127,14 +123,17 @@ class Parser { /// We treat this differently than collapsed abbreviations (like "-abc") to /// handle the possible value that may follow it. bool parseSoloOption() { - var soloOpt = _soloOpt.firstMatch(current); - if (soloOpt == null) return false; - - var option = grammar.findByAbbreviation(soloOpt[1]); + // Hand coded regexp: r'^-([a-zA-Z0-9])$' + // Length must be two, hyphen followed by any letter/digit. + if (current.length != 2) return false; + if (!current.startsWith('-')) return false; + var opt = current.substring(1, 2); + if (!_isLetterOrDigit(opt.codeUnitAt(0))) return false; + + var option = grammar.findByAbbreviation(opt); if (option == null) { // Walk up to the parent command if possible. - validate( - parent != null, 'Could not find an option or flag "-${soloOpt[1]}".'); + validate(parent != null, 'Could not find an option or flag "-$opt".'); return parent.parseSoloOption(); } @@ -153,12 +152,28 @@ class Parser { /// (like "-abc") or a single abbreviation with the value directly attached /// to it (like "-mrelease"). bool parseAbbreviation(Parser innermostCommand) { - var abbrOpt = _abbrOpt.firstMatch(current); - if (abbrOpt == null) return false; + // Hand coded regexp: r'^-([a-zA-Z0-9]+)(.*)$' + // Hyphen then at least one letter/digit then zero or more + // anything-but-newlines. + if (current.length < 2) return false; + if (!current.startsWith('-')) return false; + + // Find where we go from letters/digits to rest. + var index = 1; + while ( + index < current.length && _isLetterOrDigit(current.codeUnitAt(index))) { + ++index; + } + // Must be at least one letter/digit. + if (index == 1) 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 lettersAndDigits = current.substring(1, index); + var rest = current.substring(index); + if (rest.contains('\n') || rest.contains('\r')) return false; + + var c = lettersAndDigits.substring(0, 1); var first = grammar.findByAbbreviation(c); if (first == null) { // Walk up to the parent command if possible. @@ -168,22 +183,22 @@ class Parser { } 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]}'; + var value = '${lettersAndDigits.substring(1)}$rest'; 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] == '', + rest == '', 'Option "-$c" is a flag and cannot handle value ' - '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".'); + '"${lettersAndDigits.substring(1)}$rest".'); // Not an option, so all characters should be flags. // We use "innermostCommand" here so that if a parent command parses the // *first* letter, subcommands can still be found to parse the other // letters. - for (var i = 0; i < abbrOpt[1].length; i++) { - var c = abbrOpt[1].substring(i, i + 1); + for (var i = 0; i < lettersAndDigits.length; i++) { + var c = lettersAndDigits.substring(i, i + 1); innermostCommand.parseShortFlag(c); } } @@ -213,21 +228,33 @@ class Parser { /// 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() { - var longOpt = _longOpt.firstMatch(current); - if (longOpt == null) return false; + // Hand coded regexp: r'^--([a-zA-Z\-_0-9]+)(=(.*))?$' + // Two hyphens then at least one letter/digit/hyphen, optionally an equal + // sign followed by zero or more anything-but-newlines. + + if (!current.startsWith('--')) return false; + + var index = current.indexOf('='); + var name = index == -1 ? current.substring(2) : current.substring(2, index); + for (var i = 0; i != name.length; ++i) { + if (!_isLetterOrDigitOrHyphen(name.codeUnitAt(i))) return false; + } + var value = index == -1 ? null : current.substring(index + 1); + if (value != null && (value.contains('\n') || value.contains('\r'))) { + return false; + } - var name = longOpt[1]; var option = grammar.options[name]; if (option != null) { args.removeFirst(); if (option.isFlag) { - validate(longOpt[3] == null, - 'Flag option "$name" should not be given a value.'); + validate( + value == null, 'Flag option "$name" should not be given a value.'); setFlag(results, option, true); - } else if (longOpt[3] != null) { + } else if (value != null) { // We have a value like --foo=bar. - setOption(results, option, longOpt[3]); + setOption(results, option, value); } else { // Option like --foo, so look for the value as the next arg. readNextArgAsValue(option); @@ -302,3 +329,14 @@ class Parser { '"$value" is not an allowed value for option "${option.name}".'); } } + +bool _isLetterOrDigit(int codeUnit) => + // Uppercase letters. + (codeUnit >= 65 && codeUnit <= 90) || + // Lowercase letters. + (codeUnit >= 97 && codeUnit <= 122) || + // Digits. + (codeUnit >= 48 && codeUnit <= 57); + +bool _isLetterOrDigitOrHyphen(int codeUnit) => + _isLetterOrDigit(codeUnit) || codeUnit == 45; From 1f54c67f41a0184442928129eac164195fa59f02 Mon Sep 17 00:00:00 2001 From: Devon Carew Date: Fri, 28 Feb 2020 13:22:04 -0500 Subject: [PATCH 172/263] prep for publishing 1.5.3 (dart-lang/args#133) prep for publishing 1.5.3 --- pkgs/args/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 9d4301e3..3d71214b 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.3-dev +version: 1.5.3 homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From e8e6b8fd6be7e98c64c7f7f16e5e3c224b387826 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Sat, 29 Feb 2020 10:03:22 +0100 Subject: [PATCH 173/263] Address review comments. --- pkgs/args/CHANGELOG.md | 5 ++++- pkgs/args/lib/src/parser.dart | 2 +- pkgs/args/test/parse_performance_test.dart | 14 +++++++++----- 3 files changed, 14 insertions(+), 7 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index b2f4c3ed..522c3309 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,6 +1,9 @@ ## 1.5.3 -* Improve arg parsing performance for large numbers of args. +* Improve arg parsing performance: use queues instead of lists internally to + get linear instead of quadratic performance, which is important for large + numbers of args (>1000). And, use simple string manipulation instead of + regular expressions for a 1.5x improvement everywhere. * No longer automatically add a 'help' option to commands that don't validate their arguments (fix #123). diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index fe886870..8dafef94 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -127,7 +127,7 @@ class Parser { // Length must be two, hyphen followed by any letter/digit. if (current.length != 2) return false; if (!current.startsWith('-')) return false; - var opt = current.substring(1, 2); + var opt = current[1]; if (!_isLetterOrDigit(opt.codeUnitAt(0))) return false; var option = grammar.findByAbbreviation(opt); diff --git a/pkgs/args/test/parse_performance_test.dart b/pkgs/args/test/parse_performance_test.dart index e0c5313f..881fbfef 100644 --- a/pkgs/args/test/parse_performance_test.dart +++ b/pkgs/args/test/parse_performance_test.dart @@ -8,11 +8,11 @@ import 'package:test/test.dart'; void main() { group('ArgParser.parse() is fast', () { test('for short flags', () { - _test(ArgParser()..addFlag('short', abbr: 's'), '-s'); + _testParserPerformance(ArgParser()..addFlag('short', abbr: 's'), '-s'); }); test('for abbreviations', () { - _test( + _testParserPerformance( ArgParser() ..addFlag('short', abbr: 's') ..addFlag('short2', abbr: 't') @@ -22,17 +22,21 @@ void main() { }); test('for long flags', () { - _test(ArgParser()..addFlag('long-flag'), '--long-flag'); + _testParserPerformance(ArgParser()..addFlag('long-flag'), '--long-flag'); }); test('for long options with =', () { - _test(ArgParser()..addOption('long-option-name'), + _testParserPerformance(ArgParser()..addOption('long-option-name'), '--long-option-name=long-option-value'); }); }); } -void _test(ArgParser parser, String string) { +/// Tests how quickly [parser] parses [string]. +/// +/// Checks that a 10x increase in arg count does not lead to greater than 30x +/// increase in parse time. +void _testParserPerformance(ArgParser parser, String string) { var baseSize = 50000; var baseList = List.generate(baseSize, (_) => string); From 290256556852d34a735260f31025a489d2fbf127 Mon Sep 17 00:00:00 2001 From: David Morgan Date: Tue, 3 Mar 2020 14:50:27 +0100 Subject: [PATCH 174/263] Update CHANGELOG.md Co-Authored-By: Jacob MacDonald --- pkgs/args/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 522c3309..6543f248 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.5.3 +## 1.5.3-dev * Improve arg parsing performance: use queues instead of lists internally to get linear instead of quadratic performance, which is important for large From fdd98e1170ef88a07dec8b7c49adbd3a112a13b4 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 12 Mar 2020 18:16:02 -0700 Subject: [PATCH 175/263] Enforce and fix comment_references lint (dart-lang/args#138) --- pkgs/args/analysis_options.yaml | 1 + pkgs/args/lib/src/arg_parser.dart | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 090b8b22..53b584db 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -9,6 +9,7 @@ linter: - await_only_futures - camel_case_types - cancel_subscriptions + - comment_references - constant_identifier_names - control_flow_in_finally - directives_ordering diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 72211bac..2ec5f625 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -113,7 +113,7 @@ class ArgParser { /// The [callback] argument is invoked with the flag's value when the flag /// is parsed. Note that this makes argument parsing order-dependent in ways /// that are often surprising, and its use is discouraged in favor of reading - /// values from the [ArgResult]. + /// values from the [ArgResults]. /// /// If [hide] is `true`, this option won't be included in [usage]. /// @@ -170,7 +170,7 @@ class ArgParser { /// The [callback] argument is invoked with the option's value when the option /// is parsed. Note that this makes argument parsing order-dependent in ways /// that are often surprising, and its use is discouraged in favor of reading - /// values from the [ArgResult]. + /// values from the [ArgResults]. /// /// The [allowMultiple] and [splitCommas] options are deprecated; the /// [addMultiOption] method should be used instead. @@ -240,7 +240,7 @@ class ArgParser { /// The [callback] argument is invoked with the option's value when the option /// is parsed. Note that this makes argument parsing order-dependent in ways /// that are often surprising, and its use is discouraged in favor of reading - /// values from the [ArgResult]. + /// values from the [ArgResults]. /// /// If [splitCommas] is `true` (the default), multiple options may be passed /// by writing `--option a,b` in addition to `--option a --option b`. From 779215e2c11458cd3ca4b881a683dfe986cd4259 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 12 Mar 2020 18:46:20 -0700 Subject: [PATCH 176/263] Reference CommandRunner from addCommand docs (dart-lang/args#139) Closes dart-lang/args#134 The `addCommand` API is a little confusing, it doesn't impact the usage string the way you might expect. In the future we might consider hiding this API, for now call out the confusing aspect and point to the API that most folks should use. --- pkgs/args/CHANGELOG.md | 7 ++++++- pkgs/args/lib/src/arg_parser.dart | 3 +++ pkgs/args/pubspec.yaml | 2 +- 3 files changed, 10 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 6543f248..cf18c3f4 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,9 @@ -## 1.5.3-dev +## 1.5.4-dev + +* Point towards `CommandRunner` in the docs for `ArgParser.addCommand` since it + is what most authors will want to use instead. + +## 1.5.3 * Improve arg parsing performance: use queues instead of lists internally to get linear instead of quadratic performance, which is important for large diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 2ec5f625..aae89d60 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -82,6 +82,9 @@ class ArgParser { /// A command is a named argument which may in turn define its own options and /// subcommands using the given parser. If [parser] is omitted, implicitly /// creates a new one. Returns the parser for the command. + /// + /// Note that adding commands this way will not impact the [usage] string. To + /// add commands which are included in the usage string see `CommandRunner`. ArgParser addCommand(String name, [ArgParser parser]) { // Make sure the name isn't in use. if (_commands.containsKey(name)) { diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 3d71214b..6ebe018e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.3 +version: 1.5.4-dev homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From 47add098bf41fec2dae84968207bfdfe48e669ef Mon Sep 17 00:00:00 2001 From: David Morgan Date: Mon, 16 Mar 2020 15:18:54 +0100 Subject: [PATCH 177/263] Fix parsing: re-allow underscore in long names. (dart-lang/args#141) --- pkgs/args/lib/src/parser.dart | 10 +++++++--- pkgs/args/test/parse_test.dart | 9 +++++++++ 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 8dafef94..cb152881 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -237,7 +237,7 @@ class Parser { var index = current.indexOf('='); var name = index == -1 ? current.substring(2) : current.substring(2, index); for (var i = 0; i != name.length; ++i) { - if (!_isLetterOrDigitOrHyphen(name.codeUnitAt(i))) return false; + if (!_isLetterDigitHyphenOrUnderscore(name.codeUnitAt(i))) return false; } var value = index == -1 ? null : current.substring(index + 1); if (value != null && (value.contains('\n') || value.contains('\r'))) { @@ -338,5 +338,9 @@ bool _isLetterOrDigit(int codeUnit) => // Digits. (codeUnit >= 48 && codeUnit <= 57); -bool _isLetterOrDigitOrHyphen(int codeUnit) => - _isLetterOrDigit(codeUnit) || codeUnit == 45; +bool _isLetterDigitHyphenOrUnderscore(int codeUnit) => + _isLetterOrDigit(codeUnit) || + // Hyphen. + codeUnit == 45 || + // Underscore. + codeUnit == 95; diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 9c874879..96395674 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -61,6 +61,15 @@ void main() { expect(results['verbose'], isTrue); expect(results['Verbose'], isFalse); }); + + test('match letters, numbers, hyphens and underscores', () { + var parser = ArgParser(); + var allCharacters = + 'abcdefghijklmnopqrstuvwxyz-ABCDEFGHIJKLMNOPQRSTUVWXYZ_0123456789'; + parser.addFlag(allCharacters); + var results = parser.parse(['--$allCharacters']); + expect(results[allCharacters], isTrue); + }); }); group('flags negated with "no-"', () { From 0af1175bc15e1f29587291ac161d6ea93d56de5c Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 16 Mar 2020 10:15:50 -0700 Subject: [PATCH 178/263] Changelog for underscore fix, prepare to publish (dart-lang/args#142) --- pkgs/args/CHANGELOG.md | 3 ++- pkgs/args/pubspec.yaml | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index cf18c3f4..b87c5b62 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,5 +1,6 @@ -## 1.5.4-dev +## 1.5.4 +* Fix a bug with option names containing underscores. * Point towards `CommandRunner` in the docs for `ArgParser.addCommand` since it is what most authors will want to use instead. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 6ebe018e..b8b58d9a 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.4-dev +version: 1.5.4 homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From c29ccdaeba27b18ed343105d6e1f199fbcaa497f Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 16 Mar 2020 14:02:25 -0700 Subject: [PATCH 179/263] Remove extra blank lines between options (dart-lang/args#137) Fixes dart-lang/args#48 The extra blank lines imply a grouping, however the grouping is arbitrary based on which options happened to wrap, and may vary based on the terminal width. Instead keep all options without extra newlines. --- pkgs/args/CHANGELOG.md | 5 +++++ pkgs/args/lib/src/usage.dart | 6 ------ pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/usage_test.dart | 11 ----------- 4 files changed, 6 insertions(+), 18 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index b87c5b62..fd666af2 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,8 @@ +## 1.6.0-dev + +* Remove the blank lines in usage which separated the help for options that + happened to span multiple lines. + ## 1.5.4 * Fix a bug with option names containing underscores. diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 932935d6..d244cada 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -112,12 +112,6 @@ class Usage { write(2, '(defaults to "${option.defaultsTo}")'); } } - - // 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(); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index b8b58d9a..865a7a9a 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.5.4 +version: 1.6.0-dev homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 64d3afd3..45818b9a 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -96,7 +96,6 @@ void main() { validateUsage(parser, ''' --[no-]affirm Should be on (defaults to on) - --[no-]negate Should be off --[no-]null Should be null '''); @@ -118,10 +117,8 @@ void main() { validateUsage(parser, ''' --single Can be anything (defaults to "whatevs") - --multiple Can be anything (defaults to "whatevs") - --allow-multi Can be anything (defaults to "whatevs") '''); @@ -354,22 +351,17 @@ void main() { validateUsage(parser, ''' --[no-]long The flag with a really long help text that will be wrapped. - --[no-]longNewline The flag with a really long help text and newlines that will still be wrapped because it is really long. - --[no-]solid The-flag-with-no-whitespace-that-wi ll-be-wrapped-by-splitting-a-word. - --[no-]longWhitespace The flag with a really long help text and whitespace at the start. - --[no-]longTrailspace The flag with a really long help text and whitespace at the end. - --[no-]small1 a --[no-]small2 a --[no-]small3 a @@ -400,7 +392,6 @@ void main() { text that will be wrapped. - --[no-]longNewline The flag with a really @@ -414,7 +405,6 @@ void main() { because it is really long. - --[no-]solid The-flag-w ith-no-whi tespace-th @@ -422,7 +412,6 @@ void main() { -wrapped-b y-splittin g-a-word. - --[no-]small1 a --[no-]small2 a --[no-]small3 a From 857fd555bca6246a3e23d1828fa57b493ea41e4e Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Mon, 16 Mar 2020 14:22:06 -0700 Subject: [PATCH 180/263] Remove help form the list of commands in usage (dart-lang/args#140) Closes dart-lang/args#131 The `help` command is redundant against the bottom line of the usage text which explains how to use help in a more understandable way. --- pkgs/args/CHANGELOG.md | 1 + pkgs/args/lib/src/help_command.dart | 3 +++ pkgs/args/test/command_runner_test.dart | 21 ++++++++++++--------- 3 files changed, 16 insertions(+), 9 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index fd666af2..a6b05ad6 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,5 +1,6 @@ ## 1.6.0-dev +* Remove `help` from the list of commands in usage. * Remove the blank lines in usage which separated the help for options that happened to span multiple lines. diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index cf68c3f9..255f2d42 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -18,6 +18,9 @@ class HelpCommand extends Command { @override String get invocation => '${runner.executableName} help [command]'; + @override + bool get hidden => true; + @override T run() { // Show the default help if no command was specified. diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index 6961945e..ce484011 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -48,8 +48,7 @@ Global options: -h, --help Print this usage information. Available commands: - foo Set a value. - help Display help information for test. + foo Set a value. Run "test help " for more information about a command.''')); }); @@ -66,7 +65,6 @@ Global options: -h, --help Print this usage information. Available commands: - help Display help information for test. multiline Multi Run "test help " for more information about a command.''')); @@ -84,7 +82,6 @@ Global options: -h, --help Print this usage information. Available commands: - help Display help information for test. multiline Multi line. @@ -110,12 +107,20 @@ Run "test help " for more information about a command.''')); }); test("doesn't print hidden commands", () { - runner.addCommand(HiddenCommand()); + runner..addCommand(HiddenCommand())..addCommand(FooCommand()); expect(runner.usage, equals(''' A test command runner. -$_defaultUsage''')); +Usage: test [arguments] + +Global options: +-h, --help Print this usage information. + +Available commands: + foo Set a value. + +Run "test help " for more information about a command.''')); }); test("doesn't print aliases", () { @@ -131,7 +136,6 @@ Global options: Available commands: aliased Set a value. - help Display help information for test. Run "test help " for more information about a command.''')); }); @@ -428,8 +432,7 @@ Global options: -h, --help Print this usage information. Available commands: - foo Set a value. - help Display help information for test. + foo Set a value. Run "test help " for more information about a command.''')); }); From f84b80a7a4e11d911e82eedd711b5750e365cdac Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 17 Mar 2020 11:04:18 -0700 Subject: [PATCH 181/263] Prepare for publish (dart-lang/args#143) --- pkgs/args/CHANGELOG.md | 2 +- pkgs/args/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index a6b05ad6..7ea5e20f 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.6.0-dev +## 1.6.0 * Remove `help` from the list of commands in usage. * Remove the blank lines in usage which separated the help for options that diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 865a7a9a..47f5c408 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.6.0-dev +version: 1.6.0 homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From 4d9ee0423d43cbb2d80e73218d923e4a0089e129 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Tue, 23 Jun 2020 12:31:59 -0700 Subject: [PATCH 182/263] Clean up some doc comments and code style (dart-lang/args#147) - Note some argument restrictions when `name` must be a valid option. - Remove a comment that adds no information on a constructor. - Use a consistent pattern for checking valid option names - `containsKey` rather than check for a `null` value. - End a doc comment with a period. - Change a getter doc comment to a noun phrase. - Remove some repetitive details from doc comments, like types. - Add explicit `show` to all exports rather than rely on catching all package private stuff with a `hide`. - Use `.toSet()` over `Set.from` to copy a set. --- pkgs/args/lib/args.dart | 8 ++++---- pkgs/args/lib/src/arg_results.dart | 24 +++++++++++++----------- pkgs/args/lib/src/option.dart | 2 +- 3 files changed, 18 insertions(+), 16 deletions(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index 97b47b54..a49c536b 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -2,7 +2,7 @@ // 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. -export 'src/arg_parser.dart'; -export 'src/arg_parser_exception.dart'; -export 'src/arg_results.dart' hide newArgResults; -export 'src/option.dart' hide newOption; +export 'src/arg_parser.dart' show ArgParser; +export 'src/arg_parser_exception.dart' show ArgParserException; +export 'src/arg_results.dart' show ArgResults; +export 'src/option.dart' show Option; diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index ca0f493e..63450766 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -32,8 +32,8 @@ class ArgResults { /// The option values that were parsed from arguments. final Map _parsed; - /// 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`. + /// The name of the command for which these options are parsed, or `null` if + /// these are the top-level results. final String name; /// The command that was selected, or `null` if none was. @@ -49,16 +49,17 @@ class ArgResults { /// `--` was reached. final List rest; - /// The original list of arguments that were parsed. + /// The original arguments that were parsed. final List arguments; - /// Creates a new [ArgResults]. ArgResults._(this._parser, this._parsed, this.name, this.command, List rest, List arguments) : rest = UnmodifiableListView(rest), arguments = UnmodifiableListView(arguments); - /// Gets the parsed command-line option named [name]. + /// Returns the parsed ore default command-line option named [name]. + /// + /// [name] must be a valid option name in the parser. dynamic operator [](String name) { if (!_parser.options.containsKey(name)) { throw ArgumentError('Could not find an option named "$name".'); @@ -67,12 +68,12 @@ class ArgResults { return _parser.options[name].getOrDefault(_parsed[name]); } - /// Get the names of the available options as an [Iterable]. + /// The names of the available options. /// - /// This includes the options whose values were parsed or that have defaults. - /// Options that weren't present and have no default will be omitted. + /// Includes the options whose values were parsed or that have defaults. + /// Options that weren't present and have no default are omitted. Iterable get options { - var result = Set.from(_parsed.keys); + var result = _parsed.keys.toSet(); // Include the options that have defaults. _parser.options.forEach((name, option) { @@ -87,9 +88,10 @@ class ArgResults { /// /// Returns `false` if it wasn't provided and the default value or no default /// value would be used instead. + /// + /// [name] must be a valid option name in the parser. bool wasParsed(String name) { - var option = _parser.options[name]; - if (option == null) { + if (!_parser.options.containsKey(name)) { throw ArgumentError('Could not find an option named "$name".'); } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 897f9683..7fd91603 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -52,7 +52,7 @@ class Option { /// A map from values in [allowed] to documentation for those values. final Map allowedHelp; - /// The value this option will have if the user doesn't explicitly pass it in + /// The value this option will have if the user doesn't explicitly pass it. final dynamic defaultsTo; @Deprecated('Use defaultsTo instead.') From c4bf4c31d9f450274fc463f92ce2d80431f6d531 Mon Sep 17 00:00:00 2001 From: Kevin Moore Date: Fri, 9 Oct 2020 16:34:19 -0700 Subject: [PATCH 183/263] Update http URLs to https (dart-lang/args#152) Use https URLs to improve pub score. --- pkgs/args/CHANGELOG.md | 2 ++ pkgs/args/README.md | 4 ++-- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 7ea5e20f..4f324d0d 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,5 @@ +## 1.6.1-dev + ## 1.6.0 * Remove `help` from the list of commands in usage. diff --git a/pkgs/args/README.md b/pkgs/args/README.md index b483c3f0..e2d777be 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -383,8 +383,8 @@ The resulting string looks something like this: [ia32] Intel x86 ``` -[posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 -[gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces +[posix]: https://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.html#tag_12_02 +[gnu]: https://www.gnu.org/prep/standards/standards.html#Command_002dLine-Interfaces [ArgParser]: https://pub.dev/documentation/args/latest/args/ArgParser/ArgParser.html [ArgParserException]: https://pub.dev/documentation/args/latest/args/ArgParserException-class.html [ArgResults]: https://pub.dev/documentation/args/latest/args/ArgResults-class.html diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 47f5c408..68915fc1 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 1.6.0 +version: 1.6.1-dev homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From 6fb9c35fc01f60e0575bf814401315e43799b1e5 Mon Sep 17 00:00:00 2001 From: Sam Rawlins Date: Mon, 2 Nov 2020 08:07:42 -0800 Subject: [PATCH 184/263] Remove unused dart:async imports. (dart-lang/args#157) As of Dart 2.1, Future/Stream have been exported from dart:core. --- pkgs/args/test/test_utils.dart | 2 -- 1 file changed, 2 deletions(-) diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index 8c6373fa..68b96d3c 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -2,8 +2,6 @@ // 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. -import 'dart:async'; - import 'package:args/args.dart'; import 'package:args/command_runner.dart'; import 'package:test/test.dart'; From 4088d838f7d023d7d6e282d214f7a140571ddb89 Mon Sep 17 00:00:00 2001 From: Brett Sutton Date: Thu, 5 Nov 2020 12:46:40 -0800 Subject: [PATCH 185/263] Improve the docs for CommandRunner (dart-lang/args#148) --- pkgs/args/README.md | 62 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 55 insertions(+), 7 deletions(-) diff --git a/pkgs/args/README.md b/pkgs/args/README.md index e2d777be..7d5fe5ec 100644 --- a/pkgs/args/README.md +++ b/pkgs/args/README.md @@ -270,16 +270,36 @@ the left of `"commit"`, it is given to the top-level parser. If you're writing a command-based application, you can use the [CommandRunner][] and [Command][] classes to help structure it. [CommandRunner][] has built-in support for dispatching to [Command][]s based on command-line arguments, as well -as handling `--help` flags and invalid arguments. For example: +as handling `--help` flags and invalid arguments. + +When using the [CommandRunner][] it replaces the [ArgParser][]. + +In the following example we build a dart application called `dgit` that takes commands `commit` and `stash`. + +The [CommandRunner][] takes an `executableName` which is used to generate the help message. + +e.g. +`dgit commit -a` + +File `dgit.dart` ```dart -var runner = CommandRunner("git", "Distributed version control.") - ..addCommand(CommitCommand()) - ..addCommand(StashCommand()) - ..run(['commit', '-a']); // Calls [CommitCommand.run()] + +void main(List args){ + var runner = CommandRunner("dgit", "A dart implementation of distributed version control.") + ..addCommand(CommitCommand()) + ..addCommand(StashCommand()) + ..run(args); ``` -Custom commands are defined by extending the [Command][] class. For example: +When the above `run(args)` line executes it parses the command line args looking for one of the commands (`commit` or `stash`). + + + +If the [CommandRunner][] finds a matching command then the [CommandRunner][] calls the overridden `run()` method on the matching command (e.g. CommitCommand().run). + + +Commands are defined by extending the [Command][] class. For example: ```dart class CommitCommand extends Command { @@ -289,18 +309,45 @@ class CommitCommand extends Command { final description = "Record changes to the repository."; CommitCommand() { + // we can add command specific arguments here. // [argParser] is automatically created by the parent class. argParser.addFlag('all', abbr: 'a'); } // [run] may also return a Future. void run() { - // [argResults] is set before [run()] is called and contains the options + // [argResults] is set before [run()] is called and contains the flags/options // passed to this command. print(argResults['all']); } } ``` +### CommandRunner Arguments +The [CommandRunner][] allows you to specify both global args as well as command specific arguments (and even sub-command specific arguments). + +#### Global Arguments +Add argments directly to the [CommandRunner] to specify global arguments: + +Adding global arguments +```dart +var runner = CommandRunner('dgit', "A dart implementation of distributed version control."); +// add global flag +runner.addFlag('verbose', abbr: 'v', help: 'increase logging'); +``` + +#### Command specific Arguments +Add arguments to each [Command][] to specify [Command][] specific arguments. + +```dart + + CommitCommand() { + // we can add command specific arguments here. + // [argParser] is automatically created by the parent class. + argParser.addFlag('all', abbr: 'a'); + } + +``` +### SubCommands Commands can also have subcommands, which are added with [addSubcommand][]. A command with subcommands can't run its own code, so [run][] doesn't need to be @@ -318,6 +365,7 @@ class StashCommand extends Command { } ``` +### Default Help Command [CommandRunner][] automatically adds a `help` command that displays usage information for commands, as well as support for the `--help` flag for all commands. If it encounters an error parsing the arguments or processing a From 1f5c78b820e14ba7068bcf2fb4547f594fc52470 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 6 Nov 2020 09:07:38 -0800 Subject: [PATCH 186/263] Migrate to null safety (dart-lang/args#159) Initial migration to null safety, futher updates still pending and breaking changes possible. --- pkgs/args/.travis.yml | 30 +++++--- pkgs/args/CHANGELOG.md | 2 +- pkgs/args/lib/command_runner.dart | 73 ++++++++++---------- pkgs/args/lib/src/allow_anything_parser.dart | 44 ++++++------ pkgs/args/lib/src/arg_parser.dart | 73 ++++++++++---------- pkgs/args/lib/src/arg_parser_exception.dart | 2 +- pkgs/args/lib/src/arg_results.dart | 10 +-- pkgs/args/lib/src/help_command.dart | 26 +++---- pkgs/args/lib/src/option.dart | 39 ++++++----- pkgs/args/lib/src/parser.dart | 27 ++++---- pkgs/args/lib/src/usage.dart | 18 ++--- pkgs/args/lib/src/utils.dart | 8 +-- pkgs/args/pubspec.yaml | 14 ++-- pkgs/args/test/allow_anything_test.dart | 11 +-- pkgs/args/test/args_test.dart | 36 +++++----- pkgs/args/test/command_parse_test.dart | 42 +++++------ pkgs/args/test/command_runner_test.dart | 2 +- pkgs/args/test/command_test.dart | 2 +- pkgs/args/test/parse_test.dart | 5 +- pkgs/args/test/test_utils.dart | 14 ++-- pkgs/args/test/trailing_options_test.dart | 6 +- pkgs/args/test/usage_test.dart | 2 +- 22 files changed, 251 insertions(+), 235 deletions(-) diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml index a80f7c7b..bdf0b3c2 100644 --- a/pkgs/args/.travis.yml +++ b/pkgs/args/.travis.yml @@ -1,19 +1,31 @@ language: dart dart: -- 2.3.0 - dev -dart_task: -- test: -p vm -- dartanalyzer: --fatal-infos --fatal-warnings . - -matrix: +jobs: include: - - dart: dev - dart_task: dartfmt + - stage: analyze_and_format + name: "Analyze" + dart: dev + os: linux + script: dartanalyzer --fatal-warnings --fatal-infos . + - stage: analyze_and_format + name: "Format" + dart: dev + os: linux + script: dartfmt -n --set-exit-if-changed . + - stage: test + name: "Vm Tests" + dart: dev + os: linux + script: pub run test -p vm + - stage: test + name: "Web Tests" + dart: dev + os: linux + script: pub run test -p chrome -# Only building master means that we don't run two builds for each pull request. branches: only: [master] diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 4f324d0d..fcc32295 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,4 +1,4 @@ -## 1.6.1-dev +## 2.0.0-nullsafety ## 1.6.0 diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 0fbf6972..0a6566f5 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -45,7 +45,7 @@ class CommandRunner { /// /// If a subclass overrides this to return a string, it will automatically be /// added to the end of [usage]. - String get usageFooter => null; + String? get usageFooter => null; /// Returns [usage] with [description] removed from the beginning. String get _usageWithoutDescription { @@ -60,7 +60,7 @@ class CommandRunner { buffer.write(_wrap( 'Run "$executableName help " for more information about a command.')); if (usageFooter != null) { - buffer.write('\n${_wrap(usageFooter)}'); + buffer.write('\n${_wrap(usageFooter!)}'); } return buffer.toString(); } @@ -77,7 +77,7 @@ class CommandRunner { ArgParser get argParser => _argParser; final ArgParser _argParser; - CommandRunner(this.executableName, this.description, {int usageLineLength}) + CommandRunner(this.executableName, this.description, {int? usageLineLength}) : _argParser = ArgParser(usageLineLength: usageLineLength) { argParser.addFlag('help', abbr: 'h', negatable: false, help: 'Print this usage information.'); @@ -91,7 +91,7 @@ class CommandRunner { void printUsage() => print(usage); /// Throws a [UsageException] with [message]. - void usageException(String message) => + Never usageException(String message) => throw UsageException(message, _usageWithoutDescription); /// Adds [Command] as a top-level command to this runner. @@ -108,7 +108,7 @@ class CommandRunner { /// /// This always returns a [Future] in case the command is asynchronous. The /// [Future] will throw a [UsageException] if [args] was invalid. - Future run(Iterable args) => + Future run(Iterable args) => Future.sync(() => runCommand(parse(args))); /// Parses [args] and returns the result, converting an [ArgParserException] @@ -124,11 +124,10 @@ class CommandRunner { var command = commands[error.commands.first]; for (var commandName in error.commands.skip(1)) { - command = command.subcommands[commandName]; + command = command!.subcommands[commandName]; } - command.usageException(error.message); - return null; + command!.usageException(error.message); } } @@ -142,10 +141,10 @@ class CommandRunner { /// here to enable verbose logging before running the command. /// /// This returns the return value of [Command.run]. - Future runCommand(ArgResults topLevelResults) async { + Future runCommand(ArgResults topLevelResults) async { var argResults = topLevelResults; var commands = _commands; - Command command; + Command? command; var commandString = executableName; while (commands.isNotEmpty) { @@ -170,11 +169,11 @@ class CommandRunner { } // Step into the command. - argResults = argResults.command; + argResults = argResults.command!; command = commands[argResults.name]; - command._globalResults = topLevelResults; + command!._globalResults = topLevelResults; command._argResults = argResults; - commands = command._subcommands; + commands = command._subcommands as Map>; commandString += ' ${argResults.name}'; if (argResults.options.contains('help') && argResults['help']) { @@ -184,20 +183,20 @@ class CommandRunner { } if (topLevelResults['help']) { - command.printUsage(); + command!.printUsage(); return null; } // Make sure there aren't unexpected arguments. - if (!command.takesArguments && argResults.rest.isNotEmpty) { + if (!command!.takesArguments && argResults.rest.isNotEmpty) { command.usageException( 'Command "${argResults.name}" does not take any arguments.'); } - return (await command.run()) as T; + return (await command.run()) as T?; } - String _wrap(String text, {int hangingIndent}) => wrapText(text, + String _wrap(String text, {int? hangingIndent}) => wrapText(text, length: argParser.usageLineLength, hangingIndent: hangingIndent); } @@ -229,7 +228,7 @@ abstract class Command { for (var command = parent; command != null; command = command.parent) { parents.add(command.name); } - parents.add(runner.executableName); + parents.add(runner!.executableName); var invocation = parents.reversed.join(' '); return _subcommands.isNotEmpty @@ -241,31 +240,31 @@ abstract class Command { /// /// This will be `null` until [addSubcommand] has been called with /// this command. - Command get parent => _parent; - Command _parent; + Command? get parent => _parent; + Command? _parent; /// The command runner for this command. /// /// This will be `null` until [CommandRunner.addCommand] has been called with /// this command or one of its parents. - CommandRunner get runner { + CommandRunner? get runner { if (parent == null) return _runner; - return parent.runner; + return parent!.runner; } - CommandRunner _runner; + CommandRunner? _runner; /// The parsed global argument results. /// /// This will be `null` until just before [Command.run] is called. - ArgResults get globalResults => _globalResults; - ArgResults _globalResults; + ArgResults? get globalResults => _globalResults; + ArgResults? _globalResults; /// The parsed argument results for this command. /// /// This will be `null` until just before [Command.run] is called. - ArgResults get argResults => _argResults; - ArgResults _argResults; + ArgResults? get argResults => _argResults; + ArgResults? _argResults; /// The argument parser for this command. /// @@ -289,9 +288,9 @@ abstract class Command { /// /// If a subclass overrides this to return a string, it will automatically be /// added to the end of [usage]. - String get usageFooter => null; + String? get usageFooter => null; - String _wrap(String text, {int hangingIndent}) { + String _wrap(String text, {int? hangingIndent}) { return wrapText(text, length: argParser.usageLineLength, hangingIndent: hangingIndent); } @@ -316,11 +315,11 @@ abstract class Command { buffer.writeln(); buffer.write( - _wrap('Run "${runner.executableName} help" to see global options.')); + _wrap('Run "${runner!.executableName} help" to see global options.')); if (usageFooter != null) { buffer.writeln(); - buffer.write(_wrap(usageFooter)); + buffer.write(_wrap(usageFooter!)); } return buffer.toString(); @@ -374,7 +373,7 @@ abstract class Command { /// /// The return value is wrapped in a `Future` if necessary and returned by /// [CommandRunner.runCommand]. - FutureOr run() { + FutureOr? run() { throw UnimplementedError(_wrap('Leaf command $this must implement run().')); } @@ -395,7 +394,7 @@ abstract class Command { void printUsage() => print(usage); /// Throws a [UsageException] with [message]. - void usageException(String message) => + Never usageException(String message) => throw UsageException(_wrap(message), _usageWithoutDescription); } @@ -404,13 +403,13 @@ abstract class Command { /// [isSubcommand] indicates whether the commands should be called "commands" or /// "subcommands". String _getCommandUsage(Map commands, - {bool isSubcommand = false, int lineLength}) { + {bool isSubcommand = false, int? lineLength}) { // Don't include aliases. var names = - commands.keys.where((name) => !commands[name].aliases.contains(name)); + commands.keys.where((name) => !commands[name]!.aliases.contains(name)); // Filter out hidden ones, unless they are all hidden. - var visible = names.where((name) => !commands[name].hidden); + var visible = names.where((name) => !commands[name]!.hidden); if (visible.isNotEmpty) names = visible; // Show the commands alphabetically. @@ -420,7 +419,7 @@ String _getCommandUsage(Map commands, var buffer = StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); var columnStart = length + 5; for (var name in names) { - var lines = wrapTextAsLines(commands[name].summary, + var lines = wrapTextAsLines(commands[name]!.summary, start: columnStart, length: lineLength); buffer.writeln(); buffer.write(' ${padRight(name, length)} ${lines.first}'); diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index e6f13d09..16f97fd5 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -20,21 +20,21 @@ class AllowAnythingParser implements ArgParser { @override bool get allowsAnything => true; @override - int get usageLineLength => null; + int? get usageLineLength => null; @override - ArgParser addCommand(String name, [ArgParser parser]) { + ArgParser addCommand(String name, [ArgParser? parser]) { throw UnsupportedError( "ArgParser.allowAnything().addCommands() isn't supported."); } @override void addFlag(String name, - {String abbr, - String help, - bool defaultsTo = false, + {String? abbr, + String? help, + bool? defaultsTo = false, bool negatable = true, - void Function(bool) callback, + void Function(bool)? callback, bool hide = false}) { throw UnsupportedError( "ArgParser.allowAnything().addFlag() isn't supported."); @@ -42,15 +42,15 @@ class AllowAnythingParser implements ArgParser { @override void addOption(String name, - {String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, - String defaultsTo, - Function callback, + {String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, + String? defaultsTo, + Function? callback, bool allowMultiple = false, - bool splitCommas, + bool? splitCommas, bool hide = false}) { throw UnsupportedError( "ArgParser.allowAnything().addOption() isn't supported."); @@ -58,13 +58,13 @@ class AllowAnythingParser implements ArgParser { @override void addMultiOption(String name, - {String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, - Iterable defaultsTo, - void Function(List) callback, + {String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, + Iterable? defaultsTo, + void Function(List)? callback, bool splitCommas = true, bool hide = false}) { throw UnsupportedError( @@ -93,5 +93,5 @@ class AllowAnythingParser implements ArgParser { } @override - Option findByAbbreviation(String abbr) => null; + Option? findByAbbreviation(String abbr) => null; } diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index aae89d60..7ccb5b4e 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -44,7 +44,7 @@ class ArgParser { /// there is no whitespace at which to split). /// /// If null (the default), help messages are not wrapped. - final int usageLineLength; + final int? usageLineLength; /// Whether or not this parser treats unrecognized options as non-option /// arguments. @@ -56,7 +56,7 @@ class ArgParser { /// flags and options that appear after positional arguments. If it's `false`, /// the parser stops parsing as soon as it finds an argument that is neither /// an option nor a command. - factory ArgParser({bool allowTrailingOptions = true, int usageLineLength}) => + factory ArgParser({bool allowTrailingOptions = true, int? usageLineLength}) => ArgParser._({}, {}, allowTrailingOptions: allowTrailingOptions, usageLineLength: usageLineLength); @@ -75,7 +75,7 @@ class ArgParser { options = UnmodifiableMapView(options), _commands = commands, commands = UnmodifiableMapView(commands), - allowTrailingOptions = allowTrailingOptions ?? false; + allowTrailingOptions = allowTrailingOptions; /// Defines a command. /// @@ -85,7 +85,7 @@ class ArgParser { /// /// Note that adding commands this way will not impact the [usage] string. To /// add commands which are included in the usage string see `CommandRunner`. - ArgParser addCommand(String name, [ArgParser parser]) { + ArgParser addCommand(String name, [ArgParser? parser]) { // Make sure the name isn't in use. if (_commands.containsKey(name)) { throw ArgumentError('Duplicate command "$name".'); @@ -125,11 +125,11 @@ class ArgParser { /// * There is already an option named [name]. /// * There is already an option using abbreviation [abbr]. void addFlag(String name, - {String abbr, - String help, - bool defaultsTo = false, + {String? abbr, + String? help, + bool? defaultsTo = false, bool negatable = true, - void Function(bool) callback, + void Function(bool)? callback, bool hide = false}) { _addOption( name, @@ -186,15 +186,15 @@ class ArgParser { /// * There is already an option using abbreviation [abbr]. /// * [splitCommas] is passed but [allowMultiple] is `false`. void addOption(String name, - {String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, - String defaultsTo, - Function callback, + {String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, + String? defaultsTo, + Function? callback, @Deprecated('Use addMultiOption() instead.') bool allowMultiple = false, - @Deprecated('Use addMultiOption() instead.') bool splitCommas, + @Deprecated('Use addMultiOption() instead.') bool? splitCommas, bool hide = false}) { if (!allowMultiple && splitCommas != null) { throw ArgumentError( @@ -255,13 +255,13 @@ class ArgParser { /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. void addMultiOption(String name, - {String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, - Iterable defaultsTo, - void Function(List) callback, + {String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, + Iterable? defaultsTo, + void Function(List)? callback, bool splitCommas = true, bool hide = false}) { _addOption( @@ -280,16 +280,16 @@ class ArgParser { void _addOption( String name, - String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, + String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, defaultsTo, - Function callback, + Function? callback, OptionType type, {bool negatable = false, - bool splitCommas, + bool? splitCommas, bool hide = false}) { // Make sure the name isn't in use. if (_options.containsKey(name)) { @@ -341,16 +341,19 @@ class ArgParser { /// Get the default value for an option. Useful after parsing to test if the /// user specified something other than the default. dynamic getDefault(String option) { - if (!options.containsKey(option)) { + var value = options[option]; + if (value == null) { throw ArgumentError('No option named $option'); } - return options[option].defaultsTo; + return value.defaultsTo; } /// Finds the option whose abbreviation is [abbr], or `null` if no option has /// that abbreviation. - Option findByAbbreviation(String abbr) { - return options.values - .firstWhere((option) => option.abbr == abbr, orElse: () => null); + Option? findByAbbreviation(String abbr) { + for (var option in options.values) { + if (option.abbr == abbr) return option; + } + return null; } } diff --git a/pkgs/args/lib/src/arg_parser_exception.dart b/pkgs/args/lib/src/arg_parser_exception.dart index f0d57d5b..56ac851d 100644 --- a/pkgs/args/lib/src/arg_parser_exception.dart +++ b/pkgs/args/lib/src/arg_parser_exception.dart @@ -9,7 +9,7 @@ class ArgParserException extends FormatException { /// This will be empty if the error was on the root parser. final List commands; - ArgParserException(String message, [Iterable commands]) + ArgParserException(String message, [Iterable? commands]) : commands = commands == null ? const [] : List.unmodifiable(commands), super(message); } diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index 63450766..fb2d0c82 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -13,8 +13,8 @@ import 'arg_parser.dart'; ArgResults newArgResults( ArgParser parser, Map parsed, - String name, - ArgResults command, + String? name, + ArgResults? command, List rest, List arguments) { return ArgResults._(parser, parsed, name, command, rest, arguments); @@ -34,12 +34,12 @@ class ArgResults { /// The name of the command for which these options are parsed, or `null` if /// these are the top-level results. - final String name; + final String? name; /// The command that was selected, or `null` if none was. /// /// This will contain the options that were selected for that command. - final ArgResults command; + final ArgResults? command; /// The remaining command-line arguments that were not parsed as options or /// flags. @@ -65,7 +65,7 @@ class ArgResults { throw ArgumentError('Could not find an option named "$name".'); } - return _parser.options[name].getOrDefault(_parsed[name]); + return _parser.options[name]!.getOrDefault(_parsed[name]); } /// The names of the available options. diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index 255f2d42..c3c2bbf8 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -13,37 +13,37 @@ class HelpCommand extends Command { @override String get description => - 'Display help information for ${runner.executableName}.'; + 'Display help information for ${runner!.executableName}.'; @override - String get invocation => '${runner.executableName} help [command]'; + String get invocation => '${runner!.executableName} help [command]'; @override bool get hidden => true; @override - T run() { + T? run() { // Show the default help if no command was specified. - if (argResults.rest.isEmpty) { - runner.printUsage(); + if (argResults!.rest.isEmpty) { + runner!.printUsage(); return null; } // Walk the command tree to show help for the selected command or // subcommand. - var commands = runner.commands; - Command command; - var commandString = runner.executableName; + var commands = runner!.commands; + Command? command; + var commandString = runner!.executableName; - for (var name in argResults.rest) { + for (var name in argResults!.rest) { if (commands.isEmpty) { - command.usageException( + command!.usageException( 'Command "$commandString" does not expect a subcommand.'); } if (commands[name] == null) { if (command == null) { - runner.usageException('Could not find a command named "$name".'); + runner!.usageException('Could not find a command named "$name".'); } command.usageException( @@ -51,11 +51,11 @@ class HelpCommand extends Command { } command = commands[name]; - commands = command.subcommands; + commands = command!.subcommands as Map>; commandString += ' $name'; } - command.printUsage(); + command!.printUsage(); return null; } } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 7fd91603..958929a9 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -8,16 +8,16 @@ /// get to it. This function isn't exported to the public API of the package. Option newOption( String name, - String abbr, - String help, - String valueHelp, - Iterable allowed, - Map allowedHelp, + String? abbr, + String? help, + String? valueHelp, + Iterable? allowed, + Map? allowedHelp, defaultsTo, - Function callback, + Function? callback, OptionType type, - {bool negatable, - bool splitCommas, + {bool? negatable, + bool? splitCommas, bool hide = false}) { return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, type, @@ -35,22 +35,22 @@ class Option { /// /// For example, `abbr: "a"` will allow the user to pass `-a value` or /// `-avalue`. - final String abbr; + final String? abbr; @Deprecated('Use abbr instead.') - String get abbreviation => abbr; + String? get abbreviation => abbr; /// A description of this option. - final String help; + final String? help; /// A name for the value this option takes. - final String valueHelp; + final String? valueHelp; /// A list of valid values for this option. - final List allowed; + final List? allowed; /// A map from values in [allowed] to documentation for those values. - final Map allowedHelp; + final Map? allowedHelp; /// The value this option will have if the user doesn't explicitly pass it. final dynamic defaultsTo; @@ -64,10 +64,10 @@ class Option { /// value to `false`. /// /// This is `null` unless [type] is [OptionType.flag]. - final bool negatable; + final bool? negatable; /// The callback to invoke with the option's value when the option is parsed. - final Function callback; + final Function? callback; /// Whether this is a flag, a single value option, or a multi-value option. final OptionType type; @@ -93,13 +93,13 @@ class Option { this.abbr, this.help, this.valueHelp, - Iterable allowed, - Map allowedHelp, + Iterable? allowed, + Map? allowedHelp, this.defaultsTo, this.callback, OptionType type, {this.negatable, - bool splitCommas, + bool? splitCommas, this.hide = false}) : allowed = allowed == null ? null : List.unmodifiable(allowed), allowedHelp = @@ -119,6 +119,7 @@ class Option { throw ArgumentError('Name "$name" contains invalid characters.'); } + var abbr = this.abbr; if (abbr != null) { if (abbr.length != 1) { throw ArgumentError('Abbreviation must be null or have length 1.'); diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index cb152881..4860a2e1 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -16,11 +16,11 @@ import 'option.dart'; class Parser { /// If parser is parsing a command's options, this will be the name of the /// command. For top-level results, this returns `null`. - final String commandName; + final String? commandName; /// The parser for the supercommand of this command parser, or `null` if this /// is the top-level parser. - final Parser parent; + final Parser? parent; /// The grammar being parsed. final ArgParser grammar; @@ -35,7 +35,7 @@ class Parser { final Map results = {}; Parser(this.commandName, this.grammar, this.args, - [this.parent, List rest]) { + [this.parent, List? rest]) { if (rest != null) this.rest.addAll(rest); } @@ -50,7 +50,7 @@ class Parser { grammar, const {}, commandName, null, arguments, arguments); } - ArgResults commandResults; + ArgResults? commandResults; // Parse the args. while (args.isNotEmpty) { @@ -71,7 +71,6 @@ class Parser { try { commandResults = commandParser.parse(); } on ArgParserException catch (error) { - if (commandName == null) rethrow; throw ArgParserException( error.message, [commandName, ...error.commands]); } @@ -95,8 +94,8 @@ class Parser { // Invoke the callbacks. grammar.options.forEach((name, option) { - if (option.callback == null) return; - option.callback(option.getOrDefault(results[name])); + var callback = option.callback; + if (callback != null) callback(option.getOrDefault(results[name])); }); // Add in the leftover arguments we didn't parse to the innermost command. @@ -134,7 +133,7 @@ class Parser { if (option == null) { // Walk up to the parent command if possible. validate(parent != null, 'Could not find an option or flag "-$opt".'); - return parent.parseSoloOption(); + return parent!.parseSoloOption(); } args.removeFirst(); @@ -179,7 +178,7 @@ class Parser { // Walk up to the parent command if possible. validate( parent != null, 'Could not find an option with short name "-$c".'); - return parent.parseAbbreviation(innermostCommand); + return parent!.parseAbbreviation(innermostCommand); } else if (!first.isFlag) { // The first character is a non-flag option, so the rest must be the // value. @@ -213,7 +212,7 @@ class Parser { // Walk up to the parent command if possible. validate( parent != null, 'Could not find an option with short name "-$c".'); - parent.parseShortFlag(c); + parent!.parseShortFlag(c); return; } @@ -266,18 +265,18 @@ class Parser { if (option == null) { // Walk up to the parent command if possible. validate(parent != null, 'Could not find an option named "$name".'); - return parent.parseLongOption(); + return parent!.parseLongOption(); } args.removeFirst(); validate(option.isFlag, 'Cannot negate non-flag option "$name".'); - validate(option.negatable, 'Cannot negate option "$name".'); + validate(option.negatable!, 'Cannot negate option "$name".'); setFlag(results, option, false); } else { // Walk up to the parent command if possible. validate(parent != null, 'Could not find an option named "$name".'); - return parent.parseLongOption(); + return parent!.parseLongOption(); } return true; @@ -325,7 +324,7 @@ class Parser { void _validateAllowed(Option option, String value) { if (option.allowed == null) return; - validate(option.allowed.contains(value), + validate(option.allowed!.contains(value), '"$value" is not an allowed value for option "${option.name}".'); } } diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index d244cada..07bf6ed9 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -26,7 +26,7 @@ class Usage { final List optionsAndSeparators; /// The working buffer for the generated usage text. - StringBuffer buffer; + late StringBuffer buffer; /// The column that the "cursor" is currently on. /// @@ -35,7 +35,7 @@ class Usage { int currentColumn = 0; /// The width in characters of each column. - List columnWidths; + late List columnWidths; /// The number of sequential lines of text that have been written to the last /// column (which shows help info). @@ -56,7 +56,7 @@ class Usage { /// The horizontal character position at which help text is wrapped. Help that /// extends past this column will be wrapped at the nearest whitespace (or /// truncated if there is no available whitespace). - final int lineLength; + final int? lineLength; Usage(this.optionsAndSeparators, {this.lineLength}); @@ -82,15 +82,15 @@ class Usage { write(0, getAbbreviation(option)); write(1, getLongOption(option)); - if (option.help != null) write(2, option.help); + if (option.help != null) write(2, option.help!); if (option.allowedHelp != null) { - var allowedNames = option.allowedHelp.keys.toList(growable: false); + var allowedNames = option.allowedHelp!.keys.toList(growable: false); allowedNames.sort(); newline(); for (var name in allowedNames) { write(1, getAllowedTitle(option, name)); - write(2, option.allowedHelp[name]); + write(2, option.allowedHelp![name]!); } newline(); } else if (option.allowed != null) { @@ -122,7 +122,7 @@ class Usage { String getLongOption(Option option) { var result; - if (option.negatable) { + if (option.negatable!) { result = '--[no-]${option.name}'; } else { result = '--${option.name}'; @@ -155,7 +155,7 @@ class Usage { // Make room for the allowed help. if (option.allowedHelp != null) { - for (var allowed in option.allowedHelp.keys) { + for (var allowed in option.allowedHelp!.keys) { title = math.max(title, getAllowedTitle(option, allowed).length); } } @@ -252,7 +252,7 @@ class Usage { var allowedBuffer = StringBuffer(); allowedBuffer.write('['); var first = true; - for (var allowed in option.allowed) { + for (var allowed in option.allowed!) { if (!first) allowedBuffer.write(', '); allowedBuffer.write(allowed); if (isDefault(allowed)) { diff --git a/pkgs/args/lib/src/utils.dart b/pkgs/args/lib/src/utils.dart index 09319ede..4516848f 100644 --- a/pkgs/args/lib/src/utils.dart +++ b/pkgs/args/lib/src/utils.dart @@ -32,7 +32,7 @@ String padRight(String source, int length) => /// /// If [length] is not specified, then no wrapping occurs, and the original /// [text] is returned unchanged. -String wrapText(String text, {int length, int hangingIndent}) { +String wrapText(String text, {int? length, int? hangingIndent}) { if (length == null) return text; hangingIndent ??= 0; var splitText = text.split('\n'); @@ -58,12 +58,12 @@ String wrapText(String text, {int length, int hangingIndent}) { notIndented = wrapTextAsLines(trimmedText, length: length - leadingWhitespace.length); } - String hangingIndentString; + String? hangingIndentString; result.addAll(notIndented.map((String line) { // Don't return any lines with just whitespace on them. if (line.isEmpty) return ''; var result = '${hangingIndentString ?? ''}$leadingWhitespace$line'; - hangingIndentString ??= ' ' * hangingIndent; + hangingIndentString ??= ' ' * hangingIndent!; return result; })); } @@ -80,7 +80,7 @@ String wrapText(String text, {int length, int hangingIndent}) { /// If [length] is not specified, then no wrapping occurs, and the original /// [text] is returned after splitting it on newlines. Whitespace is not trimmed /// in this case. -List wrapTextAsLines(String text, {int start = 0, int length}) { +List wrapTextAsLines(String text, {int start = 0, int? length}) { assert(start >= 0); /// Returns true if the code unit at [index] in [text] is a whitespace diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 68915fc1..48d74f40 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,13 +1,19 @@ name: args -version: 1.6.1-dev +version: 2.0.0-nullsafety homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set of options and values using GNU and POSIX style options. environment: - sdk: '>=2.3.0 <3.0.0' + sdk: '>=2.12.0-0 <3.0.0' dev_dependencies: - pedantic: ^1.4.0 - test: '^1.5.1' + pedantic: ^1.10.0-nullsafety.3 + test: ^1.16.0-nullsafety.9 + +# Required to get a version solve due to cyclic dependency +dependency_overrides: + test_core: ^0.3.12-nullsafety.9 + coverage: ">=0.13.4 <0.15.0" + analyzer: ">=0.39.5 <0.41.0" diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index 29038a79..06a4e137 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -10,7 +10,7 @@ import 'test_utils.dart'; void main() { group('new ArgParser.allowAnything()', () { - ArgParser parser; + late ArgParser parser; setUp(() { parser = ArgParser.allowAnything(); }); @@ -49,10 +49,11 @@ void main() { var commandParser = ArgParser()..addCommand('command', parser); var results = commandParser.parse(['command', '--foo', '-abc', '--', 'bar']); - expect(results.command.options, isEmpty); - expect(results.command.rest, equals(['--foo', '-abc', '--', 'bar'])); - expect(results.command.arguments, equals(['--foo', '-abc', '--', 'bar'])); - expect(results.command.name, equals('command')); + expect(results.command!.options, isEmpty); + expect(results.command!.rest, equals(['--foo', '-abc', '--', 'bar'])); + expect( + results.command!.arguments, equals(['--foo', '-abc', '--', 'bar'])); + expect(results.command!.name, equals('command')); }); test('works as a subcommand in a CommandRunner', () async { diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index b5dd3ee5..f101f902 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -86,7 +86,7 @@ void main() { test('throws ArgumentError if the abbreviation is an invalid value', () { var parser = ArgParser(); - for (var name in _invalidOptions.where((v) => v != null)) { + for (var name in _invalidOptions) { throwsIllegalArg(() => parser.addOption('flummox', abbr: name)); } }); @@ -296,25 +296,25 @@ void main() { parser.addMultiOption('multi-no'); parser.addMultiOption('multi-def', defaultsTo: ['def']); - expect(parser.options['flag-no'].getOrDefault(null), equals(null)); - expect(parser.options['flag-no'].getOrDefault(false), equals(false)); - expect(parser.options['flag-def'].getOrDefault(null), equals(true)); - expect(parser.options['flag-def'].getOrDefault(false), equals(false)); - expect(parser.options['single-no'].getOrDefault(null), equals(null)); - expect(parser.options['single-no'].getOrDefault('v'), equals('v')); - expect(parser.options['single-def'].getOrDefault(null), equals('def')); - expect(parser.options['single-def'].getOrDefault('v'), equals('v')); - expect(parser.options['allow-multi-no'].getOrDefault(null), equals([])); + expect(parser.options['flag-no']!.getOrDefault(null), equals(null)); + expect(parser.options['flag-no']!.getOrDefault(false), equals(false)); + expect(parser.options['flag-def']!.getOrDefault(null), equals(true)); + expect(parser.options['flag-def']!.getOrDefault(false), equals(false)); + expect(parser.options['single-no']!.getOrDefault(null), equals(null)); + expect(parser.options['single-no']!.getOrDefault('v'), equals('v')); + expect(parser.options['single-def']!.getOrDefault(null), equals('def')); + expect(parser.options['single-def']!.getOrDefault('v'), equals('v')); + expect(parser.options['allow-multi-no']!.getOrDefault(null), equals([])); expect( - parser.options['allow-multi-no'].getOrDefault(['v']), equals(['v'])); - expect(parser.options['allow-multi-def'].getOrDefault(null), + parser.options['allow-multi-no']!.getOrDefault(['v']), equals(['v'])); + expect(parser.options['allow-multi-def']!.getOrDefault(null), equals(['def'])); - expect( - parser.options['allow-multi-def'].getOrDefault(['v']), equals(['v'])); - expect(parser.options['multi-no'].getOrDefault(null), equals([])); - expect(parser.options['multi-no'].getOrDefault(['v']), equals(['v'])); - expect(parser.options['multi-def'].getOrDefault(null), equals(['def'])); - expect(parser.options['multi-def'].getOrDefault(['v']), equals(['v'])); + expect(parser.options['allow-multi-def']!.getOrDefault(['v']), + equals(['v'])); + expect(parser.options['multi-no']!.getOrDefault(null), equals([])); + expect(parser.options['multi-no']!.getOrDefault(['v']), equals(['v'])); + expect(parser.options['multi-def']!.getOrDefault(null), equals(['def'])); + expect(parser.options['multi-def']!.getOrDefault(['v']), equals(['v'])); }); }); } diff --git a/pkgs/args/test/command_parse_test.dart b/pkgs/args/test/command_parse_test.dart index afa8ebe9..c0a2cf29 100644 --- a/pkgs/args/test/command_parse_test.dart +++ b/pkgs/args/test/command_parse_test.dart @@ -36,7 +36,7 @@ void main() { var args = parser.parse(['install']); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); expect(args.rest, isEmpty); }); @@ -46,7 +46,7 @@ void main() { command.addOption('path'); var args = parser.parse(['install', '--path', 'some/path']); - expect(args.command['path'], equals('some/path')); + expect(args.command!['path'], equals('some/path')); }); test('parses a parent solo option before the command', () { @@ -56,7 +56,7 @@ void main() { var args = parser.parse(['-m', 'debug', 'install']); expect(args['mode'], equals('debug')); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent solo option after the command', () { @@ -66,7 +66,7 @@ void main() { var args = parser.parse(['install', '-m', 'debug']); expect(args['mode'], equals('debug')); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent option before the command', () { @@ -76,7 +76,7 @@ void main() { var args = parser.parse(['--verbose', 'install']); expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent option after the command', () { @@ -86,7 +86,7 @@ void main() { var args = parser.parse(['install', '--verbose']); expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent negated option before the command', () { @@ -96,7 +96,7 @@ void main() { var args = parser.parse(['--no-verbose', 'install']); expect(args['verbose'], isFalse); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent negated option after the command', () { @@ -106,7 +106,7 @@ void main() { var args = parser.parse(['install', '--no-verbose']); expect(args['verbose'], isFalse); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent abbreviation before the command', () { @@ -118,7 +118,7 @@ void main() { var args = parser.parse(['-dv', 'install']); expect(args['debug'], isTrue); expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('parses a parent abbreviation after the command', () { @@ -130,7 +130,7 @@ void main() { var args = parser.parse(['install', '-dv']); expect(args['debug'], isTrue); expect(args['verbose'], isTrue); - expect(args.command.name, equals('install')); + expect(args.command!.name, equals('install')); }); test('does not parse a solo command option before the command', () { @@ -168,10 +168,10 @@ void main() { var args = parser.parse(['cmd', 'subcmd', '-abc']); expect(args['apple'], isTrue); - expect(args.command.name, equals('cmd')); - expect(args.command['banana'], isTrue); - expect(args.command.command.name, equals('subcmd')); - expect(args.command.command['cherry'], isTrue); + expect(args.command!.name, equals('cmd')); + expect(args.command!['banana'], isTrue); + expect(args.command!.command!.name, equals('subcmd')); + expect(args.command!.command!['cherry'], isTrue); }); test('option is given to innermost command that can take it', () { @@ -183,9 +183,9 @@ void main() { var args = parser.parse(['cmd', 'subcmd', '--verbose']); expect(args['verbose'], isFalse); - expect(args.command.name, equals('cmd')); - expect(args.command['verbose'], isTrue); - expect(args.command.command.name, equals('subcmd')); + expect(args.command!.name, equals('cmd')); + expect(args.command!['verbose'], isTrue); + expect(args.command!.command!.name, equals('subcmd')); }); test('remaining arguments are given to the innermost command', () { @@ -193,11 +193,11 @@ void main() { parser.addCommand('cmd')..addCommand('subcmd'); var args = parser.parse(['cmd', 'subcmd', 'other', 'stuff']); - expect(args.command.name, equals('cmd')); + expect(args.command!.name, equals('cmd')); expect(args.rest, isEmpty); - expect(args.command.command.name, equals('subcmd')); - expect(args.command.rest, isEmpty); - expect(args.command.command.rest, equals(['other', 'stuff'])); + expect(args.command!.command!.name, equals('subcmd')); + expect(args.command!.rest, isEmpty); + expect(args.command!.command!.rest, equals(['other', 'stuff'])); }); }); } diff --git a/pkgs/args/test/command_runner_test.dart b/pkgs/args/test/command_runner_test.dart index ce484011..ed29e082 100644 --- a/pkgs/args/test/command_runner_test.dart +++ b/pkgs/args/test/command_runner_test.dart @@ -19,7 +19,7 @@ Available commands: Run "test help " for more information about a command.'''; void main() { - var runner; + late var runner; setUp(() { runner = CommandRunner('test', 'A test command runner.'); }); diff --git a/pkgs/args/test/command_test.dart b/pkgs/args/test/command_test.dart index a5c44947..d4be4ae8 100644 --- a/pkgs/args/test/command_test.dart +++ b/pkgs/args/test/command_test.dart @@ -7,7 +7,7 @@ import 'package:test/test.dart'; import 'test_utils.dart'; void main() { - var foo; + late var foo; setUp(() { foo = FooCommand(); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 96395674..1c50c4af 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -154,12 +154,11 @@ void main() { }); test('are invoked even if the option is not present', () { - var a = 'not called'; var parser = ArgParser(); - parser.addOption('a', callback: (value) => a = value); + parser.addOption('a', + callback: expectAsync1((value) => expect(value, isNull))); parser.parse([]); - expect(a, isNull); }); group('with allowMultiple', () { diff --git a/pkgs/args/test/test_utils.dart b/pkgs/args/test/test_utils.dart index 68b96d3c..5136f48f 100644 --- a/pkgs/args/test/test_utils.dart +++ b/pkgs/args/test/test_utils.dart @@ -223,7 +223,7 @@ class AllowAnythingCommand extends Command { } } -void throwsIllegalArg(function, {String reason}) { +void throwsIllegalArg(function, {String? reason}) { expect(function, throwsArgumentError, reason: reason); } @@ -231,11 +231,7 @@ void throwsFormat(ArgParser parser, List args) { expect(() => parser.parse(args), throwsFormatException); } -Matcher throwsUsageException(message, usage) { - return throwsA(predicate((error) { - expect(error, TypeMatcher()); - expect(error.message, message); - expect(error.usage, usage); - return true; - })); -} +Matcher throwsUsageException(String message, String usage) => + throwsA(isA() + .having((e) => e.message, 'message', message) + .having((e) => e.usage, 'usage', usage)); diff --git a/pkgs/args/test/trailing_options_test.dart b/pkgs/args/test/trailing_options_test.dart index 174a1d03..323f582b 100644 --- a/pkgs/args/test/trailing_options_test.dart +++ b/pkgs/args/test/trailing_options_test.dart @@ -12,7 +12,7 @@ void main() { }); group('when trailing options are allowed', () { - var parser; + late var parser; setUp(() { parser = ArgParser(allowTrailingOptions: true); }); @@ -93,7 +93,7 @@ void main() { results = parser.parse(['cmd', '-f', 'a', '-v', '--unknown']); expect(results['flag'], isTrue); // Not trailing. - expect(results.command['verbose'], isFalse); - expect(results.command.rest, equals(['a', '-v', '--unknown'])); + expect(results.command!['verbose'], isFalse); + expect(results.command!.rest, equals(['a', '-v', '--unknown'])); }); } diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 45818b9a..3b63d45c 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -502,7 +502,7 @@ String unindentString(String text) { // Count the indentation of the last line. var whitespace = RegExp('^ *'); - var indent = whitespace.firstMatch(lines[lines.length - 1])[0].length; + var indent = whitespace.firstMatch(lines[lines.length - 1])![0]!.length; // Drop the last line. It only exists for specifying indentation. lines.removeLast(); From 05a7d86dbb56443af5924225b4a3115b3e2f64f0 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 6 Nov 2020 10:18:34 -0800 Subject: [PATCH 187/263] Remove already deprecated APIs (dart-lang/args#165) --- pkgs/args/CHANGELOG.md | 8 ++ pkgs/args/lib/src/allow_anything_parser.dart | 3 - pkgs/args/lib/src/arg_parser.dart | 36 +---- pkgs/args/lib/src/option.dart | 15 --- pkgs/args/test/allow_anything_test.dart | 1 - pkgs/args/test/args_test.dart | 33 ----- pkgs/args/test/parse_test.dart | 135 ------------------- pkgs/args/test/usage_test.dart | 26 +--- 8 files changed, 16 insertions(+), 241 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index fcc32295..f6535ad7 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,5 +1,13 @@ ## 2.0.0-nullsafety +* **BREAKING** Remove APIs that had been marked as deprecated. + * `allowMulti` and `splitCommas` arguments to `ArgParser.addOption`, use + `ArgParser.addMultiOption`. + * `ArgParser.getUsage(),` use `ArgParser.usage`. + * `Option.abbreviation`, use `Option.abbr`. + * `Option.defaultValue`, use `Option.defaultsTo`. + * `OptionType.FLAG/SINGLE/MULTIPLE`, use `OptionType.flag/single/multiple`. + ## 1.6.0 * Remove `help` from the list of commands in usage. diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 16f97fd5..64040b82 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -81,9 +81,6 @@ class AllowAnythingParser implements ArgParser { ArgResults parse(Iterable args) => Parser(null, this, Queue.of(args)).parse(); - @override - String getUsage() => usage; - @override String get usage => ''; diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 7ccb5b4e..6f25efbc 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -2,10 +2,6 @@ // 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. -// TODO(nweiz): Remove this ignore when sdk#30084 is fixed or when args no -// longer refers to its own deprecated members. -// ignore_for_file: deprecated_member_use - import 'dart:collection'; import 'allow_anything_parser.dart'; @@ -175,16 +171,12 @@ class ArgParser { /// that are often surprising, and its use is discouraged in favor of reading /// values from the [ArgResults]. /// - /// The [allowMultiple] and [splitCommas] options are deprecated; the - /// [addMultiOption] method should be used instead. - /// /// If [hide] is `true`, this option won't be included in [usage]. /// /// Throws an [ArgumentError] if: /// /// * There is already an option with name [name]. /// * There is already an option using abbreviation [abbr]. - /// * [splitCommas] is passed but [allowMultiple] is `false`. void addOption(String name, {String? abbr, String? help, @@ -193,27 +185,9 @@ class ArgParser { Map? allowedHelp, String? defaultsTo, Function? callback, - @Deprecated('Use addMultiOption() instead.') bool allowMultiple = false, - @Deprecated('Use addMultiOption() instead.') bool? splitCommas, bool hide = false}) { - if (!allowMultiple && splitCommas != null) { - throw ArgumentError( - 'splitCommas may not be set if allowMultiple is false.'); - } - - _addOption( - name, - abbr, - help, - valueHelp, - allowed, - allowedHelp, - allowMultiple - ? (defaultsTo == null ? [] : [defaultsTo]) - : defaultsTo, - callback, - allowMultiple ? OptionType.multiple : OptionType.single, - splitCommas: splitCommas, + _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, + callback, OptionType.single, hide: hide); } @@ -325,12 +299,6 @@ class ArgParser { ArgResults parse(Iterable args) => Parser(null, this, Queue.of(args)).parse(); - /// Generates a string displaying usage information for the defined options. - /// - /// This is basically the help text shown on the command line. - @Deprecated('Replaced with get usage. getUsage() will be removed in args 1.0') - String getUsage() => usage; - /// Generates a string displaying usage information for the defined options. /// /// This is basically the help text shown on the command line. diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 958929a9..5361f262 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -37,9 +37,6 @@ class Option { /// `-avalue`. final String? abbr; - @Deprecated('Use abbr instead.') - String? get abbreviation => abbr; - /// A description of this option. final String? help; @@ -55,9 +52,6 @@ class Option { /// The value this option will have if the user doesn't explicitly pass it. final dynamic defaultsTo; - @Deprecated('Use defaultsTo instead.') - dynamic get defaultValue => defaultsTo; - /// Whether this flag's value can be set to `false`. /// /// For example, if [name] is `flag`, the user can pass `--no-flag` to set its @@ -155,9 +149,6 @@ class OptionType { /// The presence of the option name itself in the argument list means `true`. static const flag = OptionType._('OptionType.flag'); - @Deprecated('Use OptionType.flag instead.') - static const FLAG = flag; // ignore: constant_identifier_names - /// An option that takes a single value. /// /// Examples: @@ -169,9 +160,6 @@ class OptionType { /// If the option is passed more than once, the last one wins. static const single = OptionType._('OptionType.single'); - @Deprecated('Use OptionType.single instead.') - static const SINGLE = single; // ignore: constant_identifier_names - /// An option that allows multiple values. /// /// Example: @@ -182,9 +170,6 @@ class OptionType { /// a list, even if one or no values were passed. static const multiple = OptionType._('OptionType.multiple'); - @Deprecated('Use OptionType.multiple instead.') - static const MULTIPLE = multiple; // ignore: constant_identifier_names - final String name; const OptionType._(this.name); diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index 06a4e137..0ca31bff 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -21,7 +21,6 @@ void main() { expect(parser.allowTrailingOptions, isFalse); expect(parser.allowsAnything, isTrue); expect(parser.usage, isEmpty); - expect(parser.getUsage(), isEmpty); // ignore: deprecated_member_use expect(parser.findByAbbreviation('a'), isNull); }); diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index f101f902..70ccd781 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -110,23 +110,6 @@ void main() { } }); - test( - 'throws ArgumentError if splitCommas is passed with allowMultiple: ' - 'false', () { - var parser = ArgParser(); - throwsIllegalArg(() { - parser.addOption( - 'flummox', splitCommas: true, // ignore: deprecated_member_use - ); - }); - throwsIllegalArg(() { - parser.addOption( - 'flummox', - splitCommas: false, // ignore: deprecated_member_use - ); - }); - }); - test('accepts valid option names', () { var parser = ArgParser(); @@ -284,15 +267,6 @@ void main() { parser.addFlag('flag-def', defaultsTo: true); parser.addOption('single-no'); parser.addOption('single-def', defaultsTo: 'def'); - parser.addOption( - 'allow-multi-no', - allowMultiple: true, // ignore: deprecated_member_use - ); - parser.addOption( - 'allow-multi-def', - allowMultiple: true, // ignore: deprecated_member_use - defaultsTo: 'def', - ); parser.addMultiOption('multi-no'); parser.addMultiOption('multi-def', defaultsTo: ['def']); @@ -304,13 +278,6 @@ void main() { expect(parser.options['single-no']!.getOrDefault('v'), equals('v')); expect(parser.options['single-def']!.getOrDefault(null), equals('def')); expect(parser.options['single-def']!.getOrDefault('v'), equals('v')); - expect(parser.options['allow-multi-no']!.getOrDefault(null), equals([])); - expect( - parser.options['allow-multi-no']!.getOrDefault(['v']), equals(['v'])); - expect(parser.options['allow-multi-def']!.getOrDefault(null), - equals(['def'])); - expect(parser.options['allow-multi-def']!.getOrDefault(['v']), - equals(['v'])); expect(parser.options['multi-no']!.getOrDefault(null), equals([])); expect(parser.options['multi-no']!.getOrDefault(['v']), equals(['v'])); expect(parser.options['multi-def']!.getOrDefault(null), equals(['def'])); diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 1c50c4af..c54e1424 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -161,108 +161,6 @@ void main() { parser.parse([]); }); - group('with allowMultiple', () { - test('for multiple present, options are invoked with value as a list', - () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse(['--a=v', '--a=x']); - expect(a, equals(['v', 'x'])); - - // This reified type is important in strong mode so that people can - // safely write "as List". - expect(a, TypeMatcher>()); - }); - - test( - 'for single present, options are invoked with value as a single ' - 'element list', () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse(['--a=v']); - expect(a, equals(['v'])); - }); - - test('for absent, options are invoked with default value as a list', - () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - defaultsTo: 'v', - callback: (value) => a = value); - - parser.parse([]); - expect(a, equals(['v'])); - }); - - test('for absent, options are invoked with value as an empty list', () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse([]); - expect(a, isEmpty); - }); - - test('parses comma-separated strings', () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v', 'w', 'x'])); - }); - - test("doesn't parse comma-separated strings with splitCommas: false", - () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - splitCommas: false, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v,w', 'x'])); - }); - - test('parses empty strings', () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - callback: (value) => a = value); - - parser.parse(['--a=,v', '--a=w,', '--a=,', '--a=x,,y', '--a', '']); - expect(a, equals(['', 'v', 'w', '', '', '', 'x', '', 'y', ''])); - }); - - test('with allowed parses comma-separated strings', () { - var a; - var parser = ArgParser(); - parser.addOption('a', - allowMultiple: true, // ignore: deprecated_member_use - allowed: ['v', 'w', 'x'], - callback: (value) => a = value); - - parser.parse(['--a=v,w', '--a=x']); - expect(a, equals(['v', 'w', 'x'])); - }); - }); - group('with addMultiOption', () { test('for multiple present, options are invoked with value as a list', () { @@ -437,17 +335,6 @@ void main() { }); group('throw if a comma-separated value is not allowed', () { - test('with allowMultiple', () { - var parser = ArgParser(); - parser.addOption( - 'mode', - abbr: 'm', allowMultiple: true, // ignore: deprecated_member_use - allowed: ['debug', 'release'], - ); - - throwsFormat(parser, ['-mdebug,profile']); - }); - test('with addMultiOption', () { var parser = ArgParser(); parser @@ -559,17 +446,6 @@ void main() { }); group('returns a List', () { - test('with allowMultiple', () { - var parser = ArgParser(); - parser.addOption( - 'define', allowMultiple: true, // ignore: deprecated_member_use - ); - var args = parser.parse(['--define=1']); - expect(args['define'], equals(['1'])); - args = parser.parse(['--define=1', '--define=2']); - expect(args['define'], equals(['1', '2'])); - }); - test('with addMultiOption', () { var parser = ArgParser(); parser.addMultiOption('define'); @@ -581,17 +457,6 @@ void main() { }); group('returns the default value if not explicitly set', () { - test('with allowMultiple', () { - var parser = ArgParser(); - parser.addOption( - 'define', - defaultsTo: '0', - allowMultiple: true, // ignore: deprecated_member_use - ); - var args = parser.parse(['']); - expect(args['define'], equals(['0'])); - }); - test('with addMultiOption', () { var parser = ArgParser(); parser.addMultiOption('define', defaultsTo: ['0']); diff --git a/pkgs/args/test/usage_test.dart b/pkgs/args/test/usage_test.dart index 3b63d45c..0ba33f02 100644 --- a/pkgs/args/test/usage_test.dart +++ b/pkgs/args/test/usage_test.dart @@ -107,20 +107,12 @@ void main() { help: 'Can be anything', defaultsTo: 'whatevs'); parser.addMultiOption('multiple', help: 'Can be anything', defaultsTo: ['whatevs']); - parser.addOption( - 'allow-multi', - help: 'Can be anything', - defaultsTo: 'whatevs', - allowMultiple: true, // ignore: deprecated_member_use - ); validateUsage(parser, ''' - --single Can be anything - (defaults to "whatevs") - --multiple Can be anything - (defaults to "whatevs") - --allow-multi Can be anything - (defaults to "whatevs") + --single Can be anything + (defaults to "whatevs") + --multiple Can be anything + (defaults to "whatevs") '''); }); @@ -142,16 +134,10 @@ void main() { parser.addMultiOption('implicit', help: 'Implicit default'); parser .addMultiOption('explicit', help: 'Explicit default', defaultsTo: []); - parser.addOption( - 'allow-multi', - help: 'Implicit with allowMultiple', - allowMultiple: true, // ignore: deprecated_member_use - ); validateUsage(parser, ''' - --implicit Implicit default - --explicit Explicit default - --allow-multi Implicit with allowMultiple + --implicit Implicit default + --explicit Explicit default '''); }); From 7047a0613a52c9588ef32dbd705c17dbfeedfc34 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Fri, 6 Nov 2020 10:24:38 -0800 Subject: [PATCH 188/263] Use Null as return type for HelpCommand.run (dart-lang/args#164) This may make it easier to understand choices around nullability by making it explicit that the help command can never return a useful value from it's `run` method. Remove the unnecessary return value and return statement since a `Null` return type may be treated similarly to `void`. --- pkgs/args/lib/src/help_command.dart | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/pkgs/args/lib/src/help_command.dart b/pkgs/args/lib/src/help_command.dart index c3c2bbf8..d598cfdf 100644 --- a/pkgs/args/lib/src/help_command.dart +++ b/pkgs/args/lib/src/help_command.dart @@ -22,11 +22,11 @@ class HelpCommand extends Command { bool get hidden => true; @override - T? run() { + Null run() { // Show the default help if no command was specified. if (argResults!.rest.isEmpty) { runner!.printUsage(); - return null; + return; } // Walk the command tree to show help for the selected command or @@ -56,6 +56,5 @@ class HelpCommand extends Command { } command!.printUsage(); - return null; } } From 33da436438e482ac43d853d6e7cfb1f0432a59d6 Mon Sep 17 00:00:00 2001 From: Bob Nystrom Date: Mon, 30 Nov 2020 16:16:24 -0800 Subject: [PATCH 189/263] Bump to 2.0.0 in anticipation of release. (dart-lang/args#166) * Bump to 2.0.0 in anticipation of release. Also tweaked a couple of tiny things. * Correct version number. --- pkgs/args/CHANGELOG.md | 21 ++++++++++++--------- pkgs/args/lib/command_runner.dart | 10 +++++----- pkgs/args/pubspec.yaml | 2 +- 3 files changed, 18 insertions(+), 15 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index f6535ad7..a17da8d0 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,12 +1,15 @@ -## 2.0.0-nullsafety - -* **BREAKING** Remove APIs that had been marked as deprecated. - * `allowMulti` and `splitCommas` arguments to `ArgParser.addOption`, use - `ArgParser.addMultiOption`. - * `ArgParser.getUsage(),` use `ArgParser.usage`. - * `Option.abbreviation`, use `Option.abbr`. - * `Option.defaultValue`, use `Option.defaultsTo`. - * `OptionType.FLAG/SINGLE/MULTIPLE`, use `OptionType.flag/single/multiple`. +## 2.0.0-nullsafety.0 + +* Migrate to null safety. +* **BREAKING** Remove APIs that had been marked as deprecated: + + * Instead of the `allowMulti` and `splitCommas` arguments to + `ArgParser.addOption()`, use `ArgParser.addMultiOption()`. + * Instead of `ArgParser.getUsage()`, use `ArgParser.usage`. + * Instead of `Option.abbreviation`, use `Option.abbr`. + * Instead of `Option.defaultValue`, use `Option.defaultsTo`. + * Instead of `OptionType.FLAG/SINGLE/MULTIPLE`, use + `OptionType.flag/single/multiple`. ## 1.6.0 diff --git a/pkgs/args/lib/command_runner.dart b/pkgs/args/lib/command_runner.dart index 0a6566f5..90b6a429 100644 --- a/pkgs/args/lib/command_runner.dart +++ b/pkgs/args/lib/command_runner.dart @@ -122,12 +122,12 @@ class CommandRunner { } on ArgParserException catch (error) { if (error.commands.isEmpty) usageException(error.message); - var command = commands[error.commands.first]; + var command = commands[error.commands.first]!; for (var commandName in error.commands.skip(1)) { - command = command!.subcommands[commandName]; + command = command.subcommands[commandName]!; } - command!.usageException(error.message); + command.usageException(error.message); } } @@ -170,8 +170,8 @@ class CommandRunner { // Step into the command. argResults = argResults.command!; - command = commands[argResults.name]; - command!._globalResults = topLevelResults; + command = commands[argResults.name]!; + command._globalResults = topLevelResults; command._argResults = argResults; commands = command._subcommands as Map>; commandString += ' ${argResults.name}'; diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 48d74f40..4649817e 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 2.0.0-nullsafety +version: 2.0.0-nullsafety.0 homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set From 081a06efe207c4d43f8bf8347140651b9e07bbd3 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 9 Dec 2020 16:14:56 -0800 Subject: [PATCH 190/263] Remove ignore for deprecated usage (dart-lang/args#167) --- pkgs/args/analysis_options.yaml | 3 --- 1 file changed, 3 deletions(-) diff --git a/pkgs/args/analysis_options.yaml b/pkgs/args/analysis_options.yaml index 53b584db..03843d06 100644 --- a/pkgs/args/analysis_options.yaml +++ b/pkgs/args/analysis_options.yaml @@ -1,7 +1,4 @@ include: package:pedantic/analysis_options.yaml -analyzer: - errors: - deprecated_member_use_from_same_package: ignore linter: rules: - avoid_null_checks_in_equality_operators From fb6df857c9c58cf538e3329e204be07d6be02eea Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 10 Dec 2020 09:01:38 -0800 Subject: [PATCH 191/263] Give a specific function type for callback (dart-lang/args#168) Closes dart-lang/args#149 --- pkgs/args/CHANGELOG.md | 1 + pkgs/args/lib/src/allow_anything_parser.dart | 2 +- pkgs/args/lib/src/arg_parser.dart | 9 +++++---- pkgs/args/test/parse_test.dart | 9 +++++---- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index a17da8d0..69eee2c7 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -10,6 +10,7 @@ * Instead of `Option.defaultValue`, use `Option.defaultsTo`. * Instead of `OptionType.FLAG/SINGLE/MULTIPLE`, use `OptionType.flag/single/multiple`. +* Add a more specific function type to the `callback` argument of `addOption`. ## 1.6.0 diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 64040b82..a91e155c 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -48,7 +48,7 @@ class AllowAnythingParser implements ArgParser { Iterable? allowed, Map? allowedHelp, String? defaultsTo, - Function? callback, + void Function(String?)? callback, bool allowMultiple = false, bool? splitCommas, bool hide = false}) { diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 6f25efbc..72d09da6 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -167,9 +167,10 @@ class ArgParser { /// user doesn't explicitly pass it in (or `null` by default). /// /// The [callback] argument is invoked with the option's value when the option - /// is parsed. Note that this makes argument parsing order-dependent in ways - /// that are often surprising, and its use is discouraged in favor of reading - /// values from the [ArgResults]. + /// is parsed, or with `null` if the option was not parsed. + /// Note that this makes argument parsing order-dependent in ways that are + /// often surprising, and its use is discouraged in favor of reading values + /// from the [ArgResults]. /// /// If [hide] is `true`, this option won't be included in [usage]. /// @@ -184,7 +185,7 @@ class ArgParser { Iterable? allowed, Map? allowedHelp, String? defaultsTo, - Function? callback, + void Function(String?)? callback, bool hide = false}) { _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, OptionType.single, diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index c54e1424..6f56db4b 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -145,15 +145,16 @@ void main() { }); test('for absent options are invoked with the default value', () { - var a; var parser = ArgParser(); - parser.addOption('a', defaultsTo: 'v', callback: (value) => a = value); + parser.addOption('a', + defaultsTo: 'v', + callback: expectAsync1((value) => expect(value, 'v'))); parser.parse([]); - expect(a, equals('v')); }); - test('are invoked even if the option is not present', () { + test('for absent options are invoked with null if there is no default', + () { var parser = ArgParser(); parser.addOption('a', callback: expectAsync1((value) => expect(value, isNull))); From 9705f04ccdd9a92a71341e54d0076a6d06c9011a Mon Sep 17 00:00:00 2001 From: Oliver Sand Date: Fri, 11 Dec 2020 17:02:48 +0100 Subject: [PATCH 192/263] Fix link to use https (dart-lang/args#174) --- pkgs/args/CHANGELOG.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 69eee2c7..7670a41f 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -189,7 +189,7 @@ * Allow option values that look like options. This more closely matches the behavior of [`getopt`][getopt], the *de facto* standard for option parsing. -[getopt]: http://man7.org/linux/man-pages/man3/getopt.3.html +[getopt]: https://man7.org/linux/man-pages/man3/getopt.3.html ## 0.13.1 From 5511029676f513483d7aa8473b7d4c5db37196f1 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 16 Dec 2020 15:13:04 -0800 Subject: [PATCH 193/263] Re-expose the class OptionType (dart-lang/args#175) This was hidden in dart-lang/args#147 but it is used by some external packages and there is no strong reason to make this an additional breaking change. --- pkgs/args/lib/args.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pkgs/args/lib/args.dart b/pkgs/args/lib/args.dart index a49c536b..6011d1eb 100644 --- a/pkgs/args/lib/args.dart +++ b/pkgs/args/lib/args.dart @@ -5,4 +5,4 @@ export 'src/arg_parser.dart' show ArgParser; export 'src/arg_parser_exception.dart' show ArgParserException; export 'src/arg_results.dart' show ArgResults; -export 'src/option.dart' show Option; +export 'src/option.dart' show Option, OptionType; From 4acaafa17c0a011f09b47ad3282472c86d53cc96 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 16 Dec 2020 15:13:42 -0800 Subject: [PATCH 194/263] Refactor usage.dart (dart-lang/args#169) - Add a top level method rather than require a constructor call and call to `generate()` when there is otherwise no reason to hold on to the `Usage` object. This makes it more clear that the implementation is following a method object pattern. - Update doc comment which had drifted from the original implementation and no longer referred to the correct arguments. - Make the `StringBuffer` field final since it is now a guaranteed single use object. - Make everything possible private. This revealed that there was a field being updated but never read and can be removed. - Remove `get` from method names. - Make the `_columnWidths` late final rather than initialize it manually in the call to `generate()`. - Pull out sub-methods `_writeSeparator` and `_writeOption` from `generate` to reduce nesting and the length of the overall method. - use `.take(size)` instead of `.sublist(size)` since we only need an Iterable. - Use `.first` and `.last` over manual indexing. - Use `.removeAt(0)` over `.removeRange(0,1)`. - Rewrite a for loop with `.addAll` calls to a List literal with a for loop element. --- pkgs/args/lib/src/arg_parser.dart | 2 +- pkgs/args/lib/src/usage.dart | 215 ++++++++++++++---------------- 2 files changed, 100 insertions(+), 117 deletions(-) diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 72d09da6..c584492d 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -304,7 +304,7 @@ class ArgParser { /// /// This is basically the help text shown on the command line. String get usage { - return Usage(_optionsAndSeparators, lineLength: usageLineLength).generate(); + return generateUsage(_optionsAndSeparators, lineLength: usageLineLength); } /// Get the default value for an option. Useful after parsing to test if the diff --git a/pkgs/args/lib/src/usage.dart b/pkgs/args/lib/src/usage.dart index 07bf6ed9..6e44244d 100644 --- a/pkgs/args/lib/src/usage.dart +++ b/pkgs/args/lib/src/usage.dart @@ -7,8 +7,7 @@ import 'dart:math' as math; import '../args.dart'; import 'utils.dart'; -/// Takes an [ArgParser] and generates a string of usage (i.e. help) text for -/// its defined options. +/// Generates a string of usage (i.e. help) text for a list of options. /// /// Internally, it works like a tabular printer. The output is divided into /// three horizontal columns, like so: @@ -18,32 +17,32 @@ import 'utils.dart'; /// /// 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. -class Usage { +/// +/// [lineLength] specifies the horizontal character position at which the help +/// text is wrapped. Help that extends past this column will be wrapped at the +/// nearest whitespace (or truncated if there is no available whitespace). If +/// `null` there will not be any wrapping. +String generateUsage(List optionsAndSeparators, {int? lineLength}) => + _Usage(optionsAndSeparators, lineLength).generate(); + +class _Usage { /// Abbreviation, long name, help. static const _columnCount = 3; /// A list of the [Option]s intermingled with [String] separators. - final List optionsAndSeparators; + final List _optionsAndSeparators; /// The working buffer for the generated usage text. - late StringBuffer buffer; + final _buffer = StringBuffer(); /// 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; + int _currentColumn = 0; /// The width in characters of each column. - late List columnWidths; - - /// 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. - int numHelpLines = 0; + late final _columnWidths = _calculateColumnWidths(); /// How many newlines need to be rendered before the next bit of text can be /// written. @@ -51,76 +50,75 @@ class Usage { /// 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. - int newlinesNeeded = 0; + int _newlinesNeeded = 0; - /// The horizontal character position at which help text is wrapped. Help that - /// extends past this column will be wrapped at the nearest whitespace (or - /// truncated if there is no available whitespace). + /// The horizontal character position at which help text is wrapped. + /// + /// Help that extends past this column will be wrapped at the nearest + /// whitespace (or truncated if there is no available whitespace). final int? lineLength; - Usage(this.optionsAndSeparators, {this.lineLength}); + _Usage(this._optionsAndSeparators, this.lineLength); /// Generates a string displaying usage information for the defined options. /// This is basically the help text shown on the command line. String generate() { - buffer = StringBuffer(); - - calculateColumnWidths(); - - for (var optionOrSeparator in optionsAndSeparators) { + for (var optionOrSeparator in _optionsAndSeparators) { if (optionOrSeparator is String) { - // Ensure that there's always a blank line before a separator. - if (buffer.isNotEmpty) buffer.write('\n\n'); - buffer.write(optionOrSeparator); - newlinesNeeded = 1; + _writeSeparator(optionOrSeparator); continue; } - var option = optionOrSeparator as Option; if (option.hide) continue; + _writeOption(option); + } - write(0, getAbbreviation(option)); - write(1, getLongOption(option)); + return _buffer.toString(); + } - if (option.help != null) write(2, option.help!); + void _writeSeparator(String separator) { + // Ensure that there's always a blank line before a separator. + if (_buffer.isNotEmpty) _buffer.write('\n\n'); + _buffer.write(separator); + _newlinesNeeded = 1; + } - if (option.allowedHelp != null) { - var allowedNames = option.allowedHelp!.keys.toList(growable: false); - allowedNames.sort(); - newline(); - for (var name in allowedNames) { - write(1, getAllowedTitle(option, name)); - write(2, option.allowedHelp![name]!); - } - newline(); - } else if (option.allowed != null) { - write(2, buildAllowedList(option)); - } else if (option.isFlag) { - if (option.defaultsTo == true) { - write(2, '(defaults to on)'); - } - } else if (option.isMultiple) { - if (option.defaultsTo != null && option.defaultsTo.isNotEmpty) { - write( - 2, - '(defaults to ' + - option.defaultsTo.map((value) => '"$value"').join(', ') + - ')'); - } - } else { - if (option.defaultsTo != null) { - write(2, '(defaults to "${option.defaultsTo}")'); - } + void _writeOption(Option option) { + _write(0, _abbreviation(option)); + _write(1, _longOption(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, _allowedTitle(option, name)); + _write(2, option.allowedHelp![name]!); } + _newline(); + } else if (option.allowed != null) { + _write(2, _buildAllowedList(option)); + } else if (option.isFlag) { + if (option.defaultsTo == true) { + _write(2, '(defaults to on)'); + } + } else if (option.isMultiple) { + if (option.defaultsTo != null && option.defaultsTo.isNotEmpty) { + var defaults = + (option.defaultsTo as List).map((value) => '"$value"').join(', '); + _write(2, '(defaults to $defaults)'); + } + } else if (option.defaultsTo != null) { + _write(2, '(defaults to "${option.defaultsTo}")'); } - - return buffer.toString(); } - String getAbbreviation(Option option) => + String _abbreviation(Option option) => option.abbr == null ? '' : '-${option.abbr}, '; - String getLongOption(Option option) { + String _longOption(Option option) { var result; if (option.negatable!) { result = '--[no-]${option.name}'; @@ -133,118 +131,103 @@ class Usage { return result; } - String getAllowedTitle(Option option, String allowed) { + String _allowedTitle(Option option, String allowed) { var isDefault = option.defaultsTo is List ? option.defaultsTo.contains(allowed) : option.defaultsTo == allowed; return ' [$allowed]' + (isDefault ? ' (default)' : ''); } - void calculateColumnWidths() { + List _calculateColumnWidths() { var abbr = 0; var title = 0; - for (var option in optionsAndSeparators) { + for (var option in _optionsAndSeparators) { if (option is! Option) continue; if (option.hide) continue; // Make room in the first column if there are abbreviations. - abbr = math.max(abbr, getAbbreviation(option).length); + abbr = math.max(abbr, _abbreviation(option).length); // Make room for the option. - title = math.max(title, getLongOption(option).length); + title = math.max(title, _longOption(option).length); // Make room for the allowed help. if (option.allowedHelp != null) { for (var allowed in option.allowedHelp!.keys) { - title = math.max(title, getAllowedTitle(option, allowed).length); + title = math.max(title, _allowedTitle(option, allowed).length); } } } // Leave a gutter between the columns. title += 4; - columnWidths = [abbr, title]; + return [abbr, title]; } - void newline() { - newlinesNeeded++; - currentColumn = 0; - numHelpLines = 0; + void _newline() { + _newlinesNeeded++; + _currentColumn = 0; } - void write(int column, String text) { + void _write(int column, String text) { var lines = text.split('\n'); // If we are writing the last column, word wrap it to fit. - if (column == columnWidths.length && lineLength != null) { - var wrappedLines = []; - var start = columnWidths - .sublist(0, column) - .reduce((start, width) => start += width); - - for (var line in lines) { - wrappedLines - .addAll(wrapTextAsLines(line, start: start, length: lineLength)); - } - - lines = wrappedLines; + if (column == _columnWidths.length && lineLength != null) { + var start = + _columnWidths.take(column).reduce((start, width) => start + width); + lines = [ + for (var line in lines) + ...wrapTextAsLines(line, start: start, length: lineLength), + ]; } // Strip leading and trailing empty lines. - while (lines.isNotEmpty && lines[0].trim() == '') { - lines.removeRange(0, 1); + while (lines.isNotEmpty && lines.first.trim() == '') { + lines.removeAt(0); } - - while (lines.isNotEmpty && lines[lines.length - 1].trim() == '') { + while (lines.isNotEmpty && lines.last.trim() == '') { lines.removeLast(); } for (var line in lines) { - writeLine(column, line); + _writeLine(column, line); } } - void writeLine(int column, String text) { + void _writeLine(int column, String text) { // Write any pending newlines. - while (newlinesNeeded > 0) { - buffer.write('\n'); - newlinesNeeded--; + while (_newlinesNeeded > 0) { + _buffer.write('\n'); + _newlinesNeeded--; } // Advance until we are at the right column (which may mean wrapping around // to the next line. - while (currentColumn != column) { - if (currentColumn < _columnCount - 1) { - buffer.write(' ' * columnWidths[currentColumn]); + while (_currentColumn != column) { + if (_currentColumn < _columnCount - 1) { + _buffer.write(' ' * _columnWidths[_currentColumn]); } else { - buffer.write('\n'); + _buffer.write('\n'); } - currentColumn = (currentColumn + 1) % _columnCount; + _currentColumn = (_currentColumn + 1) % _columnCount; } - if (column < columnWidths.length) { + if (column < _columnWidths.length) { // Fixed-size column, so pad it. - buffer.write(text.padRight(columnWidths[column])); + _buffer.write(text.padRight(_columnWidths[column])); } else { // The last column, so just write it. - buffer.write(text); + _buffer.write(text); } // Advance to the next column. - currentColumn = (currentColumn + 1) % _columnCount; + _currentColumn = (_currentColumn + 1) % _columnCount; // If we reached the last column, we need to wrap to the next line. - if (column == _columnCount - 1) newlinesNeeded++; - - // Keep track of how many consecutive lines we've written in the last - // column. - if (column == _columnCount - 1) { - numHelpLines++; - } else { - numHelpLines = 0; - } + if (column == _columnCount - 1) _newlinesNeeded++; } - String buildAllowedList(Option option) { + String _buildAllowedList(Option option) { var isDefault = option.defaultsTo is List ? option.defaultsTo.contains : (value) => value == option.defaultsTo; From 971e62c08da7155af3d0eebef86ab337a1eb2bb1 Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Wed, 16 Dec 2020 15:14:08 -0800 Subject: [PATCH 195/263] Deprecate methods name get* (dart-lang/args#172) Towards dart-lang/args#171 --- pkgs/args/lib/src/allow_anything_parser.dart | 5 ++++ pkgs/args/lib/src/arg_parser.dart | 8 +++-- pkgs/args/lib/src/arg_results.dart | 2 +- pkgs/args/lib/src/option.dart | 5 +++- pkgs/args/lib/src/parser.dart | 2 +- pkgs/args/test/allow_anything_test.dart | 2 +- pkgs/args/test/args_test.dart | 31 ++++++++++---------- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index a91e155c..831e3a68 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -84,6 +84,11 @@ class AllowAnythingParser implements ArgParser { @override String get usage => ''; + @override + dynamic defaultFor(String option) { + throw ArgumentError('No option named $option'); + } + @override dynamic getDefault(String option) { throw ArgumentError('No option named $option'); diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index c584492d..4279738a 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -307,9 +307,8 @@ class ArgParser { return generateUsage(_optionsAndSeparators, lineLength: usageLineLength); } - /// Get the default value for an option. Useful after parsing to test if the - /// user specified something other than the default. - dynamic getDefault(String option) { + /// Returns the default value for [option]. + dynamic defaultFor(String option) { var value = options[option]; if (value == null) { throw ArgumentError('No option named $option'); @@ -317,6 +316,9 @@ class ArgParser { return value.defaultsTo; } + @Deprecated('Use defaultFor instead.') + dynamic getDefault(String option) => defaultFor(option); + /// Finds the option whose abbreviation is [abbr], or `null` if no option has /// that abbreviation. Option? findByAbbreviation(String abbr) { diff --git a/pkgs/args/lib/src/arg_results.dart b/pkgs/args/lib/src/arg_results.dart index fb2d0c82..0e49a765 100644 --- a/pkgs/args/lib/src/arg_results.dart +++ b/pkgs/args/lib/src/arg_results.dart @@ -65,7 +65,7 @@ class ArgResults { throw ArgumentError('Could not find an option named "$name".'); } - return _parser.options[name]!.getOrDefault(_parsed[name]); + return _parser.options[name]!.valueOrDefault(_parsed[name]); } /// The names of the available options. diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index 5361f262..a85acc85 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -133,12 +133,15 @@ class Option { /// For single-valued options, it will be [defaultsTo] if set or `null` /// otherwise. For multiple-valued options, it will be an empty list or a /// list containing [defaultsTo] if set. - dynamic getOrDefault(value) { + dynamic valueOrDefault(value) { if (value != null) return value; if (isMultiple) return defaultsTo ?? []; return defaultsTo; } + @Deprecated('Use valueOrDefault instead.') + dynamic getOrDefault(value) => valueOrDefault(value); + static final _invalidChars = RegExp(r'''[ \t\r\n"'\\/]'''); } diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index 4860a2e1..c14a01d3 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -95,7 +95,7 @@ class Parser { // Invoke the callbacks. grammar.options.forEach((name, option) { var callback = option.callback; - if (callback != null) callback(option.getOrDefault(results[name])); + if (callback != null) callback(option.valueOrDefault(results[name])); }); // Add in the leftover arguments we didn't parse to the innermost command. diff --git a/pkgs/args/test/allow_anything_test.dart b/pkgs/args/test/allow_anything_test.dart index 0ca31bff..2addd263 100644 --- a/pkgs/args/test/allow_anything_test.dart +++ b/pkgs/args/test/allow_anything_test.dart @@ -32,7 +32,7 @@ void main() { }); test('getDefault() throws an error', () { - expect(() => parser.getDefault('option'), throwsArgumentError); + expect(() => parser.defaultFor('option'), throwsArgumentError); }); test('parses all values as rest arguments', () { diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index 70ccd781..d3bfd448 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -124,13 +124,13 @@ void main() { test('returns the default value for an option', () { var parser = ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); - expect(parser.getDefault('mode'), 'debug'); + expect(parser.defaultFor('mode'), 'debug'); }); test('throws if the option is unknown', () { var parser = ArgParser(); parser.addOption('mode', defaultsTo: 'debug'); - throwsIllegalArg(() => parser.getDefault('undefined')); + throwsIllegalArg(() => parser.defaultFor('undefined')); }); }); @@ -261,7 +261,7 @@ void main() { }); group('Option', () { - test('.getOrDefault() returns a type-specific default value', () { + test('.valueOrDefault() returns a type-specific default value', () { var parser = ArgParser(); parser.addFlag('flag-no', defaultsTo: null); parser.addFlag('flag-def', defaultsTo: true); @@ -270,18 +270,19 @@ void main() { parser.addMultiOption('multi-no'); parser.addMultiOption('multi-def', defaultsTo: ['def']); - expect(parser.options['flag-no']!.getOrDefault(null), equals(null)); - expect(parser.options['flag-no']!.getOrDefault(false), equals(false)); - expect(parser.options['flag-def']!.getOrDefault(null), equals(true)); - expect(parser.options['flag-def']!.getOrDefault(false), equals(false)); - expect(parser.options['single-no']!.getOrDefault(null), equals(null)); - expect(parser.options['single-no']!.getOrDefault('v'), equals('v')); - expect(parser.options['single-def']!.getOrDefault(null), equals('def')); - expect(parser.options['single-def']!.getOrDefault('v'), equals('v')); - expect(parser.options['multi-no']!.getOrDefault(null), equals([])); - expect(parser.options['multi-no']!.getOrDefault(['v']), equals(['v'])); - expect(parser.options['multi-def']!.getOrDefault(null), equals(['def'])); - expect(parser.options['multi-def']!.getOrDefault(['v']), equals(['v'])); + expect(parser.options['flag-no']!.valueOrDefault(null), equals(null)); + expect(parser.options['flag-no']!.valueOrDefault(false), equals(false)); + expect(parser.options['flag-def']!.valueOrDefault(null), equals(true)); + expect(parser.options['flag-def']!.valueOrDefault(false), equals(false)); + expect(parser.options['single-no']!.valueOrDefault(null), equals(null)); + expect(parser.options['single-no']!.valueOrDefault('v'), equals('v')); + expect(parser.options['single-def']!.valueOrDefault(null), equals('def')); + expect(parser.options['single-def']!.valueOrDefault('v'), equals('v')); + expect(parser.options['multi-no']!.valueOrDefault(null), equals([])); + expect(parser.options['multi-no']!.valueOrDefault(['v']), equals(['v'])); + expect( + parser.options['multi-def']!.valueOrDefault(null), equals(['def'])); + expect(parser.options['multi-def']!.valueOrDefault(['v']), equals(['v'])); }); }); } From bd0fd385309f8191b1b593fa2941f9f8c2e038a4 Mon Sep 17 00:00:00 2001 From: Alexander Thomas Date: Thu, 17 Dec 2020 20:31:04 +0100 Subject: [PATCH 196/263] Migrate to GitHub Actions (dart-lang/args#176) --- pkgs/args/.github/workflows/test-package.yml | 64 ++++++++++++++++++++ pkgs/args/.travis.yml | 34 ----------- 2 files changed, 64 insertions(+), 34 deletions(-) create mode 100644 pkgs/args/.github/workflows/test-package.yml delete mode 100644 pkgs/args/.travis.yml diff --git a/pkgs/args/.github/workflows/test-package.yml b/pkgs/args/.github/workflows/test-package.yml new file mode 100644 index 00000000..8bc4bac5 --- /dev/null +++ b/pkgs/args/.github/workflows/test-package.yml @@ -0,0 +1,64 @@ +name: Dart CI + +on: + # Run CI on pushes to the main branch, and on PRs against main. + push: + branches: [ master ] + pull_request: + branches: [ master ] + schedule: + - cron: "0 0 * * 0" +env: + PUB_ENVIRONMENT: bot.github + +jobs: + # Check code formatting and static analysis on a single OS (linux) + # against Dart dev. + analyze: + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.1 + with: + channel: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Check formatting + if: always() && steps.install.outcome == 'success' + run: dart format --output=none --set-exit-if-changed . + - name: Analyze code + run: dart analyze + if: always() && steps.install.outcome == 'success' + + # Run tests on a matrix consisting of three dimensions: + # 1. OS: ubuntu-latest, (macos-latest, windows-latest) + # 2. release channel: dev, (stable) + test: + needs: analyze + runs-on: ${{ matrix.os }} + strategy: + fail-fast: false + matrix: + # Add macos-latest and/or windows-latest if relevant for this package + os: [ubuntu-latest] + # Add stable if the package should also be tested on stable + sdk: [dev] + steps: + - uses: actions/checkout@v2 + - uses: dart-lang/setup-dart@v0.1 + with: + channel: ${{ matrix.sdk }} + - id: install + name: Install dependencies + run: dart pub get + - name: Run VM tests + run: dart test --platform vm + if: always() && steps.install.outcome == 'success' + - name: Run Chrome tests + run: dart test --platform chrome + if: always() && steps.install.outcome == 'success' diff --git a/pkgs/args/.travis.yml b/pkgs/args/.travis.yml deleted file mode 100644 index bdf0b3c2..00000000 --- a/pkgs/args/.travis.yml +++ /dev/null @@ -1,34 +0,0 @@ -language: dart - -dart: -- dev - -jobs: - include: - - stage: analyze_and_format - name: "Analyze" - dart: dev - os: linux - script: dartanalyzer --fatal-warnings --fatal-infos . - - stage: analyze_and_format - name: "Format" - dart: dev - os: linux - script: dartfmt -n --set-exit-if-changed . - - stage: test - name: "Vm Tests" - dart: dev - os: linux - script: pub run test -p vm - - stage: test - name: "Web Tests" - dart: dev - os: linux - script: pub run test -p chrome - -branches: - only: [master] - -cache: - directories: - - $HOME/.pub-cache From 79e61e03f2210f363b354644337a24dd4ed04cdd Mon Sep 17 00:00:00 2001 From: Nate Bosch Date: Thu, 17 Dec 2020 12:34:45 -0800 Subject: [PATCH 197/263] Add a test for passing allowedHelp without allowed (dart-lang/args#170) Avoid a potential breaking solution for dart-lang/args#25 --- pkgs/args/test/parse_test.dart | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/pkgs/args/test/parse_test.dart b/pkgs/args/test/parse_test.dart index 6f56db4b..a785f6ee 100644 --- a/pkgs/args/test/parse_test.dart +++ b/pkgs/args/test/parse_test.dart @@ -433,6 +433,16 @@ void main() { expect(args['mode'], equals('debug')); }); + test('do not throw if there is no allowed set with allowedHelp', () { + var parser = ArgParser(); + parser.addOption('mode', allowedHelp: { + 'debug': 'During development.', + 'release': 'For customers.' + }); + var args = parser.parse(['--mode=profile']); + expect(args['mode'], equals('profile')); + }); + test('throw if the value is not in the allowed set', () { var parser = ArgParser(); parser.addOption('mode', allowed: ['debug', 'release']); From 1bcd1d312ea2f7908003f7d0692a8b010d17d4e3 Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Fri, 5 Feb 2021 13:43:02 -0800 Subject: [PATCH 198/263] stable null safety release (dart-lang/args#180) --- pkgs/args/CHANGELOG.md | 4 ++++ pkgs/args/pubspec.yaml | 6 +++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index 7670a41f..d5de6a9e 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,7 @@ +## 2.0.0 + +* Stable null safety release. + ## 2.0.0-nullsafety.0 * Migrate to null safety. diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index 4649817e..f39c81fc 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 2.0.0-nullsafety.0 +version: 2.0.0 homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set @@ -9,8 +9,8 @@ environment: sdk: '>=2.12.0-0 <3.0.0' dev_dependencies: - pedantic: ^1.10.0-nullsafety.3 - test: ^1.16.0-nullsafety.9 + pedantic: ^1.10.0 + test: ^1.16.0 # Required to get a version solve due to cyclic dependency dependency_overrides: From 5b58d6d76ed1eac6fa6122c6c48f7193ccba62eb Mon Sep 17 00:00:00 2001 From: Jacob MacDonald Date: Thu, 18 Feb 2021 07:18:00 -0800 Subject: [PATCH 199/263] Add support for argument name aliases (dart-lang/args#182) Closes https://github.com/dart-lang/args/issues/181 Add `aliases` named argument to `addFlag`, `addOption`, and `addMultiOption`, as well as a public `findByNameOrAlias` method on `ArgParser`. This allows you to provide aliases for an argument name, which eases the transition from one argument name to another. Keys are _not_ added to the `options` map for each alias. cc @Hixie --- pkgs/args/CHANGELOG.md | 7 +++ pkgs/args/lib/src/allow_anything_parser.dart | 12 +++-- pkgs/args/lib/src/arg_parser.dart | 55 +++++++++++++++----- pkgs/args/lib/src/option.dart | 14 +++-- pkgs/args/lib/src/parser.dart | 4 +- pkgs/args/pubspec.yaml | 2 +- pkgs/args/test/args_test.dart | 34 ++++++++++++ pkgs/args/test/parse_test.dart | 25 +++++++++ 8 files changed, 131 insertions(+), 22 deletions(-) diff --git a/pkgs/args/CHANGELOG.md b/pkgs/args/CHANGELOG.md index d5de6a9e..d09d0069 100644 --- a/pkgs/args/CHANGELOG.md +++ b/pkgs/args/CHANGELOG.md @@ -1,3 +1,10 @@ +## 2.1.0-dev + +* Add `aliases` named argument to `addFlag`, `addOption`, and `addMultiOption`, + as well as a public `findByNameOrAlias` method on `ArgParser`. This allows + you to provide aliases for an argument name, which eases the transition from + one argument name to another. + ## 2.0.0 * Stable null safety release. diff --git a/pkgs/args/lib/src/allow_anything_parser.dart b/pkgs/args/lib/src/allow_anything_parser.dart index 831e3a68..becff98e 100644 --- a/pkgs/args/lib/src/allow_anything_parser.dart +++ b/pkgs/args/lib/src/allow_anything_parser.dart @@ -35,7 +35,8 @@ class AllowAnythingParser implements ArgParser { bool? defaultsTo = false, bool negatable = true, void Function(bool)? callback, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { throw UnsupportedError( "ArgParser.allowAnything().addFlag() isn't supported."); } @@ -51,7 +52,8 @@ class AllowAnythingParser implements ArgParser { void Function(String?)? callback, bool allowMultiple = false, bool? splitCommas, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { throw UnsupportedError( "ArgParser.allowAnything().addOption() isn't supported."); } @@ -66,7 +68,8 @@ class AllowAnythingParser implements ArgParser { Iterable? defaultsTo, void Function(List)? callback, bool splitCommas = true, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { throw UnsupportedError( "ArgParser.allowAnything().addMultiOption() isn't supported."); } @@ -96,4 +99,7 @@ class AllowAnythingParser implements ArgParser { @override Option? findByAbbreviation(String abbr) => null; + + @override + Option? findByNameOrAlias(String name) => null; } diff --git a/pkgs/args/lib/src/arg_parser.dart b/pkgs/args/lib/src/arg_parser.dart index 4279738a..87f7a02c 100644 --- a/pkgs/args/lib/src/arg_parser.dart +++ b/pkgs/args/lib/src/arg_parser.dart @@ -16,6 +16,9 @@ class ArgParser { final Map _options; final Map _commands; + /// A map of aliases to the option names they alias. + final Map _aliases; + /// The options that have been defined for this parser. final Map options; @@ -53,7 +56,7 @@ class ArgParser { /// the parser stops parsing as soon as it finds an argument that is neither /// an option nor a command. factory ArgParser({bool allowTrailingOptions = true, int? usageLineLength}) => - ArgParser._({}, {}, + ArgParser._({}, {}, {}, allowTrailingOptions: allowTrailingOptions, usageLineLength: usageLineLength); @@ -66,6 +69,7 @@ class ArgParser { factory ArgParser.allowAnything() = AllowAnythingParser; ArgParser._(Map options, Map commands, + this._aliases, {bool allowTrailingOptions = true, this.usageLineLength}) : _options = options, options = UnmodifiableMapView(options), @@ -116,6 +120,9 @@ class ArgParser { /// /// If [hide] is `true`, this option won't be included in [usage]. /// + /// If [aliases] is provided, these are used as aliases for [name]. These + /// aliases will not appear as keys in the [options] map. + /// /// Throws an [ArgumentError] if: /// /// * There is already an option named [name]. @@ -126,7 +133,8 @@ class ArgParser { bool? defaultsTo = false, bool negatable = true, void Function(bool)? callback, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { _addOption( name, abbr, @@ -138,7 +146,8 @@ class ArgParser { callback == null ? null : (value) => callback(value as bool), OptionType.flag, negatable: negatable, - hide: hide); + hide: hide, + aliases: aliases); } /// Defines an option that takes a value. @@ -174,6 +183,9 @@ class ArgParser { /// /// If [hide] is `true`, this option won't be included in [usage]. /// + /// If [aliases] is provided, these are used as aliases for [name]. These + /// aliases will not appear as keys in the [options] map. + /// /// Throws an [ArgumentError] if: /// /// * There is already an option with name [name]. @@ -186,10 +198,11 @@ class ArgParser { Map? allowedHelp, String? defaultsTo, void Function(String?)? callback, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { _addOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, OptionType.single, - hide: hide); + hide: hide, aliases: aliases); } /// Defines an option that takes multiple values. @@ -225,6 +238,9 @@ class ArgParser { /// /// If [hide] is `true`, this option won't be included in [usage]. /// + /// If [aliases] is provided, these are used as aliases for [name]. These + /// aliases will not appear as keys in the [options] map. + /// /// Throws an [ArgumentError] if: /// /// * There is already an option with name [name]. @@ -238,7 +254,8 @@ class ArgParser { Iterable? defaultsTo, void Function(List)? callback, bool splitCommas = true, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { _addOption( name, abbr, @@ -250,7 +267,8 @@ class ArgParser { callback == null ? null : (value) => callback(value as List), OptionType.multiple, splitCommas: splitCommas, - hide: hide); + hide: hide, + aliases: aliases); } void _addOption( @@ -265,10 +283,11 @@ class ArgParser { OptionType type, {bool negatable = false, bool? splitCommas, - bool hide = false}) { - // Make sure the name isn't in use. - if (_options.containsKey(name)) { - throw ArgumentError('Duplicate option "$name".'); + bool hide = false, + List aliases = const []}) { + var allNames = [name, ...aliases]; + if (allNames.any((name) => findByNameOrAlias(name) != null)) { + throw ArgumentError('Duplicate option or alias "$name".'); } // Make sure the abbreviation isn't too long or in use. @@ -282,9 +301,15 @@ class ArgParser { var option = newOption(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, type, - negatable: negatable, splitCommas: splitCommas, hide: hide); + negatable: negatable, + splitCommas: splitCommas, + hide: hide, + aliases: aliases); _options[name] = option; _optionsAndSeparators.add(option); + for (var alias in aliases) { + _aliases[alias] = name; + } } /// Adds a separator line to the usage. @@ -309,7 +334,7 @@ class ArgParser { /// Returns the default value for [option]. dynamic defaultFor(String option) { - var value = options[option]; + var value = findByNameOrAlias(option); if (value == null) { throw ArgumentError('No option named $option'); } @@ -327,4 +352,8 @@ class ArgParser { } return null; } + + /// Finds the option whose name or alias matches [name], or `null` if no + /// option has that name or alias. + Option? findByNameOrAlias(String name) => options[_aliases[name] ?? name]; } diff --git a/pkgs/args/lib/src/option.dart b/pkgs/args/lib/src/option.dart index a85acc85..7a91a4b8 100644 --- a/pkgs/args/lib/src/option.dart +++ b/pkgs/args/lib/src/option.dart @@ -18,10 +18,14 @@ Option newOption( OptionType type, {bool? negatable, bool? splitCommas, - bool hide = false}) { + bool hide = false, + List aliases = const []}) { return Option._(name, abbr, help, valueHelp, allowed, allowedHelp, defaultsTo, callback, type, - negatable: negatable, splitCommas: splitCommas, hide: hide); + negatable: negatable, + splitCommas: splitCommas, + hide: hide, + aliases: aliases); } /// A command-line option. @@ -73,6 +77,9 @@ class Option { /// Whether this option should be hidden from usage documentation. final bool hide; + /// All aliases for [name]. + final List aliases; + /// Whether the option is boolean-valued flag. bool get isFlag => type == OptionType.flag; @@ -94,7 +101,8 @@ class Option { OptionType type, {this.negatable, bool? splitCommas, - this.hide = false}) + this.hide = false, + this.aliases = const []}) : allowed = allowed == null ? null : List.unmodifiable(allowed), allowedHelp = allowedHelp == null ? null : Map.unmodifiable(allowedHelp), diff --git a/pkgs/args/lib/src/parser.dart b/pkgs/args/lib/src/parser.dart index c14a01d3..9cfaa60b 100644 --- a/pkgs/args/lib/src/parser.dart +++ b/pkgs/args/lib/src/parser.dart @@ -243,7 +243,7 @@ class Parser { return false; } - var option = grammar.options[name]; + var option = grammar.findByNameOrAlias(name); if (option != null) { args.removeFirst(); if (option.isFlag) { @@ -261,7 +261,7 @@ class Parser { } else if (name.startsWith('no-')) { // See if it's a negated flag. name = name.substring('no-'.length); - option = grammar.options[name]; + option = grammar.findByNameOrAlias(name); if (option == null) { // Walk up to the parent command if possible. validate(parent != null, 'Could not find an option named "$name".'); diff --git a/pkgs/args/pubspec.yaml b/pkgs/args/pubspec.yaml index f39c81fc..c980dba6 100644 --- a/pkgs/args/pubspec.yaml +++ b/pkgs/args/pubspec.yaml @@ -1,5 +1,5 @@ name: args -version: 2.0.0 +version: 2.1.0-dev homepage: https://github.com/dart-lang/args description: >- Library for defining parsers for parsing raw command-line arguments into a set diff --git a/pkgs/args/test/args_test.dart b/pkgs/args/test/args_test.dart index d3bfd448..04bbc471 100644 --- a/pkgs/args/test/args_test.dart +++ b/pkgs/args/test/args_test.dart @@ -184,6 +184,40 @@ void main() { }); }); + group('ArgParser.findByNameOrAlias', () { + test('returns null if there is no match', () { + var parser = ArgParser(); + expect(parser.findByNameOrAlias('a'), isNull); + }); + + test('can find options by alias', () { + var parser = ArgParser()..addOption('a', aliases: ['b']); + expect(parser.findByNameOrAlias('b'), + isA