From fc9be38ee4653e830d7e7d2ae56d5596aa9cee3a Mon Sep 17 00:00:00 2001 From: Mykyta Kovalenko Date: Wed, 8 Mar 2023 11:40:39 +0100 Subject: [PATCH 1/2] Add support for nested paths in SlnGenSolutionFolder --- .../SlnHierarchyTests.cs | 34 ++++++++++++++++ src/Microsoft.VisualStudio.SlnGen/SlnFile.cs | 14 ++----- .../SlnHierarchy.cs | 40 ++++++++++++++----- 3 files changed, 68 insertions(+), 20 deletions(-) diff --git a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs index e9dd6c65..14d3e443 100644 --- a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs +++ b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs @@ -218,6 +218,40 @@ public void HierarchyWithCollapsedFoldersIsCorrectlyFormed() StringCompareShould.IgnoreLineEndings); } + [Fact] + public void SlnGenSolutionFolderNestedHierarchy() + { + SlnProject[] projects = + { + new SlnProject + { + FullPath = "baz.csproj", + Name = "baz", + ProjectGuid = Guid.NewGuid(), + ProjectTypeGuid = SlnProject.DefaultLegacyProjectTypeGuid, + SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz") + }, + new SlnProject + { + FullPath = "baz1.csproj", + Name = "baz1", + ProjectGuid = Guid.NewGuid(), + ProjectTypeGuid = SlnProject.DefaultLegacyProjectTypeGuid, + SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz1") + } + }; + + SlnHierarchy hierarchy = SlnHierarchy.CreateFromProjectSolutionFolder(projects); + + GetFolderStructureAsString(hierarchy.Folders).ShouldBe( + $@"zoo - zoo +zoo{Path.DirectorySeparatorChar}foo - foo +zoo{Path.DirectorySeparatorChar}foo{Path.DirectorySeparatorChar}bar - bar +zoo{Path.DirectorySeparatorChar}foo{Path.DirectorySeparatorChar}bar{Path.DirectorySeparatorChar}baz - baz +zoo{Path.DirectorySeparatorChar}foo{Path.DirectorySeparatorChar}bar{Path.DirectorySeparatorChar}baz1 - baz1", + StringCompareShould.IgnoreLineEndings); + } + private static string GetFolderStructureAsString(IEnumerable folders) { return string.Join( diff --git a/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs b/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs index b4781ab4..91a7e157 100644 --- a/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs +++ b/src/Microsoft.VisualStudio.SlnGen/SlnFile.cs @@ -209,15 +209,6 @@ public static (string solutionFileFullPath, int customProjectTypeGuidCount, int solution.AddSolutionItems(solutionItems); - if (!arguments.EnableFolders()) - { - foreach (SlnProject project in solution._projects.Distinct(new EqualityComparer((x, y) => string.Equals(x.SolutionFolder, y.SolutionFolder))) - .Where(i => !string.IsNullOrWhiteSpace(i.SolutionFolder) && i.SolutionFolder.Contains(Path.DirectorySeparatorChar))) - { - logger.LogError($"The value of SlnGenSolutionFolder \"{project.SolutionFolder}\" cannot contain directory separators."); - } - } - if (!logger.HasLoggedErrors) { solution.Save(solutionFileFullPath, arguments.EnableFolders(), arguments.EnableCollapseFolders(), arguments.EnableAlwaysBuild()); @@ -439,7 +430,7 @@ internal void Save(string rootPath, TextWriter writer, bool useFolders, bool col { foreach (SlnFolder folder in hierarchy.Folders) { - string projectSolutionPath = (useFolders ? folder.FullPath.ToRelativePath(rootPath) : folder.Name).ToSolutionPath(); + string projectSolutionPath = (useFolders ? folder.FullPath.ToRelativePath(rootPath) : folder.FullPath).ToSolutionPath(); // Try to preserve the folder GUID if a matching relative folder path was parsed from an existing solution if (ExistingProjectGuids != null && ExistingProjectGuids.TryGetValue(projectSolutionPath, out Guid projectGuid)) @@ -531,7 +522,8 @@ internal void Save(string rootPath, TextWriter writer, bool useFolders, bool col writer.WriteLine($@" {project.ProjectGuid.ToSolutionString()} = {folder.FolderGuid.ToSolutionString()}"); } - if (useFolders) + // guard against synthetic root name + if (folder.Parent.Name != string.Empty) { writer.WriteLine($@" {folder.FolderGuid.ToSolutionString()} = {folder.Parent.FolderGuid.ToSolutionString()}"); } diff --git a/src/Microsoft.VisualStudio.SlnGen/SlnHierarchy.cs b/src/Microsoft.VisualStudio.SlnGen/SlnHierarchy.cs index 07332ebe..e6ca1f2c 100644 --- a/src/Microsoft.VisualStudio.SlnGen/SlnHierarchy.cs +++ b/src/Microsoft.VisualStudio.SlnGen/SlnHierarchy.cs @@ -80,19 +80,41 @@ public static SlnHierarchy CreateFromProjectSolutionFolder(IReadOnlyList !string.IsNullOrWhiteSpace(i.SolutionFolder))) { - if (!hierarchy._pathToSlnFolderMap.TryGetValue(project.SolutionFolder, out SlnFolder folder)) + string[] nestedFolders = project.SolutionFolder.Split(new[] { Path.DirectorySeparatorChar }, StringSplitOptions.RemoveEmptyEntries); + + for (int i = 0; i < nestedFolders.Length; i++) { - folder = new SlnFolder(project.SolutionFolder) - { - Parent = hierarchy._rootFolder, - }; + string folderPath = string.Join(Path.DirectorySeparatorChar.ToString(), nestedFolders, 0, i + 1); - hierarchy._pathToSlnFolderMap.Add(project.SolutionFolder, folder); + if (!hierarchy._pathToSlnFolderMap.TryGetValue(folderPath, out SlnFolder nested)) + { + SlnFolder parent; + + if (i == 0) + { + parent = hierarchy._rootFolder; + } + else + { + string parentString = Path.GetDirectoryName(folderPath); + parent = hierarchy._pathToSlnFolderMap[parentString]; + } + + nested = new SlnFolder(folderPath) + { + Parent = parent, + }; + + hierarchy._pathToSlnFolderMap.Add(folderPath, nested); + + parent.Folders.Add(nested); + } - hierarchy._rootFolder.Folders.Add(folder); + if (i == nestedFolders.Length - 1) + { + nested.Projects.Add(project); + } } - - folder.Projects.Add(project); } return hierarchy; From b364154ab3dcef7ac1ebfb25877af983ed67ae1a Mon Sep 17 00:00:00 2001 From: Mykyta Kovalenko Date: Wed, 8 Mar 2023 11:52:21 +0100 Subject: [PATCH 2/2] Fix StyleCop warnings --- .../SlnHierarchyTests.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs index 14d3e443..75160e4e 100644 --- a/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs +++ b/src/Microsoft.VisualStudio.SlnGen.UnitTests/SlnHierarchyTests.cs @@ -229,7 +229,7 @@ public void SlnGenSolutionFolderNestedHierarchy() Name = "baz", ProjectGuid = Guid.NewGuid(), ProjectTypeGuid = SlnProject.DefaultLegacyProjectTypeGuid, - SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz") + SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz"), }, new SlnProject { @@ -237,8 +237,8 @@ public void SlnGenSolutionFolderNestedHierarchy() Name = "baz1", ProjectGuid = Guid.NewGuid(), ProjectTypeGuid = SlnProject.DefaultLegacyProjectTypeGuid, - SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz1") - } + SolutionFolder = Path.Combine("zoo", "foo", "bar", "baz1"), + }, }; SlnHierarchy hierarchy = SlnHierarchy.CreateFromProjectSolutionFolder(projects); @@ -251,7 +251,7 @@ public void SlnGenSolutionFolderNestedHierarchy() zoo{Path.DirectorySeparatorChar}foo{Path.DirectorySeparatorChar}bar{Path.DirectorySeparatorChar}baz1 - baz1", StringCompareShould.IgnoreLineEndings); } - + private static string GetFolderStructureAsString(IEnumerable folders) { return string.Join(