sort files in sln explorer

This commit is contained in:
Matt Parker
2025-10-31 18:21:40 +10:00
parent 150f9613dc
commit 8a693ce19d
5 changed files with 44 additions and 21 deletions

View File

@@ -108,7 +108,17 @@ public class IdeFileExternalChangeHandler
}
sharpIdeFile = new SharpIdeFile(filePath, Path.GetFileName(filePath), containingFolderOrProject, []);
containingFolderOrProject.Files.Add(sharpIdeFile);
var correctInsertionPosition = containingFolderOrProject.Files.list.BinarySearch(sharpIdeFile, SharpIdeFileComparer.Instance);
if (correctInsertionPosition < 0)
{
correctInsertionPosition = ~correctInsertionPosition;
}
else
{
throw new InvalidOperationException("File already exists in the containing folder or project");
}
containingFolderOrProject.Files.Insert(correctInsertionPosition, sharpIdeFile);
SolutionModel.AllFiles.Add(sharpIdeFile);
await _fileChangedService.SharpIdeFileAdded(sharpIdeFile, await File.ReadAllTextAsync(filePath));

View File

@@ -11,8 +11,8 @@ public class SharpIdeFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildShar
public required string Path { get; set; }
public string ChildNodeBasePath => Path;
public required string Name { get; set; }
public ObservableSortedSet<SharpIdeFile> Files { get; init; }
public ObservableSortedSet<SharpIdeFolder> Folders { get; init; }
public ObservableList<SharpIdeFile> Files { get; init; }
public ObservableList<SharpIdeFolder> Folders { get; init; }
public bool Expanded { get; set; }
[SetsRequiredMembers]
@@ -21,8 +21,8 @@ public class SharpIdeFolder : ISharpIdeNode, IExpandableSharpIdeNode, IChildShar
Parent = parent;
Path = folderInfo.FullName;
Name = folderInfo.Name;
Files = new ObservableSortedSet<SharpIdeFile>(folderInfo.GetFiles(this, allFiles), SharpIdeFileComparer.Instance);
Folders = new ObservableSortedSet<SharpIdeFolder>(this.GetSubFolders(this, allFiles, allFolders), SharpIdeFolderComparer.Instance);
Files = new ObservableList<SharpIdeFile>(folderInfo.GetFiles(this, allFiles));
Folders = new ObservableList<SharpIdeFolder>(this.GetSubFolders(this, allFiles, allFolders));
}
public SharpIdeFolder()

View File

@@ -52,7 +52,9 @@ public static class TreeMapperV2
allFolders.Add(subFolder);
});
return subFolders.ToList();
var sharpIdeFolders = subFolders.ToList();
sharpIdeFolders.Sort(SharpIdeFolderComparer.Instance);
return sharpIdeFolders;
}
public static List<SharpIdeFile> GetFiles(string csprojectPath, SharpIdeProjectModel sharpIdeProjectModel, ConcurrentBag<SharpIdeFile> allFiles)
@@ -73,11 +75,13 @@ public static class TreeMapperV2
return [];
}
return fileInfos.Select(f => new SharpIdeFile(f.FullName, f.Name, parent, allFiles)
var sharpIdeFiles = fileInfos.Select(f => new SharpIdeFile(f.FullName, f.Name, parent, allFiles)
{
Path = f.FullName,
Name = f.Name,
Parent = parent
}).ToList();
sharpIdeFiles.Sort(SharpIdeFileComparer.Instance);
return sharpIdeFiles;
}
}

View File

