Preparations for the 1.0 release

* Less cluttered solution layout.
* Move examples to a repository of its own.
* Move Roslyn analyzer to a repository of its own.
* Enable central package management.
* Clean up csproj files.
* Add README file to NuGet packages.
This commit is contained in:
Patrik Svensson
2024-08-05 20:41:45 +02:00
committed by Patrik Svensson
parent bb72b44d60
commit 42fd801876
677 changed files with 272 additions and 6214 deletions

View File

@ -98,4 +98,7 @@ dotnet_diagnostic.IDE0044.severity = warning
dotnet_diagnostic.RCS1047.severity = none
# RCS1090: Call 'ConfigureAwait(false)'.
dotnet_diagnostic.RCS1090.severity = warning
dotnet_diagnostic.RCS1090.severity = warning
# The file header is missing or not located at the top of the file
dotnet_diagnostic.SA1633.severity = none

View File

@ -7,19 +7,35 @@
<MinVerSkip Condition="'$(Configuration)' == 'Debug'">true</MinVerSkip>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
<IsPackable>false</IsPackable>
<Nullable>enable</Nullable>
<NoWarn>$(NoWarn);SA1633</NoWarn>
</PropertyGroup>
<PropertyGroup Label="Deterministic Build" Condition="'$(GITHUB_ACTIONS)' == 'true'">
<ContinuousIntegrationBuild>true</ContinuousIntegrationBuild>
</PropertyGroup>
<ItemGroup Condition="$(IsPackable)">
<None Include="$(MSBuildThisFileDirectory)../resources/nuget/logo.png">
<Pack>true</Pack>
<PackagePath>\</PackagePath>
<Link>Properties/Package/Logo.png</Link>
</None>
<None Include="$(MSBuildThisFileDirectory)../resources/nuget/$(AssemblyName).md">
<Pack>true</Pack>
<PackagePath>\README.md</PackagePath>
<Link>Properties/Package/README.md</Link>
</None>
</ItemGroup>
<PropertyGroup Label="Package Information">
<Description>A library that makes it easier to create beautiful console applications.</Description>
<Copyright>Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray</Copyright>
<Authors>Patrik Svensson, Phil Scott, Nils Andresen, Cédric Luthi, Frank Ray</Authors>
<RepositoryType>git</RepositoryType>
<RepositoryUrl>https://github.com/spectreconsole/spectre.console</RepositoryUrl>
<PackageIcon>small-logo.png</PackageIcon>
<PackageIcon>logo.png</PackageIcon>
<PackageReadmeFile>README.md</PackageReadmeFile>
<PackageRequireLicenseAcceptance>True</PackageRequireLicenseAcceptance>
<PackageProjectUrl>https://github.com/spectreconsole/spectre.console</PackageProjectUrl>
<PackageLicenseExpression>MIT</PackageLicenseExpression>
@ -31,14 +47,18 @@
<EmbedUntrackedSources>true</EmbedUntrackedSources>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)/stylecop.json" Link="Properties/stylecop.json"/>
</ItemGroup>
<!-- Allow folks to build with minimal dependencies (though they will need to provide their own Version data) -->
<ItemGroup Label="Build Tools Package References" Condition="'$(UseBuildTimeTools)' != 'false'">
<PackageReference Include="MinVer" PrivateAssets="All" Version="4.3.0" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0" />
<PackageReference Include="StyleCop.Analyzers" Version="1.2.0-beta.556">
<PackageReference Include="MinVer" PrivateAssets="All" />
<PackageReference Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" />
<PackageReference Include="StyleCop.Analyzers">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
<PackageReference Include="Roslynator.Analyzers" Version="4.12.2">
<PackageReference Include="Roslynator.Analyzers">
<PrivateAssets>All</PrivateAssets>
</PackageReference>
</ItemGroup>

View File

@ -0,0 +1,30 @@
<Project>
<PropertyGroup>
<ManagePackageVersionsCentrally>true</ManagePackageVersionsCentrally>
</PropertyGroup>
<ItemGroup Label="Dependencies">
<PackageVersion Include="MinVer" PrivateAssets="All" Version="5.0.0"/>
<PackageVersion Include="Microsoft.SourceLink.GitHub" PrivateAssets="All" Version="8.0.0"/>
<PackageVersion Include="Wcwidth.Sources" Version="2.0.0"/>
<PackageVersion Include="IsExternalInit" Version="1.0.3"/>
<PackageVersion Include="Microsoft.Extensions.DependencyInjection" Version="8.0.0"/>
<PackageVersion Include="Microsoft.NET.Test.Sdk" Version="17.10.0"/>
<PackageVersion Include="Shouldly" Version="4.2.1"/>
<PackageVersion Include="Spectre.Verify.Extensions" Version="22.3.1"/>
<PackageVersion Include="Verify.Xunit" Version="26.1.6"/>
<PackageVersion Include="xunit" Version="2.9.0"/>
<PackageVersion Include="xunit.runner.visualstudio" Version="2.8.2"/>
<PackageVersion Include="System.Memory" Version="4.5.5" />
<PackageVersion Include="SixLabors.ImageSharp" Version="3.1.5" />
<PackageVersion Include="Nullable" Version="1.3.1"/>
<PackageVersion Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" />
</ItemGroup>
<ItemGroup Label="Static Analysis">
<PackageVersion Include="StyleCop.Analyzers" PrivateAssets="All" Version="1.2.0-beta.556"/>
<PackageVersion Include="Roslynator.Analyzers" PrivateAssets="All" Version="4.12.4"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,17 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with ImageSharp superpowers.</Description>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -0,0 +1,20 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
<ImplicitUsings>true</ImplicitUsings>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with JSON superpowers.</Description>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\..\Spectre.Console\Internal\Extensions\CharExtensions.cs" Link="Internal\CharExtensions.cs" />
<Compile Include="..\..\Spectre.Console\Internal\Extensions\EnumerableExtensions.cs" Link="Internal\EnumerableExtensions.cs" />
<Compile Include="..\..\Spectre.Console\Internal\Text\StringBuffer.cs" Link="Internal\StringBuffer.cs" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,15 +0,0 @@
namespace Spectre.Console.Analyzer.Sandbox;
/// <summary>
/// Sample sandbox for testing out analyzers.
/// </summary>
public static class Program
{
/// <summary>
/// The program's entry point.
/// </summary>
public static void Main()
{
AnsiConsole.WriteLine("Project is set up with a reference to Spectre.Console.Analyzer");
}
}

View File

