Skip to content

Commit 48faa68

Browse files
authored
expose HelpOption and HelpAction, move help configuration to the action (#2102)
* expose HelpOption and HelpAction, move help configuration to the action * address code review feedback: make Option.Action virtual and lazy
1 parent 02a776b commit 48faa68

File tree

21 files changed

+228
-286
lines changed

21 files changed

+228
-286
lines changed

src/System.CommandLine.ApiCompatibility.Tests/ApiCompatibilityApprovalTests.System_CommandLine_api_is_not_changed.approved.txt

Lines changed: 15 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -72,8 +72,6 @@ System.CommandLine
7272
public CommandLineBuilder UseExceptionHandler(System.Func<System.Exception,System.CommandLine.Invocation.InvocationContext,System.Int32> onException = null, System.Int32 errorExitCode = 1)
7373
public CommandLineBuilder UseHelp(System.Nullable<System.Int32> maxWidth = null)
7474
public CommandLineBuilder UseHelp(System.String name, System.String[] helpAliases)
75-
public CommandLineBuilder UseHelp(System.Action<System.CommandLine.Help.HelpContext> customize, System.Nullable<System.Int32> maxWidth = null)
76-
public CommandLineBuilder UseHelpBuilder(System.Func<System.CommandLine.Invocation.InvocationContext,System.CommandLine.Help.HelpBuilder> getHelpBuilder)
7775
public CommandLineBuilder UseParseDirective(System.Int32 errorExitCode = 1)
7876
public CommandLineBuilder UseParseErrorReporting(System.Int32 errorExitCode = 1)
7977
public CommandLineBuilder UseSuggestDirective()
@@ -83,7 +81,7 @@ System.CommandLine
8381
public CommandLineBuilder UseVersionOption(System.String name, System.String[] aliases)
8482
public class CommandLineConfiguration
8583
public static CommandLineBuilder CreateBuilder(Command rootCommand)
86-
.ctor(Command command, System.Boolean enablePosixBundling = True, System.Boolean enableTokenReplacement = True, System.Func<System.CommandLine.Invocation.InvocationContext,System.CommandLine.Help.HelpBuilder> helpBuilderFactory = null, System.CommandLine.Parsing.TryReplaceToken tokenReplacer = null)
84+
.ctor(Command command, System.Boolean enablePosixBundling = True, System.Boolean enableTokenReplacement = True, System.CommandLine.Parsing.TryReplaceToken tokenReplacer = null)
8785
public System.Collections.Generic.IReadOnlyList<Directive> Directives { get; }
8886
public System.Boolean EnablePosixBundling { get; }
8987
public System.Boolean EnableTokenReplacement { get; }
@@ -106,6 +104,7 @@ System.CommandLine
106104
public System.Collections.Generic.IEnumerable<System.CommandLine.Completions.CompletionItem> GetCompletions(System.CommandLine.Completions.CompletionContext context)
107105
public class EnvironmentVariablesDirective : Directive
108106
.ctor()
107+
public CliAction Action { get; set; }
109108
public interface IConsole : System.CommandLine.IO.IStandardError, System.CommandLine.IO.IStandardIn, System.CommandLine.IO.IStandardOut
110109
public abstract class Option : Symbol, System.CommandLine.Binding.IValueDescriptor
111110
public CliAction Action { get; set; }
@@ -133,6 +132,7 @@ System.CommandLine
133132
public static Option<T> AcceptExistingOnly<T>(this Option<T> option)
134133
public class ParseDirective : Directive
135134
.ctor(System.Int32 errorExitCode = 1)
135+
public CliAction Action { get; set; }
136136
public class ParseResult
137137
public CliAction Action { get; }
138138
public System.CommandLine.Parsing.CommandResult CommandResult { get; }
@@ -160,6 +160,7 @@ System.CommandLine
160160
.ctor(System.String description = )
161161
public class SuggestDirective : Directive
162162
.ctor()
163+
public CliAction Action { get; set; }
163164
public abstract class Symbol
164165
public System.String Description { get; set; }
165166
public System.Boolean IsHidden { get; set; }
@@ -196,6 +197,11 @@ System.CommandLine.Completions
196197
public TextCompletionContext AtCursorPosition(System.Int32 position)
197198
public class TokenCompletionContext : CompletionContext
198199
System.CommandLine.Help
200+
public class HelpAction : System.CommandLine.CliAction
201+
.ctor()
202+
public HelpBuilder Builder { get; set; }
203+
public System.Int32 Invoke(System.CommandLine.Invocation.InvocationContext context)
204+
public System.Threading.Tasks.Task<System.Int32> InvokeAsync(System.CommandLine.Invocation.InvocationContext context, System.Threading.CancellationToken cancellationToken = null)
199205
public class HelpBuilder
200206
.ctor(System.Int32 maxWidth = 2147483647)
201207
public System.Int32 MaxWidth { get; }
@@ -226,6 +232,12 @@ System.CommandLine.Help
226232
public HelpBuilder HelpBuilder { get; }
227233
public System.IO.TextWriter Output { get; }
228234
public System.CommandLine.ParseResult ParseResult { get; }
235+
public class HelpOption : System.CommandLine.Option<System.Boolean>, System.CommandLine.Binding.IValueDescriptor
236+
.ctor(System.String name, System.String[] aliases)
237+
.ctor()
238+
public System.CommandLine.CliAction Action { get; set; }
239+
public System.Boolean Equals(System.Object obj)
240+
public System.Int32 GetHashCode()
229241
public class TwoColumnHelpRow, System.IEquatable<TwoColumnHelpRow>
230242
.ctor(System.String firstColumnText, System.String secondColumnText)
231243
public System.String FirstColumnText { get; }
@@ -237,7 +249,6 @@ System.CommandLine.Invocation
237249
public class InvocationContext
238250
.ctor(System.CommandLine.ParseResult parseResult, System.CommandLine.IConsole console = null)
239251
public System.CommandLine.IConsole Console { get; set; }
240-
public System.CommandLine.Help.HelpBuilder HelpBuilder { get; }
241252
public System.CommandLine.ParseResult ParseResult { get; set; }
242253
public T GetValue<T>(Option<T> option)
243254
public T GetValue<T>(Argument<T> argument)

src/System.CommandLine.Generator.Tests/GeneratedCommandHandlerTests.cs

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -135,30 +135,26 @@ public async Task Can_generate_handler_with_well_know_parameters_types()
135135
InvocationContext? boundInvocationContext = null;
136136
IConsole? boundConsole = null;
137137
ParseResult? boundParseResult = null;
138-
HelpBuilder? boundHelpBuilder = null;
139138

140139
void Execute(
141140
InvocationContext invocationContext,
142141
IConsole console,
143-
ParseResult parseResult,
144-
HelpBuilder helpBuilder)
142+
ParseResult parseResult)
145143
{
146144
boundInvocationContext = invocationContext;
147145
boundConsole = console;
148146
boundParseResult = parseResult;
149-
boundHelpBuilder = helpBuilder;
150147
}
151148

