UbisamBase 플랫폼 위에서 도는 플러그인. 지금까지 만든 것을 그대로 담는다. - 폴더 비교 / 파일 비교(부분 수용·워드랩·찾기) / 3-Way 병합 / Git 조작 - 비교 엔진: histogram 정렬 · 이동 블록 감지 · 무시 규칙 · 13개 언어 토크나이저 - doc/ 에 작업 지시서와 진행 기록 빌드 산출물(bin/obj)은 추적하지 않는다 - 플랫폼 런타임이 빌드 때마다 D:\UbisamPlatform\bin 에서 복사되어 들어오므로 저장소에 담을 이유가 없다. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
106 lines
4.4 KiB
C#
106 lines
4.4 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace MergeHub.Services
|
|
{
|
|
/// <summary>편집기에 그려 넣을 줄들과, 그중 어느 줄이 필러인지.</summary>
|
|
public sealed class EditorSnapshot
|
|
{
|
|
public EditorSnapshot(string[] lines, bool[] filler)
|
|
{
|
|
Lines = lines;
|
|
Filler = filler;
|
|
}
|
|
|
|
/// <summary>편집기 본문. 필러 자리는 빈 문자열이다.</summary>
|
|
public string[] Lines { get; }
|
|
|
|
/// <summary>같은 인덱스의 줄이 필러(= LOCAL 에 없는 줄)인지.</summary>
|
|
public bool[] Filler { get; }
|
|
}
|
|
|
|
/// <summary>
|
|
/// LOCAL 패널을 "항상 고칠 수 있는 편집기"로 쓰기 위한 줄 계산.
|
|
///
|
|
/// 화면에서 BASE 와 LOCAL 은 같은 내용이 같은 눈높이에 와야 한다. 그래서 BASE 에만 있는
|
|
/// 줄(= LOCAL 에서 지워진 줄) 자리에는 LOCAL 쪽에 <b>빈 줄(필러)</b>을 끼워 넣는다.
|
|
/// 필러는 화면을 맞추려고 넣은 것일 뿐 파일 내용이 아니므로, 저장하거나 다시 정렬하기 전에
|
|
/// 걷어내야 한다.
|
|
///
|
|
/// 문제는 사용자가 편집기에서 줄을 넣고 지운 뒤라 필러 위치가 밀려 있다는 점이다.
|
|
/// 그래서 "고치기 전 편집기 내용"과 "지금 편집기 내용"을 정렬해 보고,
|
|
/// <b>예전 필러와 그대로 대응되면서 여전히 비어 있는 줄</b>만 필러로 판정해 걷어낸다.
|
|
/// 필러 자리에 사용자가 뭔가 써 넣었다면 그건 새로 추가한 줄이므로 그대로 살린다.
|
|
/// </summary>
|
|
public static class EditorLayout
|
|
{
|
|
/// <summary>정렬 결과를 편집기에 그릴 줄들로 바꾼다.</summary>
|
|
public static EditorSnapshot Build(IReadOnlyList<(string Text, bool HasLocal)> rows)
|
|
{
|
|
var lines = new string[rows.Count];
|
|
var filler = new bool[rows.Count];
|
|
|
|
for (var i = 0; i < rows.Count; i++)
|
|
{
|
|
filler[i] = !rows[i].HasLocal;
|
|
lines[i] = rows[i].HasLocal ? rows[i].Text : string.Empty;
|
|
}
|
|
|
|
return new EditorSnapshot(lines, filler);
|
|
}
|
|
|
|
/// <summary>
|
|
/// 사용자가 고친 편집기 내용에서 필러를 걷어내 실제 LOCAL 줄만 남긴다.
|
|
/// </summary>
|
|
/// <param name="previous">고치기 전 편집기 줄(필러 포함)</param>
|
|
/// <param name="wasFiller">그때 어느 줄이 필러였는지</param>
|
|
/// <param name="current">지금 편집기 줄</param>
|
|
public static List<string> StripFillers(IReadOnlyList<string> previous,
|
|
IReadOnlyList<bool> wasFiller,
|
|
IReadOnlyList<string> current)
|
|
{
|
|
var kept = new List<string>(current.Count);
|
|
|
|
// 고치기 전 내용이 없으면 걷어낼 필러도 없다.
|
|
if (previous.Count == 0)
|
|
{
|
|
foreach (var line in current) kept.Add(line);
|
|
return kept;
|
|
}
|
|
|
|
// 어느 줄이 그대로 남았는지 짚어 준다. 빈 줄은 흔해서 앵커가 되지 못하므로
|
|
// 주변의 진짜 코드 줄에 딸려 자리를 잡는다 - 그래서 정렬기를 쓰는 것이다.
|
|
var map = LineAligner.Align(previous, current);
|
|
|
|
foreach (var row in map)
|
|
{
|
|
if (row.LocalNumber is null) continue; // 사용자가 지운 줄
|
|
|
|
var text = row.LocalText ?? string.Empty;
|
|
|
|
var cameFromFiller = row.BaseNumber is int oldNumber
|
|
&& oldNumber >= 1 && oldNumber <= wasFiller.Count
|
|
&& wasFiller[oldNumber - 1];
|
|
|
|
// 필러였고 여전히 비어 있으면 화면용 빈 줄일 뿐이다.
|
|
if (cameFromFiller && text.Length == 0) continue;
|
|
|
|
kept.Add(text);
|
|
}
|
|
|
|
return kept;
|
|
}
|
|
|
|
/// <summary>두 줄 목록이 같은지. 편집기 글을 다시 써야 하는지 판단할 때 쓴다.</summary>
|
|
public static bool SameLines(IReadOnlyList<string> a, IReadOnlyList<string> b)
|
|
{
|
|
if (a.Count != b.Count) return false;
|
|
|
|
for (var i = 0; i < a.Count; i++)
|
|
if (!string.Equals(a[i], b[i], StringComparison.Ordinal)) return false;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
}
|