Add implicit usings analyzer and csproj formatter
This commit is contained in:
@@ -43,6 +43,12 @@ public partial class MainWindowViewModel : ViewModelBase
|
||||
ParityResults.Add(result);
|
||||
}
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task FormatCsProjFile(CancellationToken token)
|
||||
{
|
||||
FormatCsproj.FormatCsprojFile(SolutionFilePath);
|
||||
}
|
||||
|
||||
[RelayCommand]
|
||||
private async Task LoadSolutionFile(CancellationToken token)
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
<Button Command="{Binding LoadSolutionFolderCommand}" >Select Solution Folder</Button>
|
||||
<Button Command="{Binding LoadSolutionFileCommand}">Select Solution File</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="SolutionFolderPath" Text="{Binding SolutionFolderPath}" />
|
||||
<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.AddCommand<CompareCommand>("compare");
|
||||
config.AddCommand<ImplicitUsingsCommand>("implicit-usings");
|
||||
config.AddCommand<FormatCsprojCommand>("format-csproj");
|
||||
});
|
||||
|
||||
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;
|
||||
}
|
||||
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