using System; using System.Collections.Generic; using System.IO; using System.Threading.Tasks; using Avalonia.Controls; using Avalonia.Threading; using CommunityToolkit.Mvvm.ComponentModel; namespace SourceGit.ViewModels { public class RevisionCompare : ObservableObject { public object StartPoint { get => _startPoint; private set => SetProperty(ref _startPoint, value); } public object EndPoint { get => _endPoint; private set => SetProperty(ref _endPoint, value); } public List VisibleChanges { get => _visibleChanges; private set => SetProperty(ref _visibleChanges, value); } public List SelectedChanges { get => _selectedChanges; set { if (SetProperty(ref _selectedChanges, value)) { if (value != null && value.Count == 1) { var option = new Models.DiffOption(GetSHA(_startPoint), GetSHA(_endPoint), value[0]); DiffContext = new DiffContext(_repo, option, _diffContext); } else { DiffContext = null; } } } } public string SearchFilter { get => _searchFilter; set { if (SetProperty(ref _searchFilter, value)) { RefreshVisible(); } } } public DiffContext DiffContext { get => _diffContext; private set => SetProperty(ref _diffContext, value); } public RevisionCompare(string repo, Models.Commit startPoint, Models.Commit endPoint) { _repo = repo; _startPoint = (object)startPoint ?? new Models.CompareTargetWorktree(); _endPoint = (object)endPoint ?? new Models.CompareTargetWorktree(); Task.Run(Refresh); } public void Cleanup() { _repo = null; _startPoint = null; _endPoint = null; if (_changes != null) _changes.Clear(); if (_visibleChanges != null) _visibleChanges.Clear(); if (_selectedChanges != null) _selectedChanges.Clear(); _searchFilter = null; _diffContext = null; } public void NavigateTo(string commitSHA) { var repo = App.FindOpenedRepository(_repo); repo?.NavigateToCommit(commitSHA); } public void Swap() { (StartPoint, EndPoint) = (_endPoint, _startPoint); SelectedChanges = []; Task.Run(Refresh); } public void ClearSearchFilter() { SearchFilter = string.Empty; } public ContextMenu CreateChangeContextMenu() { if (_selectedChanges == null || _selectedChanges.Count != 1) return null; var change = _selectedChanges[0]; var menu = new ContextMenu(); var diffWithMerger = new MenuItem(); diffWithMerger.Header = App.Text("DiffWithMerger"); diffWithMerger.Icon = App.CreateMenuIcon("Icons.OpenWith"); diffWithMerger.Click += (_, ev) => { var opt = new Models.DiffOption(GetSHA(_startPoint), GetSHA(_endPoint), change); var toolType = Preference.Instance.ExternalMergeToolType; var toolPath = Preference.Instance.ExternalMergeToolPath; Task.Run(() => Commands.MergeTool.OpenForDiff(_repo, toolType, toolPath, opt)); ev.Handled = true; }; menu.Items.Add(diffWithMerger); if (change.Index != Models.ChangeState.Deleted) { var full = Path.GetFullPath(Path.Combine(_repo, change.Path)); var explore = new MenuItem(); explore.Header = App.Text("RevealFile"); explore.Icon = App.CreateMenuIcon("Icons.Explore"); explore.IsEnabled = File.Exists(full); explore.Click += (_, ev) => { Native.OS.OpenInFileManager(full, true); ev.Handled = true; }; menu.Items.Add(explore); } var copyPath = new MenuItem(); copyPath.Header = App.Text("CopyPath"); copyPath.Icon = App.CreateMenuIcon("Icons.Copy"); copyPath.Click += (_, ev) => { App.CopyText(change.Path); ev.Handled = true; }; menu.Items.Add(copyPath); var copyFileName = new MenuItem(); copyFileName.Header = App.Text("CopyFileName"); copyFileName.Icon = App.CreateMenuIcon("Icons.Copy"); copyFileName.Click += (_, e) => { App.CopyText(Path.GetFileName(change.Path)); e.Handled = true; }; menu.Items.Add(copyFileName); return menu; } private void RefreshVisible() { if (_changes == null) return; if (string.IsNullOrEmpty(_searchFilter)) { VisibleChanges = _changes; } else { var visible = new List(); foreach (var c in _changes) { if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) visible.Add(c); } VisibleChanges = visible; } } private void Refresh() { _changes = new Commands.CompareRevisions(_repo, GetSHA(_startPoint), GetSHA(_endPoint)).Result(); var visible = _changes; if (!string.IsNullOrWhiteSpace(_searchFilter)) { visible = []; foreach (var c in _changes) { if (c.Path.Contains(_searchFilter, StringComparison.OrdinalIgnoreCase)) visible.Add(c); } } Dispatcher.UIThread.Invoke(() => VisibleChanges = visible); } private string GetSHA(object obj) { return obj is Models.Commit commit ? commit.SHA : string.Empty; } private string _repo; private object _startPoint = null; private object _endPoint = null; private List _changes = null; private List _visibleChanges = null; private List _selectedChanges = null; private string _searchFilter = string.Empty; private DiffContext _diffContext = null; } }