Imported Upstream version 5.2.0.175

Former-commit-id: bb0468d0f257ff100aa895eb5fe583fb5dfbf900
This commit is contained in:
Xamarin Public Jenkins (auto-signing)
2017-06-07 13:16:24 +00:00
parent 4bdbaf4a88
commit 966bba02bb
8776 changed files with 346420 additions and 149650 deletions

View File

@@ -0,0 +1,286 @@
<Type Name="Command" FullName="Mono.Options.Command">
<TypeSignature Language="C#" Value="public class Command" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit Command extends System.Object" />
<AssemblyInfo>
<AssemblyName>Mono.Options</AssemblyName>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ThreadingSafetyStatement>
Public <c>static</c> members of this type are thread safe.
Any instance members are not guaranteed to be thread safe.
</ThreadingSafetyStatement>
<Base>
<BaseTypeName>System.Object</BaseTypeName>
</Base>
<Interfaces />
<Docs>
<summary>
Represents a program command.
</summary>
<remarks>
<para>
Many command-line utilities are <i>suites</i> of commands, with a single
"outer" command and multiple commands. Examples of this style of
utility includes <b>git</b>, <b>svn</b>, and <b>mdoc</b>.
</para>
<para>
A <c>Command</c> represents a specific command in such a suite.
It has a <see cref="P:Mono.Options.Command.Name" /> which is the
command name for invocation purposes, optional help text through
the <see cref="P:Mono.Options.Command.Help" /> property, an optional
<see cref="T:Mono.Options.OptionSet" /> accessible through the
<see cref="P:Mono.Options.Command.Options" /> property for command-line
parsing, and two ways to have code executed when a command is
invoked: the <see cref="P:Mono.Options.Command.Run" /> property and
the <see cref="M:Mono.Options.Command.Invoke" /> method.
</para>
</remarks>
</Docs>
<Members>
<Member MemberName=".ctor">
<MemberSignature Language="C#" Value="public Command (string name, string help = null);" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor(string name, string help) cil managed" />
<MemberType>Constructor</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<Parameters>
<Parameter Name="name" Type="System.String" />
<Parameter Name="help" Type="System.String" />
</Parameters>
<Docs>
<param name="name">
A <see cref="T:System.String" /> which is the command name.
</param>
<param name="help">
A <see cref="T:System.String" /> which is the command help text.
</param>
<summary>
Creates and initializes a new instance of the <c>Command</c> class.
</summary>
<remarks>
<para>
This constructor initializes the
<see cref="P:Mono.Options.Command.Name" /> property of the new
instance using <paramref name="name" /> and initializes the
<see cref="P:Mono.Options.Command.Help" /> property of the new
instance using <paramref name="help" />.
</para>
</remarks>
<exception cref="T:System.ArgumentNullException">
<paramref name="name" /> is <see langword="null" />.
</exception>
</Docs>
</Member>
<Member MemberName="CommandSet">
<MemberSignature Language="C#" Value="public Mono.Options.CommandSet CommandSet { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance class Mono.Options.CommandSet CommandSet" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Mono.Options.CommandSet</ReturnType>
</ReturnValue>
<Docs>
<summary>
A <see cref="T:Mono.Options.CommandSet" /> instance which owns the
<c>Command</c>.
</summary>
<value>
A <see cref="T:Mono.Options.CommandSet" /> instance which owns the
<c>Command</c>.
</value>
<remarks>
<para>
A <c>Command</c> instance may belong to only one
<see cref="T:Mono.Options.CommandSet" /> instance.
The <c>CommandSet</c> property is set upon calling
<see cref="M:Mono.Options.CommandSet.Add(Mono.Options.Command)" />.
</para>
<para>
If the <c>Command</c> instance has not yet been added to a
<c>CommandSet</c>, then this property is <see langword="null" />.
</para>
<para>
Use the <c>CommandSet</c> instance from either the
<see cref="P:Mono.Options.Command.Run" /> property or an overridden
<see cref="M:Mono.Options.Command.Invoke" /> method to access
localization facilities through
<see cref="P:Mono.Options.CommandSet.MessageLocalizer" />, the
preferred message output stream through
<see cref="P:Mono.Options.CommandSet.Out" />, and other features.
</para>
</remarks>
</Docs>
</Member>
<Member MemberName="Help">
<MemberSignature Language="C#" Value="public string Help { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance string Help" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.String</ReturnType>
</ReturnValue>
<Docs>
<summary>
A short, one-line, description of the <c>Command</c>.
</summary>
<value>
A <see cref="T:System.String" /> containing the optional help text
of the <c>Command</c>.
</value>
<remarks>
<para>
The <c>Help</c> property text is shown when the <c>help</c>
command is invoked.
</para>
</remarks>
</Docs>
</Member>
<Member MemberName="Invoke">
<MemberSignature Language="C#" Value="public virtual int Invoke (System.Collections.Generic.IEnumerable&lt;string&gt; arguments);" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig newslot virtual instance int32 Invoke(class System.Collections.Generic.IEnumerable`1&lt;string&gt; arguments) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Int32</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="arguments" Type="System.Collections.Generic.IEnumerable&lt;System.String&gt;" />
</Parameters>
<Docs>
<param name="arguments">
A <see cref="T:System.Collections.Generic.IEnumerable{System.String}" />
which contains the unprocessed command-line arguments.
</param>
<summary>
Invoked by <see cref="M:Mono.Options.CommandSet.Run" /> when a command
has been executed.
</summary>
<returns>
A <see cref="T:System.Int32" /> which should be treated as the process
exit value.
</returns>
<remarks>
<para>
The value returned by <c>Invoke()</c> is the return value of
<see cref="M:Mono.Options.CommandSet.Run" />, and should be treated
as a possible process exit value.
</para>
<block subset="none" type="behaviors">
<para>
If the <c>Invoke()</c> method is not overridden by a subclass,
the <c>Invoke()</c> method will use
<see cref="P:Mono.Options.Command.Options" /> to parse
<paramref name="arguments" />, and pass any un-processed values
on to <see cref="P:Mono.Options.Command.Run" />.
</para>
<para>
If the <c>Options</c> property is <see langword="null" />, then no
option processing will occur, and <paramref name="arguments" />
will be provided to the <c>Run</c> property as-is.
</para>
<para>
If the <c>Run</c> property is <see langword="null" />, then
no further processing occurs.
</para>
</block>
<block subset="none" type="overrides">
<para>
Method overrides do not need to call the base class method.
</para>
</block>
</remarks>
</Docs>
</Member>
<Member MemberName="Name">
<MemberSignature Language="C#" Value="public string Name { get; }" />
<MemberSignature Language="ILAsm" Value=".property instance string Name" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.String</ReturnType>
</ReturnValue>
<Docs>
<summary>
The name of the <c>Command</c>, which is used for command invocation.
</summary>
<value>
A <see cref="T:System.String" /> which is the name of the <c>Command</c>.
</value>
<remarks>
<para>
The <c>Name</c> value must be unique across all <c>Commmand</c> instances
referred to by a <see cref="T:Mono.Options.CommandSet" />.
</para>
</remarks>
</Docs>
</Member>
<Member MemberName="Options">
<MemberSignature Language="C#" Value="public Mono.Options.OptionSet Options { get; set; }" />
<MemberSignature Language="ILAsm" Value=".property instance class Mono.Options.OptionSet Options" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>Mono.Options.OptionSet</ReturnType>
</ReturnValue>
<Docs>
<summary>
Optional command-line option information.
</summary>
<value>
A <see cref="T:Mono.Options.OptionSet" /> instance which contains the
available command-line options for the <c>Command</c>.
</value>
<remarks>
<para>
If the <c>Options</c> property is not <see langword="null" /> when
the command is processed,
<see cref="M:Mono.Options.OptionSet.Parse" /> will be invoked on
the <c>Options</c> instance, and the return value of
<c>OptionSet.Parse()</c> will be forwarded to
<see cref="M:Mono.Options.Command.Invoke" />.
</para>
</remarks>
</Docs>
</Member>
<Member MemberName="Run">
<MemberSignature Language="C#" Value="public Action&lt;System.Collections.Generic.IEnumerable&lt;string&gt;&gt; Run { get; set; }" />
<MemberSignature Language="ILAsm" Value=".property instance class System.Action`1&lt;class System.Collections.Generic.IEnumerable`1&lt;string&gt;&gt; Run" />
<MemberType>Property</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Action&lt;System.Collections.Generic.IEnumerable&lt;System.String&gt;&gt;</ReturnType>
</ReturnValue>
<Docs>
<summary>
Optional command handler.
</summary>
<value>
A <see cref="T:System.Action{System.Collections.Generic.IEnumerable{System.String}}" />
delegate which is executed by
<see cref="M:Mono.Options.Command.Invoke" />.
</value>
<remarks>
<para>
The <c>Run</c> property is executed by the the
<see cref="M:Mono.Options.Command.Invoke" /> method when
<see cref="M:Mono.Options.CommandSet.Run" /> dispatches to a
<c>Command</c> instance.
</para>
</remarks>
</Docs>
</Member>
</Members>
</Type>

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,51 @@
<Type Name="HelpCommand" FullName="Mono.Options.HelpCommand">
<TypeSignature Language="C#" Value="public class HelpCommand : Mono.Options.Command" />
<TypeSignature Language="ILAsm" Value=".class public auto ansi beforefieldinit HelpCommand extends Mono.Options.Command" />
<AssemblyInfo>
<AssemblyName>Mono.Options</AssemblyName>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<Base>
<BaseTypeName>Mono.Options.Command</BaseTypeName>
</Base>
<Interfaces />
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
<Members>
<Member MemberName=".ctor">
<MemberSignature Language="C#" Value="public HelpCommand ();" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig specialname rtspecialname instance void .ctor() cil managed" />
<MemberType>Constructor</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<Parameters />
<Docs>
<summary>To be added.</summary>
<remarks>To be added.</remarks>
</Docs>
</Member>
<Member MemberName="Invoke">
<MemberSignature Language="C#" Value="public override int Invoke (System.Collections.Generic.IEnumerable&lt;string&gt; arguments);" />
<MemberSignature Language="ILAsm" Value=".method public hidebysig virtual instance int32 Invoke(class System.Collections.Generic.IEnumerable`1&lt;string&gt; arguments) cil managed" />
<MemberType>Method</MemberType>
<AssemblyInfo>
<AssemblyVersion>0.2.3.0</AssemblyVersion>
</AssemblyInfo>
<ReturnValue>
<ReturnType>System.Int32</ReturnType>
</ReturnValue>
<Parameters>
<Parameter Name="arguments" Type="System.Collections.Generic.IEnumerable&lt;System.String&gt;" />
</Parameters>
<Docs>
<param name="arguments">To be added.</param>
<summary>To be added.</summary>
<returns>To be added.</returns>
<remarks>To be added.</remarks>
</Docs>
</Member>
</Members>
</Type>

