diff --git a/src/CommandLine/Core/Scalar.cs b/src/CommandLine/Core/Scalar.cs index 215ca2d2..e1541bd3 100644 --- a/src/CommandLine/Core/Scalar.cs +++ b/src/CommandLine/Core/Scalar.cs @@ -16,7 +16,7 @@ public static IEnumerable Partition( { return from tseq in tokens.Pairwise( (f, s) => - f.IsName() && s.IsValue() + f.IsName() && s.IsValueUnforced() ? typeLookup(f.Text).MapValueOrDefault(info => info.TargetType == TargetType.Scalar ? new[] { f, s } : new Token[] { }, new Token[] { }) : new Token[] { }) diff --git a/src/CommandLine/Core/Sequence.cs b/src/CommandLine/Core/Sequence.cs index 10b9c600..95602458 100644 --- a/src/CommandLine/Core/Sequence.cs +++ b/src/CommandLine/Core/Sequence.cs @@ -33,7 +33,7 @@ public static IEnumerable Partition( break; case SequenceState.TokenFound: - if (token.IsValue()) + if (token.IsValueUnforced()) { if (sequences.TryGetValue(nameToken, out var sequence)) { diff --git a/src/CommandLine/Core/Token.cs b/src/CommandLine/Core/Token.cs index 2afee98f..4e9bb847 100644 --- a/src/CommandLine/Core/Token.cs +++ b/src/CommandLine/Core/Token.cs @@ -27,9 +27,14 @@ public static Token Value(string text) return new Value(text); } - public static Token Value(string text, bool explicitlyAssigned) + public static Token Value(string text, bool forced) { - return new Value(text, explicitlyAssigned); + return new Value(text, forced); + } + + public static Token ValueForced(string text) + { + return new Value(text, true); } public TokenType Tag @@ -79,22 +84,22 @@ public bool Equals(Name other) class Value : Token, IEquatable { - private readonly bool explicitlyAssigned; + private readonly bool forced; public Value(string text) : this(text, false) { } - public Value(string text, bool explicitlyAssigned) + public Value(string text, bool forced) : base(TokenType.Value, text) { - this.explicitlyAssigned = explicitlyAssigned; + this.forced = forced; } - public bool ExplicitlyAssigned + public bool Forced { - get { return explicitlyAssigned; } + get { return forced; } } public override bool Equals(object obj) @@ -110,7 +115,7 @@ public override bool Equals(object obj) public override int GetHashCode() { - return new { Tag, Text }.GetHashCode(); + return new { Tag, Text, Forced }.GetHashCode(); } public bool Equals(Value other) @@ -120,7 +125,7 @@ public bool Equals(Value other) return false; } - return Tag.Equals(other.Tag) && Text.Equals(other.Text); + return Tag.Equals(other.Tag) && Text.Equals(other.Text) && this.Forced == other.Forced; } } @@ -135,5 +140,15 @@ public static bool IsValue(this Token token) { return token.Tag == TokenType.Value; } + + public static bool IsValueForced(this Token token) + { + return token.IsValue() && ((Value)token).Forced; + } + + public static bool IsValueUnforced(this Token token) + { + return token.IsValue() && ! ((Value)token).Forced; + } } -} \ No newline at end of file +} diff --git a/src/CommandLine/Core/Tokenizer.cs b/src/CommandLine/Core/Tokenizer.cs index 25a14cdc..a35a0d28 100644 --- a/src/CommandLine/Core/Tokenizer.cs +++ b/src/CommandLine/Core/Tokenizer.cs @@ -34,6 +34,8 @@ public static Result, Error> Tokenize( int consumeNext = 0; Action onConsumeNext = (n => consumeNext = consumeNext + n); + bool isForced = false; + var tokens = new List(); var enumerator = arguments.GetEnumerator(); @@ -44,21 +46,22 @@ public static Result, Error> Tokenize( break; case string arg when consumeNext > 0: - tokens.Add(new Value(arg)); + tokens.Add(new Value(arg, isForced)); consumeNext = consumeNext - 1; break; case "--" when allowDashDash: consumeNext = System.Int32.MaxValue; + isForced = true; break; case "--": - tokens.Add(new Value("--")); + tokens.Add(new Value("--", isForced)); break; case "-": // A single hyphen is always a value (it usually means "read from stdin" or "write to stdout") - tokens.Add(new Value("-")); + tokens.Add(new Value("-", isForced)); break; case string arg when arg.StartsWith("--"): @@ -71,7 +74,7 @@ public static Result, Error> Tokenize( case string arg: // If we get this far, it's a plain value - tokens.Add(new Value(arg)); + tokens.Add(new Value(arg, isForced)); break; } } diff --git a/tests/CommandLine.Tests/Fakes/Options_With_Option_Sequence_And_Value_Sequence.cs b/tests/CommandLine.Tests/Fakes/Options_With_Option_Sequence_And_Value_Sequence.cs new file mode 100644 index 00000000..c0ce7cdf --- /dev/null +++ b/tests/CommandLine.Tests/Fakes/Options_With_Option_Sequence_And_Value_Sequence.cs @@ -0,0 +1,13 @@ +using System.Collections.Generic; + +namespace CommandLine.Tests.Fakes +{ + public class Options_With_Option_Sequence_And_Value_Sequence + { + [Option('o', "option-seq")] + public IEnumerable OptionSequence { get; set; } + + [Value(0)] + public IEnumerable ValueSequence { get; set; } + } +} diff --git a/tests/CommandLine.Tests/Unit/ParserTests.cs b/tests/CommandLine.Tests/Unit/ParserTests.cs index 46aad457..081121bc 100644 --- a/tests/CommandLine.Tests/Unit/ParserTests.cs +++ b/tests/CommandLine.Tests/Unit/ParserTests.cs @@ -132,6 +132,26 @@ public void Parse_options_with_double_dash() // Teardown } + [Fact] + public void Parse_options_with_double_dash_and_option_sequence() + { + var expectedOptions = new Options_With_Option_Sequence_And_Value_Sequence + { + OptionSequence = new[] { "option1", "option2", "option3" }, + ValueSequence = new[] { "value1", "value2", "value3" } + }; + + var sut = new Parser(with => with.EnableDashDash = true); + + // Exercize system + var result = + sut.ParseArguments( + new[] { "--option-seq", "option1", "option2", "option3", "--", "value1", "value2", "value3" }); + + // Verify outcome + ((Parsed)result).Value.Should().BeEquivalentTo(expectedOptions); + } + [Fact] public void Parse_options_with_double_dash_in_verbs_scenario() {