diff --git a/DotNetSolutionTools.App/DotNetSolutionTools.App.csproj b/DotNetSolutionTools.App/DotNetSolutionTools.App.csproj index 42d8291..39a8aeb 100644 --- a/DotNetSolutionTools.App/DotNetSolutionTools.App.csproj +++ b/DotNetSolutionTools.App/DotNetSolutionTools.App.csproj @@ -6,6 +6,7 @@ true app.manifest true + enable @@ -28,6 +29,6 @@ - + diff --git a/DotNetSolutionTools.App/Services/FileService.cs b/DotNetSolutionTools.App/Services/FileService.cs new file mode 100644 index 0000000..72441f0 --- /dev/null +++ b/DotNetSolutionTools.App/Services/FileService.cs @@ -0,0 +1,40 @@ +using Avalonia; +using Avalonia.Controls.ApplicationLifetimes; +using Avalonia.Platform.Storage; + +namespace DotNetSolutionTools.App.Services; + +public class FileService +{ + public async Task DoOpenFilePickerAsync() + { + if ( + Application.Current?.ApplicationLifetime + is not IClassicDesktopStyleApplicationLifetime desktop + || desktop.MainWindow?.StorageProvider is not { } provider + ) + throw new NullReferenceException("Missing StorageProvider instance."); + + var files = await provider.OpenFilePickerAsync( + new FilePickerOpenOptions() { Title = "Open Text File", AllowMultiple = false } + ); + + return files?.Count >= 1 ? files[0] : null; + } + + public async Task DoOpenFolderPickerAsync() + { + if ( + Application.Current?.ApplicationLifetime + is not IClassicDesktopStyleApplicationLifetime desktop + || desktop.MainWindow?.StorageProvider is not { } provider + ) + throw new NullReferenceException("Missing StorageProvider instance."); + + var folder = await provider.OpenFolderPickerAsync( + new FolderPickerOpenOptions() { Title = "Open Text File", AllowMultiple = false } + ); + + return folder?.Count >= 1 ? folder[0] : null; + } +} diff --git a/DotNetSolutionTools.App/ViewModels/MainWindowViewModel.cs b/DotNetSolutionTools.App/ViewModels/MainWindowViewModel.cs index 0deac88..192c451 100644 --- a/DotNetSolutionTools.App/ViewModels/MainWindowViewModel.cs +++ b/DotNetSolutionTools.App/ViewModels/MainWindowViewModel.cs @@ -1,21 +1,15 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Avalonia; -using Avalonia.Controls.ApplicationLifetimes; -using Avalonia.Platform.Storage; +using System.Collections.ObjectModel; using CommunityToolkit.Mvvm.ComponentModel; using CommunityToolkit.Mvvm.Input; +using DotNetSolutionTools.App.Services; using DotNetSolutionTools.Core; namespace DotNetSolutionTools.App.ViewModels; public partial class MainWindowViewModel : ViewModelBase { + private readonly FileService _fileService = new(); + [ObservableProperty] private string _solutionFolderPath; @@ -23,33 +17,57 @@ public partial class MainWindowViewModel : ViewModelBase private string _solutionFilePath; [ObservableProperty] - private string? _fileText; + private string _csprojFilePath; [ObservableProperty] - private ObservableCollection _parityResults = new ObservableCollection() - { - "Test" - }; + private ObservableCollection _parityResults = new() { }; [RelayCommand] private async Task ExecuteParityChecker(CancellationToken token) { - var results = - DotNetSolutionTools.Core.SolutionParityChecker.CompareSolutionAndCSharpProjects( - SolutionFolderPath, - SolutionFilePath - ); + var results = SolutionProjectParity.CompareSolutionAndCSharpProjects( + SolutionFolderPath, + SolutionFilePath + ); ParityResults.Clear(); foreach (var result in results) - { ParityResults.Add(result); - } } [RelayCommand] private async Task FormatCsProjFile(CancellationToken token) { - FormatCsproj.FormatCsprojFile(SolutionFilePath); + FormatCsproj.FormatCsprojFile(CsprojFilePath); + } + + [RelayCommand] + private async Task FormatAllCsprojFilesInSolutionFile(CancellationToken token) + { + var csprojList = SolutionProjectParity.RetrieveAllCSharpProjectFullPathsFromFolder( + SolutionFolderPath + ); + foreach (var csproj in csprojList) + { + FormatCsproj.FormatCsprojFile(csproj); + } + } + + [RelayCommand] + private async Task FormatAllCsprojFilesInSolutionFolder(CancellationToken token) + { + var csprojList = SolutionProjectParity.RetrieveAllCSharpProjectFullPathsFromFolder( + SolutionFolderPath + ); + foreach (var csproj in csprojList) + { + FormatCsproj.FormatCsprojFile(csproj); + } + } + + [RelayCommand] + private async Task CheckForMissingImplicitUsingsInSolutionFile(CancellationToken token) + { + ImplicitUsings.FindCSharpProjectsMissingImplicitUsings(SolutionFilePath); } [RelayCommand] @@ -58,7 +76,7 @@ public partial class MainWindowViewModel : ViewModelBase ErrorMessages?.Clear(); try { - var file = await DoOpenFilePickerAsync(); + var file = await _fileService.DoOpenFilePickerAsync(); if (file is null) return; @@ -70,13 +88,19 @@ public partial class MainWindowViewModel : ViewModelBase } } + [RelayCommand] + private async Task ClearSolutionFile(CancellationToken token) + { + SolutionFilePath = string.Empty; + } + [RelayCommand] private async Task LoadSolutionFolder(CancellationToken token) { ErrorMessages?.Clear(); try { - var folder = await DoOpenFolderPickerAsync(); + var folder = await _fileService.DoOpenFolderPickerAsync(); if (folder is null) return; @@ -88,49 +112,33 @@ public partial class MainWindowViewModel : ViewModelBase } } - private async Task DoOpenFilePickerAsync() + [RelayCommand] + private async Task ClearSolutionFolder(CancellationToken token) { - // For learning purposes, we opted to directly get the reference - // for StorageProvider APIs here inside the ViewModel. - - // For your real-world apps, you should follow the MVVM principles - // by making service classes and locating them with DI/IoC. - - // See IoCFileOps project for an example of how to accomplish this. - if ( - Application.Current?.ApplicationLifetime - is not IClassicDesktopStyleApplicationLifetime desktop - || desktop.MainWindow?.StorageProvider is not { } provider - ) - throw new NullReferenceException("Missing StorageProvider instance."); - - var files = await provider.OpenFilePickerAsync( - new FilePickerOpenOptions() { Title = "Open Text File", AllowMultiple = false } - ); - - return files?.Count >= 1 ? files[0] : null; + SolutionFolderPath = string.Empty; } - private async Task DoOpenFolderPickerAsync() + [RelayCommand] + private async Task LoadCsprojFile(CancellationToken token) { - // For learning purposes, we opted to directly get the reference - // for StorageProvider APIs here inside the ViewModel. + ErrorMessages?.Clear(); + try + { + var folder = await _fileService.DoOpenFilePickerAsync(); + if (folder is null) + return; - // For your real-world apps, you should follow the MVVM principles - // by making service classes and locating them with DI/IoC. + CsprojFilePath = folder.Path.AbsolutePath; + } + catch (Exception e) + { + ErrorMessages?.Add(e.Message); + } + } - // See IoCFileOps project for an example of how to accomplish this. - if ( - Application.Current?.ApplicationLifetime - is not IClassicDesktopStyleApplicationLifetime desktop - || desktop.MainWindow?.StorageProvider is not { } provider - ) - throw new NullReferenceException("Missing StorageProvider instance."); - - var folder = await provider.OpenFolderPickerAsync( - new FolderPickerOpenOptions() { Title = "Open Text File", AllowMultiple = false } - ); - - return folder?.Count >= 1 ? folder[0] : null; + [RelayCommand] + private async Task ClearCsprojFile(CancellationToken token) + { + CsprojFilePath = string.Empty; } } diff --git a/DotNetSolutionTools.App/ViewModels/PreviewMainWindowViewModel.cs b/DotNetSolutionTools.App/ViewModels/PreviewMainWindowViewModel.cs new file mode 100644 index 0000000..d831b27 --- /dev/null +++ b/DotNetSolutionTools.App/ViewModels/PreviewMainWindowViewModel.cs @@ -0,0 +1,53 @@ +using System.Collections.ObjectModel; +using CommunityToolkit.Mvvm.ComponentModel; +using CommunityToolkit.Mvvm.Input; + +namespace DotNetSolutionTools.App.ViewModels; + +public partial class PreviewMainWindowViewModel : ViewModelBase +{ + [ObservableProperty] + private string _solutionFolderPath = + "C:\\Users\\matt\\source\\repos\\DotNetSolutionTools\\DotNetSolutionTools.App"; + + [ObservableProperty] + private string _solutionFilePath = + "C:\\Users\\matt\\source\\repos\\DotNetSolutionTools\\DotNetSolutionTools.App.sln"; + + [ObservableProperty] + private string _csprojFilePath = + "C:\\Users\\matt\\source\\repos\\DotNetSolutionTools\\DotNetSolutionTools.App.csproj"; + + [ObservableProperty] + private ObservableCollection _parityResults = new() { "Error Message" }; + + [RelayCommand] + private async Task ExecuteParityChecker(CancellationToken token) + { + throw new NotImplementedException(); + } + + [RelayCommand] + private async Task FormatCsProjFile(CancellationToken token) + { + throw new NotImplementedException(); + } + + [RelayCommand] + private async Task LoadSolutionFile(CancellationToken token) + { + throw new NotImplementedException(); + } + + [RelayCommand] + private async Task LoadSolutionFolder(CancellationToken token) + { + throw new NotImplementedException(); + } + + [RelayCommand] + private async Task LoadCsprojFile(CancellationToken token) + { + throw new NotImplementedException(); + } +} diff --git a/DotNetSolutionTools.App/Views/MainWindow.axaml b/DotNetSolutionTools.App/Views/MainWindow.axaml index 472c6ba..a1d108b 100644 --- a/DotNetSolutionTools.App/Views/MainWindow.axaml +++ b/DotNetSolutionTools.App/Views/MainWindow.axaml @@ -4,27 +4,86 @@ xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:viewModels="clr-namespace:DotNetSolutionTools.App.ViewModels" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" + Width="800" Height="450" x:Class="DotNetSolutionTools.App.Views.MainWindow" x:DataType="viewModels:MainWindowViewModel" Icon="/Assets/avalonia-logo.ico" - Title="FileOps"> + Title="DotNetSolutionTools"> - - Welcome to Avalonia! - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + - + \ No newline at end of file diff --git a/DotNetSolutionTools.CLI/Commands/CompareCommand.cs b/DotNetSolutionTools.CLI/Commands/CompareCommand.cs index af70e9d..dd601af 100644 --- a/DotNetSolutionTools.CLI/Commands/CompareCommand.cs +++ b/DotNetSolutionTools.CLI/Commands/CompareCommand.cs @@ -28,7 +28,7 @@ public class CompareCommand : Command var pathToSolutionFile = settings.SolutionFilePath; Console.WriteLine($"Retrieving C# Projects from {folderDirectory}"); - var csprojList = SolutionParityChecker.RetrieveAllCSharpProjectNamesFromFolder( + var csprojList = SolutionProjectParity.RetrieveAllCSharpProjectNamesFromFolder( folderDirectory ); @@ -45,7 +45,7 @@ public class CompareCommand : Command Console.WriteLine($"Parsing Solution File: {pathToSolutionFile}"); // Load the solution file - var solutionFile = SolutionParityChecker.ParseSolutionFileFromPath(pathToSolutionFile); + var solutionFile = SolutionProjectParity.ParseSolutionFileFromPath(pathToSolutionFile); if (solutionFile == null) { Console.WriteLine( @@ -55,7 +55,7 @@ public class CompareCommand : Command } // Get the list of projects - var projectsMissingFromSolution = SolutionParityChecker.FindProjectsMissingFromSolution( + var projectsMissingFromSolution = SolutionProjectParity.FindProjectsMissingFromSolution( csprojList, solutionFile ); diff --git a/DotNetSolutionTools.CLI/Commands/FormatCsprojCommand.cs b/DotNetSolutionTools.CLI/Commands/FormatCsprojCommand.cs index 4c5c195..242599f 100644 --- a/DotNetSolutionTools.CLI/Commands/FormatCsprojCommand.cs +++ b/DotNetSolutionTools.CLI/Commands/FormatCsprojCommand.cs @@ -29,7 +29,7 @@ public class FormatCsprojCommand : Command } else if (!string.IsNullOrWhiteSpace(settings.SolutionFilePath)) { - var test = SolutionParityChecker.ParseSolutionFileFromPath(settings.SolutionFilePath); + var test = SolutionProjectParity.ParseSolutionFileFromPath(settings.SolutionFilePath); if (test == null) { Console.WriteLine( @@ -37,7 +37,7 @@ public class FormatCsprojCommand : Command ); return 1; } - var cSharpProjects = SolutionParityChecker.GetCSharpProjectObjectsFromSolutionFile( + var cSharpProjects = SolutionProjectParity.GetCSharpProjectObjectsFromSolutionFile( test ); Console.WriteLine($"Found {cSharpProjects.Count} C# Projects"); @@ -54,7 +54,7 @@ public class FormatCsprojCommand : Command var folderDirectory = settings.SolutionFolderPath; // Include the trailing slash Console.WriteLine($"Retrieving C# Projects from {folderDirectory}"); - var csprojList = SolutionParityChecker.RetrieveAllCSharpProjectFullPathsFromFolder( + var csprojList = SolutionProjectParity.RetrieveAllCSharpProjectFullPathsFromFolder( folderDirectory ); diff --git a/DotNetSolutionTools.CLI/Commands/ImplicitUsingsCommand.cs b/DotNetSolutionTools.CLI/Commands/ImplicitUsingsCommand.cs index 0bb05c5..03f306e 100644 --- a/DotNetSolutionTools.CLI/Commands/ImplicitUsingsCommand.cs +++ b/DotNetSolutionTools.CLI/Commands/ImplicitUsingsCommand.cs @@ -34,7 +34,7 @@ public class ImplicitUsingsCommand : Command var pathToSolutionFile = settings.SolutionFilePath; Console.WriteLine($"Retrieving Solution from {pathToSolutionFile}"); - var solutionFile = SolutionParityChecker.ParseSolutionFileFromPath(pathToSolutionFile); + var solutionFile = SolutionProjectParity.ParseSolutionFileFromPath(pathToSolutionFile); if (solutionFile == null) { Console.WriteLine( @@ -42,7 +42,7 @@ public class ImplicitUsingsCommand : Command ); return 1; } - var cSharpProjects = SolutionParityChecker.GetCSharpProjectObjectsFromSolutionFile( + var cSharpProjects = SolutionProjectParity.GetCSharpProjectObjectsFromSolutionFile( solutionFile ); Console.WriteLine($"Found {cSharpProjects.Count} C# Projects"); @@ -67,7 +67,7 @@ public class ImplicitUsingsCommand : Command Console.WriteLine("=================================================="); Console.WriteLine("Adding missing implicit usings"); ImplicitUsings.AddMissingImplicitUsings(projectsMissingImplicitUsings); - var updatedProjects = SolutionParityChecker.GetCSharpProjectObjectsFromSolutionFile( + var updatedProjects = SolutionProjectParity.GetCSharpProjectObjectsFromSolutionFile( solutionFile ); var projectsWithMissing = ImplicitUsings.FindCSharpProjectsMissingImplicitUsings( diff --git a/DotNetSolutionTools.Core/FormatCsproj.cs b/DotNetSolutionTools.Core/FormatCsproj.cs index 797e9ea..d659582 100644 --- a/DotNetSolutionTools.Core/FormatCsproj.cs +++ b/DotNetSolutionTools.Core/FormatCsproj.cs @@ -19,14 +19,13 @@ public static class FormatCsproj using var wr = new StreamWriter(csprojFilePath, false, Encoding.Default); - var settings = - new XmlWriterSettings - { - Indent = true, - IndentChars = "\t", - NewLineOnAttributes = false, - OmitXmlDeclaration = true - }; + var settings = new XmlWriterSettings + { + Indent = true, + IndentChars = "\t", + NewLineOnAttributes = false, + OmitXmlDeclaration = true + }; using (var writer = XmlWriter.Create(wr, settings)) { @@ -34,4 +33,4 @@ public static class FormatCsproj writer.Close(); } } -} \ No newline at end of file +} diff --git a/DotNetSolutionTools.Core/ImplicitUsings.cs b/DotNetSolutionTools.Core/ImplicitUsings.cs index 2b6be75..4214525 100644 --- a/DotNetSolutionTools.Core/ImplicitUsings.cs +++ b/DotNetSolutionTools.Core/ImplicitUsings.cs @@ -4,10 +4,26 @@ namespace DotNetSolutionTools.Core; public static class ImplicitUsings { - public static List FindCSharpProjectsMissingImplicitUsings(List projectList) + public static List FindCSharpProjectsMissingImplicitUsings(string solutionFilePath) + { + var solutionFile = SolutionFile.Parse(solutionFilePath); + var csprojList = SolutionProjectParity.GetCSharpProjectObjectsFromSolutionFile( + solutionFile + ); + var projectsMissingImplicitUsings = FindCSharpProjectsMissingImplicitUsings(csprojList); + var projectsMissingImplicitUsingsStringList = projectsMissingImplicitUsings + .Select(x => x.FullPath) + .ToList(); + + return projectsMissingImplicitUsingsStringList; + } + + public static List FindCSharpProjectsMissingImplicitUsings( + List projectList + ) { var projectsMissingImplicitUsings = new List(); - + foreach (var project in projectList) { var implicitUsings = project.PropertyGroups @@ -22,7 +38,9 @@ public static class ImplicitUsings return projectsMissingImplicitUsings; } - public static void AddMissingImplicitUsings(List projectsMissingImplicitUsings) + public static void AddMissingImplicitUsings( + List projectsMissingImplicitUsings + ) { foreach (var project in projectsMissingImplicitUsings) { @@ -35,16 +53,20 @@ public static class ImplicitUsings } } - public static void EnableDisabledImplicitUsings(List projectsMissingImplicitUsings) + public static void EnableDisabledImplicitUsings( + List projectsMissingImplicitUsings + ) { throw new NotImplementedException(); } - public static void EnableAllImplicitUsings(List projectsMissingImplicitUsings) + public static void EnableAllImplicitUsings( + List projectsMissingImplicitUsings + ) { throw new NotImplementedException(); } - + public static bool ProjectIsMissingImplicitUsings(ProjectRootElement project) { var implicitUsings = project.PropertyGroups @@ -57,4 +79,4 @@ public static class ImplicitUsings return false; } -} \ No newline at end of file +} diff --git a/DotNetSolutionTools.Core/SolutionParityChecker.cs b/DotNetSolutionTools.Core/SolutionProjectParity.cs similarity index 98% rename from DotNetSolutionTools.Core/SolutionParityChecker.cs rename to DotNetSolutionTools.Core/SolutionProjectParity.cs index 47d1e2d..24a9dfa 100644 --- a/DotNetSolutionTools.Core/SolutionParityChecker.cs +++ b/DotNetSolutionTools.Core/SolutionProjectParity.cs @@ -2,7 +2,7 @@ namespace DotNetSolutionTools.Core; -public static class SolutionParityChecker +public static class SolutionProjectParity { public static List CompareSolutionAndCSharpProjects( string solutionFolderPath,