Ubisam-MergeTool/doc/history/2026-09-10-4차.md
ksj09406 aa30988a35 엔터 고침이 만든 되돌이 문제를 고침
TextChanged 안에서 재정렬을 부르니 편집기 글을 자기 이벤트 안에서 갈아 끼우게 됐다.
캐럿이 엉키고 글자가 엉뚱한 자리에 찍혔다.
타이머 간격을 1ms 로 두어 이 입력이 끝난 다음 차례에 돌게 했다. 속도는 그대로다.
2026-09-10 21:59:16 +09:00

24 KiB
Raw Blame History

4차 — 로컬 머지툴 사용성 (2026-09-10 ~)

지시서: 작업지시/4차.md


D-1. doc/history/ 를 만들고 진행상황을 나눴다 — 완료 (2026-09-10)

왜 - 진행상황.md 가 721줄이었고 한 파일에 다섯 가지가 섞여 있었다. 주차 목표 · 차수별 상태 표 · 항목별 상세 · 미룬 것 · 완료 기록. "3차 D-3 에서 무슨 판단을 했더라" 를 찾으려면 스크롤로 뒤져야 했다.

이렇게 나눴다.

doc/
  진행상황.md              73줄. 지금 대기·진행 중인 것만
  작업지시/
    3차.md · 4차.md · 머지툴보강.md · 사용성보강.md
  history/
    changelog.md          한 줄 요약 + 날짜별 문서 목록
    2026-09-08-2주차.md   90줄
    2026-09-09-3차.md     565줄
    2026-09-10-4차.md     이 파일

옮기기만 하고 내용은 고치지 않았다. 지시서가 그렇게 시켰다 - 다시 쓰면 그때 무슨 판단을 했는지가 흐려진다. 옮긴 자리에 날짜와 제목만 얹었다.

지시서를 doc/작업지시/ 로 옮긴 것은 git mv 로 했다. 새로 쓰고 지우면 이력이 끊긴다. git log --follow 로 옛 이력을 그대로 따라갈 수 있다.

링크는 열다섯 개가 전부 살아 있다. 옮기면서 다섯 군데를 고쳤다. doc/작업지시-3차.md → 작업지시/3차.md, 지시서 안의 doc/진행상황.md → ../진행상황.md, 작업지시-머지툴보강.md → 머지툴보강.md. 문서 전체를 훑어 남은 옛 경로가 없는 것을 확인했다.

곁들여 하나 - 미룬 것 목록에 최근 파일 목록 이 남아 있었다. 4차 B-1 로 올라온 항목이라 두 자리에 다르게 적혀 있었다. 미룬 것 쪽에서 뺐다.

판정 기준 확인

기준 결과
진행상황.md 200줄 아래 721 → 73줄
대기·진행 중인 것이 한 화면에 4차 열세 항목이 한 화면에 들어온다
끝난 차수 상세를 링크 한 번으로 changelog 한 줄 → 날짜별 파일
링크가 깨지지 않음 15개 전부 정상 (스크립트로 확인)

D-2. 앞으로의 기록 규칙을 정했다 — 완료 (2026-09-10)

왜 - D-1 로 자리를 나눠 놓기만 하면 다시 한 파일로 쌓인다. 어디에 적을지를 규칙으로 못박아야 한다.

지시서 작업지시/4차.md 의 공통 규칙을 고쳤다. 항목 하나가 끝나면 세 군데에 남긴다.

  1. 상세는 doc/history/YYYY-MM-DD-주제.md — 원인 · 판단 · 확인한 결과
  2. 진행상황.md 은 상태만 — 여기에 상세를 적지 않는다
  3. history/changelog.md 에 한 줄 — 날짜 · 무엇을 왜 · 커밋

곁들여 둘

  • 진행 순서를 A → B → C 에서 D → A → B → C 로 고쳤다. D 를 먼저 하라는 말이 D 묶음 안에만 있어서, 공통 규칙만 읽으면 A 부터 시작하게 되어 있었다. 규칙과 본문이 어긋나 있었다.
  • 플랫폼 changelog 경로를 UbisamPlatform/doc/history/changelog.md 로 풀어 적었다. MergeHub 에도 같은 이름의 파일이 생겨서 그냥 doc/history/changelog.md 라고 쓰면 어느 쪽인지 알 수 없다. 두 파일은 기준이 다르다 - 플랫폼은 "다른 소비 프로그램에 미치는 영향", MergeHub 는 "무엇을 왜".

