Imported Upstream version 5.16.0.100

Former-commit-id: 38faa55fb9669e35e7d8448b15c25dc447f25767
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2018-08-07 15:19:03 +00:00
parent 0a9828183b
commit 7d7f676260
4419 changed files with 170950 additions and 90273 deletions

View File

@@ -1,4 +1,3 @@
<?xml version="1.0" encoding="UTF-8"?>
<Type Name="CommandSet" FullName="Mono.Options.CommandSet">
<TypeSignature Language="C#" Value="public class CommandSet : System.Collections.ObjectModel.KeyedCollection&lt;string,Mono.Options.Command&gt;" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit CommandSet extends System.Collections.ObjectModel.KeyedCollection`2&lt;string, class Mono.Options.Command&gt;" />
@@ -90,17 +89,28 @@ class CommandDemo {
new Command ("echo", "Echo arguments to the screen") {
Run = ca =&gt; Console.WriteLine ("{0}", string.Join (" ", ca)),
},
new RequiresArgs (),
new RequiresArgsCommand (),
"Commands with spaces are supported:",
new Command ("has spaces", "spaces?!") {
Run = ca =&gt; Console.WriteLine ("spaces, yo! {0}", string.Join (" ", ca)),
},
"Nested CommandSets are also supported. They're invoked similarly to commands with spaces.",
new CommandSet ("set") {
new Command ("file type", "Does something or other.") {
Run = ca =&gt; Console.WriteLine ("File type set to: {0}", string.Join (" ", ca)),
},
},
};
commands.Add (commands);
return commands.Run (args);
}
public static int Verbosity;
}
class RequiresArgs : Command {
class RequiresArgsCommand : Command {
public RequiresArgs ()
public RequiresArgsCommand ()
: base ("requires-args", "Class-based Command subclass")
{
Options = new OptionSet () {
@@ -149,6 +159,7 @@ class RequiresArgs : Command {
Use `commands help` for usage.
$ mono commands.exe --help
# HelpCommand.Invoke: arguments=
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
@@ -159,8 +170,14 @@ Global options:
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
Commands with spaces are supported:
has spaces spaces?!
Nested CommandSets are also supported. They're invoked similarly to commands
with spaces.
set file type Does something or other.
$ mono commands.exe help
# HelpCommand.Invoke: arguments=
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
@@ -171,18 +188,27 @@ Global options:
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
Commands with spaces are supported:
has spaces spaces?!
Nested CommandSets are also supported. They're invoked similarly to commands
with spaces.
set file type Does something or other.
$ mono commands.exe help --help
# HelpCommand.Invoke: arguments=--help
Usage: commands COMMAND [OPTIONS]
Use `commands help COMMAND` for help on a specific command.
Available commands:
echo Echo arguments to the screen
has spaces spaces?!
requires-args Class-based Command subclass
set file type Does something or other.
help Show this message and exit
$ mono commands.exe help echo
# HelpCommand.Invoke: arguments=echo
--help
$ mono commands.exe echo --help
@@ -196,6 +222,7 @@ commands: Missing required argument `--name=NAME`.
commands: Use `commands help requires-args` for details.
$ mono commands.exe help requires-args
# HelpCommand.Invoke: arguments=requires-args
usage: commands requires-args [OPTIONS]
Class-based Command subclass example.
@@ -217,8 +244,15 @@ commands: Unknown command: invalid-command
commands: Use `commands help` for usage.
$ mono commands.exe help invalid-command
# HelpCommand.Invoke: arguments=invalid-command
commands: Unknown command: invalid-command
commands: Use `commands help` for usage.
$ mono commands.exe has spaces
spaces, yo!
$ mono commands.exe set file type whatever
File type set to: whatever
</code>
<para>
The <c>commands.exe</c> output is short, informing the user that
@@ -426,6 +460,40 @@ commands: Use `commands help` for usage.
</exception>
</Docs>
</Member>
<Member MemberName="Add">
<MemberSignature Language="C#" Value="public Mono.Options.CommandSet Add (Mono.Options.CommandSet nestedCommands);" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig instance class Mono.Options.CommandSet Add(class Mono.Options.CommandSet nestedCommands) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Mono.Options.CommandSet</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="nestedCommands" Type="Mono.Options.CommandSet" />
</Parameters>
<Docs>
<param name="nestedCommands">
The <see cref="T:Mono.Options.OptionSet" /> to register.
</param>
<summary>Adds <paramref name="nestedCommands" /> as a suite of sub-commands.</summary>
<returns>
The current <see cref="T:Mono.Options.CommandSet" /> instance.
This is to permit method chaining.
</returns>
<remarks>
<para>
When a <c>CommandSet</c> is a child of a <c>CommandSet</c>, the nested
<c>CommandSet</c> commands can be invoked by prefixing the command name
with the <c>CommandSet</c> suite name within the arguments array.
</para>
</remarks>
<exception cref="T:System.ArgumentNullException">
<paramref name="option" /> is <see langword="null" />.
</exception>
</Docs>
</Member>
<Member MemberName="Add">
<MemberSignature Language="C#" Value="public Mono.Options.CommandSet Add (Mono.Options.Option option);" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig instance class Mono.Options.CommandSet Add(class Mono.Options.Option option) cil managed" />
@@ -1392,4 +1460,4 @@ commands: Use `commands help` for usage.
</Docs>
</Member>
</Members>
</Type>
</Type>

View File

@@ -25,17 +25,28 @@ class CommandDemo {
new Command ("echo", "Echo arguments to the screen") {
Run = ca => Console.WriteLine ("{0}", string.Join (" ", ca)),
},
new RequiresArgs (),
new RequiresArgsCommand (),
"Commands with spaces are supported:",
new Command ("has spaces", "spaces?!") {
Run = ca => Console.WriteLine ("spaces, yo! {0}", string.Join (" ", ca)),
},
"Nested CommandSets are also supported. They're invoked similarly to commands with spaces.",
new CommandSet ("set") {
new Command ("file type", "Does something or other.") {
Run = ca => Console.WriteLine ("File type set to: {0}", string.Join (" ", ca)),
},
},
};
commands.Add (commands);
return commands.Run (args);
}
public static int Verbosity;
}
class RequiresArgs : Command {
class RequiresArgsCommand : Command {
public RequiresArgs ()
public RequiresArgsCommand ()
: base ("requires-args", "Class-based Command subclass")
{
Options = new OptionSet () {

View File

@@ -23,3 +23,7 @@ mono Documentation/en/examples/commands.exe requires-args -n World
mono Documentation/en/examples/commands.exe invalid-command
mono Documentation/en/examples/commands.exe help invalid-command
mono Documentation/en/examples/commands.exe has spaces
mono Documentation/en/examples/commands.exe set file type whatever

View File

@@ -2,6 +2,7 @@ $ mono commands.exe
Use `commands help` for usage.
$ mono commands.exe --help
# HelpCommand.Invoke: arguments=
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
@@ -12,8 +13,14 @@ Global options:
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
Commands with spaces are supported:
has spaces spaces?!
Nested CommandSets are also supported. They're invoked similarly to commands
with spaces.
set file type Does something or other.
$ mono commands.exe help
# HelpCommand.Invoke: arguments=
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
@@ -24,18 +31,27 @@ Global options:
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
Commands with spaces are supported:
has spaces spaces?!
Nested CommandSets are also supported. They're invoked similarly to commands
with spaces.
set file type Does something or other.
$ mono commands.exe help --help
# HelpCommand.Invoke: arguments=--help
Usage: commands COMMAND [OPTIONS]
Use `commands help COMMAND` for help on a specific command.
Available commands:
echo Echo arguments to the screen
has spaces spaces?!
requires-args Class-based Command subclass
set file type Does something or other.
help Show this message and exit
$ mono commands.exe help echo
# HelpCommand.Invoke: arguments=echo
--help
$ mono commands.exe echo --help
@@ -49,6 +65,7 @@ commands: Missing required argument `--name=NAME`.
commands: Use `commands help requires-args` for details.
$ mono commands.exe help requires-args
# HelpCommand.Invoke: arguments=requires-args
usage: commands requires-args [OPTIONS]
Class-based Command subclass example.
@@ -70,5 +87,12 @@ commands: Unknown command: invalid-command
commands: Use `commands help` for usage.
$ mono commands.exe help invalid-command
# HelpCommand.Invoke: arguments=invalid-command
commands: Unknown command: invalid-command
commands: Use `commands help` for usage.
$ mono commands.exe has spaces
spaces, yo!
$ mono commands.exe set file type whatever
File type set to: whatever

View File

@@ -1267,7 +1267,7 @@ namespace Mono.Options
}
CommandOption co = p as CommandOption;
if (co != null) {
WriteCommandDescription (o, co.Command);
WriteCommandDescription (o, co.Command, co.CommandName);
continue;
}
@@ -1311,9 +1311,9 @@ namespace Mono.Options
}
}
internal void WriteCommandDescription (TextWriter o, Command c)
internal void WriteCommandDescription (TextWriter o, Command c, string commandName)
{
var name = new string (' ', 8) + c.Name;
var name = new string (' ', 8) + (commandName ?? c.Name);
if (name.Length < OptionWidth - 1) {
WriteDescription (o, name + new string (' ', OptionWidth - name.Length) + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth);
} else {
@@ -1476,10 +1476,27 @@ namespace Mono.Options
if (string.IsNullOrEmpty (name))
throw new ArgumentNullException (nameof (name));
Name = name;
Name = NormalizeCommandName (name);
Help = help;
}
static string NormalizeCommandName (string name)
{
var value = new StringBuilder (name.Length);
var space = false;
for (int i = 0; i < name.Length; ++i) {
if (!char.IsWhiteSpace (name, i)) {
space = false;
value.Append (name [i]);
}
else if (!space) {
space = true;
value.Append (' ');
}
}
return value.ToString ();
}
public virtual int Invoke (IEnumerable<string> arguments)
{
var rest = Options?.Parse (arguments) ?? arguments;
@@ -1491,16 +1508,18 @@ namespace Mono.Options
class CommandOption : Option
{
public Command Command {get;}
public string CommandName {get;}
// Prototype starts with '=' because this is an invalid prototype
// (see Option.ParsePrototype(), and thus it'll prevent Category
// instances from being accidentally used as normal options.
public CommandOption (Command command, bool hidden = false)
: base ("=:Command:= " + command?.Name, command?.Name, maxValueCount: 0, hidden: hidden)
public CommandOption (Command command, string commandName = null, bool hidden = false)
: base ("=:Command:= " + (commandName ?? command?.Name), (commandName ?? command?.Name), maxValueCount: 0, hidden: hidden)
{
if (command == null)
throw new ArgumentNullException (nameof (command));
Command = command;
CommandName = commandName ?? command.Name;
}
protected override void OnParseComplete (OptionContext c)
@@ -1574,12 +1593,15 @@ namespace Mono.Options
public class CommandSet : KeyedCollection<string, Command>
{
readonly OptionSet options;
readonly TextWriter outWriter;
readonly TextWriter errorWriter;
readonly string suite;
HelpCommand help;
OptionSet options;
TextWriter outWriter;
TextWriter errorWriter;
internal List<CommandSet> NestedCommandSets;
internal HelpCommand help;
internal bool showHelp;
@@ -1719,6 +1741,47 @@ namespace Mono.Options
return this;
}
public CommandSet Add (CommandSet nestedCommands)
{
if (nestedCommands == null)
throw new ArgumentNullException (nameof (nestedCommands));
if (NestedCommandSets == null) {
NestedCommandSets = new List<CommandSet> ();
}
if (!AlreadyAdded (nestedCommands)) {
NestedCommandSets.Add (nestedCommands);
foreach (var o in nestedCommands.options) {
if (o is CommandOption c) {
options.Add (new CommandOption (c.Command, $"{nestedCommands.Suite} {c.CommandName}"));
}
else {
options.Add (o);
}
}
}
nestedCommands.options = this.options;
nestedCommands.outWriter = this.outWriter;
nestedCommands.errorWriter = this.errorWriter;
return this;
}
bool AlreadyAdded (CommandSet value)
{
if (value == this)
return true;
if (NestedCommandSets == null)
return false;
foreach (var nc in NestedCommandSets) {
if (nc.AlreadyAdded (value))
return true;
}
return false;
}
public int Run (IEnumerable<string> arguments)
{
if (arguments == null)
@@ -1744,12 +1807,11 @@ namespace Mono.Options
Out.WriteLine (options.MessageLocalizer ($"Use `{Suite} help` for usage."));
return 1;
}
var command = Contains (extra [0]) ? this [extra [0]] : null;
var command = GetCommand (extra);
if (command == null) {
help.WriteUnknownCommand (extra [0]);
return 1;
}
extra.RemoveAt (0);
if (showHelp) {
if (command.Options?.Contains ("help") ?? true) {
extra.Add ("--help");
@@ -1760,6 +1822,51 @@ namespace Mono.Options
}
return command.Invoke (extra);
}
internal Command GetCommand (List<string> extra)
{
return TryGetLocalCommand (extra) ?? TryGetNestedCommand (extra);
}
Command TryGetLocalCommand (List<string> extra)
{
var name = extra [0];
if (Contains (name)) {
extra.RemoveAt (0);
return this [name];
}
for (int i = 1; i < extra.Count; ++i) {
name = name + " " + extra [i];
if (!Contains (name))
continue;
extra.RemoveRange (0, i+1);
return this [name];
}
return null;
}
Command TryGetNestedCommand (List<string> extra)
{
if (NestedCommandSets == null)
return null;
var nestedCommands = NestedCommandSets.Find (c => c.Suite == extra [0]);
if (nestedCommands == null)
return null;
var extraCopy = new List<string> (extra);
extraCopy.RemoveAt (0);
if (extraCopy.Count == 0)
return null;
var command = nestedCommands.GetCommand (extraCopy);
if (command != null) {
extra.Clear ();
extra.AddRange (extraCopy);
return command;
}
return null;
}
}
public class HelpCommand : Command
@@ -1772,23 +1879,28 @@ namespace Mono.Options
public override int Invoke (IEnumerable<string> arguments)
{
var extra = new List<string> (arguments ?? new string [0]);
Console.WriteLine ($"# HelpCommand.Invoke: arguments={string.Join (" ", arguments)}");
var _ = CommandSet.Options.MessageLocalizer;
if (extra.Count == 0) {
CommandSet.Options.WriteOptionDescriptions (CommandSet.Out);
return 0;
}
var command = CommandSet.Contains (extra [0])
? CommandSet [extra [0]]
: null;
if (command == this || extra [0] == "--help") {
var command = CommandSet.GetCommand (extra);
if (command == this || extra.Contains ("--help")) {
CommandSet.Out.WriteLine (_ ($"Usage: {CommandSet.Suite} COMMAND [OPTIONS]"));
CommandSet.Out.WriteLine (_ ($"Use `{CommandSet.Suite} help COMMAND` for help on a specific command."));
CommandSet.Out.WriteLine ();
CommandSet.Out.WriteLine (_ ($"Available commands:"));
CommandSet.Out.WriteLine ();
foreach (var c in CommandSet) {
CommandSet.Options.WriteCommandDescription (CommandSet.Out, c);
var commands = GetCommands ();
commands.Sort ((x, y) => string.Compare (x.Key, y.Key, StringComparison.OrdinalIgnoreCase));
foreach (var c in commands) {
if (c.Key == "help") {
continue;
}
CommandSet.Options.WriteCommandDescription (CommandSet.Out, c.Value, c.Key);
}
CommandSet.Options.WriteCommandDescription (CommandSet.Out, CommandSet.help, "help");
return 0;
}
if (command == null) {
@@ -1802,6 +1914,36 @@ namespace Mono.Options
return command.Invoke (new [] { "--help" });
}
List<KeyValuePair<string, Command>> GetCommands ()
{
var commands = new List<KeyValuePair<string, Command>> ();
foreach (var c in CommandSet) {
commands.Add (new KeyValuePair<string, Command>(c.Name, c));
}
if (CommandSet.NestedCommandSets == null)
return commands;
foreach (var nc in CommandSet.NestedCommandSets) {
AddNestedCommands (commands, "", nc);
}
return commands;
}
void AddNestedCommands (List<KeyValuePair<string, Command>> commands, string outer, CommandSet value)
{
foreach (var v in value) {
commands.Add (new KeyValuePair<string, Command>($"{outer}{value.Suite} {v.Name}", v));
}
if (value.NestedCommandSets == null)
return;
foreach (var nc in value.NestedCommandSets) {
AddNestedCommands (commands, $"{outer}{value.Suite} ", nc);
}
}
internal void WriteUnknownCommand (string unknownCommand)
{
CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Unknown command: {unknownCommand}"));

View File

@@ -98,6 +98,7 @@ namespace MonoTests.Mono.Options
{
var c = new CommandSet ("cs");
Assert.Throws<ArgumentNullException> (() => c.Add ((Command)null));
Assert.Throws<ArgumentNullException> (() => c.Add ((CommandSet)null));
}
[Test]
@@ -153,7 +154,22 @@ namespace MonoTests.Mono.Options
"start a working area (see also: git help tutorial)",
new Command ("clone", "Clone a repository into a new directory"),
new Command ("init", "Create an empty Git repository or reinitialize an existing one"),
new Command ("this\thas spaces", "Spaces in command names?!"),
new Command ("thisIsAVeryLongCommandNameInOrderToInduceWrapping", "Create an empty Git repository or reinitialize an existing one. Let's make this really long to cause a line wrap, shall we?"),
new CommandSet ("nested") {
"Surely nested commands need flavor text?",
new Command ("foo", "nested foo help"),
"And more text?",
new Command ("bar", "nested bar help"),
new CommandSet ("again") {
"Yet more text!",
new Command ("and again", "Wee! Nesting!"),
}
},
new CommandSet ("another") {
new Command ("foo", "another foo help"),
new Command ("bar", "another bar help"),
},
};
var expectedHelp = new StringWriter ();
@@ -170,10 +186,20 @@ namespace MonoTests.Mono.Options
expectedHelp.WriteLine (" clone Clone a repository into a new directory");
expectedHelp.WriteLine (" init Create an empty Git repository or reinitialize an");
expectedHelp.WriteLine (" existing one");
expectedHelp.WriteLine (" this has spaces Spaces in command names?!");
expectedHelp.WriteLine (" thisIsAVeryLongCommandNameInOrderToInduceWrapping");
expectedHelp.WriteLine (" Create an empty Git repository or reinitialize an");
expectedHelp.WriteLine (" existing one. Let's make this really long to");
expectedHelp.WriteLine (" cause a line wrap, shall we?");
expectedHelp.WriteLine ("Surely nested commands need flavor text?");
expectedHelp.WriteLine (" nested foo nested foo help");
expectedHelp.WriteLine ("And more text?");
expectedHelp.WriteLine (" nested bar nested bar help");
expectedHelp.WriteLine ("Yet more text!");
expectedHelp.WriteLine (" nested again and again");
expectedHelp.WriteLine (" Wee! Nesting!");
expectedHelp.WriteLine (" another foo another foo help");
expectedHelp.WriteLine (" another bar another bar help");
Assert.AreEqual (0, git.Run (new [] { "help" }));
Assert.AreEqual (expectedHelp.ToString (), o.ToString ());
@@ -184,9 +210,16 @@ namespace MonoTests.Mono.Options
expectedHelpHelp.WriteLine ();
expectedHelpHelp.WriteLine ("Available commands:");
expectedHelpHelp.WriteLine ();
expectedHelpHelp.WriteLine (" another bar another bar help");
expectedHelpHelp.WriteLine (" another foo another foo help");
expectedHelpHelp.WriteLine (" clone Clone a repository into a new directory");
expectedHelpHelp.WriteLine (" init Create an empty Git repository or reinitialize an");
expectedHelpHelp.WriteLine (" existing one");
expectedHelpHelp.WriteLine (" nested again and again");
expectedHelpHelp.WriteLine (" Wee! Nesting!");
expectedHelpHelp.WriteLine (" nested bar nested bar help");
expectedHelpHelp.WriteLine (" nested foo nested foo help");
expectedHelpHelp.WriteLine (" this has spaces Spaces in command names?!");
expectedHelpHelp.WriteLine (" thisIsAVeryLongCommandNameInOrderToInduceWrapping");
expectedHelpHelp.WriteLine (" Create an empty Git repository or reinitialize an");
expectedHelpHelp.WriteLine (" existing one. Let's make this really long to");
@@ -203,9 +236,13 @@ namespace MonoTests.Mono.Options
{
var a = 0;
var b = 0;
var d = 0;
var g = 0;
var c = new CommandSet ("set") {
new Command ("a") { Run = v => a = v.Count () },
new Command ("b") { Run = v => b = v.Count () },
new Command ("c d") { Run = v => d = v.Count () },
new Command ("e\t f\ng") { Run = v => g = v.Count () },
};
Assert.AreEqual (0, c.Run (new [] { "a", "extra" }));
Assert.AreEqual (1, a);
@@ -219,6 +256,54 @@ namespace MonoTests.Mono.Options
Assert.AreEqual (0, c.Run (new [] { "b", "one", "two" }));
Assert.AreEqual (0, a);
Assert.AreEqual (2, b);
Assert.AreEqual (1, c.Run (new [] { "c"}));
Assert.AreEqual (0, c.Run (new [] { "c d", "one"}));
Assert.AreEqual (1, d);
Assert.AreEqual (0, c.Run (new [] { "c", "d", "one", "two"}));
Assert.AreEqual (2, d);
Assert.AreEqual (1, c.Run (new [] { "e" }));
Assert.AreEqual (1, c.Run (new [] { "e f" }));
Assert.AreEqual (1, c.Run (new [] { "e", "f" }));
Assert.AreEqual (0, c.Run (new [] { "e f g"}));
Assert.AreEqual (0, g);
Assert.AreEqual (0, c.Run (new [] { "e f g", "one"}));
Assert.AreEqual (1, g);
Assert.AreEqual (0, c.Run (new [] { "e", "f", "g", "one", "two", "three"}));
Assert.AreEqual (3, g);
}
[Test]
public void Run_Command_NestedCommandSets ()
{
var a = 0;
var i_b = 0;
var i_i_c = 0;
var outer = new CommandSet ("outer") {
new Command ("a") { Run = v => a = v.Count () },
new CommandSet ("intermediate") {
new Command ("b") { Run = v => i_b = v.Count () },
new CommandSet ("inner") {
new Command ("c") { Run = v => i_i_c = v.Count () },
}
},
};
Assert.AreEqual (0, outer.Run (new[]{"a", "1"}));
Assert.AreEqual (1, a);
Assert.AreEqual (1, outer.Run (new[]{"intermediate"}));
Assert.AreEqual (0, outer.Run (new[]{"intermediate", "b", "1", "2"}));
Assert.AreEqual (2, i_b);
Assert.AreEqual (1, outer.Run (new[]{"intermediate inner"}));
Assert.AreEqual (0, outer.Run (new[]{"intermediate", "inner", "c", "1", "2", "3"}));
Assert.AreEqual (3, i_i_c);
}
[Test]