From fbe6b80cd9fa838b626cf3c9674ddcfc4bc02891 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Fri, 31 Oct 2025 18:57:37 +1000 Subject: [PATCH] move file on rename --- .../SharpIdeSolutionModificationService.cs | 37 +++++++++++++++++-- .../SolutionExplorer/SolutionExplorerPanel.cs | 24 ++++++++++++ 2 files changed, 57 insertions(+), 4 deletions(-) diff --git a/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs b/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs index 037327d..9a5f896 100644 --- a/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs +++ b/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs @@ -137,6 +137,16 @@ public class SharpIdeSolutionModificationService(FileChangedService fileChangedS { var sharpIdeFile = new SharpIdeFile(newFilePath, fileName, parentNode, []); + var correctInsertionPosition = GetInsertionPosition(parentNode, sharpIdeFile); + + parentNode.Files.Insert(correctInsertionPosition, sharpIdeFile); + SolutionModel.AllFiles.Add(sharpIdeFile); + await _fileChangedService.SharpIdeFileAdded(sharpIdeFile, contents); + return sharpIdeFile; + } + + private static int GetInsertionPosition(IFolderOrProject parentNode, SharpIdeFile sharpIdeFile) + { var correctInsertionPosition = parentNode.Files.list.BinarySearch(sharpIdeFile, SharpIdeFileComparer.Instance); if (correctInsertionPosition < 0) { @@ -147,10 +157,25 @@ public class SharpIdeSolutionModificationService(FileChangedService fileChangedS throw new InvalidOperationException("File already exists in the containing folder or project"); } - parentNode.Files.Insert(correctInsertionPosition, sharpIdeFile); - SolutionModel.AllFiles.Add(sharpIdeFile); - await _fileChangedService.SharpIdeFileAdded(sharpIdeFile, contents); - return sharpIdeFile; + return correctInsertionPosition; + } + + private static int GetMovePosition(IFolderOrProject parentNode, SharpIdeFile sharpIdeFile) + { + var correctInsertionPosition = parentNode.Files.list + .FindAll(x => x != sharpIdeFile) // TODO: Investigate allocations + .BinarySearch(sharpIdeFile, SharpIdeFileComparer.Instance); + + if (correctInsertionPosition < 0) + { + correctInsertionPosition = ~correctInsertionPosition; + } + else + { + throw new InvalidOperationException("File already exists in the containing folder or project"); + } + + return correctInsertionPosition; } public async Task RemoveFile(SharpIdeFile file) @@ -180,6 +205,10 @@ public class SharpIdeSolutionModificationService(FileChangedService fileChangedS var newFilePath = Path.Combine(Path.GetDirectoryName(oldPath)!, renamedFileName); fileToRename.Name = renamedFileName; fileToRename.Path = newFilePath; + var parentFolderOrProject = (IFolderOrProject)fileToRename.Parent; + var currentPosition = parentFolderOrProject.Files.IndexOf(fileToRename); + var insertionPosition = GetMovePosition(parentFolderOrProject, fileToRename); + parentFolderOrProject.Files.Move(currentPosition, insertionPosition); await _fileChangedService.SharpIdeFileRenamed(fileToRename, oldPath); return fileToRename; } diff --git a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs index 2b2b08b..fbc4fae 100644 --- a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs +++ b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs @@ -276,6 +276,7 @@ public partial class SolutionExplorerPanel : MarginContainer .SubscribeAwait(async (innerEvent, ct) => await (innerEvent.Action switch { NotifyCollectionChangedAction.Add => this.InvokeAsync(() => innerEvent.NewItem.View.Value = CreateFileTreeItem(_tree, folderItem, innerEvent.NewItem.Value, innerEvent.NewStartingIndex)), + NotifyCollectionChangedAction.Move => MoveTreeItem(_tree, innerEvent.NewItem.View, innerEvent.NewItem.Value, innerEvent.OldStartingIndex, innerEvent.NewStartingIndex), NotifyCollectionChangedAction.Remove => FreeTreeItem(innerEvent.OldItem.View.Value), _ => Task.CompletedTask })).AddTo(this); @@ -308,6 +309,29 @@ public partial class SolutionExplorerPanel : MarginContainer return fileItem; } + + private async Task MoveTreeItem(Tree tree, TreeItemContainer treeItemContainer, SharpIdeFile sharpIdeFile, int oldStartingIndex, int newStartingIndex) + { + if (oldStartingIndex == newStartingIndex) throw new InvalidOperationException("Old and new starting indexes are the same"); + var treeItem = treeItemContainer.Value!; + var sharpIdeParent = sharpIdeFile.Parent as IFolderOrProject; + Guard.Against.Null(sharpIdeParent, nameof(sharpIdeParent)); + var folderCount = sharpIdeParent.Folders.Count; + newStartingIndex += folderCount; + var treeParent = treeItem.GetParent()!; + await this.InvokeAsync(() => + { + // The API for moving TreeItems is painful - we can only move an Item before or after another item + treeParent.RemoveChild(treeItem); + var newItem = tree.CreateItem(treeParent, newStartingIndex); + newItem.SetText(0, treeItem.GetText(0)); + newItem.SetIcon(0, treeItem.GetIcon(0)); + newItem.SetMetadata(0, treeItem.GetMetadata(0)); + newItem.SetCustomColor(0, treeItem.GetCustomColor(0)); + treeItemContainer.Value = newItem; + treeItem.Free(); + }); + } private async Task FreeTreeItem(TreeItem? item) {