diff --git a/src/Spectre.Console.Cli/CommandContext.cs b/src/Spectre.Console.Cli/CommandContext.cs
index c175ec4b8..89a2876ba 100644
--- a/src/Spectre.Console.Cli/CommandContext.cs
+++ b/src/Spectre.Console.Cli/CommandContext.cs
@@ -13,6 +13,11 @@ public sealed class CommandContext
///
public IRemainingArguments Remaining { get; }
+ ///
+ /// Gets all the arguments that were passed to the applicaton.
+ ///
+ public IReadOnlyList Arguments { get; }
+
///
/// Gets the name of the command.
///
@@ -32,11 +37,17 @@ public sealed class CommandContext
///
/// Initializes a new instance of the class.
///
+ /// All arguments that were passed to the application.
/// The remaining arguments.
/// The command name.
/// The command data.
- public CommandContext(IRemainingArguments remaining, string name, object? data)
+ public CommandContext(
+ IEnumerable arguments,
+ IRemainingArguments remaining,
+ string name,
+ object? data)
{
+ Arguments = arguments.ToSafeReadOnlyList();
Remaining = remaining ?? throw new System.ArgumentNullException(nameof(remaining));
Name = name ?? throw new System.ArgumentNullException(nameof(name));
Data = data;
diff --git a/src/Spectre.Console.Cli/IRemainingArguments.cs b/src/Spectre.Console.Cli/IRemainingArguments.cs
index 8127416f1..acd3b6055 100644
--- a/src/Spectre.Console.Cli/IRemainingArguments.cs
+++ b/src/Spectre.Console.Cli/IRemainingArguments.cs
@@ -12,6 +12,7 @@ public interface IRemainingArguments
///
/// Gets the raw, non-parsed remaining arguments.
+ /// This is normally everything after the `--` delimiter.
///
IReadOnlyList Raw { get; }
}
\ No newline at end of file
diff --git a/src/Spectre.Console.Cli/Internal/CommandExecutor.cs b/src/Spectre.Console.Cli/Internal/CommandExecutor.cs
index 0c34da5b3..2c2b1594f 100644
--- a/src/Spectre.Console.Cli/Internal/CommandExecutor.cs
+++ b/src/Spectre.Console.Cli/Internal/CommandExecutor.cs
@@ -17,7 +17,7 @@ public async Task Execute(IConfiguration configuration, IEnumerable
throw new ArgumentNullException(nameof(configuration));
}
- args ??= new List();
+ var arguments = args.ToSafeReadOnlyList();
_registrar.RegisterInstance(typeof(IConfiguration), configuration);
_registrar.RegisterLazy(typeof(IAnsiConsole), () => configuration.Settings.Console.GetConsole());
@@ -31,7 +31,7 @@ public async Task Execute(IConfiguration configuration, IEnumerable
if (model.DefaultCommand == null)
{
// Got at least one argument?
- var firstArgument = args.FirstOrDefault();
+ var firstArgument = arguments.FirstOrDefault();
if (firstArgument != null)
{
// Asking for version? Kind of a hack, but it's alright.
@@ -47,7 +47,7 @@ public async Task Execute(IConfiguration configuration, IEnumerable
}
// Parse and map the model against the arguments.
- var parsedResult = ParseCommandLineArguments(model, configuration.Settings, args);
+ var parsedResult = ParseCommandLineArguments(model, configuration.Settings, arguments);
// Register the arguments with the container.
_registrar.RegisterInstance(typeof(CommandTreeParserResult), parsedResult);
@@ -79,7 +79,7 @@ public async Task Execute(IConfiguration configuration, IEnumerable
}
// Is this the default and is it called without arguments when there are required arguments?
- if (leaf.Command.IsDefaultCommand && args.Count() == 0 && leaf.Command.Parameters.Any(p => p.Required))
+ if (leaf.Command.IsDefaultCommand && arguments.Count == 0 && leaf.Command.Parameters.Any(p => p.Required))
{
// Display help for default command.
configuration.Settings.Console.SafeRender(helpProvider.Write(model, leaf.Command));
@@ -87,15 +87,18 @@ public async Task Execute(IConfiguration configuration, IEnumerable
}
// Create the content.
- var context = new CommandContext(parsedResult.Remaining, leaf.Command.Name, leaf.Command.Data);
+ var context = new CommandContext(
+ arguments,
+ parsedResult.Remaining,
+ leaf.Command.Name,
+ leaf.Command.Data);
// Execute the command tree.
return await Execute(leaf, parsedResult.Tree, context, resolver, configuration).ConfigureAwait(false);
}
}
-#pragma warning disable CS8603 // Possible null reference return.
- private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IEnumerable args)
+ private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, CommandAppSettings settings, IReadOnlyList args)
{
var parser = new CommandTreeParser(model, settings.CaseSensitivity, settings.ParsingMode, settings.ConvertFlagsToRemainingArguments);
@@ -103,7 +106,7 @@ private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, Co
var tokenizerResult = CommandTreeTokenizer.Tokenize(args);
var parsedResult = parser.Parse(parserContext, tokenizerResult);
- var lastParsedLeaf = parsedResult?.Tree?.GetLeafCommand();
+ var lastParsedLeaf = parsedResult.Tree?.GetLeafCommand();
var lastParsedCommand = lastParsedLeaf?.Command;
if (lastParsedLeaf != null && lastParsedCommand != null &&
lastParsedCommand.IsBranch && !lastParsedLeaf.ShowHelp &&
@@ -122,7 +125,6 @@ private CommandTreeParserResult ParseCommandLineArguments(CommandModel model, Co
return parsedResult;
}
-#pragma warning restore CS8603 // Possible null reference return.
private static string ResolveApplicationVersion(IConfiguration configuration)
{
diff --git a/src/Spectre.Console.Cli/Internal/Extensions/EnumerableExtensions.cs b/src/Spectre.Console.Cli/Internal/Extensions/EnumerableExtensions.cs
new file mode 100644
index 000000000..ea775cff5
--- /dev/null
+++ b/src/Spectre.Console.Cli/Internal/Extensions/EnumerableExtensions.cs
@@ -0,0 +1,14 @@
+namespace Spectre.Console.Cli;
+
+internal static class EnumerableExtensions
+{
+ public static IReadOnlyList ToSafeReadOnlyList(this IEnumerable source)
+ {
+ return source switch
+ {
+ null => new List(),
+ IReadOnlyList list => list,
+ _ => source.ToList(),
+ };
+ }
+}
\ No newline at end of file
diff --git a/src/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj b/src/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj
index e74335a6c..69ba6c246 100644
--- a/src/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj
+++ b/src/Spectre.Console.ImageSharp/Spectre.Console.ImageSharp.csproj
@@ -13,7 +13,7 @@
-
+
diff --git a/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Context.cs b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Context.cs
new file mode 100644
index 000000000..ec88fd135
--- /dev/null
+++ b/test/Spectre.Console.Cli.Tests/Unit/CommandAppTests.Context.cs
@@ -0,0 +1,23 @@
+namespace Spectre.Console.Tests.Unit.Cli;
+
+public sealed partial class CommandAppTests
+{
+ [Fact]
+ [Expectation("Should_Expose_Raw_Arguments")]
+ public void Should_Return_Correct_Text_When_Command_Is_Unknown()
+ {
+ // Given
+ var app = new CommandAppTester();
+ app.Configure(config =>
+ {
+ config.AddCommand("test");
+ });
+
+ // When
+ var result = app.Run("test", "--foo", "32", "--lol");
+
+ // Then
+ result.Context.ShouldNotBeNull();
+ result.Context.Arguments.ShouldBe(new[] { "test", "--foo", "32", "--lol" });
+ }
+}
\ No newline at end of file