Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(766)

Side by Side Diff: pkg/args/args.dart

Issue 10919249: Make args package follow new layout. (Closed) Base URL: https://dart.googlecode.com/svn/branches/bleeding_edge/dart
Patch Set: Fix path to args.dart in testrunner. Created 8 years, 3 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
« no previous file with comments | « no previous file | pkg/args/example.dart » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
(Empty)
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
3 // BSD-style license that can be found in the LICENSE file.
4
5 /**
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.
8 *
9 * ## Defining options ##
10 *
11 * To use this library, you create an [ArgParser] object which will contain
12 * the set of options you support:
13 *
14 * var parser = new ArgParser();
15 *
16 * Then you define a set of options on that parser using [addOption()] and
17 * [addFlag()]. The minimal way to create an option is:
18 *
19 * parser.addOption('name');
20 *
21 * This creates an option named "name". Options must be given a value on the
22 * command line. If you have a simple on/off flag, you can instead use:
23 *
24 * parser.addFlag('name');
25 *
26 * (From here on out "option" will refer to both "regular" options and flags.
27 * In cases where the distinction matters, we'll use "non-flag option".)
28 *
29 * Options may have an optional single-character abbreviation:
30 *
31 * parser.addOption('mode', abbr: 'm');
32 * parser.addFlag('verbose', abbr: 'v');
33 *
34 * They may also specify a default value. The default value will be used if the
35 * option isn't provided:
36 *
37 * parser.addOption('mode', defaultsTo: 'debug');
38 * parser.addFlag('verbose', defaultsTo: false);
39 *
40 * The default value for non-flag options can be any [String]. For flags, it
41 * must be a [bool].
42 *
43 * To validate non-flag options, you may provide an allowed set of values. When
44 * you do, it will throw a [FormatException] when you parse the arguments if
45 * the value for an option is not in the allowed set:
46 *
47 * parser.addOption('mode', allowed: ['debug', 'release']);
48 *
49 * You can provide a callback when you define an option. When you later parse
50 * a set of arguments, the callback for that option will be invoked with the
51 * value provided for it:
52 *
53 * parser.addOption('mode', callback: (mode) => print('Got mode $mode));
54 * parser.addFlag('verbose', callback: (verbose) {
55 * if (verbose) print('Verbose');
56 * });
57 *
58 * The callback for each option will *always* be called when you parse a set of
59 * arguments. If the option isn't provided in the args, the callback will be
60 * passed the default value, or `null` if there is none set.
61 *
62 * ## Parsing arguments ##
63 *
64 * Once you have an [ArgParser] set up with some options and flags, you use it
65 * by calling [ArgParser.parse()] with a set of arguments:
66 *
67 * var results = parser.parse(['some', 'command', 'line', 'args']);
68 *
69 * These will usually come from `new Options().arguments`, but you can pass in
70 * any list of strings. It returns an instance of [ArgResults]. This is a
71 * map-like object that will return the value of any parsed option.
72 *
73 * var parser = new ArgParser();
74 * parser.addOption('mode');
75 * parser.addFlag('verbose', defaultsTo: true);
76 * var results = parser.parse('['--mode', 'debug', 'something', 'else']);
77 *
78 * print(results['mode']); // debug
79 * print(results['verbose']); // true
80 *
81 * The [parse()] method will stop as soon as it reaches `--` or anything that
82 * it doesn't recognize as an option, flag, or option value. If there are still
83 * arguments left, they will be provided to you in
84 * [ArgResults.rest].
85 *
86 * print(results.rest); // ['something', 'else']
87 *
88 * ## Specifying options ##
89 *
90 * To actually pass in options and flags on the command line, use GNU or POSIX
91 * style. If you define an option like:
92 *
93 * parser.addOption('name', abbr: 'n');
94 *
95 * Then a value for it can be specified on the command line using any of:
96 *
97 * --name=somevalue
98 * --name somevalue
99 * -nsomevalue
100 * -n somevalue
101 *
102 * Given this flag:
103 *
104 * parser.addFlag('name', abbr: 'n');
105 *
106 * You can set it on using one of:
107 *
108 * --name
109 * -n
110 *
111 * Or set it off using:
112 *
113 * --no-name
114 *
115 * Multiple flag abbreviation can also be collapsed into a single argument. If
116 * you define:
117 *
118 * parser.addFlag('verbose', abbr: 'v');
119 * parser.addFlag('french', abbr: 'f');
120 * parser.addFlag('iambic-pentameter', abbr: 'i');
121 *
122 * Then all three flags could be set using:
123 *
124 * -vfi
125 *
126 * By default, an option has only a single value, with later option values
127 * overriding earlier ones; for example:
128 *
129 * var parser = new ArgParser();
130 * parser.addOption('mode');
131 * var results = parser.parse(['--mode', 'on', '--mode', 'off']);
132 * print(results['mode']); // prints 'off'
133 *
134 * If you need multiple values, set the [allowMultiple] flag. In that
135 * case the option can occur multiple times and when parsing arguments a
136 * List of values will be returned:
137 *
138 * var parser = new ArgParser();
139 * parser.addOption('mode', allowMultiple: true);
140 * var results = parser.parse(['--mode', 'on', '--mode', 'off']);
141 * print(results['mode']); // prints '[on, off]'
142 *
143 * ## Usage ##
144 *
145 * This library can also be used to automatically generate nice usage help
146 * text like you get when you run a program with `--help`. To use this, you
147 * will also want to provide some help text when you create your options. To
148 * define help text for the entire option, do:
149 *
150 * parser.addOption('mode', help: 'The compiler configuration',
151 * allowed: ['debug', 'release']);
152 * parser.addFlag('verbose', help: 'Show additional diagnostic info');
153 *
154 * For non-flag options, you can also provide detailed help for each expected
155 * value using a map:
156 *
157 * parser.addOption('arch', help: 'The architecture to compile for',
158 * allowedHelp: {
159 * 'ia32': 'Intel x86',
160 * 'arm': 'ARM Holding 32-bit chip'
161 * });
162 *
163 * If you define a set of options like the above, then calling this:
164 *
165 * print(parser.getUsage());
166 *
167 * Will display something like:
168 *
169 * --mode The compiler configuration
170 * [debug, release]
171 *
172 * --[no-]verbose Show additional diagnostic info
173 * --arch The architecture to compile for
174 *
175 * [arm] ARM Holding 32-bit chip
176 * [ia32] Intel x86
177 *
178 * [posix]: http://pubs.opengroup.org/onlinepubs/009695399/basedefs/xbd_chap12.h tml#tag_12_02
179 * [gnu]: http://www.gnu.org/prep/standards/standards.html#Command_002dLine-Inte rfaces
180 */
181 #library('args');
182
183 #import('dart:math');
184
185 #import('utils.dart');
186
187 /**
188 * A class for taking a list of raw command line arguments and parsing out
189 * options and flags from them.
190 */
191 class ArgParser {
192 static const _SOLO_OPT = const RegExp(@'^-([a-zA-Z0-9])$');
193 static const _ABBR_OPT = const RegExp(@'^-([a-zA-Z0-9]+)(.*)$');
194 static const _LONG_OPT = const RegExp(@'^--([a-zA-Z\-_0-9]+)(=(.*))?$');
195
196 final Map<String, _Option> _options;
197
198 /**
199 * The names of the options, in the order that they were added. This way we
200 * can generate usage information in the same order.
201 */
202 // TODO(rnystrom): Use an ordered map type, if one appears.
203 final List<String> _optionNames;
204
205 /** The current argument list being parsed. Set by [parse()]. */
206 List<String> _args;
207
208 /** Index of the current argument being parsed in [_args]. */
209 int _current;
210
211 /** Creates a new ArgParser. */
212 ArgParser()
213 : _options = <String, _Option>{},
214 _optionNames = <String>[];
215
216 /**
217 * Defines a flag. Throws an [IllegalArgumentException] if:
218 *
219 * * There is already an option named [name].
220 * * There is already an option using abbreviation [abbr].
221 */
222 void addFlag(String name, [String abbr, String help, bool defaultsTo = false,
223 bool negatable = true, void callback(bool value)]) {
224 _addOption(name, abbr, help, null, null, defaultsTo, callback,
225 isFlag: true, negatable: negatable);
226 }
227
228 /**
229 * Defines a value-taking option. Throws an [IllegalArgumentException] if:
230 *
231 * * There is already an option with name [name].
232 * * There is already an option using abbreviation [abbr].
233 */
234 void addOption(String name, [String abbr, String help, List<String> allowed,
235 Map<String, String> allowedHelp, String defaultsTo,
236 void callback(bool value), bool allowMultiple = false]) {
237 _addOption(name, abbr, help, allowed, allowedHelp, defaultsTo,
238 callback, isFlag: false, allowMultiple: allowMultiple);
239 }
240
241 void _addOption(String name, String abbr, String help, List<String> allowed,
242 Map<String, String> allowedHelp, defaultsTo,
243 void callback(bool value), [bool isFlag, bool negatable = false,
244 bool allowMultiple = false]) {
245 // Make sure the name isn't in use.
246 if (_options.containsKey(name)) {
247 throw new IllegalArgumentException('Duplicate option "$name".');
248 }
249
250 // Make sure the abbreviation isn't too long or in use.
251 if (abbr != null) {
252 if (abbr.length > 1) {
253 throw new IllegalArgumentException(
254 'Abbreviation "$abbr" is longer than one character.');
255 }
256
257 var existing = _findByAbbr(abbr);
258 if (existing != null) {
259 throw new IllegalArgumentException(
260 'Abbreviation "$abbr" is already used by "${existing.name}".');
261 }
262 }
263
264 _options[name] = new _Option(name, abbr, help, allowed, allowedHelp,
265 defaultsTo, callback, isFlag: isFlag, negatable: negatable,
266 allowMultiple: allowMultiple);
267 _optionNames.add(name);
268 }
269
270 /**
271 * Parses [args], a list of command-line arguments, matches them against the
272 * flags and options defined by this parser, and returns the result.
273 */
274 ArgResults parse(List<String> args) {
275 _args = args;
276 _current = 0;
277 var results = {};
278
279 // Initialize flags to their defaults.
280 _options.forEach((name, option) {
281 if (option.allowMultiple) {
282 results[name] = [];
283 } else {
284 results[name] = option.defaultValue;
285 }
286 });
287
288 // Parse the args.
289 for (_current = 0; _current < args.length; _current++) {
290 var arg = args[_current];
291
292 if (arg == '--') {
293 // Reached the argument terminator, so stop here.
294 _current++;
295 break;
296 }
297
298 // Try to parse the current argument as an option. Note that the order
299 // here matters.
300 if (_parseSoloOption(results)) continue;
301 if (_parseAbbreviation(results)) continue;
302 if (_parseLongOption(results)) continue;
303
304 // If we got here, the argument doesn't look like an option, so stop.
305 break;
306 }
307
308 // Set unspecified multivalued arguments to their default value,
309 // if any, and invoke the callbacks.
310 for (var name in _optionNames) {
311 var option = _options[name];
312 if (option.allowMultiple &&
313 results[name].length == 0 &&
314 option.defaultValue != null) {
315 results[name].add(option.defaultValue);
316 }
317 if (option.callback != null) option.callback(results[name]);
318 }
319
320 // Add in the leftover arguments we didn't parse.
321 return new ArgResults(results,
322 _args.getRange(_current, _args.length - _current));
323 }
324
325 /**
326 * Generates a string displaying usage information for the defined options.
327 * This is basically the help text shown on the command line.
328 */
329 String getUsage() {
330 return new _Usage(this).generate();
331 }
332
333 /**
334 * Called during parsing to validate the arguments. Throws a
335 * [FormatException] if [condition] is `false`.
336 */
337 _validate(bool condition, String message) {
338 if (!condition) throw new FormatException(message);
339 }
340
341 /** Validates and stores [value] as the value for [option]. */
342 _setOption(Map results, _Option option, value) {
343 // See if it's one of the allowed values.
344 if (option.allowed != null) {
345 _validate(option.allowed.some((allow) => allow == value),
346 '"$value" is not an allowed value for option "${option.name}".');
347 }
348
349 if (option.allowMultiple) {
350 results[option.name].add(value);
351 } else {
352 results[option.name] = value;
353 }
354 }
355
356 /**
357 * Pulls the value for [option] from the next argument in [_args] (where the
358 * current option is at index [_current]. Validates that there is a valid
359 * value there.
360 */
361 void _readNextArgAsValue(Map results, _Option option) {
362 _current++;
363 // Take the option argument from the next command line arg.
364 _validate(_current < _args.length,
365 'Missing argument for "${option.name}".');
366
367 // Make sure it isn't an option itself.
368 _validate(!_ABBR_OPT.hasMatch(_args[_current]) &&
369 !_LONG_OPT.hasMatch(_args[_current]),
370 'Missing argument for "${option.name}".');
371
372 _setOption(results, option, _args[_current]);
373 }
374
375 /**
376 * Tries to parse the current argument as a "solo" option, which is a single
377 * hyphen followed by a single letter. We treat this differently than
378 * collapsed abbreviations (like "-abc") to handle the possible value that
379 * may follow it.
380 */
381 bool _parseSoloOption(Map results) {
382 var soloOpt = _SOLO_OPT.firstMatch(_args[_current]);
383 if (soloOpt == null) return false;
384
385 var option = _findByAbbr(soloOpt[1]);
386 _validate(option != null,
387 'Could not find an option or flag "-${soloOpt[1]}".');
388
389 if (option.isFlag) {
390 _setOption(results, option, true);
391 } else {
392 _readNextArgAsValue(results, option);
393 }
394
395 return true;
396 }
397
398 /**
399 * Tries to parse the current argument as a series of collapsed abbreviations
400 * (like "-abc") or a single abbreviation with the value directly attached
401 * to it (like "-mrelease").
402 */
403 bool _parseAbbreviation(Map results) {
404 var abbrOpt = _ABBR_OPT.firstMatch(_args[_current]);
405 if (abbrOpt == null) return false;
406
407 // If the first character is the abbreviation for a non-flag option, then
408 // the rest is the value.
409 var c = abbrOpt[1].substring(0, 1);
410 var first = _findByAbbr(c);
411 if (first == null) {
412 _validate(false, 'Could not find an option with short name "-$c".');
413 } else if (!first.isFlag) {
414 // The first character is a non-flag option, so the rest must be the
415 // value.
416 var value = '${abbrOpt[1].substring(1)}${abbrOpt[2]}';
417 _setOption(results, first, value);
418 } else {
419 // If we got some non-flag characters, then it must be a value, but
420 // if we got here, it's a flag, which is wrong.
421 _validate(abbrOpt[2] == '',
422 'Option "-$c" is a flag and cannot handle value '
423 '"${abbrOpt[1].substring(1)}${abbrOpt[2]}".');
424
425 // Not an option, so all characters should be flags.
426 for (var i = 0; i < abbrOpt[1].length; i++) {
427 var c = abbrOpt[1].substring(i, i + 1);
428 var option = _findByAbbr(c);
429 _validate(option != null,
430 'Could not find an option with short name "-$c".');
431
432 // In a list of short options, only the first can be a non-flag. If
433 // we get here we've checked that already.
434 _validate(option.isFlag,
435 'Option "-$c" must be a flag to be in a collapsed "-".');
436
437 _setOption(results, option, true);
438 }
439 }
440
441 return true;
442 }
443
444 /**
445 * Tries to parse the current argument as a long-form named option, which
446 * may include a value like "--mode=release" or "--mode release".
447 */
448 bool _parseLongOption(Map results) {
449 var longOpt = _LONG_OPT.firstMatch(_args[_current]);
450 if (longOpt == null) return false;
451
452 var name = longOpt[1];
453 var option = _options[name];
454 if (option != null) {
455 if (option.isFlag) {
456 _validate(longOpt[3] == null,
457 'Flag option "$name" should not be given a value.');
458
459 _setOption(results, option, true);
460 } else if (longOpt[3] != null) {
461 // We have a value like --foo=bar.
462 _setOption(results, option, longOpt[3]);
463 } else {
464 // Option like --foo, so look for the value as the next arg.
465 _readNextArgAsValue(results, option);
466 }
467 } else if (name.startsWith('no-')) {
468 // See if it's a negated flag.
469 name = name.substring('no-'.length);
470 option = _options[name];
471 _validate(option != null, 'Could not find an option named "$name".');
472 _validate(option.isFlag, 'Cannot negate non-flag option "$name".');
473 _validate(option.negatable, 'Cannot negate option "$name".');
474
475 _setOption(results, option, false);
476 } else {
477 _validate(option != null, 'Could not find an option named "$name".');
478 }
479
480 return true;
481 }
482
483 /**
484 * Finds the option whose abbreviation is [abbr], or `null` if no option has
485 * that abbreviation.
486 */
487 _Option _findByAbbr(String abbr) {
488 for (var option in _options.getValues()) {
489 if (option.abbreviation == abbr) return option;
490 }
491
492 return null;
493 }
494
495 /**
496 * Get the default value for an option. Useful after parsing to test
497 * if the user specified something other than the default.
498 */
499 getDefault(String option) {
500 if (!_options.containsKey(option)) {
501 throw new IllegalArgumentException('No option named $option');
502 }
503 return _options[option].defaultValue;
504 }
505 }
506
507 /**
508 * The results of parsing a series of command line arguments using
509 * [ArgParser.parse()]. Includes the parsed options and any remaining unparsed
510 * command line arguments.
511 */
512 class ArgResults {
513 final Map _options;
514
515 /**
516 * The remaining command-line arguments that were not parsed as options or
517 * flags. If `--` was used to separate the options from the remaining
518 * arguments, it will not be included in this list.
519 */
520 final List<String> rest;
521
522 /** Creates a new [ArgResults]. */
523 ArgResults(this._options, this.rest);
524
525 /** Gets the parsed command-line option named [name]. */
526 operator [](String name) {
527 if (!_options.containsKey(name)) {
528 throw new IllegalArgumentException(
529 'Could not find an option named "$name".');
530 }
531
532 return _options[name];
533 }
534
535 /** Get the names of the options as a [Collection]. */
536 Collection<String> get options => _options.getKeys();
537 }
538
539 class _Option {
540 final String name;
541 final String abbreviation;
542 final List allowed;
543 final defaultValue;
544 final Function callback;
545 final String help;
546 final Map<String, String> allowedHelp;
547 final bool isFlag;
548 final bool negatable;
549 final bool allowMultiple;
550
551 _Option(this.name, this.abbreviation, this.help, this.allowed,
552 this.allowedHelp, this.defaultValue, this.callback, [this.isFlag,
553 this.negatable, this.allowMultiple = false]);
554 }
555
556 /**
557 * Takes an [ArgParser] and generates a string of usage (i.e. help) text for its
558 * defined options. Internally, it works like a tabular printer. The output is
559 * divided into three horizontal columns, like so:
560 *
561 * -h, --help Prints the usage information
562 * | | | |
563 *
564 * It builds the usage text up one column at a time and handles padding with
565 * spaces and wrapping to the next line to keep the cells correctly lined up.
566 */
567 class _Usage {
568 static const NUM_COLUMNS = 3; // Abbreviation, long name, help.
569
570 /** The parser this is generating usage for. */
571 final ArgParser args;
572
573 /** The working buffer for the generated usage text. */
574 StringBuffer buffer;
575
576 /**
577 * The column that the "cursor" is currently on. If the next call to
578 * [write()] is not for this column, it will correctly handle advancing to
579 * the next column (and possibly the next row).
580 */
581 int currentColumn = 0;
582
583 /** The width in characters of each column. */
584 List<int> columnWidths;
585
586 /**
587 * The number of sequential lines of text that have been written to the last
588 * column (which shows help info). We track this so that help text that spans
589 * multiple lines can be padded with a blank line after it for separation.
590 * Meanwhile, sequential options with single-line help will be compacted next
591 * to each other.
592 */
593 int numHelpLines = 0;
594
595 /**
596 * How many newlines need to be rendered before the next bit of text can be
597 * written. We do this lazily so that the last bit of usage doesn't have
598 * dangling newlines. We only write newlines right *before* we write some
599 * real content.
600 */
601 int newlinesNeeded = 0;
602
603 _Usage(this.args);
604
605 /**
606 * Generates a string displaying usage information for the defined options.
607 * This is basically the help text shown on the command line.
608 */
609 String generate() {
610 buffer = new StringBuffer();
611
612 calculateColumnWidths();
613
614 for (var name in args._optionNames) {
615 var option = args._options[name];
616 write(0, getAbbreviation(option));
617 write(1, getLongOption(option));
618
619 if (option.help != null) write(2, option.help);
620
621 if (option.allowedHelp != null) {
622 var allowedNames = option.allowedHelp.getKeys();
623 allowedNames.sort((a, b) => a.compareTo(b));
624 newline();
625 for (var name in allowedNames) {
626 write(1, getAllowedTitle(name));
627 write(2, option.allowedHelp[name]);
628 }
629 newline();
630 } else if (option.allowed != null) {
631 write(2, buildAllowedList(option));
632 } else if (option.defaultValue != null) {
633 if (option.isFlag && option.defaultValue == true) {
634 write(2, '(defaults to on)');
635 } else if (!option.isFlag) {
636 write(2, '(defaults to "${option.defaultValue}")');
637 }
638 }
639
640 // If any given option displays more than one line of text on the right
641 // column (i.e. help, default value, allowed options, etc.) then put a
642 // blank line after it. This gives space where it's useful while still
643 // keeping simple one-line options clumped together.
644 if (numHelpLines > 1) newline();
645 }
646
647 return buffer.toString();
648 }
649
650 String getAbbreviation(_Option option) {
651 if (option.abbreviation != null) {
652 return '-${option.abbreviation}, ';
653 } else {
654 return '';
655 }
656 }
657
658 String getLongOption(_Option option) {
659 if (option.negatable) {
660 return '--[no-]${option.name}';
661 } else {
662 return '--${option.name}';
663 }
664 }
665
666 String getAllowedTitle(String allowed) {
667 return ' [$allowed]';
668 }
669
670 void calculateColumnWidths() {
671 int abbr = 0;
672 int title = 0;
673 for (var name in args._optionNames) {
674 var option = args._options[name];
675
676 // Make room in the first column if there are abbreviations.
677 abbr = max(abbr, getAbbreviation(option).length);
678
679 // Make room for the option.
680 title = max(title, getLongOption(option).length);
681
682 // Make room for the allowed help.
683 if (option.allowedHelp != null) {
684 for (var allowed in option.allowedHelp.getKeys()) {
685 title = max(title, getAllowedTitle(allowed).length);
686 }
687 }
688 }
689
690 // Leave a gutter between the columns.
691 title += 4;
692 columnWidths = [abbr, title];
693 }
694
695 newline() {
696 newlinesNeeded++;
697 currentColumn = 0;
698 numHelpLines = 0;
699 }
700
701 write(int column, String text) {
702 var lines = text.split('\n');
703
704 // Strip leading and trailing empty lines.
705 while (lines.length > 0 && lines[0].trim() == '') {
706 lines.removeRange(0, 1);
707 }
708
709 while (lines.length > 0 && lines[lines.length - 1].trim() == '') {
710 lines.removeLast();
711 }
712
713 for (var line in lines) {
714 writeLine(column, line);
715 }
716 }
717
718 writeLine(int column, String text) {
719 // Write any pending newlines.
720 while (newlinesNeeded > 0) {
721 buffer.add('\n');
722 newlinesNeeded--;
723 }
724
725 // Advance until we are at the right column (which may mean wrapping around
726 // to the next line.
727 while (currentColumn != column) {
728 if (currentColumn < NUM_COLUMNS - 1) {
729 buffer.add(padRight('', columnWidths[currentColumn]));
730 } else {
731 buffer.add('\n');
732 }
733 currentColumn = (currentColumn + 1) % NUM_COLUMNS;
734 }
735
736 if (column < columnWidths.length) {
737 // Fixed-size column, so pad it.
738 buffer.add(padRight(text, columnWidths[column]));
739 } else {
740 // The last column, so just write it.
741 buffer.add(text);
742 }
743
744 // Advance to the next column.
745 currentColumn = (currentColumn + 1) % NUM_COLUMNS;
746
747 // If we reached the last column, we need to wrap to the next line.
748 if (column == NUM_COLUMNS - 1) newlinesNeeded++;
749
750 // Keep track of how many consecutive lines we've written in the last
751 // column.
752 if (column == NUM_COLUMNS - 1) {
753 numHelpLines++;
754 } else {
755 numHelpLines = 0;
756 }
757 }
758
759 buildAllowedList(_Option option) {
760 var allowedBuffer = new StringBuffer();
761 allowedBuffer.add('[');
762 bool first = true;
763 for (var allowed in option.allowed) {
764 if (!first) allowedBuffer.add(', ');
765 allowedBuffer.add(allowed);
766 if (allowed == option.defaultValue) {
767 allowedBuffer.add(' (default)');
768 }
769 first = false;
770 }
771 allowedBuffer.add(']');
772 return allowedBuffer.toString();
773 }
774 }
OLDNEW
« no previous file with comments | « no previous file | pkg/args/example.dart » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698