@ -1,15 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<OutputType>Exe</OutputType>
<TargetFramework>net8.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj" PrivateAssets="all" ReferenceOutputAssembly="false" OutputItemType="Analyzer" />
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,79 +0,0 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 16
VisualStudioVersion = 16.0.30114.105
MinimumVisualStudioVersion = 10.0.40219.1
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Analyzer", "Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj", "{18178142-A80D-424F-882D-DB0F787210BD}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Analyzer.Sandbox", "Spectre.Console.Analyzer.Sandbox\Spectre.Console.Analyzer.Sandbox.csproj", "{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Analyzer.Tests", "..\test\Spectre.Console.Analyzer.Tests\Spectre.Console.Analyzer.Tests.csproj", "{609D5D1B-D904-4A31-B237-A04B49910166}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console", "Spectre.Console\Spectre.Console.csproj", "{6BFF310F-9601-4E5D-BC80-118AC708D72A}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|Any CPU.Build.0 = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|x64.ActiveCfg = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|x64.Build.0 = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|x86.ActiveCfg = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Debug|x86.Build.0 = Debug|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|Any CPU.ActiveCfg = Release|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|Any CPU.Build.0 = Release|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|x64.ActiveCfg = Release|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|x64.Build.0 = Release|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|x86.ActiveCfg = Release|Any CPU
{18178142-A80D-424F-882D-DB0F787210BD}.Release|x86.Build.0 = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|Any CPU.Build.0 = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|x64.ActiveCfg = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|x64.Build.0 = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|x86.ActiveCfg = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Debug|x86.Build.0 = Debug|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|Any CPU.ActiveCfg = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|Any CPU.Build.0 = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|x64.ActiveCfg = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|x64.Build.0 = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|x86.ActiveCfg = Release|Any CPU
{44D2E280-8FCD-4FC1-9133-F61E344FD6A6}.Release|x86.Build.0 = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|Any CPU.Build.0 = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|x64.ActiveCfg = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|x64.Build.0 = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|x86.ActiveCfg = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Debug|x86.Build.0 = Debug|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|Any CPU.ActiveCfg = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|Any CPU.Build.0 = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|x64.ActiveCfg = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|x64.Build.0 = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|x86.ActiveCfg = Release|Any CPU
{609D5D1B-D904-4A31-B237-A04B49910166}.Release|x86.Build.0 = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|Any CPU.Build.0 = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|x64.ActiveCfg = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|x64.Build.0 = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|x86.ActiveCfg = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Debug|x86.Build.0 = Debug|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|Any CPU.ActiveCfg = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|Any CPU.Build.0 = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|x64.ActiveCfg = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|x64.Build.0 = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|x86.ActiveCfg = Release|Any CPU
{6BFF310F-9601-4E5D-BC80-118AC708D72A}.Release|x86.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {3BABBA82-B60D-45CE-9C56-8B833474DD3C}
EndGlobalSection
EndGlobal

View File

@ -1,6 +0,0 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>

View File

@ -1,90 +0,0 @@
namespace Spectre.Console.Analyzer;
/// <summary>
/// Analyzer to suggest using available instances of AnsiConsole over the static methods.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class FavorInstanceAnsiConsoleOverStaticAnalyzer : SpectreAnalyzer
{
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic;
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(_diagnosticDescriptor);
/// <inheritdoc />
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
{
var ansiConsoleType = compilationStartContext.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
if (ansiConsoleType == null)
{
return;
}
compilationStartContext.RegisterOperationAction(
context =>
{
// if this operation isn't an invocation against one of the System.Console methods
// defined in _methods then we can safely stop analyzing and return;
var invocationOperation = (IInvocationOperation)context.Operation;
if (!SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod.ContainingType, ansiConsoleType))
{
return;
}
// if we aren't in a method then it might be too complex for us to handle.
if (!invocationOperation.Syntax.Ancestors().OfType<MethodDeclarationSyntax>().Any())
{
return;
}
if (!HasFieldAnsiConsole(invocationOperation.Syntax) &&
!HasParameterAnsiConsole(invocationOperation.Syntax))
{
return;
}
var methodSymbol = invocationOperation.TargetMethod;
var displayString = SymbolDisplay.ToDisplayString(
methodSymbol,
SymbolDisplayFormat.CSharpShortErrorMessageFormat
.WithParameterOptions(SymbolDisplayParameterOptions.None)
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
context.ReportDiagnostic(
Diagnostic.Create(
_diagnosticDescriptor,
invocationOperation.Syntax.GetLocation(),
displayString));
}, OperationKind.Invocation);
}
private static bool HasParameterAnsiConsole(SyntaxNode syntaxNode)
{
return syntaxNode
.Ancestors().OfType<MethodDeclarationSyntax>()
.First()
.ParameterList.Parameters
.Any(i => i.Type?.NormalizeWhitespace()?.ToString() == "IAnsiConsole");
}
private static bool HasFieldAnsiConsole(SyntaxNode syntaxNode)
{
var isStatic = syntaxNode
.Ancestors()
.OfType<MethodDeclarationSyntax>()
.First()
.Modifiers.Any(i => i.IsKind(SyntaxKind.StaticKeyword));
return syntaxNode
.Ancestors().OfType<ClassDeclarationSyntax>()
.First()
.Members
.OfType<FieldDeclarationSyntax>()
.Any(i =>
i.Declaration.Type.NormalizeWhitespace().ToString() == "IAnsiConsole" &&
(!isStatic ^ i.Modifiers.Any(modifier => modifier.IsKind(SyntaxKind.StaticKeyword))));
}
}

View File

@ -1,74 +0,0 @@
namespace Spectre.Console.Analyzer;
/// <summary>
/// Analyzer to detect calls to live renderables within a live renderable context.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
[Shared]
public class NoConcurrentLiveRenderablesAnalyzer : SpectreAnalyzer
{
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
Descriptors.S1020_AvoidConcurrentCallsToMultipleLiveRenderables;
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(_diagnosticDescriptor);
/// <inheritdoc />
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
{
var liveTypes = Constants.LiveRenderables
.Select(i => compilationStartContext.Compilation.GetTypeByMetadataName(i))
.Where(i => i != null)
.ToImmutableArray();
if (liveTypes.Length == 0)
{
return;
}
compilationStartContext.RegisterOperationAction(
context =>
{
var invocationOperation = (IInvocationOperation)context.Operation;
var methodSymbol = invocationOperation.TargetMethod;
const string StartMethod = "Start";
if (methodSymbol.Name != StartMethod)
{
return;
}
if (liveTypes.All(i => !SymbolEqualityComparer.Default.Equals(i, methodSymbol.ContainingType)))
{
return;
}
var model = context.Operation.SemanticModel!;
var parentInvocations = invocationOperation
.Syntax.Ancestors()
.OfType<InvocationExpressionSyntax>()
.Select(i => model.GetOperation(i, context.CancellationToken))
.OfType<IInvocationOperation>()
.ToList();
if (parentInvocations.All(parent =>
parent.TargetMethod.Name != StartMethod || !liveTypes.Contains(parent.TargetMethod.ContainingType, SymbolEqualityComparer.Default)))
{
return;
}
var displayString = SymbolDisplay.ToDisplayString(
methodSymbol,
SymbolDisplayFormat.CSharpShortErrorMessageFormat
.WithParameterOptions(SymbolDisplayParameterOptions.None)
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
context.ReportDiagnostic(
Diagnostic.Create(
_diagnosticDescriptor,
invocationOperation.Syntax.GetLocation(),
displayString));
}, OperationKind.Invocation);
}
}

View File

@ -1,80 +0,0 @@
namespace Spectre.Console.Analyzer;
/// <summary>
/// Analyzer to detect calls to live renderables within a live renderable context.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
[Shared]
public class NoPromptsDuringLiveRenderablesAnalyzer : SpectreAnalyzer
{
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
Descriptors.S1021_AvoidPromptCallsDuringLiveRenderables;
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(_diagnosticDescriptor);
/// <inheritdoc />
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
{
var ansiConsoleType = compilationStartContext.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
var ansiConsoleExtensionsType = compilationStartContext.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsoleExtensions");
if (ansiConsoleType is null && ansiConsoleExtensionsType is null)
{
return;
}
compilationStartContext.RegisterOperationAction(
context =>
{
// if this operation isn't an invocation against one of the System.Console methods
// defined in _methods then we can safely stop analyzing and return;
var invocationOperation = (IInvocationOperation)context.Operation;
var methodSymbol = invocationOperation.TargetMethod;
var promptMethods = ImmutableArray.Create("Ask", "Confirm", "Prompt");
if (!promptMethods.Contains(methodSymbol.Name))
{
return;
}
if (!SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, ansiConsoleType) &&
!SymbolEqualityComparer.Default.Equals(methodSymbol.ContainingType, ansiConsoleExtensionsType))
{
return;
}
var model = context.Operation.SemanticModel!;
var parentInvocations = invocationOperation
.Syntax.Ancestors()
.OfType<InvocationExpressionSyntax>()
.Select(i => model.GetOperation(i, context.CancellationToken))
.OfType<IInvocationOperation>()
.ToList();
var liveTypes = Constants.LiveRenderables
.Select(i => context.Compilation.GetTypeByMetadataName(i))
.ToImmutableArray();
if (parentInvocations.All(parent =>
parent.TargetMethod.Name != "Start" ||
!liveTypes.Contains(parent.TargetMethod.ContainingType, SymbolEqualityComparer.Default)))
{
return;
}
var displayString = SymbolDisplay.ToDisplayString(
methodSymbol,
SymbolDisplayFormat.CSharpShortErrorMessageFormat
.WithParameterOptions(SymbolDisplayParameterOptions.None)
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
context.ReportDiagnostic(
Diagnostic.Create(
_diagnosticDescriptor,
invocationOperation.Syntax.GetLocation(),
displayString));
}, OperationKind.Invocation);
}
}

View File

@ -1,22 +0,0 @@
namespace Spectre.Console.Analyzer;
/// <summary>
/// Base class for Spectre analyzers.
/// </summary>
public abstract class SpectreAnalyzer : DiagnosticAnalyzer
{
/// <inheritdoc />
public override void Initialize(AnalysisContext context)
{
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.Analyze | GeneratedCodeAnalysisFlags.ReportDiagnostics);
context.EnableConcurrentExecution();
context.RegisterCompilationStartAction(AnalyzeCompilation);
}
/// <summary>
/// Analyze compilation.
/// </summary>
/// <param name="compilationStartContext">Compilation Start Analysis Context.</param>
protected abstract void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext);
}

