Skip to content

Commit 3646b05

Browse files
WIP: before merging HelpFormatting and HelpConfiguration
1 parent 7058640 commit 3646b05

39 files changed

+2336
-897
lines changed

System.CommandLine.sln

Lines changed: 20 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -62,12 +62,15 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.Generato
6262
EndProject
6363
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.Generator.Tests", "src\System.CommandLine.Generator.Tests\System.CommandLine.Generator.Tests.csproj", "{70B98293-2F69-4262-AADD-D3EEE12046A8}"
6464
EndProject
65-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.CommandLine.Generator.CommandHandler", "src\System.CommandLine.Generator.CommandHandler\System.CommandLine.Generator.CommandHandler.csproj", "{591EF370-7AD7-4624-8B9D-FD15010CA657}"
66-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.CommandLine.NamingConventionBinder", "src\System.CommandLine.NamingConventionBinder\System.CommandLine.NamingConventionBinder.csproj", "{10DFE204-B027-49DA-BD77-08ECA18DD357}"
65+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.Generator.CommandHandler", "src\System.CommandLine.Generator.CommandHandler\System.CommandLine.Generator.CommandHandler.csproj", "{591EF370-7AD7-4624-8B9D-FD15010CA657}"
6766
EndProject
68-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.CommandLine.NamingConventionBinder.Tests", "src\System.CommandLine.NamingConventionBinder.Tests\System.CommandLine.NamingConventionBinder.Tests.csproj", "{789A05F2-5EF6-4FE8-9609-4706207E047E}"
67+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.NamingConventionBinder", "src\System.CommandLine.NamingConventionBinder\System.CommandLine.NamingConventionBinder.csproj", "{10DFE204-B027-49DA-BD77-08ECA18DD357}"
6968
EndProject
70-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "System.CommandLine.ApiCompatibility.Tests", "src\System.CommandLine.ApiCompatibility.Tests\System.CommandLine.ApiCompatibility.Tests.csproj", "{A54EE328-D456-4BAF-A180-84E77E6409AC}"
69+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.NamingConventionBinder.Tests", "src\System.CommandLine.NamingConventionBinder.Tests\System.CommandLine.NamingConventionBinder.Tests.csproj", "{789A05F2-5EF6-4FE8-9609-4706207E047E}"
70+
EndProject
71+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.ApiCompatibility.Tests", "src\System.CommandLine.ApiCompatibility.Tests\System.CommandLine.ApiCompatibility.Tests.csproj", "{A54EE328-D456-4BAF-A180-84E77E6409AC}"
72+
EndProject
73+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "System.CommandLine.Help", "src\System.CommandLine.Help\System.CommandLine.Help.csproj", "{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}"
7174
EndProject
7275
Global
7376
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@@ -319,6 +322,18 @@ Global
319322
{A54EE328-D456-4BAF-A180-84E77E6409AC}.Release|x64.Build.0 = Release|Any CPU
320323
{A54EE328-D456-4BAF-A180-84E77E6409AC}.Release|x86.ActiveCfg = Release|Any CPU
321324
{A54EE328-D456-4BAF-A180-84E77E6409AC}.Release|x86.Build.0 = Release|Any CPU
325+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
326+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|Any CPU.Build.0 = Debug|Any CPU
327+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|x64.ActiveCfg = Debug|Any CPU
328+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|x64.Build.0 = Debug|Any CPU
329+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|x86.ActiveCfg = Debug|Any CPU
330+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Debug|x86.Build.0 = Debug|Any CPU
331+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|Any CPU.ActiveCfg = Release|Any CPU
332+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|Any CPU.Build.0 = Release|Any CPU
333+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|x64.ActiveCfg = Release|Any CPU
334+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|x64.Build.0 = Release|Any CPU
335+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|x86.ActiveCfg = Release|Any CPU
336+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D}.Release|x86.Build.0 = Release|Any CPU
322337
EndGlobalSection
323338
GlobalSection(SolutionProperties) = preSolution
324339
HideSolutionNode = FALSE
@@ -344,6 +359,7 @@ Global
344359
{10DFE204-B027-49DA-BD77-08ECA18DD357} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45}
345360
{789A05F2-5EF6-4FE8-9609-4706207E047E} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45}
346361
{A54EE328-D456-4BAF-A180-84E77E6409AC} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45}
362+
{ACC54C40-9CFC-40ED-9AE1-58C87CB0924D} = {E5B1EC71-0FC4-4FAA-9C65-32D5016FBC45}
347363
EndGlobalSection
348364
GlobalSection(ExtensibilityGlobals) = postSolution
349365
SolutionGuid = {5C159F93-800B-49E7-9905-EE09F8B8434A}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<IsPackable>true</IsPackable>
5+
<PackageId>System.CommandLine.Help.Common</PackageId>
6+
<TargetFrameworks>$(TargetFrameworkForNETSDK);netstandard2.0</TargetFrameworks>
7+
<Nullable>enable</Nullable>
8+
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
9+
<LangVersion>10</LangVersion>
10+
<Description>
11+
This package provides the default Help API used by System.CommandLine and System.CommandLineHelp
12+
</Description>
13+
<GenerateDocumentationFile>true</GenerateDocumentationFile>
14+
<EnableSingleFileAnalyzer>true</EnableSingleFileAnalyzer>
15+
</PropertyGroup>
16+
17+
<PropertyGroup Condition="'$(TargetFramework)' == '$(TargetFrameworkForNETSDK)'">
18+
<IsTrimmable>true</IsTrimmable>
19+
<EnableTrimAnalyzer>true</EnableTrimAnalyzer>
20+
</PropertyGroup>
21+
22+
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
23+
<DebugType>portable</DebugType>
24+
</PropertyGroup>
25+
26+
<ItemGroup>
27+
<Compile Include="..\System.Diagnostics.CodeAnalysis.cs" Link="System.Diagnostics.CodeAnalysis\System.Diagnostics.CodeAnalysis.cs" />
28+
</ItemGroup>
29+
30+
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
31+
<PackageReference Include="System.Memory" Version="4.5.4" />
32+
</ItemGroup>
33+
34+
<ItemGroup>
35+
<Compile Update="Properties\Resources.Designer.cs">
36+
<DesignTime>True</DesignTime>
37+
<AutoGen>True</AutoGen>
38+
<DependentUpon>Resources.resx</DependentUpon>
39+
</Compile>
40+
</ItemGroup>
41+
42+
<ItemGroup>
43+
<EmbeddedResource Update="Properties\Resources.resx" GenerateSource="true">
44+
<Generator>ResXFileCodeGenerator</Generator>
45+
<LastGenOutput>Resources.Designer.cs</LastGenOutput>
46+
</EmbeddedResource>
47+
</ItemGroup>
48+
49+
<ItemGroup>
50+
<InternalsVisibleTo Include="System.CommandLine.NamingConventionBinder" />
51+
</ItemGroup>
52+
53+
</Project>
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace System.CommandLine.Help
5+
{
6+
public class CliHelpArguments : CliHelpSection
7+
{
8+
public CliHelpArguments(CliHelpConfiguration helpConfiguration, HelpContext helpContext)
9+
: base(helpConfiguration, helpContext, LocalizationResources.HelpArgumentsTitle())
10+
{
11+
}
12+
13+
public override IEnumerable<string>? GetBody(CliSymbol current)
14+
{
15+
if (current is null)
16+
{ return null; }
17+
18+
var selfAndParents = current.Parents
19+
.Prepend(current)
20+
.OfType<CliCommand>()
21+
.Reverse();
22+
23+
var table = selfAndParents
24+
.SelectMany(cmd => cmd.Arguments.Where(a => !a.Hidden))
25+
.Select(a => GetTwoColumnRow(a))
26+
.Distinct();
27+
28+
return table is null
29+
? null
30+
: CliHelpHelpers.WriteTwoColumns(table, HelpContext.MaxWidth, Indent);
31+
}
32+
33+
public override IEnumerable<string>? GetClosing(CliSymbol current)
34+
=> null;
35+
36+
private TwoColumnHelpRow? GetTwoColumnRow(CliArgument argument)
37+
{
38+
_ = argument ?? throw new ArgumentNullException(nameof(argument));
39+
40+
string firstColumnText = SymbolOutput.GetUsage(argument);
41+
string secondColumnText = GetSecondColumnText(argument);
42+
43+
return new TwoColumnHelpRow(firstColumnText, secondColumnText);
44+
45+
string GetSecondColumnText(CliArgument argument)
46+
{
47+
var symbolDescription = argument.Description ?? string.Empty;
48+
49+
var defaultValueDescription = SymbolOutput.GetDefaultValueText(argument, true);
50+
if (string.IsNullOrEmpty(defaultValueDescription))
51+
{
52+
return $"{symbolDescription}".Trim();
53+
}
54+
else
55+
{
56+
return $"{symbolDescription} [{defaultValueDescription}]".Trim();
57+
}
58+
59+
60+
}
61+
}
62+
63+
///// <summary>
64+
///// Gets the usage title for an argument (for example: <c>&lt;value&gt;</c>, typically used in the first column text in the arguments usage section, or within the synopsis.
65+
///// </summary>
66+
//private static string GetArgumentUsageLabel(CliArgument argument)
67+
//{
68+
// // Argument.HelpName is always first choice
69+
// if (!string.IsNullOrWhiteSpace(argument.HelpName))
70+
// {
71+
// return $"<{argument.HelpName}>";
72+
// }
73+
74+
// if (argument.ValueType == typeof(bool))
75+
// {
76+
// return string.Empty;
77+
// }
78+
79+
// var completionItems = argument.GetCompletions(CompletionContext.Empty);
80+
// if (completionItems.Any())
81+
// {
82+
83+
// IEnumerable<string> completions = completionItems
84+
// .Select(item => item.Label);
85+
86+
// string joined = string.Join("|", completions);
87+
88+
// if (!string.IsNullOrEmpty(joined))
89+
// {
90+
// return $"<{joined}>";
91+
// }
92+
// }
93+
94+
// return $"<{argument.Name}>";
95+
//}
96+
97+
98+
//private string GetArgumentDefaultValue(
99+
// CliArgument argument,
100+
// bool displayArgumentName)
101+
//{
102+
// string? displayedDefaultValue = GetArgumentDefaultValue(argument);
103+
104+
// return string.IsNullOrWhiteSpace(displayedDefaultValue)
105+
// ? ""
106+
// : $"{GetLabel(argument, displayArgumentName)}: {displayedDefaultValue}";
107+
108+
// static string GetLabel(CliArgument argument, bool displayArgumentName) =>
109+
// displayArgumentName
110+
// ? LocalizationResources.HelpArgumentDefaultValueLabel()
111+
// : argument.Name;
112+
//}
113+
114+
//public static string GetArgumentDefaultValue(CliSymbol symbol)
115+
//{
116+
// var defaultValue = symbol switch
117+
// {
118+
// CliArgument argument => argument.GetHelpDefaultValue(),
119+
// CliOption option => option.GetHelpDefaultValue(),
120+
// _ => null
121+
// };
122+
// if (defaultValue is not null)
123+
// {
124+
// if (defaultValue is IEnumerable enumerable and not string)
125+
// {
126+
// return string.Join("|", enumerable.OfType<object>().ToArray());
127+
// }
128+
// else
129+
// {
130+
// return defaultValue.ToString() ?? "";
131+
// }
132+
// }
133+
// return string.Empty;
134+
//}
135+
}
136+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
using System.Collections.Generic;
2+
using System.Linq;
3+
4+
namespace System.CommandLine.Help
5+
{
6+
public class CliHelpBuilder : IHelpBuilder
7+
{
8+
/// <param name="maxWidth">The maximum width in characters after which help output is wrapped.</param>
9+
public CliHelpBuilder(CliHelpConfiguration? configuration = null)
10+
{
11+
HelpConfiguration = configuration ?? new CliHelpConfiguration();
12+
}
13+
14+
public CliHelpConfiguration HelpConfiguration { get; }
15+
16+
/// <summary>
17+
/// Writes help output for the specified command.
18+
/// </summary>
19+
public virtual void Write(HelpContext context)
20+
{
21+
_ = context ?? throw new ArgumentNullException(nameof(context));
22+
23+
// KAD: Consider this: If the user explicitly typed a hidden command, should they be able to get help for deprecated or preview features?
24+
//if (context.Command.Hidden)
25+
//{
26+
// return;
27+
//}
28+
29+
foreach (var section in HelpConfiguration.CurrentLayout.GetSections(context))
30+
{
31+
IEnumerable<string> output = new List<string>();
32+
var body = section.GetBody(context.Command);
33+
if ((body == null || !body.Any()) && !section.EmitHeaderOnEmptyBody)
34+
{ continue; }
35+
36+
var opening = section.GetOpening(context.Command);
37+
var closing = section.GetClosing(context.Command);
38+
39+
if (opening is not null)
40+
{
41+
if (section.EmitHeaderOnEmptyBody || (body is not null && body.Any()))
42+
{
43+
output = output.Concat(opening);
44+
}
45+
}
46+
if (body is not null && body.Any())
47+
{
48+
output = output.Concat(body);
49+
}
50+
51+
if (closing is not null)
52+
{
53+
if (section.EmitHeaderOnEmptyBody || (body is not null && body.Any()))
54+
{
55+
output = output.Concat(closing);
56+
}
57+
}
58+
59+
if (output.Any())
60+
{
61+
CliHelpHelpers.WriteLines(output, context);
62+
CliHelpHelpers.WriteBlankLine(context);
63+
}
64+
}
65+
66+
context.Output.WriteLine();
67+
context.Output.WriteLine();
68+
}
69+
}
70+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
namespace System.CommandLine.Help
2+
{
3+
public class CliHelpConfiguration
4+
{
5+
private CliHelpLayout? currentLayout;
6+
7+
public CliHelpConfiguration(CliHelpFormatting? helpFormatting = null,
8+
CliHelpSymbolOutput? symbolOutput = null, int maxWidth = int.MaxValue)
9+
{
10+
HelpFormatting = helpFormatting ?? new CliHelpFormatting();
11+
SymbolOutput = symbolOutput ?? new CliHelpSymbolOutput(this );
12+
}
13+
14+
//public Dictionary<string, CliHelpLayout> Layouts => new();
15+
public CliHelpLayout CurrentLayout
16+
{
17+
get => currentLayout ?? new CliHelpLayout(this);
18+
set => currentLayout = value;
19+
}
20+
public CliHelpFormatting HelpFormatting { get; set; }
21+
public CliHelpSymbolOutput SymbolOutput { get; }
22+
}
23+
}
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
namespace System.CommandLine.Help;
2+
3+
public class CliHelpFormatting
4+
{
5+
public CliHelpFormatting(int indent = 0, int maxWidth = 0)
6+
{
7+
Indent = indent == 0 ? 2 : indent;
8+
MaxWidth = maxWidth;
9+
}
10+
11+
public int Indent { get; }
12+
public int MaxWidth { get; }
13+
14+
/// <summary>
15+
/// Dispalys the heading. This can be overridden to provide custom formatting
16+
/// </summary>
17+
/// <param name="heading"></param>
18+
public virtual string Heading(string? heading)
19+
=> heading = heading ?? string.Empty;
20+
21+
}

0 commit comments

Comments
 (0)