search set line position of selected file

This commit is contained in:
Matt Parker
2025-09-25 01:01:45 +10:00
parent f8cd8d18d5
commit a5e6ece9f3
10 changed files with 49 additions and 16 deletions

View File

@@ -0,0 +1,7 @@
namespace SharpIDE.Application.Features.Analysis;
public struct SharpIdeFileLinePosition
{
public required int Line { get; set; }
public required int Column { get; set; }
}

View File

@@ -31,6 +31,7 @@ public static class AsyncEventExtensions
{ {
public static void InvokeParallelFireAndForget(this MulticastDelegate @event) => FireAndForget(() => @event.InvokeParallelAsync()); public static void InvokeParallelFireAndForget(this MulticastDelegate @event) => FireAndForget(() => @event.InvokeParallelAsync());
public static void InvokeParallelFireAndForget<T>(this MulticastDelegate @event, T arg) => FireAndForget(() => @event.InvokeParallelAsync(arg)); public static void InvokeParallelFireAndForget<T>(this MulticastDelegate @event, T arg) => FireAndForget(() => @event.InvokeParallelAsync(arg));
public static void InvokeParallelFireAndForget<T, U>(this MulticastDelegate @event, T arg, U arg2) => FireAndForget(() => @event.InvokeParallelAsync(arg, arg2));
private static async void FireAndForget(Func<Task> action) private static async void FireAndForget(Func<Task> action)
{ {
@@ -53,6 +54,10 @@ public static class AsyncEventExtensions
{ {
return InvokeDelegatesAsync(@event.GetInvocationList(), del => ((Func<T, Task>)del)(arg)); return InvokeDelegatesAsync(@event.GetInvocationList(), del => ((Func<T, Task>)del)(arg));
} }
public static Task InvokeParallelAsync<T, U>(this MulticastDelegate @event, T arg, U arg2)
{
return InvokeDelegatesAsync(@event.GetInvocationList(), del => ((Func<T, U, Task>)del)(arg, arg2));
}
private static async Task InvokeDelegatesAsync(IEnumerable<Delegate> invocationList, Func<Delegate, Task> delegateExecutorDelegate) private static async Task InvokeDelegatesAsync(IEnumerable<Delegate> invocationList, Func<Delegate, Task> delegateExecutorDelegate)
{ {

View File

@@ -5,6 +5,7 @@ namespace SharpIDE.Application.Features.Search;
public class SearchResult public class SearchResult
{ {
public required SharpIdeFile File { get; set; } public required SharpIdeFile File { get; set; }
public required int LineNumber { get; set; } public required int Line { get; set; }
public required int StartColumn { get; set; }
public required string LineText { get; set; } public required string LineText { get; set; }
} }

View File

@@ -28,7 +28,8 @@ public static class SearchService
results.Add(new SearchResult results.Add(new SearchResult
{ {
File = file, File = file,
LineNumber = index + 1, Line = index + 1,
StartColumn = line.IndexOf(searchTerm, StringComparison.OrdinalIgnoreCase) + 1,
LineText = line.Trim() LineText = line.Trim()
}); });
} }

View File

@@ -1,5 +1,6 @@
using Ardalis.GuardClauses; using Ardalis.GuardClauses;
using Godot; using Godot;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Debugging; using SharpIDE.Application.Features.Debugging;
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
@@ -48,15 +49,18 @@ public partial class CodeEditorPanel : MarginContainer
tab.QueueFree(); tab.QueueFree();
} }
public async Task SetSharpIdeFile(SharpIdeFile file) public async Task SetSharpIdeFile(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
{ {
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
var existingTab = await this.InvokeAsync(() => _tabContainer.GetChildren().OfType<SharpIdeCodeEdit>().FirstOrDefault(t => t.SharpIdeFile == file)); var existingTab = await this.InvokeAsync(() => _tabContainer.GetChildren().OfType<SharpIdeCodeEdit>().FirstOrDefault(t => t.SharpIdeFile == file));
if (existingTab is not null) if (existingTab is not null)
{ {
var existingTabIndex = existingTab.GetIndex(); var existingTabIndex = existingTab.GetIndex();
if (existingTabIndex == _tabContainer.CurrentTab) return; await this.InvokeAsync(() =>
await this.InvokeAsync(() => _tabContainer.CurrentTab = existingTabIndex); {
_tabContainer.CurrentTab = existingTabIndex;
if (fileLinePosition is not null) existingTab.SetFileLinePosition(fileLinePosition.Value);
});
return; return;
} }
var newTab = _sharpIdeCodeEditScene.Instantiate<SharpIdeCodeEdit>(); var newTab = _sharpIdeCodeEditScene.Instantiate<SharpIdeCodeEdit>();
@@ -71,6 +75,7 @@ public partial class CodeEditorPanel : MarginContainer
_tabContainer.CurrentTab = newTabIndex; _tabContainer.CurrentTab = newTabIndex;
}); });
await newTab.SetSharpIdeFile(file); await newTab.SetSharpIdeFile(file);
if (fileLinePosition is not null) await this.InvokeAsync(() => newTab.SetFileLinePosition(fileLinePosition.Value));
} }
private async Task OnDebuggerExecutionStopped(ExecutionStopInfo executionStopInfo) private async Task OnDebuggerExecutionStopped(ExecutionStopInfo executionStopInfo)

