Skip to content

Commit c1ef7f7

Browse files
committed
Added parent command accessor support
- Added parent command accessor support. Sub-commands can get a reference to the parent command by adding a property of the parent command type. - ParseResultExtensions.Bind improvement: Binding will be done only once per definition class, so calling this method consecutively for the same definition class will return the cached result.
1 parent 5e6b904 commit c1ef7f7

File tree

220 files changed

+2655
-1480
lines changed

Some content is hidden

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

220 files changed

+2655
-1480
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>1.8.5</VersionPrefix>
5+
<VersionPrefix>1.8.6</VersionPrefix>
66
<Product>DotMake Command-Line</Product>
77
<Company>DotMake</Company>
88
<!-- Copyright is also used for NuGet metadata -->

src/DotMake.CommandLine.Shared/Attributes/CliArgumentAttribute.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ namespace DotMake.CommandLine
2828
/// <example>
2929
/// <inheritdoc cref="Cli" path="/example/code[@id='gettingStartedDelegate']" />
3030
/// <inheritdoc cref="Cli" path="/example/code[@id='gettingStartedClass']" />
31+
/// <code source="..\TestApp\Commands\ParentCommandAccessorCliCommand.cs" region="ParentCommandAccessorCliCommand" language="cs" />
3132
/// </example>
3233
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
3334
public class CliArgumentAttribute : Attribute

src/DotMake.CommandLine.Shared/Attributes/CliOptionAttribute.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,10 @@ namespace DotMake.CommandLine
3232
/// </para>
3333
/// </summary>
3434
/// <example>
35+
/// <inheritdoc cref="Cli" path="/example/code[@id='gettingStartedDelegate']" />
3536
/// <inheritdoc cref="Cli" path="/example/code[@id='gettingStartedClass']" />
3637
/// <code source="..\TestApp\Commands\RecursiveOptionCliCommand.cs" region="RecursiveOptionCliCommand" language="cs" />
38+
/// <code source="..\TestApp\Commands\ParentCommandAccessorCliCommand.cs" region="ParentCommandAccessorCliCommand" language="cs" />
3739
/// </example>
3840
[AttributeUsage(AttributeTargets.Property | AttributeTargets.Parameter)]
3941
public class CliOptionAttribute : Attribute

src/DotMake.CommandLine.SourceGeneration/CliCommandInfo.cs

Lines changed: 78 additions & 64 deletions
Large diffs are not rendered by default.

src/DotMake.CommandLine.SourceGeneration/CliParentCommandRefInfo.cs renamed to src/DotMake.CommandLine.SourceGeneration/CliParentCommandAccessorInfo.cs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -3,25 +3,19 @@
33

44
namespace DotMake.CommandLine.SourceGeneration
55
{
6-
public class CliParentCommandRefInfo : CliSymbolInfo, IEquatable<CliParentCommandRefInfo>
6+
public class CliParentCommandAccessorInfo : CliSymbolInfo, IEquatable<CliParentCommandAccessorInfo>
77
{
8-
public const string DiagnosticName = "CLI parent command reference";
8+
public const string DiagnosticName = "CLI parent command accessor";
99

10-
public CliParentCommandRefInfo(IPropertySymbol symbol, SyntaxNode syntaxNode, SemanticModel semanticModel,
11-
int parentTreeIndex, CliCommandSettings parentCommandSettings) : base(symbol, syntaxNode, semanticModel)
10+
public CliParentCommandAccessorInfo(ISymbol symbol, SyntaxNode syntaxNode, SemanticModel semanticModel)
11+
: base(symbol, syntaxNode, semanticModel)
1212
{
13-
ParentTreeIndex = parentTreeIndex;
14-
ParentCommandSettings = parentCommandSettings;
13+
Symbol = (IPropertySymbol)symbol;
1514

1615
Analyze();
17-
18-
if (HasProblem)
19-
return;
2016
}
2117

22-
public int ParentTreeIndex { get; }
23-
public CliCommandSettings ParentCommandSettings { get; }
24-
public new IPropertySymbol Symbol => (IPropertySymbol)base.Symbol;
18+
public new IPropertySymbol Symbol;
2519

2620
private void Analyze()
2721
{
@@ -40,7 +34,7 @@ private void Analyze()
4034
}
4135
}
4236

43-
public bool Equals(CliParentCommandRefInfo other)
37+
public bool Equals(CliParentCommandAccessorInfo other)
4438
{
4539
return base.Equals(other);
4640
}

src/DotMake.CommandLine.SourceGeneration/SymbolExtensions.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ public static ITypeSymbol GetUnderlyingTypeIfNullable(this ITypeSymbol type)
101101
}
102102