View File

@ -1,61 +0,0 @@
namespace Spectre.Console.Analyzer;
/// <summary>
/// Analyzer to enforce the use of AnsiConsole over System.Console for known methods.
/// </summary>
[DiagnosticAnalyzer(LanguageNames.CSharp)]
public class UseSpectreInsteadOfSystemConsoleAnalyzer : SpectreAnalyzer
{
private static readonly DiagnosticDescriptor _diagnosticDescriptor =
Descriptors.S1000_UseAnsiConsoleOverSystemConsole;
private static readonly string[] _methods = { "WriteLine", "Write" };
/// <inheritdoc />
public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics =>
ImmutableArray.Create(_diagnosticDescriptor);
/// <inheritdoc />
protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext)
{
var systemConsoleType = compilationStartContext.Compilation.GetTypeByMetadataName("System.Console");
var spectreConsoleType = compilationStartContext.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
if (systemConsoleType == null || spectreConsoleType == null)
{
return;
}
compilationStartContext.RegisterOperationAction(
context =>
{
// if this operation isn't an invocation against one of the System.Console methods
// defined in _methods then we can safely stop analyzing and return;
var invocationOperation = (IInvocationOperation)context.Operation;
var methodName = System.Array.Find(_methods, i => i.Equals(invocationOperation.TargetMethod.Name));
if (methodName == null)
{
return;
}
if (!SymbolEqualityComparer.Default.Equals(invocationOperation.TargetMethod.ContainingType, systemConsoleType))
{
return;
}
var methodSymbol = invocationOperation.TargetMethod;
var displayString = SymbolDisplay.ToDisplayString(
methodSymbol,
SymbolDisplayFormat.CSharpShortErrorMessageFormat
.WithParameterOptions(SymbolDisplayParameterOptions.None)
.WithGenericsOptions(SymbolDisplayGenericsOptions.None));
context.ReportDiagnostic(
Diagnostic.Create(
_diagnosticDescriptor,
invocationOperation.Syntax.GetLocation(),
displayString));
}, OperationKind.Invocation);
}
}

View File

@ -1,14 +0,0 @@
namespace Spectre.Console.Analyzer;
internal static class Constants
{
internal const string StaticInstance = "AnsiConsole";
internal const string SpectreConsole = "Spectre.Console";
internal static readonly string[] LiveRenderables =
{
"Spectre.Console.LiveDisplay",
"Spectre.Console.Progress",
"Spectre.Console.Status",
};
}

View File

@ -1,76 +0,0 @@
using static Microsoft.CodeAnalysis.DiagnosticSeverity;
using static Spectre.Console.Analyzer.Descriptors.Category;
namespace Spectre.Console.Analyzer;
/// <summary>
/// Code analysis descriptors.
/// </summary>
public static class Descriptors
{
internal enum Category
{
Usage, // 1xxx
}
private static readonly ConcurrentDictionary<Category, string> _categoryMapping = new();
private static DiagnosticDescriptor Rule(string id, string title, Category category, DiagnosticSeverity defaultSeverity, string messageFormat, string? description = null)
{
var helpLink = $"https://spectreconsole.net/analyzer/rules/{id.ToLowerInvariant()}";
const bool IsEnabledByDefault = true;
return new DiagnosticDescriptor(
id,
title,
messageFormat,
_categoryMapping.GetOrAdd(category, c => c.ToString()),
defaultSeverity,
IsEnabledByDefault,
description,
helpLink);
}
/// <summary>
/// Gets definitions of diagnostics Spectre1000.
/// </summary>
public static DiagnosticDescriptor S1000_UseAnsiConsoleOverSystemConsole { get; } =
Rule(
"Spectre1000",
"Use AnsiConsole instead of System.Console",
Usage,
Warning,
"Use AnsiConsole instead of System.Console");
/// <summary>
/// Gets definitions of diagnostics Spectre1010.
/// </summary>
public static DiagnosticDescriptor S1010_FavorInstanceAnsiConsoleOverStatic { get; } =
Rule(
"Spectre1010",
"Favor the use of the instance of AnsiConsole over the static helper.",
Usage,
Info,
"Favor the use of the instance of AnsiConsole over the static helper.");
/// <summary>
/// Gets definitions of diagnostics Spectre1020.
/// </summary>
public static DiagnosticDescriptor S1020_AvoidConcurrentCallsToMultipleLiveRenderables { get; } =
Rule(
"Spectre1020",
"Avoid calling other live renderables while a current renderable is running.",
Usage,
Warning,
"Avoid calling other live renderables while a current renderable is running.");
/// <summary>
/// Gets definitions of diagnostics Spectre1020.
/// </summary>
public static DiagnosticDescriptor S1021_AvoidPromptCallsDuringLiveRenderables { get; } =
Rule(
"Spectre1021",
"Avoid prompting for input while a current renderable is running.",
Usage,
Warning,
"Avoid prompting for input while a current renderable is running.");
}

View File

