mirror of
				https://github.com/nsnail/spectre.console.git
				synced 2025-10-31 17:15:28 +08:00 
			
		
		
		
	Adding analyzer project
Contains two analyzers with fixes * Use AnsiConsole over System.Console * Favor local instance over static implementation
This commit is contained in:
		 Phil Scott
					Phil Scott
				
			
				
					committed by
					
						 Patrik Svensson
						Patrik Svensson
					
				
			
			
				
	
			
			
			 Patrik Svensson
						Patrik Svensson
					
				
			
						parent
						
							d015a4966f
						
					
				
				
					commit
					4f293d887d
				
			
							
								
								
									
										16
									
								
								examples/Console/AnalyzerTester/AnalyzerTester.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								examples/Console/AnalyzerTester/AnalyzerTester.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,16 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|   <PropertyGroup> | ||||
|     <OutputType>Exe</OutputType> | ||||
|     <TargetFramework>net5.0</TargetFramework> | ||||
|   </PropertyGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\..\..\src\Spectre.Console\Spectre.Console.csproj" /> | ||||
|     <ProjectReference Include="..\..\..\src\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj" | ||||
|                       PrivateAssets="all" | ||||
|                       ReferenceOutputAssembly="false" | ||||
|                       OutputItemType="Analyzer" /> | ||||
|   </ItemGroup> | ||||
|  | ||||
| </Project> | ||||
							
								
								
									
										32
									
								
								examples/Console/AnalyzerTester/Program.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										32
									
								
								examples/Console/AnalyzerTester/Program.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,32 @@ | ||||