View File

@@ -1 +1 @@
67225ad77680005658a2e3e423e91d7120289bdc
56ba9fdfdbae727a7812af0e87eb613942f9b05e

View File

@@ -0,0 +1,78 @@
// Sub-commands with Mono.Options.CommandSet
//
// Compile as:
// mcs -r:Mono.Options.dll commands.cs
using System;
using System.Collections.Generic;
using Mono.Options;
class CommandDemo {
public static int Main (string[] args)
{
var commands = new CommandSet ("commands") {
"usage: commands COMMAND [OPTIONS]",
"",
"Mono.Options.CommandSet sample app.",
"",
"Global options:",
{ "v:",
"Output verbosity.",
(int? n) => Verbosity = n.HasValue ? n.Value : Verbosity + 1 },
"",
"Available commands:",
new Command ("echo", "Echo arguments to the screen") {
Run = ca => Console.WriteLine ("{0}", string.Join (" ", ca)),
},
new RequiresArgs (),
};
return commands.Run (args);
}
public static int Verbosity;
}
class RequiresArgs : Command {
public RequiresArgs ()
: base ("requires-args", "Class-based Command subclass")
{
Options = new OptionSet () {
"usage: commands requires-args [OPTIONS]",
"",
"Class-based Command subclass example.",
{ "name|n=",
"{name} of person to greet.",
v => Name = v },
{ "help|h|?",
"Show this message and exit.",
v => ShowHelp = v != null },
};
}
public bool ShowHelp {get; private set;}
public new string Name {get; private set;}
public override int Invoke (IEnumerable<string> args)
{
try {
var extra = Options.Parse (args);
if (ShowHelp) {
Options.WriteOptionDescriptions (CommandSet.Out);
return 0;
}
if (string.IsNullOrEmpty (Name)) {
Console.Error.WriteLine ("commands: Missing required argument `--name=NAME`.");
Console.Error.WriteLine ("commands: Use `commands help requires-args` for details.");
return 1;
}
Console.WriteLine ($"Hello, {Name}!");
return 0;
}
catch (Exception e) {
Console.Error.WriteLine ("commands: {0}", CommandDemo.Verbosity >= 1 ? e.ToString () : e.Message);
return 1;
}
}
}