판정 기준은 4차 A~C 를 진행하는 동안 이 규칙대로 쌓이는지로 본다. 지금부터 적용한다.

A-1. 저장하지 않은 편집이 말없이 사라지던 것 — 완료 (2026-09-10)

먼저 조사한 것 — 어느 길로 들어오면 사라졌나

HomeViewModel 에서 본문을 다시 읽는 자리를 전부 훑었다. 열한 군데였고 성격이 셋으로 갈렸다.

길 어떻게 들어오나 판정
OnPlainCompareRequested 폴더 비교 더블클릭 · 폴더 검색 결과 · 끌어다 놓기 · 탐색기 우클릭 · git mergetool 막음
PickLeftFile / PickRightFile 기준 파일 · 내 파일 단추 막음
DropFile 경로 칸에 파일을 놓기 막음
OnSelectedFileChanged Git 탭 파일 목록에서 다른 파일 고르기 막음 (취소하면 목록 선택도 되돌린다)
OnSelectedNodeChanged 이력에서 다른 커밋 고르기 막음 (취소하면 커밋 선택도 되돌린다)
OnRemoteBranchChanged 3-Way 켜고 끄기 막음 (취소하면 브랜치도 되돌린다)
SwitchDemoAsync 데모 폴더 전환 막음
RevertAll 전체 되돌리기 단추 막음 (되살릴 수 없어 한 번 묻는다)
ReloadLinesFrom(keepAsEdited: true) 비교 규칙 변경 · 무시 규칙 변경 안 막음 — 편집을 지키도록 이미 만들어져 있다
LoadDefaultPlainFiles 켤 때 기본 파일 열기 안 막음 — 아직 고친 것이 있을 수 없다
RefreshCommits → LoadCommitAsync 커밋·체크아웃 뒤 목록 다시 세우기 일부러 안 막음 (아래)

비교 규칙을 바꾸는 길에 손대지 않은 것이 중요하다. 지시서가 그렇게 못박았고, 실제로 그 길은 keepAsEdited: true 로 편집을 지킨다. 여기까지 확인 창을 붙였으면 규칙 하나 바꿀 때마다 창이 떠서 못 쓴다.

RefreshCommits 는 일부러 두었다. 사용자가 고른 것이 아니라 프로그램이 목록을 다시 세우는 자리다. 그 한가운데 창을 띄우면 무엇에 답하는 창인지 알 수 없고, 취소해도 목록은 이미 새로 세워진 뒤라 되돌릴 자리가 없다. _suspendGuard 로 그 구간만 묻지 않게 했다. 남은 구멍이라는 것은 인정한다 - 커밋·체크아웃을 한 뒤에는 고치던 것이 다시 읽힌다. 다만 그 조작들은 대개 저장한 뒤에 하는 일이다.

어떻게 막았나

ConfirmDiscard(무엇을 하려는지) 하나로 모았다. 고친 것이 없으면 묻지 않는다 - 매번 뜨는 창은 읽지 않고 누르게 되고, 정작 위험할 때도 그냥 눌러 버린다.

세 갈래로 묻는다. 예/아니오로는 "버리고 가기"와 "그만두기"가 구분되지 않는다 - 아니오를 눌렀는데 그대로 열려 버리면 그것이 곧 잃는 것이다. 플랫폼에 MessageUtil.ShowChoice 를 새로 넣었다(플랫폼 ccfed6f).

저장하고 계속 은 커밋하지 않고 파일에만 쓴다(SaveWorkingCopy). Git 모드의 저장은 커밋 메시지가 있어야 하는데 확인 창에서 메시지를 받을 수는 없다. 커밋은 사용자가 따로 할 일이다. 작업 게이트를 타지 않는다 - 확인 창이 UI 스레드에서 답을 기다리는 중이라 게이트를 기다리면 서로 막힌다. 저장이 실패하면 넘어가지 않는다. 여기서 넘어가면 저장한 줄 알고 잃는다.

