Initial commit to new repository
This commit is contained in:
99
src/AxCopilot/Services/DiffService.cs
Normal file
99
src/AxCopilot/Services/DiffService.cs
Normal file
@@ -0,0 +1,99 @@
|
||||
namespace AxCopilot.Services;
|
||||
|
||||
/// <summary>
|
||||
/// 라인 기반 텍스트 diff 서비스. 두 텍스트의 변경 사항을 비교합니다.
|
||||
/// </summary>
|
||||
public static class DiffService
|
||||
{
|
||||
public enum DiffType { Equal, Added, Removed }
|
||||
|
||||
public record DiffLine(DiffType Type, int? OldLineNo, int? NewLineNo, string Content);
|
||||
|
||||
/// <summary>두 텍스트를 라인 단위로 비교하여 diff 결과를 반환합니다.</summary>
|
||||
public static List<DiffLine> ComputeDiff(string oldText, string newText)
|
||||
{
|
||||
var oldLines = (oldText ?? "").Split('\n');
|
||||
var newLines = (newText ?? "").Split('\n');
|
||||
var result = new List<DiffLine>();
|
||||
|
||||
// LCS (Longest Common Subsequence) 기반 간단 diff
|
||||
var lcs = ComputeLcs(oldLines, newLines);
|
||||
int oi = 0, ni = 0, li = 0;
|
||||
|
||||
while (oi < oldLines.Length || ni < newLines.Length)
|
||||
{
|
||||
if (li < lcs.Count && oi < oldLines.Length && ni < newLines.Length &&
|
||||
oldLines[oi].TrimEnd('\r') == lcs[li] && newLines[ni].TrimEnd('\r') == lcs[li])
|
||||
{
|
||||
result.Add(new DiffLine(DiffType.Equal, oi + 1, ni + 1, oldLines[oi].TrimEnd('\r')));
|
||||
oi++; ni++; li++;
|
||||
}
|
||||
else if (li < lcs.Count && oi < oldLines.Length &&
|
||||
oldLines[oi].TrimEnd('\r') != lcs[li])
|
||||
{
|
||||
result.Add(new DiffLine(DiffType.Removed, oi + 1, null, oldLines[oi].TrimEnd('\r')));
|
||||
oi++;
|
||||
}
|
||||
else if (ni < newLines.Length &&
|
||||
(li >= lcs.Count || newLines[ni].TrimEnd('\r') != lcs[li]))
|
||||
{
|
||||
result.Add(new DiffLine(DiffType.Added, null, ni + 1, newLines[ni].TrimEnd('\r')));
|
||||
ni++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// 나머지 처리
|
||||
if (oi < oldLines.Length)
|
||||
{
|
||||
result.Add(new DiffLine(DiffType.Removed, oi + 1, null, oldLines[oi].TrimEnd('\r')));
|
||||
oi++;
|
||||
}
|
||||
if (ni < newLines.Length)
|
||||
{
|
||||
result.Add(new DiffLine(DiffType.Added, null, ni + 1, newLines[ni].TrimEnd('\r')));
|
||||
ni++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private static List<string> ComputeLcs(string[] a, string[] b)
|
||||
{
|
||||
int m = a.Length, n = b.Length;
|
||||
var dp = new int[m + 1, n + 1];
|
||||
|
||||
for (int i = 1; i <= m; i++)
|
||||
for (int j = 1; j <= n; j++)
|
||||
dp[i, j] = a[i - 1].TrimEnd('\r') == b[j - 1].TrimEnd('\r')
|
||||
? dp[i - 1, j - 1] + 1
|
||||
: Math.Max(dp[i - 1, j], dp[i, j - 1]);
|
||||
|
||||
// 역추적
|
||||
var lcs = new List<string>();
|
||||
int x = m, y = n;
|
||||
while (x > 0 && y > 0)
|
||||
{
|
||||
if (a[x - 1].TrimEnd('\r') == b[y - 1].TrimEnd('\r'))
|
||||
{
|
||||
lcs.Add(a[x - 1].TrimEnd('\r'));
|
||||
x--; y--;
|
||||
}
|
||||
else if (dp[x - 1, y] > dp[x, y - 1])
|
||||
x--;
|
||||
else
|
||||
y--;
|
||||
}
|
||||
lcs.Reverse();
|
||||
return lcs;
|
||||
}
|
||||
|
||||
/// <summary>diff 결과를 통계 요약 문자열로 반환합니다.</summary>
|
||||
public static string GetSummary(List<DiffLine> diff)
|
||||
{
|
||||
int added = diff.Count(d => d.Type == DiffType.Added);
|
||||
int removed = diff.Count(d => d.Type == DiffType.Removed);
|
||||
return $"+{added} -{removed} 라인 변경";
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user