Skip to content

Commit 6cc3cbd

Browse files
committed
Merge branch 'develop' into ViktorHofer-netstandard20
2 parents 06ec69c + d638310 commit 6cc3cbd

15 files changed

+217
-54
lines changed

README.md

+2-2
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ __This library provides _hassle free_ command line parsing with a constantly upd
3737

3838
You can utilize the parser library in several ways:
3939

40-
- Install via Nuget/Paket
40+
- Install via Nuget/Paket: [https://www.nuget.org/packages/CommandLineParser/](https://www.nuget.org/packages/CommandLineParser/)
4141
- Integrate directly into your project by copying the .cs files into your project.
4242
- ILMerge during your build process.
4343

@@ -98,7 +98,7 @@ class Options
9898
public bool Verbose { get; set; }
9999

100100
[Option("stdin",
101-
Default = false
101+
Default = false,
102102
HelpText = "Read from stdin")]
103103
public bool stdin { get; set; }
104104

src/CommandLine/Core/InstanceBuilder.cs

+26-15
Original file line numberDiff line numberDiff line change
@@ -20,18 +20,22 @@ public static ParserResult<T> Build<T>(
2020
StringComparer nameComparer,
2121
bool ignoreValueCase,
2222
CultureInfo parsingCulture,
23+
bool autoHelp,
24+
bool autoVersion,
2325
IEnumerable<ErrorType> nonFatalErrors)
2426
{
2527
var typeInfo = factory.MapValueOrDefault(f => f().GetType(), typeof(T));
2628

2729
var specProps = typeInfo.GetSpecifications(pi => SpecificationProperty.Create(
28-
Specification.FromProperty(pi), pi, Maybe.Nothing<object>()));
30+
Specification.FromProperty(pi), pi, Maybe.Nothing<object>()))
31+
.Memorize();
2932

3033
var specs = from pt in specProps select pt.Specification;
3134

3235
var optionSpecs = specs
3336
.ThrowingValidate(SpecificationGuards.Lookup)
34-
.OfType<OptionSpecification>();
37+
.OfType<OptionSpecification>()
38+
.Memorize();
3539