View File

@@ -0,0 +1,25 @@
mono Documentation/en/examples/commands.exe
mono Documentation/en/examples/commands.exe --help
mono Documentation/en/examples/commands.exe help
mono Documentation/en/examples/commands.exe help --help
mono Documentation/en/examples/commands.exe help echo
mono Documentation/en/examples/commands.exe echo --help
mono Documentation/en/examples/commands.exe echo hello, world
mono Documentation/en/examples/commands.exe requires-args
mono Documentation/en/examples/commands.exe help requires-args
mono Documentation/en/examples/commands.exe requires-args --help
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

View File

@@ -0,0 +1,74 @@
$ mono commands.exe
Use `commands help` for usage.
$ mono commands.exe --help
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
Global options:
-v[=VALUE] Output verbosity.
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
$ mono commands.exe help
usage: commands COMMAND [OPTIONS]
Mono.Options.CommandSet sample app.
Global options:
-v[=VALUE] Output verbosity.
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
$ mono commands.exe help --help
Usage: commands COMMAND [OPTIONS]
Use `commands help COMMAND` for help on a specific command.
Available commands:
echo Echo arguments to the screen
requires-args Class-based Command subclass
help Show this message and exit
$ mono commands.exe help echo
--help
$ mono commands.exe echo --help
--help
$ mono commands.exe echo hello, world
hello, world
$ mono commands.exe requires-args
commands: Missing required argument `--name=NAME`.
commands: Use `commands help requires-args` for details.
$ mono commands.exe help requires-args
usage: commands requires-args [OPTIONS]
Class-based Command subclass example.
--name, -n=name name of person to greet.
--help, -h, -? Show this message and exit.
$ mono commands.exe requires-args --help
usage: commands requires-args [OPTIONS]
Class-based Command subclass example.
--name, -n=name name of person to greet.
--help, -h, -? Show this message and exit.
$ mono commands.exe requires-args -n World
Hello, World!
$ mono commands.exe invalid-command
commands: Unknown command: invalid-command
commands: Use `commands help` for usage.
$ mono commands.exe help invalid-command
commands: Unknown command: invalid-command
commands: Use `commands help` for usage.

