File indexing completed on 2024-12-01 13:46:44

0001 // clang-format off
0002 /*
0003  * KDiff3 - Text Diff And Merge Tool
0004  *
0005  * SPDX-FileCopyrightText: 2002-2011 Joachim Eibl, joachim.eibl at gmx.de
0006  * SPDX-FileCopyrightText: 2018-2020 Michael Reeves reeves.87@gmail.com
0007  * SPDX-License-Identifier: GPL-2.0-or-later
0008  */
0009 // clang-format on
0010 
0011 #include "MergeEditLine.h"
0012 
0013 #include "diff.h"
0014 #include "LineRef.h"
0015 
0016 #include <memory>
0017 #include <optional>
0018 #include <vector>
0019 
0020 QString MergeEditLine::getString(const std::shared_ptr<const LineDataVector> &pLineDataA, const std::shared_ptr<const LineDataVector> &pLineDataB, const std::shared_ptr<const LineDataVector> &pLineDataC) const
0021 {
0022     //Triggered by resize event during early init. Ignore these calls.
0023     if((mSrc == e_SrcSelector::A && pLineDataA->empty()) || (mSrc == e_SrcSelector::B && pLineDataB->empty()) || (mSrc == e_SrcSelector::C && pLineDataC->empty()))
0024         return QString();
0025 
0026     if(isRemoved() || (!isModified() && mSrc == e_SrcSelector::None))
0027     {
0028         return QString();
0029     }
0030 
0031     if(!isModified())
0032     {
0033         std::optional<LineData> lineData;
0034         assert(mSrc == e_SrcSelector::A || mSrc == e_SrcSelector::B || mSrc == e_SrcSelector::C);
0035 
0036         if(mSrc == e_SrcSelector::A && m_id3l->getLineA().isValid())
0037             lineData = (*pLineDataA)[m_id3l->getLineA()];
0038         else if(mSrc == e_SrcSelector::B && m_id3l->getLineB().isValid())
0039             lineData = (*pLineDataB)[m_id3l->getLineB()];
0040         else if(mSrc == e_SrcSelector::C && m_id3l->getLineC().isValid())
0041             lineData = (*pLineDataC)[m_id3l->getLineC()];
0042 
0043         //Not an error.
0044         if(!lineData.has_value())
0045         {
0046             return QString();
0047         }
0048 
0049         return lineData->getLine();
0050     }
0051 
0052     return mStr;
0053 }
0054 
0055 bool MergeBlock::isSameKind(const MergeBlock &mb2) const
0056 {
0057     if(bConflict && mb2.bConflict)
0058     {
0059         // Both lines have conflicts: If one is only a white space conflict and
0060         // the other one is a real conflict, then this line returns false.
0061         return mId3l->isEqualAC() == mb2.mId3l->isEqualAC() && mId3l->isEqualAB() == mb2.mId3l->isEqualAB();
0062     }
0063     else
0064         return (
0065             (!bConflict && !mb2.bConflict && bDelta && mb2.bDelta && srcSelect == mb2.srcSelect && (mergeDetails == mb2.mergeDetails || (mergeDetails != e_MergeDetails::eBCAddedAndEqual && mb2.mergeDetails != e_MergeDetails::eBCAddedAndEqual))) ||
0066             (!bDelta && !mb2.bDelta));
0067 }
0068 
0069 // Calculate the merge information for the given Diff3Line.
0070 // Results will be stored in this and bLineRemoved.
0071 void MergeBlock::mergeOneLine(const Diff3Line &diffRec, bool &bLineRemoved, bool bTwoInputs)
0072 {
0073     mergeDetails = e_MergeDetails::eDefault;
0074     bConflict = false;
0075     bLineRemoved = false;
0076     srcSelect = e_SrcSelector::None;
0077 
0078     if(bTwoInputs) // Only two input files
0079     {
0080         if(diffRec.getLineA().isValid() && diffRec.getLineB().isValid())
0081         {
0082             if(!diffRec.hasFineDiffAB())
0083             {
0084                 mergeDetails = e_MergeDetails::eNoChange;
0085                 srcSelect = e_SrcSelector::A;
0086             }
0087             else
0088             {
0089                 mergeDetails = e_MergeDetails::eBChanged;
0090                 bConflict = true;
0091             }
0092         }
0093         else
0094         {
0095             mergeDetails = e_MergeDetails::eBDeleted;
0096             bConflict = true;
0097         }
0098         return;
0099     }
0100 
0101     // A is base.
0102     if(diffRec.getLineA().isValid() && diffRec.getLineB().isValid() && diffRec.getLineC().isValid())
0103     {
0104         if(!diffRec.hasFineDiffAB() && !diffRec.hasFineDiffBC() && !diffRec.hasFineDiffCA())
0105         {
0106             mergeDetails = e_MergeDetails::eNoChange;
0107             srcSelect = e_SrcSelector::A;
0108         }
0109         else if(!diffRec.hasFineDiffAB() && diffRec.hasFineDiffBC() && diffRec.hasFineDiffCA())
0110         {
0111             mergeDetails = e_MergeDetails::eCChanged;
0112             srcSelect = e_SrcSelector::C;
0113         }
0114         else if(diffRec.hasFineDiffAB() && diffRec.hasFineDiffBC() && !diffRec.hasFineDiffCA())
0115         {
0116             mergeDetails = e_MergeDetails::eBChanged;
0117             srcSelect = e_SrcSelector::B;
0118         }
0119         else if(diffRec.hasFineDiffAB() && !diffRec.hasFineDiffBC() && diffRec.hasFineDiffCA())
0120         {
0121             mergeDetails = e_MergeDetails::eBCChangedAndEqual;
0122             srcSelect = e_SrcSelector::C;
0123         }
0124         else if(diffRec.hasFineDiffAB() && diffRec.hasFineDiffBC() && diffRec.hasFineDiffCA())
0125         {
0126             mergeDetails = e_MergeDetails::eBCChanged;
0127             bConflict = true;
0128         }
0129         else
0130         {
0131             assert(false);
0132         }
0133     }
0134     else if(diffRec.getLineA().isValid() && diffRec.getLineB().isValid() && !diffRec.getLineC().isValid())
0135     {
0136         if(diffRec.hasFineDiffAB())
0137         {
0138             mergeDetails = e_MergeDetails::eBChanged_CDeleted;
0139             bConflict = true;
0140         }
0141         else
0142         {
0143             mergeDetails = e_MergeDetails::eCDeleted;
0144             bLineRemoved = true;
0145             srcSelect = e_SrcSelector::C;
0146         }
0147     }
0148     else if(diffRec.getLineA().isValid() && !diffRec.getLineB().isValid() && diffRec.getLineC().isValid())
0149     {
0150         if(diffRec.hasFineDiffCA())
0151         {
0152             mergeDetails = e_MergeDetails::eCChanged_BDeleted;
0153             bConflict = true;
0154         }
0155         else
0156         {
0157             mergeDetails = e_MergeDetails::eBDeleted;
0158             bLineRemoved = true;
0159             srcSelect = e_SrcSelector::B;
0160         }
0161     }
0162     else if(!diffRec.getLineA().isValid() && diffRec.getLineB().isValid() && diffRec.getLineC().isValid())
0163     {
0164         if(diffRec.hasFineDiffBC())
0165         {
0166             mergeDetails = e_MergeDetails::eBCAdded;
0167             bConflict = true;
0168         }
0169         else // B==C
0170         {
0171             mergeDetails = e_MergeDetails::eBCAddedAndEqual;
0172             srcSelect = e_SrcSelector::C;
0173         }
0174     }
0175     else if(!diffRec.getLineA().isValid() && !diffRec.getLineB().isValid() && diffRec.getLineC().isValid())
0176     {
0177         mergeDetails = e_MergeDetails::eCAdded;
0178         srcSelect = e_SrcSelector::C;
0179     }
0180     else if(!diffRec.getLineA().isValid() && diffRec.getLineB().isValid() && !diffRec.getLineC().isValid())
0181     {
0182         mergeDetails = e_MergeDetails::eBAdded;
0183         srcSelect = e_SrcSelector::B;
0184     }
0185     else if(diffRec.getLineA().isValid() && !diffRec.getLineB().isValid() && !diffRec.getLineC().isValid())
0186     {
0187         mergeDetails = e_MergeDetails::eBCDeleted;
0188         bLineRemoved = true;
0189         srcSelect = e_SrcSelector::C;
0190     }
0191     else
0192         assert(false);
0193 }
0194 /*
0195     Build a new MergeBlockList from scratch using a Diff3LineList.
0196 */
0197 void MergeBlockList::buildFromDiff3(const Diff3LineList &diff3List, bool isThreeway)
0198 {
0199     LineType lineIdx = 0;
0200     for(auto it = diff3List.cbegin(); it != diff3List.cend(); ++it)
0201     {
0202         const Diff3Line &d = *it;
0203         MergeBlock mb;
0204         bool bLineRemoved;
0205 
0206         mb.mergeOneLine(d, bLineRemoved, !isThreeway);
0207         mb.dectectWhiteSpaceConflict(d, isThreeway);
0208 
0209         mb.d3lLineIdx = lineIdx;
0210         mb.bDelta = mb.srcSelect != e_SrcSelector::A;
0211         mb.mId3l = it;
0212         mb.srcRangeLength = 1;
0213 
0214         MergeBlock *lBack = empty() ? nullptr : &back();
0215 
0216         bool bSame = lBack != nullptr && mb.isSameKind(*lBack);
0217         if(bSame)
0218         {
0219             ++lBack->srcRangeLength;
0220             if(lBack->isWhiteSpaceConflict() && !mb.isWhiteSpaceConflict())
0221                 lBack->bWhiteSpaceConflict = false;
0222         }
0223         else
0224         {
0225             push_back(mb);
0226         }
0227 
0228         if(!mb.isConflict())
0229         {
0230             MergeBlock &tmpBack = back();
0231             MergeEditLine mel(mb.id3l());
0232             mel.setSource(mb.srcSelect, bLineRemoved);
0233             tmpBack.list().push_back(mel);
0234         }
0235         else if(lBack == nullptr || !lBack->isConflict() || !bSame)
0236         {
0237             MergeBlock &tmpBack = back();
0238             MergeEditLine mel(mb.id3l());
0239             mel.setConflict();
0240             tmpBack.list().push_back(mel);
0241         }
0242 
0243         ++lineIdx;
0244     }
0245 }
0246 /*
0247     Changes default merge settings currently used when not in auto mode or if white space is being auto solved.
0248 */
0249 void MergeBlockList::updateDefaults(const e_SrcSelector defaultSelector, const bool bConflictsOnly, const bool bWhiteSpaceOnly)
0250 {
0251     // Change all auto selections
0252     MergeBlockList::iterator mbIt;
0253     for(mbIt = begin(); mbIt != end(); ++mbIt)
0254     {
0255         MergeBlock &mb = *mbIt;
0256         bool bConflict = mb.list().empty() || mb.list().begin()->isConflict();
0257         if(mb.isDelta() && !(mb.hasModfiedText() && (bConflictsOnly || bWhiteSpaceOnly)) && (!bConflictsOnly || bConflict) && (!bWhiteSpaceOnly || mb.isWhiteSpaceConflict()))
0258         {
0259             mb.list().clear();
0260             if(defaultSelector == e_SrcSelector::Invalid)
0261             {
0262                 MergeEditLine mel(mb.id3l());
0263 
0264                 mel.setConflict();
0265                 mb.bConflict = true;
0266                 mb.list().push_back(mel);
0267             }
0268             else
0269             {
0270                 Diff3LineList::const_iterator d3llit = mb.id3l();
0271                 qint32 j;
0272 
0273                 for(j = 0; j < mb.srcRangeLength; ++j)
0274                 {
0275                     MergeEditLine mel(d3llit);
0276                     mel.setSource(defaultSelector, false);
0277 
0278                     LineRef srcLine = defaultSelector == e_SrcSelector::A ? d3llit->getLineA() : defaultSelector == e_SrcSelector::B ? d3llit->getLineB() :
0279                                                                                              defaultSelector == e_SrcSelector::C     ? d3llit->getLineC() :
0280                                                                                                                                        LineRef();
0281                     if(srcLine.isValid())
0282                     {
0283                         mb.list().push_back(mel);
0284                     }
0285 
0286                     ++d3llit;
0287                 }
0288 
0289                 if(mb.list().empty()) // Make a line nevertheless
0290                 {
0291                     MergeEditLine mel(mb.id3l());
0292                     mel.setRemoved(defaultSelector);
0293                     mb.list().push_back(mel);
0294                 }
0295             }
0296         }
0297     }
0298 }
0299 
0300 void MergeBlock::dectectWhiteSpaceConflict(const Diff3Line &d, const bool isThreeWay)
0301 {
0302     // Automatic solving for only whitespace changes.
0303     if(isConflict() &&
0304        ((!isThreeWay && (d.isEqualAB() || (d.isWhiteLine(e_SrcSelector::A) && d.isWhiteLine(e_SrcSelector::B)))) ||
0305         (isThreeWay && ((d.isEqualAB() && d.isEqualAC()) || (d.isWhiteLine(e_SrcSelector::A) && d.isWhiteLine(e_SrcSelector::B) && d.isWhiteLine(e_SrcSelector::C))))))
0306     {
0307         bWhiteSpaceConflict = true;
0308     }
0309 }
0310 // Remove all lines that are empty, because no src lines are there.
0311 void MergeBlock::removeEmptySource()
0312 {
0313     LineRef oldSrcLine;
0314     e_SrcSelector oldSrc = e_SrcSelector::Invalid;
0315     MergeEditLineList::iterator melIt;
0316     for(melIt = list().begin(); melIt != list().end();)
0317     {
0318         MergeEditLine& mel = *melIt;
0319         e_SrcSelector melsrc = mel.src();
0320 
0321         LineRef srcLine = mel.isRemoved() ? LineRef() : melsrc == e_SrcSelector::A ? mel.id3l()->getLineA() : melsrc == e_SrcSelector::B ? mel.id3l()->getLineB() : melsrc == e_SrcSelector::C ? mel.id3l()->getLineC() : LineRef();
0322 
0323         // At least one line remains because oldSrc != melsrc for first line in list
0324         // Other empty lines will be removed
0325         if(!srcLine.isValid() && !oldSrcLine.isValid() && oldSrc == melsrc)
0326             melIt = list().erase(melIt);
0327         else
0328             ++melIt;
0329 
0330         oldSrcLine = srcLine;
0331         oldSrc = melsrc;
0332     }
0333 }
0334 
0335 // Returns the iterator to the MergeBlock after the split
0336 MergeBlockList::iterator MergeBlockList::splitAtDiff3LineIdx(qint32 d3lLineIdx)
0337 {
0338     MergeBlockList::iterator i;
0339     for(i = begin(); i != end(); ++i)
0340     {
0341         if(i->getIndex() == d3lLineIdx)
0342         {
0343             // No split needed, this is the beginning of a MergeBlock
0344             return i;
0345         }
0346         else if(i->getIndex() > d3lLineIdx)
0347         {
0348             // The split must be in the previous MergeBlock
0349             --i;
0350             MergeBlock &mb = *i;
0351             MergeBlock newMB;
0352             mb.split(newMB, d3lLineIdx);
0353             ++i;
0354             return insert(i, newMB);
0355         }
0356     }
0357     // The split must be in the previous MergeBlock
0358     --i;
0359     MergeBlock &mb = *i;
0360     MergeBlock newMB;
0361     mb.split(newMB, d3lLineIdx);
0362     ++i;
0363     return insert(i, newMB);
0364 }