Skip to content

Commit 9652972

Browse files
committed
Version 2.6.8
- Added: `Command` and `RootCommand` properties to `CliParser` class: ```c# var parser = Cli.GetParser<RootCliCommand>(); //Access the command that is used to parse the command line input. var command = parser.Command; //Access the root of the command that is used to parse the command line input. //If parser.Command is already a root command, this will be same instance. //If it's a sub-command, it will be the root of that sub-command. var rootCommand = parser.RootCommand; ``` - Fixed: SourceGenerator sometimes triggers a concurrency exception. Use `ConcurrentDictionary` for `GenerationCounts`. - Fixed: Do not add auto name or alias for root commands as it can unnecessarily conflict with children.
1 parent 685078b commit 9652972

File tree

124 files changed

+1190
-1232
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

124 files changed

+1190
-1232
lines changed

src/Directory.Build.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
<PropertyGroup>
44
<!-- https://learn.microsoft.com/en-us/dotnet/core/project-sdk/msbuild-props#generateassemblyinfo -->
5-
<VersionPrefix>2.6.7</VersionPrefix>
5+
<VersionPrefix>2.6.8</VersionPrefix>
66
<Product>DotMake Command-Line</Product>
77
<Company>DotMake</Company>
88
<!-- Copyright is also used for NuGet metadata -->

src/DotMake.CommandLine.SourceGeneration/CliIncrementalGenerator.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
using Microsoft.CodeAnalysis;
22
using System;
3-
using System.Collections.Generic;
3+
using System.Collections.Concurrent;
44
using System.Linq;
55
using System.Reflection;
66
using Microsoft.CodeAnalysis.CSharp;
@@ -21,7 +21,7 @@ public class CliIncrementalGenerator : IIncrementalGenerator
2121
private static readonly Type Type = typeof(CliIncrementalGenerator);
2222
private static readonly string Version = Type.Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
2323
private static readonly string RoslynVersion = typeof(IIncrementalGenerator).Assembly.GetCustomAttribute<AssemblyFileVersionAttribute>()?.Version;
24-
private static readonly Dictionary<string, int> GenerationCounts = new(StringComparer.OrdinalIgnoreCase);
24+
private static readonly ConcurrentDictionary<string, int> GenerationCounts = new(StringComparer.OrdinalIgnoreCase);
2525

