diff --git a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs index f771a5c234b7..c445aec1b153 100644 --- a/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs +++ b/src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs @@ -19,28 +19,6 @@ internal class RemoveProjectFromSolutionCommand : CommandBase private readonly string _fileOrDirectory; private readonly IReadOnlyCollection _projects; - private int CountNonFolderDescendants( - SolutionModel solution, - SolutionFolderModel item, - Dictionary solutionItemsGroupedByParent, - Dictionary cached) - { - if (cached.ContainsKey(item)) - { - return cached[item]; - } - int count = item.Files?.Count ?? 0; - var children = solutionItemsGroupedByParent.TryGetValue(item, out var items) ? items : Array.Empty(); - foreach (var child in children) - { - count += child is SolutionFolderModel folderModel - ? CountNonFolderDescendants(solution, folderModel, solutionItemsGroupedByParent, cached) - : 1; - } - cached.Add(item, count); - return count; - } - public RemoveProjectFromSolutionCommand(ParseResult parseResult) : base(parseResult) { _fileOrDirectory = parseResult.GetValue(SlnCommandParser.SlnArgument); @@ -92,7 +70,7 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< ISolutionSerializer serializer = SlnCommandParser.GetSolutionSerializer(solutionFileFullPath); SolutionModel solution = await serializer.OpenAsync(solutionFileFullPath, cancellationToken); - // set UTF8 BOM encoding for .sln + // set UTF-8 BOM encoding for .sln if (serializer is ISolutionSerializer v12Serializer) { solution.SerializerExtension = v12Serializer.CreateModelExtension(new() @@ -115,21 +93,40 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable< } } - Dictionary solutionItemsGroupedByParent = solution.SolutionItems - .Where(i => i.Parent != null) - .GroupBy(i => i.Parent) - .ToDictionary(g => g.Key, g => g.ToArray()); - - Dictionary nonFolderDescendantsCount = new(); - foreach (var item in solution.SolutionFolders) + for (int i = 0; i < solution.SolutionFolders.Count; i++) { - CountNonFolderDescendants(solution, item, solutionItemsGroupedByParent, nonFolderDescendantsCount); - } + var folder = solution.SolutionFolders[i]; + int nonFolderDescendants = 0; + Stack stack = new(); + stack.Push(folder); - var emptyFolders = nonFolderDescendantsCount.Where(i => i.Value == 0).Select(i => i.Key); - foreach (var folder in emptyFolders) - { - solution.RemoveFolder(folder); + while (stack.Count > 0) + { + var current = stack.Pop(); + + nonFolderDescendants += current.Files?.Count ?? 0; + foreach (var child in solution.SolutionItems) + { + if (child is { Parent: var parent } && parent == current) + { + if (child is SolutionFolderModel childFolder) + { + stack.Push(childFolder); + } + else + { + nonFolderDescendants++; + } + } + } + } + + if (nonFolderDescendants == 0) + { + solution.RemoveFolder(folder); + // After removal, adjust index and continue to avoid skipping folders after removal + i--; + } } await serializer.SaveAsync(solutionFileFullPath, solution, cancellationToken);