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());
71 PrintWordDiffList(int nPanes, const std::vector<WordDiff>& worddiffs)
73 String lines = _T("[word-diff list]\n");
74 for (size_t i = 0; i < worddiffs.size(); ++i)
76 lines += strutils::format(_T("worddiff[%d] "), i);
77 for (int j = 0; j < nPanes; ++j)
79 lines += strutils::format(_T("pane:%d (line:%d,col:%d)-(line:%d,col:%d) "),
80 j, worddiffs[i].beginline[j], worddiffs[i].begin[j], worddiffs[i].endline[j], worddiffs[i].end[j]);
84 OutputDebugString(lines.c_str());
89 * @brief Create map from virtual line to real line.
91 static std::vector<std::array<int, 2>>
92 CreateVirtualLineToRealLineMap(
93 const DiffMap& diffmap, int nlines0, int nlines1)
95 std::vector<std::array<int, 2>> vlines;
96 int line0 = 0, line1 = 0;
97 while (line0 < nlines0)
99 const int map_line0 = diffmap.m_map[line0];
100 if (map_line0 == DiffMap::GHOST_MAP_ENTRY || map_line0 == DiffMap::BAD_MAP_ENTRY)
102 vlines.push_back({ line0++, DiffMap::GHOST_MAP_ENTRY });
106 while (line1 < map_line0)
107 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, line1++ });
108 vlines.push_back({ line0++, line1++ });
111 while (line1 < nlines1)
112 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, line1++ });
113 ValidateVirtualLineToRealLineMap(vlines, std::array<int, 2>{ nlines0, nlines1 });
115 PrintVirtualLineToRealLineMap(_T("vline"), vlines);
121 * @brief Create map from virtual line to real line. (3-way)
123 static std::vector<std::array<int, 3>>
124 CreateVirtualLineToRealLineMap3way(
125 const DiffMap& diffmap01, const DiffMap& diffmap12, const DiffMap& diffmap20,
126 int nlines0, int nlines1, int nlines2)
128 std::vector<std::array<int, 2>> vlines01 = CreateVirtualLineToRealLineMap(diffmap01, nlines0, nlines1);
129 std::vector<std::array<int, 2>> vlines12 = CreateVirtualLineToRealLineMap(diffmap12, nlines1, nlines2);
130 std::vector<std::array<int, 2>> vlines20 = CreateVirtualLineToRealLineMap(diffmap20, nlines2, nlines0);
131 std::vector<std::array<int, 3>> vlines;
132 size_t i01 = 0, i12 = 0, i20 = 0;
133 int line0 = 0, line1 = 0, line2 = 0;
134 bool is_vlines20_usable = true;
136 for (line1 = 0; line1 < nlines1; ++line1)
142 for (; i01 < vlines01.size(); ++i01)
143 if (vlines01[i01][1] == line1)
145 for (; i12 < vlines12.size(); ++i12)
146 if (vlines12[i12][0] == line1)
148 assert(i01 < vlines01.size() && i12 < vlines12.size());
150 bool used_vlines20 = false;
151 if (is_vlines20_usable)
153 if (vlines12[i12][1] != DiffMap::GHOST_MAP_ENTRY && vlines01[i01][0] != DiffMap::GHOST_MAP_ENTRY)
156 line2 = vlines12[i12][1];
157 line0 = vlines01[i01][0];
159 for (i20tmp = i20b; i20tmp < vlines20.size(); ++i20tmp)
160 if (vlines20[i20tmp][0] == line2 && vlines20[i20tmp][1] == line0)
162 if (i20tmp < vlines20.size())
165 for (; i20 < i20tmp; ++i20)
166 vlines.push_back({ vlines20[i20][1], DiffMap::GHOST_MAP_ENTRY, vlines20[i20][0] });
168 used_vlines20 = true;
173 is_vlines20_usable = false;
179 is_vlines20_usable = false;
185 size_t i01tmp, i12tmp;
186 for (i01tmp = i01b, i12tmp = i12b; i01tmp < i01 && i12tmp < i12; ++i01tmp, ++i12tmp)
187 vlines.push_back({ vlines01[i01tmp][0], DiffMap::GHOST_MAP_ENTRY, vlines12[i12tmp][1] });
188 if (i01 - i01b < i12 - i12b)
191 for (; i12tmp < i12; ++i12tmp)
192 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY, vlines12[i12tmp][1] });
194 else if (i01 - i01b > i12 - i12b)
197 for (; i01tmp < i01; ++i01tmp)
198 vlines.push_back({ vlines01[i01tmp][0], DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY });
201 vlines.push_back({ vlines01[i01++][0], line1, vlines12[i12++][1]});
204 if (i01 < vlines01.size() || i12 < vlines12.size())
206 if (is_vlines20_usable)
209 for (; i20 < vlines20.size(); ++i20)
210 vlines.push_back({ vlines20[i20][1], DiffMap::GHOST_MAP_ENTRY, vlines20[i20][0] });
215 for (; i01 < vlines01.size() && i12 < vlines12.size(); ++i01, ++i12)
216 vlines.push_back({ vlines01[i01][0], DiffMap::GHOST_MAP_ENTRY, vlines12[i12][1] });
217 if (vlines01.size() - i01 < vlines12.size() - i12)
220 for (; i12 < vlines12.size(); ++i12)
221 vlines.push_back({ DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY, vlines12[i12][1] });
223 else if (vlines01.size() - i01 > vlines12.size() - i12)
226 for (; i01 < vlines01.size(); ++i01)
227 vlines.push_back({ vlines01[i01][0], DiffMap::GHOST_MAP_ENTRY, DiffMap::GHOST_MAP_ENTRY });
231 ValidateVirtualLineToRealLineMap(vlines, std::array<int, 3>{ nlines0, nlines1, nlines2 });
233 PrintVirtualLineToRealLineMap(_T("vline01"), vlines01);
234 PrintVirtualLineToRealLineMap(_T("vline12"), vlines12);
235 PrintVirtualLineToRealLineMap(_T("vline20"), vlines20);
236 PrintVirtualLineToRealLineMap(_T("vline3way"), vlines);
241 OP_TYPE CMergeDoc::ComputeOpType3way(
242 const std::vector<std::array<int, 3>>& vlines, size_t index,
243 const DIFFRANGE& diffrange, const DIFFOPTIONS& diffOptions)
245 if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
246 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
247 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
251 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
252 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
253 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
257 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
258 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
259 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
264 int line0 = diffrange.begin[0] + vlines[index][0];
265 int line1 = diffrange.begin[1] + vlines[index][1];
266 int line2 = diffrange.begin[2] + vlines[index][2];
268 if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
269 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
270 vlines[index][2] == DiffMap::GHOST_MAP_ENTRY)
272 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
273 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
274 if (strdiff::Compare(strLine0, strLine1,
275 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
276 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
280 else if (vlines[index][0] != DiffMap::GHOST_MAP_ENTRY &&
281 vlines[index][1] == DiffMap::GHOST_MAP_ENTRY &&
282 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
284 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
285 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
286 if (strdiff::Compare(strLine0, strLine2,
287 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
288 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
292 else if (vlines[index][0] == DiffMap::GHOST_MAP_ENTRY &&
293 vlines[index][1] != DiffMap::GHOST_MAP_ENTRY &&
294 vlines[index][2] != DiffMap::GHOST_MAP_ENTRY)
296 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
297 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
298 if (strdiff::Compare(strLine1, strLine2,
299 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
300 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
306 String strLine0(m_ptBuf[0]->GetLineChars(line0), m_ptBuf[0]->GetFullLineLength(line0));
307 String strLine1(m_ptBuf[1]->GetLineChars(line1), m_ptBuf[1]->GetFullLineLength(line1));
308 String strLine2(m_ptBuf[2]->GetLineChars(line2), m_ptBuf[2]->GetFullLineLength(line2));
309 if (strdiff::Compare(strLine0, strLine1,
310 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
311 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
313 if (strdiff::Compare(strLine0, strLine2,
314 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
315 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
317 if (strdiff::Compare(strLine1, strLine2,
318 !diffOptions.bIgnoreCase, !diffOptions.bIgnoreEol,
319 diffOptions.nIgnoreWhitespace, diffOptions.bIgnoreNumbers) == 0)
326 * @brief Divide diff blocks to match lines in diff blocks.
328 void CMergeDoc::AdjustDiffBlocks()
331 int nDiffCount = m_diffList.GetSize();
333 // Go through and do our best to line up lines within each diff block
334 // between left side and right side
335 DiffList newDiffList;
337 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
339 const DIFFRANGE & diffrange = *m_diffList.DiffRangeAt(nDiff);
340 // size map correctly (it will hold one entry for each left-side line
341 int nlines0 = diffrange.end[0] - diffrange.begin[0] + 1;
342 int nlines1 = diffrange.end[1] - diffrange.begin[1] + 1;
343 if (nlines0>0 && nlines1>0)
345 // Call worker to do all lines in block
346 int lo0 = 0, hi0 = nlines0-1;
347 int lo1 = 0, hi1 = nlines1-1;
348 const std::vector<WordDiff> worddiffs = GetWordDiffArrayInRange(diffrange.begin, diffrange.end);
350 PrintWordDiffList(2, worddiffs);
353 diffmap.InitDiffMap(nlines0);
354 AdjustDiffBlock(diffmap, diffrange, worddiffs, 0, 1, lo0, hi0, lo1, hi1);
355 ValidateDiffMap(diffmap);
356 std::vector<std::array<int, 2>> vlines = CreateVirtualLineToRealLineMap(diffmap, nlines0, nlines1);
358 // divide diff blocks
359 int line0 = 0, line1 = 0;
360 for (size_t i = 0; i < vlines.size();)
364 if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
365 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
367 while (i < vlines.size() &&
368 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
369 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
375 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
376 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
377 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
378 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
379 dr.blank[0] = dr.blank[1] = -1;
380 dr.op = diffrange.op;
381 newDiffList.AddDiff(dr);
383 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
384 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
386 while (i < vlines.size() &&
387 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
388 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY))
393 dr.begin[0] = diffrange.begin[0] + line0;
394 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
395 dr.end[0] = diffrange.begin[0] + line0 - 1;
396 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
397 dr.blank[0] = dr.blank[1] = -1;
398 dr.op = diffrange.op;
399 newDiffList.AddDiff(dr);
401 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
402 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY))
404 while (i < vlines.size() &&
405 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
406 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY))
411 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
412 dr.begin[1] = diffrange.begin[1] + line1;
413 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
414 dr.end[1] = diffrange.begin[1] + line1 - 1;
415 dr.blank[0] = dr.blank[1] = -1;
416 dr.op = diffrange.op;
417 newDiffList.AddDiff(dr);
427 newDiffList.AddDiff(diffrange);
431 // recreate m_diffList
433 nDiffCount = newDiffList.GetSize();
434 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
435 m_diffList.AddDiff(*newDiffList.DiffRangeAt(nDiff));
439 * @brief Divide diff blocks to match lines in diff blocks. (3-way)
441 void CMergeDoc::AdjustDiffBlocks3way()
444 int nDiffCount = m_diffList.GetSize();
446 // Go through and do our best to line up lines within each diff block
447 // between left side and right side
448 DiffList newDiffList;
450 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
452 const DIFFRANGE & diffrange = *m_diffList.DiffRangeAt(nDiff);
453 // size map correctly (it will hold one entry for each left-side line
454 int nlines0 = diffrange.end[0] - diffrange.begin[0] + 1;
455 int nlines1 = diffrange.end[1] - diffrange.begin[1] + 1;
456 int nlines2 = diffrange.end[2] - diffrange.begin[2] + 1;
457 if ((nlines0 > 0) + (nlines1 > 0) + (nlines2 > 0) > 1)
459 // Call worker to do all lines in block
460 int lo0 = 0, hi0 = nlines0 - 1;
461 int lo1 = 0, hi1 = nlines1 - 1;
462 int lo2 = 0, hi2 = nlines2 - 1;
463 const std::vector<WordDiff> worddiffs01 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 0, 1);
464 const std::vector<WordDiff> worddiffs12 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 1, 2);
465 const std::vector<WordDiff> worddiffs20 = GetWordDiffArrayInRange(diffrange.begin, diffrange.end, 2, 0);
466 DiffMap diffmap01, diffmap12, diffmap20;
467 diffmap01.InitDiffMap(nlines0);
468 diffmap12.InitDiffMap(nlines1);
469 diffmap20.InitDiffMap(nlines2);
470 AdjustDiffBlock(diffmap01, diffrange, worddiffs01, 0, 1, lo0, hi0, lo1, hi1);
471 AdjustDiffBlock(diffmap12, diffrange, worddiffs12, 1, 2, lo1, hi1, lo2, hi2);
472 AdjustDiffBlock(diffmap20, diffrange, worddiffs20, 2, 0, lo2, hi2, lo0, hi0);
473 ValidateDiffMap(diffmap01);
474 ValidateDiffMap(diffmap12);
475 ValidateDiffMap(diffmap20);
476 std::vector<std::array<int, 3>> vlines = CreateVirtualLineToRealLineMap3way(diffmap01, diffmap12, diffmap20, nlines0, nlines1, nlines2);
478 DIFFOPTIONS diffOptions = {0};
479 m_diffWrapper.GetOptions(&diffOptions);
480 std::vector<OP_TYPE> opary(vlines.size());
481 for (size_t i = 0; i < vlines.size(); ++i)
482 opary[i] = (diffrange.op == OP_TRIVIAL) ?
484 ComputeOpType3way(vlines, i, diffrange, diffOptions);
485 // divide diff blocks
486 int line0 = 0, line1 = 0, line2 = 0;
487 for (size_t i = 0; i < vlines.size();)
491 OP_TYPE op = opary[i];
492 if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
493 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
494 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
496 while (i < vlines.size() &&
497 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
498 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
499 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
507 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
508 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
509 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
510 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
511 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
512 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
513 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
515 newDiffList.AddDiff(dr);
517 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
518 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
519 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
521 while (i < vlines.size() &&
522 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
523 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
524 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
531 dr.begin[0] = diffrange.begin[0] + line0;
532 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
533 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
534 dr.end[0] = diffrange.begin[0] + line0 - 1;
535 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
536 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
537 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
539 newDiffList.AddDiff(dr);
541 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
542 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
543 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
545 while (i < vlines.size() &&
546 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
547 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
548 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
555 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
556 dr.begin[1] = diffrange.begin[1] + line1;
557 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
558 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
559 dr.end[1] = diffrange.begin[1] + line1 - 1;
560 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
561 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
563 newDiffList.AddDiff(dr);
565 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
566 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
567 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
569 while (i < vlines.size() &&
570 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
571 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
572 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
579 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
580 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
581 dr.begin[2] = diffrange.begin[2] + line2;
582 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
583 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
584 dr.end[2] = diffrange.begin[2] + line2 - 1;
585 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
587 newDiffList.AddDiff(dr);
589 else if ((vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
590 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
591 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
593 while (i < vlines.size() &&
594 (vlines[i][0] != DiffMap::GHOST_MAP_ENTRY) &&
595 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
596 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
602 dr.begin[0] = diffrange.begin[0] + vlines[ib][0];
603 dr.begin[1] = diffrange.begin[1] + line1;
604 dr.begin[2] = diffrange.begin[2] + line2;
605 dr.end[0] = diffrange.begin[0] + vlines[i - 1][0];
606 dr.end[1] = diffrange.begin[1] + line1 - 1;
607 dr.end[2] = diffrange.begin[2] + line2 - 1;
608 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
610 newDiffList.AddDiff(dr);
612 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
613 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
614 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY))
616 while (i < vlines.size() &&
617 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
618 (vlines[i][1] != DiffMap::GHOST_MAP_ENTRY) &&
619 (vlines[i][2] == DiffMap::GHOST_MAP_ENTRY) &&
625 dr.begin[0] = diffrange.begin[0] + line0;
626 dr.begin[1] = diffrange.begin[1] + vlines[ib][1];
627 dr.begin[2] = diffrange.begin[2] + line2;
628 dr.end[0] = diffrange.begin[0] + line0 - 1;
629 dr.end[1] = diffrange.begin[1] + vlines[i - 1][1];
630 dr.end[2] = diffrange.begin[2] + line2 - 1;
631 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
633 newDiffList.AddDiff(dr);
635 else if ((vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
636 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
637 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY))
639 while (i < vlines.size() &&
640 (vlines[i][0] == DiffMap::GHOST_MAP_ENTRY) &&
641 (vlines[i][1] == DiffMap::GHOST_MAP_ENTRY) &&
642 (vlines[i][2] != DiffMap::GHOST_MAP_ENTRY) &&
648 dr.begin[0] = diffrange.begin[0] + line0;
649 dr.begin[1] = diffrange.begin[1] + line1;
650 dr.begin[2] = diffrange.begin[2] + vlines[ib][2];
651 dr.end[0] = diffrange.begin[0] + line0 - 1;
652 dr.end[1] = diffrange.begin[1] + line1 - 1;
653 dr.end[2] = diffrange.begin[2] + vlines[i - 1][2];
654 dr.blank[0] = dr.blank[1] = dr.blank[2] = -1;
656 newDiffList.AddDiff(dr);
666 newDiffList.AddDiff(diffrange);
670 // recreate m_diffList
672 nDiffCount = newDiffList.GetSize();
673 for (nDiff = 0; nDiff < nDiffCount; nDiff++)
674 m_diffList.AddDiff(*newDiffList.DiffRangeAt(nDiff));
678 * @brief Return cost of making strings equal
680 * The cost of making them equal is the measure of their dissimilarity
681 * which is their Levenshtein distance.
683 int CMergeDoc::GetMatchCost(const DIFFRANGE& dr, int i0, int i1, int line0, int line1, const std::vector<WordDiff>& worddiffs)
686 WordDiff dummyWordDiff(0, 0, dr.begin[0], dr.begin[0], 0, 0, dr.begin[1], dr.begin[1]);
687 WordDiff& prevWordDiff = dummyWordDiff;
688 for (size_t i = 0; i < worddiffs.size(); ++i)
690 if ((prevWordDiff.endline[0] <= line0 && line0 <= worddiffs[i].beginline[0]) &&
691 (prevWordDiff.endline[1] <= line1 && line1 <= worddiffs[i].beginline[1]) &&
692 (line0 - prevWordDiff.endline[0] == line1 - prevWordDiff.endline[1]))
694 if (line0 == worddiffs[i].beginline[0])
696 matchlen += worddiffs[i].begin[0] - ((prevWordDiff.endline[0] == line0) ? prevWordDiff.end[0] : 0);
698 else /* line0 < worddiffs[i].beginline[0] */
700 matchlen += m_ptBuf[i0]->GetFullLineLength(line0) - ((prevWordDiff.endline[0] == line0) ? prevWordDiff.end[0] : 0);
703 prevWordDiff = worddiffs[i];
705 if (worddiffs.empty())
707 if (line0 - dr.begin[i0] == line1 - dr.begin[i1])
708 matchlen += m_ptBuf[i0]->GetFullLineLength(line0);
712 if (prevWordDiff.endline[0] <= line0 && prevWordDiff.endline[1] <= line1 &&
713 (line0 - prevWordDiff.endline[0] == line1 - prevWordDiff.endline[1]))
715 matchlen += m_ptBuf[i0]->GetFullLineLength(line0) - ((prevWordDiff.endline[0] == line0) ? prevWordDiff.end[0] : 0);
720 String str = strutils::format(_T("pane%d,%d line%5d,%5d match len: %d\n"), i0, i1, line0, line1, matchlen);
721 OutputDebugString(str.c_str());
728 * @brief Map lines from left to right for specified range in diff block, as best we can
729 * Map left side range [lo0;hi0] to right side range [lo1;hi1]
730 * (ranges include ends)
733 * Find best match, and use that to split problem into two parts (above & below match)
734 * and call ourselves recursively to solve each smaller problem
736 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)
738 // Map & lo & hi numbers are all relative to this block
739 // We need to know offsets to find actual line strings from buffer
740 int offset0 = diffrange.begin[i0];
741 int offset1 = diffrange.begin[i1];
743 // # of lines on left and right
744 int lines0 = hi0 - lo0 + 1;
745 int lines1 = hi1 - lo1 + 1;
747 // shortcut special case
754 for (int w=0; w<lines0; ++w)
755 diffMap.m_map[w] = DiffMap::GHOST_MAP_ENTRY;
758 if (lines0 == 1 && lines1 == 1)
760 diffMap.m_map[lo0] = hi1;
764 // Bail out if range is large
765 auto tooManyCharacters = [&](int pane, int start, int end)
767 size_t charCount = 0;
768 const int charCountMax = 4096;
769 for (int i = start; i <= end; ++i)
771 charCount += m_ptBuf[pane]->GetLineLength(i);
772 if (charCount > charCountMax)
777 if (tooManyCharacters(i0, offset0 + lo0, offset0 + hi0) || tooManyCharacters(i1, offset1 + lo1, offset1 + hi1))
779 // Do simple 1:1 mapping
780 // but try to stay within original block
781 // Cannot be outside both sides, so just test for outside one side
785 // right side is off the bottom
786 // so put it as close to bottom as possible
787 tlo = hi1 - (hi0-lo0);
792 // put it as close to top as possible
794 thi = lo1 + (hi0-lo0);
797 for (int w=0; w<lines0; ++w)
800 diffMap.m_map[w] = w + tlo;
802 diffMap.m_map[w] = DiffMap::GHOST_MAP_ENTRY;
808 int ibest=-1, isavings=0x7fffffff, itarget=-1;
809 for (int i=lo0; i<=hi0; ++i)
811 for (int j=lo1; j<=hi1; ++j)
813 int savings = GetMatchCost(diffrange, i0, i1, offset0 + i, offset1 + j, worddiffs);
815 // Need to penalize assignments that push us outside the box
816 // further than is required
817 if (savings < isavings)
825 // I think we have to find at least one
826 // because cost (Levenshtein distance) is less than or equal to maxlen
827 // so cost is always nonnegative
830 ASSERT(diffMap.m_map[ibest] == DiffMap::BAD_MAP_ENTRY);
832 diffMap.m_map[ibest] = itarget;
834 // Half of the remaining problem is below our match
839 AdjustDiffBlock(diffMap, diffrange, worddiffs, i0, i1, lo0, ibest-1, lo1, itarget-1);
843 // No target space for the ones below our match
844 for (int x = lo0; x < ibest; ++x)
846 diffMap.m_map[x] = DiffMap::GHOST_MAP_ENTRY;
851 // Half of the remaining problem is above our match
856 AdjustDiffBlock(diffMap, diffrange, worddiffs, i0, i1, ibest + 1, hi0,
861 // No target space for the ones above our match
862 for (int x = ibest + 1; x <= hi0; ++x)
864 diffMap.m_map[x] = DiffMap::GHOST_MAP_ENTRY;