@ -1,202 +0,0 @@
using Microsoft.CodeAnalysis.Editing;
using Microsoft.CodeAnalysis.Simplification;
namespace Spectre.Console.Analyzer.CodeActions;
/// <summary>
/// Code action to change calls to System.Console to AnsiConsole.
/// </summary>
public class SwitchToAnsiConsoleAction : CodeAction
{
private readonly Document _document;
private readonly InvocationExpressionSyntax _originalInvocation;
/// <summary>
/// Initializes a new instance of the <see cref="SwitchToAnsiConsoleAction"/> class.
/// </summary>
/// <param name="document">Document to change.</param>
/// <param name="originalInvocation">The method to change.</param>
/// <param name="title">Title of the fix.</param>
public SwitchToAnsiConsoleAction(Document document, InvocationExpressionSyntax originalInvocation, string title)
{
_document = document;
_originalInvocation = originalInvocation;
Title = title;
}
/// <inheritdoc />
public override string Title { get; }
/// <inheritdoc />
public override string EquivalenceKey => Title;
/// <inheritdoc />
protected override async Task<Document> GetChangedDocumentAsync(CancellationToken cancellationToken)
{
var editor = await DocumentEditor.CreateAsync(_document, cancellationToken).ConfigureAwait(false);
var compilation = editor.SemanticModel.Compilation;
var operation = editor.SemanticModel.GetOperation(_originalInvocation, cancellationToken) as IInvocationOperation;
if (operation == null)
{
return _document;
}
// If there is an IAnsiConsole passed into the method then we'll use it.
// otherwise we'll check for a field level instance.
// if neither of those exist we'll fall back to the static param.
var spectreConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole");
var iansiConsoleSymbol = compilation.GetTypeByMetadataName("Spectre.Console.IAnsiConsole");
ISymbol? accessibleConsoleSymbol = spectreConsoleSymbol;
if (iansiConsoleSymbol != null)
{
var isInStaticContext = IsInStaticContext(operation, cancellationToken, out var parentStaticMemberStartPosition);
foreach (var symbol in editor.SemanticModel.LookupSymbols(operation.Syntax.GetLocation().SourceSpan.Start))
{
// LookupSymbols check the accessibility of the symbol, but it can
// suggest instance members when the current context is static.
var symbolType = symbol switch
{
IParameterSymbol parameter => parameter.Type,
IFieldSymbol field when !isInStaticContext || field.IsStatic => field.Type,
IPropertySymbol { GetMethod: not null } property when !isInStaticContext || property.IsStatic => property.Type,
ILocalSymbol local => local.Type,
_ => null,
};
// Locals can be returned even if there are not valid in the current context. For instance,
// it can return locals declared after the current location. Or it can return locals that
// should not be accessible in a static local function.
//
// void Sample()
// {
// int local = 0;
// static void LocalFunction() => local; <-- local is invalid here but LookupSymbols suggests it
// }
//
// Parameters from the ancestor methods or local functions are also returned even if the operation is in a static local function.
if (symbol.Kind is SymbolKind.Local or SymbolKind.Parameter)
{
var localPosition = symbol.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax(cancellationToken).GetLocation().SourceSpan.Start;
// The local is not part of the source tree
if (localPosition == null)
{
break;
}
// The local is declared after the current expression
if (localPosition > _originalInvocation.Span.Start)
{
break;
}
// The local is declared outside the static local function
if (isInStaticContext && localPosition < parentStaticMemberStartPosition)
{
break;
}
}
if (IsOrImplementSymbol(symbolType, iansiConsoleSymbol))
{
accessibleConsoleSymbol = symbol;
break;
}
}
}
if (accessibleConsoleSymbol == null)
{
return _document;
}
// Replace the original invocation
var generator = editor.Generator;
var consoleExpression = accessibleConsoleSymbol switch
{
ITypeSymbol typeSymbol => generator.TypeExpression(typeSymbol, addImport: true).WithAdditionalAnnotations(Simplifier.AddImportsAnnotation),
_ => generator.IdentifierName(accessibleConsoleSymbol.Name),
};
var newExpression = generator.InvocationExpression(generator.MemberAccessExpression(consoleExpression, operation.TargetMethod.Name), _originalInvocation.ArgumentList.Arguments)
.WithLeadingTrivia(_originalInvocation.GetLeadingTrivia())
.WithTrailingTrivia(_originalInvocation.GetTrailingTrivia());
editor.ReplaceNode(_originalInvocation, newExpression);
return editor.GetChangedDocument();
}
private static bool IsOrImplementSymbol(ITypeSymbol? symbol, ITypeSymbol interfaceSymbol)
{
if (symbol == null)
{
return false;
}
if (SymbolEqualityComparer.Default.Equals(symbol, interfaceSymbol))
{
return true;
}
foreach (var iface in symbol.AllInterfaces)
{
if (SymbolEqualityComparer.Default.Equals(iface, interfaceSymbol))
{
return true;
}
}
return false;
}
private static bool IsInStaticContext(IOperation operation, CancellationToken cancellationToken, out int parentStaticMemberStartPosition)
{
// Local functions can be nested, and an instance local function can be declared
// in a static local function. So, you need to continue to check ancestors when a
// local function is not static.
foreach (var member in operation.Syntax.Ancestors())
{
if (member is LocalFunctionStatementSyntax localFunction)
{
var symbol = operation.SemanticModel!.GetDeclaredSymbol(localFunction, cancellationToken);
if (symbol != null && symbol.IsStatic)
{
parentStaticMemberStartPosition = localFunction.GetLocation().SourceSpan.Start;
return true;
}
}
else if (member is LambdaExpressionSyntax lambdaExpression)
{
var symbol = operation.SemanticModel!.GetSymbolInfo(lambdaExpression, cancellationToken).Symbol;
if (symbol != null && symbol.IsStatic)
{
parentStaticMemberStartPosition = lambdaExpression.GetLocation().SourceSpan.Start;
return true;
}
}
else if (member is AnonymousMethodExpressionSyntax anonymousMethod)
{
var symbol = operation.SemanticModel!.GetSymbolInfo(anonymousMethod, cancellationToken).Symbol;
if (symbol != null && symbol.IsStatic)
{
parentStaticMemberStartPosition = anonymousMethod.GetLocation().SourceSpan.Start;
return true;
}
}
else if (member is MethodDeclarationSyntax methodDeclaration)
{
parentStaticMemberStartPosition = methodDeclaration.GetLocation().SourceSpan.Start;
var symbol = operation.SemanticModel!.GetDeclaredSymbol(methodDeclaration, cancellationToken);
return symbol != null && symbol.IsStatic;
}
}
parentStaticMemberStartPosition = -1;
return false;
}
}

View File

@ -1,35 +0,0 @@
namespace Spectre.Console.Analyzer.FixProviders;
/// <summary>
/// Fix provider to change System.Console calls to AnsiConsole calls.
/// </summary>
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public class StaticAnsiConsoleToInstanceFix : CodeFixProvider
{
/// <inheritdoc />
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id);
/// <inheritdoc />
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
/// <inheritdoc />
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root != null)
{
var methodDeclaration = root.FindNode(context.Span, getInnermostNodeForTie: true).FirstAncestorOrSelf<InvocationExpressionSyntax>();
if (methodDeclaration != null)
{
context.RegisterCodeFix(
new SwitchToAnsiConsoleAction(
context.Document,
methodDeclaration,
"Convert static AnsiConsole calls to local instance."),
context.Diagnostics);
}
}
}
}

View File

@ -1,35 +0,0 @@
namespace Spectre.Console.Analyzer.FixProviders;
/// <summary>
/// Fix provider to change System.Console calls to AnsiConsole calls.
/// </summary>
[ExportCodeFixProvider(LanguageNames.CSharp)]
[Shared]
public class SystemConsoleToAnsiConsoleFix : CodeFixProvider
{
/// <inheritdoc />
public sealed override ImmutableArray<string> FixableDiagnosticIds => ImmutableArray.Create(
Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id);
/// <inheritdoc />
public sealed override FixAllProvider GetFixAllProvider() => WellKnownFixAllProviders.BatchFixer;
/// <inheritdoc />
public override async Task RegisterCodeFixesAsync(CodeFixContext context)
{
var root = await context.Document.GetSyntaxRootAsync(context.CancellationToken).ConfigureAwait(false);
if (root != null)
{
var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf<InvocationExpressionSyntax>();
if (methodDeclaration != null)
{
context.RegisterCodeFix(
new SwitchToAnsiConsoleAction(
context.Document,
methodDeclaration,
"Convert static call to AnsiConsole to Spectre.Console.AnsiConsole"),
context.Diagnostics);
}
}
}
}

View File

@ -1,8 +0,0 @@
using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory;
namespace Spectre.Console.Analyzer;
internal static class Syntax
{
public static readonly UsingDirectiveSyntax SpectreUsing = UsingDirective(QualifiedName(IdentifierName("Spectre"), IdentifierName("Console")));
}

View File

