| OLD | NEW | 
|---|
| 1 // Copyright (c) 2014, the Dart project authors.  Please see the AUTHORS file | 1 // Copyright (c) 2014, 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 library args.command_runner; | 5 library args.command_runner; | 
| 6 | 6 | 
| 7 import 'dart:async'; | 7 import 'dart:async'; | 
| 8 import 'dart:collection'; | 8 import 'dart:collection'; | 
| 9 import 'dart:math' as math; | 9 import 'dart:math' as math; | 
| 10 | 10 | 
| (...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 54 | 54 | 
| 55 ${_getCommandUsage(_commands)} | 55 ${_getCommandUsage(_commands)} | 
| 56 | 56 | 
| 57 Run "$executableName help <command>" for more information about a command.'''; | 57 Run "$executableName help <command>" for more information about a command.'''; | 
| 58 | 58 | 
| 59     if (usageFooter != null) usage += "\n$usageFooter"; | 59     if (usageFooter != null) usage += "\n$usageFooter"; | 
| 60     return usage; | 60     return usage; | 
| 61   } | 61   } | 
| 62 | 62 | 
| 63   /// An unmodifiable view of all top-level commands defined for this runner. | 63   /// An unmodifiable view of all top-level commands defined for this runner. | 
| 64   Map<String, Command> get commands => | 64   Map<String, Command> get commands => new UnmodifiableMapView(_commands); | 
| 65       new UnmodifiableMapView(_commands); |  | 
| 66   final _commands = new Map<String, Command>(); | 65   final _commands = new Map<String, Command>(); | 
| 67 | 66 | 
| 68   /// The top-level argument parser. | 67   /// The top-level argument parser. | 
| 69   /// | 68   /// | 
| 70   /// Global options should be registered with this parser; they'll end up | 69   /// Global options should be registered with this parser; they'll end up | 
| 71   /// available via [Command.globalResults]. Commands should be registered with | 70   /// available via [Command.globalResults]. Commands should be registered with | 
| 72   /// [addCommand] rather than directly on the parser. | 71   /// [addCommand] rather than directly on the parser. | 
| 73   final argParser = new ArgParser(); | 72   final argParser = new ArgParser(); | 
| 74 | 73 | 
| 75   CommandRunner(this.executableName, this.description) { | 74   CommandRunner(this.executableName, this.description) { | 
| 76     argParser.addFlag('help', abbr: 'h', negatable: false, | 75     argParser.addFlag('help', | 
| 77         help: 'Print this usage information.'); | 76         abbr: 'h', negatable: false, help: 'Print this usage information.'); | 
| 78     addCommand(new HelpCommand()); | 77     addCommand(new HelpCommand()); | 
| 79   } | 78   } | 
| 80 | 79 | 
| 81   /// Prints the usage information for this runner. | 80   /// Prints the usage information for this runner. | 
| 82   /// | 81   /// | 
| 83   /// This is called internally by [run] and can be overridden by subclasses to | 82   /// This is called internally by [run] and can be overridden by subclasses to | 
| 84   /// control how output is displayed or integrate with a logging system. | 83   /// control how output is displayed or integrate with a logging system. | 
| 85   void printUsage() => print(usage); | 84   void printUsage() => print(usage); | 
| 86 | 85 | 
| 87   /// Throws a [UsageException] with [message]. | 86   /// Throws a [UsageException] with [message]. | 
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 199   /// A single-line template for how to invoke this command (e.g. `"pub get | 198   /// A single-line template for how to invoke this command (e.g. `"pub get | 
| 200   /// [package]"`). | 199   /// [package]"`). | 
| 201   String get invocation { | 200   String get invocation { | 
| 202     var parents = [name]; | 201     var parents = [name]; | 
| 203     for (var command = parent; command != null; command = command.parent) { | 202     for (var command = parent; command != null; command = command.parent) { | 
| 204       parents.add(command.name); | 203       parents.add(command.name); | 
| 205     } | 204     } | 
| 206     parents.add(runner.executableName); | 205     parents.add(runner.executableName); | 
| 207 | 206 | 
| 208     var invocation = parents.reversed.join(" "); | 207     var invocation = parents.reversed.join(" "); | 
| 209     return _subcommands.isNotEmpty ? | 208     return _subcommands.isNotEmpty | 
| 210         "$invocation <subcommand> [arguments]" : | 209         ? "$invocation <subcommand> [arguments]" | 
| 211         "$invocation [arguments]"; | 210         : "$invocation [arguments]"; | 
| 212   } | 211   } | 
| 213 | 212 | 
| 214   /// The command's parent command, if this is a subcommand. | 213   /// The command's parent command, if this is a subcommand. | 
| 215   /// | 214   /// | 
| 216   /// This will be `null` until [Command.addSubcommmand] has been called with | 215   /// This will be `null` until [Command.addSubcommmand] has been called with | 
| 217   /// this command. | 216   /// this command. | 
| 218   Command get parent => _parent; | 217   Command get parent => _parent; | 
| 219   Command _parent; | 218   Command _parent; | 
| 220 | 219 | 
| 221   /// The command runner for this command. | 220   /// The command runner for this command. | 
| (...skipping 34 matching lines...) Expand 10 before | Expand all | Expand 10 after  Loading... | 
| 256 | 255 | 
| 257   /// An optional footer for [usage]. | 256   /// An optional footer for [usage]. | 
| 258   /// | 257   /// | 
| 259   /// If a subclass overrides this to return a string, it will automatically be | 258   /// If a subclass overrides this to return a string, it will automatically be | 
| 260   /// added to the end of [usage]. | 259   /// added to the end of [usage]. | 
| 261   final String usageFooter = null; | 260   final String usageFooter = null; | 
| 262 | 261 | 
| 263   /// Returns [usage] with [description] removed from the beginning. | 262   /// Returns [usage] with [description] removed from the beginning. | 
| 264   String get _usageWithoutDescription { | 263   String get _usageWithoutDescription { | 
| 265     var buffer = new StringBuffer() | 264     var buffer = new StringBuffer() | 
| 266         ..writeln('Usage: $invocation') | 265       ..writeln('Usage: $invocation') | 
| 267         ..writeln(argParser.usage); | 266       ..writeln(argParser.usage); | 
| 268 | 267 | 
| 269     if (_subcommands.isNotEmpty) { | 268     if (_subcommands.isNotEmpty) { | 
| 270       buffer.writeln(); | 269       buffer.writeln(); | 
| 271       buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true)); | 270       buffer.writeln(_getCommandUsage(_subcommands, isSubcommand: true)); | 
| 272     } | 271     } | 
| 273 | 272 | 
| 274     buffer.writeln(); | 273     buffer.writeln(); | 
| 275     buffer.write('Run "${runner.executableName} help" to see global options.'); | 274     buffer.write('Run "${runner.executableName} help" to see global options.'); | 
| 276 | 275 | 
| 277     if (usageFooter != null) { | 276     if (usageFooter != null) { | 
| 278       buffer.writeln(); | 277       buffer.writeln(); | 
| 279       buffer.write(usageFooter); | 278       buffer.write(usageFooter); | 
| 280     } | 279     } | 
| 281 | 280 | 
| 282     return buffer.toString(); | 281     return buffer.toString(); | 
| 283   } | 282   } | 
| 284 | 283 | 
| 285   /// An unmodifiable view of all sublevel commands of this command. | 284   /// An unmodifiable view of all sublevel commands of this command. | 
| 286   Map<String, Command> get subcommands => | 285   Map<String, Command> get subcommands => new UnmodifiableMapView(_subcommands); | 
| 287       new UnmodifiableMapView(_subcommands); |  | 
| 288   final _subcommands = new Map<String, Command>(); | 286   final _subcommands = new Map<String, Command>(); | 
| 289 | 287 | 
| 290   /// Whether or not this command should be hidden from help listings. | 288   /// Whether or not this command should be hidden from help listings. | 
| 291   /// | 289   /// | 
| 292   /// This is intended to be overridden by commands that want to mark themselves | 290   /// This is intended to be overridden by commands that want to mark themselves | 
| 293   /// hidden. | 291   /// hidden. | 
| 294   /// | 292   /// | 
| 295   /// By default, leaf commands are always visible. Branch commands are visible | 293   /// By default, leaf commands are always visible. Branch commands are visible | 
| 296   /// as long as any of their leaf commands are visible. | 294   /// as long as any of their leaf commands are visible. | 
| 297   bool get hidden { | 295   bool get hidden { | 
| (...skipping 16 matching lines...) Expand all  Loading... | 
| 314 | 312 | 
| 315   /// Alternate names for this command. | 313   /// Alternate names for this command. | 
| 316   /// | 314   /// | 
| 317   /// These names won't be used in the documentation, but they will work when | 315   /// These names won't be used in the documentation, but they will work when | 
| 318   /// invoked on the command line. | 316   /// invoked on the command line. | 
| 319   /// | 317   /// | 
| 320   /// This is intended to be overridden. | 318   /// This is intended to be overridden. | 
| 321   final aliases = const <String>[]; | 319   final aliases = const <String>[]; | 
| 322 | 320 | 
| 323   Command() { | 321   Command() { | 
| 324     argParser.addFlag('help', abbr: 'h', negatable: false, | 322     argParser.addFlag('help', | 
| 325         help: 'Print this usage information.'); | 323         abbr: 'h', negatable: false, help: 'Print this usage information.'); | 
| 326   } | 324   } | 
| 327 | 325 | 
| 328   /// Runs this command. | 326   /// Runs this command. | 
| 329   /// | 327   /// | 
| 330   /// If this returns a [Future], [CommandRunner.run] won't complete until the | 328   /// If this returns a [Future], [CommandRunner.run] won't complete until the | 
| 331   /// returned [Future] does. Otherwise, the return value is ignored. | 329   /// returned [Future] does. Otherwise, the return value is ignored. | 
| 332   run() { | 330   run() { | 
| 333     throw new UnimplementedError("Leaf command $this must implement run()."); | 331     throw new UnimplementedError("Leaf command $this must implement run()."); | 
| 334   } | 332   } | 
| 335 | 333 | 
| (...skipping 18 matching lines...) Expand all  Loading... | 
| 354       throw new UsageException(message, _usageWithoutDescription); | 352       throw new UsageException(message, _usageWithoutDescription); | 
| 355 } | 353 } | 
| 356 | 354 | 
| 357 /// Returns a string representation of [commands] fit for use in a usage string. | 355 /// Returns a string representation of [commands] fit for use in a usage string. | 
| 358 /// | 356 /// | 
| 359 /// [isSubcommand] indicates whether the commands should be called "commands" or | 357 /// [isSubcommand] indicates whether the commands should be called "commands" or | 
| 360 /// "subcommands". | 358 /// "subcommands". | 
| 361 String _getCommandUsage(Map<String, Command> commands, | 359 String _getCommandUsage(Map<String, Command> commands, | 
| 362     {bool isSubcommand: false}) { | 360     {bool isSubcommand: false}) { | 
| 363   // Don't include aliases. | 361   // Don't include aliases. | 
| 364   var names = commands.keys | 362   var names = | 
| 365       .where((name) => !commands[name].aliases.contains(name)); | 363       commands.keys.where((name) => !commands[name].aliases.contains(name)); | 
| 366 | 364 | 
| 367   // Filter out hidden ones, unless they are all hidden. | 365   // Filter out hidden ones, unless they are all hidden. | 
| 368   var visible = names.where((name) => !commands[name].hidden); | 366   var visible = names.where((name) => !commands[name].hidden); | 
| 369   if (visible.isNotEmpty) names = visible; | 367   if (visible.isNotEmpty) names = visible; | 
| 370 | 368 | 
| 371   // Show the commands alphabetically. | 369   // Show the commands alphabetically. | 
| 372   names = names.toList()..sort(); | 370   names = names.toList()..sort(); | 
| 373   var length = names.map((name) => name.length).reduce(math.max); | 371   var length = names.map((name) => name.length).reduce(math.max); | 
| 374 | 372 | 
| 375   var buffer = new StringBuffer( | 373   var buffer = | 
| 376       'Available ${isSubcommand ? "sub" : ""}commands:'); | 374       new StringBuffer('Available ${isSubcommand ? "sub" : ""}commands:'); | 
| 377   for (var name in names) { | 375   for (var name in names) { | 
| 378     buffer.writeln(); | 376     buffer.writeln(); | 
| 379     buffer.write('  ${padRight(name, length)}   ' | 377     buffer.write('  ${padRight(name, length)}   ' | 
| 380         '${commands[name].description.split("\n").first}'); | 378         '${commands[name].description.split("\n").first}'); | 
| 381   } | 379   } | 
| 382 | 380 | 
| 383   return buffer.toString(); | 381   return buffer.toString(); | 
| 384 } | 382 } | 
| OLD | NEW | 
|---|