From 5e1f1dfdd660de53c0fe17263cb34b12475a9d70 Mon Sep 17 00:00:00 2001 From: Matt Parker <61717342+MattParkerDev@users.noreply.github.com> Date: Sun, 19 Oct 2025 20:20:27 +1000 Subject: [PATCH] add add file v1 --- .../Features/Analysis/RoslynAnalysis.cs | 21 +++++++++++++++++++ .../FileWatching/FileChangedService.cs | 20 ++++++++++++++++++ .../IdeFileExternalChangeHandler.cs | 18 +++++++++++++++- 3 files changed, 58 insertions(+), 1 deletion(-) diff --git a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs index fd2bdbb..5a5095f 100644 --- a/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs +++ b/src/SharpIDE.Application/Features/Analysis/RoslynAnalysis.cs @@ -739,4 +739,25 @@ public class RoslynAnalysis _workspace.TryApplyChanges(newSolution); } + + public async Task AddDocument(SharpIdeFile fileModel, string content) + { + using var _ = SharpIdeOtel.Source.StartActivity($"{nameof(RoslynAnalysis)}.{nameof(AddDocument)}"); + await _solutionLoadedTcs.Task; + Guard.Against.Null(fileModel, nameof(fileModel)); + Guard.Against.NullOrEmpty(content, nameof(content)); + + var project = _workspace!.CurrentSolution.Projects.Single(s => s.FilePath == ((IChildSharpIdeNode)fileModel).GetNearestProjectNode()!.FilePath); + + var sourceText = SourceText.From(content, Encoding.UTF8); + + var newSolution = fileModel switch + { + { IsRazorFile: true } => _workspace.CurrentSolution.AddAdditionalDocument(DocumentId.CreateNewId(project.Id), fileModel.Name, sourceText, filePath: fileModel.Path), + { IsCsharpFile: true } => _workspace.CurrentSolution.AddDocument(DocumentId.CreateNewId(project.Id), fileModel.Name, sourceText, filePath: fileModel.Path), + _ => throw new InvalidOperationException("AddDocument failed: File is not in workspace") + }; + + _workspace.TryApplyChanges(newSolution); + } } diff --git a/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs b/src/SharpIDE.Application/Features/FileWatching/FileChangedService.cs index b329f86..ad4a44c 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 SharpIdeFileAdded(SharpIdeFile file, string content) + { + if (file.IsRoslynWorkspaceFile) + { + await HandleWorkspaceFileAdded(file, content); + } + // TODO: handle csproj added + } + // All file changes should go via this service public async Task SharpIdeFileChanged(SharpIdeFile file, string newContents, FileChangeType changeType) { @@ -86,4 +95,15 @@ public class FileChangedService(RoslynAnalysis roslynAnalysis, IdeOpenTabsFileMa GlobalEvents.Instance.SolutionAltered.InvokeParallelFireAndForget(); await _roslynAnalysis.UpdateSolutionDiagnostics(newCts.Token); } + + private async Task HandleWorkspaceFileAdded(SharpIdeFile file, string contents) + { + var newCts = new CancellationTokenSource(); + var oldCts = Interlocked.Exchange(ref _updateSolutionDiagnosticsCts, newCts); + await oldCts.CancelAsync(); + oldCts.Dispose(); + await _roslynAnalysis.AddDocument(file, contents); + 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 da1ab4d..d48a09e 100644 --- a/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs +++ b/src/SharpIDE.Application/Features/FileWatching/IdeFileExternalChangeHandler.cs @@ -1,4 +1,6 @@ -using SharpIDE.Application.Features.Events; +using Ardalis.GuardClauses; +using SharpIDE.Application.Features.Events; +using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; namespace SharpIDE.Application.Features.FileWatching; @@ -11,6 +13,20 @@ public class IdeFileExternalChangeHandler { _fileChangedService = fileChangedService; GlobalEvents.Instance.FileSystemWatcherInternal.FileChanged.Subscribe(OnFileChanged); + GlobalEvents.Instance.FileSystemWatcherInternal.FileCreated.Subscribe(OnFileCreated); + } + + private async Task OnFileCreated(string filePath) + { + // Create a new sharpIdeFile, update SolutionModel + var sharpIdeFile = SolutionModel.AllFiles.SingleOrDefault(f => f.Path == filePath); + if (sharpIdeFile == null) + { + // If sharpIdeFile is null, it means the file was created externally, and we need to create it and add it to the solution model + // sharpIdeFile = TODO; + } + Guard.Against.Null(sharpIdeFile, nameof(sharpIdeFile)); + await _fileChangedService.SharpIdeFileAdded(sharpIdeFile, await File.ReadAllTextAsync(filePath)); } private async Task OnFileChanged(string filePath)