Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file |
| 2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
| 3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
| 4 | 4 |
| 5 /** | 5 /** |
| 6 * This library lets you define parsers for parsing raw command-line arguments | 6 * This library lets you define parsers for parsing raw command-line arguments |
| 7 * into a set of options and values using [GNU][] and [POSIX][] style options. | 7 * into a set of options and values using [GNU][] and [POSIX][] style options. |
| 8 * | 8 * |
| 9 * ## Defining options ## | 9 * ## Defining options ## |
| 10 * | 10 * |
| (...skipping 105 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 116 * you define: | 116 * you define: |
| 117 * | 117 * |
| 118 * parser.addFlag('verbose', abbr: 'v'); | 118 * parser.addFlag('verbose', abbr: 'v'); |
| 119 * parser.addFlag('french', abbr: 'f'); | 119 * parser.addFlag('french', abbr: 'f'); |
| 120 * parser.addFlag('iambic-pentameter', abbr: 'i'); | 120 * parser.addFlag('iambic-pentameter', abbr: 'i'); |
| 121 * | 121 * |
| 122 * Then all three flags could be set using: | 122 * Then all three flags could be set using: |
| 123 * | 123 * |
| 124 * -vfi | 124 * -vfi |
| 125 * | 125 * |
| 126 * By default, an option has only a single value, with later option values | |
| 127 * overriding earlier ones, unless you set the [multiValued] flag. In that | |
| 128 * case the option can occur multiple times and when parsing arguments a | |
| 129 * List of values will be returned. | |
|
Bob Nystrom
2012/08/09 23:08:40
You should show an example of this.
gram
2012/08/09 23:47:00
Done.
| |
| 130 * | |
| 126 * ## Usage ## | 131 * ## Usage ## |
| 127 * | 132 * |
| 128 * This library can also be used to automatically generate nice usage help | 133 * This library can also be used to automatically generate nice usage help |
| 129 * text like you get when you run a program with `--help`. To use this, you | 134 * text like you get when you run a program with `--help`. To use this, you |
| 130 * will also want to provide some help text when you create your options. To | 135 * will also want to provide some help text when you create your options. To |
| 131 * define help text for the entire option, do: | 136 * define help text for the entire option, do: |
| 132 * | 137 * |
| 133 * parser.addOption('mode', help: 'The compiler configuration', | 138 * parser.addOption('mode', help: 'The compiler configuration', |
| 134 * allowed: ['debug', 'release']); | 139 * allowed: ['debug', 'release']); |
| 135 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); | 140 * parser.addFlag('verbose', help: 'Show additional diagnostic info'); |
| (...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 207 } | 212 } |
| 208 | 213 |
| 209 /** | 214 /** |
| 210 * Defines a value-taking option. Throws an [IllegalArgumentException] if: | 215 * Defines a value-taking option. Throws an [IllegalArgumentException] if: |
| 211 * | 216 * |
| 212 * * There is already an option with name [name]. | 217 * * There is already an option with name [name]. |
| 213 * * There is already an option using abbreviation [abbr]. | 218 * * There is already an option using abbreviation [abbr]. |
| 214 */ | 219 */ |
| 215 void addOption(String name, [String abbr, String help, List<String> allowed, | 220 void addOption(String name, [String abbr, String help, List<String> allowed, |
| 216 Map<String, String> allowedHelp, String defaultsTo, | 221 Map<String, String> allowedHelp, String defaultsTo, |
| 217 void callback(bool value)]) { | 222 void callback(bool value), bool multiValued]) { |
|
Bob Nystrom
2012/08/09 23:08:40
How about:
"multiValued" -> "allowMultiple"
gram
2012/08/09 23:47:00
Done.
| |
| 218 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, | 223 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo, |
| 219 callback, isFlag: false); | 224 callback, isFlag: false, multiValued: multiValued); |
| 220 } | 225 } |
| 221 | 226 |
| 222 void _addOption(String name, String abbr, String help, List<String> allowed, | 227 void _addOption(String name, String abbr, String help, List<String> allowed, |
| 223 Map<String, String> allowedHelp, defaultsTo, | 228 Map<String, String> allowedHelp, defaultsTo, |
| 224 void callback(bool value), [bool isFlag, bool negatable = false]) { | 229 void callback(bool value), [bool isFlag, bool negatable = false, |
| 230 bool multiValued = false]) { | |
| 225 // Make sure the name isn't in use. | 231 // Make sure the name isn't in use. |
| 226 if (_options.containsKey(name)) { | 232 if (_options.containsKey(name)) { |
| 227 throw new IllegalArgumentException('Duplicate option "$name".'); | 233 throw new IllegalArgumentException('Duplicate option "$name".'); |
| 228 } | 234 } |
| 229 | 235 |
| 230 // Make sure the abbreviation isn't too long or in use. | 236 // Make sure the abbreviation isn't too long or in use. |
| 231 if (abbr != null) { | 237 if (abbr != null) { |
| 232 if (abbr.length > 1) { | 238 if (abbr.length > 1) { |
| 233 throw new IllegalArgumentException( | 239 throw new IllegalArgumentException( |
| 234 'Abbreviation "$abbr" is longer than one character.'); | 240 'Abbreviation "$abbr" is longer than one character.'); |
| 235 } | 241 } |
| 236 | 242 |
| 237 var existing = _findByAbbr(abbr); | 243 var existing = _findByAbbr(abbr); |
| 238 if (existing != null) { | 244 if (existing != null) { |
| 239 throw new IllegalArgumentException( | 245 throw new IllegalArgumentException( |
| 240 'Abbreviation "$abbr" is already used by "${existing.name}".'); | 246 'Abbreviation "$abbr" is already used by "${existing.name}".'); |
| 241 } | 247 } |
| 242 } | 248 } |
| 243 | 249 |
| 244 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, | 250 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp, |
| 245 defaultsTo, callback, isFlag: isFlag, negatable: negatable); | 251 defaultsTo, callback, isFlag: isFlag, negatable: negatable, |
| 252 multiValued: multiValued); | |
| 246 _optionNames.add(name); | 253 _optionNames.add(name); |
| 247 } | 254 } |
| 248 | 255 |
| 249 /** | 256 /** |
| 250 * Parses [args], a list of command-line arguments, matches them against the | 257 * Parses [args], a list of command-line arguments, matches them against the |
| 251 * flags and options defined by this parser, and returns the result. | 258 * flags and options defined by this parser, and returns the result. |
| 252 */ | 259 */ |
| 253 ArgResults parse(List<String> args) { | 260 ArgResults parse(List<String> args) { |
| 254 _args = args; | 261 _args = args; |
| 255 _current = 0; | 262 _current = 0; |
| 256 var results = {}; | 263 var results = {}; |
| 257 | 264 |
| 258 // Initialize flags to their defaults. | 265 // Initialize flags to their defaults. |
| 259 _options.forEach((name, option) { | 266 _options.forEach((name, option) { |
| 260 results[name] = option.defaultValue; | 267 if (option.multiValued) { |
| 268 results[name] = new List(); | |
|
Bob Nystrom
2012/08/09 23:08:40
'new List()' => '[]'
gram
2012/08/09 23:47:00
Done.
| |
| 269 } else { | |
| 270 results[name] = option.defaultValue; | |
| 271 } | |
| 261 }); | 272 }); |
| 262 | 273 |
| 263 // Parse the args. | 274 // Parse the args. |
| 264 for (_current = 0; _current < args.length; _current++) { | 275 for (_current = 0; _current < args.length; _current++) { |
| 265 var arg = args[_current]; | 276 var arg = args[_current]; |
| 266 | 277 |
| 267 if (arg == '--') { | 278 if (arg == '--') { |
| 268 // Reached the argument terminator, so stop here. | 279 // Reached the argument terminator, so stop here. |
| 269 _current++; | 280 _current++; |
| 270 break; | 281 break; |
| 271 } | 282 } |
| 272 | 283 |
| 273 // Try to parse the current argument as an option. Note that the order | 284 // Try to parse the current argument as an option. Note that the order |
| 274 // here matters. | 285 // here matters. |
| 275 if (_parseSoloOption(results)) continue; | 286 if (_parseSoloOption(results)) continue; |
| 276 if (_parseAbbreviation(results)) continue; | 287 if (_parseAbbreviation(results)) continue; |
| 277 if (_parseLongOption(results)) continue; | 288 if (_parseLongOption(results)) continue; |
| 278 | 289 |
| 279 // If we got here, the argument doesn't look like an option, so stop. | 290 // If we got here, the argument doesn't look like an option, so stop. |
| 280 break; | 291 break; |
| 281 } | 292 } |
| 282 | 293 |
| 283 // Invoke the callbacks. | 294 // Set unspecified multivalued arguments to their default value, |
| 295 // if any, and invoke the callbacks. | |
| 284 for (var name in _optionNames) { | 296 for (var name in _optionNames) { |
| 285 var option = _options[name]; | 297 var option = _options[name]; |
| 298 if (option.multiValued && | |
| 299 results[name].length == 0 && | |
| 300 option.defaultValue != null) { | |
| 301 results[name].add(option.defaultValue); | |
| 302 } | |
| 286 if (option.callback != null) option.callback(results[name]); | 303 if (option.callback != null) option.callback(results[name]); |
| 287 } | 304 } |
| 288 | 305 |
| 289 // Add in the leftover arguments we didn't parse. | 306 // Add in the leftover arguments we didn't parse. |
| 290 return new ArgResults(results, | 307 return new ArgResults(results, |
| 291 _args.getRange(_current, _args.length - _current)); | 308 _args.getRange(_current, _args.length - _current)); |
| 292 } | 309 } |
| 293 | 310 |
| 294 /** | 311 /** |
| 295 * Generates a string displaying usage information for the defined options. | 312 * Generates a string displaying usage information for the defined options. |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 307 if (!condition) throw new FormatException(message); | 324 if (!condition) throw new FormatException(message); |
| 308 } | 325 } |
| 309 | 326 |
| 310 /** Validates and stores [value] as the value for [option]. */ | 327 /** Validates and stores [value] as the value for [option]. */ |
| 311 _setOption(Map results, _Option option, value) { | 328 _setOption(Map results, _Option option, value) { |
| 312 // See if it's one of the allowed values. | 329 // See if it's one of the allowed values. |
| 313 if (option.allowed != null) { | 330 if (option.allowed != null) { |
| 314 _validate(option.allowed.some((allow) => allow == value), | 331 _validate(option.allowed.some((allow) => allow == value), |
| 315 '"$value" is not an allowed value for option "${option.name}".'); | 332 '"$value" is not an allowed value for option "${option.name}".'); |
| 316 } | 333 } |
| 317 | 334 if (option.multiValued) { |
|
Bob Nystrom
2012/08/09 23:08:40
Nit, but can you add a blank line above this? I te
gram
2012/08/09 23:47:00
Done.
| |
| 318 results[option.name] = value; | 335 results[option.name].add(value); |
| 336 } else { | |
| 337 results[option.name] = value; | |
| 338 } | |
| 319 } | 339 } |
| 320 | 340 |
| 321 /** | 341 /** |
| 322 * Pulls the value for [option] from the next argument in [_args] (where the | 342 * Pulls the value for [option] from the next argument in [_args] (where the |
| 323 * current option is at index [_current]. Validates that there is a valid | 343 * current option is at index [_current]. Validates that there is a valid |
| 324 * value there. | 344 * value there. |
| 325 */ | 345 */ |
| 326 void _readNextArgAsValue(Map results, _Option option) { | 346 void _readNextArgAsValue(Map results, _Option option) { |
| 327 _current++; | 347 _current++; |
| 328 // Take the option argument from the next command line arg. | 348 // Take the option argument from the next command line arg. |
| (...skipping 161 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 490 class _Option { | 510 class _Option { |
| 491 final String name; | 511 final String name; |
| 492 final String abbreviation; | 512 final String abbreviation; |
| 493 final List allowed; | 513 final List allowed; |
| 494 final defaultValue; | 514 final defaultValue; |
| 495 final Function callback; | 515 final Function callback; |
| 496 final String help; | 516 final String help; |
| 497 final Map<String, String> allowedHelp; | 517 final Map<String, String> allowedHelp; |
| 498 final bool isFlag; | 518 final bool isFlag; |
| 499 final bool negatable; | 519 final bool negatable; |
| 520 final bool multiValued; | |
| 500 | 521 |
| 501 _Option(this.name, this.abbreviation, this.help, this.allowed, | 522 _Option(this.name, this.abbreviation, this.help, this.allowed, |
| 502 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, | 523 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag, |
| 503 this.negatable]); | 524 this.negatable, this.multiValued]); |
| 504 } | 525 } |
| 505 | 526 |
| 506 /** | 527 /** |
| 507 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its | 528 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its |
| 508 * defined options. Internally, it works like a tabular printer. The output is | 529 * defined options. Internally, it works like a tabular printer. The output is |
| 509 * divided into three horizontal columns, like so: | 530 * divided into three horizontal columns, like so: |
| 510 * | 531 * |
| 511 * -h, --help Prints the usage information | 532 * -h, --help Prints the usage information |
| 512 * | | | | | 533 * | | | | |
| 513 * | 534 * |
| (...skipping 201 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 715 allowedBuffer.add(allowed); | 736 allowedBuffer.add(allowed); |
| 716 if (allowed == option.defaultValue) { | 737 if (allowed == option.defaultValue) { |
| 717 allowedBuffer.add(' (default)'); | 738 allowedBuffer.add(' (default)'); |
| 718 } | 739 } |
| 719 first = false; | 740 first = false; |
| 720 } | 741 } |
| 721 allowedBuffer.add(']'); | 742 allowedBuffer.add(']'); |
| 722 return allowedBuffer.toString(); | 743 return allowedBuffer.toString(); |
| 723 } | 744 } |
| 724 } | 745 } |
| OLD | NEW |