code_review: PR #1408
Some checks are pending
Continuous Integration / Build (push) Waiting to run
Continuous Integration / Prepare version string (push) Waiting to run
Continuous Integration / Package (push) Blocked by required conditions

Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
leo 2025-06-10 11:18:20 +08:00
parent 0ea4021a64
commit ee4d8a6a0e
No known key found for this signature in database
4 changed files with 182 additions and 130 deletions

View file

@ -1,5 +1,6 @@
using System; using System;
using System.Collections.Generic; using System.Collections.Generic;
using System.Threading;
using System.Threading.Tasks; using System.Threading.Tasks;
using Avalonia.Threading; using Avalonia.Threading;
@ -10,10 +11,25 @@ namespace SourceGit.ViewModels
{ {
public class Blame : ObservableObject public class Blame : ObservableObject
{ {
public string Title public string FilePath
{ {
get => _title; get;
private set => SetProperty(ref _title, value); }
public Models.Commit Revision
{
get => _revision;
private set => SetProperty(ref _revision, value);
}
public Models.BlameData Data
{
get => _data;
private set
{
if (SetProperty(ref _data, value))
OnPropertyChanged(nameof(IsBinary));
}
} }
public bool IsBinary public bool IsBinary
@ -21,92 +37,27 @@ namespace SourceGit.ViewModels
get => _data != null && _data.IsBinary; get => _data != null && _data.IsBinary;
} }
public bool CanMoveBack public bool CanBack
{ {
get => _shaHistoryIndex > 0 && _shaHistory.Count > 1; get => _navigationActiveIndex > 0;
}
public bool CanMoveForward
{
get => _shaHistoryIndex < _shaHistory.Count - 1;
} }
public Models.BlameData Data public bool CanForward
{ {
get => _data; get => _navigationActiveIndex < _navigationHistory.Count - 1;
private set => SetProperty(ref _data, value);
} }
public Blame(string repo, string file, string revision) public Blame(string repo, string file, Models.Commit commit)
{ {
var sha = commit.SHA.Substring(0, 10);
FilePath = file;
Revision = commit;
_repo = repo; _repo = repo;
_file = file; _navigationHistory.Add(sha);
_commits.Add(sha, commit);
SetBlameData($"{revision.AsSpan(0, 10)}", true); SetBlameData(sha);
}
private void SetBlameData(string commitSHA, bool resetHistoryForward)
{
Title = $"{_file} @ {commitSHA}";
Task.Run(() =>
{
var result = new Commands.Blame(_repo, _file, commitSHA).Result();
Dispatcher.UIThread.Invoke(() =>
{
Data = result;
OnPropertyChanged(nameof(IsBinary));
});
});
if (resetHistoryForward)
{
if (_shaHistoryIndex < _shaHistory.Count - 1)
_shaHistory.RemoveRange(_shaHistoryIndex + 1, _shaHistory.Count - _shaHistoryIndex - 1);
if (_shaHistory.Count == 0 || _shaHistory[_shaHistoryIndex] != commitSHA)
{
_shaHistory.Add(commitSHA);
_shaHistoryIndex = _shaHistory.Count - 1;
}
}
OnPropertyChanged(nameof(CanMoveBack));
OnPropertyChanged(nameof(CanMoveForward));
}
public void Back()
{
--_shaHistoryIndex;
if (_shaHistoryIndex < 0)
_shaHistoryIndex = 0;
NavigateToCommit(_shaHistory[_shaHistoryIndex], false);
}
public void Forward()
{
++_shaHistoryIndex;
if (_shaHistoryIndex >= _shaHistory.Count)
_shaHistoryIndex = _shaHistory.Count - 1;
NavigateToCommit(_shaHistory[_shaHistoryIndex], false);
}
public void NavigateToCommit(string commitSHA, bool resetHistoryForward)
{
var launcher = App.GetLauncher();
if (launcher == null)
return;
foreach (var page in launcher.Pages)
{
if (page.Data is Repository repo && repo.FullPath.Equals(_repo))
{
repo.NavigateToCommit(commitSHA);
SetBlameData(commitSHA, resetHistoryForward);
break;
}
}
} }
public string GetCommitMessage(string sha) public string GetCommitMessage(string sha)
@ -119,12 +70,102 @@ namespace SourceGit.ViewModels
return msg; return msg;
} }
public void Back()
{
if (_navigationActiveIndex <= 0)
return;
_navigationActiveIndex--;
OnPropertyChanged(nameof(CanBack));
OnPropertyChanged(nameof(CanForward));
NavigateToCommit(_navigationHistory[_navigationActiveIndex]);
}
public void Forward()
{
if (_navigationActiveIndex >= _navigationHistory.Count - 1)
return;
_navigationActiveIndex++;
OnPropertyChanged(nameof(CanBack));
OnPropertyChanged(nameof(CanForward));
NavigateToCommit(_navigationHistory[_navigationActiveIndex]);
}
public void NavigateToCommit(string commitSHA)
{
if (!_navigationHistory[_navigationActiveIndex].Equals(commitSHA, StringComparison.Ordinal))
{
_navigationHistory.Add(commitSHA);
_navigationActiveIndex = _navigationHistory.Count - 1;
OnPropertyChanged(nameof(CanBack));
OnPropertyChanged(nameof(CanForward));
}
if (!Revision.SHA.StartsWith(commitSHA, StringComparison.Ordinal))
SetBlameData(commitSHA);
if (App.GetLauncher() is { Pages: { } pages })
{
foreach (var page in pages)
{
if (page.Data is Repository repo && repo.FullPath.Equals(_repo))
{
repo.NavigateToCommit(commitSHA);
break;
}
}
}
}
private void SetBlameData(string commitSHA)
{
if (_cancellationSource is { IsCancellationRequested: false })
_cancellationSource.Cancel();
_cancellationSource = new CancellationTokenSource();
var token = _cancellationSource.Token;
if (_commits.TryGetValue(commitSHA, out var c))
{
Revision = c;
}
else
{
Task.Run(() =>
{
var result = new Commands.QuerySingleCommit(_repo, commitSHA).Result();
Dispatcher.UIThread.Invoke(() =>
{
if (!token.IsCancellationRequested)
{
_commits.Add(commitSHA, result);
Revision = result ?? new Models.Commit() { SHA = commitSHA };
}
});
}, token);
}
Task.Run(() =>
{
var result = new Commands.Blame(_repo, FilePath, commitSHA).Result();
Dispatcher.UIThread.Invoke(() =>
{
if (!token.IsCancellationRequested)
Data = result;
});
}, token);
}
private string _repo; private string _repo;
private string _file; private Models.Commit _revision;
private string _title; private CancellationTokenSource _cancellationSource = null;
private int _shaHistoryIndex = 0; private int _navigationActiveIndex = 0;
private List<string> _shaHistory = []; private List<string> _navigationHistory = [];
private Models.BlameData _data = null; private Models.BlameData _data = null;
private Dictionary<string, string> _commitMessages = new Dictionary<string, string>(); private Dictionary<string, Models.Commit> _commits = new();
private Dictionary<string, string> _commitMessages = new();
} }
} }