곁들여 잡은 것 — 고친 것이 없는데도 묻던 문제

만들어 놓고 화면에서 확인하다 걸렸다. 파일을 새로 열어도 HasChanges 가 꺼지지 않아 앞 파일의 상태가 그대로 끌려왔다. LoadPlainFilesAsync 에 그 줄이 빠져 있었다. 지금까지는 저장 단추가 켜진 채 남는 정도라 눈에 안 띄었는데, A-1 이 그 값을 읽으면서 드러났다.

화면에서 확인한 것

한 일 결과
고친 뒤 다른 파일 열기 Other.cpp 에 저장하지 않은 편집이 있습니다 / 다른 파일을 엽니다: WeldController.cpp
[취소] 편집기 내용 그대로, 파일도 그대로, 열린 파일도 그대로
[저장하고 계속] Other.cpp 에 고친 내용이 쓰이고 WeldController.cpp 가 열렸다
[저장하지 않고 계속] 파일은 손대지 않은 채 다음 파일이 열렸다
고친 것 없이 두 번 연속 열기 창이 뜨지 않았다

검사 514건 통과 · 빌드 경고 0 · 오류 0.

A-2. 내보낸 보고서에 만든 사람이 없던 것 — 완료 (2026-09-10)

왜 - 결과 내보내기 로 뽑은 글에 만든 때 는 있는데 누가 뽑았는지가 없었다. 남에게 보내라고 만든 기능인데 작성자가 없으면 받는 쪽이 되묻게 된다.

이름 판단을 한 함수로 모았다. MergeNote.AuthorOf 를 새로 빼고 MergeNote.Build 도 그것을 쓴다. 설정에 적힌 이름이 없으면 윈도우 계정으로 물러선다. 같은 사람이 같은 화면에서 한 일인데 주석과 보고서에 다른 이름이 남으면 그 자체가 버그다 - 판단이 두 군데로 갈리면 언젠가 어긋난다.

이름이 비어 있으면 줄 자체를 넣지 않는다. 만든 사람: 만 덩그러니 있는 것보다 없는 편이 낫다.

파일 비교 · 폴더 비교 두 보고서에 모두 들어간다. 자리는 만든 때 바로 아래다.

확인한 것

실제로 뽑아 본 파일 머리글이다.

MergeHub 파일 비교 결과
만든 때: 2026-09-10 20:33:17
만든 사람: 김승준
왼쪽  : ...\left\Other.cpp
오른쪽: ...\right\Other.cpp

화면의 머지 주석 미리보기가 // [김승준] 2026-09-10 이었으니 두 이름이 같다.

검사 8건 추가(ReportTest 23 → 31). 전체 522건 통과 · 빌드 경고 0 · 오류 0.

B-1. 최근 비교 목록 — 완료 (2026-09-10)

왜 - 같은 짝을 반복해서 여는데 그때마다 경로를 다시 골라야 했다. 매일 쓰는 사람에게 가장 잦은 마찰이다.

Services/RecentPairs.cs 신설. 화면 없이 검사할 수 있게 기억하는 일만 여기서 한다.

파일 짝과 폴더 짝을 따로 둔다. 한 목록에 섞으면 파일 비교 화면에서 폴더가 나온다. 상한은 15개 - 목록이 길면 고르는 것이 더 느려진다.

저장 형식은 탭으로 나눈 글이다. JSON 을 안 쓴 까닭 - 줄 몇 개가 전부라 라이브러리를 하나 더 매달 값이 없고, 글이면 사람이 열어 보고 손으로 고칠 수도 있다. 윈도우 경로에는 탭이 못 들어가서 나누는 데 문제가 없다. 자리는 D:\UbisamConfig\Settings\MergeHub.Recent.txt 다. 설정 화면에는 안 내놓는다 - 사람이 손으로 고칠 값이 아니고 설정 목록만 어지럽힌다.

켤 때 한 번 읽고 바뀔 때마다 바로 쓴다. 프로그램이 갑자기 꺼져도 목록이 남게 하려는 것이다.

