Add implicit usings analyzer and csproj formatter
This commit is contained in:
@@ -43,6 +43,12 @@ public partial class MainWindowViewModel : ViewModelBase
|
|||||||
ParityResults.Add(result);
|
ParityResults.Add(result);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[RelayCommand]
|
||||||
|
private async Task FormatCsProjFile(CancellationToken token)
|
||||||
|
{
|
||||||
|
FormatCsproj.FormatCsprojFile(SolutionFilePath);
|
||||||
|
}
|
||||||
|
|
||||||
[RelayCommand]
|
[RelayCommand]
|
||||||
private async Task LoadSolutionFile(CancellationToken token)
|
private async Task LoadSolutionFile(CancellationToken token)
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
<Button Command="{Binding LoadSolutionFolderCommand}" >Select Solution Folder</Button>
|
<Button Command="{Binding LoadSolutionFolderCommand}" >Select Solution Folder</Button>
|
||||||
<Button Command="{Binding LoadSolutionFileCommand}">Select Solution File</Button>
|
<Button Command="{Binding LoadSolutionFileCommand}">Select Solution File</Button>
|
||||||
<Button Command="{Binding ExecuteParityCheckerCommand}">Check Solution Parity</Button>
|
<Button Command="{Binding ExecuteParityCheckerCommand}">Check Solution Parity</Button>
|
||||||
|
<Button Command="{Binding FormatCsProjFileCommand}">Format CSharp Project File</Button>
|
||||||
<TextBlock Name="SolutionFilePath" Text="{Binding SolutionFilePath}" />
|
<TextBlock Name="SolutionFilePath" Text="{Binding SolutionFilePath}" />
|
||||||
<TextBlock Name="SolutionFolderPath" Text="{Binding SolutionFolderPath}" />
|
<TextBlock Name="SolutionFolderPath" Text="{Binding SolutionFolderPath}" />
|
||||||
<ListBox Name="Results" ItemsSource="{Binding ParityResults}"/>
|
<ListBox Name="Results" ItemsSource="{Binding ParityResults}"/>
|
||||||
|
|||||||
26
SolutionParityChecker.CLI/Commands/FormatCsprojCommand.cs
Normal file
26
SolutionParityChecker.CLI/Commands/FormatCsprojCommand.cs
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace SolutionParityChecker.CLI.Commands;
|
||||||
|
|
||||||
|
public class FormatCsprojCommand : Command<FormatCsprojCommand.Settings>
|
||||||
|
{
|
||||||
|
public sealed class Settings : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(1, "<CsprojFilePath>")]
|
||||||
|
public required string CsprojFilePath { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("-a|--enable-all")]
|
||||||
|
[Description("true to enable logging of all project files. Default is false.")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool EnableAll { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Execute(CommandContext context, Settings settings)
|
||||||
|
{
|
||||||
|
var pathToCsprojFile = settings.CsprojFilePath;
|
||||||
|
Console.WriteLine($"Retrieving C# Project from {pathToCsprojFile}");
|
||||||
|
FormatCsproj.FormatCsprojFile(pathToCsprojFile);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
83
SolutionParityChecker.CLI/Commands/ImplicitUsingsCommand.cs
Normal file
83
SolutionParityChecker.CLI/Commands/ImplicitUsingsCommand.cs
Normal file
@@ -0,0 +1,83 @@
|
|||||||
|
using System.ComponentModel;
|
||||||
|
using Spectre.Console.Cli;
|
||||||
|
|
||||||
|
namespace SolutionParityChecker.CLI.Commands;
|
||||||
|
|
||||||
|
public class ImplicitUsingsCommand : Command<ImplicitUsingsCommand.Settings>
|
||||||
|
{
|
||||||
|
public sealed class Settings : CommandSettings
|
||||||
|
{
|
||||||
|
[CommandArgument(1, "<SolutionFilePath>")]
|
||||||
|
public required string SolutionFilePath { get; set; }
|
||||||
|
|
||||||
|
[CommandOption("-m|--add-missing")]
|
||||||
|
[Description("Add Implicit Usings=true to projects missing them. Default is false.")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool AddMissing { get; set; } = false;
|
||||||
|
|
||||||
|
[CommandOption("-d|--enable-disabled")]
|
||||||
|
[Description("Sets Implicit Usings to true for any projects with it disabled. Default is false.")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool EnableDisabled { get; set; } = false;
|
||||||
|
|
||||||
|
[CommandOption("-a|--enable-all")]
|
||||||
|
[Description("Enables Implicit Usings for all projects. Default is false.")]
|
||||||
|
[DefaultValue(false)]
|
||||||
|
public bool EnableAll { get; set; } = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override int Execute(CommandContext context, Settings settings)
|
||||||
|
{
|
||||||
|
var pathToSolutionFile = settings.SolutionFilePath;
|
||||||
|
Console.WriteLine($"Retrieving Solution from {pathToSolutionFile}");
|
||||||
|
|
||||||
|
var solutionFile = SolutionParityChecker.ParseSolutionFileFromPath(pathToSolutionFile);
|
||||||
|
if (solutionFile == null)
|
||||||
|
{
|
||||||
|
Console.WriteLine(
|
||||||
|
"Failed to parse solution file. The file was either not found or malformed."
|
||||||
|
);
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
var cSharpProjects = SolutionParityChecker.GetCSharpProjectObjectsFromSolutionFile(solutionFile);
|
||||||
|
Console.WriteLine($"Found {cSharpProjects.Count} C# Projects");
|
||||||
|
Console.WriteLine("==================================================");
|
||||||
|
|
||||||
|
// Get the list of projects
|
||||||
|
var projectsMissingImplicitUsings = ImplicitUsings.FindCSharpProjectsMissingImplicitUsings(cSharpProjects);
|
||||||
|
|
||||||
|
Console.WriteLine(
|
||||||
|
$"{projectsMissingImplicitUsings.Count} C# Projects have missing or disabled implicit usings"
|
||||||
|
);
|
||||||
|
|
||||||
|
foreach (var project in projectsMissingImplicitUsings)
|
||||||
|
{
|
||||||
|
Console.WriteLine(project.DirectoryPath);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (settings.AddMissing)
|
||||||
|
{
|
||||||
|
Console.WriteLine("==================================================");
|
||||||
|
Console.WriteLine("Adding missing implicit usings");
|
||||||
|
ImplicitUsings.AddMissingImplicitUsings(projectsMissingImplicitUsings);
|
||||||
|
var updatedProjects = SolutionParityChecker.GetCSharpProjectObjectsFromSolutionFile(solutionFile);
|
||||||
|
var projectsWithMissing = ImplicitUsings.FindCSharpProjectsMissingImplicitUsings(updatedProjects);
|
||||||
|
Console.WriteLine($"There are now {projectsWithMissing.Count} C# Projects missing/disabled implicit usings");
|
||||||
|
}
|
||||||
|
if (settings.EnableDisabled)
|
||||||
|
{
|
||||||
|
Console.WriteLine("==================================================");
|
||||||
|
Console.WriteLine("Enabling disabled implicit usings");
|
||||||
|
ImplicitUsings.EnableDisabledImplicitUsings(projectsMissingImplicitUsings);
|
||||||
|
}
|
||||||
|
if (settings.EnableAll)
|
||||||
|
{
|
||||||
|
Console.WriteLine("==================================================");
|
||||||
|
Console.WriteLine("Enabling all implicit usings");
|
||||||
|
ImplicitUsings.EnableAllImplicitUsings(projectsMissingImplicitUsings);
|
||||||
|
}
|
||||||
|
Console.WriteLine("==================================================");
|
||||||
|
Console.WriteLine("Done!");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -8,6 +8,8 @@ app.Configure(config =>
|
|||||||
config.ValidateExamples();
|
config.ValidateExamples();
|
||||||
|
|
||||||
config.AddCommand<CompareCommand>("compare");
|
config.AddCommand<CompareCommand>("compare");
|
||||||
|
config.AddCommand<ImplicitUsingsCommand>("implicit-usings");
|
||||||
|
config.AddCommand<FormatCsprojCommand>("format-csproj");
|
||||||
});
|
});
|
||||||
|
|
||||||
return await app.RunAsync(args);
|
return await app.RunAsync(args);
|
||||||
|
|||||||
37
SolutionParityChecker/FormatCsproj.cs
Normal file
37
SolutionParityChecker/FormatCsproj.cs
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using System.Text;
|
||||||
|
using System.Xml;
|
||||||
|
|
||||||
|
namespace SolutionParityChecker;
|
||||||
|
|
||||||
|
public static class FormatCsproj
|
||||||
|
{
|
||||||
|
public static void FormatCsprojFile(string csprojFilePath)
|
||||||
|
{
|
||||||
|
using TextReader rd = new StreamReader(csprojFilePath, Encoding.Default);
|
||||||
|
|
||||||
|
XmlDocument doc = new XmlDocument();
|
||||||
|
doc.Load(rd);
|
||||||
|
|
||||||
|
if (rd != Console.In)
|
||||||
|
{
|
||||||
|
rd.Close();
|
||||||
|
}
|
||||||
|
|
||||||
|
using var wr = new StreamWriter(csprojFilePath, false, Encoding.Default);
|
||||||
|
|
||||||
|
var settings =
|
||||||
|
new XmlWriterSettings
|
||||||
|
{
|
||||||
|
Indent = true,
|
||||||
|
IndentChars = "\t",
|
||||||
|
NewLineOnAttributes = false,
|
||||||
|
OmitXmlDeclaration = true
|
||||||
|
};
|
||||||
|
|
||||||
|
using (var writer = XmlWriter.Create(wr, settings))
|
||||||
|
{
|
||||||
|
doc.WriteContentTo(writer);
|
||||||
|
writer.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
60
SolutionParityChecker/ImplicitUsings.cs
Normal file
60
SolutionParityChecker/ImplicitUsings.cs
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
using Microsoft.Build.Construction;
|
||||||
|
|
||||||
|
namespace SolutionParityChecker;
|
||||||
|
|
||||||
|
public static class ImplicitUsings
|
||||||
|
{
|
||||||
|
public static List<ProjectRootElement> FindCSharpProjectsMissingImplicitUsings(List<ProjectRootElement> projectList)
|
||||||
|
{
|
||||||
|
var projectsMissingImplicitUsings = new List<ProjectRootElement>();
|
||||||
|
|
||||||
|
foreach (var project in projectList)
|
||||||
|
{
|
||||||
|
var implicitUsings = project.PropertyGroups
|
||||||
|
.SelectMany(x => x.Properties)
|
||||||
|
.FirstOrDefault(x => x.Name == "ImplicitUsings");
|
||||||
|
if (implicitUsings is null || implicitUsings.Value is not "enable")
|
||||||
|
{
|
||||||
|
projectsMissingImplicitUsings.Add(project);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return projectsMissingImplicitUsings;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void AddMissingImplicitUsings(List<ProjectRootElement> projectsMissingImplicitUsings)
|
||||||
|
{
|
||||||
|
foreach (var project in projectsMissingImplicitUsings)
|
||||||
|
{
|
||||||
|
if (ProjectIsMissingImplicitUsings(project))
|
||||||
|
{
|
||||||
|
project.AddProperty("ImplicitUsings", "enable");
|
||||||
|
project.Save();
|
||||||
|
FormatCsproj.FormatCsprojFile(project.FullPath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnableDisabledImplicitUsings(List<ProjectRootElement> projectsMissingImplicitUsings)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void EnableAllImplicitUsings(List<ProjectRootElement> projectsMissingImplicitUsings)
|
||||||
|
{
|
||||||
|
throw new NotImplementedException();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static bool ProjectIsMissingImplicitUsings(ProjectRootElement project)
|
||||||
|
{
|
||||||
|
var implicitUsings = project.PropertyGroups
|
||||||
|
.SelectMany(x => x.Properties)
|
||||||
|
.FirstOrDefault(x => x.Name == "ImplicitUsings");
|
||||||
|
if (implicitUsings is null)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -60,4 +60,15 @@ public static class SolutionParityChecker
|
|||||||
|
|
||||||
return projectsMissingFromSolution;
|
return projectsMissingFromSolution;
|
||||||
}
|
}
|
||||||
|
public static List<ProjectRootElement> GetCSharpProjectObjectsFromSolutionFile(
|
||||||
|
SolutionFile solutionFile
|
||||||
|
)
|
||||||
|
{
|
||||||
|
var projectList = solutionFile.ProjectsByGuid
|
||||||
|
.Where(x => x.Value.ProjectType == SolutionProjectType.KnownToBeMSBuildFormat)
|
||||||
|
.Select(s => ProjectRootElement.Open(s.Value.AbsolutePath))
|
||||||
|
.ToList();
|
||||||
|
|
||||||
|
return projectList;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user