View file

@ -313,7 +313,7 @@ namespace SourceGit.ViewModels
blame.IsEnabled = change.Index != Models.ChangeState.Deleted; blame.IsEnabled = change.Index != Models.ChangeState.Deleted;
blame.Click += (_, ev) => blame.Click += (_, ev) =>
{ {
App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit.SHA), false); App.ShowWindow(new Blame(_repo.FullPath, change.Path, _commit), false);
ev.Handled = true; ev.Handled = true;
}; };
@ -481,7 +481,7 @@ namespace SourceGit.ViewModels
blame.IsEnabled = file.Type == Models.ObjectType.Blob; blame.IsEnabled = file.Type == Models.ObjectType.Blob;
blame.Click += (_, ev) => blame.Click += (_, ev) =>
{ {
App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit.SHA), false); App.ShowWindow(new Blame(_repo.FullPath, file.Path, _commit), false);
ev.Handled = true; ev.Handled = true;
}; };

View file

@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:vm="using:SourceGit.ViewModels" xmlns:vm="using:SourceGit.ViewModels"
xmlns:v="using:SourceGit.Views" xmlns:v="using:SourceGit.Views"
xmlns:c="using:SourceGit.Converters"
mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450" mc:Ignorable="d" d:DesignWidth="800" d:DesignHeight="450"
x:Class="SourceGit.Views.Blame" x:Class="SourceGit.Views.Blame"
x:DataType="vm:Blame" x:DataType="vm:Blame"
@ -43,22 +44,44 @@
<!-- File --> <!-- File -->
<Border Grid.Row="1" Padding="8,0" > <Border Grid.Row="1" Padding="8,0" >
<Grid> <Grid ColumnDefinitions="Auto,*,Auto,Auto,400">
<TextBlock Text="{Binding Title}" VerticalAlignment="Center"/> <Path Grid.Column="0"
<StackPanel HorizontalAlignment="Right" VerticalAlignment="Center" Orientation="Horizontal"> Width="14" Height="14"
<Button Classes="icon_button" Data="{StaticResource Icons.File}"/>
IsEnabled="{Binding CanMoveBack}"
<TextBlock Grid.Column="1"
Margin="4,0,0,0"
VerticalAlignment="Center"
Text="{Binding FilePath, Mode=OneWay}"/>
<Button Grid.Column="2"
Classes="icon_button"
IsEnabled="{Binding CanBack}"
Width="28" Width="28"
Click="HistoryBack"> Command="{Binding Back}">
<Path Width="12" Height="12" Stretch="Uniform" Data="{StaticResource Icons.TriangleLeft}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.TriangleLeft}"/>
</Button> </Button>
<Button Classes="icon_button"
IsEnabled="{Binding CanMoveForward}" <Button Grid.Column="3"
Classes="icon_button"
IsEnabled="{Binding CanForward}"
Width="28" Width="28"
Click="HistoryForward"> Command="{Binding Forward}">
<Path Width="12" Height="12" Stretch="Uniform" Data="{StaticResource Icons.TriangleRight}"/> <Path Width="12" Height="12" Data="{StaticResource Icons.TriangleRight}"/>
</Button> </Button>
</StackPanel>
<Border Grid.Column="4" Background="#40000000" CornerRadius="3" Padding="4,0" Margin="0,2">
<Grid ColumnDefinitions="Auto,*">
<TextBlock Grid.Column="0"
FontWeight="Bold"
Margin="0,0,6,0"
Text="{Binding Revision.SHA, Converter={x:Static c:StringConverters.ToShortSHA}, Mode=OneWay}"/>
<TextBlock Grid.Column="2"
Text="{Binding Revision.Subject}"
TextTrimming="CharacterEllipsis"/>
</Grid>
</Border>
</Grid> </Grid>
</Border> </Border>