@ -1,14 +0,0 @@
global using System.Collections.Concurrent;
global using System.Collections.Immutable;
global using System.Composition;
global using System.Linq;
global using System.Threading;
global using System.Threading.Tasks;
global using Microsoft.CodeAnalysis;
global using Microsoft.CodeAnalysis.CodeActions;
global using Microsoft.CodeAnalysis.CodeFixes;
global using Microsoft.CodeAnalysis.CSharp;
global using Microsoft.CodeAnalysis.CSharp.Syntax;
global using Microsoft.CodeAnalysis.Diagnostics;
global using Microsoft.CodeAnalysis.Operations;
global using Spectre.Console.Analyzer.CodeActions;

View File

@ -1,33 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<Description>Best practice analyzers for Spectre.Console.</Description>
<TargetFramework>netstandard2.0</TargetFramework>
<IsPackable>true</IsPackable>
<DevelopmentDependency>true</DevelopmentDependency>
<IncludeBuildOutput>false</IncludeBuildOutput>
<Nullable>enable</Nullable>
<NoPackageAnalysis>true</NoPackageAnalysis>
<EnforceExtendedAnalyzerRules>true</EnforceExtendedAnalyzerRules>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="Microsoft.CodeAnalysis.Analyzers" Version="3.3.4" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis" Version="4.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.8.0" PrivateAssets="all" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="4.8.0" PrivateAssets="all" />
</ItemGroup>
<ItemGroup>
<None Remove="bin\Debug\netstandard2.0\\Spectre.Console.Analyzer.dll" />
</ItemGroup>
<ItemGroup>
<None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" />
</ItemGroup>
</Project>

View File

@ -2,19 +2,15 @@
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<NoWarn>SA1633</NoWarn>
</PropertyGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
<ItemGroup Label="REMOVE THIS">
<InternalsVisibleTo Include="Spectre.Console.Cli.Tests" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
<InternalsVisibleTo Include="Spectre.Console.Cli.Tests" />
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
<PropertyGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
@ -23,9 +19,9 @@
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]" />
<PackageReference Include="Nullable" Version="1.3.1">
<PackageReference Include="Nullable">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>

View File

@ -1,23 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with ImageSharp superpowers.</Description>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
</ItemGroup>
<ItemGroup>
<PackageReference Include="SixLabors.ImageSharp" Version="3.1.5" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -1,26 +0,0 @@
<Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<ImplicitUsings>true</ImplicitUsings>
<IsPackable>true</IsPackable>
<Description>A library that extends Spectre.Console with JSON superpowers.</Description>
</PropertyGroup>
<ItemGroup>
<Compile Include="..\Spectre.Console\Internal\Extensions\CharExtensions.cs" Link="Internal\CharExtensions.cs" />
<Compile Include="..\Spectre.Console\Internal\Extensions\EnumerableExtensions.cs" Link="Internal\EnumerableExtensions.cs" />
<Compile Include="..\Spectre.Console\Internal\Text\StringBuffer.cs" Link="Internal\StringBuffer.cs" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />
</ItemGroup>
</Project>

View File

@ -3,21 +3,15 @@
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0</TargetFrameworks>
<IsTestProject>false</IsTestProject>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<Description>Contains testing utilities for Spectre.Console.</Description>
</PropertyGroup>
<ItemGroup>
<ItemGroup Label="REMOVE THIS">
<InternalsVisibleTo Include="Spectre.Console.Tests" />
<InternalsVisibleTo Include="Spectre.Console.Cli.Tests" />
</ItemGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
</ItemGroup>
<ItemGroup Label="Project References">
<ProjectReference Include="..\Spectre.Console.Cli\Spectre.Console.Cli.csproj" />
<ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" />

View File

@ -5,7 +5,7 @@ VisualStudioVersion = 17.1.32414.318
MinimumVisualStudioVersion = 15.0.26124.0
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console", "Spectre.Console\Spectre.Console.csproj", "{80DCBEF3-99D6-46C0-9C5B-42B4534D9113}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Meta", "Meta", "{20595AD4-8D75-4AF8-B6BC-9C38C160423F}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{20595AD4-8D75-4AF8-B6BC-9C38C160423F}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
Directory.Build.props = Directory.Build.props
@ -13,28 +13,34 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Meta", "Meta", "{20595AD4-8
..\dotnet-tools.json = ..\dotnet-tools.json
..\global.json = ..\global.json
stylecop.json = stylecop.json
Directory.Packages.props = Directory.Packages.props
EndProjectSection
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "GitHub", "GitHub", "{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D}"
ProjectSection(SolutionItems) = preProject
..\.github\workflows\ci.yaml = ..\.github\workflows\ci.yaml
..\.github\workflows\docs.yaml = ..\.github\workflows\docs.yaml
..\.github\workflows\publish.yaml = ..\.github\workflows\publish.yaml
EndProjectSection
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.ImageSharp", "Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj", "{0EFE694D-0770-4E71-BF4E-EC2B41362F79}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.ImageSharp", "Extensions\Spectre.Console.ImageSharp\Spectre.Console.ImageSharp.csproj", "{0EFE694D-0770-4E71-BF4E-EC2B41362F79}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Testing", "Spectre.Console.Testing\Spectre.Console.Testing.csproj", "{7D5F6704-8249-46DD-906C-9E66419F215F}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Plugins", "Plugins", "{E0E45070-123C-4A4D-AA98-2A780308876C}"
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Extensions", "Extensions", "{E0E45070-123C-4A4D-AA98-2A780308876C}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Tests", "..\test\Spectre.Console.Tests\Spectre.Console.Tests.csproj", "{60A4CADD-2B3D-48ED-89C0-1637A1F111AE}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Tests", "Tests\Spectre.Console.Tests\Spectre.Console.Tests.csproj", "{60A4CADD-2B3D-48ED-89C0-1637A1F111AE}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli", "Spectre.Console.Cli\Spectre.Console.Cli.csproj", "{1B67B74F-1243-4381-9A2B-86EA66D135C5}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli.Tests", "..\test\Spectre.Console.Cli.Tests\Spectre.Console.Cli.Tests.csproj", "{E07C46D2-714F-4116-BADD-FEE09617A9C4}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Cli.Tests", "Tests\Spectre.Console.Cli.Tests\Spectre.Console.Cli.Tests.csproj", "{E07C46D2-714F-4116-BADD-FEE09617A9C4}"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Json", "Spectre.Console.Json\Spectre.Console.Json.csproj", "{579E6E31-1E2F-4FE1-8F8C-9770878993E9}"
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Spectre.Console.Json", "Extensions\Spectre.Console.Json\Spectre.Console.Json.csproj", "{579E6E31-1E2F-4FE1-8F8C-9770878993E9}"
EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tests", "Tests", "{F34EFD87-6CEA-453F-858B-094EA413578C}"
ProjectSection(SolutionItems) = preProject
Tests\Directory.Build.props = Tests\Directory.Build.props
Tests\.editorconfig = Tests\.editorconfig
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
@ -138,6 +144,8 @@ Global
{C3E2CB5C-1517-4C75-B59A-93D4E22BEC8D} = {20595AD4-8D75-4AF8-B6BC-9C38C160423F}
{0EFE694D-0770-4E71-BF4E-EC2B41362F79} = {E0E45070-123C-4A4D-AA98-2A780308876C}
{579E6E31-1E2F-4FE1-8F8C-9770878993E9} = {E0E45070-123C-4A4D-AA98-2A780308876C}
{60A4CADD-2B3D-48ED-89C0-1637A1F111AE} = {F34EFD87-6CEA-453F-858B-094EA413578C}
{E07C46D2-714F-4116-BADD-FEE09617A9C4} = {F34EFD87-6CEA-453F-858B-094EA413578C}
EndGlobalSection
GlobalSection(ExtensibilityGlobals) = postSolution
SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C}

