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 #ifndef MERGEEDITLINE_H
0012 #define MERGEEDITLINE_H
0013 
0014 #include "diff.h"
0015 #include "LineRef.h"
0016 
0017 #include <memory>
0018 
0019 #include <QString>
0020 
0021 using MergeEditLineList = std::list<class MergeEditLine>;
0022 
0023 class MergeEditLine
0024 {
0025   public:
0026     explicit MergeEditLine(const Diff3LineList::const_iterator& i, e_SrcSelector src = e_SrcSelector::None)
0027     {
0028         m_id3l = i;
0029         mSrc = src;
0030         mLineRemoved = false;
0031         mChanged = false;
0032     }
0033 
0034     void setConflict()
0035     {
0036         mSrc = e_SrcSelector::None;
0037         mLineRemoved = false;
0038         mChanged = false;
0039         mStr = QString();
0040     }
0041     [[nodiscard]] inline bool isConflict() const { return mSrc == e_SrcSelector::None && !mLineRemoved && !mChanged; }
0042 
0043     void setRemoved(e_SrcSelector src = e_SrcSelector::None)
0044     {
0045         mSrc = src;
0046         mLineRemoved = true;
0047         mStr = QString();
0048         mChanged = (src == e_SrcSelector::None);
0049     }
0050     [[nodiscard]] inline bool isRemoved() const { return mLineRemoved; }
0051 
0052     [[nodiscard]] inline bool isEditableText() const { return !isConflict(); }
0053 
0054     void setString(const QString& s)
0055     {
0056         mStr = s;
0057         mLineRemoved = false;
0058         mSrc = e_SrcSelector::None;
0059         mChanged = true;
0060     }
0061     [[nodiscard]] QString getString(const std::shared_ptr<const LineDataVector>& pLineDataA, const std::shared_ptr<const LineDataVector>& pLineDataB, const std::shared_ptr<const LineDataVector>& pLineDataC) const;
0062     [[nodiscard]] inline bool isModified() const { return mChanged; }
0063 
0064     void setSource(e_SrcSelector src, bool bLineRemoved)
0065     {
0066         mSrc = src;
0067         mLineRemoved = bLineRemoved;
0068         if(bLineRemoved && mSrc == e_SrcSelector::None)
0069             mChanged = true;
0070         else if(mSrc != e_SrcSelector::None)
0071         {
0072             mChanged = false;
0073             mStr = u8"";
0074         }
0075     }
0076 
0077     [[nodiscard]] inline e_SrcSelector src() const { return mSrc; }
0078     [[nodiscard]] inline Diff3LineList::const_iterator id3l() const { return m_id3l; }
0079 
0080   private:
0081     Diff3LineList::const_iterator m_id3l;
0082     e_SrcSelector mSrc; // 1, 2 or 3 for A, B or C respectively, or 0 when line is from neither source.
0083     QString mStr;       // String when modified by user or null-string when orig data is used.
0084     bool mLineRemoved;
0085     bool mChanged;
0086 };
0087 
0088 class MergeBlock
0089 {
0090   private:
0091     friend class MergeBlockList;
0092 
0093     Diff3LineList::const_iterator mId3l;
0094     LineType d3lLineIdx = -1;    // Needed to show the correct window pos.
0095     LineType srcRangeLength = 0; // how many src-lines have these properties
0096     e_MergeDetails mergeDetails = e_MergeDetails::eDefault;
0097     bool bConflict = false;
0098     bool bWhiteSpaceConflict = false;
0099     bool bDelta = false;
0100     e_SrcSelector srcSelect = e_SrcSelector::None;
0101     MergeEditLineList mMergeEditLineList;
0102 
0103   public:
0104     [[nodiscard]] inline const MergeEditLineList& list() const { return mMergeEditLineList; }
0105     [[nodiscard]] inline MergeEditLineList& list() { return mMergeEditLineList; }
0106 
0107     [[nodiscard]] inline e_SrcSelector source() const { return srcSelect; }
0108 
0109     [[nodiscard]] inline LineType getIndex() const { return d3lLineIdx; }
0110     [[nodiscard]] inline LineType sourceRangeLength() const { return srcRangeLength; }
0111 
0112     [[nodiscard]] inline bool isConflict() const { return bConflict; }
0113     [[nodiscard]] inline bool isWhiteSpaceConflict() const { return bWhiteSpaceConflict; }
0114     [[nodiscard]] inline bool isDelta() const { return bDelta; }
0115     [[nodiscard]] inline bool hasModfiedText()
0116     {
0117         return std::any_of(list().cbegin(), list().cend(), [](const MergeEditLine& mel) { return mel.isModified(); });
0118     }
0119 
0120     [[nodiscard]] inline Diff3LineList::const_iterator id3l() const { return mId3l; }
0121 
0122     [[nodiscard]] inline e_MergeDetails details() const { return mergeDetails; }
0123 
0124     [[nodiscard]] inline LineType lineCount() const { return SafeInt<qint32>(list().size()); }
0125 
0126     void split(MergeBlock& mb2, LineType d3lLineIdx2) // The caller must insert the mb2 after this mb in the m_mergeLineList
0127     {
0128         if(d3lLineIdx2 < d3lLineIdx || d3lLineIdx2 >= d3lLineIdx + srcRangeLength)
0129         {
0130             assert(false);
0131             return; //Error
0132         }
0133         mb2.mergeDetails = mergeDetails;
0134         mb2.bConflict = bConflict;
0135         mb2.bWhiteSpaceConflict = bWhiteSpaceConflict;
0136         mb2.bDelta = bDelta;
0137         mb2.srcSelect = srcSelect;
0138 
0139         mb2.d3lLineIdx = d3lLineIdx2;
0140         mb2.srcRangeLength = srcRangeLength - (d3lLineIdx2 - d3lLineIdx);
0141         srcRangeLength = d3lLineIdx2 - d3lLineIdx; // current MergeBlock controls fewer lines
0142         mb2.mId3l = mId3l;
0143         for(LineType i = 0; i < srcRangeLength; ++i)
0144             ++mb2.mId3l;
0145 
0146         mb2.mMergeEditLineList.clear();
0147         // Search for best place to splice
0148         for(MergeEditLineList::iterator i = mMergeEditLineList.begin(); i != mMergeEditLineList.end(); ++i)
0149         {
0150             if(i->id3l() == mb2.mId3l)
0151             {
0152                 mb2.mMergeEditLineList.splice(mb2.mMergeEditLineList.begin(), mMergeEditLineList, i, mMergeEditLineList.end());
0153                 return;
0154             }
0155         }
0156         mb2.mMergeEditLineList.push_back(MergeEditLine(mb2.mId3l));
0157     }
0158 
0159     void join(MergeBlock& mb2) // The caller must remove the ml2 from the m_mergeLineList after this call
0160     {
0161         srcRangeLength += mb2.srcRangeLength;
0162         mb2.mMergeEditLineList.clear();
0163         mMergeEditLineList.clear();
0164         mMergeEditLineList.push_back(MergeEditLine(mId3l)); // Create a simple conflict
0165         if(mb2.bConflict) bConflict = true;
0166         if(!mb2.bWhiteSpaceConflict) bWhiteSpaceConflict = false;
0167         if(mb2.bDelta) bDelta = true;
0168     }
0169 
0170     bool isSameKind(const MergeBlock& mb2) const;
0171 
0172     void mergeOneLine(const Diff3Line& diffRec, bool& bLineRemoved, bool bTwoInputs);
0173     void dectectWhiteSpaceConflict(const Diff3Line& d, const bool isThreeWay);
0174 
0175     void removeEmptySource();
0176 };
0177 
0178 class MergeBlockList: public std::list<MergeBlock>
0179 {
0180   public:
0181     using std::list<MergeBlock>::list;
0182 
0183     void buildFromDiff3(const Diff3LineList& diff3List, bool isThreeway);
0184     void updateDefaults(const e_SrcSelector defaultSelector, const bool bConflictsOnly, const bool bWhiteSpaceOnly);
0185 
0186     MergeBlockList::iterator splitAtDiff3LineIdx(qint32 d3lLineIdx);
0187 };
0188 
0189 #endif