2626
public void Initialize(IncrementalGeneratorInitializationContext initializationContext)
2727
{
@@ -249,7 +249,7 @@ private static void AppendGeneratedCodeHeader(CodeStringBuilder sb, string gener
249249
if (GenerationCounts.TryGetValue(generationKey, out var generationCount))
250250
GenerationCounts[generationKey] = ++generationCount;
251251
else
252-
GenerationCounts.Add(generationKey, ++generationCount);
252+
GenerationCounts.TryAdd(generationKey, ++generationCount);
253253

254254
sb.AppendLine("// <auto-generated />");
255255
sb.AppendLine($"// Generated by {Type.Namespace} v{Version}");

src/DotMake.CommandLine.SourceGeneration/Outputs/CliArgumentOutput.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,14 @@ public CliArgumentOutput(CliArgumentInput input)
2929

3030
public void AppendCSharpCreateString(CodeStringBuilder sb, string varName, string varNamer, string varBindingContext)
3131
{
32-
var varNameParameter = $"{varName}Name";
33-
3432
sb.AppendLine($"// Argument for '{Input.Symbol.Name}' property");
3533

36-
if (Input.AttributeArguments.TryGetValue(nameof(CliArgumentAttribute.Name), out var nameValue))
37-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetArgumentName(\"{Input.Symbol.Name}\", \"{nameValue}\");");
38-
else
39-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetArgumentName(\"{Input.Symbol.Name}\");");
40-
4134
using (sb.AppendParamsBlockStart($"var {varName} = new {ArgumentClassNamespace}.{ArgumentClassName}<{Input.Symbol.Type.ToReferenceString()}>"))
4235
{
43-
sb.AppendLine($"{varNameParameter}");
36+
if (Input.AttributeArguments.TryGetValue(nameof(CliArgumentAttribute.Name), out var nameValue))
37+
sb.AppendLine($"{varNamer}.GetArgumentName(\"{Input.Symbol.Name}\", \"{nameValue}\")");
38+
else
39+
sb.AppendLine($"{varNamer}.GetArgumentName(\"{Input.Symbol.Name}\")");
4440
}
4541
using (sb.AppendBlockStart(null, ";"))
4642
{

src/DotMake.CommandLine.SourceGeneration/Outputs/CliCommandOutput.cs

Lines changed: 7 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -381,21 +381,20 @@ Instead we will use Bind method to get cached definition instance and call GetCo
381381

382382
public void AppendCSharpCreateString(CodeStringBuilder sb, string varName, string varRootName, string varNamer)
383383
{
384-
var varNameParameter = $"{varName}Name";
385-
386384
sb.AppendLine($"// Command for '{Input.Symbol.Name}' class");
387385
//sb.AppendLine($"// Parent tree: '{string.Join(" -> ", Input.ParentTree.Select(p=> p.Symbol))}'");
388386

389-
if (Input.AttributeArguments.TryGetValue(nameof(CliCommandAttribute.Name), out var nameValue))
390-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetCommandName(\"{Input.Symbol.Name}\", \"{nameValue}\");");
391-
else
392-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetCommandName(\"{Input.Symbol.Name}\");");
393-
394387
using (sb.AppendBlockStart($"var {varName} = IsRoot", null, null, null))
395388
{
396389
//Cannot set name for a RootCommand, it's the executable name by default
397390
sb.AppendLine($"? new {CommandClassNamespace}.{RootCommandClassName}()");
398-
sb.AppendLine($": new {CommandClassNamespace}.{CommandClassName}({varNameParameter});");
391+
using (sb.AppendParamsBlockStart($": new {CommandClassNamespace}.{CommandClassName}", ";"))
392+
{
393+
if (Input.AttributeArguments.TryGetValue(nameof(CliCommandAttribute.Name), out var nameValue))
394+
sb.AppendLine($"{varNamer}.GetCommandName(\"{Input.Symbol.Name}\", \"{nameValue}\")");
395+
else
396+
sb.AppendLine($"{varNamer}.GetCommandName(\"{Input.Symbol.Name}\")");
397+
}
399398
}
400399

401400
sb.AppendLine($"var {varRootName} = {varName} as {CommandClassNamespace}.{RootCommandClassName};");

src/DotMake.CommandLine.SourceGeneration/Outputs/CliDirectiveOutput.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -25,18 +25,14 @@ public CliDirectiveOutput(CliDirectiveInput input)
2525

2626
public void AppendCSharpCreateString(CodeStringBuilder sb, string varName, string varNamer)
2727
{
28-
var varNameParameter = $"{varName}Name";
29-
3028
sb.AppendLine($"// Directive for '{Input.Symbol.Name}' property");
3129

32-
if (Input.AttributeArguments.TryGetValue(nameof(CliDirectiveAttribute.Name), out var nameValue))
33-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetDirectiveName(\"{Input.Symbol.Name}\", \"{nameValue}\");");
34-
else
35-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetDirectiveName(\"{Input.Symbol.Name}\");");
36-
3730
using (sb.AppendParamsBlockStart($"var {varName} = new {DirectiveClassNamespace}.{DirectiveClassName}"))
3831
{
39-
sb.AppendLine($"{varNameParameter}");
32+
if (Input.AttributeArguments.TryGetValue(nameof(CliDirectiveAttribute.Name), out var nameValue))
33+
sb.AppendLine($"{varNamer}.GetDirectiveName(\"{Input.Symbol.Name}\", \"{nameValue}\")");
34+
else
35+
sb.AppendLine($"{varNamer}.GetDirectiveName(\"{Input.Symbol.Name}\")");
4036
}
4137
using (sb.AppendBlockStart(null, ";"))
4238
{

src/DotMake.CommandLine.SourceGeneration/Outputs/CliOptionOutput.cs

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -29,18 +29,14 @@ public CliOptionOutput(CliOptionInput input)
2929

3030
public void AppendCSharpCreateString(CodeStringBuilder sb, string varName, string varNamer, string varBindingContext)
3131
{
32-
var varNameParameter = $"{varName}Name";
33-
3432
sb.AppendLine($"// Option for '{Input.Symbol.Name}' property");
3533

36-
if (Input.AttributeArguments.TryGetValue(nameof(CliOptionAttribute.Name), out var nameValue))
37-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetOptionName(\"{Input.Symbol.Name}\", \"{nameValue}\");");
38-
else
39-
sb.AppendLine($"var {varNameParameter} = {varNamer}.GetOptionName(\"{Input.Symbol.Name}\");");
40-
4134
using (sb.AppendParamsBlockStart($"var {varName} = new {OptionClassNamespace}.{OptionClassName}<{Input.Symbol.Type.ToReferenceString()}>"))
4235
{
43-
sb.AppendLine($"{varNameParameter}");
36+
if (Input.AttributeArguments.TryGetValue(nameof(CliOptionAttribute.Name), out var nameValue))
37+
sb.AppendLine($"{varNamer}.GetOptionName(\"{Input.Symbol.Name}\", \"{nameValue}\")");
38+
else
39+
sb.AppendLine($"{varNamer}.GetOptionName(\"{Input.Symbol.Name}\")");
4440
}
4541
using (sb.AppendBlockStart(null, ";"))
4642
{

src/DotMake.CommandLine/CliNamer.cs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,10 @@ public void AddShortFormAlias(Command command, string symbolName, string specifi
250250
}
251251
else if (shortFormAutoGenerate.HasFlag(CliNameAutoGenerate.Commands))
252252
{
253+
//Do not add auto alias for root commands as it can unnecessarily conflict with children
254+
if (command is RootCommand)
255+
return;
256+
253257
var baseName = CliStringUtil.StripSuffixes(symbolName.Trim(), CommandSuffixes);
254258

255259
var shortForm = FindAutoShortForm(baseName, false);
@@ -347,7 +351,7 @@ private void AddTokenOrThrow(string token, TokenType tokenType, string symbolNam
347351
var existingTokenType = tuple.Item1;
348352
var existingSymbolName = tuple.Item2;
349353
throw new Exception(
350-
$"{tokenType} '{token}' for '{symbolName}' conflicts with {existingTokenType} for '{existingSymbolName}'!"
354+
$"{tokenType} \"{token}\" for \"{symbolName}\" conflicts with {existingTokenType} for \"{existingSymbolName}\" !"
351355
);
352356
}
353357

@@ -358,7 +362,7 @@ private void AddTokenOrThrow(string token, TokenType tokenType, string symbolNam
358362
var existingTokenType = tuple2.Item1;
359363
var existingSymbolName = tuple2.Item2;
360364
throw new Exception(
361-
$"{tokenType} '{token}' for '{symbolName}' conflicts with parent {existingTokenType} for '{existingSymbolName}'!"
365+
$"{tokenType} \"{token}\" for \"{symbolName}\" conflicts with parent {existingTokenType} for \"{existingSymbolName}\" !"
362366
);
363367
}
364368

src/DotMake.CommandLine/CliParser.cs

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,8 @@ internal CliParser(Type definitionType, CliSettings settings = null)
2929
settings ??= new CliSettings();
3030
this.settings = settings;
3131

32+
Command = command;
33+
3234
configuration = new CommandLineConfiguration(command)
3335
{
3436
EnablePosixBundling = settings.EnablePosixBundling,
@@ -43,6 +45,8 @@ internal CliParser(Type definitionType, CliSettings settings = null)
4345

4446
if (rootCommand != null)
4547
{
48+
RootCommand = rootCommand;
49+
4650
//CliRootCommand constructor already adds HelpOption and VersionOption so remove them
4751
foreach (var option in rootCommand.Options.Where(option => option is HelpOption or VersionOption).ToArray())
4852
rootCommand.Options.Remove(option);
@@ -88,6 +92,17 @@ internal CliParser(Type definitionType, CliSettings settings = null)
8892
}
8993
}
9094

95+
/// <summary>
96+
/// The command that is used to parse the command line input.
97+
/// </summary>
98+
public Command Command { get; }
99+
100+
/// <summary>
101+
/// The root of the command that is used to parse the command line input.
102+
/// If <see cref="Command"/> is already a root command, this will be same instance.
103+
/// If it's a sub-command, it will be the root of that sub-command.
104+
/// </summary>
105+
public RootCommand RootCommand { get; }
91106

92107
/// <summary>
93108
/// Parses a command line string array and returns the parse result for the indicated command.

src/DotMake.CommandLine/DotMake.CommandLine.csproj

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,22 @@
2121
<PackageTags>command-line CLI console System.CommandLine declarative attributes parsing command argument option generator</PackageTags>
2222
<PackageReleaseNotes>
2323
<![CDATA[
24-
- Fixed: Naming related settings were not being inherited correctly after level-2 sub-commands or above.
24+
- Added: `Command` and `RootCommand` properties to `CliParser` class:
25+
```c#
26+
var parser = Cli.GetParser<RootCliCommand>();
27+
28+
//Access the command that is used to parse the command line input.
29+
var command = parser.Command;
30+
31+
//Access the root of the command that is used to parse the command line input.
32+
//If parser.Command is already a root command, this will be same instance.
33+
//If it's a sub-command, it will be the root of that sub-command.
34+
var rootCommand = parser.RootCommand;
35+
```
2536
26-
- Fixed: NullReferenceException when invoking help output for a sub-command that had completions.
37+
- Fixed: SourceGenerator sometimes triggers a concurrency exception. Use `ConcurrentDictionary` for `GenerationCounts`.
38+
39+
- Fixed: Do not add auto name or alias for root commands as it can unnecessarily conflict with children.
2740
]]>
2841
</PackageReleaseNotes>
2942
</PropertyGroup>

src/TestApp/GeneratedFiles/net472/DotMake.CommandLine.SourceGeneration/DotMake.CommandLine.SourceGeneration.CliIncrementalGenerator/(ModuleInitializerAttribute).g.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
// <auto-generated />
2-
// Generated by DotMake.CommandLine.SourceGeneration v2.6.7.0
3-
// Roslyn (Microsoft.CodeAnalysis) v4.1400.25.27905
2+
// Generated by DotMake.CommandLine.SourceGeneration v2.6.8.0
3+
// Roslyn (Microsoft.CodeAnalysis) v4.1400.25.35903
44
// Generation: 1
55

66
#if !NET5_0_OR_GREATER

0 commit comments

Comments
 (0)