D-2, D-5: C++ 전처리 지시문 · 3-Way 덩어리 반영

D-2. C++ 문법 강조는 이미 되고 있었다. LanguageProfile.CFamily 가 확장자
아홉 가지를 맡고 예약어/주석/문자열도 들어 있다. 빠진 것은 전처리 지시문
하나였다. #include 가 # 과 include 로 쪼개져 보통 낱말처럼 칠해졌다. 줄
첫머리가 # 이면 그 줄 전체를 지시문 색으로 칠한다. C# 쪽과 같은 규칙이다.
언어표에 hasPreprocessor 를 두어 C 계열에서만 켠다.

D-5. 2-Way 는 이미 됐다. 3-Way 가 잘못 동작하고 있었다. 덩어리를 BASE 에서
가져오고 있었는데 3-Way 에서 BASE 는 공통 조상이다. 저쪽(REMOTE)을 가져오는
것이 Take Theirs 이고 덩어리 반영은 그것을 줄 묶음으로 하는 것인데, BASE 를
가져오면 조상으로 되돌리는 셈이라 뜻이 전혀 달라진다. 덩어리를 묶는 기준도
모드에 맞췄다.

확인: 충돌 6개짜리 C++ 파일에서 다음 충돌 -> 이 덩어리 반영 한 번에 네 줄이
REMOTE 값으로 바뀌고 Conflict 6 -> 2. LangTest 12건 추가. 전체 424건 통과.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
김승준 2026-09-09 14:22:55 +09:00
parent 4f33cacd4f
commit 5c8c942ba5
4 changed files with 82 additions and 11 deletions

View File

