diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index a737a8b..7fd381e 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -803,4 +803,11 @@ public class RoslynAnalysis var updatedSolution = _workspace.CurrentSolution.WithDocumentFilePath(document, sharpIdeFile.Path); _workspace.TryApplyChanges(updatedSolution); } + + public async Task RenameDocument(SharpIdeFile sharpIdeFile, string oldFilePath) + { + var documentId = _workspace!.CurrentSolution.GetDocumentIdsWithFilePath(oldFilePath).Single(); + var updatedSolution = _workspace.CurrentSolution.WithDocumentName(documentId, sharpIdeFile.Name); + _workspace.TryApplyChanges(updatedSolution); + } } diff --git a/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs b/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs index e7fd41f..417c8ad 100644 --- a/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs +++ b/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs @@ -23,6 +23,15 @@ public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileMa public SharpIdeSolutionModel SolutionModel { get; set; } = null!; + public async Task SharpIdeFileRenamed(SharpIdeFile file, string oldFilePath) + { + if (file.IsRoslynWorkspaceFile) + { + await HandleWorkspaceFileRenamed(file, oldFilePath); + } + // TODO: handle csproj moved + } + public async Task SharpIdeFileMoved(SharpIdeFile file, string oldFilePath) { if (file.IsRoslynWorkspaceFile) @@ -145,4 +154,15 @@ public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileMa GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); await _roslynAnalysis.UpdateSolutionDiagnostics(newCts.Token); } + + private async Task HandleWorkspaceFileRenamed(SharpIdeFile file, string oldFilePath) + { + var newCts = new CancellationTokenSource(); + var oldCts = Interlocked.Exchange(ref _updateSolutionDiagnosticsCts, newCts); + await oldCts.CancelAsync(); + oldCts.Dispose(); + await _roslynAnalysis.MoveDocument(file, oldFilePath); + GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); + await _roslynAnalysis.UpdateSolutionDiagnostics(newCts.Token); + } } diff --git a/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs b/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs index d467c14..488289f 100644 --- a/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs +++ b/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs @@ -16,11 +16,27 @@ public class IdeFileExternalChangeHandler _sharpIdeSolutionModificationService = sharpIdeSolutionModificationService; GlobalEvents.Instance.FileSystemWatcherInternal.FileChanged.Subscribe(OnFileChanged); GlobalEvents.Instance.FileSystemWatcherInternal.FileCreated.Subscribe(OnFileCreated); + GlobalEvents.Instance.FileSystemWatcherInternal.FileDeleted.Subscribe(OnFileDeleted); + GlobalEvents.Instance.FileSystemWatcherInternal.FileRenamed.Subscribe(OnFileRenamed); GlobalEvents.Instance.FileSystemWatcherInternal.DirectoryCreated.Subscribe(OnFolderCreated); GlobalEvents.Instance.FileSystemWatcherInternal.DirectoryDeleted.Subscribe(OnFolderDeleted); GlobalEvents.Instance.FileSystemWatcherInternal.DirectoryRenamed.Subscribe(OnFolderRenamed); } + private async Task OnFileRenamed(string oldFilePath, string newFilePath) + { + var sharpIdeFile = SolutionModel.AllFiles.SingleOrDefault(f => f.Path == oldFilePath); + if (sharpIdeFile is null) return; + await _sharpIdeSolutionModificationService.RenameFile(sharpIdeFile, Path.GetFileName(newFilePath)); + } + + private async Task OnFileDeleted(string filePath) + { + var sharpIdeFile = SolutionModel.AllFiles.SingleOrDefault(f => f.Path == filePath); + if (sharpIdeFile is null) return; + await _sharpIdeSolutionModificationService.RemoveFile(sharpIdeFile); + } + // TODO: Test - this most likely only will ever be called on linux - windows and macos(?) does delete + create on rename of folders private async Task OnFolderRenamed(string oldFolderPath, string newFolderPath) { diff --git a/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs b/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs index 9f0b34e..ba6a484 100644 --- a/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs +++ b/src/SharpIDE.Application/Features/FileWatching/SharpIdeSolutionModificationService.cs @@ -151,4 +151,14 @@ public class SharpIdeSolutionModificationService(FileChangedService fileChangedS await _fileChangedService.SharpIdeFileMoved(fileToMove, oldPath); return fileToMove; } + + public async Task RenameFile(SharpIdeFile fileToRename, string renamedFileName) + { + var oldPath = fileToRename.Path; + var newFilePath = Path.Combine(Path.GetDirectoryName(oldPath)!, renamedFileName); + fileToRename.Name = renamedFileName; + fileToRename.Path = newFilePath; + 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 83ee6e2..5b8902f 100644 --- a/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs +++ b/src/SharpIDE.Godot/Features/SolutionExplorer/SolutionExplorerPanel.cs @@ -282,6 +282,13 @@ public partial class SolutionExplorerPanel : MarginContainer fileItem.SetText(0, sharpIdeFile.Name); fileItem.SetIcon(0, CsharpFileIcon); fileItem.SetMetadata(0, new RefCountedContainer(sharpIdeFile)); + + Observable.EveryValueChanged(sharpIdeFile, folder => folder.Name) + .Skip(1).SubscribeAwait(async (s, ct) => + { + await this.InvokeAsync(() => fileItem.SetText(0, s)); + }).AddTo(this); + return fileItem; }