@@ -17,8 +17,8 @@ public interface IExpandableSharpIdeNode
public interface IFolderOrProject : IExpandableSharpIdeNode, IChildSharpIdeNode
{
public ObservableSortedSet<SharpIdeFolder> Folders { get; init; }
public ObservableSortedSet<SharpIdeFile> Files { get; init; }
public ObservableList<SharpIdeFolder> Folders { get; init; }
public ObservableList<SharpIdeFile> Files { get; init; }
public string Name { get; set; }
public string ChildNodeBasePath { get; }
}
@@ -50,9 +50,9 @@ public class SharpIdeSolutionModel : ISharpIdeNode, IExpandableSharpIdeNode
public required string DirectoryPath { get; set; }
public required ObservableHashSet<SharpIdeProjectModel> Projects { get; set; }
public required ObservableHashSet<SharpIdeSolutionFolder> SlnFolders { get; set; }
public required HashSet<SharpIdeProjectModel> AllProjects { get; set; }
public required HashSet<SharpIdeFile> AllFiles { get; set; }
public required HashSet<SharpIdeFolder> AllFolders { get; set; }
public required HashSet<SharpIdeProjectModel> AllProjects { get; set; } // TODO: this isn't thread safe
public required HashSet<SharpIdeFile> AllFiles { get; set; } // TODO: this isn't thread safe
public required HashSet<SharpIdeFolder> AllFolders { get; set; } // TODO: this isn't thread safe
public bool Expanded { get; set; }
[SetsRequiredMembers]
@@ -96,8 +96,8 @@ public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChi
public required string Name { get; set; }
public required string FilePath { get; set; }
public string ChildNodeBasePath => Path.GetDirectoryName(FilePath)!;
public required ObservableSortedSet<SharpIdeFolder> Folders { get; init; }
public required ObservableSortedSet<SharpIdeFile> Files { get; init; }
public required ObservableList<SharpIdeFolder> Folders { get; init; }
public required ObservableList<SharpIdeFile> Files { get; init; }
public bool Expanded { get; set; }
public required IExpandableSharpIdeNode Parent { get; set; }
public bool Running { get; set; }
@@ -110,8 +110,8 @@ public class SharpIdeProjectModel : ISharpIdeNode, IExpandableSharpIdeNode, IChi
Parent = parent;
Name = projectModel.Model.ActualDisplayName;
FilePath = projectModel.FullFilePath;
Files = new ObservableSortedSet<SharpIdeFile>(TreeMapperV2.GetFiles(projectModel.FullFilePath, this, allFiles), SharpIdeFileComparer.Instance);
Folders = new ObservableSortedSet<SharpIdeFolder>(TreeMapperV2.GetSubFolders(projectModel.FullFilePath, this, allFiles, allFolders), SharpIdeFolderComparer.Instance);
Files = new ObservableList<SharpIdeFile>(TreeMapperV2.GetFiles(projectModel.FullFilePath, this, allFiles));
Folders = new ObservableList<SharpIdeFolder>(TreeMapperV2.GetSubFolders(projectModel.FullFilePath, this, allFiles, allFolders));
MsBuildEvaluationProjectTask = ProjectEvaluation.GetProject(projectModel.FullFilePath);
allProjects.Add(this);
}

View File

@@ -203,7 +203,7 @@ public partial class SolutionExplorerPanel : MarginContainer
filesView.ObserveChanged()
.SubscribeAwait(async (innerEvent, ct) => await (innerEvent.Action switch
{
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, folderItem, innerEvent.NewItem.Value)),
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, folderItem, innerEvent.NewItem.Value, innerEvent.NewStartingIndex)),
NotifyCollectionChangedAction.Remove => FreeTreeItem(innerEvent.OldItem.View.Value),
_ => Task.CompletedTask
})).AddTo(this);
@@ -236,7 +236,7 @@ public partial class SolutionExplorerPanel : MarginContainer
filesView.ObserveChanged()
.SubscribeAwait(async (innerEvent, ct) => await (innerEvent.Action switch
{
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, projectItem, innerEvent.NewItem.Value)),
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, projectItem, innerEvent.NewItem.Value, innerEvent.NewStartingIndex)),
NotifyCollectionChangedAction.Remove => FreeTreeItem(innerEvent.OldItem.View.Value),
_ => Task.CompletedTask
})).AddTo(this);
@@ -275,7 +275,7 @@ public partial class SolutionExplorerPanel : MarginContainer
filesView.ObserveChanged()
.SubscribeAwait(async (innerEvent, ct) => await (innerEvent.Action switch
{
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, folderItem, innerEvent.NewItem.Value)),
NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, folderItem, innerEvent.NewItem.Value, innerEvent.NewStartingIndex)),
NotifyCollectionChangedAction.Remove => FreeTreeItem(innerEvent.OldItem.View.Value),
_ => Task.CompletedTask
})).AddTo(this);
@@ -283,9 +283,18 @@ public partial class SolutionExplorerPanel : MarginContainer
}
[RequiresGodotUiThread]
private TreeItem CreateFileTreeItem(Tree tree, TreeItem parent, SharpIdeFile sharpIdeFile)
private TreeItem CreateFileTreeItem(Tree tree, TreeItem parent, SharpIdeFile sharpIdeFile, int newStartingIndex = -1)
{
var fileItem = tree.CreateItem(parent);
// We need to offset the starting index by the number of non-file items (folders/projects) in the parent
// because the newStartingIndex is calculated based on all children, but we are only inserting files here
if (newStartingIndex >= 0)
{
var sharpIdeParent = sharpIdeFile.Parent as IFolderOrProject;
Guard.Against.Null(sharpIdeParent, nameof(sharpIdeParent));
var folderCount = sharpIdeParent.Folders.Count;
newStartingIndex += folderCount;
}
var fileItem = tree.CreateItem(parent, newStartingIndex);
fileItem.SetText(0, sharpIdeFile.Name);
fileItem.SetIcon(0, CsharpFileIcon);
fileItem.SetCustomColor(0, GetColorForGitStatus(sharpIdeFile.GitStatus));