View File

@@ -18,6 +18,9 @@
<Attribute>
<AttributeName>System.Reflection.AssemblyTitle("Mono.Options.dll")</AttributeName>
</Attribute>
<Attribute>
<AttributeName>System.Runtime.CompilerServices.CompilationRelaxations(8)</AttributeName>
</Attribute>
<Attribute>
<AttributeName>System.Runtime.CompilerServices.RuntimeCompatibility(WrapNonExceptionThrows=true)</AttributeName>
</Attribute>
@@ -30,6 +33,9 @@
<Types>
<Namespace Name="Mono.Options">
<Type Name="ArgumentSource" Kind="Class" />
<Type Name="Command" Kind="Class" />
<Type Name="CommandSet" Kind="Class" />
<Type Name="HelpCommand" Kind="Class" />
<Type Name="Option" Kind="Class" />
<Type Name="OptionAction`2" DisplayName="OptionAction&lt;TKey,TValue&gt;" Kind="Delegate" />
<Type Name="OptionContext" Kind="Class" />

View File

@@ -17,6 +17,16 @@ mono_options_DATA = Mono.Options/Options.cs
include ../../build/library.make
test-local: Mono.Options-PCL.dll
clean-local: clean-pcl
Mono.Options-PCL.dll: Mono.Options.dll.sources $(shell cat Mono.Options.dll.sources)
$(CSCOMPILE) -target:library -out:$@ -debug+ -d:PCL -r:../lib/$(PROFILE)/System.dll @$<
clean-pcl:
-rm Mono.Options-PCL.dll
install-local: install-source
uninstall-local: uninstall-source
@@ -35,6 +45,7 @@ fixup-docs:
DOC_EXAMPLES_OUTPUT = \
Documentation/en/examples/bundling.txt \
Documentation/en/examples/commands.txt \
Documentation/en/examples/context.txt \
Documentation/en/examples/greet.txt \
Documentation/en/examples/localization.txt \
@@ -44,10 +55,10 @@ $(the_libdir)/.doc-stamp: $(DOC_EXAMPLES_OUTPUT)
Documentation/en/examples/Mono.Options.dll: $(the_lib)
cp $^ $@
-cp $^.mdb $@.mdb
%.exe: %.cs Documentation/en/examples/Mono.Options.dll
$(CSCOMPILE) -debug+ -r:Mono.Posix.dll -r:System.Core.dll -lib:Documentation/en/examples -r:Mono.Options.dll -out:$@ $<
$(CSCOMPILE) -debug:portable -r:$(topdir)/class/lib/$(PROFILE)/Mono.Posix.dll -r:$(topdir)/class/lib/$(PROFILE)/System.Core.dll -r:$(topdir)/class/lib/$(PROFILE)/System.dll \
-r:$(topdir)/class/lib/$(PROFILE)/Mono.Options.dll -out:$@ $<
Documentation/en/examples/locale/es/LC_MESSAGES/localization.mo: Documentation/en/examples/localization-es.po
msgfmt $< -o $@

View File