View File

@ -1,2 +0,0 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/Highlighting/InspectionSeverities/=CheckNamespace/@EntryIndexedValue">DO_NOT_SHOW</s:String></wpf:ResourceDictionary>

View File

@ -1,6 +0,0 @@
<SolutionConfiguration>
<Settings>
<AllowParallelTestExecution>True</AllowParallelTestExecution>
<SolutionConfigured>True</SolutionConfigured>
</Settings>
</SolutionConfiguration>

View File

@ -2,22 +2,22 @@
<PropertyGroup>
<TargetFrameworks>net8.0;net7.0;net6.0;netstandard2.0</TargetFrameworks>
<Nullable>enable</Nullable>
<IsPackable>true</IsPackable>
<NoWarn>SA1633</NoWarn>
<DefineConstants>$(DefineConstants)TRACE;WCWIDTH_VISIBILITY_INTERNAL</DefineConstants>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="..\stylecop.json" Link="Properties/stylecop.json" />
<EmbeddedResource Include="Widgets\Figlet\Fonts\Standard.flf" />
<None Remove="Widgets\Figlet\Fonts\Standard.flf" />
<None Include="../../resources/gfx/small-logo.png" Pack="true" PackagePath="\" Link="Properties/small-logo.png" />
<InternalsVisibleTo Include="$(AssemblyName).Tests" />
<ItemGroup Label="REMOVE THIS">
<InternalsVisibleTo Include="$(AssemblyName).Tests"/>
</ItemGroup>
<ItemGroup>
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Memory" Version="4.5.5" />
<PackageReference Include="Wcwidth.Sources" Version="2.0.0">
<ItemGroup Label="Standard Figlet font">
<EmbeddedResource Include="Widgets\Figlet\Fonts\Standard.flf"/>
<None Remove="Widgets\Figlet\Fonts\Standard.flf"/>
</ItemGroup>
<ItemGroup Label="Dependencies">
<PackageReference Condition="'$(TargetFramework)' == 'netstandard2.0'" Include="System.Memory"/>
<PackageReference Include="Wcwidth.Sources">
<PrivateAssets>all</PrivateAssets>
</PackageReference>
</ItemGroup>
@ -28,17 +28,12 @@
</PropertyGroup>
<ItemGroup Condition="'$(TargetFramework)' == 'netstandard2.0'">
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" Version="1.0.0-alpha.160" PrivateAssets="all" />
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]" />
<PackageReference Include="Nullable" Version="1.3.1">
<PackageReference Include="TunnelVisionLabs.ReferenceAssemblyAnnotator" PrivateAssets="all"/>
<PackageDownload Include="Microsoft.NETCore.App.Ref" Version="[$(AnnotatedReferenceAssemblyVersion)]"/>
<PackageReference Include="Nullable">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
</ItemGroup>
<PropertyGroup>
<DefineConstants>$(DefineConstants)TRACE;WCWIDTH_VISIBILITY_INTERNAL</DefineConstants>
</PropertyGroup>
</Project>

107
src/Tests/.editorconfig Normal file
View File

@ -0,0 +1,107 @@
root = false
[*.cs]
# Default severity for analyzer diagnostics with category 'StyleCop.CSharp.DocumentationRules'
dotnet_analyzer_diagnostic.category-StyleCop.CSharp.DocumentationRules.severity = none
# IDE0055: Fix formatting
dotnet_diagnostic.IDE0055.severity = warning
# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
# SA1633: File should have header
dotnet_diagnostic.SA1633.severity = none
# SA1201: Elements should appear in the correct order
dotnet_diagnostic.SA1201.severity = none
# SA1202: Public members should come before private members
dotnet_diagnostic.SA1202.severity = none
# SA1309: Field names should not begin with underscore
dotnet_diagnostic.SA1309.severity = none
# SA1404: Code analysis suppressions should have justification
dotnet_diagnostic.SA1404.severity = none
# SA1516: Elements should be separated by a blank line
dotnet_diagnostic.SA1516.severity = none
# CA1303: Do not pass literals as localized parameters
dotnet_diagnostic.CA1303.severity = none
# CSA1204: Static members should appear before non-static members
dotnet_diagnostic.SA1204.severity = none
# IDE0052: Remove unread private members
dotnet_diagnostic.IDE0052.severity = warning
# IDE0063: Use simple 'using' statement
csharp_prefer_simple_using_statement = false:suggestion
# IDE0018: Variable declaration can be inlined
dotnet_diagnostic.IDE0018.severity = warning
# SA1625: Element documenation should not be copied and pasted
dotnet_diagnostic.SA1625.severity = none
# IDE0005: Using directive is unnecessary
dotnet_diagnostic.IDE0005.severity = warning
# SA1117: Parameters should be on same line or separate lines
dotnet_diagnostic.SA1117.severity = none
# SA1404: Code analysis suppression should have justification
dotnet_diagnostic.SA1404.severity = none
# SA1101: Prefix local calls with this
dotnet_diagnostic.SA1101.severity = none
# SA1633: File should have header
dotnet_diagnostic.SA1633.severity = none
# SA1649: File name should match first type name
dotnet_diagnostic.SA1649.severity = none
# SA1402: File may only contain a single type
dotnet_diagnostic.SA1402.severity = none
# CA1814: Prefer jagged arrays over multidimensional
dotnet_diagnostic.CA1814.severity = none
# RCS1194: Implement exception constructors.
dotnet_diagnostic.RCS1194.severity = none
# CA1032: Implement standard exception constructors
dotnet_diagnostic.CA1032.severity = none
# CA1826: Do not use Enumerable methods on indexable collections. Instead use the collection directly
dotnet_diagnostic.CA1826.severity = none
# RCS1079: Throwing of new NotImplementedException.
dotnet_diagnostic.RCS1079.severity = warning
# RCS1057: Add empty line between declarations.
dotnet_diagnostic.RCS1057.severity = none
# RCS1057: Validate arguments correctly
dotnet_diagnostic.RCS1227.severity = none
# IDE0004: Remove Unnecessary Cast
dotnet_diagnostic.IDE0004.severity = warning
# CA1810: Initialize reference type static fields inline
dotnet_diagnostic.CA1810.severity = none
# IDE0044: Add readonly modifier
dotnet_diagnostic.IDE0044.severity = warning
# RCS1047: Non-asynchronous method name should not end with 'Async'.
dotnet_diagnostic.RCS1047.severity = none
# RCS1090: Call 'ConfigureAwait(false)'.
dotnet_diagnostic.RCS1090.severity = warning
# CS1591: Missing XML comment for publicly visible type or member
dotnet_diagnostic.CS1591.severity = none

View File

@ -0,0 +1,11 @@
<Project>
<PropertyGroup Label="Settings">
<LangVersion>12</LangVersion>
<IsPackable>false</IsPackable>
<GenerateDocumentationFile>true</GenerateDocumentationFile>
</PropertyGroup>
<ItemGroup>
<AdditionalFiles Include="$(MSBuildThisFileDirectory)/../stylecop.json" Link="Properties/stylecop.json"/>
</ItemGroup>
</Project>

View File

@ -0,0 +1,18 @@
namespace Spectre.Console.Tests;
public static class Constants
{
public static string[] VersionCommand { get; } =
new[]
{
CliConstants.Commands.Branch,
CliConstants.Commands.Version,
};
public static string[] XmlDocCommand { get; } =
new[]
{
CliConstants.Commands.Branch,
CliConstants.Commands.XmlDoc,
};
}

View File

@ -0,0 +1,8 @@
using SystemConsole = System.Console;
namespace Spectre.Console.Tests.Data;
public abstract class AnimalCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
{
}

View File

