diff --git a/src/SharpIDE.Application/Features/Search/SearchService.cs b/src/SharpIDE.Application/Features/Search/SearchService.cs index 179ca10..f891475 100644 --- a/src/SharpIDE.Application/Features/Search/SearchService.cs +++ b/src/SharpIDE.Application/Features/Search/SearchService.cs @@ -7,7 +7,7 @@ namespace SharpIDE.Application.Features.Search; public static class SearchService { - public static async Task> FindInFiles(SharpIdeSolutionModel solutionModel, string searchTerm) + public static async Task> FindInFiles(SharpIdeSolutionModel solutionModel, string searchTerm, CancellationToken cancellationToken) { if (searchTerm.Length < 4) { @@ -17,10 +17,12 @@ public static class SearchService var timer = Stopwatch.StartNew(); var files = solutionModel.AllFiles; ConcurrentBag results = []; - await Parallel.ForEachAsync(files, async (file, ct) => + await Parallel.ForEachAsync(files, cancellationToken, async (file, ct) => { + if (cancellationToken.IsCancellationRequested) return; await foreach (var (index, line) in File.ReadLinesAsync(file.Path, ct).Index().WithCancellation(ct)) { + if (cancellationToken.IsCancellationRequested) return; if (line.Contains(searchTerm, StringComparison.OrdinalIgnoreCase)) { results.Add(new SearchResult @@ -32,9 +34,9 @@ public static class SearchService } } } - ); + ).ConfigureAwait(ConfigureAwaitOptions.SuppressThrowing); timer.Stop(); - Console.WriteLine($"Search completed in {timer.ElapsedMilliseconds} ms. Found {results.Count} results."); + Console.WriteLine($"Search completed in {timer.ElapsedMilliseconds} ms. Found {results.Count} results. {(cancellationToken.IsCancellationRequested ? "(Cancelled)" : "")}"); return results.ToList(); } } diff --git a/src/SharpIDE.Godot/Features/Search/SearchWindow.cs b/src/SharpIDE.Godot/Features/Search/SearchWindow.cs index f5731bc..c6cc176 100644 --- a/src/SharpIDE.Godot/Features/Search/SearchWindow.cs +++ b/src/SharpIDE.Godot/Features/Search/SearchWindow.cs @@ -10,6 +10,8 @@ public partial class SearchWindow : PopupPanel private VBoxContainer _searchResultsContainer = null!; public SharpIdeSolutionModel Solution { get; set; } = null!; private readonly PackedScene _searchResultEntryScene = ResourceLoader.Load("res://Features/Search/SearchResultComponent.tscn"); + + private CancellationTokenSource _cancellationTokenSource = new(); public override void _Ready() { @@ -20,22 +22,25 @@ public partial class SearchWindow : PopupPanel private async void OnTextChanged(string newText) { - GD.Print("Search text changed"); - await Task.GodotRun(() => Search(newText)); + await _cancellationTokenSource.CancelAsync(); + // TODO: Investigate allocations + _cancellationTokenSource = new CancellationTokenSource(); + var token = _cancellationTokenSource.Token; + await Task.GodotRun(() => Search(newText, token)); } - private async Task Search(string text) + private async Task Search(string text, CancellationToken cancellationToken) { - var result = await SearchService.FindInFiles(Solution, text); + var result = await SearchService.FindInFiles(Solution, text, cancellationToken); await this.InvokeAsync(() => { _searchResultsContainer.GetChildren().ToList().ForEach(s => s.QueueFree()); foreach (var searchResult in result) { - var result = _searchResultEntryScene.Instantiate(); - result.Result = searchResult; - _searchResultsContainer.AddChild(result); + var resultNode = _searchResultEntryScene.Instantiate(); + resultNode.Result = searchResult; + _searchResultsContainer.AddChild(resultNode); } }); } -} \ No newline at end of file +}