@@ -2,13 +2,14 @@
// Options.cs
//
// Authors:
// Jonathan Pryor <jpryor@novell.com>
// Jonathan Pryor <jpryor@novell.com>, <Jonathan.Pryor@microsoft.com>
// Federico Di Gregorio <fog@initd.org>
// Rolf Bjarne Kvinge <rolf@xamarin.com>
//
// Copyright (C) 2008 Novell (http://www.novell.com)
// Copyright (C) 2009 Federico Di Gregorio.
// Copyright (C) 2012 Xamarin Inc (http://www.xamarin.com)
// Copyright (C) 2017 Microsoft Corporation (http://www.microsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
@@ -31,8 +32,8 @@
//
// Compile With:
// gmcs -debug+ -r:System.Core Options.cs -o:NDesk.Options.dll
// gmcs -debug+ -d:LINQ -r:System.Core Options.cs -o:NDesk.Options.dll
// mcs -debug+ -r:System.Core Options.cs -o:Mono.Options.dll
// mcs -debug+ -d:LINQ -r:System.Core Options.cs -o:Mono.Options.dll
//
// The LINQ version just changes the implementation of
// OptionSet.Parse(IEnumerable<string>), and confers no semantic changes.
@@ -40,7 +41,7 @@
//
// A Getopt::Long-inspired option parsing library for C#.
//
// NDesk.Options.OptionSet is built upon a key/value table, where the
// Mono.Options.OptionSet is built upon a key/value table, where the
// key is a option format string and the value is a delegate that is
// invoked when the format string is matched.
//
@@ -127,6 +128,33 @@
// p.Parse (new string[]{"-a-"}); // sets v == null
//
//
// Mono.Options.CommandSet allows easily having separate commands and
// associated command options, allowing creation of a *suite* along the
// lines of **git**(1), **svn**(1), etc.
//
// CommandSet allows intermixing plain text strings for `--help` output,
// Option values -- as supported by OptionSet -- and Command instances,
// which have a name, optional help text, and an optional OptionSet.
//
// var suite = new CommandSet ("suite-name") {
// // Use strings and option values, as with OptionSet
// "usage: suite-name COMMAND [OPTIONS]+",
// { "v:", "verbosity", (int? v) => Verbosity = v.HasValue ? v.Value : Verbosity+1 },
// // Commands may also be specified
// new Command ("command-name", "command help") {
// Options = new OptionSet {/*...*/},
// Run = args => { /*...*/},
// },
// new MyCommandSubclass (),
// };
// return suite.Run (new string[]{...});
//
// CommandSet provides a `help` command, and forwards `help COMMAND`
// to the registered Command instance by invoking Command.Invoke()
// with `--help` as an option.
//
using System;
using System.Collections;
using System.Collections.Generic;
@@ -414,7 +442,7 @@ namespace Mono.Options
? new[]{prototype + this.GetHashCode ()}
: prototype.Split ('|');
if (this is OptionSet.Category)
if (this is OptionSet.Category || this is CommandOption)
return;
this.type = ParsePrototype ();
@@ -585,6 +613,11 @@ namespace Mono.Options
protected abstract void OnParseComplete (OptionContext c);
internal void InvokeOnParseComplete (OptionContext c)
{
OnParseComplete (c);
}
public override string ToString ()
{
return Prototype;
@@ -732,20 +765,26 @@ namespace Mono.Options
public class OptionSet : KeyedCollection<string, Option>
{
public OptionSet ()
: this (delegate (string f) {return f;})
: this (null)
{
}
public OptionSet (MessageLocalizerConverter localizer)
{
this.roSources = new ReadOnlyCollection<ArgumentSource> (sources);
this.localizer = localizer;
this.roSources = new ReadOnlyCollection<ArgumentSource>(sources);
if (this.localizer == null) {
this.localizer = delegate (string f) {
return f;
};
}
}
MessageLocalizerConverter localizer;
public MessageLocalizerConverter MessageLocalizer {
get {return localizer;}
internal set {localizer = value;}
}
List<ArgumentSource> sources = new List<ArgumentSource> ();
@@ -1210,6 +1249,9 @@ namespace Mono.Options
private const int Description_FirstWidth = 80 - OptionWidth;
private const int Description_RemWidth = 80 - OptionWidth - 2;
static readonly string CommandHelpIndentStart = new string (' ', OptionWidth);
static readonly string CommandHelpIndentRemaining = new string (' ', OptionWidth + 2);
public void WriteOptionDescriptions (TextWriter o)
{
foreach (Option p in this) {
@@ -1223,6 +1265,11 @@ namespace Mono.Options
WriteDescription (o, p.Description, "", 80, 80);
continue;
}
CommandOption co = p as CommandOption;
if (co != null) {
WriteCommandDescription (o, co.Command);
continue;
}
if (!WriteOptionPrototype (o, p, ref written))
continue;
@@ -1264,6 +1311,17 @@ namespace Mono.Options
}
}
internal void WriteCommandDescription (TextWriter o, Command c)
{
var name = new string (' ', 8) + c.Name;
if (name.Length < OptionWidth - 1) {
WriteDescription (o, name + new string (' ', OptionWidth - name.Length) + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth);
} else {
WriteDescription (o, name, "", 80, 80);
WriteDescription (o, CommandHelpIndentStart + c.Help, CommandHelpIndentRemaining, 80, Description_RemWidth);
}
}
void WriteDescription (TextWriter o, string value, string prefix, int firstWidth, int remWidth)
{
bool indent = false;
@@ -1403,5 +1461,341 @@ namespace Mono.Options
return StringCoda.WrappedLines (description, firstWidth, remWidth);
}
}
public class Command
{
public string Name {get;}
public string Help {get;}
public OptionSet Options {get; set;}
public Action<IEnumerable<string>> Run {get; set;}
public CommandSet CommandSet {get; internal set;}
public Command (string name, string help = null)
{
if (string.IsNullOrEmpty (name))
throw new ArgumentNullException (nameof (name));
Name = name;
Help = help;
}
public virtual int Invoke (IEnumerable<string> arguments)
{
var rest = Options?.Parse (arguments) ?? arguments;
Run?.Invoke (rest);
return 0;
}
}
class CommandOption : Option
{
public Command Command {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)
{
if (command == null)
throw new ArgumentNullException (nameof (command));
Command = command;
}
protected override void OnParseComplete (OptionContext c)
{
throw new NotSupportedException ("CommandOption.OnParseComplete should not be invoked.");
}
}
class HelpOption : Option
{
Option option;
CommandSet commands;
public HelpOption (CommandSet commands, Option d)
: base (d.Prototype, d.Description, d.MaxValueCount, d.Hidden)
{
this.commands = commands;
this.option = d;
}
protected override void OnParseComplete (OptionContext c)
{
commands.showHelp = true;
option?.InvokeOnParseComplete (c);
}
}
class CommandOptionSet : OptionSet
{
CommandSet commands;
public CommandOptionSet (CommandSet commands, MessageLocalizerConverter localizer)
: base (localizer)
{
this.commands = commands;
}
protected override void SetItem (int index, Option item)
{
if (ShouldWrapOption (item)) {
base.SetItem (index, new HelpOption (commands, item));
return;
}
base.SetItem (index, item);
}
bool ShouldWrapOption (Option item)
{
if (item == null)
return false;
var help = item as HelpOption;
if (help != null)
return false;
foreach (var n in item.Names) {
if (n == "help")
return true;
}
return false;
}
protected override void InsertItem (int index, Option item)
{
if (ShouldWrapOption (item)) {
base.InsertItem (index, new HelpOption (commands, item));
return;
}
base.InsertItem (index, item);
}
}
public class CommandSet : KeyedCollection<string, Command>
{
readonly OptionSet options;
readonly TextWriter outWriter;
readonly TextWriter errorWriter;
readonly string suite;
HelpCommand help;
internal bool showHelp;
internal OptionSet Options => options;
public CommandSet (string suite, MessageLocalizerConverter localizer = null, TextWriter output = null, TextWriter error = null)
{
if (suite == null)
throw new ArgumentNullException (nameof (suite));
this.suite = suite;
options = new CommandOptionSet (this, localizer);
outWriter = output ?? Console.Out;
errorWriter = error ?? Console.Error;
}
public string Suite => suite;
public TextWriter Out => outWriter;
public TextWriter Error => errorWriter;
public MessageLocalizerConverter MessageLocalizer => options.MessageLocalizer;
protected override string GetKeyForItem (Command item)
{
return item?.Name;
}
public new CommandSet Add (Command value)
{
if (value == null)
throw new ArgumentNullException (nameof (value));
AddCommand (value);
options.Add (new CommandOption (value));
return this;
}
void AddCommand (Command value)
{
if (value.CommandSet != null && value.CommandSet != this) {
throw new ArgumentException ("Command instances can only be added to a single CommandSet.", nameof (value));
}
value.CommandSet = this;
if (value.Options != null) {
value.Options.MessageLocalizer = options.MessageLocalizer;
}
base.Add (value);
help = help ?? value as HelpCommand;
}
public CommandSet Add (string header)
{
options.Add (header);
return this;
}
public CommandSet Add (Option option)
{
options.Add (option);
return this;
}
public CommandSet Add (string prototype, Action<string> action)
{
options.Add (prototype, action);
return this;
}
public CommandSet Add (string prototype, string description, Action<string> action)
{
options.Add (prototype, description, action);
return this;
}
public CommandSet Add (string prototype, string description, Action<string> action, bool hidden)
{
options.Add (prototype, description, action, hidden);
return this;
}
public CommandSet Add (string prototype, OptionAction<string, string> action)
{
options.Add (prototype, action);
return this;
}
public CommandSet Add (string prototype, string description, OptionAction<string, string> action)
{
options.Add (prototype, description, action);
return this;
}
public CommandSet Add (string prototype, string description, OptionAction<string, string> action, bool hidden)
{
options.Add (prototype, description, action, hidden);
return this;
}
public CommandSet Add<T> (string prototype, Action<T> action)
{
options.Add (prototype, null, action);
return this;
}
public CommandSet Add<T> (string prototype, string description, Action<T> action)
{
options.Add (prototype, description, action);
return this;
}
public CommandSet Add<TKey, TValue> (string prototype, OptionAction<TKey, TValue> action)
{
options.Add (prototype, action);
return this;
}
public CommandSet Add<TKey, TValue> (string prototype, string description, OptionAction<TKey, TValue> action)
{
options.Add (prototype, description, action);
return this;
}
public CommandSet Add (ArgumentSource source)
{
options.Add (source);
return this;
}
public int Run (IEnumerable<string> arguments)
{
if (arguments == null)
throw new ArgumentNullException (nameof (arguments));
this.showHelp = false;
if (help == null) {
help = new HelpCommand ();
AddCommand (help);
}
Action<string> setHelp = v => showHelp = v != null;
if (!options.Contains ("help")) {
options.Add ("help", "", setHelp, hidden: true);
}
if (!options.Contains ("?")) {
options.Add ("?", "", setHelp, hidden: true);
}
var extra = options.Parse (arguments);
if (extra.Count == 0) {
if (showHelp) {
return help.Invoke (extra);
}
Out.WriteLine (options.MessageLocalizer ($"Use `{Suite} help` for usage."));
return 1;
}
var command = Contains (extra [0]) ? this [extra [0]] : null;
if (command == null) {
help.WriteUnknownCommand (extra [0]);
return 1;
}
extra.RemoveAt (0);
if (showHelp) {
if (command.Options?.Contains ("help") ?? true) {
extra.Add ("--help");
return command.Invoke (extra);
}
command.Options.WriteOptionDescriptions (Out);
return 0;
}
return command.Invoke (extra);
}
}
public class HelpCommand : Command
{
public HelpCommand ()
: base ("help", help: "Show this message and exit")
{
}
public override int Invoke (IEnumerable<string> arguments)
{
var extra = new List<string> (arguments ?? new string [0]);
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") {
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);
}
return 0;
}
if (command == null) {
WriteUnknownCommand (extra [0]);
return 1;
}
if (command.Options != null) {
command.Options.WriteOptionDescriptions (CommandSet.Out);
return 0;
}
return command.Invoke (new [] { "--help" });
}
internal void WriteUnknownCommand (string unknownCommand)
{
CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Unknown command: {unknownCommand}"));
CommandSet.Error.WriteLine (CommandSet.Options.MessageLocalizer ($"{CommandSet.Suite}: Use `{CommandSet.Suite} help` for usage."));
}
}
}

