From 134c71064ece232d9253c45bde10c8fc7daa7494 Mon Sep 17 00:00:00 2001 From: leo Date: Sat, 16 Nov 2024 14:54:50 +0800 Subject: [PATCH] feature: add buttons to go to prev/next change in text diff view (#616) Signed-off-by: leo --- src/Views/DiffView.axaml | 41 +++++++----- src/Views/DiffView.axaml.cs | 28 +++++++++ src/Views/TextDiffView.axaml.cs | 108 ++++++++++++++++++++++++++++++++ 3 files changed, 161 insertions(+), 16 deletions(-) diff --git a/src/Views/DiffView.axaml b/src/Views/DiffView.axaml index 06525abd..e0627ad8 100644 --- a/src/Views/DiffView.axaml +++ b/src/Views/DiffView.axaml @@ -34,8 +34,24 @@ + + + + @@ -81,9 +94,7 @@ @@ -97,14 +108,14 @@ @@ -112,16 +123,14 @@ - diff --git a/src/Views/DiffView.axaml.cs b/src/Views/DiffView.axaml.cs index 860627d3..7184ec44 100644 --- a/src/Views/DiffView.axaml.cs +++ b/src/Views/DiffView.axaml.cs @@ -1,4 +1,6 @@ using Avalonia.Controls; +using Avalonia.Interactivity; +using Avalonia.VisualTree; namespace SourceGit.Views { @@ -8,5 +10,31 @@ namespace SourceGit.Views { InitializeComponent(); } + + private void OnGotoPrevChange(object _, RoutedEventArgs e) + { + var textDiff = this.FindDescendantOfType(); + if (textDiff == null) + return; + + textDiff.GotoPrevChange(); + if (textDiff is SingleSideTextDiffPresenter presenter) + presenter.ForceSyncScrollOffset(); + + e.Handled = true; + } + + private void OnGotoNextChange(object _, RoutedEventArgs e) + { + var textDiff = this.FindDescendantOfType(); + if (textDiff == null) + return; + + textDiff.GotoNextChange(); + if (textDiff is SingleSideTextDiffPresenter presenter) + presenter.ForceSyncScrollOffset(); + + e.Handled = true; + } } } diff --git a/src/Views/TextDiffView.axaml.cs b/src/Views/TextDiffView.axaml.cs index 913a6340..fb2693a8 100644 --- a/src/Views/TextDiffView.axaml.cs +++ b/src/Views/TextDiffView.axaml.cs @@ -498,6 +498,108 @@ namespace SourceGit.Views { } + public void GotoPrevChange() + { + var view = TextArea.TextView; + var lines = GetLines(); + var firstLineIdx = lines.Count; + foreach (var line in view.VisualLines) + { + if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted) + continue; + + var index = line.FirstDocumentLine.LineNumber - 1; + if (index >= lines.Count) + continue; + + if (firstLineIdx > index) + firstLineIdx = index; + } + + var firstLineType = lines[firstLineIdx].Type; + var isChangeFirstLine = firstLineType != Models.TextDiffLineType.Normal && firstLineType != Models.TextDiffLineType.Indicator; + if (isChangeFirstLine) + { + for (var i = firstLineIdx - 1; i >= 0; i--) + { + var prevType = lines[i].Type; + if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator) + { + ScrollToLine(i + 2); + return; + } + } + } + else + { + var prevChangeEnd = -1; + for (var i = firstLineIdx - 1; i >= 0; i--) + { + var prevType = lines[i].Type; + if (prevType == Models.TextDiffLineType.None || + prevType == Models.TextDiffLineType.Added || + prevType == Models.TextDiffLineType.Deleted) + { + prevChangeEnd = i; + break; + } + } + + if (prevChangeEnd <= 0) + return; + + for (var i = prevChangeEnd - 1; i >= 0; i--) + { + var prevType = lines[i].Type; + if (prevType == Models.TextDiffLineType.Normal || prevType == Models.TextDiffLineType.Indicator) + { + ScrollToLine(i + 2); + return; + } + } + } + } + + public void GotoNextChange() + { + var view = TextArea.TextView; + var lines = GetLines(); + var lastLineIdx = -1; + foreach (var line in view.VisualLines) + { + if (line.IsDisposed || line.FirstDocumentLine == null || line.FirstDocumentLine.IsDeleted) + continue; + + var index = line.FirstDocumentLine.LineNumber - 1; + if (index >= lines.Count) + continue; + + if (lastLineIdx < index) + lastLineIdx = index; + } + + var lastLineType = lines[lastLineIdx].Type; + var findNormalLine = lastLineType == Models.TextDiffLineType.Normal || lastLineType == Models.TextDiffLineType.Indicator; + for (var idx = lastLineIdx + 1; idx < lines.Count; idx++) + { + var nextType = lines[idx].Type; + if (nextType == Models.TextDiffLineType.None || + nextType == Models.TextDiffLineType.Added || + nextType == Models.TextDiffLineType.Deleted) + { + if (findNormalLine) + { + ScrollToLine(idx + 1); + return; + } + } + else if (!findNormalLine) + { + findNormalLine = true; + } + } + } + public override void Render(DrawingContext context) { base.Render(context); @@ -968,6 +1070,12 @@ namespace SourceGit.Views TextArea.LeftMargins.Add(new LineModifyTypeMargin()); } + public void ForceSyncScrollOffset() + { + if (DataContext is ViewModels.TwoSideTextDiff diff) + diff.SyncScrollOffset = _scrollViewer.Offset; + } + public override List GetLines() { if (DataContext is ViewModels.TwoSideTextDiff diff)