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
making it actually work and pass most of the tests
  • Loading branch information
adamsitnik committed Feb 21, 2023
commit d39fe8f6c1c27d612bfe11d4febba843e183bb84
2 changes: 1 addition & 1 deletion samples/RenderingPlayground/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,7 @@ public static void Main(

var consoleRenderer = new ConsoleRenderer(
console,
mode: invocationContext.BindingContext.OutputMode(),
mode: OutputMode.Auto,
resetAfterRender: true);

switch (sample)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
System.CommandLine.Hosting
public static class DirectiveConfigurationExtensions
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddCommandLineDirectives(this Microsoft.Extensions.Configuration.IConfigurationBuilder config, System.CommandLine.ParseResult commandline, System.String name)
public static Microsoft.Extensions.Configuration.IConfigurationBuilder AddCommandLineDirectives(this Microsoft.Extensions.Configuration.IConfigurationBuilder config, System.CommandLine.ParseResult commandline, System.CommandLine.Directive directive)
public static class HostingExtensions
public static OptionsBuilder<TOptions> BindCommandLine<TOptions>(this OptionsBuilder<TOptions> optionsBuilder)
public static Microsoft.Extensions.Hosting.IHost GetHost(this System.CommandLine.Invocation.InvocationContext invocationContext)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,7 @@ System.CommandLine
public class CommandLineBuilder
.ctor(Command rootCommand = null)
public Command Command { get; }
public System.Collections.Generic.List<Directive> Directives { get; }
public System.CommandLine.Parsing.Parser Build()
public static class CommandLineBuilderExtensions
public static CommandLineBuilder AddMiddleware(this CommandLineBuilder builder, System.CommandLine.Invocation.InvocationMiddleware middleware, System.CommandLine.Invocation.MiddlewareOrder order = Default)
Expand Down Expand Up @@ -109,8 +110,10 @@ System.CommandLine
public class Directive : Symbol
.ctor(System.String name, System.String description = null, System.Action<System.CommandLine.Invocation.InvocationContext> syncHandler = null, System.Func<System.CommandLine.Invocation.InvocationContext,System.Threading.CancellationToken,System.Threading.Tasks.Task> asyncHandler = null)
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
public System.Void OnParsed(System.CommandLine.Parsing.DirectiveResult directiveResult)
public class EnvironmentVariablesDirective : Directive
.ctor()
public System.Void OnParsed(System.CommandLine.Parsing.DirectiveResult directiveResult)
public static class Handler
public static System.Void SetHandler(this Command command, System.Action<System.CommandLine.Invocation.InvocationContext> handle)
public static System.Void SetHandler(this Command command, System.Action handle)
Expand Down Expand Up @@ -213,7 +216,6 @@ System.CommandLine
.ctor(System.Int32 errorExitCode = 1)
public class ParseResult
public System.CommandLine.Parsing.CommandResult CommandResult { get; }
public System.Collections.Generic.IReadOnlyDictionary<System.String,System.Collections.Generic.IReadOnlyList<System.String>> Directives { get; }
public System.Collections.Generic.IReadOnlyList<System.CommandLine.Parsing.ParseError> Errors { get; }
public System.CommandLine.Parsing.Parser Parser { get; }
public System.CommandLine.Parsing.CommandResult RootCommandResult { get; }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,8 @@ public IEnumerable<object> GenerateTestParseResults()

[Benchmark]
[ArgumentsSource(nameof(GenerateTestInputs))]
public IReadOnlyDictionary<string, IReadOnlyList<string>> ParseResult_Directives(string input)
=> _testParser.Parse(input).Directives;
public ParseResult ParseResult_Directives(string input)
=> _testParser.Parse(input);

[Benchmark]
[ArgumentsSource(nameof(GenerateTestParseResults))]
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System.Collections.Generic;
using System.CommandLine.Parsing;
using System.Linq;

using Microsoft.Extensions.Configuration;
Expand All @@ -9,18 +10,18 @@ public static class DirectiveConfigurationExtensions
{
public static IConfigurationBuilder AddCommandLineDirectives(
this IConfigurationBuilder config, ParseResult commandline,
string name)
Directive directive)
{
if (commandline is null)
throw new ArgumentNullException(nameof(commandline));
if (name is null)
throw new ArgumentNullException(nameof(name));
if (directive is null)
throw new ArgumentNullException(nameof(directive));

if (!commandline.Directives.TryGetValue(name, out var directives))
if (commandline.FindResultFor(directive) is not DirectiveResult result)
return config;

var kvpSeparator = new[] { '=' };
return config.AddInMemoryCollection(directives.Select(s =>
return config.AddInMemoryCollection(new string[] { result.Value }.Select(s =>
{
var parts = s.Split(kvpSeparator, count: 2);
var key = parts[0];
Expand Down
13 changes: 8 additions & 5 deletions src/System.CommandLine.Hosting/HostingExtensions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,12 +13,14 @@ namespace System.CommandLine.Hosting
{
public static class HostingExtensions
{
private const string ConfigurationDirectiveName = "config";

public static CommandLineBuilder UseHost(this CommandLineBuilder builder,
Func<string[], IHostBuilder> hostBuilderFactory,
Action<IHostBuilder> configureHost = null) =>
builder.AddMiddleware(async (invocation, cancellationToken, next) =>
Action<IHostBuilder> configureHost = null)
{
Directive configurationDirective = new("config");
builder.Directives.Add(configurationDirective);

return builder.AddMiddleware(async (invocation, cancellationToken, next) =>
{
var argsRemaining = invocation.ParseResult.UnmatchedTokens.ToArray();
var hostBuilder = hostBuilderFactory?.Invoke(argsRemaining)
Expand All @@ -27,7 +29,7 @@ public static CommandLineBuilder UseHost(this CommandLineBuilder builder,

hostBuilder.ConfigureHostConfiguration(config =>
{
config.AddCommandLineDirectives(invocation.ParseResult, ConfigurationDirectiveName);
config.AddCommandLineDirectives(invocation.ParseResult, configurationDirective);
});
hostBuilder.ConfigureServices(services =>
{
Expand All @@ -50,6 +52,7 @@ public static CommandLineBuilder UseHost(this CommandLineBuilder builder,

await host.StopAsync(cancellationToken);
});
}

public static CommandLineBuilder UseHost(this CommandLineBuilder builder,
Action<IHostBuilder> configureHost = null
Expand Down
36 changes: 16 additions & 20 deletions src/System.CommandLine.Rendering/CommandLineBuilderExtensions.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
// Copyright (c) .NET Foundation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.

using System.CommandLine.Binding;
using System.Linq;
using System.CommandLine.Parsing;

namespace System.CommandLine.Rendering
{
Expand All @@ -11,30 +10,32 @@ public static class CommandLineBuilderExtensions
public static CommandLineBuilder UseAnsiTerminalWhenAvailable(
this CommandLineBuilder builder)
{
Directive enableVtDirective = new ("enable-vt");
Directive outputDirective = new ("output");
builder.Directives.Add(enableVtDirective);
builder.Directives.Add(outputDirective);

builder.AddMiddleware(context =>
{
var console = context.Console;

var terminal = console.GetTerminal(
PreferVirtualTerminal(context.BindingContext),
OutputMode(context.BindingContext));
PreferVirtualTerminal(context.ParseResult, enableVtDirective),
OutputMode(context.ParseResult, outputDirective));

context.Console = terminal ?? console;
});

return builder;
}

internal static bool PreferVirtualTerminal(
this BindingContext context)
private static bool PreferVirtualTerminal(ParseResult parseResult, Directive enableVtDirective)
{
if (context.ParseResult.Directives.TryGetValue(
"enable-vt",
out var trueOrFalse))
if (parseResult.FindResultFor(enableVtDirective) is DirectiveResult result)
{
if (bool.TryParse(
trueOrFalse.FirstOrDefault(),
out var pvt))
string trueOrFalse = result.Value;

if (bool.TryParse(trueOrFalse, out var pvt))
{
return pvt;
}
Expand All @@ -43,15 +44,10 @@ internal static bool PreferVirtualTerminal(
return true;
}

public static OutputMode OutputMode(this BindingContext context)
private static OutputMode OutputMode(ParseResult parseResult, Directive outputDirective)
{
if (context.ParseResult.Directives.TryGetValue(
"output",
out var modeString) &&
Enum.TryParse<OutputMode>(
modeString.FirstOrDefault(),
true,
out var mode))
if (parseResult.FindResultFor(outputDirective) is DirectiveResult result
&& Enum.TryParse<OutputMode>(result.Value, true, out var mode))
{
return mode;
}
Expand Down
114 changes: 57 additions & 57 deletions src/System.CommandLine.Tests/DirectiveTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,83 +12,78 @@ namespace System.CommandLine.Tests
public class DirectiveTests
{
[Fact]
public void Directives_should_not_be_considered_as_unmatched_tokens()
public void Directives_should_not_be_considered_as_unmatched_tokens_when_they_are_enabled()
{
var option = new Option<bool>("-y");
RootCommand root = new () { new Option<bool>("-y") };
CommandLineBuilder builder = new (root);
builder.Directives.Add(new ("some"));

var result = option.Parse($"{RootCommand.ExecutableName} [parse] -y");
var result = builder.Build().Parse($"{RootCommand.ExecutableName} [parse] -y");

result.UnmatchedTokens.Should().BeEmpty();
}

[Fact]
public void Raw_tokens_still_hold_directives()
{
var option = new Option<bool>("-y");
Directive directive = new ("parse");

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

result.Directives.ContainsKey("parse").Should().BeTrue();
result.FindResultFor(directive).Should().NotBeNull();
result.Tokens.Should().Contain(t => t.Value == "[parse]");
}

[Fact]
public void Directives_should_parse_into_the_directives_collection()
{
var option = new Option<bool>("-y");

var result = option.Parse("[parse] -y");

result.Directives.ContainsKey("parse").Should().BeTrue();
}

[Fact]
public void Multiple_directives_are_allowed()
{
var option = new Option<bool>("-y");
RootCommand root = new() { new Option<bool>("-y") };
Directive parseDirective = new ("parse");
Directive suggestDirective = new ("suggest");
CommandLineBuilder builder = new(root);
builder.Directives.Add(parseDirective);
builder.Directives.Add(suggestDirective);

var result = option.Parse("[parse] [suggest] -y");
var result = builder.Build().Parse("[parse] [suggest] -y");

result.Directives.ContainsKey("parse").Should().BeTrue();
result.Directives.ContainsKey("suggest").Should().BeTrue();
result.FindResultFor(parseDirective).Should().NotBeNull();
result.FindResultFor(suggestDirective).Should().NotBeNull();
}

[Fact]
public void Directives_must_be_the_first_argument()
{
var option = new Option<bool>("-y");
Directive directive = new("parse");

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

result.Directives.Should().BeEmpty();
result.FindResultFor(directive).Should().BeNull();
}

[Theory]
[InlineData("[key:value]", "key", "value")]
[InlineData("[key:value:more]", "key", "value:more")]
[InlineData("[key:]", "key", "")]
public void Directives_can_have_a_value_which_is_everything_after_the_first_colon(
string directive,
string expectedKey,
string wholeText,
string key,
string expectedValue)
{
var option = new Option<bool>("-y");
Directive directive = new(key);

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

result.Directives.TryGetValue(expectedKey, out var values).Should().BeTrue();
values.Should().BeEquivalentTo(expectedValue);
result.FindResultFor(directive).Value.Should().Be(expectedValue);
}

[Fact]
public void Directives_without_a_value_specified_have_a_value_of_empty_string()
{
var option = new Option<bool>("-y");
Directive directive = new("parse");

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

result.Directives.TryGetValue("parse", out var values).Should().BeTrue();
values.Should().BeEmpty();
result.FindResultFor(directive).Value.Should().BeEmpty();
}

[Theory]
Expand All @@ -100,46 +95,43 @@ public void Directives_must_have_a_non_empty_key(string directive)

var result = option.Parse($"{directive} -a");

result.Directives.Should().BeEmpty();
result.UnmatchedTokens.Should().Contain(directive);
}

[Theory]
[InlineData("[par se]")]
[InlineData("[ parse]")]
[InlineData("[parse ]")]
public void Directives_cannot_contain_spaces(object value)
public void Directives_cannot_contain_spaces(string value)
{
var option = new Option<bool>("-a");

var result = option.Parse($"{value} -a");
Action create = () => new Directive(value);

result.Directives.Should().BeEmpty();
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");
//[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");
// var result = option.Parse("[directive:one] [directive:two] -a");

result.Directives.TryGetValue("directive", out var values).Should().BeTrue();
values.Should().BeEquivalentTo("one", "two");
}
// 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 Directive_count_is_based_on_distinct_instances_of_directive_name()
//{
// var command = new RootCommand();

var result = command.Parse("[one] [two] [one:a] [one:b]");
// var result = command.Parse("[one] [two] [one:a] [one:b]");

result.Directives.Should().HaveCount(2);
}
// result.Directives.Should().HaveCount(2);
//}

[Fact]
public void Directives_can_be_disabled()
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.

{
var parser = new Parser(
new CommandLineConfiguration(
Expand All @@ -150,12 +142,20 @@ public void Directives_can_be_disabled()

var result = parser.Parse("[hello]");

result.Directives.Count().Should().Be(0);
result.CommandResult
.Tokens
.Select(t => t.Value)
.Should()
.BeEquivalentTo("[hello]");
}

private static ParseResult Parse(Option option, Directive directive, string commandLine)
{
RootCommand root = new() { option };
CommandLineBuilder builder = new(root);
builder.Directives.Add(directive);

return builder.Build().Parse(commandLine);
}
}
}
}
Loading