mirror of
https://fastgit.cc/github.com/sourcegit-scm/sourcegit
synced 2026-04-30 13:51:53 +08:00
refactor: move data-only structures and enums to SourceGit.Models
Signed-off-by: leo <longshuang@msn.cn>
This commit is contained in:
79
src/Models/Conflict.cs
Normal file
79
src/Models/Conflict.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public enum ConflictResolution
|
||||
{
|
||||
None,
|
||||
UseOurs,
|
||||
UseTheirs,
|
||||
UseBothMineFirst,
|
||||
UseBothTheirsFirst,
|
||||
}
|
||||
|
||||
public enum ConflictMarkerType
|
||||
{
|
||||
Start, // <<<<<<<
|
||||
Base, // ||||||| (diff3 style)
|
||||
Separator, // =======
|
||||
End, // >>>>>>>
|
||||
}
|
||||
|
||||
public enum ConflictPanelType
|
||||
{
|
||||
Mine,
|
||||
Theirs,
|
||||
Result
|
||||
}
|
||||
|
||||
public enum ConflictLineState
|
||||
{
|
||||
Normal,
|
||||
ConflictBlockStart,
|
||||
ConflictBlock,
|
||||
ConflictBlockEnd,
|
||||
ResolvedBlockStart,
|
||||
ResolvedBlock,
|
||||
ResolvedBlockEnd,
|
||||
}
|
||||
|
||||
public record ConflictSelectedChunk(
|
||||
double Y,
|
||||
double Height,
|
||||
int ConflictIndex,
|
||||
ConflictPanelType Panel,
|
||||
bool IsResolved
|
||||
);
|
||||
|
||||
public class ConflictMarkerInfo
|
||||
{
|
||||
public int LineNumber { get; set; }
|
||||
public int StartOffset { get; set; }
|
||||
public int EndOffset { get; set; }
|
||||
public ConflictMarkerType Type { get; set; }
|
||||
}
|
||||
|
||||
public class ConflictRegion
|
||||
{
|
||||
public int StartLineInOriginal { get; set; }
|
||||
public int EndLineInOriginal { get; set; }
|
||||
public List<string> OursContent { get; set; } = new();
|
||||
public List<string> TheirsContent { get; set; } = new();
|
||||
public bool IsResolved { get; set; } = false;
|
||||
|
||||
// Line indices in the built static panels (0-based)
|
||||
public int PanelStartLine { get; set; } = -1;
|
||||
public int PanelEndLine { get; set; } = -1;
|
||||
|
||||
// Content chosen when resolved (null = unresolved, empty list = deleted)
|
||||
public List<string> ResolvedContent { get; set; } = null;
|
||||
|
||||
// Real markers from the file
|
||||
public string StartMarker { get; set; } = "<<<<<<<";
|
||||
public string SeparatorMarker { get; set; } = "=======";
|
||||
public string EndMarker { get; set; } = ">>>>>>>";
|
||||
|
||||
// Track the type of resolution
|
||||
public ConflictResolution ResolutionType { get; set; } = ConflictResolution.None;
|
||||
}
|
||||
}
|
||||
@@ -1,67 +0,0 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace SourceGit.Models
|
||||
{
|
||||
public enum ConflictResolution
|
||||
{
|
||||
None,
|
||||
UseOurs,
|
||||
UseTheirs,
|
||||
UseBothMineFirst,
|
||||
UseBothTheirsFirst,
|
||||
}
|
||||
|
||||
public class MergeConflictRegion
|
||||
{
|
||||
public int StartLine { get; set; } = 0;
|
||||
public int EndLine { get; set; } = 0;
|
||||
public bool IsConflict { get; set; } = false;
|
||||
public ConflictResolution Resolution { get; set; } = ConflictResolution.None;
|
||||
|
||||
public string BaseContent { get; set; } = string.Empty;
|
||||
public string OursContent { get; set; } = string.Empty;
|
||||
public string TheirsContent { get; set; } = string.Empty;
|
||||
}
|
||||
|
||||
public class MergeConflictDocument
|
||||
{
|
||||
public string BaseContent { get; set; } = string.Empty;
|
||||
public string OursContent { get; set; } = string.Empty;
|
||||
public string TheirsContent { get; set; } = string.Empty;
|
||||
public string ResultContent { get; set; } = string.Empty;
|
||||
|
||||
public List<MergeConflictRegion> Regions { get; set; } = new List<MergeConflictRegion>();
|
||||
|
||||
public int UnresolvedConflictCount
|
||||
{
|
||||
get
|
||||
{
|
||||
int count = 0;
|
||||
foreach (var region in Regions)
|
||||
{
|
||||
if (region.IsConflict && region.Resolution == ConflictResolution.None)
|
||||
count++;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
}
|
||||
|
||||
public bool HasUnresolvedConflicts => UnresolvedConflictCount > 0;
|
||||
}
|
||||
|
||||
public class ConflictMarkerInfo
|
||||
{
|
||||
public int LineNumber { get; set; }
|
||||
public int StartOffset { get; set; }
|
||||
public int EndOffset { get; set; }
|
||||
public ConflictMarkerType Type { get; set; }
|
||||
}
|
||||
|
||||
public enum ConflictMarkerType
|
||||
{
|
||||
Start, // <<<<<<<
|
||||
Base, // ||||||| (diff3 style)
|
||||
Separator, // =======
|
||||
End, // >>>>>>>
|
||||
}
|
||||
}
|
||||
@@ -9,57 +9,6 @@ using CommunityToolkit.Mvvm.ComponentModel;
|
||||
|
||||
namespace SourceGit.ViewModels
|
||||
{
|
||||
public enum MergeConflictPanelType
|
||||
{
|
||||
Mine,
|
||||
Theirs,
|
||||
Result
|
||||
}
|
||||
|
||||
public enum MergeConflictLineType
|
||||
{
|
||||
Normal,
|
||||
ConflictBlockStart,
|
||||
ConflictBlock,
|
||||
ConflictBlockEnd,
|
||||
ResolvedBlockStart,
|
||||
ResolvedBlock,
|
||||
ResolvedBlockEnd,
|
||||
}
|
||||
|
||||
public record MergeConflictSelectedChunk(
|
||||
double Y,
|
||||
double Height,
|
||||
int ConflictIndex,
|
||||
MergeConflictPanelType Panel,
|
||||
bool IsResolved
|
||||
);
|
||||
|
||||
// Represents a single conflict region with its original content and panel positions
|
||||
public class ConflictRegion
|
||||
{
|
||||
public int StartLineInOriginal { get; set; }
|
||||
public int EndLineInOriginal { get; set; }
|
||||
public List<string> OursContent { get; set; } = new();
|
||||
public List<string> TheirsContent { get; set; } = new();
|
||||
public bool IsResolved { get; set; } = false;
|
||||
|
||||
// Line indices in the built static panels (0-based)
|
||||
public int PanelStartLine { get; set; } = -1;
|
||||
public int PanelEndLine { get; set; } = -1;
|
||||
|
||||
// Content chosen when resolved (null = unresolved, empty list = deleted)
|
||||
public List<string> ResolvedContent { get; set; } = null;
|
||||
|
||||
// Real markers from the file
|
||||
public string StartMarker { get; set; } = "<<<<<<<";
|
||||
public string SeparatorMarker { get; set; } = "=======";
|
||||
public string EndMarker { get; set; } = ">>>>>>>";
|
||||
|
||||
// Track the type of resolution
|
||||
public Models.ConflictResolution ResolutionType { get; set; } = Models.ConflictResolution.None;
|
||||
}
|
||||
|
||||
public class MergeConflictEditor : ObservableObject
|
||||
{
|
||||
public string FilePath
|
||||
@@ -139,13 +88,13 @@ namespace SourceGit.ViewModels
|
||||
set => SetProperty(ref _scrollOffset, value);
|
||||
}
|
||||
|
||||
public MergeConflictSelectedChunk SelectedChunk
|
||||
public Models.ConflictSelectedChunk SelectedChunk
|
||||
{
|
||||
get => _selectedChunk;
|
||||
set => SetProperty(ref _selectedChunk, value);
|
||||
}
|
||||
|
||||
public IReadOnlyList<ConflictRegion> ConflictRegions => _conflictRegions;
|
||||
public IReadOnlyList<Models.ConflictRegion> ConflictRegions => _conflictRegions;
|
||||
public bool HasUnresolvedConflicts => _unresolvedConflictCount > 0;
|
||||
public bool HasUnsavedChanges => _isModified && !_resultContent.Equals(_originalContent, StringComparison.Ordinal);
|
||||
public bool CanSave => _unresolvedConflictCount == 0 && _isModified;
|
||||
@@ -193,11 +142,11 @@ namespace SourceGit.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
public MergeConflictLineType GetLineType(int line)
|
||||
public Models.ConflictLineState GetLineState(int line)
|
||||
{
|
||||
if (line >= 0 && line < _lineTypes.Count)
|
||||
return _lineTypes[line];
|
||||
return MergeConflictLineType.Normal;
|
||||
if (line >= 0 && line < _lineStates.Count)
|
||||
return _lineStates[line];
|
||||
return Models.ConflictLineState.Normal;
|
||||
}
|
||||
|
||||
public void AcceptOursAtIndex(int conflictIndex)
|
||||
@@ -380,7 +329,7 @@ namespace SourceGit.ViewModels
|
||||
|
||||
if (line.StartsWith("<<<<<<<", StringComparison.Ordinal))
|
||||
{
|
||||
var region = new ConflictRegion { StartLineInOriginal = i };
|
||||
var region = new Models.ConflictRegion { StartLineInOriginal = i };
|
||||
// Capture the start marker (e.g., "<<<<<<< HEAD")
|
||||
region.StartMarker = line;
|
||||
i++;
|
||||
@@ -554,7 +503,7 @@ namespace SourceGit.ViewModels
|
||||
// Build RESULT panel aligned with MINE/THEIRS panels
|
||||
// This ensures all three panels have the same number of lines for scroll sync
|
||||
var resultLines = new List<Models.TextDiffLine>();
|
||||
_lineTypes.Clear();
|
||||
_lineStates.Clear();
|
||||
|
||||
if (_oursDiffLines == null || _oursDiffLines.Count == 0)
|
||||
{
|
||||
@@ -569,7 +518,7 @@ namespace SourceGit.ViewModels
|
||||
while (currentLine < _oursDiffLines.Count)
|
||||
{
|
||||
// Check if we're at a conflict region
|
||||
ConflictRegion currentRegion = null;
|
||||
Models.ConflictRegion currentRegion = null;
|
||||
if (conflictIdx < _conflictRegions.Count)
|
||||
{
|
||||
var region = _conflictRegions[conflictIdx];
|
||||
@@ -636,41 +585,41 @@ namespace SourceGit.ViewModels
|
||||
resultLines.Add(new Models.TextDiffLine());
|
||||
|
||||
int added = resultLines.Count - oldLineCount;
|
||||
_lineTypes.Add(MergeConflictLineType.ResolvedBlockStart);
|
||||
_lineStates.Add(Models.ConflictLineState.ResolvedBlockStart);
|
||||
for (var i = 0; i < added - 2; i++)
|
||||
_lineTypes.Add(MergeConflictLineType.ResolvedBlock);
|
||||
_lineTypes.Add(MergeConflictLineType.ResolvedBlockEnd);
|
||||
_lineStates.Add(Models.ConflictLineState.ResolvedBlock);
|
||||
_lineStates.Add(Models.ConflictLineState.ResolvedBlockEnd);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Unresolved - show conflict markers with content, aligned with Mine/Theirs
|
||||
// First line: start marker (use real marker from file)
|
||||
resultLines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, currentRegion.StartMarker, 0, 0));
|
||||
_lineTypes.Add(MergeConflictLineType.ConflictBlockStart);
|
||||
_lineStates.Add(Models.ConflictLineState.ConflictBlockStart);
|
||||
|
||||
// Mine content lines (matches the deleted lines in Ours panel)
|
||||
foreach (var line in currentRegion.OursContent)
|
||||
{
|
||||
resultLines.Add(new Models.TextDiffLine(
|
||||
Models.TextDiffLineType.Deleted, line, 0, resultLineNumber++));
|
||||
_lineTypes.Add(MergeConflictLineType.ConflictBlock);
|
||||
_lineStates.Add(Models.ConflictLineState.ConflictBlock);
|
||||
}
|
||||
|
||||
// Separator marker between Mine and Theirs
|
||||
resultLines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, currentRegion.SeparatorMarker, 0, 0));
|
||||
_lineTypes.Add(MergeConflictLineType.ConflictBlock);
|
||||
_lineStates.Add(Models.ConflictLineState.ConflictBlock);
|
||||
|
||||
// Theirs content lines (matches the added lines in Theirs panel)
|
||||
foreach (var line in currentRegion.TheirsContent)
|
||||
{
|
||||
resultLines.Add(new Models.TextDiffLine(
|
||||
Models.TextDiffLineType.Added, line, 0, resultLineNumber++));
|
||||
_lineTypes.Add(MergeConflictLineType.ConflictBlock);
|
||||
_lineStates.Add(Models.ConflictLineState.ConflictBlock);
|
||||
}
|
||||
|
||||
// End marker (use real marker from file)
|
||||
resultLines.Add(new Models.TextDiffLine(Models.TextDiffLineType.Indicator, currentRegion.EndMarker, 0, 0));
|
||||
_lineTypes.Add(MergeConflictLineType.ConflictBlockEnd);
|
||||
_lineStates.Add(Models.ConflictLineState.ConflictBlockEnd);
|
||||
}
|
||||
|
||||
currentLine = currentRegion.PanelEndLine + 1;
|
||||
@@ -693,7 +642,7 @@ namespace SourceGit.ViewModels
|
||||
resultLines.Add(new Models.TextDiffLine());
|
||||
}
|
||||
|
||||
_lineTypes.Add(MergeConflictLineType.Normal);
|
||||
_lineStates.Add(Models.ConflictLineState.Normal);
|
||||
currentLine++;
|
||||
}
|
||||
}
|
||||
@@ -852,7 +801,7 @@ namespace SourceGit.ViewModels
|
||||
if (line.StartsWith("<<<<<<<", StringComparison.Ordinal))
|
||||
{
|
||||
// Get the current conflict region
|
||||
ConflictRegion region = null;
|
||||
Models.ConflictRegion region = null;
|
||||
if (conflictIdx < _conflictRegions.Count)
|
||||
region = _conflictRegions[conflictIdx];
|
||||
|
||||
@@ -965,14 +914,14 @@ namespace SourceGit.ViewModels
|
||||
private bool _isModified = false;
|
||||
private int _unresolvedConflictCount = 0;
|
||||
private int _currentConflictIndex = -1;
|
||||
private int _diffMaxLineNumber = 0;
|
||||
private List<Models.TextDiffLine> _oursDiffLines = [];
|
||||
private List<Models.TextDiffLine> _theirsDiffLines = [];
|
||||
private List<Models.TextDiffLine> _resultDiffLines = [];
|
||||
private int _diffMaxLineNumber = 0;
|
||||
private List<ConflictRegion> _conflictRegions = [];
|
||||
private List<MergeConflictLineType> _lineTypes = [];
|
||||
private List<Models.ConflictRegion> _conflictRegions = [];
|
||||
private List<Models.ConflictLineState> _lineStates = [];
|
||||
private Vector _scrollOffset = Vector.Zero;
|
||||
private MergeConflictSelectedChunk _selectedChunk;
|
||||
private Models.ConflictSelectedChunk _selectedChunk;
|
||||
private string _error = string.Empty;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -113,24 +113,24 @@ namespace SourceGit.Views
|
||||
set => SetValue(CurrentConflictIndexProperty, value);
|
||||
}
|
||||
|
||||
public static readonly StyledProperty<ViewModels.MergeConflictSelectedChunk> SelectedChunkProperty =
|
||||
AvaloniaProperty.Register<MergeDiffPresenter, ViewModels.MergeConflictSelectedChunk>(nameof(SelectedChunk));
|
||||
public static readonly StyledProperty<Models.ConflictSelectedChunk> SelectedChunkProperty =
|
||||
AvaloniaProperty.Register<MergeDiffPresenter, Models.ConflictSelectedChunk>(nameof(SelectedChunk));
|
||||
|
||||
public ViewModels.MergeConflictSelectedChunk SelectedChunk
|
||||
public Models.ConflictSelectedChunk SelectedChunk
|
||||
{
|
||||
get => GetValue(SelectedChunkProperty);
|
||||
set => SetValue(SelectedChunkProperty, value);
|
||||
}
|
||||
|
||||
protected ViewModels.MergeConflictPanelType PanelType
|
||||
protected Models.ConflictPanelType PanelType
|
||||
{
|
||||
get
|
||||
{
|
||||
if (IsResultPanel)
|
||||
return ViewModels.MergeConflictPanelType.Result;
|
||||
return Models.ConflictPanelType.Result;
|
||||
if (IsOldSide)
|
||||
return ViewModels.MergeConflictPanelType.Mine;
|
||||
return ViewModels.MergeConflictPanelType.Theirs;
|
||||
return Models.ConflictPanelType.Mine;
|
||||
return Models.ConflictPanelType.Theirs;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -365,7 +365,7 @@ namespace SourceGit.Views
|
||||
|
||||
if (isWithinRegion)
|
||||
{
|
||||
var newChunk = new ViewModels.MergeConflictSelectedChunk(
|
||||
var newChunk = new Models.ConflictSelectedChunk(
|
||||
viewportY, height, i, panelType, region.IsResolved);
|
||||
|
||||
// Only update if changed
|
||||
@@ -505,7 +505,7 @@ namespace SourceGit.Views
|
||||
}
|
||||
|
||||
// Update chunk with new position
|
||||
var newChunk = new ViewModels.MergeConflictSelectedChunk(
|
||||
var newChunk = new Models.ConflictSelectedChunk(
|
||||
viewportY, height, chunk.ConflictIndex, panelType, region.IsResolved);
|
||||
|
||||
if (Math.Abs(chunk.Y - newChunk.Y) > 1 || Math.Abs(chunk.Height - newChunk.Height) > 1)
|
||||
@@ -668,24 +668,24 @@ namespace SourceGit.Views
|
||||
|
||||
var lineIndex = index - 1;
|
||||
var info = lines[lineIndex];
|
||||
var lineType = vm.GetLineType(lineIndex);
|
||||
var lineState = vm.GetLineState(lineIndex);
|
||||
|
||||
var startY = line.GetTextLineVisualYPosition(line.TextLines[0], VisualYPosition.LineTop) - textView.VerticalOffset;
|
||||
var endY = line.GetTextLineVisualYPosition(line.TextLines[^1], VisualYPosition.LineBottom) - textView.VerticalOffset;
|
||||
var rect = new Rect(0, startY, width, endY - startY);
|
||||
|
||||
if (lineType == ViewModels.MergeConflictLineType.ConflictBlockStart)
|
||||
if (lineState == Models.ConflictLineState.ConflictBlockStart)
|
||||
drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, startY + 0.5), new Point(width, startY + 0.5));
|
||||
else if (lineType == ViewModels.MergeConflictLineType.ConflictBlockEnd)
|
||||
else if (lineState == Models.ConflictLineState.ConflictBlockEnd)
|
||||
drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Red, 0.6)), new Point(0, endY - 0.5), new Point(width, endY - 0.5));
|
||||
else if (lineType == ViewModels.MergeConflictLineType.ResolvedBlockStart)
|
||||
else if (lineState == Models.ConflictLineState.ResolvedBlockStart)
|
||||
drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, startY + 0.5), new Point(width, startY + 0.5));
|
||||
else if (lineType == ViewModels.MergeConflictLineType.ResolvedBlockEnd)
|
||||
else if (lineState == Models.ConflictLineState.ResolvedBlockEnd)
|
||||
drawingContext.DrawLine(new Pen(new SolidColorBrush(Colors.Green, 0.6)), new Point(0, endY - 0.5), new Point(width, endY - 0.5));
|
||||
|
||||
if (lineType >= ViewModels.MergeConflictLineType.ResolvedBlockStart)
|
||||
if (lineState >= Models.ConflictLineState.ResolvedBlockStart)
|
||||
drawingContext.DrawRectangle(new SolidColorBrush(Colors.Green, 0.1), null, rect);
|
||||
else if (lineType >= ViewModels.MergeConflictLineType.ConflictBlockStart)
|
||||
else if (lineState >= Models.ConflictLineState.ConflictBlockStart)
|
||||
drawingContext.DrawRectangle(new SolidColorBrush(Colors.Red, 0.1), null, rect);
|
||||
|
||||
var bg = GetBrushByLineType(info.Type);
|
||||
@@ -898,29 +898,20 @@ namespace SourceGit.Views
|
||||
// Get the presenter for bounds checking
|
||||
MergeDiffPresenter presenter = chunk.Panel switch
|
||||
{
|
||||
ViewModels.MergeConflictPanelType.Mine => OursPresenter,
|
||||
ViewModels.MergeConflictPanelType.Theirs => TheirsPresenter,
|
||||
ViewModels.MergeConflictPanelType.Result => ResultPresenter,
|
||||
Models.ConflictPanelType.Mine => OursPresenter,
|
||||
Models.ConflictPanelType.Theirs => TheirsPresenter,
|
||||
Models.ConflictPanelType.Result => ResultPresenter,
|
||||
_ => null
|
||||
};
|
||||
|
||||
// Show the appropriate popup based on panel type and resolved state
|
||||
Border popup;
|
||||
if (chunk.Panel == ViewModels.MergeConflictPanelType.Result && chunk.IsResolved)
|
||||
Border popup = chunk.Panel switch
|
||||
{
|
||||
// Show Undo popup for resolved conflicts in Result panel
|
||||
popup = ResultUndoPopup;
|
||||
}
|
||||
else
|
||||
{
|
||||
popup = chunk.Panel switch
|
||||
{
|
||||
ViewModels.MergeConflictPanelType.Mine => MinePopup,
|
||||
ViewModels.MergeConflictPanelType.Theirs => TheirsPopup,
|
||||
ViewModels.MergeConflictPanelType.Result => ResultPopup,
|
||||
_ => null
|
||||
};
|
||||
}
|
||||
Models.ConflictPanelType.Mine => MinePopup,
|
||||
Models.ConflictPanelType.Theirs => TheirsPopup,
|
||||
Models.ConflictPanelType.Result => chunk.IsResolved ? ResultUndoPopup : ResultPopup,
|
||||
_ => null
|
||||
};
|
||||
|
||||
if (popup != null && presenter != null)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user