LCOV - code coverage report
Current view: top level - src/lib - Wikidiff2.cpp (source / functions) Hit Total Coverage
Test: mediawiki/php/wikidiff2 test coverage report Lines: 231 266 86.8 %
Date: 2023-07-04 10:20:16 Functions: 21 22 95.5 %

          Line data    Source code
       1             : /**
       2             :  * Diff formatter, based on code by Steinar H. Gunderson, converted to work with the
       3             :  * Dairiki diff engine by Tim Starling
       4             :  *
       5             :  * GPL.
       6             :  */
       7             : 
       8             : #include <stdio.h>
       9             : #include "Wikidiff2.h"
      10             : 
      11             : namespace wikidiff2 {
      12             : 
      13          47 : Wikidiff2::Wikidiff2(const Config & config_)
      14             :         : config(config_),
      15             :         lineDiffConfig{0},
      16          47 :         wordDiffConfig{config.maxWordLevelDiffComplexity},
      17          47 :         wordDiffCache(wordDiffConfig),
      18             :         ldpConfig{
      19          47 :             config.changeThreshold,
      20          47 :             config.initialSplitThreshold,
      21          47 :             config.finalSplitThreshold,
      22          47 :             config.maxSplitSize
      23             :         },
      24          47 :         lineDiffProcessor(ldpConfig, wordDiffCache)
      25             : {
      26          47 : }
      27             : 
      28          47 : void Wikidiff2::printDiff(const StringDiff & linediff)
      29             : {
      30          47 :     int from_index = 1, to_index = 1;
      31             : 
      32             :     // Should a line number be printed before the next context line?
      33             :     // Set to true initially so we get a line number on line 1
      34          47 :     bool showLineNumber = true;
      35             : 
      36          47 :     printFileHeader();
      37             : 
      38          47 :     int currentOffsetFrom = 0;
      39          47 :     int currentOffsetTo = 0;
      40          47 :     int newLineLength = 1;
      41         457 :     for (int i = 0; i < linediff.size(); ++i) {
      42             :         int j;
      43             :         // Line 1 changed, show heading with no leading context
      44         410 :         if (linediff[i].op != DiffOp<String>::copy && i == 0) {
      45          28 :             printBlockHeader(1, 1);
      46             :         }
      47             : 
      48         410 :         int n1 = linediff[i].from.size();
      49         410 :         int n2 = linediff[i].to.size();
      50             : 
      51         410 :         switch (linediff[i].op) {
      52          84 :             case DiffOp<String>::add:
      53             :                 // inserted lines
      54         217 :                 for (j=0; j<n2; j++) {
      55             : 
      56         133 :                     String toLine = *linediff[i].to[j];
      57             : 
      58         133 :                     if (!printMovedLineDiff(linediff, i, j, from_index, to_index+j,
      59             :                          -1, currentOffsetTo)) {
      60             : 
      61          80 :                         printAdd(toLine, from_index, to_index+j, -1, currentOffsetTo);
      62             :                     }
      63             : 
      64         133 :                     currentOffsetTo += toLine.length() + newLineLength;
      65             :                 }
      66          84 :                 to_index += n2;
      67          84 :                 break;
      68         118 :             case DiffOp<String>::del:
      69             :                 // deleted lines
      70         273 :                 for (j=0; j<n1; j++) {
      71             : 
      72         155 :                     const String & fromLine = *linediff[i].from[j];
      73             : 
      74         155 :                     if (!printMovedLineDiff(linediff, i, j, from_index+j, to_index,
      75             :                         currentOffsetFrom, -1)) {
      76             : 
      77         102 :                         printDelete(fromLine, from_index+j, to_index, currentOffsetFrom, -1);
      78             :                     }
      79             : 
      80         155 :                     currentOffsetFrom += fromLine.length() + newLineLength;
      81             :                 }
      82         118 :                 from_index += n1;
      83         118 :                 break;
      84         148 :             case DiffOp<String>::copy:
      85             :                 // copy/context
      86         570 :                 for (j=0; j<n1; j++) {
      87             : 
      88         422 :                     String line = *linediff[i].from[j];
      89             : 
      90         386 :                     if ((i != 0 && j < config.numContextLines) /*trailing*/
      91         808 :                             || (i != linediff.size() - 1 && j >= n1 - config.numContextLines)) /*leading*/ {
      92         297 :                         if (showLineNumber) {
      93          43 :                             printBlockHeader(from_index, to_index);
      94          43 :                             showLineNumber = false;
      95             :                         }
      96             : 
      97         297 :                         printContext(line, from_index, to_index, currentOffsetFrom, currentOffsetTo);
      98             :                     } else {
      99         125 :                         showLineNumber = true;
     100             :                     }
     101             : 
     102         422 :                     currentOffsetTo += line.length() + newLineLength;
     103         422 :                     currentOffsetFrom += line.length() + newLineLength;
     104             : 
     105         422 :                     from_index++;
     106         422 :                     to_index++;
     107             :                 }
     108         148 :                 break;
     109          60 :             case DiffOp<String>::change:
     110          60 :                 if (n1 != n2) {
     111             :                     // Line split
     112           0 :                     printConcatDiff(
     113           0 :                         linediff[i].from[0], n1,
     114           0 :                         linediff[i].to[0], n2,
     115             :                         from_index, to_index,
     116             :                         currentOffsetFrom, currentOffsetTo);
     117           0 :                     for (j = 0; j < n1; j++) {
     118           0 :                         currentOffsetFrom += linediff[i].from[j]->length() + newLineLength;
     119             :                     }
     120           0 :                     for (j = 0; j < n2; j++) {
     121           0 :                         currentOffsetTo += linediff[i].to[j]->length() + newLineLength;
     122             :                     }
     123           0 :                     from_index += n1;
     124           0 :                     to_index += n2;
     125             :                 } else {
     126             :                     // Replace, i.e. we do a word diff between the two sets of lines
     127         124 :                     for (j=0; j<n1; j++) {
     128          64 :                         const String * toLine = linediff[i].to[j];
     129          64 :                         const String * fromLine = linediff[i].from[j];
     130             : 
     131          64 :                         printWordDiffFromStrings(fromLine, toLine, from_index+j, to_index+j,
     132             :                             currentOffsetFrom, currentOffsetTo);
     133             : 
     134          64 :                         currentOffsetTo += toLine->length() + newLineLength;
     135          64 :                         currentOffsetFrom += fromLine->length() + newLineLength;
     136             :                     }
     137          60 :                     from_index += n1;
     138          60 :                     to_index += n1;
     139             :                 }
     140          60 :                 break;
     141             :         }
     142             : 
     143             :         // Not first line anymore, don't show line number by default
     144         410 :         showLineNumber = false;
     145             :     }
     146             : 
     147          47 :     printFileFooter();
     148          47 : }
     149             : 
     150             : /**
     151             :  * Tell registered formatters to print an added line
     152             :  *
     153             :  * @see Formatter::printAdd
     154             :  */
     155          80 : void Wikidiff2::printAdd(const String & line, int leftLine, int rightLine, int offsetFrom, int offsetTo)
     156             : {
     157         160 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     158          80 :         (*f)->printAdd(line, leftLine, rightLine, offsetFrom, offsetTo);
     159             :     }
     160          80 : }
     161             : 
     162             : /**
     163             :  * Tell registered formatters to print a deleted line
     164             :  *
     165             :  * @see Formatter::printDelete
     166             :  */
     167         102 : void Wikidiff2::printDelete(const String & line, int leftLine, int rightLine, int offsetFrom, int offsetTo)
     168             : {
     169         204 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     170         102 :         (*f)->printDelete(line, leftLine, rightLine, offsetFrom, offsetTo);
     171             :     }
     172         102 : }
     173             : 
     174             : /**
     175             :  * Tell registered formatters to print a word diff
     176             :  *
     177             :  * @see Formatter::printWordDiff
     178             :  */
     179         170 : void Wikidiff2::printWordDiff(
     180             :     const WordDiff & wordDiff,
     181             :     int leftLine, int rightLine,
     182             :     int offsetFrom, int offsetTo,
     183             :     bool printLeft, bool printRight,
     184             :     const String & srcAnchor, const String & dstAnchor,
     185             :     bool moveDirectionDownwards)
     186             : {
     187         340 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     188         170 :         (*f)->printWordDiff(wordDiff,
     189             :                 leftLine, rightLine,
     190             :                 offsetFrom, offsetTo,
     191             :                 printLeft, printRight,
     192             :                 srcAnchor, dstAnchor,
     193             :                 moveDirectionDownwards
     194         170 :         );
     195             :     }
     196         170 : }
     197             : 
     198             : /**
     199             :  * Do a word diff and then tell formatters to print it
     200             :  */
     201         170 : void Wikidiff2::printWordDiffFromStrings(
     202             :     const String * text1, const String * text2,
     203             :     int leftLine, int rightLine,
     204             :     int offsetFrom, int offsetTo,
     205             :     bool printLeft, bool printRight,
     206             :     const String & srcAnchor, const String & dstAnchor,
     207             :     bool moveDirectionDownwards)
     208             : {
     209         170 :     printWordDiff(
     210         340 :             *wordDiffCache.getDiff(text1, text2),
     211             :             leftLine, rightLine,
     212             :             offsetFrom, offsetTo,
     213             :             printLeft, printRight,
     214             :             srcAnchor, dstAnchor,
     215             :             moveDirectionDownwards
     216             :     );
     217         170 : }
     218             : 
     219           0 : void Wikidiff2::printConcatDiff(
     220             :     const String * lines1, int numLines1,
     221             :     const String * lines2, int numLines2,
     222             :     int leftLine, int rightLine,
     223             :     int offsetFrom, int offsetTo)
     224             : {
     225           0 :     const WordDiff & wordDiff = *wordDiffCache.getConcatDiff(lines1, numLines1, lines2, numLines2);
     226           0 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     227           0 :         (*f)->printConcatDiff(wordDiff, leftLine, rightLine, offsetFrom, offsetTo);
     228             :     }
     229           0 : }
     230             : 
     231             : /**
     232             :  * Tell all formatters that we are starting
     233             :  *
     234             :  * @see Formatter::printFileHeader
     235             :  */
     236          47 : void Wikidiff2::printFileHeader()
     237             : {
     238          94 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     239          47 :         (*f)->printFileHeader();
     240             :     }
     241          47 : }
     242             : 
     243             : /**
     244             :  * Tell all formatters that we are ending
     245             :  *
     246             :  * @see Formatter::printFileFooter
     247             :  */
     248          47 : void Wikidiff2::printFileFooter()
     249             : {
     250          94 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     251          47 :         (*f)->printFileFooter();
     252             :     }
     253          47 : }
     254             : 
     255             : /**
     256             :  * Tell all formatters to print a block header
     257             :  *
     258             :  * @see Formatter::printBlockHeader
     259             :  */
     260          71 : void Wikidiff2::printBlockHeader(int leftLine, int rightLine)
     261             : {
     262         142 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     263          71 :         (*f)->printBlockHeader(leftLine, rightLine);
     264             :     }
     265          71 : }
     266             : 
     267             : /**
     268             :  * Tell all formatters to print a context line
     269             :  *
     270             :  * @see Formatter::printContext
     271             :  */
     272         297 : void Wikidiff2::printContext(const String & input, int leftLine, int rightLine, int offsetFrom, int offsetTo)
     273             : {
     274         594 :     for (auto f = formatters.begin(); f != formatters.end(); f++) {
     275         297 :         (*f)->printContext(input, leftLine, rightLine, offsetFrom, offsetTo);
     276             :     }
     277         297 : }
     278             : 
     279         801 : std::shared_ptr<Wikidiff2::DiffMapEntry> Wikidiff2::getDiffMapEntry(
     280             :         const String * text1, const String * text2,
     281             :         int opIndexFrom, int opLineFrom,
     282             :         int opIndexTo, int opLineTo)
     283             : {
     284             :     return std::make_shared<DiffMapEntry>(
     285             :             wordDiffCache.getDiffStats(text1, text2),
     286             :             opIndexFrom, opLineFrom,
     287         801 :             opIndexTo, opLineTo);
     288             : }
     289             : 
     290             : /**
     291             :  * Detect a moved line at the current position. If there was a moved line, print it
     292             :  * and return true. If there was no moved line, do nothing and return false.
     293             :  *
     294             :  * @param linediff The line-level diff
     295             :  * @param opIndex The current index into linediff
     296             :  * @param opLine The current index into linediff[opIndex].from or
     297             :  *   linediff[opIndex].to, specifying a particular added or deleted line.
     298             :  * @param leftLine The 1-based line number on the LHS
     299             :  * @param rightLine The 1-based line number on the RHS
     300             :  * @param offsetFrom The 0-based byte offset in the LHS input string
     301             :  * @param offsetTo The 0-based byte offset in the RHS input string
     302             :  */
     303         288 : bool Wikidiff2::printMovedLineDiff(const StringDiff & linediff, int opIndex, int opLine,
     304             :     int leftLine, int rightLine, int offsetFrom, int offsetTo)
     305             : {
     306             :     // helper fn creates 64-bit lookup key from opIndex and opLine
     307        2243 :     auto makeKey = [](int index, int line) {
     308        2243 :         return uint64_t(index) << 32 | line;
     309             :     };
     310             : 
     311         212 :     auto makeAnchorName = [](int index, int line, bool lhs) {
     312             :         char ch[2048];
     313         212 :         snprintf(ch, sizeof(ch), "movedpara_%d_%d_%s", index, line, lhs? "lhs": "rhs");
     314         212 :         return String(ch);
     315             :     };
     316             : 
     317             :     // check whether this paragraph immediately follows the other.
     318             :     // if so, they will be matched up next to each other and displayed as a change, not a move.
     319         106 :     auto isNext = [] (int opIndex, int opLine, int otherIndex, int otherLine) {
     320         106 :         if(otherIndex==opIndex && otherLine==opLine+1)
     321           0 :             return true;
     322         106 :         if(otherIndex==opIndex+1 && otherLine==0)
     323           0 :             return true;
     324         106 :         return false;
     325             :     };
     326             : 
     327             :     // compare positions of moved lines, return true if moved downwards
     328         106 :     auto movedir = [] (int opIndex, int opLine, int otherIndex, int otherLine) {
     329         106 :         return (otherIndex > opIndex) || (otherIndex == opIndex && otherLine > opLine);
     330             :     };
     331             : 
     332             : #ifdef DEBUG_MOVED_LINES
     333             :     auto debugPrintf = [this](const char *fmt, ...) {
     334             :         char ch[2048];
     335             :         va_list ap;
     336             :         va_start(ap, fmt);
     337             :         vsnprintf(ch, sizeof(ch), fmt, ap);
     338             :         va_end(ap);
     339             : 
     340             :         std::cerr << ch << std::endl;
     341             :     };
     342             : #else
     343        2129 :     auto debugPrintf = [](...) { };
     344             : #endif
     345             : 
     346         288 :     if(!allowPrintMovedLineDiff(linediff, config.maxMovedLines)) {
     347           0 :         debugPrintf("printMovedLineDiff: diff too large (maxMovedLines=%ld), not detecting moved lines",
     348             :             config.maxMovedLines);
     349           0 :         return false;
     350             :     }
     351             : 
     352         288 :     debugPrintf("printMovedLineDiff (...), %d, %d", opIndex, opLine);
     353             : 
     354         288 :     bool printLeft = linediff[opIndex].op == DiffOp<String>::del ? true : false;
     355         288 :     bool printRight = !printLeft;
     356             : 
     357             :     // check whether this op actually refers to the diff map entry
     358         913 :     auto cmpDiffMapEntries = [&](int otherIndex, int otherLine) -> bool {
     359             :         // check whether the other paragraph already exists in the diff map.
     360         913 :         uint64_t otherKey = makeKey(otherIndex, otherLine);
     361         913 :         auto it = diffMap.find(otherKey);
     362         913 :         if (it != diffMap.end()) {
     363             :             // if found, check whether it refers to the current paragraph.
     364         286 :             auto other = it->second;
     365         286 :             bool cmp = (printLeft ?
     366          72 :                 other->opIndexFrom == opIndex && other->opLineFrom == opLine :
     367          71 :                 other->opIndexTo == opIndex && other->opLineTo == opLine);
     368         143 :             if(!cmp && (printLeft ? other->lhsDisplayed : other->rhsDisplayed)) {
     369             :                 // the paragraph was already moved to a different place. a move operation can only have one source and one destination.
     370           0 :                 debugPrintf("printMovedLineDiff(..., %d, %d): excluding this candidate (multiple potential matches). op=%s, printLeft %s, otheridx/line %d/%d, found %d/%d, other->lhsDisplayed %s, other->rhsDisplayed  %s",
     371             :                     opIndex, opLine,
     372           0 :                     linediff[opIndex].op == DiffOp<String>::add ? "add": linediff[opIndex].op == DiffOp<String>::del ? "del": "???",
     373           0 :                     printLeft ? "true" : "false",
     374           0 :                     otherIndex, otherLine, (printLeft ? other->opIndexFrom : other->opIndexTo), (printLeft? other->opLineFrom: other->opLineTo),
     375           0 :                     other->lhsDisplayed ? "true" : "false",
     376           0 :                     other->rhsDisplayed ? "true" : "false");
     377           0 :                 return false;
     378             :             }
     379             :             // the entry in the diff map refers to this paragraph.
     380         572 :             debugPrintf("printMovedLineDiff(..., %d, %d): diffMap entry refers to this paragraph (or other side not displayed). op=%s, printLeft %s, otheridx/line %d/%d, found %d/%d",
     381             :                 opIndex, opLine,
     382         143 :                 linediff[opIndex].op == DiffOp<String>::add ? "add": linediff[opIndex].op == DiffOp<String>::del ? "del": "???",
     383         143 :                 printLeft ? "true" : "false",
     384         286 :                 otherIndex, otherLine, (printLeft ? other->opIndexFrom : other->opIndexTo), (printLeft? other->opLineFrom: other->opLineTo));
     385         143 :             return true;
     386             :         }
     387             :         // no entry in the diffMap.
     388        1540 :         debugPrintf("printMovedLineDiff(..., %d, %d): no diffMap entry found. op=%s, printLeft %s, otheridx/line %d/%d",
     389             :             opIndex, opLine,
     390         770 :             linediff[opIndex].op == DiffOp<String>::add ? "add": linediff[opIndex].op == DiffOp<String>::del ? "del": "???",
     391         770 :             printLeft ? "true" : "false",
     392             :             otherIndex, otherLine);
     393         770 :         return true;
     394         288 :     };
     395             : 
     396             :     // look for corresponding moved line for the opposite case in moved-line-map
     397             :     // if moved line exists:
     398             :     //     print diff to the moved line, omitting the left/right side for added/deleted line
     399         288 :     uint64_t key = makeKey(opIndex, opLine);
     400         288 :     auto it = diffMap.find(key);
     401         288 :     if (it != diffMap.end()) {
     402         106 :         auto best = it->second;
     403          53 :         int otherIndex = linediff[opIndex].op == DiffOp<String>::add ? best->opIndexFrom : best->opIndexTo;
     404          53 :         int otherLine = linediff[opIndex].op == DiffOp<String>::add ? best->opLineFrom : best->opLineTo;
     405             : 
     406          53 :         if(!cmpDiffMapEntries(otherIndex, otherLine))
     407           0 :             return false;
     408             : 
     409          53 :         if(isNext(otherIndex, otherLine, opIndex, opLine)) {
     410           0 :             debugPrintf("this one was already shown as a change, not displaying again...");
     411           0 :             return true;
     412             :         } else {
     413             :             // XXXX todo: we already have the diff, don't have to do it again, just have to print it
     414          53 :             printWordDiffFromStrings(
     415          53 :                 linediff[best->opIndexFrom].from[best->opLineFrom],
     416          53 :                 linediff[best->opIndexTo].to[best->opLineTo],
     417             :                 leftLine, rightLine, offsetFrom, offsetTo, printLeft, printRight,
     418         106 :                 makeAnchorName(opIndex, opLine, printLeft),
     419         106 :                 makeAnchorName(otherIndex, otherLine, !printLeft),
     420          53 :                 movedir(opIndex,opLine, otherIndex,otherLine));
     421             :         }
     422             : 
     423          53 :         if(printLeft)
     424          26 :             best->lhsDisplayed = true;
     425             :         else
     426          27 :             best->rhsDisplayed = true;
     427             : 
     428          53 :         debugPrintf("found in diffmap. copy: %d, del: %d, add: %d, change: %d, similarity: %.4f\n"
     429             :                     "from: (%d,%d) to: (%d,%d)",
     430          53 :             best->ds.opCharCount[DiffOp<Word>::copy], best->ds.opCharCount[DiffOp<Word>::del], best->ds.opCharCount[DiffOp<Word>::add], best->ds.opCharCount[DiffOp<Word>::change], best->ds.charSimilarity,
     431          53 :             best->opIndexFrom, best->opLineFrom, best->opIndexTo, best->opLineTo);
     432             : 
     433          53 :         return true;
     434             :     }
     435             : 
     436         235 :     debugPrintf("nothing found in moved-line-map");
     437             : 
     438             :     // else:
     439             :     //     try to find a corresponding moved line in deleted/added lines
     440         235 :     int otherOp = (linediff[opIndex].op == DiffOp<String>::add ? DiffOp<String>::del : DiffOp<String>::add);
     441         470 :     std::shared_ptr<DiffMapEntry> found = nullptr;
     442        5644 :     for (int i = 0; i < linediff.size(); ++i) {
     443        5409 :         if (linediff[i].op == otherOp) {
     444         681 :             auto& lines = (linediff[opIndex].op == DiffOp<String>::add ? linediff[i].from : linediff[i].to);
     445        1611 :             for (int k = 0; k < lines.size(); ++k) {
     446         930 :                 auto it= diffMap.find(makeKey(i, k));
     447         930 :                 if(it!=diffMap.end())
     448             :                 {
     449         213 :                     auto found = it->second;
     450         213 :                     debugPrintf("found: lhsDisplayed=%s, rhsDisplayed=%s\n", found->lhsDisplayed? "true": "false", found->rhsDisplayed? "true": "false");
     451         213 :                     if( (printLeft && found->lhsDisplayed) || (printRight && found->rhsDisplayed) )
     452             :                     {
     453         129 :                         debugPrintf("%chs already displayed, not considering this one", printLeft? 'l': 'r');
     454         129 :                         continue;
     455             :                     }
     456             :                 }
     457         801 :                 std::shared_ptr<DiffMapEntry> tmp;
     458             :                 bool potentialMatch;
     459         801 :                 if (otherOp == DiffOp<String>::del) {
     460         395 :                     tmp = getDiffMapEntry(linediff[opIndex].to[opLine], lines[k], i, k, opIndex, opLine);
     461         395 :                     potentialMatch = cmpDiffMapEntries(tmp->opIndexFrom, tmp->opLineFrom);
     462             :                 } else {
     463         406 :                     tmp = getDiffMapEntry(lines[k], linediff[opIndex].from[opLine], opIndex, opLine, i, k);
     464         406 :                     potentialMatch = cmpDiffMapEntries(tmp->opIndexTo, tmp->opLineTo);
     465             :                 }
     466         801 :                 if (!found || (tmp->ds.charSimilarity > found->ds.charSimilarity) && potentialMatch) {
     467         349 :                     found= tmp;
     468             :                 }
     469             :             }
     470             :         }
     471             :     }
     472             : 
     473         235 :     if(found)
     474         186 :         debugPrintf("candidate found with similarity %.2f (from %d:%d to %d:%d)", found->ds.charSimilarity, found->opIndexFrom, found->opLineFrom, found->opIndexTo, found->opLineTo);
     475             : 
     476             :     // if candidate exists:
     477             :     //     add candidate to moved-line-map twice, for add/del case
     478             :     //     print diff to the moved line, omitting the left/right side for added/deleted line
     479         235 :     if (found && found->ds.charSimilarity > config.movedLineThreshold) {
     480             :         // if we displayed a diff to the found block before, don't display this one as moved.
     481          59 :         int otherIndex = linediff[opIndex].op == DiffOp<String>::add ? found->opIndexFrom : found->opIndexTo;
     482          59 :         int otherLine = linediff[opIndex].op == DiffOp<String>::add ? found->opLineFrom : found->opLineTo;
     483             : 
     484          59 :         if(!cmpDiffMapEntries(otherIndex, otherLine))
     485           0 :             return false;
     486             : 
     487          59 :         if(diffMap.find(makeKey(otherIndex, otherLine)) != diffMap.end()) {
     488           6 :             debugPrintf("found existing diffMap entry -- not overwriting.");
     489           6 :             return false;
     490             :         }
     491             : 
     492          53 :         if(printLeft)
     493          27 :             found->lhsDisplayed = true;
     494             :         else
     495          26 :             found->rhsDisplayed = true;
     496             : 
     497          53 :         diffMap[key] = found;
     498          53 :         diffMap[makeKey(otherIndex, otherLine)] = found;
     499          53 :         debugPrintf("inserting (%d,%d) + (%d,%d)", opIndex, opLine, otherIndex, otherLine);
     500             : 
     501          53 :         if(isNext(opIndex, opLine, otherIndex, otherLine)) {
     502           0 :             debugPrintf("This one immediately follows, displaying as change...");
     503           0 :             printWordDiffFromStrings(
     504           0 :                 linediff[found->opIndexFrom].from[found->opLineFrom],
     505           0 :                 linediff[found->opIndexTo].to[found->opLineTo],
     506             :                 leftLine, rightLine, offsetFrom, offsetTo);
     507           0 :             found->lhsDisplayed = true;
     508           0 :             found->rhsDisplayed = true;
     509             :         }
     510             :         else {
     511             :             // XXXX todo: we already have the diff, don't have to do it again, just have to print it
     512          53 :             printWordDiffFromStrings(
     513          53 :                 linediff[found->opIndexFrom].from[found->opLineFrom],
     514          53 :                 linediff[found->opIndexTo].to[found->opLineTo],
     515             :                 leftLine, rightLine, offsetFrom, offsetTo, printLeft, printRight,
     516         106 :                 makeAnchorName(opIndex, opLine, printLeft),
     517         106 :                 makeAnchorName(otherIndex, otherLine, !printLeft),
     518          53 :                 movedir(opIndex,opLine, otherIndex,otherLine));
     519             :         }
     520             : 
     521          53 :         debugPrintf("copy: %d, del: %d, add: %d, change: %d, similarity: %.4f\n"
     522             :                     "from: (%d,%d) to: (%d,%d)",
     523          53 :             found->ds.opCharCount[DiffOp<Word>::copy], found->ds.opCharCount[DiffOp<Word>::del], found->ds.opCharCount[DiffOp<Word>::add], found->ds.opCharCount[DiffOp<Word>::change], found->ds.charSimilarity,
     524          53 :             found->opIndexFrom, found->opLineFrom, found->opIndexTo, found->opLineTo);
     525             : 
     526          53 :         return true;
     527             :     }
     528             : 
     529         176 :     return false;
     530             : }
     531             : 
     532          94 : void Wikidiff2::explodeLines(const String & text, StringVector &lines)
     533             : {
     534          94 :     String::const_iterator ptr = text.begin();
     535        1354 :     while (ptr != text.end()) {
     536        1260 :         String::const_iterator ptr2 = std::find(ptr, text.end(), '\n');
     537        1260 :         lines.push_back(String(ptr, ptr2));
     538             : 
     539        1260 :         ptr = ptr2;
     540        1260 :         if (ptr != text.end()) {
     541        1198 :             ++ptr;
     542             :         }
     543             :     }
     544          94 : }
     545             : 
     546          47 : void Wikidiff2::execute(const String & text1, const String & text2)
     547             : {
     548             :     // Split input strings into lines
     549          94 :     StringVector lines1;
     550          94 :     StringVector lines2;
     551          47 :     explodeLines(text1, lines1);
     552          47 :     explodeLines(text2, lines2);
     553             : 
     554          47 :     wordDiffCache.setLines(&lines1, &lines2);
     555             : 
     556             :     // Do the diff
     557          94 :     StringDiff lineDiff(lineDiffConfig, lines1, lines2);
     558          47 :     lineDiffProcessor.process(lineDiff);
     559          47 :     printDiff(lineDiff);
     560             : 
     561          47 :     wordDiffCache.setLines(nullptr, nullptr);
     562          47 : }
     563             : 
     564          47 : void Wikidiff2::addFormatter(Formatter & formatter)
     565             : {
     566          47 :     formatters.push_back(&formatter);
     567          47 : }
     568             : 
     569             : } // namespace wikidiff2

Generated by: LCOV version 1.13