View File

@@ -141,6 +141,16 @@ public partial class SharpIdeCodeEdit : CodeEdit
}); });
} }
public void SetFileLinePosition(SharpIdeFileLinePosition fileLinePosition)
{
var line = fileLinePosition.Line - 1;
var column = fileLinePosition.Column - 1;
SetCaretLine(line);
SetCaretColumn(column);
CenterViewportToCaret();
GrabFocus();
}
// TODO: Ensure not running on UI thread // TODO: Ensure not running on UI thread
public async Task SetSharpIdeFile(SharpIdeFile file) public async Task SetSharpIdeFile(SharpIdeFile file)
{ {

View File

@@ -1,4 +1,5 @@
using Godot; using Godot;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Search; using SharpIDE.Application.Features.Search;
namespace SharpIDE.Godot.Features.Search; namespace SharpIDE.Godot.Features.Search;
@@ -25,7 +26,8 @@ public partial class SearchResultComponent : MarginContainer
private void OnButtonPressed() private void OnButtonPressed()
{ {
GodotGlobalEvents.InvokeFileExternallySelected(Result.File); var fileLinePosition = new SharpIdeFileLinePosition { Line = Result.Line, Column = Result.StartColumn };
GodotGlobalEvents.InvokeFileExternallySelected(Result.File, fileLinePosition);
ParentSearchWindow.Hide(); ParentSearchWindow.Hide();
} }
@@ -34,6 +36,6 @@ public partial class SearchResultComponent : MarginContainer
if (result is null) return; if (result is null) return;
_matchingLineLabel.Text = result.LineText; _matchingLineLabel.Text = result.LineText;
_fileNameLabel.Text = result.File.Name; _fileNameLabel.Text = result.File.Name;
_lineNumberLabel.Text = result.LineNumber.ToString(); _lineNumberLabel.Text = result.Line.ToString();
} }
} }

View File