| using Spectre.Console; | ||||
|  | ||||
| namespace AnalyzerTester | ||||
| { | ||||
|     class Program | ||||
|     { | ||||
|         static void Main(string[] args) | ||||
|         { | ||||
|             AnsiConsole.WriteLine("Hello World!"); | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     class Dependency | ||||
|     { | ||||
|         private readonly IAnsiConsole _ansiConsole; | ||||
|  | ||||
|         public Dependency(IAnsiConsole ansiConsole) | ||||
|         { | ||||
|             _ansiConsole = ansiConsole; | ||||
|         } | ||||
|  | ||||
|         public void DoIt() | ||||
|         { | ||||
|             _ansiConsole.WriteLine("Hey mom!"); | ||||
|         } | ||||
|  | ||||
|         public void DoIt(IAnsiConsole thisConsole) | ||||
|         { | ||||
|             thisConsole.WriteLine("Hey mom!"); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										25
									
								
								src/Spectre.Console.Analyzer/Analyzers/BaseAnalyzer.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										25
									
								
								src/Spectre.Console.Analyzer/Analyzers/BaseAnalyzer.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,25 @@ | ||||
| using Microsoft.CodeAnalysis.Diagnostics; | ||||
|  | ||||
| namespace Spectre.Console.Analyzer | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Base class for Spectre analyzers. | ||||
|     /// </summary> | ||||
|     public abstract class BaseAnalyzer : 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); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,89 @@ | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.CSharp; | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| using Microsoft.CodeAnalysis.Diagnostics; | ||||
| using Microsoft.CodeAnalysis.Operations; | ||||
|  | ||||
| namespace Spectre.Console.Analyzer | ||||
| { | ||||
|     /// <summary> | ||||
|     /// Analyzer to suggest using available instances of AnsiConsole over the static methods. | ||||
|     /// </summary> | ||||
|     [DiagnosticAnalyzer(LanguageNames.CSharp)] | ||||
|     public class FavorInstanceAnsiConsoleOverStaticAnalyzer : BaseAnalyzer | ||||
|     { | ||||
|         private static readonly DiagnosticDescriptor _diagnosticDescriptor = | ||||
|             Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic; | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         public override ImmutableArray<DiagnosticDescriptor> SupportedDiagnostics => | ||||
|             ImmutableArray.Create(_diagnosticDescriptor); | ||||
|  | ||||
|         /// <inheritdoc /> | ||||
|         protected override void AnalyzeCompilation(CompilationStartAnalysisContext compilationStartContext) | ||||
|         { | ||||
|             compilationStartContext.RegisterOperationAction( | ||||
|                 context => | ||||
|                 { | ||||
|                     var ansiConsoleType = context.Compilation.GetTypeByMetadataName("Spectre.Console.AnsiConsole"); | ||||
|  | ||||
|                     // 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 (!Equals(invocationOperation.TargetMethod.ContainingType, ansiConsoleType)) | ||||
|                     { | ||||
|                         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.Kind() == 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.Kind() == SyntaxKind.StaticKeyword))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,63 @@ | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.CSharp; | ||||
| using Microsoft.CodeAnalysis.Diagnostics; | ||||
| using Microsoft.CodeAnalysis.Operations; | ||||
|  | ||||
| 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 : BaseAnalyzer | ||||
|     { | ||||
|         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) | ||||
|         { | ||||
|             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 systemConsoleType = context.Compilation.GetTypeByMetadataName("System.Console"); | ||||
|  | ||||
|                 if (!Equals(invocationOperation.TargetMethod.ContainingType, systemConsoleType)) | ||||
|                 { | ||||
|                     return; | ||||
|                 } | ||||
|  | ||||
|                 var methodName = System.Array.Find(_methods, i => i.Equals(invocationOperation.TargetMethod.Name)); | ||||
|                 if (methodName == null) | ||||
|                 { | ||||
|                     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); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										8
									
								
								src/Spectre.Console.Analyzer/Constants.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								src/Spectre.Console.Analyzer/Constants.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,8 @@ | ||||
| namespace Spectre.Console.Analyzer | ||||
| { | ||||
|     internal static class Constants | ||||
|     { | ||||
|         internal const string StaticInstance = "AnsiConsole"; | ||||
|         internal const string SpectreConsole = "Spectre.Console"; | ||||
|     } | ||||
| } | ||||
							
								
								
									
										49
									
								
								src/Spectre.Console.Analyzer/Descriptors.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								src/Spectre.Console.Analyzer/Descriptors.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,49 @@ | ||||
| using System.Collections.Concurrent; | ||||
| using Microsoft.CodeAnalysis; | ||||
| 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/spectre.console.analyzers/rules/{id}"; | ||||
|             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."); | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,115 @@ | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.CodeActions; | ||||
| using Microsoft.CodeAnalysis.CSharp; | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| using static Microsoft.CodeAnalysis.CSharp.SyntaxFactory; | ||||
|  | ||||
| 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 originalCaller = ((MemberAccessExpressionSyntax)_originalInvocation.Expression).Name.ToString(); | ||||
|  | ||||
|             var syntaxTree = await _document.GetSyntaxTreeAsync(cancellationToken).ConfigureAwait(false); | ||||
|             var root = (CompilationUnitSyntax)await syntaxTree.GetRootAsync(cancellationToken).ConfigureAwait(false); | ||||
|  | ||||
|             // If there is an ansiConsole 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 ansiConsoleParameterDeclaration = GetAnsiConsoleParameterDeclaration(); | ||||
|             var ansiConsoleFieldIdentifier = GetAnsiConsoleFieldDeclaration(); | ||||
|             var ansiConsoleIdentifier = ansiConsoleParameterDeclaration ?? | ||||
|                                         ansiConsoleFieldIdentifier ?? | ||||
|                                         Constants.StaticInstance; | ||||
|  | ||||
|             // Replace the System.Console call with a call to the identifier above. | ||||
|             var newRoot = root.ReplaceNode( | ||||
|                 _originalInvocation, | ||||
|                 GetImportedSpectreCall(originalCaller, ansiConsoleIdentifier)); | ||||
|  | ||||
|             // If we are calling the static instance and Spectre isn't imported yet we should do so. | ||||
|             if (ansiConsoleIdentifier == Constants.StaticInstance && root.Usings.ToList().All(i => i.Name.ToString() != Constants.SpectreConsole)) | ||||
|             { | ||||
|                 newRoot = newRoot.AddUsings(Syntax.SpectreUsing); | ||||
|             } | ||||
|  | ||||
|             return _document.WithSyntaxRoot(newRoot); | ||||
|         } | ||||
|  | ||||
|         private string? GetAnsiConsoleParameterDeclaration() | ||||
|         { | ||||
|             return _originalInvocation | ||||
|                 .Ancestors().OfType<MethodDeclarationSyntax>() | ||||
|                 .First() | ||||
|                 .ParameterList.Parameters | ||||
|                 .FirstOrDefault(i => i.Type.NormalizeWhitespace().ToString() == "IAnsiConsole") | ||||
|                 ?.Identifier.Text; | ||||
|         } | ||||
|  | ||||
|         private string? GetAnsiConsoleFieldDeclaration() | ||||
|         { | ||||
|             // let's look to see if our call is in a static method. | ||||
|             // if so we'll only want to look for static IAnsiConsoles | ||||
|             // and vice-versa if we aren't. | ||||
|             var isStatic = _originalInvocation | ||||
|                 .Ancestors() | ||||
|                 .OfType<MethodDeclarationSyntax>() | ||||
|                 .First() | ||||
|                 .Modifiers.Any(i => i.Kind() == SyntaxKind.StaticKeyword); | ||||
|  | ||||
|             return _originalInvocation | ||||
|                 .Ancestors().OfType<ClassDeclarationSyntax>() | ||||
|                 .First() | ||||
|                 .Members | ||||
|                 .OfType<FieldDeclarationSyntax>() | ||||
|                 .FirstOrDefault(i => | ||||
|                     i.Declaration.Type.NormalizeWhitespace().ToString() == "IAnsiConsole" && | ||||
|                     (!isStatic ^ i.Modifiers.Any(modifier => modifier.Kind() == SyntaxKind.StaticKeyword))) | ||||
|                 ?.Declaration.Variables.First().Identifier.Text; | ||||
|         } | ||||
|  | ||||
|         private ExpressionSyntax GetImportedSpectreCall(string originalCaller, string ansiConsoleIdentifier) | ||||
|         { | ||||
|             return ExpressionStatement( | ||||
|                     InvocationExpression( | ||||
|                             MemberAccessExpression( | ||||
|                                 SyntaxKind.SimpleMemberAccessExpression, | ||||
|                                 IdentifierName(ansiConsoleIdentifier), | ||||
|                                 IdentifierName(originalCaller))) | ||||
|                         .WithArgumentList( | ||||
|                         _originalInvocation.ArgumentList)) | ||||
|             .Expression; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| using System.Collections.Immutable; | ||||
| using System.Composition; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.CodeFixes; | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| using Spectre.Console.Analyzer.CodeActions; | ||||
|  | ||||
| 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); | ||||
|             var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf<InvocationExpressionSyntax>(); | ||||
|             context.RegisterCodeFix( | ||||
|                 new SwitchToAnsiConsoleAction(context.Document, methodDeclaration, "Convert static AnsiConsole calls to local instance."), | ||||
|                 context.Diagnostics); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,35 @@ | ||||
| using System.Collections.Immutable; | ||||
| using System.Composition; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.CodeFixes; | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| using Spectre.Console.Analyzer.CodeActions; | ||||
|  | ||||
| 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); | ||||
|             var methodDeclaration = root.FindNode(context.Span).FirstAncestorOrSelf<InvocationExpressionSyntax>(); | ||||
|             context.RegisterCodeFix( | ||||
|                 new SwitchToAnsiConsoleAction(context.Document, methodDeclaration, "Convert static call to AnsiConsole to Spectre.Console.AnsiConsole"), | ||||
|                 context.Diagnostics); | ||||
|         } | ||||
|     } | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/Spectre.Console.Analyzer/Fixes/Syntax.cs
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/Spectre.Console.Analyzer/Fixes/Syntax.cs
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,10 @@ | ||||
| using Microsoft.CodeAnalysis.CSharp.Syntax; | ||||
| 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"))); | ||||
|     } | ||||
| } | ||||
							
								
								
									
										23
									
								
								src/Spectre.Console.Analyzer/Spectre.Console.Analyzer.csproj
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										23
									
								
								src/Spectre.Console.Analyzer/Spectre.Console.Analyzer.csproj
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,23 @@ | ||||
| <Project Sdk="Microsoft.NET.Sdk"> | ||||
|  | ||||
|     <PropertyGroup> | ||||
|         <TargetFramework>netstandard2.0</TargetFramework> | ||||
|         <IsPackable>true</IsPackable> | ||||
|         <Nullable>enable</Nullable> | ||||
|     </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="2.9.8" PrivateAssets="all" /> | ||||
|         <PackageReference Include="Microsoft.CodeAnalysis.CSharp" Version="2.6.1" /> | ||||
|         <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" /> | ||||
|     </ItemGroup> | ||||
|  | ||||
|     <ItemGroup> | ||||
|         <None Update="tools\*.ps1" CopyToOutputDirectory="Always" Pack="true" PackagePath="tools" /> | ||||
|         <None Include="$(OutputPath)\$(AssemblyName).dll" Pack="true" PackagePath="analyzers/dotnet/cs" Visible="false" /> | ||||
|     </ItemGroup> | ||||
| </Project> | ||||
| @@ -0,0 +1,39 @@ | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.Testing; | ||||
| using Spectre.Console.Analyzer; | ||||
| using Xunit; | ||||
| using AnalyzerVerify = | ||||
|     Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier< | ||||
|         Spectre.Console.Analyzer.FavorInstanceAnsiConsoleOverStaticAnalyzer>; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers.Analyzers | ||||
| { | ||||
|     public class FavorInstanceAnsiConsoleOverStaticAnalyzerTests | ||||
|     { | ||||
|         private static readonly DiagnosticResult _expectedDiagnostics = new( | ||||
|             Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id, | ||||
|             DiagnosticSeverity.Info); | ||||
|  | ||||
|         [Fact] | ||||
|         public async void Console_Write_Has_Warning() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     IAnsiConsole _ansiConsole = AnsiConsole.Console;     | ||||
|  | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         _ansiConsole.Write(""this is fine""); | ||||
|         AnsiConsole.Write(""Hello, World""); | ||||
|     }  | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(11, 9)) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,53 @@ | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.Testing; | ||||
| using Spectre.Console.Analyzer; | ||||
| using Xunit; | ||||
| using AnalyzerVerify = | ||||
|     Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier< | ||||
|         Spectre.Console.Analyzer.UseSpectreInsteadOfSystemConsoleAnalyzer>; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers.Analyzers | ||||
| { | ||||
|     public class UseSpectreInsteadOfSystemConsoleAnalyzerTests | ||||
|     { | ||||
|         private static readonly DiagnosticResult _expectedDiagnostics = new( | ||||
|             Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id, | ||||
|             DiagnosticSeverity.Warning); | ||||
|  | ||||
|         [Fact] | ||||
|         public async void Console_Write_Has_Warning() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
|  | ||||
| class TestClass { | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         Console.Write(""Hello, World""); | ||||
|     }  | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9)) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public async void Console_WriteLine_Has_Warning() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     void TestMethod() { | ||||
|         Console.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyAnalyzerAsync(Source, _expectedDiagnostics.WithLocation(7, 9)) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,56 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Threading; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis.CodeFixes; | ||||
| using Microsoft.VisualStudio.Composition; | ||||
| using Spectre.Console.Analyzer; | ||||
| using Spectre.Console.Analyzer.FixProviders; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers | ||||
| { | ||||
|     internal static class CodeFixProviderDiscovery | ||||
|     { | ||||
|         private static readonly Lazy<IExportProviderFactory> _exportProviderFactory; | ||||
|  | ||||
|         static CodeFixProviderDiscovery() | ||||
|         { | ||||
|             _exportProviderFactory = new Lazy<IExportProviderFactory>( | ||||
|                 () => | ||||
|                 { | ||||
|                     var discovery = new AttributedPartDiscovery(Resolver.DefaultInstance, isNonPublicSupported: true); | ||||
|                     var parts = Task.Run(() => discovery.CreatePartsAsync(typeof(SystemConsoleToAnsiConsoleFix).Assembly)).GetAwaiter().GetResult(); | ||||
|                     var catalog = ComposableCatalog.Create(Resolver.DefaultInstance).AddParts(parts); | ||||
|  | ||||
|                     var configuration = CompositionConfiguration.Create(catalog); | ||||
|                     var runtimeComposition = RuntimeComposition.CreateRuntimeComposition(configuration); | ||||
|                     return runtimeComposition.CreateExportProviderFactory(); | ||||
|                 }, | ||||
|                 LazyThreadSafetyMode.ExecutionAndPublication); | ||||
|         } | ||||
|  | ||||
|         public static IEnumerable<CodeFixProvider> GetCodeFixProviders(string language) | ||||
|         { | ||||
|             var exportProvider = _exportProviderFactory.Value.CreateExportProvider(); | ||||
|             var exports = exportProvider.GetExports<CodeFixProvider, LanguageMetadata>(); | ||||
|             return exports.Where(export => export.Metadata.Languages.Contains(language)).Select(export => export.Value); | ||||
|         } | ||||
|  | ||||
|         private class LanguageMetadata | ||||
|         { | ||||
|             public LanguageMetadata(IDictionary<string, object> data) | ||||
|             { | ||||
|                 if (!data.TryGetValue(nameof(ExportCodeFixProviderAttribute.Languages), out var languages)) | ||||
|                 { | ||||
|                     languages = Array.Empty<string>(); | ||||
|                 } | ||||
|  | ||||
|                 Languages = ((string[])languages).ToImmutableArray(); | ||||
|             } | ||||
|  | ||||
|             public ImmutableArray<string> Languages { get; } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,54 @@ | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.Testing; | ||||
| using Spectre.Console.Analyzer; | ||||
| using Xunit; | ||||
| using AnalyzerVerify = | ||||
|     Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier< | ||||
|         Spectre.Console.Analyzer.FavorInstanceAnsiConsoleOverStaticAnalyzer>; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers.Fixes | ||||
| { | ||||
|     public class UseInstanceOfStaticAnsiConsoleTests | ||||
|     { | ||||
|         private static readonly DiagnosticResult _expectedDiagnostic = new( | ||||
|             Descriptors.S1010_FavorInstanceAnsiConsoleOverStatic.Id, | ||||
|             DiagnosticSeverity.Info); | ||||
|  | ||||
|         [Fact] | ||||
|         public async Task SystemConsole_replaced_with_AnsiConsole() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     IAnsiConsole _ansiConsole = AnsiConsole.Console;     | ||||
|  | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         _ansiConsole.Write(""this is fine""); | ||||
|         AnsiConsole.Write(""Hello, World""); | ||||
|     }  | ||||
| }"; | ||||
|  | ||||
|             const string FixedSource = @" | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     IAnsiConsole _ansiConsole = AnsiConsole.Console;     | ||||
|  | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         _ansiConsole.Write(""this is fine""); | ||||
|         _ansiConsole.Write(""Hello, World""); | ||||
|     }  | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,152 @@ | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis; | ||||
| using Microsoft.CodeAnalysis.Testing; | ||||
| using Spectre.Console.Analyzer; | ||||
| using Xunit; | ||||
| using AnalyzerVerify = | ||||
|     Spectre.Console.Tests.CodeAnalyzers.SpectreAnalyzerVerifier< | ||||
|         Spectre.Console.Analyzer.UseSpectreInsteadOfSystemConsoleAnalyzer>; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers.Fixes | ||||
| { | ||||
|     public class UseSpectreInsteadOfSystemConsoleFixTests | ||||
|     { | ||||
|         private static readonly DiagnosticResult _expectedDiagnostic = new( | ||||
|             Descriptors.S1000_UseAnsiConsoleOverSystemConsole.Id, | ||||
|             DiagnosticSeverity.Warning); | ||||
|  | ||||
|         [Fact] | ||||
|         public async Task SystemConsole_replaced_with_AnsiConsole() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         Console.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             const string FixedSource = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         AnsiConsole.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public async Task SystemConsole_replaced_with_imported_AnsiConsole() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         Console.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             const string FixedSource = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         AnsiConsole.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(8, 9), FixedSource) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public async Task SystemConsole_replaced_with_field_AnsiConsole() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     IAnsiConsole _ansiConsole; | ||||
|  | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         Console.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             const string FixedSource = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     IAnsiConsole _ansiConsole; | ||||
|  | ||||
|     void TestMethod()  | ||||
|     { | ||||
|         _ansiConsole.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|  | ||||
|         [Fact] | ||||
|         public async Task SystemConsole_replaced_with_static_field_AnsiConsole() | ||||
|         { | ||||
|             const string Source = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     static IAnsiConsole _ansiConsole; | ||||
|  | ||||
|     static void TestMethod()  | ||||
|     { | ||||
|         Console.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             const string FixedSource = @" | ||||
| using System; | ||||
| using Spectre.Console; | ||||
|  | ||||
| class TestClass  | ||||
| { | ||||
|     static IAnsiConsole _ansiConsole; | ||||
|  | ||||
|     static void TestMethod()  | ||||
|     { | ||||
|         _ansiConsole.WriteLine(""Hello, World""); | ||||
|     } | ||||
| }"; | ||||
|  | ||||
|             await AnalyzerVerify | ||||
|                 .VerifyCodeFixAsync(Source, _expectedDiagnostic.WithLocation(11, 9), FixedSource) | ||||
|                 .ConfigureAwait(false); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,87 @@ | ||||
| using System; | ||||
| using System.Collections.Generic; | ||||
| using System.Collections.Immutable; | ||||
| using System.Linq; | ||||
| using System.Threading.Tasks; | ||||
| using Microsoft.CodeAnalysis.CodeFixes; | ||||
| using Microsoft.CodeAnalysis.CSharp.Testing; | ||||
| using Microsoft.CodeAnalysis.Diagnostics; | ||||
| using Microsoft.CodeAnalysis.Testing; | ||||
| using Microsoft.CodeAnalysis.Testing.Verifiers; | ||||
|  | ||||
| namespace Spectre.Console.Tests.CodeAnalyzers | ||||
| { | ||||
|     public static class SpectreAnalyzerVerifier<TAnalyzer> | ||||
|         where TAnalyzer : DiagnosticAnalyzer, new() | ||||
|     { | ||||
|         public static Task VerifyCodeFixAsync(string source, DiagnosticResult expected, string fixedSource) | ||||
|             => VerifyCodeFixAsync(source, new[] { expected }, fixedSource); | ||||
|  | ||||
|         private static Task VerifyCodeFixAsync(string source, IEnumerable<DiagnosticResult> expected, string fixedSource) | ||||
|         { | ||||
|             // Roslyn fixers always use \r\n for newlines, regardless of OS environment settings, so we normalize | ||||
|             // the source as it typically comes from multi-line strings with varying newlines. | ||||
|             if (Environment.NewLine != "\r\n") | ||||
|             { | ||||
|                 source = source.Replace(Environment.NewLine, "\r\n"); | ||||
|                 fixedSource = fixedSource.Replace(Environment.NewLine, "\r\n"); | ||||
|             } | ||||
|  | ||||
|             var test = new Test | ||||
|             { | ||||
|                 TestCode = source, | ||||
|                 FixedCode = fixedSource, | ||||
|             }; | ||||
|  | ||||
|             test.ExpectedDiagnostics.AddRange(expected); | ||||
|             return test.RunAsync(); | ||||
|         } | ||||
|  | ||||
|         public static Task VerifyAnalyzerAsync(string source, params DiagnosticResult[] expected) | ||||
|         { | ||||
|             var test = new Test | ||||
|             { | ||||
|                 TestCode = source, | ||||
|                 CompilerDiagnostics = CompilerDiagnostics.All, | ||||
|                 ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre, | ||||
|                 TestBehaviors = TestBehaviors.SkipGeneratedCodeCheck, | ||||
|             }; | ||||
|  | ||||
|             test.ExpectedDiagnostics.AddRange(expected); | ||||
|             return test.RunAsync(); | ||||
|         } | ||||
|  | ||||
|         // Code fix tests support both analyzer and code fix testing. This test class is derived from the code fix test | ||||
|         // to avoid the need to maintain duplicate copies of the customization work. | ||||
|         private class Test : CSharpCodeFixTest<TAnalyzer, EmptyCodeFixProvider, XUnitVerifier> | ||||
|         { | ||||
|             public Test() | ||||
|             { | ||||
|                 ReferenceAssemblies = CodeAnalyzerHelper.CurrentSpectre; | ||||
|                 TestBehaviors |= TestBehaviors.SkipGeneratedCodeCheck; | ||||
|             } | ||||
|  | ||||
|             protected override IEnumerable<CodeFixProvider> GetCodeFixProviders() | ||||
|             { | ||||
|                 var analyzer = new TAnalyzer(); | ||||
|                 foreach (var provider in CodeFixProviderDiscovery.GetCodeFixProviders(Language)) | ||||
|                 { | ||||
|                     if (analyzer.SupportedDiagnostics.Any(diagnostic => provider.FixableDiagnosticIds.Contains(diagnostic.Id))) | ||||
|                     { | ||||
|                         yield return provider; | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     internal static class CodeAnalyzerHelper | ||||
|     { | ||||
|         internal static ReferenceAssemblies CurrentSpectre { get; } | ||||
|  | ||||
|         static CodeAnalyzerHelper() | ||||
|         { | ||||
|             CurrentSpectre = ReferenceAssemblies.Net.Net50.AddAssemblies(ImmutableArray.Create(typeof(AnsiConsole).Assembly.Location.Replace(".dll", string.Empty))); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| @@ -17,6 +17,9 @@ | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Analyzer.Testing.XUnit" Version="1.1.0" /> | ||||
|     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.CodeFix.Testing.XUnit" Version="1.1.0" /> | ||||
|     <PackageReference Include="Microsoft.CodeAnalysis.CSharp.Workspaces" Version="2.6.1" /> | ||||
|     <PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.8.3" /> | ||||
|     <PackageReference Include="Shouldly" Version="4.0.3" /> | ||||
|     <PackageReference Include="Spectre.Verify.Extensions" Version="0.3.0" /> | ||||
| @@ -29,6 +32,7 @@ | ||||
|   </ItemGroup> | ||||
|  | ||||
|   <ItemGroup> | ||||
|     <ProjectReference Include="..\Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj" /> | ||||
|     <ProjectReference Include="..\Spectre.Console.Testing\Spectre.Console.Testing.csproj" /> | ||||
|     <ProjectReference Include="..\Spectre.Console\Spectre.Console.csproj" /> | ||||
|   </ItemGroup> | ||||
|   | ||||
| @@ -90,6 +90,10 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Shared", "..\examples\Share | ||||
| EndProject | ||||
| Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Live", "..\examples\Console\Live\Live.csproj", "{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AnalyzerTester", "..\examples\Console\AnalyzerTester\AnalyzerTester.csproj", "{D2B32355-D99F-480B-92BF-9FAABE79ADD4}" | ||||
| EndProject | ||||
| Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Spectre.Console.Analyzer", "Spectre.Console.Analyzer\Spectre.Console.Analyzer.csproj", "{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}" | ||||
| EndProject | ||||
| Global | ||||
| 	GlobalSection(SolutionConfigurationPlatforms) = preSolution | ||||
| 		Debug|Any CPU = Debug|Any CPU | ||||
| @@ -472,6 +476,30 @@ Global | ||||
| 		{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4}.Release|x86.Build.0 = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|Any CPU.Build.0 = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x64.ActiveCfg = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x64.Build.0 = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x86.ActiveCfg = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Debug|x86.Build.0 = Debug|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|Any CPU.ActiveCfg = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|Any CPU.Build.0 = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x64.ActiveCfg = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x64.Build.0 = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x86.ActiveCfg = Release|Any CPU | ||||
| 		{26006ACD-F19D-4C2A-8864-FE0D6C15B58C}.Release|x86.Build.0 = Release|Any CPU | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(SolutionProperties) = preSolution | ||||
| 		HideSolutionNode = FALSE | ||||
| @@ -508,6 +536,7 @@ Global | ||||
| 		{A0C772BA-C5F4-451D-AA7A-4045F2FA0201} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 		{8428A7DD-29FC-4417-9CA0-B90D34B26AB2} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201} | ||||
| 		{E607AA2A-A4A6-48E4-8AAB-B0EB74EACAA1} = {F0575243-121F-4DEE-9F6B-246E26DC0844} | ||||
| 		{D2B32355-D99F-480B-92BF-9FAABE79ADD4} = {A0C772BA-C5F4-451D-AA7A-4045F2FA0201} | ||||
| 	EndGlobalSection | ||||
| 	GlobalSection(ExtensibilityGlobals) = postSolution | ||||
| 		SolutionGuid = {5729B071-67A0-48FB-8B1B-275E6822086C} | ||||
|   | ||||
		Reference in New Issue
	
	Block a user