없는 경로를 자동으로 지우지 않는다. 지시서가 그렇게 못박았다 - 네트워크 드라이브나 USB 는 잠깐 안 보일 뿐인데 지워 버리면 다시 찾아 들어가야 한다. 흐리게 보여 주고 열려고 할 때 알린다.

비교가 끝난 짝만 담는다. 읽다 실패했거나 취소한 짝을 남기면 다음에 골랐을 때 또 실패한다.

목록 이름은 양쪽 끝 이름만 쓴다. 전체 경로는 길어서 목록에서 읽히지 않는다. 두 이름이 같으면 하나만 적고(Other.cpp), 다르면 둘 다 적는다(기준.cs ↔ 내작업.cs). 전체 경로는 툴팁에 있다.

여는 길은 다른 데와 같은 RequestPlainCompare 다. 그래서 저장하지 않은 편집도 A-1 에서 함께 걸린다.

화면에서 확인한 것

한 일 결과
파일 비교 · 폴더 비교를 한 번씩 MergeHub.Recent.txt 에 F 줄과 D 줄이 하나씩
프로그램을 껐다 켜기 두 목록이 그대로 남았다
폴더 최근에서 고르기 두 칸이 채워지고 비교가 바로 돌았다 (전체 2개 · 다름 2)
파일 최근에서 고르기 BASE · Weld.cpp / LOCAL · Weld.cpp 로 열렸다
짝의 두 파일을 지운 뒤 목록에 그대로 남고 흐리게 보였다
그 죽은 짝을 고르기 두 파일 모두 찾을 수 없습니다 토스트, 화면은 그대로

검사 33건 추가(RecentTest). 전체 555건 통과 · 빌드 경고 0 · 오류 0.

B-3. 폴더 비교에서 다음 다른 파일로 — 완료 (2026-09-10)

왜 - 파일 하나를 보고 폴더 탭으로 돌아갔다가 다음 파일을 다시 열어야 했다. 다른 파일이 스무 개면 이 왕복을 스무 번 한다.

GitWorkspace 에 훑는 중인 목록(Queue)과 자리(QueueIndex)를 뒀다. 폴더 비교가 채우고 파일 비교가 딛는다 - 두 화면이 서로를 모르는 채로 같은 목록을 봐야 해서 이미 통로 노릇을 하는 이 객체에 뒀다.

원본 결과가 아니라 지금 트리를 훑어 목록을 만든다. 필터로 "코드가 다른 것만" 을 걸어 뒀으면 그 안에서만 움직여야 한다 - 목록에 없는 파일로 넘어가면 왜 거기로 갔는지 알 수 없다.

찾은 줄 목록도 훑을 목록이 된다. 같은 파일이 여러 줄 걸렸으면 한 번만 담는다 - 다음 파일을 눌렀는데 같은 파일이 다시 열리면 멈춘 것처럼 보인다.

최근 목록으로 연 파일은 목록을 비운다. 남아 있던 목록을 그대로 두면 다음 파일이 엉뚱한 데로 간다.

먼저 묻고 그 다음에 자리를 옮긴다. 확인 창에서 취소하면 자리도 그대로여야 하는데, 자리를 먼저 옮기면 취소했을 때 목록 위치만 앞서 나간다. 물은 뒤에는 두 번 묻지 않도록 막는다.

단축키는 Ctrl+PageUp · Ctrl+PageDown 이다. Alt+PageDown(북마크)은 그대로 두었고 검사로 빼앗지 않았음을 확인한다.

화면에서 확인한 것 (파일 네 개 · 하나는 하위 폴더)

한 일 자리 열린 파일 이전/다음
검색 결과로 열기 1 / 4 A.cpp 끔 / 켬
다음 파일 ×3 2 → 3 → 4 B.cpp → C.cpp → D.cpp 마지막에서 다음이 꺼짐
이전 파일 ×2 3 → 2 C.cpp → B.cpp 둘 다 켬
고친 뒤 다음 파일 → 취소 2 / 4 그대로 B.cpp 그대로 편집 내용도 그대로
다시 눌러 버리고 계속 3 / 4 C.cpp B.cpp 파일은 손대지 않음