View file

@ -224,9 +224,7 @@ namespace SourceGit.Views
if (rect.Contains(pos)) if (rect.Contains(pos))
{ {
if (DataContext is ViewModels.Blame blame) if (DataContext is ViewModels.Blame blame)
{ blame.NavigateToCommit(info.CommitSHA);
blame.NavigateToCommit(info.CommitSHA, true);
}
e.Handled = true; e.Handled = true;
break; break;
@ -433,8 +431,6 @@ namespace SourceGit.Views
public Blame() public Blame()
{ {
InitializeComponent(); InitializeComponent();
AddHandler(PointerReleasedEvent, MouseUpHandler, handledEventsToo: true);
} }
protected override void OnClosed(EventArgs e) protected override void OnClosed(EventArgs e)
@ -443,30 +439,22 @@ namespace SourceGit.Views
GC.Collect(); GC.Collect();
} }
private void HistoryBack(object _, RoutedEventArgs e) protected override void OnPointerReleased(PointerReleasedEventArgs e)
{ {
if (DataContext is ViewModels.Blame blame) base.OnPointerReleased(e);
{
blame.Back();
}
}
private void HistoryForward(object _, RoutedEventArgs e)
{
if (DataContext is ViewModels.Blame blame)
{
blame.Forward();
}
}
private void MouseUpHandler(object sender, PointerReleasedEventArgs e) if (!e.Handled && DataContext is ViewModels.Blame blame)
{ {
if (e.InitialPressMouseButton == MouseButton.XButton1) if (e.InitialPressMouseButton == MouseButton.XButton1)
{ {
HistoryBack(null, null); blame.Back();
e.Handled = true;
} }
else if (e.InitialPressMouseButton == MouseButton.XButton2) else if (e.InitialPressMouseButton == MouseButton.XButton2)
{ {
HistoryForward(null, null); blame.Forward();
e.Handled = true;
}
} }
} }
} }