1212using Avalonia . Interactivity ;
1313using Avalonia . Media ;
1414using Avalonia . Threading ;
15+ using Avalonia . VisualTree ;
1516
1617using AvaloniaEdit ;
1718using AvaloniaEdit . Document ;
@@ -105,6 +106,15 @@ public Models.ConflictSelectedChunk SelectedChunk
105106 set => SetValue ( SelectedChunkProperty , value ) ;
106107 }
107108
109+ public static readonly StyledProperty < ViewModels . TextDiffDisplayRange > DisplayRangeProperty =
110+ AvaloniaProperty . Register < MergeDiffPresenter , ViewModels . TextDiffDisplayRange > ( nameof ( DisplayRange ) ) ;
111+
112+ public ViewModels . TextDiffDisplayRange DisplayRange
113+ {
114+ get => GetValue ( DisplayRangeProperty ) ;
115+ set => SetValue ( DisplayRangeProperty , value ) ;
116+ }
117+
108118 protected override Type StyleKeyOverride => typeof ( TextEditor ) ;
109119
110120 public MergeDiffPresenter ( ) : base ( new TextArea ( ) , new TextDocument ( ) )
@@ -147,7 +157,10 @@ protected override void OnLoaded(RoutedEventArgs e)
147157 TextArea . TextView . PointerEntered += OnTextViewPointerChanged ;
148158 TextArea . TextView . PointerMoved += OnTextViewPointerChanged ;
149159 TextArea . TextView . PointerWheelChanged += OnTextViewPointerWheelChanged ;
160+ TextArea . TextView . VisualLinesChanged += OnTextViewVisualLinesChanged ;
150161 TextArea . TextView . LineTransformers . Add ( new MergeDiffIndicatorTransformer ( this ) ) ;
162+
163+ OnTextViewVisualLinesChanged ( null , null ) ;
151164 }
152165
153166 protected override void OnUnloaded ( RoutedEventArgs e )
@@ -156,6 +169,7 @@ protected override void OnUnloaded(RoutedEventArgs e)
156169 TextArea . TextView . PointerEntered -= OnTextViewPointerChanged ;
157170 TextArea . TextView . PointerMoved -= OnTextViewPointerChanged ;
158171 TextArea . TextView . PointerWheelChanged -= OnTextViewPointerWheelChanged ;
172+ TextArea . TextView . VisualLinesChanged -= OnTextViewVisualLinesChanged ;
159173
160174 if ( _textMate != null )
161175 {
@@ -253,6 +267,34 @@ private void OnTextViewPointerWheelChanged(object sender, PointerWheelEventArgs
253267 Dispatcher . UIThread . Post ( ( ) => UpdateSelectedChunkPosition ( vm , y ) ) ;
254268 }
255269
270+ private void OnTextViewVisualLinesChanged ( object sender , EventArgs e )
271+ {
272+ if ( ! TextArea . TextView . VisualLinesValid )
273+ {
274+ SetCurrentValue ( DisplayRangeProperty , null ) ;
275+ return ;
276+ }
277+
278+ var lines = DiffLines ;
279+ var start = int . MaxValue ;
280+ var count = 0 ;
281+ foreach ( var line in TextArea . TextView . VisualLines )
282+ {
283+ if ( line . IsDisposed || line . FirstDocumentLine == null || line . FirstDocumentLine . IsDeleted )
284+ continue ;
285+
286+ var index = line . FirstDocumentLine . LineNumber - 1 ;
287+ if ( index >= lines . Count )
288+ continue ;
289+
290+ count ++ ;
291+ if ( start > index )
292+ start = index ;
293+ }
294+
295+ SetCurrentValue ( DisplayRangeProperty , new ViewModels . TextDiffDisplayRange ( start , start + count ) ) ;
296+ }
297+
256298 private void OnTextViewScrollChanged ( object sender , ScrollChangedEventArgs e )
257299 {
258300 if ( _scrollViewer == null || DataContext is not ViewModels . MergeConflictEditor vm )
@@ -525,6 +567,97 @@ private IBrush GetBrushByLineType(Models.TextDiffLineType type)
525567 private readonly MergeDiffPresenter _presenter ;
526568 }
527569
570+ public class MergeConflictMinimap : Control
571+ {
572+ public static readonly StyledProperty < ViewModels . TextDiffDisplayRange > DisplayRangeProperty =
573+ AvaloniaProperty . Register < MergeConflictMinimap , ViewModels . TextDiffDisplayRange > ( nameof ( DisplayRange ) ) ;
574+
575+ public ViewModels . TextDiffDisplayRange DisplayRange
576+ {
577+ get => GetValue ( DisplayRangeProperty ) ;
578+ set => SetValue ( DisplayRangeProperty , value ) ;
579+ }
580+
581+ public static readonly StyledProperty < int > UnsolvedCountProperty =
582+ AvaloniaProperty . Register < MergeConflictMinimap , int > ( nameof ( UnsolvedCount ) ) ;
583+
584+ public int UnsolvedCount
585+ {
586+ get => GetValue ( UnsolvedCountProperty ) ;
587+ set => SetValue ( UnsolvedCountProperty , value ) ;
588+ }
589+
590+ public override void Render ( DrawingContext context )
591+ {
592+ context . DrawRectangle ( Brushes . Transparent , null , new Rect ( 0 , 0 , Bounds . Width , Bounds . Height ) ) ;
593+
594+ if ( DataContext is not ViewModels . MergeConflictEditor vm )
595+ return ;
596+
597+ var total = vm . OursDiffLines . Count ;
598+ var unitHeight = Bounds . Height / ( total * 1.0 ) ;
599+ var conflicts = vm . ConflictRegions ;
600+ var blockBGs = new SolidColorBrush [ ] { new SolidColorBrush ( Colors . Red , 0.6 ) , new SolidColorBrush ( Colors . Green , 0.6 ) } ;
601+ foreach ( var c in conflicts )
602+ {
603+ var topY = c . StartLineInOriginal * unitHeight ;
604+ var bottomY = ( c . EndLineInOriginal + 1 ) * unitHeight ;
605+ var bg = blockBGs [ c . IsResolved ? 1 : 0 ] ;
606+ context . DrawRectangle ( bg , null , new Rect ( 0 , topY , Bounds . Width , bottomY - topY ) ) ;
607+ }
608+
609+ var range = DisplayRange ;
610+ if ( range == null || range . End == 0 )
611+ return ;
612+
613+ var startY = range . Start * unitHeight ;
614+ var endY = range . End * unitHeight ;
615+ var color = ( Color ) this . FindResource ( "SystemAccentColor" ) ;
616+ var brush = new SolidColorBrush ( color , 0.2 ) ;
617+ var pen = new Pen ( color . ToUInt32 ( ) ) ;
618+ var rect = new Rect ( 0 , startY , Bounds . Width , endY - startY ) ;
619+
620+ context . DrawRectangle ( brush , null , rect ) ;
621+ context . DrawLine ( pen , rect . TopLeft , rect . TopRight ) ;
622+ context . DrawLine ( pen , rect . BottomLeft , rect . BottomRight ) ;
623+ }
624+
625+ protected override void OnPropertyChanged ( AvaloniaPropertyChangedEventArgs change )
626+ {
627+ base . OnPropertyChanged ( change ) ;
628+
629+ if ( change . Property == DisplayRangeProperty ||
630+ change . Property == UnsolvedCountProperty ||
631+ change . Property . Name . Equals ( nameof ( ActualThemeVariant ) , StringComparison . Ordinal ) )
632+ InvalidateVisual ( ) ;
633+ }
634+
635+ protected override void OnPointerPressed ( PointerPressedEventArgs e )
636+ {
637+ base . OnPointerPressed ( e ) ;
638+
639+ if ( DataContext is not ViewModels . MergeConflictEditor vm )
640+ return ;
641+
642+ var total = vm . OursDiffLines . Count ;
643+ var range = DisplayRange ;
644+ if ( range == null || range . End == 0 )
645+ return ;
646+
647+ var unitHeight = Bounds . Height / ( total * 1.0 ) ;
648+ var startY = range . Start * unitHeight ;
649+ var endY = range . End * unitHeight ;
650+ var pressedY = e . GetPosition ( this ) . Y ;
651+ if ( pressedY >= startY && pressedY <= endY )
652+ return ;
653+
654+ var line = Math . Max ( 1 , Math . Min ( total , ( int ) Math . Ceiling ( pressedY / unitHeight ) ) ) ;
655+ var editor = this . FindAncestorOfType < MergeConflictEditor > ( ) ;
656+ if ( editor != null )
657+ editor . OursPresenter . ScrollToLine ( line ) ;
658+ }
659+ }
660+
528661 public partial class MergeConflictEditor : ChromelessWindow
529662 {
530663 public MergeConflictEditor ( )
@@ -547,7 +680,7 @@ protected override async void OnClosing(WindowClosingEventArgs e)
547680 if ( DataContext is not ViewModels . MergeConflictEditor vm )
548681 return ;
549682
550- if ( _forceClose || ! vm . HasUnsavedChanges )
683+ if ( _forceClose || vm . UnsolvedCount < vm . ConflictRegions . Count )
551684 {
552685 vm . PropertyChanged -= OnViewModelPropertyChanged ;
553686 return ;
@@ -580,7 +713,7 @@ private void OnViewModelPropertyChanged(object sender, PropertyChangedEventArgs
580713
581714 private void OnGotoPrevConflict ( object sender , RoutedEventArgs e )
582715 {
583- if ( IsLoaded && DataContext is ViewModels . MergeConflictEditor vm && vm . HasUnresolvedConflicts )
716+ if ( IsLoaded && DataContext is ViewModels . MergeConflictEditor vm && vm . UnsolvedCount > 0 )
584717 {
585718 var view = OursPresenter . TextArea ? . TextView ;
586719 var lines = vm . OursDiffLines ;
@@ -623,7 +756,7 @@ private void OnGotoPrevConflict(object sender, RoutedEventArgs e)
623756
624757 private void OnGotoNextConflict ( object sender , RoutedEventArgs e )
625758 {
626- if ( IsLoaded && DataContext is ViewModels . MergeConflictEditor vm && vm . HasUnresolvedConflicts )
759+ if ( IsLoaded && DataContext is ViewModels . MergeConflictEditor vm && vm . UnsolvedCount > 0 )
627760 {
628761 var view = OursPresenter . TextArea ? . TextView ;
629762 var lines = vm . OursDiffLines ;
0 commit comments