검사 5건 추가(ShortcutTest 63 → 68). 전체 560건 통과 · 빌드 경고 0 · 오류 0.

C-1. 같은 경로가 세 군데 나오던 것 — 완료 (2026-09-10)

무엇이 문제였나 - 같은 경로가 셋에 있었다. ① 위 툴바의 잘린 경로 ② 패널 머리글 BASE · A.cpp ③ 그 아래 경로 상자의 전체 경로.

①은 260px 안에서 잘려 읽을 수 없었고, ③은 패널마다 세로 40px 남짓을 먹었다.

둘을 없앴다. 전체 경로는 머리글 툴팁으로 옮겼다.

복사하는 방법은 지켰다. ③이 읽기전용 TextBox 였던 까닭이 드래그 복사다. 줄을 없애면서 복사까지 없어지면 안 되므로 머리글 우클릭 → 전체 경로 복사 로 옮겼다. 클립보드가 다른 프로그램에 잡혀 있으면 실패하므로 그때는 알리고 넘어간다.

놓는 자리는 단추로 옮겼다. ①이 3차 D-7 의 슬롯 드롭 자리였다. 없애면 그 기능이 사라지므로 기준 파일... · 내 파일... 단추가 받게 했다. 잘린 글자보다 단추가 겨냥하기 쉽다. 단추 툴팁에 전체 경로를 걸어 두어 지금 무엇이 물려 있는지도 그대로 보인다.

행 잠금은 그대로다. 양쪽 패널에서 같은 줄을 없앴고 머리 높이는 SharedSizeGroup 이 묶고 있다. 3-Way 에서는 ③이 이미 접혀 있었으므로 달라지는 것이 없다.

화면에서 확인한 것

확인 결과
코드가 시작하는 자리 머리글 바로 아래로 올라왔다 (2560×1600 에서 약 58px, 코드 두 줄)
두 패널 01행 높이 같다
머리글 툴팁 BASE · A.cpp → …\left\A.cpp / LOCAL · A.cpp → …\right\A.cpp
단추 툴팁 각각 지금 물린 전체 경로
머리글 우클릭 → 전체 경로 복사 클립보드에 전체 경로가 들어왔다
탐색기에서 기준 파일... 단추에 놓기 BASE · C.cpp 로 바뀌고 LOCAL 은 그대로

검사 560건 통과 · 빌드 경고 0 · 오류 0.

C-1 다시 — 파일 이름만 남긴 것이 잘못이었다 (2026-09-10)

사용자 지적 - "파일 비교는 서로 다른 경로에 있는 파일을 보는 것 아니냐. 경로가 같으면 같은 파일일 텐데 왜 경로를 지웠느냐."

맞는 지적이다. 처음 고칠 때 머리글을 BASE · {파일 이름} 으로 줄였는데, 파일 비교에서 이름이 같은 경우가 오히려 흔하다(같은 파일의 두 판을 견주는 것이 주된 쓰임이다). 그러면 양쪽 머리글이 BASE · Weld.cpp / LOCAL · Weld.cpp 로 똑같아져 어느 쪽이 어느 쪽인지 알 수 없다. 실제로 B-1 확인 때 그 화면이 나왔는데 그때는 못 알아봤다. 툴팁에 전체 경로가 있긴 하지만 양쪽에 하나씩 올려 보고 머릿속에서 견줘야 한다.

전체 경로도 아니고 파일 이름도 아닌, 다른 부분만 적는다. PathUtil.DistinctTail 을 새로 두어 같은 앞부분만 떼고 나머지를 보여 준다.

D:\work\trunk\src\a.cpp   →  trunk\src\a.cpp
D:\work\branch\src\a.cpp  →  branch\src\a.cpp

파일 이름 칸은 언제나 남긴다. 드라이브가 다르거나 겹치는 앞부분이 없으면 전체 경로를 그대로 준다. 대소문자와 슬래시 방향은 같게 본다.

확인 - 이름이 같은 두 파일을 열었더니 BASE · left\A.cpp / LOCAL · right\A.cpp 로 갈렸다. 검사 11건 추가(PathTest 43 → 54). 전체 571건 통과.

