From d7e5f2dc4a04e333e350fbad5c9d2b908b672880 Mon Sep 17 00:00:00 2001 From: Mikkel Rasmussen Date: Sun, 16 Jun 2024 02:20:03 +0200 Subject: [PATCH 1/5] Created alias variable by allowing multiple command attributes, generation changes and test that passes --- src/ConsoleAppFramework/Command.cs | 2 ++ src/ConsoleAppFramework/CommandHelpBuilder.cs | 2 +- .../ConsoleAppGenerator.cs | 2 +- src/ConsoleAppFramework/Emitter.cs | 10 ++++++++ src/ConsoleAppFramework/Parser.cs | 12 ++++++++-- .../ConsoleAppBuilderTest.cs | 23 +++++++++++++++++++ 6 files changed, 47 insertions(+), 4 deletions(-) diff --git a/src/ConsoleAppFramework/Command.cs b/src/ConsoleAppFramework/Command.cs index 2cd3eee..2b728dc 100644 --- a/src/ConsoleAppFramework/Command.cs +++ b/src/ConsoleAppFramework/Command.cs @@ -22,6 +22,8 @@ public record class Command public bool IsRootCommand => Name == ""; public required string Name { get; init; } + public required string[] Aliases { get; set; } + public required EquatableArray Parameters { get; init; } public required string Description { get; init; } public required MethodKind MethodKind { get; init; } diff --git a/src/ConsoleAppFramework/CommandHelpBuilder.cs b/src/ConsoleAppFramework/CommandHelpBuilder.cs index 0e7a73e..2a9d7cd 100644 --- a/src/ConsoleAppFramework/CommandHelpBuilder.cs +++ b/src/ConsoleAppFramework/CommandHelpBuilder.cs @@ -217,7 +217,7 @@ static string BuildMethodListMessage(IEnumerable commands, out int maxW var formatted = commands .Select(x => { - return (Command: x.Name, x.Description); + return (Command: string.Join(", ", Array.Empty().Concat([x.Name]).Concat(x.Aliases)), x.Description); }) .ToArray(); maxWidth = formatted.Max(x => x.Command.Length); diff --git a/src/ConsoleAppFramework/ConsoleAppGenerator.cs b/src/ConsoleAppFramework/ConsoleAppGenerator.cs index 14d78c6..666cf73 100644 --- a/src/ConsoleAppFramework/ConsoleAppGenerator.cs +++ b/src/ConsoleAppFramework/ConsoleAppGenerator.cs @@ -144,7 +144,7 @@ internal sealed class ArgumentAttribute : Attribute { } -[AttributeUsage(AttributeTargets.Method, AllowMultiple = false, Inherited = false)] +[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] internal sealed class CommandAttribute : Attribute { public string Command { get; } diff --git a/src/ConsoleAppFramework/Emitter.cs b/src/ConsoleAppFramework/Emitter.cs index 987d8a3..9f4c9ff 100644 --- a/src/ConsoleAppFramework/Emitter.cs +++ b/src/ConsoleAppFramework/Emitter.cs @@ -447,8 +447,18 @@ void EmitRunBody(ILookup groupedCommands, int depth, bool { var leafCommand = groupedCommands[""].FirstOrDefault(); IDisposable? ifBlcok = null; + if (leafCommand is not null) + { + // Add or-ing of aliases + foreach (var alias in leafCommand.Command.Aliases) + { + sb.AppendLine($"case \"{alias}\":"); + } + } if (!(groupedCommands.Count == 1 && leafCommand != null)) { + + ifBlcok = sb.BeginBlock($"if (args.Length == {depth})"); } EmitLeafCommand(leafCommand); diff --git a/src/ConsoleAppFramework/Parser.cs b/src/ConsoleAppFramework/Parser.cs index 019bf15..f545a38 100644 --- a/src/ConsoleAppFramework/Parser.cs +++ b/src/ConsoleAppFramework/Parser.cs @@ -141,8 +141,9 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod .Select(x => { string commandName; - var commandAttribute = x.GetAttributes().FirstOrDefault(x => x.AttributeClass?.Name == "CommandAttribute"); - if (commandAttribute != null) + var commandAttributes = x.GetAttributes().Where(x => x.AttributeClass?.Name == "CommandAttribute"); + var firstCommandAttribute = commandAttributes.FirstOrDefault(); + if (firstCommandAttribute != null) { commandName = (x.GetAttributes()[0].ConstructorArguments[0].Value as string)!; } @@ -154,6 +155,11 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod var command = ParseFromMethodSymbol(x, false, (commandPath == null) ? commandName : $"{commandPath.Trim()} {commandName}", typeFilters); if (command == null) return null; + if (commandAttributes.Count() > 1) + { + command.Aliases = commandAttributes.Skip(1).Select((ca, i) => (x.GetAttributes()[1 + i].ConstructorArguments[0].Value as string)!).ToArray(); + } + command.CommandMethodInfo = methodInfoBase with { MethodName = x.Name }; return command; }) @@ -367,6 +373,7 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod var cmd = new Command { Name = commandName, + Aliases = [], IsAsync = isAsync, IsVoid = isVoid, Parameters = parameters, @@ -522,6 +529,7 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod var cmd = new Command { Name = commandName, + Aliases = [], IsAsync = isAsync, IsVoid = isVoid, Parameters = parameters, diff --git a/tests/ConsoleAppFramework.GeneratorTests/ConsoleAppBuilderTest.cs b/tests/ConsoleAppFramework.GeneratorTests/ConsoleAppBuilderTest.cs index a6a9bf5..3faa8a9 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/ConsoleAppBuilderTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/ConsoleAppBuilderTest.cs @@ -241,6 +241,29 @@ public void Do() verifier.Execute(code, "nomunomu", "yeah"); } + + [Fact] + public void CommandAttrAlias() + { + var code = """ +var builder = ConsoleApp.Create(); +builder.Add(); +builder.Run(args); + +public class MyClass() +{ + [Command("nomunomu")] + [Command("nomunomu2")] + public void Do() + { + Console.Write("yeah"); + } +} +"""; + + verifier.Execute(code, "nomunomu", "yeah"); + verifier.Execute(code, "nomunomu2", "yeah"); + } } From 2b80cf4171df6a555f1dd9c7cabd4b4a03e7a1af Mon Sep 17 00:00:00 2001 From: Mikkel Rasmussen Date: Sun, 16 Jun 2024 02:50:37 +0200 Subject: [PATCH 2/5] Added command alias help test --- .../HelpTest.cs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs index 48778a2..32e1d35 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs @@ -305,4 +305,54 @@ hello my world. """); } + + [Fact] + public void CommandAlias() + { + var code = """ +var app = ConsoleApp.Create(); +app.Add(); +app.Run(args); + +public class MyClass +{ + /// + /// hello my world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. + [Command("hello-world")] + [Command("hello-our-world")] + public void HelloWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } +} +"""; + + + verifier.Execute(code, args: "--help", expected: """ +Usage: [command] [-h|--help] [--version] + +Commands: + hello-world, hello-our-world hello my world. + +"""); + + + var expectedCommandHelp = """ +Usage: hello-world [arguments...] [options...] [-h|--help] [--version] + +hello my world. + +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +"""; + verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); + verifier.Execute(code, args: "hello-our-world --help", expected: expectedCommandHelp); + } } From d0277fef5e059a0fd4fcc274b0db3a50ee8e07c5 Mon Sep 17 00:00:00 2001 From: Mikkel Rasmussen Date: Sun, 16 Jun 2024 10:54:03 +0200 Subject: [PATCH 3/5] Added CommandAttribute to classes, added current failing test CommandAliasWithRoot --- .../ConsoleAppGenerator.cs | 2 +- src/ConsoleAppFramework/Parser.cs | 21 +++- .../HelpTest.cs | 99 +++++++++++++++++++ 3 files changed, 118 insertions(+), 4 deletions(-) diff --git a/src/ConsoleAppFramework/ConsoleAppGenerator.cs b/src/ConsoleAppFramework/ConsoleAppGenerator.cs index 666cf73..99e31de 100644 --- a/src/ConsoleAppFramework/ConsoleAppGenerator.cs +++ b/src/ConsoleAppFramework/ConsoleAppGenerator.cs @@ -144,7 +144,7 @@ internal sealed class ArgumentAttribute : Attribute { } -[AttributeUsage(AttributeTargets.Method, AllowMultiple = true, Inherited = false)] +[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = false)] internal sealed class CommandAttribute : Attribute { public string Command { get; } diff --git a/src/ConsoleAppFramework/Parser.cs b/src/ConsoleAppFramework/Parser.cs index f545a38..6714aaf 100644 --- a/src/ConsoleAppFramework/Parser.cs +++ b/src/ConsoleAppFramework/Parser.cs @@ -104,6 +104,15 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod return []; } + List aliases = []; + if (type.TypeKind.HasFlag(TypeKind.Class)) + { + foreach (var item in type.GetAttributes().Where(x => x.AttributeClass?.Name == "CommandAttribute")) + { + aliases.Add((item.ConstructorArguments[0].Value as string)!); + } + } + var hasIDisposable = type.AllInterfaces.Any(x => SymbolEqualityComparer.Default.Equals(x, wellKnownTypes.IDisposable)); var hasIAsyncDisposable = type.AllInterfaces.Any(x => SymbolEqualityComparer.Default.Equals(x, wellKnownTypes.IAsyncDisposable)); @@ -143,7 +152,11 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod string commandName; var commandAttributes = x.GetAttributes().Where(x => x.AttributeClass?.Name == "CommandAttribute"); var firstCommandAttribute = commandAttributes.FirstOrDefault(); - if (firstCommandAttribute != null) + if (aliases.Count > 0) + { + commandName = aliases[0]; + } + else if (firstCommandAttribute != null) { commandName = (x.GetAttributes()[0].ConstructorArguments[0].Value as string)!; } @@ -155,11 +168,13 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod var command = ParseFromMethodSymbol(x, false, (commandPath == null) ? commandName : $"{commandPath.Trim()} {commandName}", typeFilters); if (command == null) return null; - if (commandAttributes.Count() > 1) + if (commandAttributes.Count() > 0) { - command.Aliases = commandAttributes.Skip(1).Select((ca, i) => (x.GetAttributes()[1 + i].ConstructorArguments[0].Value as string)!).ToArray(); + aliases.AddRange(commandAttributes.Select((ca, i) => (x.GetAttributes()[i].ConstructorArguments[0].Value as string)!)); } + command.Aliases = aliases.Select(a => NameConverter.ToKebabCase(a)).Distinct().Where(s => !s.Equals(commandName)).ToArray(); + command.CommandMethodInfo = methodInfoBase with { MethodName = x.Name }; return command; }) diff --git a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs index 32e1d35..eaff80b 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs @@ -345,6 +345,105 @@ public void HelloWorld([Argument]int boo, string fooBar) hello my world. +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +"""; + verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); + verifier.Execute(code, args: "hello-our-world --help", expected: expectedCommandHelp); + } + + [Fact] + public void CommandAliasWithRoot() + { + var code = """ +var app = ConsoleApp.Create(); +app.Add(); +app.Run(args); + +public class MyClass +{ + /// + /// hello my world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. + [Command("")] + [Command("hello-world")] + public void HelloWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } +} +"""; + + + verifier.Execute(code, args: "--help", expected: """ +Usage: [command] [-h|--help] [--version] + +Commands: + hello-world hello my world. + +"""); + + + var expectedCommandHelp = """ +Usage: hello-world [arguments...] [options...] [-h|--help] [--version] + +hello my world. + +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +"""; + verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); + } + + [Fact] + public void CommandAliasClassAttribute() + { + var code = """ +var app = ConsoleApp.Create(); +app.Add(); +app.Run(args); + +[Command("hello-world")] +public class MyClass +{ + /// + /// hello my world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. + [Command("hello-our-world")] + public void HelloWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } +} +"""; + + + verifier.Execute(code, args: "--help", expected: """ +Usage: [command] [-h|--help] [--version] + +Commands: + hello-world, hello-our-world hello my world. + +"""); + + + var expectedCommandHelp = """ +Usage: hello-world [arguments...] [options...] [-h|--help] [--version] + +hello my world. + Arguments: [0] my boo is not boo. From 0aff408a6635fe3335a6a4f67d7560fb2bb2f0a8 Mon Sep 17 00:00:00 2001 From: Mikkel Rasmussen Date: Sun, 16 Jun 2024 11:26:21 +0200 Subject: [PATCH 4/5] Updated testcase --- tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs index eaff80b..e97a2f5 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs @@ -389,7 +389,6 @@ public void HelloWorld([Argument]int boo, string fooBar) """); - var expectedCommandHelp = """ Usage: hello-world [arguments...] [options...] [-h|--help] [--version] @@ -403,6 +402,7 @@ hello my world. """; verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); + verifier.Execute(code, args: "", expected: expectedCommandHelp); } [Fact] From e7851c1d2ed1a3dd9ed9ff2413b69c8ca0df96bd Mon Sep 17 00:00:00 2001 From: Mikkel Rasmussen Date: Sun, 16 Jun 2024 15:35:06 +0200 Subject: [PATCH 5/5] Fixed code so tests passes --- src/ConsoleAppFramework/Emitter.cs | 13 +- src/ConsoleAppFramework/Parser.cs | 22 +-- .../HelpTest.cs | 149 ++++++++++++++++-- 3 files changed, 162 insertions(+), 22 deletions(-) diff --git a/src/ConsoleAppFramework/Emitter.cs b/src/ConsoleAppFramework/Emitter.cs index 9f4c9ff..ce5c2b0 100644 --- a/src/ConsoleAppFramework/Emitter.cs +++ b/src/ConsoleAppFramework/Emitter.cs @@ -1,4 +1,5 @@ using Microsoft.CodeAnalysis; +using System.Linq; using System.Reflection.Metadata; namespace ConsoleAppFramework; @@ -447,7 +448,7 @@ void EmitRunBody(ILookup groupedCommands, int depth, bool { var leafCommand = groupedCommands[""].FirstOrDefault(); IDisposable? ifBlcok = null; - if (leafCommand is not null) + if (leafCommand is not null && !leafCommand.Command.Name.Equals("")) { // Add or-ing of aliases foreach (var alias in leafCommand.Command.Aliases) @@ -457,8 +458,6 @@ void EmitRunBody(ILookup groupedCommands, int depth, bool } if (!(groupedCommands.Count == 1 && leafCommand != null)) { - - ifBlcok = sb.BeginBlock($"if (args.Length == {depth})"); } EmitLeafCommand(leafCommand); @@ -472,9 +471,15 @@ void EmitRunBody(ILookup groupedCommands, int depth, bool return; } + IEnumerable> aliases = []; + if (leafCommand?.Command.Aliases.Length > 0) + { + aliases = leafCommand.Command.Aliases.SelectMany(lc => commandIds.GroupBy(a => lc)); + } + using (sb.BeginBlock($"switch (args[{depth}])")) { - foreach (var commands in groupedCommands.Where(x => x.Key != "")) + foreach (var commands in groupedCommands.Where(x => x.Key != "").Concat(aliases)) { using (sb.BeginIndent($"case \"{commands.Key}\":")) { diff --git a/src/ConsoleAppFramework/Parser.cs b/src/ConsoleAppFramework/Parser.cs index 6714aaf..f37bc91 100644 --- a/src/ConsoleAppFramework/Parser.cs +++ b/src/ConsoleAppFramework/Parser.cs @@ -104,12 +104,12 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod return []; } - List aliases = []; + List rootAliases = []; if (type.TypeKind.HasFlag(TypeKind.Class)) { foreach (var item in type.GetAttributes().Where(x => x.AttributeClass?.Name == "CommandAttribute")) { - aliases.Add((item.ConstructorArguments[0].Value as string)!); + rootAliases.Add((item.ConstructorArguments[0].Value as string)!); } } @@ -152,11 +152,7 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod string commandName; var commandAttributes = x.GetAttributes().Where(x => x.AttributeClass?.Name == "CommandAttribute"); var firstCommandAttribute = commandAttributes.FirstOrDefault(); - if (aliases.Count > 0) - { - commandName = aliases[0]; - } - else if (firstCommandAttribute != null) + if (firstCommandAttribute != null) { commandName = (x.GetAttributes()[0].ConstructorArguments[0].Value as string)!; } @@ -168,12 +164,20 @@ internal class Parser(DiagnosticReporter context, InvocationExpressionSyntax nod var command = ParseFromMethodSymbol(x, false, (commandPath == null) ? commandName : $"{commandPath.Trim()} {commandName}", typeFilters); if (command == null) return null; + List methodAliases = []; if (commandAttributes.Count() > 0) { - aliases.AddRange(commandAttributes.Select((ca, i) => (x.GetAttributes()[i].ConstructorArguments[0].Value as string)!)); + methodAliases.AddRange(commandAttributes.Select((ca, i) => (x.GetAttributes()[i].ConstructorArguments[0].Value as string)!)); } - command.Aliases = aliases.Select(a => NameConverter.ToKebabCase(a)).Distinct().Where(s => !s.Equals(commandName)).ToArray(); + if (command.IsRootCommand) + { + command.Aliases = methodAliases.Concat(rootAliases).Select(a => NameConverter.ToKebabCase(a)).Distinct().Where(s => !s.Equals(commandName)).ToArray(); + } + else + { + command.Aliases = methodAliases.Select(a => NameConverter.ToKebabCase(a)).Distinct().Where(s => !s.Equals(commandName)).ToArray(); + } command.CommandMethodInfo = methodInfoBase with { MethodName = x.Name }; return command; diff --git a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs index e97a2f5..3cf938c 100644 --- a/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs +++ b/tests/ConsoleAppFramework.GeneratorTests/HelpTest.cs @@ -322,7 +322,7 @@ public class MyClass /// -b, my boo is not boo. /// -f|-fb, my foo is not bar. [Command("hello-world")] - [Command("hello-our-world")] + [Command("hw")] public void HelloWorld([Argument]int boo, string fooBar) { Console.Write("Hello World! " + fooBar); @@ -335,7 +335,7 @@ public void HelloWorld([Argument]int boo, string fooBar) Usage: [command] [-h|--help] [--version] Commands: - hello-world, hello-our-world hello my world. + hello-world, hw hello my world. """); @@ -353,7 +353,7 @@ hello my world. """; verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); - verifier.Execute(code, args: "hello-our-world --help", expected: expectedCommandHelp); + verifier.Execute(code, args: "hw --help", expected: expectedCommandHelp); } [Fact] @@ -373,6 +373,72 @@ public class MyClass /// -f|-fb, my foo is not bar. [Command("")] [Command("hello-world")] + [Command("hello-world2")] + [Command("hello-world3")] + public void HelloWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } + + [Command("hello-our-world")] + public void HelloOurWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } +} +"""; + + var expectedCommandHelp = """ +Usage: [command] [arguments...] [options...] [-h|--help] [--version] + +hello my world. + +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +Commands: + hello-our-world + +"""; + + var expectedSubCommand = """ +Usage: [arguments...] [options...] [-h|--help] [--version] + +hello my world. + +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +"""; + + verifier.Execute(code, args: "--help", expected: expectedCommandHelp); + verifier.Execute(code, args: "", expected: expectedSubCommand); + verifier.Execute(code, args: "hello-world --help", expected: expectedSubCommand); + } + + [Fact] + public void CommandAliasClassAttribute() + { + var code = """ +var app = ConsoleApp.Create(); +app.Add(); +app.Run(args); + +[Command("hello-world")] +public class MyClass +{ + /// + /// hello my world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. + [Command("hw")] public void HelloWorld([Argument]int boo, string fooBar) { Console.Write("Hello World! " + fooBar); @@ -385,12 +451,13 @@ public void HelloWorld([Argument]int boo, string fooBar) Usage: [command] [-h|--help] [--version] Commands: - hello-world hello my world. + hw hello my world. """); + var expectedCommandHelp = """ -Usage: hello-world [arguments...] [options...] [-h|--help] [--version] +Usage: hw [arguments...] [options...] [-h|--help] [--version] hello my world. @@ -401,12 +468,11 @@ hello my world. -f|-fb|--foo-bar my foo is not bar. (Required) """; - verifier.Execute(code, args: "hello-world --help", expected: expectedCommandHelp); - verifier.Execute(code, args: "", expected: expectedCommandHelp); + verifier.Execute(code, args: "hw --help", expected: expectedCommandHelp); } [Fact] - public void CommandAliasClassAttribute() + public void CommandClassAttributeAndRootMethod() { var code = """ var app = ConsoleApp.Create(); @@ -421,11 +487,75 @@ public class MyClass /// /// -b, my boo is not boo. /// -f|-fb, my foo is not bar. + [Command("")] + public void HelloWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } + + /// + /// hello our world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. [Command("hello-our-world")] + public void HelloOurWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } +} +"""; + + + verifier.Execute(code, args: "--help", expected: """ +Usage: [command] [arguments...] [options...] [-h|--help] [--version] + +hello my world. + +Arguments: + [0] my boo is not boo. + +Options: + -f|-fb|--foo-bar my foo is not bar. (Required) + +Commands: + hello-our-world hello our world. + +"""); + } + + [Fact] + public void CommandClassAttributeAndMethodCoalescing() + { + var code = """ +var app = ConsoleApp.Create(); +app.Add(); +app.Run(args); + +[Command("hello-world")] +[Command("hw")] +public class MyClass +{ + /// + /// hello my world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. public void HelloWorld([Argument]int boo, string fooBar) { Console.Write("Hello World! " + fooBar); } + + /// + /// hello our world. + /// + /// -b, my boo is not boo. + /// -f|-fb, my foo is not bar. + [Command("hello-our-world")] + public void HelloOurWorld([Argument]int boo, string fooBar) + { + Console.Write("Hello World! " + fooBar); + } } """; @@ -434,7 +564,8 @@ public void HelloWorld([Argument]int boo, string fooBar) Usage: [command] [-h|--help] [--version] Commands: - hello-world, hello-our-world hello my world. + hello-our-world hello our world. + hello-world, hw hello my world. """);