search v2
This commit is contained in:
10
src/SharpIDE.Application/Features/Search/SearchResult.cs
Normal file
10
src/SharpIDE.Application/Features/Search/SearchResult.cs
Normal file
@@ -0,0 +1,10 @@
|
|||||||
|
using SharpIDE.Application.Features.SolutionDiscovery;
|
||||||
|
|
||||||
|
namespace SharpIDE.Application.Features.Search;
|
||||||
|
|
||||||
|
public class SearchResult
|
||||||
|
{
|
||||||
|
public required SharpIdeFile File { get; set; }
|
||||||
|
public required int LineNumber { get; set; }
|
||||||
|
public required string LineText { get; set; }
|
||||||
|
}
|
||||||
@@ -1,4 +1,5 @@
|
|||||||
using System.Collections.Concurrent;
|
using System.Collections.Concurrent;
|
||||||
|
using System.Diagnostics;
|
||||||
using System.IO.MemoryMappedFiles;
|
using System.IO.MemoryMappedFiles;
|
||||||
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
|
|
||||||
@@ -6,24 +7,34 @@ namespace SharpIDE.Application.Features.Search;
|
|||||||
|
|
||||||
public static class SearchService
|
public static class SearchService
|
||||||
{
|
{
|
||||||
public static async Task FindInFiles(SharpIdeSolutionModel solutionModel, string searchTerm)
|
public static async Task<List<SearchResult>> FindInFiles(SharpIdeSolutionModel solutionModel, string searchTerm)
|
||||||
{
|
{
|
||||||
if (searchTerm.Length < 4)
|
if (searchTerm.Length < 4)
|
||||||
{
|
{
|
||||||
return;
|
return [];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var timer = Stopwatch.StartNew();
|
||||||
var files = solutionModel.AllFiles;
|
var files = solutionModel.AllFiles;
|
||||||
ConcurrentBag<string> results = [];
|
ConcurrentBag<SearchResult> results = [];
|
||||||
await Parallel.ForEachAsync(files, async (file, ct) =>
|
await Parallel.ForEachAsync(files, async (file, ct) =>
|
||||||
{
|
{
|
||||||
await foreach (var (index, line) in File.ReadLinesAsync(file.Path, ct).Index().WithCancellation(ct))
|
await foreach (var (index, line) in File.ReadLinesAsync(file.Path, ct).Index().WithCancellation(ct))
|
||||||
{
|
{
|
||||||
if (line.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))
|
if (line.Contains(searchTerm, StringComparison.OrdinalIgnoreCase))
|
||||||
{
|
{
|
||||||
results.Add($"{file.Path} (Line {index + 1}): {line.Trim()}");
|
results.Add(new SearchResult
|
||||||
|
{
|
||||||
|
File = file,
|
||||||
|
LineNumber = index + 1,
|
||||||
|
LineText = line.Trim()
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
timer.Stop();
|
||||||
|
Console.WriteLine($"Search completed in {timer.ElapsedMilliseconds} ms. Found {results.Count} results.");
|
||||||
|
return results.ToList();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
29
src/SharpIDE.Godot/Features/Search/SearchResultComponent.cs
Normal file
29
src/SharpIDE.Godot/Features/Search/SearchResultComponent.cs
Normal file
@@ -0,0 +1,29 @@
|
|||||||
|
using Godot;
|
||||||
|
using SharpIDE.Application.Features.Search;
|
||||||
|
|
||||||
|
namespace SharpIDE.Godot.Features.Search;
|
||||||
|
|
||||||
|
public partial class SearchResultComponent : MarginContainer
|
||||||
|
{
|
||||||
|
private Label _matchingLineLabel = null!;
|
||||||
|
private Label _fileNameLabel = null!;
|
||||||
|
private Label _lineNumberLabel = null!;
|
||||||
|
|
||||||
|
public SearchResult Result { get; set; } = null!;
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_matchingLineLabel = GetNode<Label>("%MatchingLineLabel");
|
||||||
|
_fileNameLabel = GetNode<Label>("%FileNameLabel");
|
||||||
|
_lineNumberLabel = GetNode<Label>("%LineNumberLabel");
|
||||||
|
SetValue(Result);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void SetValue(SearchResult result)
|
||||||
|
{
|
||||||
|
if (result is null) return;
|
||||||
|
_matchingLineLabel.Text = result.LineText;
|
||||||
|
_fileNameLabel.Text = result.File.Name;
|
||||||
|
_lineNumberLabel.Text = result.LineNumber.ToString();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
uid://6sdu34jtdrux
|
||||||
@@ -0,0 +1,30 @@
|
|||||||
|
[gd_scene load_steps=2 format=3 uid="uid://d358tex0duum8"]
|
||||||
|
|
||||||
|
[ext_resource type="Script" uid="uid://6sdu34jtdrux" path="res://Features/Search/SearchResultComponent.cs" id="1_6ov2c"]
|
||||||
|
|
||||||
|
[node name="SearchResultComponent" type="MarginContainer"]
|
||||||
|
anchors_preset = 10
|
||||||
|
anchor_right = 1.0
|
||||||
|
offset_bottom = 23.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
script = ExtResource("1_6ov2c")
|
||||||
|
|
||||||
|
[node name="HBoxContainer" type="HBoxContainer" parent="."]
|
||||||
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="MatchingLineLabel" type="Label" parent="HBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
text = "Line matching search result"
|
||||||
|
text_overrun_behavior = 3
|
||||||
|
|
||||||
|
[node name="FileNameLabel" type="Label" parent="HBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "FileName.cs"
|
||||||
|
|
||||||
|
[node name="LineNumberLabel" type="Label" parent="HBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "24"
|
||||||
@@ -1,8 +1,41 @@
|
|||||||
using Godot;
|
using Godot;
|
||||||
|
using SharpIDE.Application.Features.Search;
|
||||||
|
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
|
||||||
|
|
||||||
namespace SharpIDE.Godot.Features.Search;
|
namespace SharpIDE.Godot.Features.Search;
|
||||||
|
|
||||||
public partial class SearchWindow : PopupPanel
|
public partial class SearchWindow : PopupPanel
|
||||||
{
|
{
|
||||||
|
private LineEdit _lineEdit = null!;
|
||||||
|
private VBoxContainer _searchResultsContainer = null!;
|
||||||
|
public SharpIdeSolutionModel Solution { get; set; } = null!;
|
||||||
|
private readonly PackedScene _searchResultEntryScene = ResourceLoader.Load<PackedScene>("res://Features/Search/SearchResultComponent.tscn");
|
||||||
|
|
||||||
|
public override void _Ready()
|
||||||
|
{
|
||||||
|
_lineEdit = GetNode<LineEdit>("%SearchLineEdit");
|
||||||
|
_searchResultsContainer = GetNode<VBoxContainer>("%SearchResultsVBoxContainer");
|
||||||
|
_lineEdit.TextChanged += OnTextChanged;
|
||||||
|
}
|
||||||
|
|
||||||
|
private async void OnTextChanged(string newText)
|
||||||
|
{
|
||||||
|
GD.Print("Search text changed");
|
||||||
|
await Task.GodotRun(() => Search(newText));
|
||||||
|
}
|
||||||
|
|
||||||
|
private async Task Search(string text)
|
||||||
|
{
|
||||||
|
var result = await SearchService.FindInFiles(Solution, text);
|
||||||
|
await this.InvokeAsync(() =>
|
||||||
|
{
|
||||||
|
_searchResultsContainer.GetChildren().ToList().ForEach(s => s.QueueFree());
|
||||||
|
foreach (var searchResult in result)
|
||||||
|
{
|
||||||
|
var result = _searchResultEntryScene.Instantiate<SearchResultComponent>();
|
||||||
|
result.Result = searchResult;
|
||||||
|
_searchResultsContainer.AddChild(result);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,6 +1,7 @@
|
|||||||
[gd_scene load_steps=2 format=3 uid="uid://8lk0qj233a7p"]
|
[gd_scene load_steps=3 format=3 uid="uid://8lk0qj233a7p"]
|
||||||
|
|
||||||
[ext_resource type="Script" uid="uid://bah6tmifl41ce" path="res://Features/Search/SearchWindow.cs" id="1_ft33p"]
|
[ext_resource type="Script" uid="uid://bah6tmifl41ce" path="res://Features/Search/SearchWindow.cs" id="1_ft33p"]
|
||||||
|
[ext_resource type="PackedScene" uid="uid://d358tex0duum8" path="res://Features/Search/SearchResultComponent.tscn" id="2_cuaw5"]
|
||||||
|
|
||||||
[node name="SearchWindow" type="PopupPanel"]
|
[node name="SearchWindow" type="PopupPanel"]
|
||||||
oversampling_override = 1.0
|
oversampling_override = 1.0
|
||||||
@@ -40,6 +41,19 @@ layout_mode = 2
|
|||||||
theme_override_font_sizes/font_size = 14
|
theme_override_font_sizes/font_size = 14
|
||||||
text = " 30 matches in 5 files"
|
text = " 30 matches in 5 files"
|
||||||
|
|
||||||
[node name="LineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
|
[node name="SearchLineEdit" type="LineEdit" parent="MarginContainer/VBoxContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
text = "Test"
|
text = "Test"
|
||||||
|
|
||||||
|
[node name="ScrollContainer" type="ScrollContainer" parent="MarginContainer/VBoxContainer"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_vertical = 3
|
||||||
|
|
||||||
|
[node name="SearchResultsVBoxContainer" type="VBoxContainer" parent="MarginContainer/VBoxContainer/ScrollContainer"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[node name="SearchResultComponent" parent="MarginContainer/VBoxContainer/ScrollContainer/SearchResultsVBoxContainer" instance=ExtResource("2_cuaw5")]
|
||||||
|
layout_mode = 2
|
||||||
|
|||||||
@@ -80,6 +80,7 @@ public partial class IdeRoot : Control
|
|||||||
_solutionExplorerPanel.SolutionModel = solutionModel;
|
_solutionExplorerPanel.SolutionModel = solutionModel;
|
||||||
_codeEditorPanel.Solution = solutionModel;
|
_codeEditorPanel.Solution = solutionModel;
|
||||||
_bottomPanelManager.Solution = solutionModel;
|
_bottomPanelManager.Solution = solutionModel;
|
||||||
|
_searchWindow.Solution = solutionModel;
|
||||||
Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred();
|
Callable.From(_solutionExplorerPanel.RepopulateTree).CallDeferred();
|
||||||
RoslynAnalysis.StartSolutionAnalysis(solutionModel);
|
RoslynAnalysis.StartSolutionAnalysis(solutionModel);
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user