Skip to content

Commit

Permalink
Make file paths clickable with control in output
Browse files Browse the repository at this point in the history
  • Loading branch information
HendrikMennen committed Oct 30, 2024
1 parent e1ad52a commit 0a5cee5
Show file tree
Hide file tree
Showing 2 changed files with 157 additions and 2 deletions.
4 changes: 3 additions & 1 deletion src/OneWare.Output/Views/OutputBaseView.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
using Avalonia.Media;
using Avalonia.Threading;
using AvaloniaEdit;
using DynamicData;
using DynamicData.Binding;
using OneWare.Output.ViewModels;

Expand Down Expand Up @@ -77,7 +78,8 @@ public OutputBaseView()
protected void UpdateLineColors(OutputBaseViewModel evm)
{
if (Output == null) throw new NullReferenceException(nameof(Output));
Output.TextArea.TextView.LineTransformers.Clear();
for(var i = 2; i < Output.TextArea.TextView.LineTransformers.Count; i++)
Output.TextArea.TextView.LineTransformers.RemoveAt(i);
for (var i = 0; i < evm.LineColors.Count; i++)
if (evm.LineColors[i] != null)
Output.TextArea.TextView.LineTransformers.Add(new LineColorizer(i + 1, evm.LineColors[i]));
Expand Down
155 changes: 154 additions & 1 deletion src/OneWare.Output/Views/OutputView.axaml.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,164 @@
using System.Text.RegularExpressions;
using Avalonia;
using Avalonia.Input;
using Avalonia.Interactivity;
using Avalonia.Media;
using OneWare.Essentials.EditorExtensions;
using OneWare.Essentials.Extensions;
using OneWare.Essentials.Helpers;
using OneWare.Essentials.Models;
using OneWare.Essentials.Services;
using OneWare.Essentials.ViewModels;
using Prism.Ioc;

namespace OneWare.Output.Views;

public partial class OutputView : OutputBaseView
{
private readonly TextModificationService _modificationService;

private PointerEventArgs? _lastMovedArgs;

private SearchResult? _searchResult;

public OutputView()
{
InitializeComponent();


_modificationService = new TextModificationService(Output.TextArea.TextView);
Output.TextArea.TextView.LineTransformers.Add(_modificationService);
Output.Options.AllowScrollBelowDocument = false;

Output.AddHandler(PointerPressedEvent, PointerPressedAfterCaretUpdate, RoutingStrategies.Bubble, true);

}

protected override void OnPointerMoved(PointerEventArgs e)
{
base.OnPointerMoved(e);

_lastMovedArgs = e;

if(Output.Document.Text.Length == 0) Output.Document.Text = "VhdlSim_tb.vhd:43:5: signal interface \"led\" has no association in entity";

if (e.KeyModifiers == PlatformHelper.ControlKey)
{
SearchPath();
}
else
{
ResetControlModification();
}
}

protected override void OnKeyDown(KeyEventArgs e)
{
if(e.KeyModifiers == PlatformHelper.ControlKey)
{
SearchPath();
}
base.OnKeyDown(e);
}

protected override void OnKeyUp(KeyEventArgs e)
{
if(e.KeyModifiers == PlatformHelper.ControlKey)
{
ResetControlModification();
}
base.OnKeyUp(e);
}

private void PointerPressedAfterCaretUpdate(object? sender, PointerPressedEventArgs e)
{
if(!e.GetCurrentPoint(null).Properties.IsLeftButtonPressed) return;

_ = OpenFileAsync();
}

private async Task OpenFileAsync()
{
if(_searchResult == null) return;

var result = ContainerLocator.Container.Resolve<IProjectExplorerService>()
.ActiveProject?.SearchRelativePath(_searchResult.Path);

if (result is IFile file)
{
var doc = await ContainerLocator.Container.Resolve<IDockService>().OpenFileAsync(file);
if (doc is not IEditor evb) return;

var offset = evb.CurrentDocument.GetOffset(_searchResult.Line, _searchResult.Column);

evb.Select(offset, 0);
}
}

protected override void OnLostFocus(RoutedEventArgs e)
{
ResetControlModification();
base.OnLostFocus(e);
}

private void SearchPath()
{
if(_lastMovedArgs == null) return;

var pointerPosition = Output.GetOffsetFromPointerPosition(_lastMovedArgs);

if(pointerPosition < 0) return;

var line = Output.Document.GetLineByOffset(pointerPosition);
var lineText = Output.Document.GetText(line);

var regex = ExtractFilePathRegex();

var matches = regex.Matches(lineText);

var match = matches.FirstOrDefault(x => x.Index <= pointerPosition && x.Index + x.Length >= pointerPosition);

if (match is not { Success: true }) return;

var path = match.Groups[1].Value;
var fileLine = int.Parse(match.Groups[2].Value);
var fileColumn = int.Parse(match.Groups[3].Value);

_searchResult = new SearchResult(path, fileLine, fileColumn);

var result = ContainerLocator.Container.Resolve<IProjectExplorerService>()
.ActiveProject?.SearchRelativePath(_searchResult.Path);

if(result == null) return;

Output.TextArea.TextView.Cursor = Cursor.Parse("Hand");

_modificationService.SetModification("Control_Underline", new TextModificationSegment(
line.Offset + match.Index,
line.Offset + match.Index + match.Length)
{ Decorations = TextDecorationCollection.Parse("Underline") });
}

private void ResetControlModification()
{
Output.TextArea.TextView.Cursor = Cursor.Parse("IBeam");
_modificationService.ClearModification("Control_Underline");
_searchResult = null;
}

[GeneratedRegex(@"^(.*?):(\d+):(\d+)")]
private static partial Regex ExtractFilePathRegex();

private class SearchResult
{
public string Path { get; }
public int Line { get; }
public int Column { get; }

public SearchResult(string path, int line, int column)
{
Path = path;
Line = line;
Column = column;
}
}
}

0 comments on commit 0a5cee5

Please sign in to comment.