View File

@@ -1,5 +1,7 @@
Mono.Options/BaseRocksFixture.cs
Mono.Options/CollectionContract.cs
Mono.Options/CommandTest.cs
Mono.Options/CommandSetTest.cs
Mono.Options/ListContract.cs
Mono.Options/OptionContextTest.cs
Mono.Options/OptionSetTest.cs

View File

@@ -0,0 +1,249 @@
//
// CommandSetTest.cs
//
// Authors:
// Jonathan Pryor <Jonathan.Pryor@microsoft.com>
//
// Copyright (C) 2017 Microsoft (http://www.microsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
#if NDESK_OPTIONS
using NDesk.Options;
#else
using Mono.Options;
#endif
using Cadenza.Collections.Tests;
using NUnit.Framework;
#if NDESK_OPTIONS
namespace Tests.NDesk.Options
#else
namespace MonoTests.Mono.Options
#endif
{
[TestFixture]
public class CommandSetTest : ListContract<Command>
{
protected override ICollection<Command> CreateCollection (IEnumerable<Command> values)
{
var set = new CommandSet ("test");
foreach (var value in values)
set.Add (value);
return set;
}
protected override Command CreateValueA ()
{
return new Command (
"foo",
"foo help");
}
protected override Command CreateValueB ()
{
return new Command (
"bar",
"bar help");
}
protected override Command CreateValueC ()
{
return new Command (
"baz",
"baz help");
}
static IEnumerable<string> _ (params string [] a)
{
return a;
}
[Test]
public void Constructor_SuiteRequired ()
{
Assert.Throws<ArgumentNullException> (() => new CommandSet (null));
}
[Test]
public void Add_NullCommand ()
{
var c = new CommandSet ("cs");
Assert.Throws<ArgumentNullException> (() => c.Add ((Command)null));
}
[Test]
public void Add_CommandCanBeAddedToOnlyOneSet ()
{
var cs1 = new CommandSet ("cs1");
var cs2 = new CommandSet ("cs2");
var c = new Command ("command", "help");
cs1.Add (c);
Assert.Throws<ArgumentException> (() => cs2.Add (c));
}
[Test]
public void Add_SetsCommandSet ()
{
var cs = new CommandSet ("cs");
var c = new Command ("command");
Assert.IsNull (c.CommandSet);
cs.Add (c);
Assert.AreSame (cs, c.CommandSet);
}
[Test]
public void Add_DuplicateCommand ()
{
var s = new CommandSet ("set");
s.Add (new Command ("value"));
Assert.Throws<ArgumentException> (() => s.Add (new Command ("value")));
}
[Test]
public void Run_Help ()
{
var o = new StringWriter ();
var e = new StringWriter ();
var showVersion = false;
var showHelp = false;
var git = new CommandSet ("git", output: o, error: e) {
"usage: git [--version] ... <command> [<args>]",
"",
"Common Options:",
{ "version",
"show version info",
v => showVersion = v != null },
{ "help",
"show this message and exit",
v => showHelp = v != null },
"",
"These are common Git commands used in various situations:",
"",
"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 ("thisIsAVeryLongCommandNameInOrderToInduceWrapping", "Create an empty Git repository or reinitialize an existing one. Let's make this really long to cause a line wrap, shall we?"),
};
var expectedHelp = new StringWriter ();
expectedHelp.WriteLine ("usage: git [--version] ... <command> [<args>]");
expectedHelp.WriteLine ("");
expectedHelp.WriteLine ("Common Options:");
expectedHelp.WriteLine (" --version show version info");
expectedHelp.WriteLine (" --help show this message and exit");
expectedHelp.WriteLine ("");
expectedHelp.WriteLine ("These are common Git commands used in various situations:");
expectedHelp.WriteLine ("");
expectedHelp.WriteLine ("start a working area (see also: git help tutorial)");
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 (" 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?");
Assert.AreEqual (0, git.Run (new [] { "help" }));
Assert.AreEqual (expectedHelp.ToString (), o.ToString ());
var expectedHelpHelp = new StringWriter ();
expectedHelpHelp.WriteLine ("Usage: git COMMAND [OPTIONS]");
expectedHelpHelp.WriteLine ("Use `git help COMMAND` for help on a specific command.");
expectedHelpHelp.WriteLine ();
expectedHelpHelp.WriteLine ("Available commands:");
expectedHelpHelp.WriteLine ();
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 (" thisIsAVeryLongCommandNameInOrderToInduceWrapping");
expectedHelpHelp.WriteLine (" Create an empty Git repository or reinitialize an");
expectedHelpHelp.WriteLine (" existing one. Let's make this really long to");
expectedHelpHelp.WriteLine (" cause a line wrap, shall we?");
expectedHelpHelp.WriteLine (" help Show this message and exit");
o.GetStringBuilder ().Clear ();
Assert.AreEqual (0, git.Run (new [] { "help", "--help" }));
Assert.AreEqual (expectedHelpHelp.ToString (), o.ToString ());
}
[Test]
public void Run_Command ()
{
var a = 0;
var b = 0;
var c = new CommandSet ("set") {
new Command ("a") { Run = v => a = v.Count () },
new Command ("b") { Run = v => b = v.Count () },
};
Assert.AreEqual (0, c.Run (new [] { "a", "extra" }));
Assert.AreEqual (1, a);
Assert.AreEqual (0, b);
a = b = 0;
Assert.AreEqual (0, c.Run (new [] { "b" }));
Assert.AreEqual (0, a);
Assert.AreEqual (0, b);
Assert.AreEqual (0, c.Run (new [] { "b", "one", "two" }));
Assert.AreEqual (0, a);
Assert.AreEqual (2, b);
}
[Test]
public void Run_HelpCommandSendsHelpOption ()
{
var e = new Command ("echo");
e.Run = (args) => e.CommandSet.Out.WriteLine (string.Join (" ", args));
var o = new StringWriter ();
var c = new CommandSet ("set", output:o) {
e,
};
Assert.AreEqual (0, c.Run (new [] { "help", "echo" }));
var expected = $"--help{Environment.NewLine}";
var actual = o.ToString ();
Assert.AreEqual (expected, actual);
}
[Test]
public void Run_NullArgument ()
{
var c = new CommandSet ("c");
Assert.Throws<ArgumentNullException> (() => c.Run (null));
}
}
}

