Ubisam-MergeTool/src/MergeHub/Models/MergeLineCollection.cs
ksj09406 0530832e79 머지 주석 · 타이핑 속도 · 탭 알림 (사용자 요청 · 보고)
Accept 뒤 Alt+Down 을 두 번 눌러야 다음 차이로 가던 것 - 반영하며 캐럿 위에
주석 줄이 끼면 글자 수로 지킨 캐럿 자리가 그 주석 줄이 됐다. 주석을 끼우거나
빼기 직전 캐럿 행을 기억했다가, 글을 갈아 끼운 뒤 그 행의 편집기 줄에 캐럿을
둔다. 임시 기록으로 확인한 것: 글을 막 갈아 끼운 TextBox 의 줄 정보는 아직
옛 글 기준이다 - 줄 시작 자리는 새 글에서 직접 센다.

줄을 지우는 반영(BASE 에 없는 줄)에도 주석을 남긴다. 한 줄이면 "삭제",
구간 · 메서드로 여러 줄이면 한 번만 "삭제 N줄"(사용자 선택). 왼쪽으로 반영 ·
3-Way 저쪽 반영에도 같은 규칙.

색 구분 "줄 안에서 바뀐 부분" 에 줄 수를 붙였다(이 칸만 없었다).

타이핑: 1,806줄에서 재 보니 엔터마다 도는 재정렬 0.4초의 대부분이 목록에
1,800줄을 한 줄씩 넣으며 알림이 1,800번 가는 것이었다(파일 비교 자체 0.012초).
MergeLineCollection 으로 한 번에 넣고(ReplaceAll), 재정렬에서는 번호 말고 모든
값이 같은 줄은 기존 객체를 두고 번호만 고쳐 가운데만 빼고 넣는다(Patch,
MergeLine.SameStateExceptNumbers). 엔터 한 번 약 0.55초 -> 약 0.05초.
화면 요소 재활용(Recycling)은 효과가 없어 넣지 않았다.

탭이 20개를 넘어 오래 안 본 탭이 조용히 닫히면 알림을 띄운다.

시험: 빌드 경고 0 · 오류 0. 하네스 NoteTest 106 · PatchTest(새) 16.
화면 g12-note(주석 · 캐럿 · 되돌리기) · g14-inline · g15-typing-tabs(새 줄
제자리 · 번호 896 -> 897 -> 898, 21번째 탭에서 알림 · 20개 유지) 통과.
기존 회귀는 사용자 지시("방금 요청한 것만")로 돌리지 않았다.

Agent: main (claude-opus-5-5)

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-30 18:27:47 +09:00

71 lines
3.6 KiB
C#

using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Collections.Specialized;
using System.ComponentModel;
namespace MergeHub.Models
{
/// <summary>
/// 비교 화면의 정렬 행 목록. <see cref="ReplaceAll"/> 로 <b>통째로 갈아 끼우고 알림은 한 번</b>만 보낸다.
///
/// <para>
/// 편집기에서 엔터를 치거나 손을 멈출 때마다 줄을 다시 정렬하는데, 예전에는 <c>Clear</c> 뒤 한 줄씩 <c>Add</c> 해서
/// 1,800줄이면 "하나 추가됨" 알림이 1,800번 가고 칸 목록 넷이 알림마다 반응했다. 재서 보니 재정렬 한 번 0.37초 가운데
/// <b>그 알림들이 0.33초</b>였다(파일 비교 자체는 0.01초) - 엔터마다 화면이 0.4초씩 멈춰 "글자가 뒤따라오는" 까닭(2026-09-30).
/// </para>
/// </summary>
public sealed class MergeLineCollection : ObservableCollection<MergeLine>
{
/// <summary>가운데(바뀐 줄) 이보다 많으면 줄마다 빼고 넣지 않고 통째로 갈아 끼운다 - 알림이 줄 수만큼 가면 도로 느려진다.</summary>
public const int PatchLimit = 200;
/// <summary>
/// 새 정렬 결과를 넣되, <b>번호 말고는 모든 값이 같은 줄</b>은 기존 객체를 그대로 두고 번호만 고친다(2026-09-30).
///
/// <para>
/// 통째로 갈아 끼우면(<see cref="ReplaceAll"/>) 줄 객체가 모두 새것이라 칸 목록이 보이는 줄 요소를 전부 다시 만든다 -
/// 재정렬 뒤 화면이 다시 그려지기까지 0.15초 가운데 0.12초가 그것이었다(색칠은 0.02초). 엔터를 치면 위 · 아래 줄은
/// 그대로이고 끼운 자리 아래는 번호만 밀리므로, 앞 · 뒤로 같은 줄을 걸러 내고 <b>가운데만</b> 빼고 넣는다.
/// 값이 하나라도 다른 줄(반영 표시 · 단어 차이 · 규칙)은 새 객체로 바꾼다 - 옛 객체에 새 값을 옮겨 적다 하나라도 빠뜨리면 틀린 색이 남는다.
/// </para>
/// </summary>
public void Patch(IList<MergeLine> rows)
{
var oldCount = Count;
var newCount = rows.Count;
var head = 0;
while (head < oldCount && head < newCount && this[head].SameStateExceptNumbers(rows[head])) head++;
var tail = 0;
while (tail < oldCount - head && tail < newCount - head &&
this[oldCount - 1 - tail].SameStateExceptNumbers(rows[newCount - 1 - tail])) tail++;
var oldMiddle = oldCount - head - tail;
var newMiddle = newCount - head - tail;
if (oldMiddle + newMiddle > PatchLimit)
{
ReplaceAll(rows);
return;
}
for (var i = 0; i < head; i++) this[i].TakeNumbersFrom(rows[i]);
for (var k = 0; k < tail; k++) this[oldCount - 1 - k].TakeNumbersFrom(rows[newCount - 1 - k]);
for (var i = 0; i < oldMiddle; i++) RemoveAt(head);
for (var i = 0; i < newMiddle; i++) Insert(head + i, rows[head + i]);
}
public void ReplaceAll(IEnumerable<MergeLine> rows)
{
CheckReentrancy();
Items.Clear();
foreach (var row in rows) Items.Add(row);
OnPropertyChanged(new PropertyChangedEventArgs(nameof(Count)));
OnPropertyChanged(new PropertyChangedEventArgs("Item[]"));
OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
}
}
}