From 7f01df73698aa04fff49325d9ea99c35272ec780 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Sun, 17 Aug 2025 14:18:25 +1000 Subject: [PATCH] rework mapping --- .../SolutionDiscovery/SharpIdeFile.cs | 14 ++++- .../SolutionDiscovery/SharpIdeFolder.cs | 21 ++++++- .../SolutionDiscovery/TreeMapperV2.cs | 34 ++++-------- .../VsPersistence/SharpIdeModels.cs | 55 +++++++++++++++++-- .../VsPersistence/VsPersistenceMapper.cs | 44 +-------------- 5 files changed, 95 insertions(+), 73 deletions(-) diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs index e63a7e3..0eb3df5 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFile.cs @@ -1,9 +1,19 @@ -using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; +using System.Diagnostics.CodeAnalysis; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.SolutionDiscovery; -public class SharpIdeFile : ISharpIdeNode +public class SharpIdeFile : ISharpIdeNode, IChildSharpIdeNode { + public required IExpandableSharpIdeNode Parent { get; set; } public required string Path { get; set; } public required string Name { get; set; } + + [SetsRequiredMembers] + internal SharpIdeFile(string fullPath, string name, IExpandableSharpIdeNode parent) + { + Path = fullPath; + Name = name; + Parent = parent; + } } diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs index 78c89fc..cb2c157 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/SharpIdeFolder.cs @@ -1,12 +1,29 @@ -using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; +using System.Diagnostics.CodeAnalysis; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.SolutionDiscovery; -public class SharpIdeFolder : ISharpIdeNode +public class SharpIdeFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildSharpIdeNode { + public required IExpandableSharpIdeNode Parent { get; set; } public required string Path { get; set; } public required string Name { get; set; } public required List Files { get; set; } public required List Folders { get; set; } public bool Expanded { get; set; } + + [SetsRequiredMembers] + public SharpIdeFolder(DirectoryInfo folderInfo, IExpandableSharpIdeNode parent) + { + Parent = parent; + Path = folderInfo.FullName; + Name = folderInfo.Name; + Files = folderInfo.GetFiles(this); + Folders = this.GetSubFolders(this); + } + + public SharpIdeFolder() + { + + } } diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs index a3f6112..4c42f29 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/TreeMapperV2.cs @@ -1,4 +1,5 @@ using System.Collections.Concurrent; +using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.SolutionDiscovery; @@ -9,22 +10,23 @@ public static class TreeMapperV2 return folder.Files .Concat(folder.Folders.SelectMany(sub => sub.GetAllFiles())); } - public static List GetSubFolders(string csprojectPath) + public static List GetSubFolders(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel) { var projectDirectory = Path.GetDirectoryName(csprojectPath)!; var rootFolder = new SharpIdeFolder { + Parent = sharpIdeProjectModel, Path = projectDirectory, Name = null!, Files = [], Folders = [] }; - var subFolders = rootFolder.GetSubFolders(); + var subFolders = rootFolder.GetSubFolders(sharpIdeProjectModel); return subFolders; } private static readonly string[] _excludedFolders = ["bin", "obj", "node_modules"]; - public static List GetSubFolders(this SharpIdeFolder folder) + public static List GetSubFolders(this SharpIdeFolder folder, IExpandableSharpIdeNode parent) { var directoryInfo = new DirectoryInfo(folder.Path); ConcurrentBag subFolders = []; @@ -45,33 +47,20 @@ public static class TreeMapperV2 Parallel.ForEach(subFolderInfos, subFolderInfo => { - var subFolder = new SharpIdeFolder - { - Path = subFolderInfo.FullName, - Name = subFolderInfo.Name, - Files = GetFiles(subFolderInfo), - Folders = new SharpIdeFolder - { - Path = subFolderInfo.FullName, - Name = subFolderInfo.Name, - Files = [], - Folders = [] - }.GetSubFolders() - }; - + var subFolder = new SharpIdeFolder(subFolderInfo, parent); subFolders.Add(subFolder); }); return subFolders.ToList(); } - public static List GetFiles(string csprojectPath) + public static List GetFiles(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel) { var projectDirectory = Path.GetDirectoryName(csprojectPath)!; var directoryInfo = new DirectoryInfo(projectDirectory); - return GetFiles(directoryInfo); + return GetFiles(directoryInfo, sharpIdeProjectModel); } - public static List GetFiles(DirectoryInfo directoryInfo) + public static List GetFiles(this DirectoryInfo directoryInfo, IExpandableSharpIdeNode parent) { List fileInfos; try @@ -83,10 +72,11 @@ public static class TreeMapperV2 return []; } - return fileInfos.Select(f => new SharpIdeFile + return fileInfos.Select(f => new SharpIdeFile(f.FullName, f.Name, parent) { Path = f.FullName, - Name = f.Name + Name = f.Name, + Parent = parent }).ToList(); } } diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs index 82cc378..b7bf399 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/SharpIdeModels.cs @@ -1,37 +1,84 @@ -using System.Threading.Channels; +using System.Diagnostics.CodeAnalysis; +using System.Threading.Channels; using Microsoft.Build.Evaluation; +using SharpIDE.Application.Features.Evaluation; namespace SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; public interface ISharpIdeNode; -public class SharpIdeSolutionModel : ISharpIdeNode +public interface IExpandableSharpIdeNode +{ + public bool Expanded { get; set; } +} +public interface IChildSharpIdeNode +{ + public IExpandableSharpIdeNode Parent { get; set; } +} + +public class SharpIdeSolutionModel : ISharpIdeNode, IExpandableSharpIdeNode { public required string Name { get; set; } public required string FilePath { get; set; } public required List Projects { get; set; } public required List Folders { get; set; } public required HashSet AllProjects { get; set; } + public bool Expanded { get; set; } + + [SetsRequiredMembers] + internal SharpIdeSolutionModel(string solutionFilePath, IntermediateSolutionModel intermediateModel) + { + var solutionName = Path.GetFileName(solutionFilePath); + AllProjects = []; + Name = solutionName; + FilePath = solutionFilePath; + Projects = intermediateModel.Projects.Select(s => new SharpIdeProjectModel(s, AllProjects, this)).ToList(); + Folders = intermediateModel.SolutionFolders.Select(s => new SharpIdeSolutionFolder(s, AllProjects, this)).ToList(); + } } -public class SharpIdeSolutionFolder : ISharpIdeNode +public class SharpIdeSolutionFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildSharpIdeNode { public required string Name { get; set; } public required List Folders { get; set; } public required List Projects { get; set; } public required List Files { get; set; } public bool Expanded { get; set; } + public required IExpandableSharpIdeNode Parent { get; set; } + + [SetsRequiredMembers] + internal SharpIdeSolutionFolder(IntermediateSlnFolderModel intermediateModel, HashSet allProjects, IExpandableSharpIdeNode parent) + { + Name = intermediateModel.Model.Name; + Parent = parent; + Files = intermediateModel.Files.Select(s => new SharpIdeFile(s.FullPath, s.Name, this)).ToList(); + Folders = intermediateModel.Folders.Select(x => new SharpIdeSolutionFolder(x, allProjects, this)).ToList(); + Projects = intermediateModel.Projects.Select(x => new SharpIdeProjectModel(x, allProjects, this)).ToList(); + } } -public class SharpIdeProjectModel : ISharpIdeNode +public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChildSharpIdeNode { public required string Name { get; set; } public required string FilePath { get; set; } public required List Folders { get; set; } public required List Files { get; set; } public bool Expanded { get; set; } + public required IExpandableSharpIdeNode Parent { get; set; } public bool Running { get; set; } public CancellationTokenSource? RunningCancellationTokenSource { get; set; } public required Task MsBuildEvaluationProjectTask { get; set; } + [SetsRequiredMembers] + internal SharpIdeProjectModel(IntermediateProjectModel projectModel, HashSet allProjects, IExpandableSharpIdeNode parent) + { + Parent = parent; + Name = projectModel.Model.ActualDisplayName; + FilePath = projectModel.FullFilePath; + Files = TreeMapperV2.GetFiles(projectModel.FullFilePath, this); + Folders = TreeMapperV2.GetSubFolders(projectModel.FullFilePath, this); + MsBuildEvaluationProjectTask = ProjectEvaluation.GetProject(projectModel.FullFilePath); + allProjects.Add(this); + } + public Project MsBuildEvaluationProject => MsBuildEvaluationProjectTask.IsCompletedSuccessfully ? MsBuildEvaluationProjectTask.Result : throw new InvalidOperationException("Do not attempt to access the MsBuildEvaluationProject before it has been loaded"); diff --git a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/VsPersistenceMapper.cs b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/VsPersistenceMapper.cs index 27e949a..705e7ec 100644 --- a/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/VsPersistenceMapper.cs +++ b/src/SharpIDE.Application/Features/SolutionDiscovery/VsPersistence/VsPersistenceMapper.cs @@ -11,53 +11,11 @@ public static class VsPersistenceMapper // This intermediate model is pretty much useless, but I have left it around as we grab the project nodes with it, which we might use later. var intermediateModel = await IntermediateMapper.GetIntermediateModel(solutionFilePath, cancellationToken); - var solutionName = Path.GetFileName(solutionFilePath); - var allProjects = new HashSet(); - var solutionModel = new SharpIdeSolutionModel - { - Name = solutionName, - FilePath = solutionFilePath, - Projects = intermediateModel.Projects.Select(s => GetSharpIdeProjectModel(s, allProjects)).ToList(), - AllProjects = allProjects, - Folders = intermediateModel.SolutionFolders.Select(s => new SharpIdeSolutionFolder - { - Name = s.Model.Name, - Files = s.Files.Select(GetSharpIdeFile).ToList(), - Folders = s.Folders.Select(x => GetSharpIdeSolutionFolder(x, allProjects)).ToList(), - Projects = s.Projects.Select(x => GetSharpIdeProjectModel(x, allProjects)).ToList() - }).ToList(), - }; + var solutionModel = new SharpIdeSolutionModel(solutionFilePath, intermediateModel); timer.Stop(); Console.WriteLine($"Solution model fully created in {timer.ElapsedMilliseconds} ms"); return solutionModel; } - private static SharpIdeProjectModel GetSharpIdeProjectModel(IntermediateProjectModel projectModel, HashSet allProjects) - { - var project = new SharpIdeProjectModel - { - Name = projectModel.Model.ActualDisplayName, - FilePath = projectModel.FullFilePath, - Files = TreeMapperV2.GetFiles(projectModel.FullFilePath), - Folders = TreeMapperV2.GetSubFolders(projectModel.FullFilePath), - MsBuildEvaluationProjectTask = ProjectEvaluation.GetProject(projectModel.FullFilePath) - }; - allProjects.Add(project); - return project; - } - - private static SharpIdeSolutionFolder GetSharpIdeSolutionFolder(IntermediateSlnFolderModel folderModel, HashSet allProjects) => new SharpIdeSolutionFolder() - { - Name = folderModel.Model.Name, - Files = folderModel.Files.Select(GetSharpIdeFile).ToList(), - Folders = folderModel.Folders.Select(s => GetSharpIdeSolutionFolder(s, allProjects)).ToList(), - Projects = folderModel.Projects.Select(s => GetSharpIdeProjectModel(s, allProjects)).ToList() - }; - - private static SharpIdeFile GetSharpIdeFile(IntermediateSlnFolderFileModel fileModel) => new SharpIdeFile - { - Path = fileModel.FullPath, - Name = fileModel.Name - }; }