103103
/// <summary>
104-
/// Gets all own and inherited members (not distinct).
104+
/// Gets all own and then inherited members (not distinct).
105105
/// </summary>
106106
public static IEnumerable<ISymbol> GetAllMembers(this ITypeSymbol type)
107107
{

src/DotMake.CommandLine/Cli.cs

Lines changed: 0 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -339,21 +339,6 @@ public static ParseResult Parse<TDefinition>(string commandLine, CliSettings set
339339
return configuration.Parse(commandLine);
340340
}
341341

342-
/// <summary>
343-
/// Creates a new instance of the command definition class and binds/populates the properties from the parse result.
344-
/// If the command line input is not for the indicated definition class (e.g. it's for a sub-command but not for the indicated root command or vice versa),
345-
/// then the returned instance would be empty (i.e. properties would have default values).
346-
/// </summary>
347-
/// <typeparam name="TDefinition"><inheritdoc cref="GetConfiguration{TDefinition}" path="/typeparam[@name='TDefinition']/node()" /></typeparam>
348-
/// <param name="parseResult">The parse result.</param>
349-
/// <returns>The parsed value or a configured default.</returns>
350-
public static TDefinition Bind<TDefinition>(this ParseResult parseResult)
351-
{
352-
var commandBuilder = CliCommandBuilder.Get<TDefinition>();
353-
354-
return (TDefinition)commandBuilder.Bind(parseResult);
355-
}
356-
357342
private static string[] FixArgs(string[] args)
358343
{
359344
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)

src/DotMake.CommandLine/CliBindContext.cs

Lines changed: 0 additions & 52 deletions
This file was deleted.

src/DotMake.CommandLine/CliCommandBuilder.cs

Lines changed: 18 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using System;
2+
using System.Collections.Concurrent;
23
using System.Collections.Generic;
34
using System.CommandLine;
45
using System.CommandLine.Parsing;
@@ -14,9 +15,11 @@ namespace DotMake.CommandLine
1415
public abstract class CliCommandBuilder
1516
{
1617
/// <summary>
17-
/// A delegate which is set by the source generator to be called from <see cref="Bind(CliBindContext)"/> method.
18+
/// A delegate which is set by the source generator to be called from <see cref="Bind(ParseResult)"/> method.
1819
/// </summary>
19-
protected Func<CliBindContext, object> BindFunc;
20+
protected Func<ParseResult, object> Binder;
21+
22+
private readonly ConcurrentDictionary<ParseResult, object> bindResults = new();
2023

2124
/// <summary>
2225
/// Initializes a new instance of the <see cref="CliCommandBuilder" /> class.
@@ -67,28 +70,26 @@ protected CliCommandBuilder()
6770
public abstract CliCommand Build();
6871

6972
/// <summary>
70-
/// Creates a new instance of the definition class and binds/populates the properties from the parse result.
73+
/// Creates a new instance of the command definition class and binds/populates the properties from the parse result.
74+
/// Note that binding will be done only once, so calling this method consecutively will return the cached result.
75+
/// <para>
76+
/// If the command line input is not for this definition class (e.g. it's for a sub-command but not for
77+
/// this root command or vice versa), then the returned instance would be empty (i.e. properties would have default values).
78+
/// </para>
7179
/// </summary>
7280
/// <param name="parseResult">A parse result describing the outcome of the parse operation.</param>
7381
/// <returns>An instance of the definition class whose properties were bound/populated from the parse result.</returns>
7482
public object Bind(ParseResult parseResult)
7583
{
76-
return Bind(new CliBindContext(parseResult));
77-
}
78-
79-
/// <summary>
80-
/// Creates a new instance of the definition class and binds/populates the properties from the parse result.
81-
/// </summary>
82-
/// <param name="cliBindContext">A <see cref="CliBindContext"/> instance to use for the binding operation.</param>
83-
/// <returns>An instance of the definition class whose properties were bound/populated from the parse result.</returns>
84-
public object Bind(CliBindContext cliBindContext)
85-
{
86-
if (BindFunc == null)
87-
throw new Exception("Ensure Build method is called first.");
84+
return bindResults.GetOrAdd(parseResult, pr =>
85+
{
86+
if (Binder == null)
87+
throw new Exception("Binder is not set. Ensure Build method is called first.");
8888

89-
return BindFunc(cliBindContext);
89+
return Binder(pr);
90+
});
9091
}
91-
92+
9293
/// <summary>
9394
/// Gets the command builders that are nested/external children of this command builder.
9495
/// </summary>

src/DotMake.CommandLine/DotMake.CommandLine.csproj

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,9 @@
2020
<Description>Declarative syntax for System.CommandLine via attributes for easy, fast, strongly-typed (no reflection) usage. Includes a source generator which automagically converts your classes to CLI commands and properties to CLI options or CLI arguments.</Description>
2121
<PackageTags>command-line CLI console System.CommandLine declarative attributes parsing command argument option generator</PackageTags>
2222
<PackageReleaseNotes>
23-
- Added theme support. CliSettings.Theme property can be set to predefined themes Red, DarkRed, Green, DarkGreen, Blue, DarkBlue
24-
or a custom CliTheme. These color and formatting option are mainly used by the help output.
23+
- Added parent command accessor support. Sub-commands can get a reference to the parent command by adding a property of the parent command type.
24+
- ParseResultExtensions.Bind improvement: Binding will be done only once per definition class, so calling this method consecutively for
25+
the same definition class will return the cached result.
2526
</PackageReleaseNotes>
2627
</PropertyGroup>
2728

0 commit comments

Comments
 (0)