남길 것 - 화면을 줄이는 일은 무엇이 남아야 하는지부터 정하고 줄여야 한다. 여기서는 "경로가 길다"만 보고 줄였는데, 정작 그 경로가 두 칸을 가르는 유일한 정보였다.

C-1 또 한 번 — 경로를 읽으라고 적는다 (2026-09-10)

사용자 지적 - "경로를 좀 잘 보이게 해볼까."

머리글의 위아래가 뒤집혀 있었다. 늘 똑같은 BASE 는 크고 파란데, 정작 이 칸이 무엇인지 말하는 경로는 회색 작은 글씨였다. 정보를 담은 쪽이 뒤로 물러나 있었다.

세 가지를 바꿨다.

  • 경로 줄에서 BASE · LOCAL · 접두사를 뺐다. 위 제목이 이미 그 말을 하고 있다.
  • 회색(Muted) 을 걷고 본문 색으로, 11 → 12px, 글꼴은 코드와 같은 Consolas 로. 경로는 기호가 많아 고정폭이 읽기 쉽다.
  • 갈리는 첫 칸만 굵은 강조색으로 칠한다(left\ · right\). 나머지는 양쪽이 같은 글자라 읽을 값이 없다. 어디가 다른지 눈이 바로 간다.
BASE                          LOCAL
left\WeldController.cpp       right\WeldController.cpp
^^^^                          ^^^^^  (굵은 강조색)

Git 모드도 같이 정리됐다 - BASE / 9c9e0bb · StartWeld.cs, LOCAL / master.

걸린 것 하나 - SplitLead 의 튜플 요소 이름을 Rest 로 뒀다가 CS8126 이 났다. Rest 는 ValueTuple 이 제 필드 이름으로 쓰는 예약어다. Tail 로 바꿨다.

검사 571건 통과 · 빌드 경고 0 · 오류 0.

C-2. 아래쪽 정보가 세 군데로 흩어져 있던 것 — 완료 (2026-09-10)

무엇이 문제였나 - 같은 성격의 정보가 세 자리에 나뉘어 있었다.

BASE 바닥      Diff 요약 / 총 43 라인 · 차이 10 라인
MERGED 바닥①   1행 1열 | 차이 - / 5 | UTF-8 · CRLF · C#
MERGED 바닥②   편집 0건 · 되돌리기 0건 · 남은 차이 10 라인 | 코드 검사 이상 없음

같은 뜻의 숫자가 세 벌이었다 - 차이 10 라인 · 남은 차이 10 라인 · 차이 - / 5. 어느 것을 봐야 할지 헷갈리고, 패널마다 54px 를 먹었다.

패널 안이 아니라 창 아래 한 줄로 옮겼다. 자리 규칙을 정했다.

자리 무엇
왼쪽 지금 어디에 있는지 — 1행 1열 · 차이 3 / 5
가운데 얼마나 남았는지 — 총 34줄 · 남은 차이 5줄 (고친 것이 있으면 편집·되돌리기가 붙는다)
오른쪽 이 파일이 무엇인지 — UTF-8 · CRLF · C# · 비교 규칙 · 코드 검사

곁들여 얻은 것 - 상태바가 패널 밖으로 나오면서 두 패널의 코드 영역 높이가 저절로 같아진다. 전에는 BASE 와 MERGED 의 바닥 높이를 손으로 맞춰 두어야 했다(PaneFooterHeight 상수가 그 흔적이다). 3-Way 에서도 네 칸이 같은 자리에 선다.

숫자를 하나씩만 남겼다. 총 N줄 은 파일 크기, 남은 차이 N줄 은 남은 일이다. 편집·되돌리기 건수는 0건일 때는 아예 적지 않는다 - 늘 0건 0건 이 붙어 있으면 눈이 그냥 지나친다.

화면에서 확인한 것