@ -127,10 +127,10 @@ CommunityToolkit 커맨드는 CommandManager 를 구독하지 않는데 이 둘
| 상태 | 항목 |
|---|---|
| 대기 | D-1 주석만 다른 파일을 다른 색으로 |
| 대기 | D-2 C++ 문법 강조 |
| **완료** | D-2 C++ 문법 강조 (이미 되고 있었음 · 전처리 지시문만 추가) |
| 대기 | D-3 폴더 안 전체 검색 |
| ~~취소~~ | ~~D-4 비교 창 여러 개~~ (2026-09-09 사용자 판단으로 뺌) |
| 대기 | D-5 덩어리 단위 반영 (2-Way 는 이미 됨 · 3-Way 확인 필요) |
| **완료** | D-5 덩어리 단위 반영 (3-Way 에서 BASE 를 가져오던 것을 REMOTE 로) |
| 대기 | D-6 비교 결과 텍스트 내보내기 |
| 대기 | D-7 드래그 앤 드롭으로 열기 |
@ -147,6 +147,35 @@ CommunityToolkit 커맨드는 CommandManager 를 구독하지 않는데 이 둘
---
### D-2 · D-5 — **완료** (2026-09-09)
#### D-2. C++ 문법 강조
**이미 되고 있었다.** `LanguageProfile.CFamily` 가 `.c .h .cpp .hpp .cc .hh .cxx .hxx .inl` 을
모두 맡고 있고 예약어·주석·문자열도 들어 있다. 지시서에서 확인만 하라고 적어 둔 대로 확인했다.
**빠진 것은 전처리 지시문 하나였다.** `#include` 는 `#` 과 `include` 로 쪼개져 보통 낱말처럼
칠해졌다. 줄 첫머리가 `#` 이면 그 줄 전체를 지시문 색으로 칠하게 했다 - C# 쪽과 같은 규칙이다.
같은 모양인데 언어마다 다르게 보이면 오히려 헷갈린다.
언어표에 `hasPreprocessor` 를 두어 <b>C 계열에서만</b> 켠다. 자바·자바스크립트는 `#` 이 지시문이 아니다.
**검사**: `LangTest` 12건 추가(40 → 52건).
#### D-5. 덩어리 단위 반영
**2-Way 는 이미 됐다**(`이 덩어리 반영` · `Alt+Shift+→`). 확인했다.
**3-Way 가 잘못 동작하고 있었다.** 덩어리를 BASE 에서 가져오고 있었는데, 3-Way 에서 BASE 는
<b>공통 조상</b>이다. 저쪽(REMOTE)을 가져오는 것이 `Take Theirs` 이고 덩어리 반영은 그것을
줄 묶음으로 하는 것인데, BASE 를 가져오면 <b>조상으로 되돌리는 셈</b>이라 뜻이 전혀 달라진다.
덩어리를 묶는 기준도 같이 바꿨다 - 2-Way 는 "BASE 를 가져올 수 있는 줄", 3-Way 는
"저쪽을 가져올 수 있는 줄"이 이어진 구간이다.
**확인**: 충돌 6개짜리 C++ 파일에서 `다음 충돌` → `이 덩어리 반영` 한 번에
네 줄이 REMOTE 값으로 바뀌고 `Conflict 6 → 2`, `머지 반영함 4`.
단축키(`Alt+Shift+→`)도 같은 명령을 부르므로 함께 고쳐졌다.
---
### C. 잔손질 — **완료** (2026-09-09)
#### C-1. 코드 위 툴팁 제거

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using MergeHub.Views.Controls;
@ -46,6 +46,23 @@ namespace MergeHub.Services
stillInBlockComment = false;
}
// ---- 전처리 지시문 ----
//
// 줄 첫머리가 <c>#</c> 이면 그 줄 전체가 지시문이다(#include · #define · #ifdef).
// C# 쪽과 같은 규칙으로 칠한다 - 같은 모양인데 언어마다 다르게 보이면 더 헷갈린다.
// 블록 주석이 이미 열려 있던 줄은 해당 없다(i 가 0 이 아니다).
if (profile.HasPreprocessor && i == 0)
{
var firstVisible = 0;
while (firstVisible < line.Length && char.IsWhiteSpace(line[firstVisible])) firstVisible++;
if (firstVisible < line.Length && line[firstVisible] == '#')
{
tokens.Add(new CodeToken(firstVisible, line.Length - firstVisible, CodeTokenKind.Preprocessor));
return tokens;
}
}
while (i < line.Length)
{
var start = i;

View File

@ -1,4 +1,4 @@
using System;
using System;
using System.Collections.Generic;
using System.IO;
@ -29,7 +29,8 @@ namespace MergeHub.Services
bool caseSensitive = true,
bool isCode = true,
bool hasBraces = false,
bool useCSharpTokenizer = false)
bool useCSharpTokenizer = false,
bool hasPreprocessor = false)
{
Name = name;
Extensions = extensions;
@ -41,6 +42,7 @@ namespace MergeHub.Services
IsCode = isCode;
HasBraces = hasBraces;
UseCSharpTokenizer = useCSharpTokenizer;
HasPreprocessor = hasPreprocessor;
var comparer = caseSensitive ? StringComparer.Ordinal : StringComparer.OrdinalIgnoreCase;
Keywords = new HashSet<string>(keywords ?? Array.Empty<string>(), comparer);
@ -56,6 +58,12 @@ namespace MergeHub.Services
public char[] Quotes { get; }
public bool CaseSensitive { get; }
/// <summary>
/// <c>#include</c> · <c>#define</c> 처럼 <b>줄 첫머리의 # 지시문</b>을 쓰는 언어인지.
/// C 계열이 그렇다. 켜 두면 그 줄 전체를 지시문 색으로 칠한다 - C# 과 같은 규칙이다.
/// </summary>
public bool HasPreprocessor { get; }
/// <summary>문법 색을 입힐 대상인지. 평문·표 형식 문서는 false 다.</summary>
public bool IsCode { get; }
@ -111,7 +119,8 @@ namespace MergeHub.Services
"inline", "operator", "new", "delete", "this", "nullptr", "true", "false",
"using", "typedef", "sizeof", "friend", "explicit", "mutable", "constexpr"
},
controlKeywords: CFamilyControl, typeKeywords: CFamilyTypes, hasBraces: true);
controlKeywords: CFamilyControl, typeKeywords: CFamilyTypes, hasBraces: true,
hasPreprocessor: true);
public static readonly LanguageProfile Java = new LanguageProfile(
"Java", new[] { ".java" },

View File

@ -2193,24 +2193,40 @@ namespace MergeHub.ViewModels
var block = BlockAround(CaretRow);
if (block.Count == 0) return;
foreach (var line in block) Apply(line, requery: false);
// 2-Way 는 BASE 를, 3-Way 는 저쪽(REMOTE)을 가져온다. BlockAround 주석 참고.
foreach (var line in block)
{
if (IsThreeWay) ApplyRemote(line, requery: false);
else Apply(line, requery: false);
}
AfterEdit();
MessageUtil.ShowSuccessToast($"{block.Count}줄 반영");
}
/// <summary>그 줄이 속한 차이 덩어리. 차이가 아니면 빈 목록이다.</summary>
/// <summary>
/// 그 줄이 속한 차이 덩어리. 차이가 아니면 빈 목록이다.
///
/// <b>무엇을 가져오는지가 모드마다 다르다.</b>
/// 2-Way : BASE 를 결과로 가져온다. 덩어리는 "BASE 를 가져올 수 있는 줄"이 이어진 구간.
/// 3-Way : BASE 는 공통 조상이라 가져올 것이 아니다. 저쪽(REMOTE)을 가져오는 것이
/// <c>Take Theirs</c> 이고, 덩어리 반영은 그것을 줄 묶음으로 하는 것이다.
/// 여기서 BASE 를 가져오면 <b>조상으로 되돌리는 셈</b>이라 뜻이 전혀 달라진다.
/// </summary>
private List<MergeLine> BlockAround(int row)
{
var found = new List<MergeLine>();
if (row < 0 || row >= Lines.Count) return found;
if (!Lines[row].CanTakeBase) return found;
bool Takeable(MergeLine line) => IsThreeWay ? line.CanTakeRemote : line.CanTakeBase;
if (!Takeable(Lines[row])) return found;
var first = row;
while (first > 0 && Lines[first - 1].CanTakeBase) first--;
while (first > 0 && Takeable(Lines[first - 1])) first--;
var last = row;
while (last + 1 < Lines.Count && Lines[last + 1].CanTakeBase) last++;
while (last + 1 < Lines.Count && Takeable(Lines[last + 1])) last++;
for (var i = first; i <= last; i++) found.Add(Lines[i]);
return found;