diff --git a/CMakeLists.txt b/CMakeLists.txt index 9e875c7..206ff43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,107 +1,107 @@ #cmake < 3.1 has no sane way of checking C++11 features and needed flags cmake_minimum_required(VERSION 3.1 FATAL_ERROR) project(kdiff3) set(CMAKE_CXX_EXTENSIONS OFF ) #don't use non-standard extensions set(CMAKE_EXPORT_COMPILE_COMMANDS ON) set(ECM_MIN_VERSION "5.10.0") set(QT_MIN_VERSION "5.6.0") set(KF5_MIN_VERSION "5.23.0") find_package(ECM ${ECM_MIN_VERSION} CONFIG REQUIRED) set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH} ) include(KDEInstallDirs) include(KDECompilerSettings NO_POLICY_SCOPE) include(KDECMakeSettings NO_POLICY_SCOPE) include(FeatureSummary) include(ECMInstallIcons) include(ECMAddAppIcon) include(ECMSetupVersion) ecm_setup_version(1.9.70 VARIABLE_PREFIX KDIFF3 VERSION_HEADER ${CMAKE_BINARY_DIR}/src/version.h) find_package( Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED COMPONENTS Core Gui Widgets PrintSupport ) find_package( KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS I18n CoreAddons Crash DocTools IconThemes ) option(ENABLE_CLANG_TIDY "Run clang-tidy if available and cmake version >=3.6" OFF) -set(KDiff3_LIBRARIES ${Qt5PrintSupport_LIBRARIES} KF5::I18n KF5::CoreAddons KF5::Crash KF5::IconThemes ) +set(KDiff3_LIBRARIES ${Qt5PrintSupport_LIBRARIES} KF5::I18n KF5::CoreAddons KF5::IconThemes ) if (CMAKE_CXX_COMPILER_ID MATCHES "Clang") #Adjust clang specific warnings set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wshadow") set(CLANG_WARNING_FLAGS "-Wno-invalid-pp-token -Wno-comment -Wshorten-64-to-32 -Wstring-conversion -Wc++11-narrowing -fstack-protector-all") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${CLANG_WARNING_FLAGS}") elseif(CMAKE_CXX_COMPILER_ID MATCHES "MSVC") add_definitions(-DNOMINMAX) #Suppress MSVCs min/max macros elseif(CMAKE_CXX_COMPILER_ID MATCHES "GNU") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fstack-check") if(CMAKE_CXX_COMPILER_VERSION VERSION_GREATER 7.0) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wduplicated-cond -Wduplicated-branches -Wshadow") endif() endif() #new in cmake 3.6+ integrate clang-tidy if(ENABLE_CLANG_TIDY AND NOT ${CMAKE_VERSION} VERSION_LESS "3.6.0") find_program(CLANG_TIDY_EXE NAMES "clang-tidy" "clang-tidy-7" "clang-tidy-6.0" "clang-tidy-6" DOC "Path to clang-tidy executable") if(NOT CLANG_TIDY_EXE) message(STATUS "clang-tidy not found disabling integration.") else() message(STATUS "Found clang-tidy: ${CLANG_TIDY_EXE}") set(CMAKE_CXX_CLANG_TIDY "${CLANG_TIDY_EXE}" "-header-filter=.*") endif() endif() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") set( needed_features cxx_nullptr cxx_override cxx_nonstatic_member_init cxx_inheriting_constructors cxx_static_assert ) add_definitions( -DQT_DEPRECATED_WARNINGS #Get warnings from QT about deprecated functions. -DQT_NO_URL_CAST_FROM_STRING # casting from string to url does not always behave as you might think -DQT_RESTRICTED_CAST_FROM_ASCII #casting from char*/QByteArray to QString can produce unexpected results for non-latin characters. -DQT_NO_CAST_TO_ASCII ) add_subdirectory(src) add_subdirectory(doc) add_subdirectory(kdiff3fileitemactionplugin) ki18n_install(po) kdoctools_install(po) feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/src/diff.h b/src/diff.h index 70e5138..91c9708 100644 --- a/src/diff.h +++ b/src/diff.h @@ -1,452 +1,450 @@ /*************************************************************************** * Copyright (C) 2003-2007 by Joachim Eibl * * Copyright (C) 2018 Michael Reeves reeves.87@gmail.com * * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifndef DIFF_H #define DIFF_H #include "common.h" #include "fileaccess.h" #include "options.h" #include "gnudiff_diff.h" #include "SourceData.h" #include "Logging.h" #include //enum must be sequential with no gaps to allow loop interiation of values enum e_SrcSelector { Min = -1, Invalid=-1, None=0, A = 1, B = 2, C = 3, Max=C }; enum e_MergeDetails { eDefault, eNoChange, eBChanged, eCChanged, eBCChanged, // conflict eBCChangedAndEqual, // possible conflict eBDeleted, eCDeleted, eBCDeleted, // possible conflict eBChanged_CDeleted, // conflict eCChanged_BDeleted, // conflict eBAdded, eCAdded, eBCAdded, // conflict eBCAddedAndEqual // possible conflict }; // Each range with matching elements is followed by a range with differences on either side. // Then again range of matching elements should follow. class Diff { public: qint32 nofEquals; qint64 diff1; qint64 diff2; Diff(qint32 eq, qint64 d1, qint64 d2) { nofEquals = eq; diff1 = d1; diff2 = d2; } }; typedef std::list DiffList; class LineData { private: QSharedPointer mBuffer; //QString pLine; QtNumberType mFirstNonWhiteChar = 0; qint64 mOffset = 0; QtNumberType mSize = 0; Q_DECL_DEPRECATED bool bContainsPureComment = false; public: explicit LineData() = default; // needed for QtInternal reasons should not be used. inline LineData(const QSharedPointer &buffer, const qint64 inOffset, QtNumberType inSize = 0) { mBuffer = buffer; mOffset = inOffset; mSize = inSize; } Q_REQUIRED_RESULT inline int size() const { return mSize; } inline void setFirstNonWhiteChar(const qint32 firstNonWhiteChar) { mFirstNonWhiteChar = firstNonWhiteChar;} Q_REQUIRED_RESULT inline qint32 getFirstNonWhiteChar() const { return mFirstNonWhiteChar; } /* QString::fromRawData allows us to create a light weight QString backed by the buffer memmory. */ Q_REQUIRED_RESULT inline const QString getLine() const { return QString::fromRawData(mBuffer->data() + mOffset, mSize); } - //inline void setLine(const QString& line) { pLine = line;} - - Q_REQUIRED_RESULT const QSharedPointer& getBuffer() const { return mBuffer; } + Q_REQUIRED_RESULT inline const QSharedPointer& getBuffer() const { return mBuffer; } Q_REQUIRED_RESULT inline qint64 getOffset() const { return mOffset; } Q_REQUIRED_RESULT int width(int tabSize) const; // Calcs width considering tabs. //int occurrences; - bool whiteLine() const { return mFirstNonWhiteChar == mSize - 1; } + inline bool whiteLine() const { return mFirstNonWhiteChar == mSize - 1; } - bool isPureComment() const { return bContainsPureComment; } - void setPureComment(const bool bPureComment) { bContainsPureComment = bPureComment; } + inline bool isPureComment() const { return bContainsPureComment; } + inline void setPureComment(const bool bPureComment) { bContainsPureComment = bPureComment; } static bool equal(const LineData& l1, const LineData& l2, bool bStrict); }; class Diff3LineList; class Diff3LineVector; class DiffBufferInfo { public: const QVector* m_pLineDataA; const QVector* m_pLineDataB; const QVector* m_pLineDataC; LineCount m_sizeA; LineCount m_sizeB; LineCount m_sizeC; const Diff3LineList* m_pDiff3LineList; const Diff3LineVector* m_pDiff3LineVector; void init(Diff3LineList* d3ll, const Diff3LineVector* d3lv, const QVector* pldA, LineCount sizeA, const QVector* pldB, LineCount sizeB, const QVector* pldC, LineCount sizeC); }; class Diff3Line { private: LineRef lineA; LineRef lineB; LineRef lineC; public: bool bAEqC = false; // These are true if equal or only white-space changes exist. bool bBEqC = false; bool bAEqB = false; bool bWhiteLineA = false; bool bWhiteLineB = false; bool bWhiteLineC = false; DiffList* pFineAB = nullptr; // These are 0 only if completely equal or if either source doesn't exist. DiffList* pFineBC = nullptr; DiffList* pFineCA = nullptr; int linesNeededForDisplay = 1; // Due to wordwrap int sumLinesNeededForDisplay = 0; // For fast conversion to m_diff3WrapLineVector DiffBufferInfo* m_pDiffBufferInfo = nullptr; // For convenience ~Diff3Line() { if(pFineAB != nullptr) delete pFineAB; if(pFineBC != nullptr) delete pFineBC; if(pFineCA != nullptr) delete pFineCA; pFineAB = nullptr; pFineBC = nullptr; pFineCA = nullptr; } LineRef getLineA() const { return lineA; } LineRef getLineB() const { return lineB; } LineRef getLineC() const { return lineC; } inline void setLineA(const LineRef& line) { lineA = line; } inline void setLineB(const LineRef& line) { lineB = line; } inline void setLineC(const LineRef& line) { lineC = line; } inline bool isEqualAB() const { return bAEqB; } inline bool isEqualAC() const { return bAEqC; } inline bool isEqualBC() const { return bBEqC; } bool operator==(const Diff3Line& d3l) const { return lineA == d3l.lineA && lineB == d3l.lineB && lineC == d3l.lineC && bAEqB == d3l.bAEqB && bAEqC == d3l.bAEqC && bBEqC == d3l.bBEqC; } const LineData* getLineData(e_SrcSelector src) const { Q_ASSERT(m_pDiffBufferInfo != nullptr); if(src == A && lineA >= 0) return &(*m_pDiffBufferInfo->m_pLineDataA)[lineA]; if(src == B && lineB >= 0) return &(*m_pDiffBufferInfo->m_pLineDataB)[lineB]; if(src == C && lineC >= 0) return &(*m_pDiffBufferInfo->m_pLineDataC)[lineC]; return nullptr; } const QString getString(const e_SrcSelector src) const { const LineData* pld = getLineData(src); if(pld) return pld->getLine(); else return QString(); } LineRef getLineInFile(e_SrcSelector src) const { if(src == A) return lineA; if(src == B) return lineB; if(src == C) return lineC; return -1; } bool fineDiff(bool bTextsTotalEqual, const e_SrcSelector selector, const QVector* v1, const QVector* v2); void mergeOneLine(e_MergeDetails& mergeDetails, bool& bConflict, bool& bLineRemoved, e_SrcSelector& src, bool bTwoInputs) const; void getLineInfo(const e_SrcSelector winIdx, const bool isTriple, int& lineIdx, DiffList*& pFineDiff1, DiffList*& pFineDiff2, // return values int& changed, int& changed2) const; private: void setFineDiff(const e_SrcSelector selector, DiffList* pDiffList) { Q_ASSERT(selector == A || selector == B || selector == C); if(selector == A) { if(pFineAB != nullptr) delete pFineAB; pFineAB = pDiffList; } else if(selector == B) { if(pFineBC != nullptr) delete pFineBC; pFineBC = pDiffList; } else if(selector == C) { if(pFineCA) delete pFineCA; pFineCA = pDiffList; } } }; class Diff3LineList : public std::list { public: bool fineDiff(const e_SrcSelector selector, const QVector* v1, const QVector* v2); void calcDiff3LineVector(Diff3LineVector& d3lv); void calcWhiteDiff3Lines(const QVector* pldA, const QVector* pldB, const QVector* pldC); //TODO: Add safety guards to prevent list from getting too large. Same problem as with QLinkedList. int size() const { if(std::list::size() > std::numeric_limits::max()) { qCDebug(kdiffMain) << "Diff3Line: List too large. size=" << std::list::size(); Q_ASSERT(false); //Unsupported size return 0; } return (int)std::list::size(); } //safe for small files same limit as exited with QLinkedList. This should ultimatly be removed. void debugLineCheck(const LineCount size, const e_SrcSelector srcSelector) const; }; class Diff3LineVector : public QVector { }; class Diff3WrapLine { public: Diff3Line* pD3L; int diff3LineIndex; int wrapLineOffset; int wrapLineLength; }; typedef QVector Diff3WrapLineVector; class TotalDiffStatus { public: inline void reset() { bBinaryAEqC = false; bBinaryBEqC = false; bBinaryAEqB = false; bTextAEqC = false; bTextBEqC = false; bTextAEqB = false; nofUnsolvedConflicts = 0; nofSolvedConflicts = 0; nofWhitespaceConflicts = 0; } inline int getUnsolvedConflicts() const { return nofUnsolvedConflicts; } inline void setUnsolvedConflicts(const int unsolved) { nofUnsolvedConflicts = unsolved; } inline int getSolvedConflicts() const { return nofSolvedConflicts; } inline void setSolvedConflicts(const int solved) { nofSolvedConflicts = solved; } inline int getWhitespaceConflicts() const { return nofWhitespaceConflicts; } inline void setWhitespaceConflicts(const int wintespace) { nofWhitespaceConflicts = wintespace; } inline int getNonWhitespaceConflicts() { return getUnsolvedConflicts() + getSolvedConflicts() - getWhitespaceConflicts(); } bool isBinaryEqualAC() const { return bBinaryAEqC; } bool isBinaryEqualBC() const { return bBinaryBEqC; } bool isBinaryEqualAB() const { return bBinaryAEqB; } bool bBinaryAEqC = false; bool bBinaryBEqC = false; bool bBinaryAEqB = false; bool bTextAEqC = false; bool bTextBEqC = false; bool bTextAEqB = false; private: int nofUnsolvedConflicts = 0; int nofSolvedConflicts = 0; int nofWhitespaceConflicts = 0; }; class ManualDiffHelpList; // A list of corresponding ranges // Three corresponding ranges. (Minimum size of a valid range is one line.) class ManualDiffHelpEntry { private: LineRef lineA1; LineRef lineA2; LineRef lineB1; LineRef lineB2; LineRef lineC1; LineRef lineC2; public: LineRef& firstLine(e_SrcSelector winIdx) { return winIdx == A ? lineA1 : (winIdx == B ? lineB1 : lineC1); } LineRef& lastLine(e_SrcSelector winIdx) { return winIdx == A ? lineA2 : (winIdx == B ? lineB2 : lineC2); } bool isLineInRange(LineRef line, e_SrcSelector winIdx) { return line >= 0 && line >= firstLine(winIdx) && line <= lastLine(winIdx); } bool operator==(const ManualDiffHelpEntry& r) const { return lineA1 == r.lineA1 && lineB1 == r.lineB1 && lineC1 == r.lineC1 && lineA2 == r.lineA2 && lineB2 == r.lineB2 && lineC2 == r.lineC2; } int calcManualDiffFirstDiff3LineIdx(const Diff3LineVector& d3lv); void getRangeForUI(const e_SrcSelector winIdx, int *rangeLine1, int *rangeLine2) const { if(winIdx == A) { *rangeLine1 = lineA1; *rangeLine2 = lineA2; } if(winIdx == B) { *rangeLine1 = lineB1; *rangeLine2 = lineB2; } if(winIdx == C) { *rangeLine1 = lineC1; *rangeLine2 = lineC2; } } inline int getLine1(const e_SrcSelector winIdx) const { return winIdx == A ? lineA1 : winIdx == B ? lineB1 : lineC1;} inline int getLine2(const e_SrcSelector winIdx) const { return winIdx == A ? lineA2 : winIdx == B ? lineB2 : lineC2;} bool isValidMove(int line1, int line2, e_SrcSelector winIdx1, e_SrcSelector winIdx2) const; }; // A list of corresponding ranges class ManualDiffHelpList: public std::list { public: bool isValidMove(int line1, int line2, e_SrcSelector winIdx1, e_SrcSelector winIdx2) const; void insertEntry(e_SrcSelector winIdx, LineRef firstLine, LineRef lastLine); bool runDiff(const QVector* p1, LineRef size1, const QVector* p2, LineRef size2, DiffList& diffList, e_SrcSelector winIdx1, e_SrcSelector winIdx2, Options* pOptions); }; void calcDiff(const QString &line1, const QString &line2, DiffList& diffList, int match, int maxSearchRange); void calcDiff3LineListUsingAB( const DiffList* pDiffListAB, Diff3LineList& d3ll); void calcDiff3LineListUsingAC( const DiffList* pDiffListAC, Diff3LineList& d3ll); void calcDiff3LineListUsingBC( const DiffList* pDiffListBC, Diff3LineList& d3ll); void correctManualDiffAlignment(Diff3LineList& d3ll, ManualDiffHelpList* pManualDiffHelpList); void calcDiff3LineListTrim(Diff3LineList& d3ll, const QVector* pldA, const QVector* pldB, const QVector* pldC, ManualDiffHelpList* pManualDiffHelpList); bool fineDiff( Diff3LineList& diff3LineList, int selector, const QVector* v1, const QVector* v2); inline bool isWhite(QChar c) { return c == ' ' || c == '\t' || c == '\r'; } /** Returns the number of equivalent spaces at position outPos. */ inline int tabber(int outPos, int tabSize) { return tabSize - (outPos % tabSize); } /** Returns a line number where the linerange [line, line+nofLines] can be displayed best. If it fits into the currently visible range then the returned value is the current firstLine. */ int getBestFirstLine(int line, int nofLines, int firstLine, int visibleLines); extern bool g_bIgnoreWhiteSpace; extern bool g_bIgnoreTrivialMatches; extern int g_bAutoSolve; // Cursor conversions that consider g_tabSize. int convertToPosInText(const QString& s, int posOnScreen, int tabSize); int convertToPosOnScreen(const QString& s, int posInText, int tabSize); enum e_CoordType { eFileCoords, eD3LLineCoords, eWrapCoords }; void calcTokenPos(const QString&, int posOnScreen, int& pos1, int& pos2, int tabSize); QString calcHistorySortKey(const QString& keyOrder, QRegExp& matchedRegExpr, const QStringList& parenthesesGroupList); bool findParenthesesGroups(const QString& s, QStringList& sl); #endif