확인 결과
2-Way 1행 1열 │ 차이 -/2 │ 총 8줄 · 남은 차이 2줄 … UTF-8 · CRLF · C/C++ │ 코드 검사 이상 없음
3-Way 같은 한 줄, 총 34줄 · 남은 차이 5줄 · 코드 검사 확인 필요 1건
두 패널 코드 높이 같다. 3-Way 네 칸도 같은 자리에 선다
같은 숫자 두 번 없다

검사 571건 통과 · 빌드 경고 0 · 오류 0.

곁들여. 엔터를 누르면 한참 뒤에 반응하던 것 — 완료 (2026-09-10)

사용자 지적 - "줄바꿈 엔터를 누르면 딜레이가 있어, 꽤 길게 체감된다."

원인 - 화면에 보이는 글자는 편집기가 아니라 아래 표시층이 그린다(편집기 글자는 투명이다). 글자를 칠 때는 ShowTypedLineNow 가 그 줄만 즉시 표시층에 옮겨 준다. 그런데 줄 수가 달라지면 (엔터 · 줄 합치기 · 여러 줄 붙여넣기) 아래 줄이 통째로 밀린 것이라 한 줄만 고쳐서는 맞지 않아 그 함수가 손을 뗀다. 그러면 남는 것은 0.6초짜리 재정렬 타이머뿐이고, 그 동안 화면에는 아무것도 나오지 않는다. 글자는 즉시 나오는데 엔터만 느렸던 까닭이다.

고친 방법 - ShowTypedLineNow 가 맞췄는지 여부를 돌려주게 하고, 못 맞춘 경우에는 타이머를 기다리지 않고 그 자리에서 재정렬한다. 빠른 길이 통하는 보통 타이핑은 지금까지처럼 타이머로 미룬다.

재 본 값 (SendKeys 왕복 시간. 이 도구 자체가 한 번에 31ms 쯤 쓴다)

상황 한 번당
200줄 파일 · 엔터 31 ms
2000줄 파일 · 엔터 32 ms
글자 치기(견줄 값) 31 ms

엔터가 글자 한 자와 구별되지 않는 수준이 됐다. 예전에는 0.6초 동안 아무것도 안 나왔다. 2000줄에서도 200줄과 같은 값이 나오는 것을 보면 재정렬 자체는 이 크기에서 문제가 아니다.

화면에서도 확인했다 - 엔터로 넣은 빈 줄이 양쪽 칸에 곧바로 나타나고 정렬이 어긋나지 않는다.

엔터 고침이 만든 되돌이 문제 — 고침 (2026-09-10)

사용자 지적 - "엔터 줄바꿈하고 글자가 이상하게 찍히고 커서 포인트 줄도 안 맞아."

내가 만든 문제였다. 바로 위에서 "그 자리에서 재정렬" 하도록 고쳤는데, 그 자리가 편집기의 TextChanged 이벤트 안이었다. 재정렬은 편집기 글을 통째로 갈아 끼우고 캐럿 자리도 다시 잡는다(OnEditorTextReplaced). 자기 이벤트 안에서 자기 글을 바꾸는 셈이라 캐럿이 엉키고 뒤따라오던 글자가 엉뚱한 자리에 찍혔다.

고친 방법 - 부르는 자리를 한 차례 뒤로 물렸다. 타이머 간격을 상황에 따라 나눈다.

상황 간격 왜
보통 타이핑 600ms 글자마다 다시 정렬하면 화면이 요동친다
줄 수가 바뀜 1ms 늦추려는 것이 아니라 이 입력이 끝난 다음 차례에 돌게 하려는 것

0 이 아니라 1ms 인 까닭이 그것이다. 값이 아니라 차례가 목적이다.

다시 확인한 것

확인 결과
2행 끝에서 엔터 → added 입력 3행에 정확히 added, 캐럿 3행 6열
양쪽 칸 정렬 BASE 01·02 · 빈 줄 · 03·04 / LOCAL 01·02·03·04·05 로 줄이 맞는다
엔터 20번 622ms (한 번당 31ms) — 글자 치기와 같은 값 유지

남길 것 - 화면 반응을 앞당길 때 어느 이벤트 안에서 부르는지를 먼저 봐야 한다. "바로 부른다"가 "지금 이 자리에서 부른다"가 되면 되돌이가 생긴다.