Skip to content
Closed
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 33 additions & 36 deletions src/Cli/dotnet/commands/dotnet-sln/remove/Program.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,28 +19,6 @@ internal class RemoveProjectFromSolutionCommand : CommandBase
private readonly string _fileOrDirectory;
private readonly IReadOnlyCollection<string> _projects;

private int CountNonFolderDescendants(
SolutionModel solution,
SolutionFolderModel item,
Dictionary<SolutionFolderModel, SolutionItemModel[]> solutionItemsGroupedByParent,
Dictionary<SolutionFolderModel, int> 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<SolutionItemModel>();
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);
Expand Down Expand Up @@ -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<SlnV12SerializerSettings> v12Serializer)
{
solution.SerializerExtension = v12Serializer.CreateModelExtension(new()
Expand All @@ -115,21 +93,40 @@ private async Task RemoveProjectsAsync(string solutionFileFullPath, IEnumerable<
}
}

Dictionary<SolutionFolderModel, SolutionItemModel[]> solutionItemsGroupedByParent = solution.SolutionItems
.Where(i => i.Parent != null)
.GroupBy(i => i.Parent)
.ToDictionary(g => g.Key, g => g.ToArray());

Dictionary<SolutionFolderModel, int> 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<SolutionFolderModel> 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);
Expand Down