diff --git a/src/Models/OpenAI.cs b/src/Models/OpenAI.cs index 516a9423..df67ff66 100644 --- a/src/Models/OpenAI.cs +++ b/src/Models/OpenAI.cs @@ -175,7 +175,7 @@ namespace SourceGit.Models var body = reader.Result; if (!rsp.IsSuccessStatusCode) { - throw new Exception($"AI service returns error code {rsp.StatusCode}. Body: {body??string.Empty}"); + throw new Exception($"AI service returns error code {rsp.StatusCode}. Body: {body ?? string.Empty}"); } return JsonSerializer.Deserialize(reader.Result, JsonCodeGen.Default.OpenAIChatResponse); diff --git a/src/Views/CommitBaseInfo.axaml b/src/Views/CommitBaseInfo.axaml index fdccbf99..d850932c 100644 --- a/src/Views/CommitBaseInfo.axaml +++ b/src/Views/CommitBaseInfo.axaml @@ -211,6 +211,20 @@ + + + + + + + + + + + + + + diff --git a/src/Views/CommitMessagePresenter.cs b/src/Views/CommitMessagePresenter.cs index a022ae99..59543af8 100644 --- a/src/Views/CommitMessagePresenter.cs +++ b/src/Views/CommitMessagePresenter.cs @@ -1,12 +1,14 @@ using System; using System.Collections.Generic; using System.Text.RegularExpressions; +using System.Threading.Tasks; using Avalonia; using Avalonia.Collections; using Avalonia.Controls; using Avalonia.Controls.Documents; using Avalonia.Input; +using Avalonia.Threading; using Avalonia.VisualTree; namespace SourceGit.Views @@ -43,7 +45,9 @@ namespace SourceGit.Views if (change.Property == MessageProperty || change.Property == IssueTrackerRulesProperty) { Inlines!.Clear(); + _inlineCommits.Clear(); _matches = null; + _lastHover = null; ClearHoveredIssueLink(); var message = Message; @@ -154,6 +158,10 @@ namespace SourceGit.Views ToolTip.SetTip(this, match.Link); ToolTip.SetIsOpen(this, true); } + else + { + ProcessHoverCommitLink(match); + } return; } @@ -256,6 +264,52 @@ namespace SourceGit.Views ClearHoveredIssueLink(); } + private void ProcessHoverCommitLink(Models.Hyperlink link) + { + var sha = link.Link; + + // If we have already queried this SHA, just use it. + if (_inlineCommits.TryGetValue(sha, out var exist)) + { + if (exist != null) + { + ToolTip.SetTip(this, exist); + ToolTip.SetIsOpen(this, true); + } + + return; + } + + var parentView = this.FindAncestorOfType(); + if (parentView is { DataContext: ViewModels.CommitDetail detail }) + { + // Record the SHA of current viewing commit in the CommitDetail panel to determine if it is changed after + // asynchronous queries. + var lastDetailCommit = detail.Commit.SHA; + Task.Run(() => + { + var c = detail.GetParent(sha); + Dispatcher.UIThread.Invoke(() => + { + // Make sure the DataContext of CommitBaseInfo is not changed. + var currentParent = this.FindAncestorOfType(); + if (currentParent is { DataContext: ViewModels.CommitDetail currentDetail } && + currentDetail.Commit.SHA == lastDetailCommit) + { + _inlineCommits.Add(sha, c); + + // Make sure user still hovers the target SHA. + if (_lastHover == link) + { + ToolTip.SetTip(this, c); + ToolTip.SetIsOpen(this, true); + } + } + }); + }); + } + } + private void ClearHoveredIssueLink() { if (_lastHover != null) @@ -268,5 +322,6 @@ namespace SourceGit.Views private List _matches = null; private Models.Hyperlink _lastHover = null; + private Dictionary _inlineCommits = new(); } } diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 544453f5..da2d9ed1 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -476,7 +476,7 @@ namespace SourceGit.Views get => GetValue(SelectedChunkProperty); set => SetValue(SelectedChunkProperty, value); } - + public static readonly StyledProperty DisplayRangeProperty = AvaloniaProperty.Register(nameof(DisplayRange), new TextDiffViewRange(0, 0)); @@ -523,7 +523,7 @@ namespace SourceGit.Views var firstLineIdx = DisplayRange.StartIdx; if (firstLineIdx <= 1) return; - + var lines = GetLines(); var firstLineType = lines[firstLineIdx].Type; var prevLineType = lines[firstLineIdx - 1].Type; @@ -761,7 +761,7 @@ namespace SourceGit.Views if (start > index) start = index; } - + SetCurrentValue(DisplayRangeProperty, new TextDiffViewRange(start, start + count)); } @@ -1313,9 +1313,9 @@ namespace SourceGit.Views private ScrollViewer _scrollViewer = null; } - + public class TextDiffViewMinimap : Control - { + { public static readonly StyledProperty AddedLineBrushProperty = AvaloniaProperty.Register(nameof(AddedLineBrush), new SolidColorBrush(Color.FromArgb(60, 0, 255, 0))); @@ -1333,7 +1333,7 @@ namespace SourceGit.Views get => GetValue(DeletedLineBrushProperty); set => SetValue(DeletedLineBrushProperty, value); } - + public static readonly StyledProperty DisplayRangeProperty = AvaloniaProperty.Register(nameof(DisplayRange), new TextDiffViewRange(0, 0)); @@ -1342,7 +1342,7 @@ namespace SourceGit.Views get => GetValue(DisplayRangeProperty); set => SetValue(DisplayRangeProperty, value); } - + public static readonly StyledProperty DisplayRangeColorProperty = AvaloniaProperty.Register(nameof(DisplayRangeColor), Colors.RoyalBlue); @@ -1376,7 +1376,7 @@ namespace SourceGit.Views total = diff.Lines.Count; RenderSingleSide(context, diff.Lines, 0, Bounds.Width); } - + var range = DisplayRange; if (range.EndIdx == 0) return; @@ -1416,7 +1416,7 @@ namespace SourceGit.Views lastLineTypeStart = i; } } - + RenderBlock(context, lastLineType, lastLineTypeStart, total - lastLineTypeStart, total, x, width); } @@ -1431,7 +1431,7 @@ namespace SourceGit.Views } } } - + public partial class TextDiffView : UserControl { public static readonly StyledProperty UseSideBySideDiffProperty =