152149
var command = new Command("command");
153150

154-
command.SetHandler<Action<InvocationContext, IConsole, ParseResult, HelpBuilder>>(Execute);
151+
command.SetHandler<Action<InvocationContext, IConsole, ParseResult>>(Execute);
155152

156153
await command.InvokeAsync("command", _console);
157154

158155
boundInvocationContext.Should().NotBeNull();
159156
boundConsole.Should().Be(_console);
160157
boundParseResult.Should().NotBeNull();
161-
boundHelpBuilder.Should().NotBeNull();
162158
}
163159

164160
[Fact]

src/System.CommandLine.Generator/Parameters/HelpBuilderParameter.cs

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

src/System.CommandLine.Generator/WellKnownTypes.cs

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -51,12 +51,6 @@ internal bool TryGet(ISymbol symbol, out Parameter? parameter)
5151
return true;
5252
}
5353

54-
if (Comparer.Equals(HelpBuilder, symbol))
55-
{
56-
parameter = new HelpBuilderParameter(HelpBuilder);
57-
return true;
58-
}
59-
6054
if (symbol.MetadataName == "System.CommandLine.Binding.BindingContext" && symbol is INamedTypeSymbol bindingContext)
6155
{
6256
parameter = new BindingContextParameter(bindingContext);

src/System.CommandLine.NamingConventionBinder/ServiceProvider.cs

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,6 @@ internal ServiceProvider(BindingContext bindingContext)
1818
{
1919
[typeof(ParseResult)] = _ => bindingContext.ParseResult,
2020
[typeof(IConsole)] = _ => bindingContext.Console,
21-
[typeof(HelpBuilder)] = _ => bindingContext.ParseResult.Configuration.HelpBuilderFactory(bindingContext.InvocationContext),
2221
[typeof(BindingContext)] = _ => bindingContext
2322
};
2423
}

src/System.CommandLine.Tests/Help/HelpBuilderTests.Customization.cs

Lines changed: 35 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -92,17 +92,21 @@ public void Option_can_customize_first_column_text_based_on_parse_result()
9292
ctx.Command.Equals(commandA)
9393
? optionAFirstColumnText
9494
: optionBFirstColumnText);
95-
var config = new CommandLineBuilder(command)
96-
.UseDefaults()
97-
.UseHelpBuilder(_ => helpBuilder)
98-
.Build();
95+
command.Options.Add(new HelpOption()
96+
{
97+
Action = new HelpAction()
98+
{
99+
Builder = helpBuilder
100+
}
101+
});
99102

100103
var console = new TestConsole();
101-
config.Invoke("root a -h", console);
104+
var config = CommandLineConfiguration.CreateBuilder(command).Build();
105+
command.Parse("root a -h", config).Invoke(console);
102106
console.Out.ToString().Should().Contain(optionAFirstColumnText);
103107

