60 Commits
2.0 ... 2.1

Author SHA1 Message Date
Tyrrrz
05a70175cc Update version 2022-01-04 22:35:59 +02:00
Tyrrrz
33ec2eb3a0 Cleanup 2022-01-04 22:31:50 +02:00
David Fallah
f6ef6cd4c0 Fix ordering of parameters within command help usage (#118) 2022-01-04 12:12:17 -08:00
Tyrrrz
a9ef693dc1 Share more stuff 2021-12-11 00:11:20 +02:00
Tyrrrz
98bbd666dc Update badges 2021-12-10 23:22:22 +02:00
Tyrrrz
4e7ed830f8 Move to shared workflows 2021-12-10 23:21:38 +02:00
Tyrrrz
ef87ff76fc Use top-level statements in demo 2021-12-08 23:52:33 +02:00
Tyrrrz
2feeb21270 C#10ify 2021-12-08 23:43:35 +02:00
Tyrrrz
9990387cfa Update readme 2021-12-05 22:20:00 +02:00
Tyrrrz
bc1bdca7c6 Update nuget packages 2021-12-05 22:05:21 +02:00
Tyrrrz
2a992d37df Update readme 2021-12-05 21:59:53 +02:00
Tyrrrz
15c87aecbb Update CI to .NET 6 2021-11-08 23:34:14 +02:00
Alexey Golub
10a46451ac Update Readme.md 2021-09-30 15:21:11 -07:00
Alexey Golub
e4c6a4174b Update readme 2021-09-04 04:20:38 -07:00
Alexey Golub
4c65f7bbee Update readme 2021-08-30 18:34:26 -07:00
Alexey Golub
5f21de0df5 Refactor webhook in CD 2021-08-28 10:30:58 -07:00
Alexey Golub
9b01b67d98 Update CD 2021-07-28 15:13:09 -07:00
Tyrrrz
4508f5e211 Add Discord 2021-07-26 19:30:54 +03:00
Alex Rosenfeld
f0cbc46df4 Add ReadKey to IConsole (#111)
Co-authored-by: Alexey Golub <tyrrrrrr@gmail.com>
2021-07-23 11:46:00 -07:00
Alex Rosenfeld
6c96e9e173 Add a clear console function (#110) 2021-07-19 04:33:07 -07:00
Tyrrrz
51cca36d2a Update version 2021-07-17 21:37:32 +03:00
Tyrrrz
84672c92f6 Unwrap TargetInvocationException to provide more user-friendly errors when binding fails 2021-07-17 21:32:15 +03:00
Tyrrrz
b1d01898b6 Add test for preamble omission 2021-07-10 19:43:21 +03:00
Tyrrrz
441a47a1a8 Update version 2021-07-09 22:23:46 +03:00
Tyrrrz
8abd7219a1 Better shimming in NoPreambleEncoding 2021-07-09 22:00:31 +03:00
Tyrrrz
df73a0bfe8 Update GitHib issue forms 2021-06-24 21:40:08 +03:00
Tyrrrz
55d12dc721 Add readme to package 2021-06-17 20:36:35 +03:00
Alexey Golub
a6ee44c1bb Fix typo in readme 2021-06-13 06:19:26 -07:00
Tyrrrz
76816e22f1 Use Basic.Reference.Assemblies to simplify reference resolving for dynamic assemblies in tests
Note: bumped `Microsoft.CodeAnalysis.CSharp` in test projects, but didn't touch the one in CliFx.Analyzers as it may have unintended side-effects.
2021-05-10 21:10:42 +03:00
Tyrrrz
daf25e59d6 Fix deprecation warning 2021-05-10 21:06:36 +03:00
Tyrrrz
f2b4e53615 Update version 2021-04-24 20:59:10 +03:00
Tyrrrz
2d519ab190 Remove the usage of ConsoleColor.DarkGray because it looks bad in some terminals
Fixes #104
2021-04-24 20:48:06 +03:00
Tyrrrz
2d479c9cb6 Refactor 2021-04-24 20:43:35 +03:00
Tyrrrz
2bb7e13e51 Use issue forms 2021-04-22 22:11:39 +03:00
Tyrrrz
6e1dfdcdd4 Update readme 2021-04-22 21:08:16 +03:00
Tyrrrz
5ba647e5c1 Update readme 2021-04-22 21:05:35 +03:00
Tyrrrz
853492695f Update readme 2021-04-22 21:04:36 +03:00
Robert Dailey
d5d72c7c50 Show choices for nullable enums in enumerable (#105) 2021-04-22 15:28:33 +03:00
Tyrrrz
d676b5832e Fix discrepancies in unicode handling between ConsoleWriter and Console.Write(...) 2021-04-21 03:16:18 +03:00
Tyrrrz
28097afc1e Update NuGet packages 2021-04-18 19:38:35 +03:00
Tyrrrz
fda96586f3 Update NuGet.config 2021-04-17 21:23:19 +03:00
Tyrrrz
fc5af8dbbc Don't write default value in help text for types that don't override ToString() 2021-04-16 23:28:39 +03:00
Tyrrrz
4835e64388 Remove GHA workarounds 2021-04-13 22:29:15 +03:00
Tyrrrz
0999c33f93 Add NuGet.config 2021-04-13 22:20:07 +03:00
Tyrrrz
595805255a Update version 2021-04-09 22:24:06 +03:00
Tyrrrz
65eaa912cf Refactor 2021-04-08 20:53:48 +03:00
Robert Dailey
038f48b78e Show choices on non-scalar enum parameters and options (#102) 2021-04-08 20:51:17 +03:00
Tyrrrz
d7460244b7 Update version 2021-03-31 12:11:50 +03:00
Tyrrrz
02766868fc Streamline analyzer packaging 2021-03-31 12:11:36 +03:00
Tyrrrz
8d7d25a144 Cleanup 2021-03-31 11:40:37 +03:00
Tyrrrz
17ded54e24 Update readme 2021-03-31 11:29:31 +03:00
Tyrrrz
54a4c32ddf Fix nullref in SystemConsoleShouldBeAvoidedAnalyzer 2021-03-31 11:28:48 +03:00
Tyrrrz
6d46e82145 Add test for SystemConsoleShouldBeAvoidedAnalyzer for when System.Console isn't used 2021-03-31 00:11:36 +03:00
Tyrrrz
fd4a2a18fe Improve comment on IConsole.RegisterCancellationHandler() 2021-03-30 16:22:10 +03:00
Tyrrrz
bfe99d620e Refactor IConsole.WithColors(...) 2021-03-30 16:10:11 +03:00
Tyrrrz
c5a111207f Seal attributes 2021-03-25 06:01:46 +02:00
Tyrrrz
544945c0e6 Don't reference analyzer assembly from main assembly 2021-03-24 02:42:03 +02:00
Tyrrrz
c616cdd750 Update version 2021-03-24 02:40:10 +02:00
Tyrrrz
d3c396956d Fix StackFrame.ParseMany(...) being too paranoid about its own failure 2021-03-24 02:34:36 +02:00
Tyrrrz
d0cbbc6d9a Don't highlight valid values in help text 2021-03-23 01:56:28 +02:00
151 changed files with 8425 additions and 8041 deletions

3
.github/FUNDING.yml vendored
View File

@@ -1,3 +0,0 @@
github: Tyrrrz
patreon: Tyrrrz
custom: ['buymeacoffee.com/Tyrrrz', 'tyrrrz.me/donate']

42
.github/ISSUE_TEMPLATE/bug-report.yml vendored Normal file
View File

@@ -0,0 +1,42 @@
name: 🐞 Bug report
description: Report broken functionality.
labels: [bug]
body:
- type: markdown
attributes:
value: |
🧐 **Guidelines:**
- Search through [existing issues](https://github.com/Tyrrrz/CliFx/issues?q=is%3Aissue) first to ensure that this bug has not been reported before.
- Write a descriptive title for your issue. Avoid generic or vague titles such as "Something's not working" or "A couple of problems".
- Keep your issue focused on one single problem. If you have multiple bug reports, please create separate issues for each of them.
- Provide as much context as possible in the details section. Include screenshots, screen recordings, links, references, or anything else you may consider relevant.
- If you want to ask a question instead of reporting a bug, please use [discussions](https://github.com/Tyrrrz/CliFx/discussions/new) instead.
- type: input
attributes:
label: Version
description: Which version of CliFx does this bug affect?
placeholder: ver X.Y.Z
validations:
required: true
- type: textarea
attributes:
label: Details
description: Clear and thorough explanation of the bug.
placeholder: I was doing X expecting Y to happen, but Z happened instead.
validations:
required: true
- type: textarea
attributes:
label: Steps to reproduce
description: Minimum steps required to reproduce the bug.
placeholder: |
- Step 1
- Step 2
- Step 3
validations:
required: true

8
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: 💬 Discord server
url: https://discord.gg/2SUWKFnHSm
about: Chat with the project community.
- name: 🗨 Discussions
url: https://github.com/Tyrrrz/CliFx/discussions/new
about: Ask and answer questions.

View File

@@ -0,0 +1,22 @@
name: ✨ Feature request
description: Request a new feature.
labels: [enhancement]
body:
- type: markdown
attributes:
value: |
🧐 **Guidelines:**
- Search through [existing issues](https://github.com/Tyrrrz/CliFx/issues?q=is%3Aissue) first to ensure that this feature has not been requested before.
- Write a descriptive title for your issue. Avoid generic or vague titles such as "Some suggestions" or "Ideas for improvement".
- Keep your issue focused on one single problem. If you have multiple feature requests, please create separate issues for each of them.
- Provide as much context as possible in the details section. Include screenshots, screen recordings, links, references, or anything else you may consider relevant.
- If you want to ask a question instead of requesting a feature, please use [discussions](https://github.com/Tyrrrz/CliFx/discussions/new) instead.
- type: textarea
attributes:
label: Details
description: Clear and thorough explanation of the feature you have in mind.
validations:
required: true

View File

@@ -1,27 +0,0 @@
name: CD
on:
push:
tags:
- "*"
jobs:
build:
runs-on: ubuntu-latest
steps:
- name: Checkout
uses: actions/checkout@v2.3.3
- name: Install .NET
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: 5.0.x
- name: Pack
run: |
dotnet nuget locals all --clear
dotnet pack CliFx --configuration Release
- name: Deploy
run: dotnet nuget push CliFx/bin/Release/*.nupkg -s nuget.org -k ${{ secrets.NUGET_TOKEN }}

View File

@@ -1,30 +0,0 @@
name: CI
on: [push, pull_request]
jobs:
build:
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest, windows-latest, macos-latest]
steps:
- name: Checkout
uses: actions/checkout@v2.3.3
- name: Install .NET
uses: actions/setup-dotnet@v1.7.2
with:
dotnet-version: 5.0.x
- name: Build & test
run: |
dotnet nuget locals all --clear
dotnet test --configuration Release --logger GitHubActions
- name: Upload coverage
uses: codecov/codecov-action@v1.0.5
with:
token: ${{ secrets.CODECOV_TOKEN }}

11
.github/workflows/main.yml vendored Normal file
View File

@@ -0,0 +1,11 @@
name: main
on: [push, pull_request]
jobs:
main:
uses: Tyrrrz/.github/.github/workflows/NuGet.yml@master
secrets:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
NUGET_TOKEN: ${{ secrets.NUGET_TOKEN }}
DISCORD_WEBHOOK: ${{ secrets.DISCORD_WEBHOOK }}

View File

@@ -1,3 +1,39 @@
### v2.1 (04-Jan-2022)
- Added `IConsole.Clear()` with corresponding implementations in `SystemConsole`, `FakeConsole`, and `FakeInMemoryConsole`. (Thanks [@Alex Rosenfeld](https://github.com/alexrosenfeld10))
- Added `IConsole.ReadKey()` with corresponding implementations in `SystemConsole`, `FakeConsole`, and `FakeInMemoryConsole`. (Thanks [@Alex Rosenfeld](https://github.com/alexrosenfeld10))
- Fixed an issue that caused parameters to appear out of order in the usage format section of the help text. (Thanks [@David Fallah](https://github.com/TAGC))
### v2.0.6 (17-Jul-2021)
- Fixed an issue where an exception thrown via reflection during parameter or option binding resulted in `Exception has been thrown by the target of an invocation` error instead of a more useful message. Such exceptions will now be unwrapped to provide better user experience.
### v2.0.5 (09-Jul-2021)
- Fixed an issue where calling `IConsole.Output.Encoding.EncodingName` and some other members threw an exception.
- Added readme file to the package.
### v2.0.4 (24-Apr-2021)
- Fixed an issue where output and error streams in `SystemConsole` defaulted to UTF8 encoding with BOM when the application was running with UTF8 codepage. `ConsoleWriter` will now discard preamble from the specified encoding. This fix brings the behavior of `SystemConsole` in line with .NET's own `System.Console` which also discards preamble for output and error streams.
- Fixed an issue where help text tried to show default values for parameters and options whose type does not override `ToString()` method.
- Fixed an issue where help text didn't show default values for parameters and options whose type is an enumerable of nullable enums. (Thanks [@Robert Dailey](https://github.com/rcdailey))
- Fixed an issue where specific parts of the help text weren't legible in some terminals due to low color resolution. Removed the usage of `ConsoleColor.DarkGray` in help text.
### 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.

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<CollectCoverage>true</CollectCoverage> <CollectCoverage>true</CollectCoverage>
@@ -13,13 +13,14 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
<PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" /> <PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" />
<PackageReference Include="FluentAssertions" Version="5.10.3" /> <PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
<PackageReference Include="xunit" Version="2.4.0" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="coverlet.msbuild" Version="3.0.3" PrivateAssets="all" /> <PackageReference Include="coverlet.msbuild" Version="3.1.0" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class CommandMustBeAnnotatedAnalyzerSpecs public class CommandMustBeAnnotatedAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustBeAnnotatedAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustBeAnnotatedAnalyzer();
@@ -69,4 +69,3 @@ public class Foo
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class CommandMustImplementInterfaceAnalyzerSpecs public class CommandMustImplementInterfaceAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustImplementInterfaceAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new CommandMustImplementInterfaceAnalyzer();
@@ -55,4 +55,3 @@ public class Foo
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -4,8 +4,8 @@ using FluentAssertions;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class GeneralSpecs public class GeneralSpecs
{ {
[Fact] [Fact]
@@ -28,4 +28,3 @@ namespace CliFx.Analyzers.Tests
diagnosticIds.Should().OnlyHaveUniqueItems(); diagnosticIds.Should().OnlyHaveUniqueItems();
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustBeInsideCommandAnalyzerSpecs public class OptionMustBeInsideCommandAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustBeInsideCommandAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustBeInsideCommandAnalyzer();
@@ -77,4 +77,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveNameOrShortNameAnalyzerSpecs public class OptionMustHaveNameOrShortNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveNameOrShortNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveNameOrShortNameAnalyzer();
@@ -83,4 +83,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveUniqueNameAnalyzerSpecs public class OptionMustHaveUniqueNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueNameAnalyzer();
@@ -89,4 +89,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveUniqueShortNameAnalyzerSpecs public class OptionMustHaveUniqueShortNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueShortNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveUniqueShortNameAnalyzer();
@@ -111,4 +111,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveValidConverterAnalyzerSpecs public class OptionMustHaveValidConverterAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidConverterAnalyzer();
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveValidNameAnalyzerSpecs public class OptionMustHaveValidNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidNameAnalyzer();
@@ -102,4 +102,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveValidShortNameAnalyzerSpecs public class OptionMustHaveValidShortNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidShortNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidShortNameAnalyzer();
@@ -83,4 +83,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class OptionMustHaveValidValidatorsAnalyzerSpecs public class OptionMustHaveValidValidatorsAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new OptionMustHaveValidValidatorsAnalyzer();
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustBeInsideCommandAnalyzerSpecs public class ParameterMustBeInsideCommandAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeInsideCommandAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeInsideCommandAnalyzer();
@@ -77,4 +77,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustBeLastIfNonScalarAnalyzerSpecs public class ParameterMustBeLastIfNonScalarAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonScalarAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeLastIfNonScalarAnalyzer();
@@ -92,4 +92,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs public class ParameterMustBeSingleIfNonScalarAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonScalarAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustBeSingleIfNonScalarAnalyzer();
@@ -92,4 +92,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustHaveUniqueNameAnalyzerSpecs public class ParameterMustHaveUniqueNameAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueNameAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueNameAnalyzer();
@@ -70,4 +70,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustHaveUniqueOrderAnalyzerSpecs public class ParameterMustHaveUniqueOrderAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueOrderAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveUniqueOrderAnalyzer();
@@ -70,4 +70,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustHaveValidConverterAnalyzerSpecs public class ParameterMustHaveValidConverterAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidConverterAnalyzer();
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class ParameterMustHaveValidValidatorsAnalyzerSpecs public class ParameterMustHaveValidValidatorsAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new ParameterMustHaveValidValidatorsAnalyzer();
@@ -93,4 +93,3 @@ public class MyCommand : ICommand
Analyzer.Should().NotProduceDiagnostics(code); Analyzer.Should().NotProduceDiagnostics(code);
} }
} }
}

View File

@@ -2,8 +2,8 @@
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Xunit; using Xunit;
namespace CliFx.Analyzers.Tests namespace CliFx.Analyzers.Tests;
{
public class SystemConsoleShouldBeAvoidedAnalyzerSpecs public class SystemConsoleShouldBeAvoidedAnalyzerSpecs
{ {
private static DiagnosticAnalyzer Analyzer { get; } = new SystemConsoleShouldBeAvoidedAnalyzer(); private static DiagnosticAnalyzer Analyzer { get; } = new SystemConsoleShouldBeAvoidedAnalyzer();
@@ -104,5 +104,23 @@ 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);
} }
} }

View File

@@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using System.Reflection;
using System.Text; using System.Text;
using Basic.Reference.Assemblies;
using FluentAssertions.Execution; using FluentAssertions.Execution;
using FluentAssertions.Primitives; using FluentAssertions.Primitives;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
@@ -11,8 +11,8 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
namespace CliFx.Analyzers.Tests.Utils namespace CliFx.Analyzers.Tests.Utils;
{
internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer, AnalyzerAssertions> internal class AnalyzerAssertions : ReferenceTypeAssertions<DiagnosticAnalyzer, AnalyzerAssertions>
{ {
protected override string Identifier { get; } = "analyzer"; protected override string Identifier { get; } = "analyzer";
@@ -58,14 +58,8 @@ namespace CliFx.Analyzers.Tests.Utils
var compilation = CSharpCompilation.Create( var compilation = CSharpCompilation.Create(
"CliFxTests_DynamicAssembly_" + Guid.NewGuid(), "CliFxTests_DynamicAssembly_" + Guid.NewGuid(),
new[] {ast}, new[] {ast},
new[] ReferenceAssemblies.Net50
{ .Append(MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location)),
MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location),
MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location)
},
// DLL to avoid having to define the Main() method // DLL to avoid having to define the Main() method
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
); );
@@ -171,4 +165,3 @@ namespace CliFx.Analyzers.Tests.Utils
{ {
public static AnalyzerAssertions Should(this DiagnosticAnalyzer analyzer) => new(analyzer); public static AnalyzerAssertions Should(this DiagnosticAnalyzer analyzer) => new(analyzer);
} }
}

View File

@@ -3,8 +3,8 @@ using CliFx.Analyzers.Utils.Extensions;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
public abstract class AnalyzerBase : DiagnosticAnalyzer public abstract class AnalyzerBase : DiagnosticAnalyzer
{ {
public DiagnosticDescriptor SupportedDiagnostic { get; } public DiagnosticDescriptor SupportedDiagnostic { get; }
@@ -37,4 +37,3 @@ namespace CliFx.Analyzers
context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None); context.ConfigureGeneratedCodeAnalysis(GeneratedCodeAnalysisFlags.None);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase public class CommandMustBeAnnotatedAnalyzer : AnalyzerBase
{ {
@@ -51,4 +51,3 @@ namespace CliFx.Analyzers
context.HandleClassDeclaration(Analyze); context.HandleClassDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase public class CommandMustImplementInterfaceAnalyzer : AnalyzerBase
{ {
@@ -45,4 +45,3 @@ namespace CliFx.Analyzers
context.HandleClassDeclaration(Analyze); context.HandleClassDeclaration(Analyze);
} }
} }
}

View File

@@ -3,8 +3,8 @@ using Microsoft.CodeAnalysis;
using System.Linq; using System.Linq;
using CliFx.Analyzers.Utils.Extensions; using CliFx.Analyzers.Utils.Extensions;
namespace CliFx.Analyzers.ObjectModel namespace CliFx.Analyzers.ObjectModel;
{
internal partial class CommandOptionSymbol internal partial class CommandOptionSymbol
{ {
public string? Name { get; } public string? Name { get; }
@@ -80,4 +80,3 @@ namespace CliFx.Analyzers.ObjectModel
public static bool IsOptionProperty(IPropertySymbol property) => public static bool IsOptionProperty(IPropertySymbol property) =>
TryGetOptionAttribute(property) is not null; TryGetOptionAttribute(property) is not null;
} }
}

View File

@@ -3,8 +3,8 @@ using System.Linq;
using CliFx.Analyzers.Utils.Extensions; using CliFx.Analyzers.Utils.Extensions;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
namespace CliFx.Analyzers.ObjectModel namespace CliFx.Analyzers.ObjectModel;
{
internal partial class CommandParameterSymbol internal partial class CommandParameterSymbol
{ {
public int Order { get; } public int Order { get; }
@@ -79,4 +79,3 @@ namespace CliFx.Analyzers.ObjectModel
public static bool IsParameterProperty(IPropertySymbol property) => public static bool IsParameterProperty(IPropertySymbol property) =>
TryGetParameterAttribute(property) is not null; TryGetParameterAttribute(property) is not null;
} }
}

View File

@@ -1,5 +1,5 @@
namespace CliFx.Analyzers.ObjectModel namespace CliFx.Analyzers.ObjectModel;
{
internal static class SymbolNames internal static class SymbolNames
{ {
public const string CliFxCommandInterface = "CliFx.ICommand"; public const string CliFxCommandInterface = "CliFx.ICommand";
@@ -12,4 +12,3 @@
public const string CliFxBindingValidatorInterface = "CliFx.Extensibility.IBindingValidator"; public const string CliFxBindingValidatorInterface = "CliFx.Extensibility.IBindingValidator";
public const string CliFxBindingValidatorClass = "CliFx.Extensibility.BindingValidator<T>"; public const string CliFxBindingValidatorClass = "CliFx.Extensibility.BindingValidator<T>";
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase public class OptionMustBeInsideCommandAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -45,4 +48,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveNameOrShortNameAnalyzer : AnalyzerBase public class OptionMustHaveNameOrShortNameAnalyzer : AnalyzerBase
{ {
@@ -37,4 +37,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase public class OptionMustHaveUniqueNameAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -59,4 +62,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase public class OptionMustHaveUniqueShortNameAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -58,4 +61,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase public class OptionMustHaveValidConverterAnalyzer : AnalyzerBase
{ {
@@ -47,4 +47,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidNameAnalyzer : AnalyzerBase public class OptionMustHaveValidNameAnalyzer : AnalyzerBase
{ {
@@ -40,4 +40,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -4,8 +4,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase public class OptionMustHaveValidShortNameAnalyzer : AnalyzerBase
{ {
@@ -40,4 +40,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase public class OptionMustHaveValidValidatorsAnalyzer : AnalyzerBase
{ {
@@ -49,4 +49,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase public class ParameterMustBeInsideCommandAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -45,4 +48,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase public class ParameterMustBeLastIfNonScalarAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -62,4 +65,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase public class ParameterMustBeSingleIfNonScalarAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -60,4 +63,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase public class ParameterMustHaveUniqueNameAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -59,4 +62,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase public class ParameterMustHaveUniqueOrderAnalyzer : AnalyzerBase
{ {
@@ -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;
@@ -52,4 +55,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase public class ParameterMustHaveValidConverterAnalyzer : AnalyzerBase
{ {
@@ -47,4 +47,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -5,8 +5,8 @@ using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase public class ParameterMustHaveValidValidatorsAnalyzer : AnalyzerBase
{ {
@@ -49,4 +49,3 @@ namespace CliFx.Analyzers
context.HandlePropertyDeclaration(Analyze); context.HandlePropertyDeclaration(Analyze);
} }
} }
}

View File

@@ -6,8 +6,8 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers namespace CliFx.Analyzers;
{
[DiagnosticAnalyzer(LanguageNames.CSharp)] [DiagnosticAnalyzer(LanguageNames.CSharp)]
public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase public class SystemConsoleShouldBeAvoidedAnalyzer : AnalyzerBase
{ {
@@ -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)
@@ -74,4 +75,3 @@ namespace CliFx.Analyzers
context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.SimpleMemberAccessExpression); context.RegisterSyntaxNodeAction(Analyze, SyntaxKind.SimpleMemberAccessExpression);
} }
} }
}

View File

@@ -4,16 +4,21 @@ using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.CSharp.Syntax; using Microsoft.CodeAnalysis.CSharp.Syntax;
using Microsoft.CodeAnalysis.Diagnostics; using Microsoft.CodeAnalysis.Diagnostics;
namespace CliFx.Analyzers.Utils.Extensions 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,8 +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);
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace CliFx.Analyzers.Utils.Extensions namespace CliFx.Analyzers.Utils.Extensions;
{
internal static class StringExtensions internal static class StringExtensions
{ {
public static string TrimEnd( public static string TrimEnd(
@@ -15,4 +15,3 @@ namespace CliFx.Analyzers.Utils.Extensions
return str; return str;
} }
} }
}

View File

@@ -4,8 +4,8 @@ using BenchmarkDotNet.Attributes;
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
[Command] [Command]
@@ -30,4 +30,3 @@ namespace CliFx.Benchmarks
.Build() .Build()
.RunAsync(Arguments, new Dictionary<string, string>()); .RunAsync(Arguments, new Dictionary<string, string>());
} }
}

View File

@@ -1,8 +1,8 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using clipr; using clipr;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class CliprCommand public class CliprCommand
@@ -24,4 +24,3 @@ namespace CliFx.Benchmarks
[Benchmark(Description = "Clipr")] [Benchmark(Description = "Clipr")]
public void ExecuteWithClipr() => CliParser.Parse<CliprCommand>(Arguments).Execute(); public void ExecuteWithClipr() => CliParser.Parse<CliprCommand>(Arguments).Execute();
} }
}

View File

@@ -1,8 +1,8 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using Cocona; using Cocona;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class CoconaCommand public class CoconaCommand
@@ -21,4 +21,3 @@ namespace CliFx.Benchmarks
[Benchmark(Description = "Cocona")] [Benchmark(Description = "Cocona")]
public void ExecuteWithCocona() => CoconaApp.Run<CoconaCommand>(Arguments); public void ExecuteWithCocona() => CoconaApp.Run<CoconaCommand>(Arguments);
} }
}

View File

@@ -1,8 +1,8 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using CommandLine; using CommandLine;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class CommandLineParserCommand public class CommandLineParserCommand
@@ -27,4 +27,3 @@ namespace CliFx.Benchmarks
.ParseArguments(Arguments, typeof(CommandLineParserCommand)) .ParseArguments(Arguments, typeof(CommandLineParserCommand))
.WithParsed<CommandLineParserCommand>(c => c.Execute()); .WithParsed<CommandLineParserCommand>(c => c.Execute());
} }
}

View File

@@ -1,8 +1,8 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using McMaster.Extensions.CommandLineUtils; using McMaster.Extensions.CommandLineUtils;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class McMasterCommand public class McMasterCommand
@@ -22,4 +22,3 @@ namespace CliFx.Benchmarks
[Benchmark(Description = "McMaster.Extensions.CommandLineUtils")] [Benchmark(Description = "McMaster.Extensions.CommandLineUtils")]
public int ExecuteWithMcMaster() => CommandLineApplication.Execute<McMasterCommand>(Arguments); public int ExecuteWithMcMaster() => CommandLineApplication.Execute<McMasterCommand>(Arguments);
} }
}

View File

@@ -1,8 +1,8 @@
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
using PowerArgs; using PowerArgs;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class PowerArgsCommand public class PowerArgsCommand
@@ -24,4 +24,3 @@ namespace CliFx.Benchmarks
[Benchmark(Description = "PowerArgs")] [Benchmark(Description = "PowerArgs")]
public void ExecuteWithPowerArgs() => Args.InvokeMain<PowerArgsCommand>(Arguments); public void ExecuteWithPowerArgs() => Args.InvokeMain<PowerArgsCommand>(Arguments);
} }
}

View File

@@ -3,8 +3,8 @@ using System.CommandLine.Invocation;
using System.Threading.Tasks; using System.Threading.Tasks;
using BenchmarkDotNet.Attributes; using BenchmarkDotNet.Attributes;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
public partial class Benchmarks public partial class Benchmarks
{ {
public class SystemCommandLineCommand public class SystemCommandLineCommand
@@ -41,4 +41,3 @@ namespace CliFx.Benchmarks
public async Task<int> ExecuteWithSystemCommandLine() => public async Task<int> ExecuteWithSystemCommandLine() =>
await new SystemCommandLineCommand().ExecuteAsync(Arguments); await new SystemCommandLineCommand().ExecuteAsync(Arguments);
} }
}

View File

@@ -3,8 +3,8 @@ using BenchmarkDotNet.Configs;
using BenchmarkDotNet.Order; using BenchmarkDotNet.Order;
using BenchmarkDotNet.Running; using BenchmarkDotNet.Running;
namespace CliFx.Benchmarks namespace CliFx.Benchmarks;
{
[RankColumn] [RankColumn]
[Orderer(SummaryOrderPolicy.FastestToSlowest)] [Orderer(SummaryOrderPolicy.FastestToSlowest)]
public partial class Benchmarks public partial class Benchmarks
@@ -14,7 +14,6 @@ namespace CliFx.Benchmarks
public static void Main() => BenchmarkRunner.Run<Benchmarks>( public static void Main() => BenchmarkRunner.Run<Benchmarks>(
DefaultConfig DefaultConfig
.Instance .Instance
.With(ConfigOptions.DisableOptimizationsValidator) .WithOptions(ConfigOptions.DisableOptimizationsValidator)
); );
} }
}

View File

@@ -2,13 +2,13 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="BenchmarkDotNet" Version="0.12.0" /> <PackageReference Include="BenchmarkDotNet" Version="0.13.1" />
<PackageReference Include="clipr" Version="1.6.1" /> <PackageReference Include="clipr" Version="1.6.1" />
<PackageReference Include="Cocona" Version="1.5.0" /> <PackageReference Include="Cocona" Version="1.6.0" />
<PackageReference Include="CommandLineParser" Version="2.8.0" /> <PackageReference Include="CommandLineParser" Version="2.8.0" />
<PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.1.0" /> <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="3.1.0" />
<PackageReference Include="PowerArgs" Version="3.6.0" /> <PackageReference Include="PowerArgs" Version="3.6.0" />

View File

@@ -2,12 +2,12 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="5.0.1" /> <PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="6.0.0" />
<PackageReference Include="Newtonsoft.Json" Version="12.0.3" /> <PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -6,8 +6,8 @@ using CliFx.Demo.Utils;
using CliFx.Exceptions; using CliFx.Exceptions;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Demo.Commands namespace CliFx.Demo.Commands;
{
[Command("book add", Description = "Add a book to the library.")] [Command("book add", Description = "Add a book to the library.")]
public partial class BookAddCommand : ICommand public partial class BookAddCommand : ICommand
{ {
@@ -67,4 +67,3 @@ namespace CliFx.Demo.Commands
Random.Next(0, 9) Random.Next(0, 9)
); );
} }
}

View File

@@ -5,8 +5,8 @@ using CliFx.Demo.Utils;
using CliFx.Exceptions; using CliFx.Exceptions;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Demo.Commands namespace CliFx.Demo.Commands;
{
[Command("book", Description = "Retrieve a book from the library.")] [Command("book", Description = "Retrieve a book from the library.")]
public class BookCommand : ICommand public class BookCommand : ICommand
{ {
@@ -32,4 +32,3 @@ namespace CliFx.Demo.Commands
return default; return default;
} }
} }
}

View File

@@ -4,8 +4,8 @@ using CliFx.Demo.Domain;
using CliFx.Demo.Utils; using CliFx.Demo.Utils;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Demo.Commands namespace CliFx.Demo.Commands;
{
[Command("book list", Description = "List all books in the library.")] [Command("book list", Description = "List all books in the library.")]
public class BookListCommand : ICommand public class BookListCommand : ICommand
{ {
@@ -34,4 +34,3 @@ namespace CliFx.Demo.Commands
return default; return default;
} }
} }
}

View File

@@ -4,8 +4,8 @@ using CliFx.Demo.Domain;
using CliFx.Exceptions; using CliFx.Exceptions;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Demo.Commands namespace CliFx.Demo.Commands;
{
[Command("book remove", Description = "Remove a book from the library.")] [Command("book remove", Description = "Remove a book from the library.")]
public class BookRemoveCommand : ICommand public class BookRemoveCommand : ICommand
{ {
@@ -33,4 +33,3 @@ namespace CliFx.Demo.Commands
return default; return default;
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace CliFx.Demo.Domain namespace CliFx.Demo.Domain;
{
public class Book public class Book
{ {
public string Title { get; } public string Title { get; }
@@ -20,4 +20,3 @@ namespace CliFx.Demo.Domain
Isbn = isbn; Isbn = isbn;
} }
} }
}

View File

@@ -1,7 +1,7 @@
using System; using System;
namespace CliFx.Demo.Domain namespace CliFx.Demo.Domain;
{
public partial class Isbn public partial class Isbn
{ {
public int EanPrefix { get; } public int EanPrefix { get; }
@@ -42,4 +42,3 @@ namespace CliFx.Demo.Domain
); );
} }
} }
}

View File

@@ -2,8 +2,8 @@
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
namespace CliFx.Demo.Domain namespace CliFx.Demo.Domain;
{
public partial class Library public partial class Library
{ {
public IReadOnlyList<Book> Books { get; } public IReadOnlyList<Book> Books { get; }
@@ -33,4 +33,3 @@ namespace CliFx.Demo.Domain
{ {
public static Library Empty { get; } = new(Array.Empty<Book>()); public static Library Empty { get; } = new(Array.Empty<Book>());
} }
}

View File

@@ -2,8 +2,8 @@
using System.Linq; using System.Linq;
using Newtonsoft.Json; using Newtonsoft.Json;
namespace CliFx.Demo.Domain namespace CliFx.Demo.Domain;
{
public class LibraryProvider public class LibraryProvider
{ {
private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json"); private static string StorageFilePath { get; } = Path.Combine(Directory.GetCurrentDirectory(), "Library.json");
@@ -21,7 +21,7 @@ namespace CliFx.Demo.Domain
var data = File.ReadAllText(StorageFilePath); var data = File.ReadAllText(StorageFilePath);
return JsonConvert.DeserializeObject<Library>(data); return JsonConvert.DeserializeObject<Library>(data) ?? Library.Empty;
} }
public Book? TryGetBook(string title) => GetLibrary().Books.FirstOrDefault(b => b.Title == title); public Book? TryGetBook(string title) => GetLibrary().Books.FirstOrDefault(b => b.Title == title);
@@ -38,4 +38,3 @@ namespace CliFx.Demo.Domain
StoreLibrary(updatedLibrary); StoreLibrary(updatedLibrary);
} }
} }
}

View File

@@ -1,15 +1,8 @@
using System; using CliFx;
using System.Threading.Tasks;
using CliFx.Demo.Commands; using CliFx.Demo.Commands;
using CliFx.Demo.Domain; using CliFx.Demo.Domain;
using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.DependencyInjection;
namespace CliFx.Demo
{
public static class Program
{
private static IServiceProvider GetServiceProvider()
{
// We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands // We use Microsoft.Extensions.DependencyInjection for injecting dependencies in commands
var services = new ServiceCollection(); var services = new ServiceCollection();
@@ -22,15 +15,11 @@ namespace CliFx.Demo
services.AddTransient<BookRemoveCommand>(); services.AddTransient<BookRemoveCommand>();
services.AddTransient<BookListCommand>(); services.AddTransient<BookListCommand>();
return services.BuildServiceProvider(); var serviceProvider = services.BuildServiceProvider();
}
public static async Task<int> Main() => return await new CliApplicationBuilder()
await new CliApplicationBuilder()
.SetDescription("Demo application showcasing CliFx features.") .SetDescription("Demo application showcasing CliFx features.")
.AddCommandsFromThisAssembly() .AddCommandsFromThisAssembly()
.UseTypeActivator(GetServiceProvider().GetRequiredService) .UseTypeActivator(serviceProvider.GetRequiredService)
.Build() .Build()
.RunAsync(); .RunAsync();
}
}

View File

@@ -2,8 +2,8 @@
using CliFx.Demo.Domain; using CliFx.Demo.Domain;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Demo.Utils namespace CliFx.Demo.Utils;
{
internal static class ConsoleExtensions internal static class ConsoleExtensions
{ {
public static void WriteBook(this ConsoleWriter writer, Book book) public static void WriteBook(this ConsoleWriter writer, Book book)
@@ -34,4 +34,3 @@ namespace CliFx.Demo.Utils
writer.WriteLine(book.Isbn); writer.WriteLine(book.Isbn);
} }
} }
}

View File

@@ -2,7 +2,7 @@
<PropertyGroup> <PropertyGroup>
<OutputType>Exe</OutputType> <OutputType>Exe</OutputType>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
</PropertyGroup> </PropertyGroup>
<ItemGroup> <ItemGroup>

View File

@@ -3,8 +3,8 @@ using System.Threading.Tasks;
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Tests.Dummy.Commands namespace CliFx.Tests.Dummy.Commands;
{
[Command("console-test")] [Command("console-test")]
public class ConsoleTestCommand : ICommand public class ConsoleTestCommand : ICommand
{ {
@@ -21,4 +21,3 @@ namespace CliFx.Tests.Dummy.Commands
return default; return default;
} }
} }
}

View File

@@ -2,8 +2,8 @@
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Tests.Dummy.Commands namespace CliFx.Tests.Dummy.Commands;
{
[Command("env-test")] [Command("env-test")]
public class EnvironmentTestCommand : ICommand public class EnvironmentTestCommand : ICommand
{ {
@@ -17,4 +17,3 @@ namespace CliFx.Tests.Dummy.Commands
return default; return default;
} }
} }
}

View File

@@ -1,8 +1,7 @@
using System.Reflection; using System.Reflection;
using System.Threading.Tasks; using System.Threading.Tasks;
namespace CliFx.Tests.Dummy namespace CliFx.Tests.Dummy;
{
// This dummy application is used in tests for scenarios // This dummy application is used in tests for scenarios
// that require an external process to properly verify. // that require an external process to properly verify.
@@ -21,4 +20,3 @@ namespace CliFx.Tests.Dummy
.Build() .Build()
.RunAsync(); .RunAsync();
} }
}

View File

@@ -6,8 +6,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class ApplicationSpecs : SpecsBase public class ApplicationSpecs : SpecsBase
{ {
public ApplicationSpecs(ITestOutputHelper testOutput) public ApplicationSpecs(ITestOutputHelper testOutput)
@@ -83,4 +83,3 @@ namespace CliFx.Tests
stdErr.Should().Contain("not a valid command"); stdErr.Should().Contain("not a valid command");
} }
} }
}

View File

@@ -6,8 +6,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class CancellationSpecs : SpecsBase public class CancellationSpecs : SpecsBase
{ {
public CancellationSpecs(ITestOutputHelper testOutput) public CancellationSpecs(ITestOutputHelper testOutput)
@@ -64,4 +64,3 @@ public class Command : ICommand
stdOut.Trim().Should().Be("Cancelled"); stdOut.Trim().Should().Be("Cancelled");
} }
} }
}

View File

@@ -1,7 +1,7 @@
<Project Sdk="Microsoft.NET.Sdk"> <Project Sdk="Microsoft.NET.Sdk">
<PropertyGroup> <PropertyGroup>
<TargetFramework>net5.0</TargetFramework> <TargetFramework>net6.0</TargetFramework>
<IsPackable>false</IsPackable> <IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject> <IsTestProject>true</IsTestProject>
<CollectCoverage>true</CollectCoverage> <CollectCoverage>true</CollectCoverage>
@@ -13,14 +13,15 @@
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<PackageReference Include="CliWrap" Version="3.3.1" /> <PackageReference Include="Basic.Reference.Assemblies" Version="1.2.4" />
<PackageReference Include="FluentAssertions" Version="5.10.3" /> <PackageReference Include="CliWrap" Version="3.3.3" />
<PackageReference Include="FluentAssertions" Version="6.2.0" />
<PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" /> <PackageReference Include="GitHubActionsTestLogger" Version="1.2.0" />
<PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="3.4.0" /> <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="4.0.1" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.9.1" /> <PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="xunit" Version="2.4.1" /> <PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" /> <PackageReference Include="xunit.runner.visualstudio" Version="2.4.3" PrivateAssets="all" />
<PackageReference Include="coverlet.msbuild" Version="3.0.3" PrivateAssets="all" /> <PackageReference Include="coverlet.msbuild" Version="3.1.0" PrivateAssets="all" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>

View File

@@ -1,6 +1,9 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading.Tasks; using System.Threading.Tasks;
using CliFx.Infrastructure;
using CliFx.Tests.Utils; using CliFx.Tests.Utils;
using CliWrap; using CliWrap;
using CliWrap.Buffered; using CliWrap.Buffered;
@@ -8,8 +11,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class ConsoleSpecs : SpecsBase public class ConsoleSpecs : SpecsBase
{ {
public ConsoleSpecs(ITestOutputHelper testOutput) public ConsoleSpecs(ITestOutputHelper testOutput)
@@ -77,9 +80,9 @@ public class Command : ICommand
// Assert // Assert
exitCode.Should().Be(0); exitCode.Should().Be(0);
Console.OpenStandardInput().Should().NotBe(FakeConsole.Input.BaseStream); Console.OpenStandardInput().Should().NotBeSameAs(FakeConsole.Input.BaseStream);
Console.OpenStandardOutput().Should().NotBe(FakeConsole.Output.BaseStream); Console.OpenStandardOutput().Should().NotBeSameAs(FakeConsole.Output.BaseStream);
Console.OpenStandardError().Should().NotBe(FakeConsole.Error.BaseStream); Console.OpenStandardError().Should().NotBeSameAs(FakeConsole.Error.BaseStream);
Console.ForegroundColor.Should().NotBe(ConsoleColor.DarkMagenta); Console.ForegroundColor.Should().NotBe(ConsoleColor.DarkMagenta);
Console.BackgroundColor.Should().NotBe(ConsoleColor.DarkMagenta); Console.BackgroundColor.Should().NotBe(ConsoleColor.DarkMagenta);
@@ -135,5 +138,21 @@ public class Command : ICommand
stdOut.Trim().Should().Be("Hello world"); stdOut.Trim().Should().Be("Hello world");
stdErr.Trim().Should().Be("Hello world"); stdErr.Trim().Should().Be("Hello world");
} }
[Fact]
public void Console_does_not_emit_preamble_when_used_with_encoding_that_has_it()
{
// Arrange
using var buffer = new MemoryStream();
using var consoleWriter = new ConsoleWriter(FakeConsole, buffer, Encoding.UTF8);
// Act
consoleWriter.Write("Hello world");
consoleWriter.Flush();
var output = consoleWriter.Encoding.GetString(buffer.ToArray());
// Assert
output.Should().Be("Hello world");
} }
} }

View File

@@ -6,8 +6,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class ConversionSpecs : SpecsBase public class ConversionSpecs : SpecsBase
{ {
public ConversionSpecs(ITestOutputHelper testOutput) public ConversionSpecs(ITestOutputHelper testOutput)
@@ -946,5 +946,47 @@ public class Command : ICommand
exitCode.Should().NotBe(0); exitCode.Should().NotBe(0);
stdErr.Should().Contain("Hello world"); stdErr.Should().Contain("Hello world");
} }
[Fact]
public async Task Parameter_or_option_value_conversion_fails_if_the_static_parse_method_throws()
{
// Arrange
var commandType = DynamicCommandBuilder.Compile(
// language=cs
@"
public class CustomType
{
public string Value { get; }
private CustomType(string value) => Value = value;
public static CustomType Parse(string value) => throw new Exception(""Hello world"");
}
[Command]
public class Command : ICommand
{
[CommandOption('f')]
public CustomType Foo { get; set; }
public ValueTask ExecuteAsync(IConsole console) => default;
}
");
var application = new CliApplicationBuilder()
.AddCommand(commandType)
.UseConsole(FakeConsole)
.Build();
// Act
var exitCode = await application.RunAsync(
new[] {"-f", "bar"},
new Dictionary<string, string>()
);
var stdErr = FakeConsole.ReadErrorString();
// Assert
exitCode.Should().NotBe(0);
stdErr.Should().Contain("Hello world");
} }
} }

View File

@@ -10,8 +10,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class DirectivesSpecs : SpecsBase public class DirectivesSpecs : SpecsBase
{ {
public DirectivesSpecs(ITestOutputHelper testOutput) public DirectivesSpecs(ITestOutputHelper testOutput)
@@ -109,4 +109,3 @@ public class Command : ICommand
); );
} }
} }
}

View File

@@ -10,8 +10,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class EnvironmentSpecs : SpecsBase public class EnvironmentSpecs : SpecsBase
{ {
public EnvironmentSpecs(ITestOutputHelper testOutput) public EnvironmentSpecs(ITestOutputHelper testOutput)
@@ -257,4 +257,3 @@ public class Command : ICommand
result.StandardOutput.Trim().Should().Be("Hello Mars!"); result.StandardOutput.Trim().Should().Be("Hello Mars!");
} }
} }
}

View File

@@ -7,8 +7,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class ErrorReportingSpecs : SpecsBase public class ErrorReportingSpecs : SpecsBase
{ {
public ErrorReportingSpecs(ITestOutputHelper testOutput) public ErrorReportingSpecs(ITestOutputHelper testOutput)
@@ -202,4 +202,3 @@ public class Command : ICommand
stdErr.Trim().Should().Be("Something went wrong"); stdErr.Trim().Should().Be("Something went wrong");
} }
} }
}

View File

@@ -7,8 +7,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class HelpTextSpecs : SpecsBase public class HelpTextSpecs : SpecsBase
{ {
public HelpTextSpecs(ITestOutputHelper testOutput) public HelpTextSpecs(ITestOutputHelper testOutput)
@@ -371,6 +371,57 @@ public class Command : ICommand
); );
} }
// https://github.com/Tyrrrz/CliFx/issues/117
[Fact]
public async Task Help_text_shows_usage_format_which_lists_all_parameters_in_specified_order()
{
// Arrange
var commandType = DynamicCommandBuilder.Compile(
// language=cs
@"
// Base members appear last in reflection order
public abstract class CommandBase : ICommand
{
[CommandParameter(0)]
public string Foo { get; set; }
public abstract ValueTask ExecuteAsync(IConsole console);
}
[Command]
public class Command : CommandBase
{
[CommandParameter(2)]
public IReadOnlyList<string> Baz { get; set; }
[CommandParameter(1)]
public string Bar { get; set; }
public override 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(
"USAGE",
"<foo>", "<bar>", "<baz...>"
);
}
[Fact] [Fact]
public async Task Help_text_shows_usage_format_which_lists_all_required_options() public async Task Help_text_shows_usage_format_which_lists_all_required_options()
{ {
@@ -577,6 +628,96 @@ 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 IReadOnlyList<CustomEnum> Foo { get; set; }
[CommandOption(""bar"")]
public IReadOnlyList<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]
public async Task Help_text_shows_all_valid_values_for_nullable_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 CustomEnum? Foo { get; set; }
[CommandOption(""bar"")]
public IReadOnlyList<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()
{ {
@@ -875,4 +1016,3 @@ public class SecondCommandSecondChildCommand : ICommand
stdOut.Trim().Should().Be("v6.9"); stdOut.Trim().Should().Be("v6.9");
} }
} }
}

View File

@@ -7,8 +7,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class OptionBindingSpecs : SpecsBase public class OptionBindingSpecs : SpecsBase
{ {
public OptionBindingSpecs(ITestOutputHelper testOutput) public OptionBindingSpecs(ITestOutputHelper testOutput)
@@ -705,4 +705,3 @@ public class Command : ICommand
stdErr.Should().Contain("expects a single argument, but provided with multiple"); stdErr.Should().Contain("expects a single argument, but provided with multiple");
} }
} }
}

View File

@@ -6,8 +6,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class ParameterBindingSpecs : SpecsBase public class ParameterBindingSpecs : SpecsBase
{ {
public ParameterBindingSpecs(ITestOutputHelper testOutput) public ParameterBindingSpecs(ITestOutputHelper testOutput)
@@ -230,4 +230,3 @@ public class Command : ICommand
stdErr.Should().Contain("Unexpected parameter(s)"); stdErr.Should().Contain("Unexpected parameter(s)");
} }
} }
}

View File

@@ -6,8 +6,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class RoutingSpecs : SpecsBase public class RoutingSpecs : SpecsBase
{ {
public RoutingSpecs(ITestOutputHelper testOutput) public RoutingSpecs(ITestOutputHelper testOutput)
@@ -183,4 +183,3 @@ public class NamedChildCommand : ICommand
stdOut.Trim().Should().Be("cmd child"); stdOut.Trim().Should().Be("cmd child");
} }
} }
}

View File

@@ -3,8 +3,8 @@ using CliFx.Infrastructure;
using CliFx.Tests.Utils.Extensions; using CliFx.Tests.Utils.Extensions;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public abstract class SpecsBase : IDisposable public abstract class SpecsBase : IDisposable
{ {
public ITestOutputHelper TestOutput { get; } public ITestOutputHelper TestOutput { get; }
@@ -20,4 +20,3 @@ namespace CliFx.Tests
FakeConsole.Dispose(); FakeConsole.Dispose();
} }
} }
}

View File

@@ -7,8 +7,8 @@ using FluentAssertions;
using Xunit; using Xunit;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests namespace CliFx.Tests;
{
public class TypeActivationSpecs : SpecsBase public class TypeActivationSpecs : SpecsBase
{ {
public TypeActivationSpecs(ITestOutputHelper testOutput) public TypeActivationSpecs(ITestOutputHelper testOutput)
@@ -162,4 +162,3 @@ public class Command : ICommand
stdErr.Should().Contain("Failed to create an instance of type"); stdErr.Should().Contain("Failed to create an instance of type");
} }
} }
}

View File

@@ -3,12 +3,13 @@ using System.Collections.Generic;
using System.IO; using System.IO;
using System.Linq; using System.Linq;
using System.Reflection; using System.Reflection;
using Basic.Reference.Assemblies;
using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis;
using Microsoft.CodeAnalysis.CSharp; using Microsoft.CodeAnalysis.CSharp;
using Microsoft.CodeAnalysis.Text; using Microsoft.CodeAnalysis.Text;
namespace CliFx.Tests.Utils namespace CliFx.Tests.Utils;
{
// This class uses Roslyn to compile commands dynamically. // This class uses Roslyn to compile commands dynamically.
// //
// It allows us to collocate commands with tests more // It allows us to collocate commands with tests more
@@ -60,16 +61,9 @@ namespace CliFx.Tests.Utils
var compilation = CSharpCompilation.Create( var compilation = CSharpCompilation.Create(
"CliFxTests_DynamicAssembly_" + Guid.NewGuid(), "CliFxTests_DynamicAssembly_" + Guid.NewGuid(),
new[] {ast}, new[] {ast},
new[] ReferenceAssemblies.Net50
{ .Append(MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location))
MetadataReference.CreateFromFile(Assembly.Load("netstandard").Location), .Append(MetadataReference.CreateFromFile(typeof(DynamicCommandBuilder).Assembly.Location)),
MetadataReference.CreateFromFile(Assembly.Load("System.Runtime").Location),
MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Console).Assembly.Location),
MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location),
MetadataReference.CreateFromFile(typeof(DynamicCommandBuilder).Assembly.Location),
MetadataReference.CreateFromFile(typeof(ICommand).Assembly.Location)
},
// DLL to avoid having to define the Main() method // DLL to avoid having to define the Main() method
new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary) new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary)
); );
@@ -112,13 +106,13 @@ namespace CliFx.Tests.Utils
// Return all defined commands // Return all defined commands
var commandTypes = generatedAssembly var commandTypes = generatedAssembly
.GetTypes() .GetTypes()
.Where(t => t.IsAssignableTo(typeof(ICommand))) .Where(t => t.IsAssignableTo(typeof(ICommand)) && !t.IsAbstract)
.ToArray(); .ToArray();
if (commandTypes.Length <= 0) if (commandTypes.Length <= 0)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"There are no command definitions in the provide source code." "There are no command definitions in the provided source code."
); );
} }
@@ -132,11 +126,10 @@ namespace CliFx.Tests.Utils
if (commandTypes.Count > 1) if (commandTypes.Count > 1)
{ {
throw new InvalidOperationException( throw new InvalidOperationException(
"There are more than one command definitions in the provide source code." "There are more than one command definitions in the provided source code."
); );
} }
return commandTypes.Single(); return commandTypes.Single();
} }
} }
}

View File

@@ -1,24 +1,22 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using FluentAssertions; using FluentAssertions;
using FluentAssertions.Collections;
using FluentAssertions.Execution; using FluentAssertions.Execution;
using FluentAssertions.Primitives; using FluentAssertions.Primitives;
namespace CliFx.Tests.Utils.Extensions namespace CliFx.Tests.Utils.Extensions;
{
internal static class AssertionExtensions internal static class AssertionExtensions
{ {
public static AndConstraint<StringCollectionAssertions> ConsistOfLines( public static void ConsistOfLines(
this StringAssertions assertions, this StringAssertions assertions,
IEnumerable<string> lines) IEnumerable<string> lines)
{ {
var actualLines = assertions.Subject.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries); var actualLines = assertions.Subject.Split(new[] {'\n', '\r'}, StringSplitOptions.RemoveEmptyEntries);
actualLines.Should().Equal(lines);
return actualLines.Should().Equal(lines);
} }
public static AndConstraint<StringCollectionAssertions> ConsistOfLines( public static void ConsistOfLines(
this StringAssertions assertions, this StringAssertions assertions,
params string[] lines) => params string[] lines) =>
assertions.ConsistOfLines((IEnumerable<string>) lines); assertions.ConsistOfLines((IEnumerable<string>) lines);
@@ -51,4 +49,3 @@ namespace CliFx.Tests.Utils.Extensions
params string[] values) => params string[] values) =>
assertions.ContainAllInOrder((IEnumerable<string>) values); assertions.ContainAllInOrder((IEnumerable<string>) values);
} }
}

View File

@@ -1,8 +1,8 @@
using CliFx.Infrastructure; using CliFx.Infrastructure;
using Xunit.Abstractions; using Xunit.Abstractions;
namespace CliFx.Tests.Utils.Extensions namespace CliFx.Tests.Utils.Extensions;
{
internal static class ConsoleExtensions internal static class ConsoleExtensions
{ {
public static void DumpToTestOutput(this FakeInMemoryConsole console, ITestOutputHelper testOutputHelper) public static void DumpToTestOutput(this FakeInMemoryConsole console, ITestOutputHelper testOutputHelper)
@@ -14,4 +14,3 @@ namespace CliFx.Tests.Utils.Extensions
testOutputHelper.WriteLine(console.ReadErrorString()); testOutputHelper.WriteLine(console.ReadErrorString());
} }
} }
}

View File

@@ -2,11 +2,10 @@
using CliFx.Attributes; using CliFx.Attributes;
using CliFx.Infrastructure; using CliFx.Infrastructure;
namespace CliFx.Tests.Utils namespace CliFx.Tests.Utils;
{
[Command] [Command]
public class NoOpCommand : ICommand public class NoOpCommand : ICommand
{ {
public ValueTask ExecuteAsync(IConsole console) => default; public ValueTask ExecuteAsync(IConsole console) => default;
} }
}

View File

@@ -1,8 +1,8 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
namespace CliFx namespace CliFx;
{
/// <summary> /// <summary>
/// Configuration of an application. /// Configuration of an application.
/// </summary> /// </summary>
@@ -36,4 +36,3 @@ namespace CliFx
IsPreviewModeAllowed = isPreviewModeAllowed; IsPreviewModeAllowed = isPreviewModeAllowed;
} }
} }
}

View File

@@ -1,5 +1,5 @@
namespace CliFx namespace CliFx;
{
/// <summary> /// <summary>
/// Metadata associated with an application. /// Metadata associated with an application.
/// </summary> /// </summary>
@@ -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; }
@@ -40,4 +40,3 @@
Description = description; Description = description;
} }
} }
}

View File

@@ -1,12 +1,12 @@
using System; using System;
namespace CliFx.Attributes namespace CliFx.Attributes;
{
/// <summary> /// <summary>
/// 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.
@@ -40,4 +40,3 @@ namespace CliFx.Attributes
{ {
} }
} }
}

View File

@@ -1,13 +1,13 @@
using System; using System;
using CliFx.Extensibility; using CliFx.Extensibility;
namespace CliFx.Attributes namespace CliFx.Attributes;
{
/// <summary> /// <summary>
/// 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.
@@ -97,4 +97,3 @@ namespace CliFx.Attributes
{ {
} }
} }
}

View File

@@ -1,13 +1,13 @@
using System; using System;
using CliFx.Extensibility; using CliFx.Extensibility;
namespace CliFx.Attributes namespace CliFx.Attributes;
{
/// <summary> /// <summary>
/// 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.
@@ -64,4 +64,3 @@ namespace CliFx.Attributes
Order = order; Order = order;
} }
} }
}

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