@@ -1,5 +1,6 @@
using Ardalis.GuardClauses; using Ardalis.GuardClauses;
using Godot; using Godot;
using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence; using SharpIDE.Application.Features.SolutionDiscovery.VsPersistence;
@@ -38,10 +39,10 @@ public partial class SolutionExplorerPanel : MarginContainer
GodotGlobalEvents.InvokeFileSelected(sharpIdeFile); GodotGlobalEvents.InvokeFileSelected(sharpIdeFile);
} }
private async Task OnFileExternallySelected(SharpIdeFile file) private async Task OnFileExternallySelected(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
{ {
await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding); await Task.CompletedTask.ConfigureAwait(ConfigureAwaitOptions.ForceYielding);
var task = GodotGlobalEvents.InvokeFileSelectedAndWait(file); var task = GodotGlobalEvents.InvokeFileSelectedAndWait(file, fileLinePosition);
var item = FindItemRecursive(_tree.GetRoot(), file); var item = FindItemRecursive(_tree.GetRoot(), file);
if (item is not null) if (item is not null)
{ {

View File

@@ -1,4 +1,5 @@
using SharpIDE.Application.Features.Events; using SharpIDE.Application.Features.Analysis;
using SharpIDE.Application.Features.Events;
using SharpIDE.Application.Features.SolutionDiscovery; using SharpIDE.Application.Features.SolutionDiscovery;
namespace SharpIDE.Godot; namespace SharpIDE.Godot;
@@ -14,11 +15,11 @@ public static class GodotGlobalEvents
public static event Func<bool, Task> BottomPanelVisibilityChangeRequested = _ => Task.CompletedTask; public static event Func<bool, Task> BottomPanelVisibilityChangeRequested = _ => Task.CompletedTask;
public static void InvokeBottomPanelVisibilityChangeRequested(bool show) => BottomPanelVisibilityChangeRequested.InvokeParallelFireAndForget(show); public static void InvokeBottomPanelVisibilityChangeRequested(bool show) => BottomPanelVisibilityChangeRequested.InvokeParallelFireAndForget(show);
public static event Func<SharpIdeFile, Task> FileSelected = _ => Task.CompletedTask; public static event Func<SharpIdeFile, SharpIdeFileLinePosition?, Task> FileSelected = (_, _) => Task.CompletedTask;
public static void InvokeFileSelected(SharpIdeFile file) => FileSelected.InvokeParallelFireAndForget(file); public static void InvokeFileSelected(SharpIdeFile file) => FileSelected.InvokeParallelFireAndForget(file);
public static async Task InvokeFileSelectedAndWait(SharpIdeFile file) => await FileSelected.InvokeParallelAsync(file); public static async Task InvokeFileSelectedAndWait(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition) => await FileSelected.InvokeParallelAsync(file, fileLinePosition);
public static event Func<SharpIdeFile, Task> FileExternallySelected = _ => Task.CompletedTask; public static event Func<SharpIdeFile, SharpIdeFileLinePosition?, Task> FileExternallySelected = (_, _) => Task.CompletedTask;
public static void InvokeFileExternallySelected(SharpIdeFile file) => FileExternallySelected.InvokeParallelFireAndForget(file); public static void InvokeFileExternallySelected(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition = null) => FileExternallySelected.InvokeParallelFireAndForget(file, fileLinePosition);
public static async Task InvokeFileExternallySelectedAndWait(SharpIdeFile file) => await FileExternallySelected.InvokeParallelAsync(file); public static async Task InvokeFileExternallySelectedAndWait(SharpIdeFile file) => await FileExternallySelected.InvokeParallelAsync(file);
} }

View File

@@ -66,9 +66,9 @@ public partial class IdeRoot : Control
await Singletons.BuildService.MsBuildSolutionAsync(_solutionExplorerPanel.SolutionModel.FilePath); await Singletons.BuildService.MsBuildSolutionAsync(_solutionExplorerPanel.SolutionModel.FilePath);
} }
private async Task OnSolutionExplorerPanelOnFileSelected(SharpIdeFile file) private async Task OnSolutionExplorerPanelOnFileSelected(SharpIdeFile file, SharpIdeFileLinePosition? fileLinePosition)
{ {
await _codeEditorPanel.SetSharpIdeFile(file); await _codeEditorPanel.SetSharpIdeFile(file, fileLinePosition);
} }
private void OnSlnFileSelected(string path) private void OnSlnFileSelected(string path)