3640
Func<T> makeDefault = () =>
3741
typeof(T).IsMutable()
@@ -42,18 +46,19 @@ public static ParserResult<T> Build<T>(
4246
Func<IEnumerable<Error>, ParserResult<T>> notParsed =
4347
errs => new NotParsed<T>(makeDefault().GetType().ToTypeInfo(), errs);
4448

49+
var argumentsList = arguments.Memorize();
4550
Func<ParserResult<T>> buildUp = () =>
4651
{
47-
var tokenizerResult = tokenizer(arguments, optionSpecs);
52+
var tokenizerResult = tokenizer(argumentsList, optionSpecs);
4853

49-
var tokens = tokenizerResult.SucceededWith();
54+
var tokens = tokenizerResult.SucceededWith().Memorize();
5055

5156
var partitions = TokenPartitioner.Partition(
5257
tokens,
5358
name => TypeLookup.FindTypeDescriptorAndSibling(name, optionSpecs, nameComparer));
54-
var optionsPartition = partitions.Item1;
55-
var valuesPartition = partitions.Item2;
56-
var errorsPartition = partitions.Item3;
59+
var optionsPartition = partitions.Item1.Memorize();
60+
var valuesPartition = partitions.Item2.Memorize();
61+
var errorsPartition = partitions.Item3.Memorize();
5762

5863
var optionSpecPropsResult =
5964
OptionMapper.MapValues(
@@ -65,7 +70,7 @@ public static ParserResult<T> Build<T>(
6570
var valueSpecPropsResult =
6671
ValueMapper.MapValues(
6772
(from pt in specProps where pt.Specification.IsValue() orderby ((ValueSpecification)pt.Specification).Index select pt),
68-
valuesPartition,
73+
valuesPartition,
6974
(vals, type, isScalar) => TypeConverter.ChangeType(vals, type, isScalar, parsingCulture, ignoreValueCase));
7075

7176
var missingValueErrors = from token in errorsPartition
@@ -75,7 +80,7 @@ public static ParserResult<T> Build<T>(
7580
.FromOptionSpecification());
7681

7782
var specPropsWithValue =
78-
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith());
83+
optionSpecPropsResult.SucceededWith().Concat(valueSpecPropsResult.SucceededWith()).Memorize();
7984

8085
var setPropertyErrors = new List<Error>();
8186

@@ -100,9 +105,13 @@ public static ParserResult<T> Build<T>(
100105
{
101106
var ctor = typeInfo.GetTypeInfo().GetConstructor((from sp in specProps select sp.Property.PropertyType).ToArray());
102107
var values = (from prms in ctor.GetParameters()
103-
join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower()
108+
join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToLower() into spv
109+
from sp in spv.DefaultIfEmpty()
104110
select
105-
sp.Value.GetValueOrDefault(
111+
sp == null
112+
? specProps.First(s => String.Equals(s.Property.Name, prms.Name, StringComparison.CurrentCultureIgnoreCase))
113+
.Property.PropertyType.GetDefaultValue()
114+
: sp.Value.GetValueOrDefault(
106115
sp.Specification.DefaultValue.GetValueOrDefault(
107116
sp.Specification.ConversionType.CreateDefaultForImmutable()))).ToArray();
108117
var immutable = (T)ctor.Invoke(values);
@@ -127,11 +136,13 @@ join sp in specPropsWithValue on prms.Name.ToLower() equals sp.Property.Name.ToL
127136
return allErrors.Except(warnings).ToParserResult(instance);
128137
};
129138

130-
var preprocessorErrors = arguments.Any()
131-
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer))
132-
: Enumerable.Empty<Error>();
139+
var preprocessorErrors = (
140+
argumentsList.Any()
141+
? arguments.Preprocess(PreprocessorGuards.Lookup(nameComparer, autoHelp, autoVersion))
142+
: Enumerable.Empty<Error>()
143+
).Memorize();
133144

134-
var result = arguments.Any()
145+
var result = argumentsList.Any()
135146
? preprocessorErrors.Any()
136147
? notParsed(preprocessorErrors)
137148
: buildUp()

src/CommandLine/Core/InstanceChooser.cs

+9-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,8 @@ public static ParserResult<object> Choose(
1919
StringComparer nameComparer,
2020
bool ignoreValueCase,
2121
CultureInfo parsingCulture,
22+
bool autoHelp,
23+
bool autoVersion,
2224
IEnumerable<ErrorType> nonFatalErrors)
2325
{
2426
Func<ParserResult<object>> choose = () =>
@@ -31,13 +33,13 @@ public static ParserResult<object> Choose(
3133

3234
var verbs = Verb.SelectFromTypes(types);
3335

34-
return preprocCompare("help")
36+
return (autoHelp && preprocCompare("help"))
3537
? MakeNotParsed(types,
3638
MakeHelpVerbRequestedError(verbs,
3739
arguments.Skip(1).FirstOrDefault() ?? string.Empty, nameComparer))
38-
: preprocCompare("version")
40+
: (autoVersion && preprocCompare("version"))
3941
? MakeNotParsed(types, new VersionRequestedError())
40-
: MatchVerb(tokenizer, verbs, arguments, nameComparer, ignoreValueCase, parsingCulture, nonFatalErrors);
42+
: MatchVerb(tokenizer, verbs, arguments, nameComparer, ignoreValueCase, parsingCulture, autoHelp, autoVersion, nonFatalErrors);
4143
};
4244

4345
return arguments.Any()
@@ -52,6 +54,8 @@ private static ParserResult<object> MatchVerb(
5254
StringComparer nameComparer,
5355
bool ignoreValueCase,
5456
CultureInfo parsingCulture,
57+
bool autoHelp,
58+
bool autoVersion,
5559
IEnumerable<ErrorType> nonFatalErrors)
5660
{
5761
return verbs.Any(a => nameComparer.Equals(a.Item1.Name, arguments.First()))
@@ -64,6 +68,8 @@ private static ParserResult<object> MatchVerb(
6468
nameComparer,
6569
ignoreValueCase,
6670
parsingCulture,
71+
autoHelp,
72+
autoVersion,
6773
nonFatalErrors)
6874
: MakeNotParsed(verbs.Select(v => v.Item2), new BadVerbSelectedError(arguments.First()));
6975
}

src/CommandLine/Core/OptionMapper.cs

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ select Tuple.Create(
4343
((OptionSpecification)pt.Specification).FromOptionSpecification()))))
4444
: Tuple.Create(pt, Maybe.Nothing<Error>());
4545
}
46-
);
46+
).Memorize();
4747
return Result.Succeed(
4848
sequencesAndErrors.Select(se => se.Item1),
4949
sequencesAndErrors.Select(se => se.Item2).OfType<Just<Error>>().Select(se => se.Value));