104108
console = new TestConsole();
105-
config.Invoke("root b -h", console);
109+
command.Parse("root b -h", config).Invoke(console);
106110
console.Out.ToString().Should().Contain(optionBFirstColumnText);
107111
}
108112

@@ -130,10 +134,15 @@ public void Option_can_customize_second_column_text_based_on_parse_result()
130134
ctx.Command.Equals(commandA)
131135
? optionADescription
132136
: optionBDescription);
137+
command.Options.Add(new HelpOption()
138+
{
139+
Action = new HelpAction()
140+
{
141+
Builder = helpBuilder
142+
}
143+
});
133144

134145
var config = new CommandLineBuilder(command)
135-
.UseDefaults()
136-
.UseHelpBuilder(_ => helpBuilder)
137146
.Build();
138147

139148
var console = new TestConsole();
@@ -253,14 +262,16 @@ public void Option_can_fallback_to_default_when_customizing(bool conditionA, boo
253262
firstColumnText: ctx => conditionA ? "custom 1st" : HelpBuilder.Default.GetOptionUsageLabel(option),
254263
secondColumnText: ctx => conditionB ? "custom 2nd" : option.Description ?? string.Empty);
255264

256-
257-
var config = new CommandLineBuilder(command)
258-
.UseDefaults()
259-
.UseHelpBuilder(_ => helpBuilder)
260-
.Build();
265+
command.Options.Add(new HelpOption()
266+
{
267+
Action = new HelpAction()
268+
{
269+
Builder = helpBuilder
270+
}
271+
});
261272

262273
var console = new TestConsole();
263-
config.Invoke("test -h", console);
274+
command.Parse("test -h", new CommandLineBuilder(command).Build()).Invoke(console);
264275
console.Out.ToString().Should().MatchRegex(expected);
265276
}
266277

@@ -295,13 +306,18 @@ public void Argument_can_fallback_to_default_when_customizing(
295306
defaultValue: ctx => conditionC ? "custom def" : HelpBuilder.Default.GetArgumentDefaultValue(argument));
296307

297308

298-
var config = new CommandLineBuilder(command)
299-
.UseDefaults()
300-
.UseHelpBuilder(_ => helpBuilder)
301-
.Build();
309+
var config = new CommandLineBuilder(command).Build();
310+
311+
command.Options.Add(new HelpOption()
312+
{
313+
Action = new HelpAction()
314+
{
315+
Builder = helpBuilder
316+
}
317+
});
302318

303319
var console = new TestConsole();
304-
config.Invoke("test -h", console);
320+
command.Parse("test -h", config).Invoke(console);
305321
console.Out.ToString().Should().MatchRegex(expected);
306322
}
307323
}

src/System.CommandLine.Tests/Invocation/InvocationPipelineTests.cs

Lines changed: 4 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -126,61 +126,12 @@ public void When_command_handler_throws_then_Invoke_does_not_handle_the_exceptio
126126
}
127127

128128
[Fact]
129-
public async Task When_no_help_builder_is_specified_it_uses_default_implementation()
129+
public void When_no_help_builder_is_specified_it_uses_default_implementation()
130130
{
131-
bool handlerWasCalled = false;
131+
HelpOption helpOption = new();
132132

133-
var command = new Command("help-command");
134-
command.SetAction((context, cancellationToken) =>
135-
{
136-
handlerWasCalled = true;
137-
context.HelpBuilder.Should().NotBeNull();
138-
return Task.FromResult(0);
139-
});
140-
141-
var config = new CommandLineBuilder(new RootCommand
142-
{
143-
command
144-
})
145-
.Build();
146-
147-
await config.InvokeAsync("help-command", new TestConsole());
148-
149-
handlerWasCalled.Should().BeTrue();
150-
}
151-
152-
[Fact]
153-
public async Task When_help_builder_factory_is_specified_it_is_used_to_create_the_help_builder()
154-
{
155-
bool handlerWasCalled = false;
156-
bool factoryWasCalled = false;
157-
158-
HelpBuilder createdHelpBuilder = null;
159-
160-
var command = new Command("help-command");
161-
command.SetAction((context, cancellationToken) =>
162-
{
163-
handlerWasCalled = true;
164-
context.HelpBuilder.Should().Be(createdHelpBuilder);
165-
createdHelpBuilder.Should().NotBeNull();
166-
return Task.FromResult(0);
167-
});
168-
169-
var config = new CommandLineBuilder(new RootCommand
170-
{
171-
command
172-
})
173-
.UseHelpBuilder(context =>
174-
{
175-
factoryWasCalled = true;
176-
return createdHelpBuilder = new HelpBuilder();
177-
})
178-
.Build();
179-
180-
await config.InvokeAsync("help-command", new TestConsole());
181-
182-
handlerWasCalled.Should().BeTrue();
183-
factoryWasCalled.Should().BeTrue();
133+
helpOption.Action.Should().NotBeNull();
134+
(helpOption.Action as HelpAction).Builder.Should().NotBeNull();
184135
}
185136
}
186137
}

0 commit comments

Comments
 (0)