mirror of
https://github.com/Tyrrrz/CliFx.git
synced 2025-10-25 15:19:17 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
595805255a | ||
|
|
65eaa912cf | ||
|
|
038f48b78e | ||
|
|
d7460244b7 | ||
|
|
02766868fc | ||
|
|
8d7d25a144 | ||
|
|
17ded54e24 | ||
|
|
54a4c32ddf | ||
|
|
6d46e82145 | ||
|
|
fd4a2a18fe | ||
|
|
bfe99d620e | ||
|
|
c5a111207f | ||
|
|
544945c0e6 | ||
|
|
c616cdd750 | ||
|
|
d3c396956d | ||
|
|
d0cbbc6d9a |
14
Changelog.md
14
Changelog.md
@@ -1,3 +1,17 @@
|
|||||||
|
### v2.0.3 (09-Apr-2021)
|
||||||
|
|
||||||
|
- Improved help text by showing valid values for non-scalar enum parameters and options. (Thanks [@Robert Dailey](https://github.com/rcdailey))
|
||||||
|
|
||||||
|
### v2.0.2 (31-Mar-2021)
|
||||||
|
|
||||||
|
- Fixed an issue where having a transitive reference to CliFx sometimes resulted in `SystemConsoleShouldBeAvoidedAnalyzer` throwing `NullReferenceException` during build.
|
||||||
|
- Fixed some documentation typos and inconsistencies.
|
||||||
|
|
||||||
|
### v2.0.1 (24-Mar-2021)
|
||||||
|
|
||||||
|
- Fixed an issue where some exceptions with async stack traces generated on .NET 3.1 or earlier were not parsed and formatted correctly.
|
||||||
|
- Fixed an issue where help text applied slightly incorrect formatting when displaying choices for enum-based parameters and properties.
|
||||||
|
|
||||||
### v2.0 (21-Mar-2021)
|
### v2.0 (21-Mar-2021)
|
||||||
|
|
||||||
> Note: this major release includes many breaking changes.
|
> Note: this major release includes many breaking changes.
|
||||||
|
|||||||
@@ -104,5 +104,24 @@ public class MyCommand : ICommand
|
|||||||
// Act & assert
|
// Act & assert
|
||||||
Analyzer.Should().NotProduceDiagnostics(code);
|
Analyzer.Should().NotProduceDiagnostics(code);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void Analyzer_does_not_report_an_error_if_a_command_does_not_access_SystemConsole()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
// language=cs
|
||||||
|
const string code = @"
|
||||||
|
[Command]
|
||||||
|
public class MyCommand : ICommand
|
||||||
|
{
|
||||||
|
public ValueTask ExecuteAsync(IConsole console)
|
||||||
|
{
|
||||||
|
return default;
|
||||||
|
}
|
||||||
|
}";
|
||||||
|
|
||||||
|
// Act & assert
|
||||||
|
Analyzer.Should().NotProduceDiagnostics(code);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -22,6 +22,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (property.ContainingType.IsAbstract)
|
if (property.ContainingType.IsAbstract)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
var option = CommandOptionSymbol.TryResolve(property);
|
var option = CommandOptionSymbol.TryResolve(property);
|
||||||
if (option is null)
|
if (option is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
var option = CommandOptionSymbol.TryResolve(property);
|
var option = CommandOptionSymbol.TryResolve(property);
|
||||||
if (option is null)
|
if (option is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (property.ContainingType.IsAbstract)
|
if (property.ContainingType.IsAbstract)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (IsScalar(property.Type))
|
if (IsScalar(property.Type))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -29,6 +29,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
if (!CommandParameterSymbol.IsParameterProperty(property))
|
if (!CommandParameterSymbol.IsParameterProperty(property))
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||||
if (parameter is null)
|
if (parameter is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -22,6 +22,9 @@ namespace CliFx.Analyzers
|
|||||||
PropertyDeclarationSyntax propertyDeclaration,
|
PropertyDeclarationSyntax propertyDeclaration,
|
||||||
IPropertySymbol property)
|
IPropertySymbol property)
|
||||||
{
|
{
|
||||||
|
if (property.ContainingType is null)
|
||||||
|
return;
|
||||||
|
|
||||||
var parameter = CommandParameterSymbol.TryResolve(property);
|
var parameter = CommandParameterSymbol.TryResolve(property);
|
||||||
if (parameter is null)
|
if (parameter is null)
|
||||||
return;
|
return;
|
||||||
|
|||||||
@@ -27,9 +27,9 @@ namespace CliFx.Analyzers
|
|||||||
|
|
||||||
while (currentNode is MemberAccessExpressionSyntax memberAccess)
|
while (currentNode is MemberAccessExpressionSyntax memberAccess)
|
||||||
{
|
{
|
||||||
var symbol = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol;
|
var member = context.SemanticModel.GetSymbolInfo(memberAccess).Symbol;
|
||||||
|
|
||||||
if (symbol is not null && symbol.ContainingType.DisplayNameMatches("System.Console"))
|
if (member?.ContainingType?.DisplayNameMatches("System.Console") == true)
|
||||||
{
|
{
|
||||||
return memberAccess;
|
return memberAccess;
|
||||||
}
|
}
|
||||||
@@ -53,7 +53,8 @@ namespace CliFx.Analyzers
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
// Check if IConsole is available in scope as an alternative to System.Console
|
// Check if IConsole is available in scope as an alternative to System.Console
|
||||||
var isConsoleInterfaceAvailable = context.Node
|
var isConsoleInterfaceAvailable = context
|
||||||
|
.Node
|
||||||
.Ancestors()
|
.Ancestors()
|
||||||
.OfType<MethodDeclarationSyntax>()
|
.OfType<MethodDeclarationSyntax>()
|
||||||
.SelectMany(m => m.ParameterList.Parameters)
|
.SelectMany(m => m.ParameterList.Parameters)
|
||||||
|
|||||||
@@ -9,11 +9,16 @@ namespace CliFx.Analyzers.Utils.Extensions
|
|||||||
internal static class RoslynExtensions
|
internal static class RoslynExtensions
|
||||||
{
|
{
|
||||||
public static bool DisplayNameMatches(this ISymbol symbol, string name) =>
|
public static bool DisplayNameMatches(this ISymbol symbol, string name) =>
|
||||||
string.Equals(symbol.ToDisplayString(), name, StringComparison.Ordinal);
|
string.Equals(
|
||||||
|
// Fully qualified name, without `global::`
|
||||||
|
symbol.ToDisplayString(SymbolDisplayFormat.CSharpErrorMessageFormat),
|
||||||
|
name,
|
||||||
|
StringComparison.Ordinal
|
||||||
|
);
|
||||||
|
|
||||||
public static void HandleClassDeclaration(
|
public static void HandleClassDeclaration(
|
||||||
this AnalysisContext analysisContext,
|
this AnalysisContext analysisContext,
|
||||||
Action<SyntaxNodeAnalysisContext, ClassDeclarationSyntax, ITypeSymbol> handler)
|
Action<SyntaxNodeAnalysisContext, ClassDeclarationSyntax, ITypeSymbol> analyze)
|
||||||
{
|
{
|
||||||
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
||||||
{
|
{
|
||||||
@@ -24,13 +29,13 @@ namespace CliFx.Analyzers.Utils.Extensions
|
|||||||
if (type is null)
|
if (type is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handler(ctx, classDeclaration, type);
|
analyze(ctx, classDeclaration, type);
|
||||||
}, SyntaxKind.ClassDeclaration);
|
}, SyntaxKind.ClassDeclaration);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static void HandlePropertyDeclaration(
|
public static void HandlePropertyDeclaration(
|
||||||
this AnalysisContext analysisContext,
|
this AnalysisContext analysisContext,
|
||||||
Action<SyntaxNodeAnalysisContext, PropertyDeclarationSyntax, IPropertySymbol> handler)
|
Action<SyntaxNodeAnalysisContext, PropertyDeclarationSyntax, IPropertySymbol> analyze)
|
||||||
{
|
{
|
||||||
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
analysisContext.RegisterSyntaxNodeAction(ctx =>
|
||||||
{
|
{
|
||||||
@@ -41,7 +46,7 @@ namespace CliFx.Analyzers.Utils.Extensions
|
|||||||
if (property is null)
|
if (property is null)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
handler(ctx, propertyDeclaration, property);
|
analyze(ctx, propertyDeclaration, property);
|
||||||
}, SyntaxKind.PropertyDeclaration);
|
}, SyntaxKind.PropertyDeclaration);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -577,6 +577,51 @@ public class Command : ICommand
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public async Task Help_text_shows_all_valid_values_for_non_scalar_enum_parameters_and_options()
|
||||||
|
{
|
||||||
|
// Arrange
|
||||||
|
var commandType = DynamicCommandBuilder.Compile(
|
||||||
|
// language=cs
|
||||||
|
@"
|
||||||
|
public enum CustomEnum { One, Two, Three }
|
||||||
|
|
||||||
|
[Command]
|
||||||
|
public class Command : ICommand
|
||||||
|
{
|
||||||
|
[CommandParameter(0)]
|
||||||
|
public List<CustomEnum> Foo { get; set; }
|
||||||
|
|
||||||
|
[CommandOption(""bar"")]
|
||||||
|
public List<CustomEnum> Bar { get; set; }
|
||||||
|
|
||||||
|
public ValueTask ExecuteAsync(IConsole console) => default;
|
||||||
|
}
|
||||||
|
");
|
||||||
|
|
||||||
|
var application = new CliApplicationBuilder()
|
||||||
|
.AddCommand(commandType)
|
||||||
|
.UseConsole(FakeConsole)
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
// Act
|
||||||
|
var exitCode = await application.RunAsync(
|
||||||
|
new[] {"--help"},
|
||||||
|
new Dictionary<string, string>()
|
||||||
|
);
|
||||||
|
|
||||||
|
var stdOut = FakeConsole.ReadOutputString();
|
||||||
|
|
||||||
|
// Assert
|
||||||
|
exitCode.Should().Be(0);
|
||||||
|
stdOut.Should().ContainAllInOrder(
|
||||||
|
"PARAMETERS",
|
||||||
|
"foo", "Choices:", "One", "Two", "Three",
|
||||||
|
"OPTIONS",
|
||||||
|
"--bar", "Choices:", "One", "Two", "Three"
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
[Fact]
|
[Fact]
|
||||||
public async Task Help_text_shows_environment_variables_for_options_that_have_them_configured_as_fallback()
|
public async Task Help_text_shows_environment_variables_for_options_that_have_them_configured_as_fallback()
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -16,7 +16,7 @@
|
|||||||
public string ExecutableName { get; }
|
public string ExecutableName { get; }
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Application version text.
|
/// Application version.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public string Version { get; }
|
public string Version { get; }
|
||||||
|
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ namespace CliFx.Attributes
|
|||||||
/// Annotates a type that defines a command.
|
/// Annotates a type that defines a command.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
[AttributeUsage(AttributeTargets.Class, Inherited = false)]
|
||||||
public class CommandAttribute : Attribute
|
public sealed class CommandAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Command's name.
|
/// Command's name.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace CliFx.Attributes
|
|||||||
/// Annotates a property that defines a command option.
|
/// Annotates a property that defines a command option.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class CommandOptionAttribute : Attribute
|
public sealed class CommandOptionAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Option name.
|
/// Option name.
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ namespace CliFx.Attributes
|
|||||||
/// Annotates a property that defines a command parameter.
|
/// Annotates a property that defines a command parameter.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
[AttributeUsage(AttributeTargets.Property)]
|
[AttributeUsage(AttributeTargets.Property)]
|
||||||
public class CommandParameterAttribute : Attribute
|
public sealed class CommandParameterAttribute : Attribute
|
||||||
{
|
{
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Parameter order.
|
/// Parameter order.
|
||||||
|
|||||||
@@ -99,7 +99,7 @@ namespace CliFx
|
|||||||
// Handle preview directive
|
// Handle preview directive
|
||||||
if (IsPreviewModeEnabled(commandInput))
|
if (IsPreviewModeEnabled(commandInput))
|
||||||
{
|
{
|
||||||
_console.WriteCommandInput(commandInput);
|
_console.Output.WriteCommandInput(commandInput);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -125,7 +125,7 @@ namespace CliFx
|
|||||||
// Handle help option
|
// Handle help option
|
||||||
if (ShouldShowHelpText(commandSchema, commandInput))
|
if (ShouldShowHelpText(commandSchema, commandInput))
|
||||||
{
|
{
|
||||||
_console.WriteHelpText(helpContext);
|
_console.Output.WriteHelpText(helpContext);
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -150,12 +150,12 @@ namespace CliFx
|
|||||||
}
|
}
|
||||||
catch (CliFxException ex)
|
catch (CliFxException ex)
|
||||||
{
|
{
|
||||||
_console.WriteException(ex);
|
_console.Error.WriteException(ex);
|
||||||
|
|
||||||
if (ex.ShowHelp)
|
if (ex.ShowHelp)
|
||||||
{
|
{
|
||||||
_console.Output.WriteLine();
|
_console.Output.WriteLine();
|
||||||
_console.WriteHelpText(helpContext);
|
_console.Output.WriteHelpText(helpContext);
|
||||||
}
|
}
|
||||||
|
|
||||||
return ex.ExitCode;
|
return ex.ExitCode;
|
||||||
@@ -200,7 +200,7 @@ namespace CliFx
|
|||||||
// developer, so we don't swallow them in that case.
|
// developer, so we don't swallow them in that case.
|
||||||
catch (Exception ex) when (!Debugger.IsAttached)
|
catch (Exception ex) when (!Debugger.IsAttached)
|
||||||
{
|
{
|
||||||
_console.WriteException(ex);
|
_console.Error.WriteException(ex);
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -37,7 +37,7 @@ namespace CliFx
|
|||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
/// Adds a command the application.
|
/// Adds a command to the application.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
public CliApplicationBuilder AddCommand<TCommand>() where TCommand : ICommand =>
|
public CliApplicationBuilder AddCommand<TCommand>() where TCommand : ICommand =>
|
||||||
AddCommand(typeof(TCommand));
|
AddCommand(typeof(TCommand));
|
||||||
|
|||||||
@@ -28,20 +28,10 @@
|
|||||||
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
<PackageReference Include="System.Threading.Tasks.Extensions" Version="4.5.4" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<!-- The following elements are responsible for embedding the analyzer assembly within the output NuGet package -->
|
<!-- Pack the analyzer assembly inside the package -->
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<ProjectReference Include="..\CliFx.Analyzers\CliFx.Analyzers.csproj" ReferenceOutputAssembly="true" IncludeAssets="CliFx.Analyzers.dll" />
|
<ProjectReference Include="../CliFx.Analyzers/CliFx.Analyzers.csproj" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
|
||||||
|
<None Include="../CliFx.Analyzers/bin/$(Configuration)/netstandard2.0/CliFx.Analyzers.dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<PropertyGroup>
|
|
||||||
<TargetsForTfmSpecificContentInPackage>$(TargetsForTfmSpecificContentInPackage);CopyAnalyzerToPackage</TargetsForTfmSpecificContentInPackage>
|
|
||||||
</PropertyGroup>
|
|
||||||
|
|
||||||
<Target Name="CopyAnalyzerToPackage">
|
|
||||||
<ItemGroup>
|
|
||||||
<TfmSpecificPackageFile Include="$(OutDir)/CliFx.Analyzers.dll" PackagePath="analyzers/dotnet/cs" BuildAction="none" />
|
|
||||||
</ItemGroup>
|
|
||||||
</Target>
|
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
@@ -93,7 +93,7 @@ namespace CliFx.Formatting
|
|||||||
|
|
||||||
internal static class CommandInputConsoleFormatterExtensions
|
internal static class CommandInputConsoleFormatterExtensions
|
||||||
{
|
{
|
||||||
public static void WriteCommandInput(this IConsole console, CommandInput commandInput) =>
|
public static void WriteCommandInput(this ConsoleWriter consoleWriter, CommandInput commandInput) =>
|
||||||
new CommandInputConsoleFormatter(console.Output).WriteCommandInput(commandInput);
|
new CommandInputConsoleFormatter(consoleWriter).WriteCommandInput(commandInput);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -129,7 +129,7 @@ namespace CliFx.Formatting
|
|||||||
|
|
||||||
internal static class ExceptionConsoleFormatterExtensions
|
internal static class ExceptionConsoleFormatterExtensions
|
||||||
{
|
{
|
||||||
public static void WriteException(this IConsole console, Exception exception) =>
|
public static void WriteException(this ConsoleWriter consoleWriter, Exception exception) =>
|
||||||
new ExceptionConsoleFormatter(console.Error).WriteException(exception);
|
new ExceptionConsoleFormatter(consoleWriter).WriteException(exception);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +191,7 @@ namespace CliFx.Formatting
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write(ConsoleColor.DarkGray, '"');
|
Write(ConsoleColor.DarkGray, '"');
|
||||||
Write(ConsoleColor.White, validValue.ToString());
|
Write(validValue.ToString());
|
||||||
Write(ConsoleColor.DarkGray, '"');
|
Write(ConsoleColor.DarkGray, '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -270,7 +270,7 @@ namespace CliFx.Formatting
|
|||||||
}
|
}
|
||||||
|
|
||||||
Write(ConsoleColor.DarkGray, '"');
|
Write(ConsoleColor.DarkGray, '"');
|
||||||
Write(ConsoleColor.White, validValue.ToString());
|
Write(validValue.ToString());
|
||||||
Write(ConsoleColor.DarkGray, '"');
|
Write(ConsoleColor.DarkGray, '"');
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -443,7 +443,7 @@ namespace CliFx.Formatting
|
|||||||
|
|
||||||
internal static class HelpConsoleFormatterExtensions
|
internal static class HelpConsoleFormatterExtensions
|
||||||
{
|
{
|
||||||
public static void WriteHelpText(this IConsole console, HelpContext context) =>
|
public static void WriteHelpText(this ConsoleWriter consoleWriter, HelpContext context) =>
|
||||||
new HelpConsoleFormatter(console.Output, context).WriteHelpText();
|
new HelpConsoleFormatter(consoleWriter, context).WriteHelpText();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -70,12 +70,16 @@ namespace CliFx.Infrastructure
|
|||||||
/// Subsequent calls to this method have no side-effects and return the same token.
|
/// Subsequent calls to this method have no side-effects and return the same token.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
/// <remarks>
|
/// <remarks>
|
||||||
|
/// <para>
|
||||||
/// Calling this method effectively makes the command cancellation-aware, which
|
/// Calling this method effectively makes the command cancellation-aware, which
|
||||||
/// means that sending an interrupt signal won't immediately terminate the application,
|
/// means that sending the interrupt signal won't immediately terminate the application,
|
||||||
/// but will instead trigger a token that the command can use to exit more gracefully.
|
/// but will instead trigger a token that the command can use to exit more gracefully.
|
||||||
///
|
/// </para>
|
||||||
/// If the user sends a second interrupt signal after the first one, the application
|
/// <para>
|
||||||
/// will terminate immediately.
|
/// Note that the handler is only respected when the user sends the interrupt signal for the first time.
|
||||||
|
/// If the user decides to issue the signal again, the application will terminate immediately
|
||||||
|
/// regardless of whether the command is cancellation-aware.
|
||||||
|
/// </para>
|
||||||
/// </remarks>
|
/// </remarks>
|
||||||
CancellationToken RegisterCancellationHandler();
|
CancellationToken RegisterCancellationHandler();
|
||||||
}
|
}
|
||||||
@@ -116,16 +120,10 @@ namespace CliFx.Infrastructure
|
|||||||
public static IDisposable WithColors(
|
public static IDisposable WithColors(
|
||||||
this IConsole console,
|
this IConsole console,
|
||||||
ConsoleColor foregroundColor,
|
ConsoleColor foregroundColor,
|
||||||
ConsoleColor backgroundColor)
|
ConsoleColor backgroundColor) =>
|
||||||
{
|
Disposable.Merge(
|
||||||
var foregroundColorRegistration = console.WithForegroundColor(foregroundColor);
|
console.WithForegroundColor(foregroundColor),
|
||||||
var backgroundColorRegistration = console.WithBackgroundColor(backgroundColor);
|
console.WithBackgroundColor(backgroundColor)
|
||||||
|
);
|
||||||
return Disposable.Create(() =>
|
|
||||||
{
|
|
||||||
foregroundColorRegistration.Dispose();
|
|
||||||
backgroundColorRegistration.Dispose();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -21,7 +21,10 @@ namespace CliFx.Schema
|
|||||||
|
|
||||||
public IReadOnlyList<object?> GetValidValues()
|
public IReadOnlyList<object?> GetValidValues()
|
||||||
{
|
{
|
||||||
var underlyingType = Type.TryGetNullableUnderlyingType() ?? Type;
|
var underlyingType =
|
||||||
|
Type.TryGetNullableUnderlyingType() ??
|
||||||
|
Type.TryGetEnumerableUnderlyingType() ??
|
||||||
|
Type;
|
||||||
|
|
||||||
// We can only get valid values for enums
|
// We can only get valid values for enums
|
||||||
if (underlyingType.IsEnum)
|
if (underlyingType.IsEnum)
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using System;
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
|
||||||
namespace CliFx.Utils
|
namespace CliFx.Utils
|
||||||
{
|
{
|
||||||
@@ -14,5 +15,14 @@ namespace CliFx.Utils
|
|||||||
internal partial class Disposable
|
internal partial class Disposable
|
||||||
{
|
{
|
||||||
public static IDisposable Create(Action dispose) => new Disposable(dispose);
|
public static IDisposable Create(Action dispose) => new Disposable(dispose);
|
||||||
|
|
||||||
|
public static IDisposable Merge(IEnumerable<IDisposable> disposables) => Create(() =>
|
||||||
|
{
|
||||||
|
foreach (var disposable in disposables)
|
||||||
|
disposable.Dispose();
|
||||||
|
});
|
||||||
|
|
||||||
|
public static IDisposable Merge(params IDisposable[] disposables) =>
|
||||||
|
Merge((IEnumerable<IDisposable>) disposables);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -91,12 +91,7 @@ namespace CliFx.Utils
|
|||||||
{
|
{
|
||||||
var matches = Pattern.Matches(stackTrace).Cast<Match>().ToArray();
|
var matches = Pattern.Matches(stackTrace).Cast<Match>().ToArray();
|
||||||
|
|
||||||
// Ensure success (all lines should be parsed)
|
if (matches.Length <= 0 || matches.Any(m => !m.Success))
|
||||||
var isSuccess =
|
|
||||||
matches.Length ==
|
|
||||||
stackTrace.Split('\n', StringSplitOptions.RemoveEmptyEntries).Length;
|
|
||||||
|
|
||||||
if (!isSuccess)
|
|
||||||
{
|
{
|
||||||
// If parsing fails, we include the original stacktrace in the
|
// If parsing fails, we include the original stacktrace in the
|
||||||
// exception so that it's shown to the user.
|
// exception so that it's shown to the user.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<Project>
|
<Project>
|
||||||
|
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<Version>2.0</Version>
|
<Version>2.0.3</Version>
|
||||||
<Company>Tyrrrz</Company>
|
<Company>Tyrrrz</Company>
|
||||||
<Copyright>Copyright (C) Alexey Golub</Copyright>
|
<Copyright>Copyright (C) Alexey Golub</Copyright>
|
||||||
<LangVersion>latest</LangVersion>
|
<LangVersion>latest</LangVersion>
|
||||||
|
|||||||
@@ -129,7 +129,8 @@ public class LogCommand : ICommand
|
|||||||
[CommandParameter(0, Description = "Value whose logarithm is to be found.")]
|
[CommandParameter(0, Description = "Value whose logarithm is to be found.")]
|
||||||
public double Value { get; init; }
|
public double Value { get; init; }
|
||||||
|
|
||||||
// Name: --base | Short name: -b
|
// Name: --base
|
||||||
|
// Short name: -b
|
||||||
[CommandOption("base", 'b', Description = "Logarithm base.")]
|
[CommandOption("base", 'b', Description = "Logarithm base.")]
|
||||||
public double Base { get; init; } = 10;
|
public double Base { get; init; } = 10;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user