View File

@@ -0,0 +1,102 @@
//
// CommandSetTest.cs
//
// Authors:
// Jonathan Pryor <Jonathan.Pryor@microsoft.com>
//
// Copyright (C) 2017 Microsoft (http://www.microsoft.com)
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Globalization;
using System.IO;
using System.Linq;
#if NDESK_OPTIONS
using NDesk.Options;
#else
using Mono.Options;
#endif
using Cadenza.Collections.Tests;
using NUnit.Framework;
#if NDESK_OPTIONS
namespace Tests.NDesk.Options
#else
namespace MonoTests.Mono.Options
#endif
{
[TestFixture]
public class CommandTest
{
[Test]
public void Constructor_NameRequired ()
{
Assert.Throws<ArgumentNullException> (() => new Command (name: null, help: null));
}
[Test]
public void Constructor ()
{
var c = new Command ("command", "help");
Assert.AreEqual ("command", c.Name);
Assert.AreEqual ("help", c.Help);
}
[Test]
public void Invoke_CallsRun ()
{
bool runInvoked = false;
var c = new Command ("command") {
Run = v => runInvoked = true,
};
Assert.AreEqual (0, c.Invoke (null));
Assert.IsTrue (runInvoked);
}
[Test]
public void Invoke_RequiresNothing ()
{
var c = new Command ("c");
Assert.AreEqual (0, c.Invoke (null));
}
[Test]
public void Invoke_UsesOptions ()
{
bool showHelp = false;
var c = new Command ("c") {
Options = new OptionSet {
{ "help", v => showHelp = v != null },
},
};
Assert.AreEqual (0, c.Invoke (new [] { "--help" }));
Assert.IsTrue (showHelp);
}
}
}