@ -0,0 +1,28 @@
namespace Spectre.Console.Tests.Data;
public sealed class AsynchronousCommand : AsyncCommand<AsynchronousCommandSettings>
{
private readonly IAnsiConsole _console;
public AsynchronousCommand(IAnsiConsole console)
{
_console = console;
}
public async override Task<int> ExecuteAsync(CommandContext context, AsynchronousCommandSettings settings)
{
// Simulate a long running asynchronous task
await Task.Delay(200);
if (settings.ThrowException)
{
throw new Exception($"Throwing exception asynchronously");
}
else
{
_console.WriteLine($"Finished executing asynchronously");
}
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public class CatCommand : AnimalCommand<CatSettings>
{
public override int Execute(CommandContext context, CatSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,30 @@
namespace Spectre.Console.Tests.Data;
[Description("The dog command.")]
public class DogCommand : AnimalCommand<DogSettings>
{
public override ValidationResult Validate(CommandContext context, DogSettings settings)
{
if (context is null)
{
throw new System.ArgumentNullException(nameof(context));
}
if (settings is null)
{
throw new System.ArgumentNullException(nameof(settings));
}
if (settings.Age > 100 && !context.Remaining.Raw.Contains("zombie"))
{
return ValidationResult.Error("Dog is too old...");
}
return base.Validate(context, settings);
}
public override int Execute(CommandContext context, DogSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,34 @@
namespace Spectre.Console.Tests.Data;
public sealed class DumpRemainingCommand : Command<EmptyCommandSettings>
{
private readonly IAnsiConsole _console;
public DumpRemainingCommand(IAnsiConsole console)
{
_console = console;
}
public override int Execute(CommandContext context, EmptyCommandSettings settings)
{
if (context.Remaining.Raw.Count > 0)
{
_console.WriteLine("# Raw");
foreach (var item in context.Remaining.Raw)
{
_console.WriteLine(item);
}
}
if (context.Remaining.Parsed.Count > 0)
{
_console.WriteLine("# Parsed");
foreach (var item in context.Remaining.Parsed)
{
_console.WriteLine(string.Format("{0}={1}", item.Key, string.Join(",", item.Select(x => x))));
}
}
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public sealed class EmptyCommand : Command<EmptyCommandSettings>
{
public override int Execute(CommandContext context, EmptyCommandSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
public sealed class GenericCommand<TSettings> : Command<TSettings>
where TSettings : CommandSettings
{
public override int Execute(CommandContext context, TSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
[Description("The giraffe command.")]
public sealed class GiraffeCommand : Command<GiraffeSettings>
{
public override int Execute(CommandContext context, GiraffeSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,17 @@
using Spectre.Console;
public class GreeterCommand : Command<OptionalArgumentWithDefaultValueSettings>
{
private readonly IAnsiConsole _console;
public GreeterCommand(IAnsiConsole console)
{
_console = console;
}
public override int Execute(CommandContext context, OptionalArgumentWithDefaultValueSettings settings)
{
_console.WriteLine(settings.Greeting);
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public sealed class HiddenOptionsCommand : Command<HiddenOptionSettings>
{
public override int Execute(CommandContext context, HiddenOptionSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
[Description("The horse command.")]
public class HorseCommand : AnimalCommand<HorseSettings>
{
public override int Execute(CommandContext context, HorseSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public sealed class InvalidCommand : Command<InvalidSettings>
{
public override int Execute(CommandContext context, InvalidSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
[Description("The lion command.")]
public class LionCommand : AnimalCommand<LionSettings>
{
public override int Execute(CommandContext context, LionSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,12 @@
namespace Spectre.Console.Tests.Data;
public sealed class NoDescriptionCommand : Command<EmptyCommandSettings>
{
[CommandOption("-f|--foo <VALUE>")]
public int Foo { get; set; }
public override int Execute(CommandContext context, EmptyCommandSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public class OptionVectorCommand : Command<OptionVectorSettings>
{
public override int Execute(CommandContext context, OptionVectorSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,9 @@
namespace Spectre.Console.Tests.Data;
public sealed class ThrowingCommand : Command<ThrowingCommandSettings>
{
public override int Execute(CommandContext context, ThrowingCommandSettings settings)
{
throw new InvalidOperationException("W00t?");
}
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
[Description("The turtle command.")]
public class TurtleCommand : AnimalCommand<TurtleSettings>
{
public override int Execute(CommandContext context, TurtleSettings settings)
{
return 0;
}
}

View File

@ -0,0 +1,18 @@
namespace Spectre.Console.Tests.Data;
public sealed class VersionCommand : Command<VersionSettings>
{
private readonly IAnsiConsole _console;
public VersionCommand(IAnsiConsole console)
{
_console = console;
}
public override int Execute(CommandContext context, VersionSettings settings)
{
_console.WriteLine($"VersionCommand ran, Version: {settings.Version ?? string.Empty}");
return 0;
}
}

View File

@ -0,0 +1,14 @@
namespace Spectre.Console.Tests.Data;
public sealed class CatAgilityConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string stringValue)
{
return stringValue.Length;
}
return base.ConvertFrom(context, culture, value);
}
}

View File

@ -0,0 +1,14 @@
namespace Spectre.Console.Tests.Data;
public sealed class StringToIntegerConverter : TypeConverter
{
public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value)
{
if (value is string stringValue)
{
return int.Parse(stringValue, CultureInfo.InvariantCulture);
}
return base.ConvertFrom(context, culture, value);
}
}

View File

@ -0,0 +1,34 @@
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli.Tests.Data.Help;
internal class CustomHelpProvider : HelpProvider
{
private readonly string version;
public CustomHelpProvider(ICommandAppSettings settings, string version)
: base(settings)
{
this.version = version;
}
public override IEnumerable<IRenderable> GetHeader(ICommandModel model, ICommandInfo command)
{
return new IRenderable[]
{
new Text("--------------------------------------"), Text.NewLine,
new Text("--- CUSTOM HELP PROVIDER ---"), Text.NewLine,
new Text("--------------------------------------"), Text.NewLine,
Text.NewLine,
};
}
public override IEnumerable<IRenderable> GetFooter(ICommandModel model, ICommandInfo command)
{
return new IRenderable[]
{
Text.NewLine,
new Text($"Version {version}"),
};
}
}

View File

@ -0,0 +1,21 @@
using Spectre.Console.Rendering;
namespace Spectre.Console.Cli.Tests.Data.Help;
internal class RedirectHelpProvider : IHelpProvider
{
public virtual IEnumerable<IRenderable> Write(ICommandModel model)
{
return Write(model, null);
}
#nullable enable
public virtual IEnumerable<IRenderable> Write(ICommandModel model, ICommandInfo? command)
#nullable disable
{
return new[]
{
new Text("Help has moved online. Please see: http://www.example.com"),
Text.NewLine,
};
}
}

View File

@ -0,0 +1,11 @@
namespace Spectre.Console.Cli.Tests.Data.Help;
internal class RenderMarkupHelpProvider : HelpProvider
{
protected override bool RenderMarkupInline { get; } = true;
public RenderMarkupHelpProvider(ICommandAppSettings settings)
: base(settings)
{
}
}

View File

@ -0,0 +1,14 @@
namespace Spectre.Console.Tests.Data;
public abstract class AnimalSettings : CommandSettings
{
[CommandOption("-a|--alive|--not-dead")]
[Description("Indicates whether or not the animal is alive.")]
public bool IsAlive { get; set; }
[CommandArgument(1, "[LEGS]")]
[Description("The number of legs.")]
[EvenNumberValidator("Animals must have an even number of legs.")]
[PositiveNumberValidator("Number of legs must be greater than 0.")]
public int Legs { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace Spectre.Console.Tests.Data;
public sealed class ArgumentOrderSettings : CommandSettings
{
[CommandArgument(0, "[QUX]")]
public int Qux { get; set; }
[CommandArgument(3, "<CORGI>")]
public int Corgi { get; set; }
[CommandArgument(1, "<BAR>")]
public int Bar { get; set; }
[CommandArgument(2, "<BAZ>")]
public int Baz { get; set; }
[CommandArgument(0, "<FOO>")]
public int Foo { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public class ArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace Spectre.Console.Tests.Data;
public sealed class AsynchronousCommandSettings : CommandSettings
{
[CommandOption("--ThrowException")]
[DefaultValue(false)]
public bool ThrowException { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace Spectre.Console.Tests.Data;
public class BarCommandSettings : FooCommandSettings
{
[CommandArgument(0, "<CORGI>")]
[Description("The corgi value.")]
public string Corgi { get; set; }
}

View File

@ -0,0 +1,11 @@
namespace Spectre.Console.Tests.Data;
public class CatSettings : MammalSettings
{
[CommandOption("--agility <VALUE>")]
[TypeConverter(typeof(CatAgilityConverter))]
[DefaultValue(10)]
[Description("The agility between 0 and 100.")]
[PositiveNumberValidator("Agility cannot be negative.")]
public int Agility { get; set; }
}

View File

@ -0,0 +1,20 @@
namespace Spectre.Console.Tests.Data;
public sealed class DogSettings : MammalSettings
{
[CommandArgument(0, "<AGE>")]
public int Age { get; set; }
[CommandOption("-g|--good-boy")]
public bool GoodBoy { get; set; }
public override ValidationResult Validate()
{
if (Name == "Tiger")
{
return ValidationResult.Error("Tiger is not a dog name!");
}
return ValidationResult.Success();
}
}

View File

@ -0,0 +1,8 @@
namespace Spectre.Console.Tests.Data;
public class FooCommandSettings : CommandSettings
{
[CommandArgument(0, "[QUX]")]
[Description("The qux value.")]
public string Qux { get; set; }
}

View File

@ -0,0 +1,8 @@
namespace Spectre.Console.Tests.Data;
public sealed class GiraffeSettings : MammalSettings
{
[CommandArgument(0, "<LENGTH>")]
[Description("The option description.")]
public int Length { get; set; }
}

View File

@ -0,0 +1,16 @@
namespace Spectre.Console.Tests.Data;
public sealed class HiddenOptionSettings : CommandSettings
{
[CommandArgument(0, "<FOO>")]
[Description("Dummy argument FOO")]
public int Foo { get; set; }
[CommandOption("--bar", IsHidden = true)]
[Description("You should not be able to read this unless you used the 'cli explain' command with the '--hidden' option")]
public int Bar { get; set; }
[CommandOption("--baz")]
[Description("Dummy option BAZ")]
public int Baz { get; set; }
}

View File

@ -0,0 +1,16 @@
using System.IO;
namespace Spectre.Console.Tests.Data;
public class HorseSettings : MammalSettings
{
[CommandOption("-d|--day <Mon|Tue>")]
public DayOfWeek Day { get; set; }
[CommandOption("--file")]
[DefaultValue("food.txt")]
public FileInfo File { get; set; }
[CommandOption("--directory")]
public DirectoryInfo Directory { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public sealed class InvalidSettings : CommandSettings
{
[CommandOption("-f|--foo [BAR]")]
public string Value { get; set; }
}

View File

@ -0,0 +1,21 @@
namespace Spectre.Console.Tests.Data;
public class LionSettings : CatSettings
{
[CommandArgument(0, "<TEETH>")]
[Description("The number of teeth the lion has.")]
public int Teeth { get; set; }
[CommandOption("-c <CHILDREN>")]
[Description("The number of children the lion has.")]
public int Children { get; set; }
[CommandOption("-d <DAY>")]
[Description("The days the lion goes hunting.")]
[DefaultValue(new[] { DayOfWeek.Monday, DayOfWeek.Thursday })]
public DayOfWeek[] HuntDays { get; set; }
[CommandOption("-w|--weight [WEIGHT]")]
[Description("The weight of the lion, in kgs.")]
public FlagValue<int?> Weight { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public class MammalSettings : AnimalSettings
{
[CommandOption("-n|-p|--name|--pet-name <VALUE>")]
public string Name { get; set; }
}

View File

@ -0,0 +1,19 @@
namespace Spectre.Console.Tests.Data;
public class MultipleArgumentVectorSettings : CommandSettings
{
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
[CommandArgument(0, "<Bars>")]
public string[] Bar { get; set; }
}
public class MultipleArgumentVectorSpecifiedFirstSettings : CommandSettings
{
[CommandArgument(1, "[Bar]")]
public string Bar { get; set; }
[CommandArgument(0, "<Foos>")]
public string[] Foo { get; set; }
}

View File

@ -0,0 +1,10 @@
namespace Spectre.Console.Tests.Data;
public class OptionVectorSettings : CommandSettings
{
[CommandOption("--foo")]
public string[] Foo { get; set; }
[CommandOption("--bar")]
public int[] Bar { get; set; }
}

View File

@ -0,0 +1,50 @@
namespace Spectre.Console.Tests.Data;
public sealed class OptionalArgumentWithDefaultValueSettings : CommandSettings
{
[CommandArgument(0, "[GREETING]")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}
public sealed class OptionalArgumentWithPropertyInitializerSettings : CommandSettings
{
[CommandArgument(0, "[NAMES]")]
public string[] Names { get; set; } = Array.Empty<string>();
[CommandOption("-c")]
public int Count { get; set; } = 1;
[CommandOption("--value")]
public int Value { get; set; } = 0;
}
public sealed class OptionalArgumentWithDefaultValueAndTypeConverterSettings : CommandSettings
{
[CommandArgument(0, "[GREETING]")]
[DefaultValue("5")]
[TypeConverter(typeof(StringToIntegerConverter))]
public int Greeting { get; set; }
}
public sealed class RequiredArgumentWithDefaultValueSettings : CommandSettings
{
[CommandArgument(0, "<GREETING>")]
[DefaultValue("Hello World")]
public string Greeting { get; set; }
}
public sealed class OptionWithArrayOfEnumDefaultValueSettings : CommandSettings
{
[CommandOption("--days")]
[DefaultValue(new[] { DayOfWeek.Sunday, DayOfWeek.Saturday })]
public DayOfWeek[] Days { get; set; }
}
public sealed class OptionWithArrayOfStringDefaultValueAndTypeConverterSettings : CommandSettings
{
[CommandOption("--numbers")]
[DefaultValue(new[] { "2", "3" })]
[TypeConverter(typeof(StringToIntegerConverter))]
public int[] Numbers { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public class ReptileSettings : AnimalSettings
{
[CommandOption("-n|-p|--name|--pet-name <VALUE>")]
public string Name { get; set; }
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public sealed class StringOptionSettings : CommandSettings
{
[CommandOption("-f|--foo")]
public string Foo { get; set; }
}

View File

@ -0,0 +1,5 @@
namespace Spectre.Console.Tests.Data;
public sealed class ThrowingCommandSettings : CommandSettings
{
}

View File

@ -0,0 +1,16 @@
namespace Spectre.Console.Tests.Data;
public sealed class TurtleSettings : ReptileSettings
{
public TurtleSettings(string name)
{
Name = name;
}
public override ValidationResult Validate()
{
return Name != "Lonely George"
? ValidationResult.Error("Only 'Lonely George' is valid name for a turtle!")
: ValidationResult.Success();
}
}

View File

@ -0,0 +1,7 @@
namespace Spectre.Console.Tests.Data;
public sealed class VersionSettings : CommandSettings
{
[CommandOption("-v|--version")]
public string Version { get; set; }
}

Some files were not shown because too many files have changed in this diff Show More