src/CommandLine/Core/PreprocessorGuards.cs

+7-6
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,14 @@ namespace CommandLine.Core
99
static class PreprocessorGuards
1010
{
1111
public static IEnumerable<Func<IEnumerable<string>, IEnumerable<Error>>>
12-
Lookup(StringComparer nameComparer)
12+
Lookup(StringComparer nameComparer, bool autoHelp, bool autoVersion)
1313
{
14-
return new List<Func<IEnumerable<string>, IEnumerable<Error>>>
15-
{
16-
HelpCommand(nameComparer),
17-
VersionCommand(nameComparer)
18-
};
14+
var list = new List<Func<IEnumerable<string>, IEnumerable<Error>>>();
15+
if (autoHelp)
16+
list.Add(HelpCommand(nameComparer));
17+
if (autoVersion)
18+
list.Add(VersionCommand(nameComparer));
19+
return list;
1920
}
2021

2122
public static Func<IEnumerable<string>, IEnumerable<Error>> HelpCommand(StringComparer nameComparer)

src/CommandLine/Core/TokenPartitioner.cs

+9-11
Original file line numberDiff line numberDiff line change
@@ -11,22 +11,20 @@ namespace CommandLine.Core
1111
static class TokenPartitioner
1212
{
1313
public static
14-
Tuple<
15-
IEnumerable<KeyValuePair<string, IEnumerable<string>>>, // options
16-
IEnumerable<string>, // values
17-
IEnumerable<Token> // errors
18-
> Partition(
14+
Tuple<IEnumerable<KeyValuePair<string, IEnumerable<string>>>, IEnumerable<string>, IEnumerable<Token>> Partition(
1915
IEnumerable<Token> tokens,
2016
Func<string, Maybe<TypeDescriptor>> typeLookup)
2117
{
18+
IEqualityComparer<Token> tokenComparer = ReferenceEqualityComparer.Default;
19+
2220
var tokenList = tokens.Memorize();
23-
var switches = Switch.Partition(tokenList, typeLookup).Memorize();
24-
var scalars = Scalar.Partition(tokenList, typeLookup).Memorize();
25-
var sequences = Sequence.Partition(tokenList, typeLookup).Memorize();
21+
var switches = new HashSet<Token>(Switch.Partition(tokenList, typeLookup), tokenComparer);
22+
var scalars = new HashSet<Token>(Scalar.Partition(tokenList, typeLookup), tokenComparer);
23+
var sequences = new HashSet<Token>(Sequence.Partition(tokenList, typeLookup), tokenComparer);
2624
var nonOptions = tokenList
27-
.Where(t => !switches.Contains(t, ReferenceEqualityComparer.Default))
28-
.Where(t => !scalars.Contains(t, ReferenceEqualityComparer.Default))
29-
.Where(t => !sequences.Contains(t, ReferenceEqualityComparer.Default)).Memorize();
25+
.Where(t => !switches.Contains(t))
26+
.Where(t => !scalars.Contains(t))
27+
.Where(t => !sequences.Contains(t)).Memorize();
3028
var values = nonOptions.Where(v => v.IsValue()).Memorize();
3129
var errors = nonOptions.Except(values, (IEqualityComparer<Token>)ReferenceEqualityComparer.Default).Memorize();
3230

src/CommandLine/Core/Tokenizer.cs

+3-3
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,7 @@ public static Result<IEnumerable<Token>, Error> Tokenize(
3636
select token)
3737
.Memorize();
3838

39-
var normalized = normalize(tokens);
39+
var normalized = normalize(tokens).Memorize();
4040

4141
var unkTokens = (from t in normalized where t.IsName() && nameLookup(t.Text) == NameLookupResult.NoOptionFound select t).Memorize();
4242

@@ -60,12 +60,12 @@ public static Result<IEnumerable<Token>, Error> ExplodeOptionList(
6060
Result<IEnumerable<Token>, Error> tokenizerResult,
6161
Func<string, Maybe<char>> optionSequenceWithSeparatorLookup)
6262
{
63-
var tokens = tokenizerResult.SucceededWith();
63+
var tokens = tokenizerResult.SucceededWith().Memorize();
6464

6565
var replaces = tokens.Select((t, i) =>
6666
optionSequenceWithSeparatorLookup(t.Text)
6767
.MapValueOrDefault(sep => Tuple.Create(i + 1, sep),
68-
Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0);
68+
Tuple.Create(-1, '\0'))).SkipWhile(x => x.Item1 < 0).Memorize();
6969

7070
var exploded = tokens.Select((t, i) =>
7171
replaces.FirstOrDefault(x => x.Item1 == i).ToMaybe()

src/CommandLine/Parser.cs

+6
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,8 @@ public ParserResult<T> ParseArguments<T>(IEnumerable<string> args)
9999
settings.NameComparer,
100100
settings.CaseInsensitiveEnumValues,
101101
settings.ParsingCulture,
102+
settings.AutoHelp,
103+
settings.AutoVersion,
102104
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
103105
settings);
104106
}
@@ -128,6 +130,8 @@ public ParserResult<T> ParseArguments<T>(Func<T> factory, IEnumerable<string> ar
128130
settings.NameComparer,
129131
settings.CaseInsensitiveEnumValues,
130132
settings.ParsingCulture,
133+
settings.AutoHelp,
134+
settings.AutoVersion,
131135
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
132136
settings);
133137
}
@@ -158,6 +162,8 @@ public ParserResult<object> ParseArguments(IEnumerable<string> args, params Type
158162
settings.NameComparer,
159163
settings.CaseInsensitiveEnumValues,
160164
settings.ParsingCulture,
165+
settings.AutoHelp,
166+
settings.AutoVersion,
161167
HandleUnknownArguments(settings.IgnoreUnknownArguments)),
162168
settings);
163169
}

