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 }