Skip to content

remove recursion from sln-remove #1

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 1 commit into from
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