src/CommandLine/ParserSettings.cs

+22
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,8 @@ public class ParserSettings : IDisposable
2020
private bool caseInsensitiveEnumValues;
2121
private TextWriter helpWriter;
2222
private bool ignoreUnknownArguments;
23+
private bool autoHelp;
24+
private bool autoVersion;
2325
private CultureInfo parsingCulture;
2426
private bool enableDashDash;
2527
private int maximumDisplayWidth;
@@ -31,6 +33,8 @@ public ParserSettings()
3133
{
3234
caseSensitive = true;
3335
caseInsensitiveEnumValues = false;
36+
autoHelp = true;
37+
autoVersion = true;
3438
parsingCulture = CultureInfo.InvariantCulture;
3539
try
3640
{
@@ -122,6 +126,24 @@ public bool IgnoreUnknownArguments
122126
set { PopsicleSetter.Set(Consumed, ref ignoreUnknownArguments, value); }
123127
}
124128

129+
/// <summary>
130+
/// Gets or sets a value indicating whether implicit option or verb 'help' should be supported.
131+
/// </summary>
132+
public bool AutoHelp
133+
{
134+
get { return autoHelp; }
135+
set { PopsicleSetter.Set(Consumed, ref autoHelp, value); }
136+
}
137+
138+
/// <summary>
139+
/// Gets or sets a value indicating whether implicit option or verb 'version' should be supported.
140+
/// </summary>
141+
public bool AutoVersion
142+
{
143+
get { return autoVersion; }
144+
set { PopsicleSetter.Set(Consumed, ref autoVersion, value); }
145+
}
146+
125147
/// <summary>
126148
/// Gets or sets a value indicating whether enable double dash '--' syntax,
127149
/// that forces parsing of all subsequent tokens as values.

src/CommandLine/Text/HeadingInfo.cs

+1-8
Original file line numberDiff line numberDiff line change
@@ -57,14 +57,7 @@ public static HeadingInfo Default
5757
{
5858
var title = ReflectionHelper.GetAttribute<AssemblyTitleAttribute>()
5959
.MapValueOrDefault(
60-
titleAttribute =>
61-
{
62-
if (titleAttribute.Title.ToLowerInvariant().EndsWith(".dll"))
63-
{
64-
return titleAttribute.Title.Substring(0, titleAttribute.Title.Length - ".dll".Length);
65-
}
66-
return titleAttribute.Title;
67-
},
60+
titleAttribute => titleAttribute.Title,
6861
ReflectionHelper.GetAssemblyName());
6962

7063
var version = ReflectionHelper.GetAttribute<AssemblyInformationalVersionAttribute>()

0 commit comments

Comments
 (0)