2 * @file MergeDocDiffSync.cpp
4 * @brief Code to layout diff blocks, to find matching lines, and insert ghost lines
14 #include "stringdiffs.h"
23 ValidateDiffMap(const DiffMap& diffmap)
25 for (const auto& line : diffmap.m_map)
26 assert(line != DiffMap::BAD_MAP_ENTRY);
31 ValidateVirtualLineToRealLineMap(
32 const std::vector<std::array<int, npanes>>& vlines,
33 const std::array<int, npanes>& nlines)
36 for (const auto& v : vlines)
38 for (int pane = 0; pane < npanes; ++pane)
40 if (v[pane] != DiffMap::GHOST_MAP_ENTRY)
42 assert(line[pane] == v[pane]);
47 for (int pane = 0; pane < npanes; ++pane)
48 assert(line[pane] == nlines[pane]);
53 PrintVirtualLineToRealLineMap(
55 const std::vector<std::array<int, npanes>>& vlines)
57 OutputDebugString((_T("[") + name + _T("]\n")).c_str());
58 for (size_t i = 0; i < vlines.size(); ++i)
60 String str = strutils::format(_T("vline%d: "), static_cast<int>(i));
61 std::vector<String> ary;
62 for (int j = 0; j < npanes; ++j)
63 ary.push_back(vlines[i][j] == DiffMap::GHOST_MAP_ENTRY ? _T("-----") : strutils::format(_T("%5d"), vlines[i][j]));
64 str += strutils::join(ary.begin(), ary.end(), _T(",")) + _T("\n");
65 OutputDebugString(str.c_str());
70 * @brief Create map from virtual line to real line.
72 static std::vector<std::array<int, 2>>
73 CreateVirtualLineToRealLineMap(
74 const DiffMap& diffmap, int nlines0, int nlines1)
76 std::vector<std::array<int, 2>> vlines;
77 int line0 = 0, line1 = 0;
78 while (line0 < nlines0)
80 const int map_line0 = diffmap.m_map[line0];
81 if (map_line0 == DiffMap::GHOST_MAP_ENTRY || map_line0 == DiffMap::BAD_MAP_ENTRY)
83 vlines.push_back({ line0++, DiffMap::GHOST_MAP_ENTRY });
87 while (line1 < map_line0)
88 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, line1++ });
89 vlines.push_back({ line0++, line1++ });
92 while (line1 < nlines1)
93 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, line1++ });
94 ValidateVirtualLineToRealLineMap(vlines, std::array<int, 2>{ nlines0, nlines1 });
96 PrintVirtualLineToRealLineMap(_T("vline"), vlines);
102 * @brief Create map from virtual line to real line. (3-way)
104 static std::vector<std::array<int, 3>>
105 CreateVirtualLineToRealLineMap3way(
106 const DiffMap& diffmap01, const DiffMap& diffmap12, const DiffMap& diffmap20,
107 int nlines0, int nlines1, int nlines2)
109 std::vector<std::array<int, 2>> vlines01 = CreateVirtualLineToRealLineMap(diffmap01, nlines0, nlines1);
110 std::vector<std::array<int, 2>> vlines12 = CreateVirtualLineToRealLineMap(diffmap12, nlines1, nlines2);
111 std::vector<std::array<int, 2>> vlines20 = CreateVirtualLineToRealLineMap(diffmap20, nlines2, nlines0);
112 std::vector<std::array<int, 3>> vlines;
113 size_t i01 = 0, i12 = 0, i20 = 0;
114 int line0 = 0, line1 = 0, line2 = 0;
115 bool is_vlines20_usable = true;
117 for (line1 = 0; line1 < nlines1; ++line1)
123 for (; i01 < vlines01.size(); ++i01)
124 if (vlines01[i01][1] == line1)
126 for (; i12 < vlines12.size(); ++i12)
127 if (vlines12[i12][0] == line1)
129 assert(i01 < vlines01.size() && i12 < vlines12.size());
131 bool used_vlines20 = false;
132 if (is_vlines20_usable)
134 if (vlines12[i12][1] != DiffMap::GHOST_MAP_ENTRY && vlines01[i01][0] != DiffMap::GHOST_MAP_ENTRY)
137 line2 = vlines12[i12][1];
138 line0 = vlines01[i01][0];
140 for (i20tmp = i20b; i20tmp < vlines20.size(); ++i20tmp)
141 if (vlines20[i20tmp][0] == line2 && vlines20[i20tmp][1] == line0)
143 if (i20tmp < vlines20.size())
146 for (; i20 < i20tmp; ++i20)
147 vlines.push_back({ vlines20[i20][1], DiffMap::GHOST_MAP_ENTRY, vlines20[i20][0] });
149 used_vlines20 = true;
154 is_vlines20_usable = false;
160 is_vlines20_usable = false;
166 size_t i01tmp, i12tmp;
167 for (i01tmp = i01b, i12tmp = i12b; i01tmp < i01 && i12tmp < i12; ++i01tmp, ++i12tmp)
168 vlines.push_back({ vlines01[i01tmp][0], DiffMap::GHOST_MAP_ENTRY, vlines12[i12tmp][1] });
169 if (i01 - i01b < i12 - i12b)
172 for (; i12tmp < i12; ++i12tmp)
173 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY, vlines12[i12tmp][1] });
175 else if (i01 - i01b > i12 - i12b)
178 for (; i01tmp < i01; ++i01tmp)
179 vlines.push_back({ vlines01[i01tmp][0], DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY });
182 vlines.push_back({ vlines01[i01++][0], line1, vlines12[i12++][1]});
185 if (i01 < vlines01.size() || i12 < vlines12.size())
187 if (is_vlines20_usable)
190 for (; i20 < vlines20.size(); ++i20)
191 vlines.push_back({ vlines20[i20][1], DiffMap::GHOST_MAP_ENTRY, vlines20[i20][0] });
196 for (; i01 < vlines01.size() && i12 < vlines12.size(); ++i01, ++i12)
197 vlines.push_back({ vlines01[i01][0], DiffMap::GHOST_MAP_ENTRY, vlines12[i12][1] });
198 if (vlines01.size() - i01 < vlines12.size() - i12)
201 for (; i12 < vlines12.size(); ++i12)
202 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY, vlines12[i12][1] });
204 else if (vlines01.size() - i01 > vlines12.size() - i12)
207 for (; i01 < vlines01.size(); ++i01)
208 vlines.push_back({ vlines01[i01][0], DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY });
212 ValidateVirtualLineToRealLineMap(vlines, std::array<int, 3>{ nlines0, nlines1, nlines2 });
214 PrintVirtualLineToRealLineMap(_T("vline01"), vlines01);
215 PrintVirtualLineToRealLineMap(_T("vline12"), vlines12);
216 PrintVirtualLineToRealLineMap(_T("vline20"), vlines20);
217 PrintVirtualLineToRealLineMap(_T("vline3way"), vlines);
222 OP_TYPE CMergeDoc::ComputeOpType3way(
223 const std::vector<std::array<int, 3>>& vlines, size_t index,
224 const DIFFRANGE& diffrange, const DIFFOPTIONS& diffOptions)
226 if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
227 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
228 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
232 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
233 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
234 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
238 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
239 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
240 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
245 int line0 = diffrange.begin[0] + vlines[index][0];
246 int line1 = diffrange.begin[1] + vlines[index][1];
247 int line2 = diffrange.begin[2] + vlines[index][2];
249 if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
250 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
251 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
253 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
254 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
255 if (strdiff::Compare(strLine0, strLine1,
256 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
257 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
261 else if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
262 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
263 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
265 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
266 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
267 if (strdiff::Compare(strLine0, strLine2,
268 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
269 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
273 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
274 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
275 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
277 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
278 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
279 if (strdiff::Compare(strLine1, strLine2,
280 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
281 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
287 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
288 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
289 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
290 if (strdiff::Compare(strLine0, strLine1,
291 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
292 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
294 if (strdiff::Compare(strLine0, strLine2,
295 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
296 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
298 if (strdiff::Compare(strLine1, strLine2,
299 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
300 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
307 * @brief Divide diff blocks to match lines in diff blocks.
309 void CMergeDoc::AdjustDiffBlocks()
312 int nDiffCount = m_diffList.GetSize();
314 // Go through and do our best to line up lines within each diff block
315 // between left side and right side
316 DiffList newDiffList;
318 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
320 const DIFFRANGE & diffrange = *m_diffList.DiffRangeAt(nDiff);
321 // size map correctly (it will hold one entry for each left-side line
322 int nlines0 = diffrange.end[0] - diffrange.begin[0] + 1;
323 int nlines1 = diffrange.end[1] - diffrange.begin[1] + 1;
324 if (nlines0>0 && nlines1>0)
326 // Call worker to do all lines in block
327 int lo0 = 0, hi0 = nlines0-1;
328 int lo1 = 0, hi1 = nlines1-1;
329 const std::vector<WordDiff> worddiffs = GetWordDiffArrayInRange(diffrange.begin, diffrange.end);
331 diffmap.InitDiffMap(nlines0);
332 AdjustDiffBlock(diffmap, diffrange, worddiffs, 0, 1, lo0, hi0, lo1, hi1);
333 ValidateDiffMap(diffmap);
334 std::vector<std::array<int, 2>> vlines = CreateVirtualLineToRealLineMap(diffmap, nlines0, nlines1);
336 // divide diff blocks
337 int line0 = 0, line1 = 0;
338 for (size_t i = 0; i < vlines.size();)
342 if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
343 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
345 while (i < vlines.size() &&
346 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
347 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
353 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
354 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
355 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
356 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
357 dr.blank[0] = dr.blank[1] = -1;
358 dr.op = diffrange.op;
359 newDiffList.AddDiff(dr);
361 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
362 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
364 while (i < vlines.size() &&
365 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
366 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
371 dr.begin[0] = diffrange.begin[0] + line0;
372 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
373 dr.end[0] = diffrange.begin[0] + line0 - 1;
374 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
375 dr.blank[0] = dr.blank[1] = -1;
376 dr.op = diffrange.op;
377 newDiffList.AddDiff(dr);
379 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
380 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY))
382 while (i < vlines.size() &&
383 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
384 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY))
389 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
390 dr.begin[1] = diffrange.begin[1] + line1;
391 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
392 dr.end[1] = diffrange.begin[1] + line1 - 1;
393 dr.blank[0] = dr.blank[1] = -1;
394 dr.op = diffrange.op;
395 newDiffList.AddDiff(dr);
405 newDiffList.AddDiff(diffrange);
409 // recreate m_diffList
411 nDiffCount = newDiffList.GetSize();
412 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
413 m_diffList.AddDiff(*newDiffList.DiffRangeAt(nDiff));
417 * @brief Divide diff blocks to match lines in diff blocks. (3-way)
419 void CMergeDoc::AdjustDiffBlocks3way()
422 int nDiffCount = m_diffList.GetSize();
424 // Go through and do our best to line up lines within each diff block
425 // between left side and right side
426 DiffList newDiffList;
428 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
430 const DIFFRANGE & diffrange = *m_diffList.DiffRangeAt(nDiff);
431 // size map correctly (it will hold one entry for each left-side line
432 int nlines0 = diffrange.end[0] - diffrange.begin[0] + 1;
433 int nlines1 = diffrange.end[1] - diffrange.begin[1] + 1;
434 int nlines2 = diffrange.end[2] - diffrange.begin[2] + 1;
435 if ((nlines0 > 0) + (nlines1 > 0) + (nlines2 > 0) > 1)
437 // Call worker to do all lines in block
438 int lo0 = 0, hi0 = nlines0 - 1;
439 int lo1 = 0, hi1 = nlines1 - 1;
440 int lo2 = 0, hi2 = nlines2 - 1;
441 const std::vector<WordDiff> worddiffs01 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 0, 1);
442 const std::vector<WordDiff> worddiffs12 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 1, 2);
443 const std::vector<WordDiff> worddiffs20 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 2, 0);
444 DiffMap diffmap01, diffmap12, diffmap20;
445 diffmap01.InitDiffMap(nlines0);
446 diffmap12.InitDiffMap(nlines1);
447 diffmap20.InitDiffMap(nlines2);
448 AdjustDiffBlock(diffmap01, diffrange, worddiffs01, 0, 1, lo0, hi0, lo1, hi1);
449 AdjustDiffBlock(diffmap12, diffrange, worddiffs12, 1, 2, lo1, hi1, lo2, hi2);
450 AdjustDiffBlock(diffmap20, diffrange, worddiffs20, 2, 0, lo2, hi2, lo0, hi0);
451 ValidateDiffMap(diffmap01);
452 ValidateDiffMap(diffmap12);
453 ValidateDiffMap(diffmap20);
454 std::vector<std::array<int, 3>> vlines = CreateVirtualLineToRealLineMap3way(diffmap01, diffmap12, diffmap20, nlines0, nlines1, nlines2);
456 DIFFOPTIONS diffOptions = {0};
457 m_diffWrapper.GetOptions(&diffOptions);
458 std::vector<OP_TYPE> opary(vlines.size());
459 for (size_t i = 0; i < vlines.size(); ++i)
460 opary[i] = (diffrange.op == OP_TRIVIAL) ?
462 ComputeOpType3way(vlines, i, diffrange, diffOptions);
463 // divide diff blocks
464 int line0 = 0, line1 = 0, line2 = 0;
465 for (size_t i = 0; i < vlines.size();)
469 OP_TYPE op = opary[i];
470 if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
471 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
472 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
474 while (i < vlines.size() &&
475 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
476 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
477 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
485 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
486 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
487 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
488 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
489 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
490 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
491 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
493 newDiffList.AddDiff(dr);
495 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
496 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
497 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
499 while (i < vlines.size() &&
500 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
501 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
502 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
509 dr.begin[0] = diffrange.begin[0] + line0;
510 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
511 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
512 dr.end[0] = diffrange.begin[0] + line0 - 1;
513 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
514 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
515 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
517 newDiffList.AddDiff(dr);
519 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
520 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
521 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
523 while (i < vlines.size() &&
524 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
525 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
526 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
533 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
534 dr.begin[1] = diffrange.begin[1] + line1;
535 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
536 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
537 dr.end[1] = diffrange.begin[1] + line1 - 1;
538 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
539 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
541 newDiffList.AddDiff(dr);
543 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
544 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
545 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
547 while (i < vlines.size() &&
548 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
549 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
550 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
557 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
558 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
559 dr.begin[2] = diffrange.begin[2] + line2;
560 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
561 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
562 dr.end[2] = diffrange.begin[2] + line2 - 1;
563 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
565 newDiffList.AddDiff(dr);
567 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
568 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
569 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
571 while (i < vlines.size() &&
572 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
573 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
574 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
580 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
581 dr.begin[1] = diffrange.begin[1] + line1;
582 dr.begin[2] = diffrange.begin[2] + line2;
583 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
584 dr.end[1] = diffrange.begin[1] + line1 - 1;
585 dr.end[2] = diffrange.begin[2] + line2 - 1;
586 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
588 newDiffList.AddDiff(dr);
590 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
591 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
592 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
594 while (i < vlines.size() &&
595 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
596 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
597 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
603 dr.begin[0] = diffrange.begin[0] + line0;
604 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
605 dr.begin[2] = diffrange.begin[2] + line2;
606 dr.end[0] = diffrange.begin[0] + line0 - 1;
607 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
608 dr.end[2] = diffrange.begin[2] + line2 - 1;
609 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
611 newDiffList.AddDiff(dr);
613 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
614 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
615 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
617 while (i < vlines.size() &&
618 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
619 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
620 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
626 dr.begin[0] = diffrange.begin[0] + line0;
627 dr.begin[1] = diffrange.begin[1] + line1;
628 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
629 dr.end[0] = diffrange.begin[0] + line0 - 1;
630 dr.end[1] = diffrange.begin[1] + line1 - 1;
631 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
632 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
634 newDiffList.AddDiff(dr);
644 newDiffList.AddDiff(diffrange);
648 // recreate m_diffList
650 nDiffCount = newDiffList.GetSize();
651 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
652 m_diffList.AddDiff(*newDiffList.DiffRangeAt(nDiff));
656 * @brief Return cost of making strings equal
658 * The cost of making them equal is the measure of their dissimilarity
659 * which is their Levenshtein distance.
661 int CMergeDoc::GetMatchCost(const DIFFRANGE& dr, int i0, int i1, int line0, int line1, const std::vector<WordDiff>& worddiffs)
664 for (size_t i = 0; i < worddiffs.size(); ++i)
668 if (line0 < worddiffs[0].beginline[0] && line1 < worddiffs[0].beginline[1])
670 if (line0 - dr.begin[i0] == line1 - dr.begin[i1])
671 matchlen += m_ptBuf[i0]->GetFullLineLength(line0);
673 else if (line0 == worddiffs[0].beginline[0] && line1 == worddiffs[0].beginline[1])
674 matchlen += worddiffs[0].begin[0];
678 if (worddiffs[i - 1].endline[0] < line0 && worddiffs[i - 1].endline[1] < line1 &&
679 line0 < worddiffs[i].beginline[0] && line1 < worddiffs[i].beginline[1])
681 if (line0 - worddiffs[i - 1].endline[0] == line1 - worddiffs[i - 1].endline[1])
682 matchlen += m_ptBuf[i0]->GetFullLineLength(line0);
684 else if (worddiffs[i - 1].endline[0] <= line0 && worddiffs[i - 1].endline[1] <= line1 &&
685 line0 <= worddiffs[i].beginline[0] && line1 <= worddiffs[i].beginline[1])
687 if (worddiffs[i - 1].endline[0] == worddiffs[i].beginline[0])
688 matchlen += worddiffs[i].begin[0] - worddiffs[i - 1].end[0];
690 matchlen += worddiffs[i].begin[0];
693 if (i == worddiffs.size() - 1 ||
694 worddiffs[i + 1].beginline[0] != worddiffs[i].endline[0] && worddiffs[i + 1].beginline[1] != worddiffs[i].endline[1])
696 if (worddiffs[i].endline[0] == line0 && worddiffs[i].endline[1] == line1)
697 matchlen += m_ptBuf[i0]->GetFullLineLength(line0) - worddiffs[i].end[0];
700 if (worddiffs.empty())
702 if (line0 - dr.begin[i0] == line1 - dr.begin[i1])
703 matchlen += m_ptBuf[i0]->GetFullLineLength(line0);
707 auto lastWordDiff = worddiffs.back();
708 if (lastWordDiff.endline[0] < line0 && lastWordDiff.endline[1] < line1)
710 if (line0 - lastWordDiff.endline[0] == line1 - lastWordDiff.endline[1])
711 matchlen += m_ptBuf[i0]->GetFullLineLength(line0);
716 String str = strutils::format(_T("pane%d,%d line%5d,%5d match len: %d\n"), i0, i1, line0, line1, matchlen);
717 OutputDebugString(str.c_str());
724 * @brief Map lines from left to right for specified range in diff block, as best we can
725 * Map left side range [lo0;hi0] to right side range [lo1;hi1]
726 * (ranges include ends)
729 * Find best match, and use that to split problem into two parts (above & below match)
730 * and call ourselves recursively to solve each smaller problem
732 void CMergeDoc::AdjustDiffBlock(DiffMap & diffMap, const DIFFRANGE & diffrange, const std::vector<WordDiff>& worddiffs, int i0, int i1, int lo0, int hi0, int lo1, int hi1)
734 // Map & lo & hi numbers are all relative to this block
735 // We need to know offsets to find actual line strings from buffer
736 int offset0 = diffrange.begin[i0];
737 int offset1 = diffrange.begin[i1];
739 // # of lines on left and right
740 int lines0 = hi0 - lo0 + 1;
741 int lines1 = hi1 - lo1 + 1;
743 // shortcut special case
750 for (int w=0; w<lines0; ++w)
751 diffMap.m_map[w] = DiffMap::GHOST_MAP_ENTRY;
754 if (lines0 == 1 && lines1 == 1)
756 diffMap.m_map[lo0] = hi1;
760 // Bail out if range is large
761 auto tooManyCharacters = [&](int pane, int start, int end)
763 size_t charCount = 0;
764 const int charCountMax = 4096;
765 for (int i = start; i <= end; ++i)
767 charCount += m_ptBuf[pane]->GetLineLength(i);
768 if (charCount > charCountMax)
773 if (tooManyCharacters(i0, offset0 + lo0, offset0 + hi0) || tooManyCharacters(i1, offset1 + lo1, offset1 + hi1))
775 // Do simple 1:1 mapping
776 // but try to stay within original block
777 // Cannot be outside both sides, so just test for outside one side
781 // right side is off the bottom
782 // so put it as close to bottom as possible
783 tlo = hi1 - (hi0-lo0);
788 // put it as close to top as possible
790 thi = lo1 + (hi0-lo0);
793 for (int w=0; w<lines0; ++w)
796 diffMap.m_map[w] = w + tlo;
798 diffMap.m_map[w] = DiffMap::GHOST_MAP_ENTRY;
804 int ibest=-1, isavings=0x7fffffff, itarget=-1;
805 for (int i=lo0; i<=hi0; ++i)
807 for (int j=lo1; j<=hi1; ++j)
809 int savings = GetMatchCost(diffrange, i0, i1, offset0 + i, offset1 + j, worddiffs);
811 // Need to penalize assignments that push us outside the box
812 // further than is required
813 if (savings < isavings)
821 // I think we have to find at least one
822 // because cost (Levenshtein distance) is less than or equal to maxlen
823 // so cost is always nonnegative
826 ASSERT(diffMap.m_map[ibest] == DiffMap::BAD_MAP_ENTRY);
828 diffMap.m_map[ibest] = itarget;
830 // Half of the remaining problem is below our match
835 AdjustDiffBlock(diffMap, diffrange, worddiffs, i0, i1, lo0, ibest-1, lo1, itarget-1);
839 // No target space for the ones below our match
840 for (int x = lo0; x < ibest; ++x)
842 diffMap.m_map[x] = DiffMap::GHOST_MAP_ENTRY;
847 // Half of the remaining problem is above our match
852 AdjustDiffBlock(diffMap, diffrange, worddiffs, i0, i1, ibest + 1, hi0,
857 // No target space for the ones above our match
858 for (int x = ibest + 1; x <= hi0; ++x)
860 diffMap.m_map[x] = DiffMap::GHOST_MAP_ENTRY;