Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
handle directive specified multiple times
  • Loading branch information
adamsitnik committed Feb 22, 2023
commit 47f9a89f9d61c4956deb59a82ce98acc1397081b
Original file line number Diff line number Diff line change
Expand Up @@ -402,7 +402,7 @@ System.CommandLine.Parsing
public class DirectiveResult : SymbolResult
public System.CommandLine.Directive Directive { get; }
public Token Token { get; }
public System.String Value { get; }
public System.Collections.Generic.IReadOnlyList<System.String> Values { get; }
public class OptionResult : SymbolResult
public System.Boolean IsImplicit { get; }
public System.CommandLine.Option Option { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,14 @@ public static IConfigurationBuilder AddCommandLineDirectives(
if (directive is null)
throw new ArgumentNullException(nameof(directive));

if (commandline.FindResultFor(directive) is not DirectiveResult result)
if (commandline.FindResultFor(directive) is not DirectiveResult result
|| result.Values.Count == 0)
{
return config;
}

var kvpSeparator = new[] { '=' };
return config.AddInMemoryCollection(new string[] { result.Value }.Select(s =>
return config.AddInMemoryCollection(result.Values.Select(s =>
{
var parts = s.Split(kvpSeparator, count: 2);
var key = parts[0];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,10 @@ public static CommandLineBuilder UseAnsiTerminalWhenAvailable(

private static bool PreferVirtualTerminal(ParseResult parseResult, Directive enableVtDirective)
{
if (parseResult.FindResultFor(enableVtDirective) is DirectiveResult result)
if (parseResult.FindResultFor(enableVtDirective) is DirectiveResult result
&& result.Values.Count == 1)
{
string trueOrFalse = result.Value;
string trueOrFalse = result.Values[0];

if (bool.TryParse(trueOrFalse, out var pvt))
{
Expand All @@ -47,7 +48,8 @@ private static bool PreferVirtualTerminal(ParseResult parseResult, Directive ena
private static OutputMode OutputMode(ParseResult parseResult, Directive outputDirective)
{
if (parseResult.FindResultFor(outputDirective) is DirectiveResult result
&& Enum.TryParse<OutputMode>(result.Value, true, out var mode))
&& result.Values.Count == 1
&& Enum.TryParse<OutputMode>(result.Values[0], true, out var mode))
{
return mode;
}
Expand Down
31 changes: 10 additions & 21 deletions src/System.CommandLine.Tests/DirectiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -73,17 +73,17 @@ public void Directives_can_have_a_value_which_is_everything_after_the_first_colo

ParseResult result = Parse(new Option<bool>("-y"), directive, $"{wholeText} -y");

result.FindResultFor(directive).Value.Should().Be(expectedValue);
result.FindResultFor(directive).Values.Single().Should().Be(expectedValue);
}

[Fact]
public void Directives_without_a_value_specified_have_a_value_of_empty_string()
public void Directives_without_a_value_specified_have_no_values()
{
Directive directive = new("parse");

ParseResult result = Parse(new Option<bool>("-y"), directive, "[parse] -y");

result.FindResultFor(directive).Value.Should().BeEmpty();
result.FindResultFor(directive).Values.Should().BeEmpty();
}

[Theory]
Expand All @@ -109,26 +109,15 @@ public void Directives_cannot_contain_spaces(string value)
create.Should().Throw<ArgumentException>();
}

//[Fact]
//public void When_a_directive_is_specified_more_than_once_then_its_values_are_aggregated()
//{
// var option = new Option<bool>("-a");

// var result = option.Parse("[directive:one] [directive:two] -a");

// result.Directives.TryGetValue("directive", out var values).Should().BeTrue();
// values.Should().BeEquivalentTo("one", "two");
//}

//[Fact]
//public void Directive_count_is_based_on_distinct_instances_of_directive_name()
//{
// var command = new RootCommand();
[Fact]
public void When_a_directive_is_specified_more_than_once_then_its_values_are_aggregated()
{
Directive directive = new("directive");

// var result = command.Parse("[one] [two] [one:a] [one:b]");
ParseResult result = Parse(new Option<bool>("-a"), directive, "[directive:one] [directive:two] -a");

// result.Directives.Should().HaveCount(2);
//}
result.FindResultFor(directive).Values.Should().BeEquivalentTo("one", "two");
}

[Fact]
public void When_directives_are_not_enabled_they_are_treated_as_regular_tokens()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I would expect them to be present as tokens now in any case, though maybe only on the root command result.

Expand Down
26 changes: 15 additions & 11 deletions src/System.CommandLine/EnvironmentVariablesDirective.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,20 +13,24 @@ public EnvironmentVariablesDirective() : base("env")

public override void OnParsed(DirectiveResult directiveResult)
{
if (string.IsNullOrEmpty(directiveResult.Value))
for (int i = 0; i < directiveResult.Values.Count; i++)
{
return;
}
string parsedValue = directiveResult.Values[i];

string[] components = directiveResult.Value.Split(new[] { '=' }, count: 2);
string variable = components.Length > 0 ? components[0].Trim() : string.Empty;
if (string.IsNullOrEmpty(variable) || components.Length < 2)
{
return;
}
int indexOfSeparator = parsedValue.AsSpan().IndexOf('=');

if (indexOfSeparator > 0)
{
ReadOnlySpan<char> variable = parsedValue.AsSpan(0, indexOfSeparator).Trim();

string value = components[1].Trim();
Environment.SetEnvironmentVariable(variable, value);
if (!variable.IsEmpty)
{
string value = parsedValue.AsSpan(indexOfSeparator + 1).Trim().ToString();

Environment.SetEnvironmentVariable(variable.ToString(), value);
}
}
}
}
}
}
15 changes: 10 additions & 5 deletions src/System.CommandLine/Parsing/DirectiveResult.cs
Original file line number Diff line number Diff line change
@@ -1,23 +1,26 @@
namespace System.CommandLine.Parsing
using System.Collections.Generic;

namespace System.CommandLine.Parsing
{
/// <summary>
/// A result produced when parsing an <see cref="Directive"/>.
/// </summary>
public sealed class DirectiveResult : SymbolResult
{
internal DirectiveResult(Directive directive, Token token, string value, SymbolResultTree symbolResultTree)
private List<string>? _values;

internal DirectiveResult(Directive directive, Token token, SymbolResultTree symbolResultTree)
: base(symbolResultTree, null) // directives don't belong to any command
{
Directive = directive;
Token = token;
Value = value;
}

/// <summary>
/// Parsed value of an [name:value] directive.
/// Parsed values of [name:value] directive(s).
/// </summary>
/// <remarks>Can be empty for [name] directives.</remarks>
public string Value { get; }
public IReadOnlyList<string> Values => _values is null ? Array.Empty<string>() : _values;

/// <summary>
/// The directive to which the result applies.
Expand All @@ -28,5 +31,7 @@ internal DirectiveResult(Directive directive, Token token, string value, SymbolR
/// The token that was parsed to specify the directive.
/// </summary>
public Token Token { get; }

internal void AddValue(string value) => (_values ??= new()).Add(value);
}
}
21 changes: 16 additions & 5 deletions src/System.CommandLine/Parsing/ParseOperation.cs
Original file line number Diff line number Diff line change
Expand Up @@ -303,14 +303,25 @@ void ParseDirective()
return;
}

DirectiveResult result;
if (_symbolResultTree.TryGetValue(directive, out var directiveResult))
{
result = (DirectiveResult)directiveResult;
result.AddToken(token);
}
else
{
result = new (directive, token, _symbolResultTree);
_symbolResultTree.Add(directive, result);
}

ReadOnlySpan<char> withoutBrackets = token.Value.AsSpan(1, token.Value.Length - 2);
int indexOfColon = withoutBrackets.IndexOf(':');
string? value = indexOfColon > 0
? withoutBrackets.Slice(indexOfColon + 1).ToString()
: string.Empty; // Directives_without_a_value_specified_have_a_value_of_empty_string
if (indexOfColon > 0)
{
result.AddValue(withoutBrackets.Slice(indexOfColon + 1).ToString());
}

DirectiveResult result = new (directive, token, value, _symbolResultTree);
_symbolResultTree.Add(directive, result);
directive.OnParsed(result);
_handler = directive.Handler;
_symbol = directive;
Expand Down
2 changes: 1 addition & 1 deletion src/System.CommandLine/SuggestDirective.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public SuggestDirective() : base("suggest", syncHandler: SyncHandler)
private static void SyncHandler(InvocationContext context)
{
SuggestDirective symbol = (SuggestDirective)context.ParseResult.Symbol;
string? parsedValues = context.ParseResult.FindResultFor(symbol)!.Value;
string? parsedValues = context.ParseResult.FindResultFor(symbol)!.Values.SingleOrDefault();
string? rawInput = context.ParseResult.CommandLineText;

int position = !string.IsNullOrEmpty(parsedValues) ? int.Parse(parsedValues) : rawInput?.Length ?? 0;
Expand Down