woboq / moc-ng

A reimplementation of Qt's moc using libclang. Contains both a drop-in remplacement, and a plugin for the clang compiler.
https://woboq.com/blog/moc-with-clang.html
GNU General Public License v3.0
204 stars 24 forks source link

Qt Creator master (upcoming 4.0) not compiling with moc-ng #7

Closed cristianadam closed 8 years ago

cristianadam commented 8 years ago

I've build moc-ng master against libclang 3.6.2 with clang 3.7.1 over visual c++ 2013.

I had to apply the following patch:

diff -Naur moc-ng-master-orig/cmake/modules/FindClang.cmake moc-ng-master/cmake/modules/FindClang.cmake
--- moc-ng-master-orig/cmake/modules/FindClang.cmake    2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/cmake/modules/FindClang.cmake 2016-02-25 22:48:24.437793400 +0100
@@ -6,7 +6,7 @@
 MACRO(FIND_AND_ADD_CLANG_LIB _libname_)
 find_library(CLANG_${_libname_}_LIB ${_libname_} ${LLVM_LIB_DIR} ${CLANG_LIB_DIR})
 if (CLANG_${_libname_}_LIB)
-   set(CLANG_LIBS ${CLANG_LIBS} ${CLANG_${_libname_}_LIB})
+   list(APPEND CLANG_LIBS ${CLANG_${_libname_}_LIB})
 endif(CLANG_${_libname_}_LIB)
 ENDMACRO(FIND_AND_ADD_CLANG_LIB)

@@ -40,7 +40,7 @@

-MESSAGE(STATUS "Clang libs: " ${CLANG_LIBS})
+MESSAGE(STATUS "Clang libs: ${CLANG_LIBS}")

 if(CLANG_LIBS)
   set(CLANG_FOUND TRUE)
diff -Naur moc-ng-master-orig/cmake/modules/FindLLVM.cmake moc-ng-master/cmake/modules/FindLLVM.cmake
--- moc-ng-master-orig/cmake/modules/FindLLVM.cmake 2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/cmake/modules/FindLLVM.cmake  2016-02-25 23:17:06.780936300 +0100
@@ -46,10 +46,13 @@
 MESSAGE(STATUS "LLVM CXX flags: " ${LLVM_COMPILE_FLAGS})
 execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --ldflags  OUTPUT_VARIABLE LLVM_LDFLAGS OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
 #--system-libs is new in llvm 3.5
-execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs  OUTPUT_VARIABLE LLVM_LDFLAGS2 OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
-string(REPLACE "\n" " " LLVM_LDFLAGS "${LLVM_LDFLAGS} ${LLVM_LDFLAGS2}")
+#execute_process(COMMAND ${LLVM_CONFIG_EXECUTABLE} --system-libs  OUTPUT_VARIABLE LLVM_LDFLAGS2 OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET)
+#string(REPLACE "\n" " " LLVM_LDFLAGS "${LLVM_LDFLAGS} ${LLVM_LDFLAGS2}")
+string(REPLACE "-L" "-LIBDIR:" LLVM_LDFLAGS "${LLVM_LDFLAGS}")
 MESSAGE(STATUS "LLVM LD flags: " ${LLVM_LDFLAGS})
 exec_program(${LLVM_CONFIG_EXECUTABLE} ARGS --libs      OUTPUT_VARIABLE LLVM_LIBS_CORE )
+string(REPLACE "-l" "" LLVM_LIBS_CORE "${LLVM_LIBS_CORE}")
+string(REPLACE " " ".lib " LLVM_LIBS_CORE "${LLVM_LIBS_CORE}")
 MESSAGE(STATUS "LLVM core libs: " ${LLVM_LIBS_CORE})
 FIND_LLVM_LIBS( ${LLVM_CONFIG_EXECUTABLE} "jit native" LLVM_LIBS_JIT LLVM_LIBS_JIT_OBJECTS )
 #STRING(REPLACE " -lLLVMCore -lLLVMSupport -lLLVMSystem" "" LLVM_LIBS_JIT ${LLVM_LIBS_JIT_RAW})
diff -Naur moc-ng-master-orig/src/CMakeLists.txt moc-ng-master/src/CMakeLists.txt
--- moc-ng-master-orig/src/CMakeLists.txt   2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/src/CMakeLists.txt    2016-02-25 22:42:46.584899700 +0100
@@ -31,30 +31,28 @@
 SET(common_srcs mocng.cpp generator.cpp propertyparser.cpp mocppcallbacks.cpp mocastconsumer.cpp
                 qbjs.cpp clangversionabstraction.cpp workaroundtests.cpp)

-add_library(mocng_plugin SHARED plugin.cpp ${common_srcs})
+#add_library(mocng_plugin SHARED plugin.cpp ${common_srcs})

-target_link_libraries(mocng_plugin  ${CLANG_LIBS} ${LLVM_LIBS_CORE} )
+#target_link_libraries(mocng_plugin  ${CLANG_LIBS} ${LLVM_LIBS_CORE} )

-set_target_properties(mocng_plugin PROPERTIES
-         LINKER_LANGUAGE CXX
-         LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/ExportedSymbolsList"
-         SOVERSION 1.0)
+#set_target_properties(mocng_plugin PROPERTIES
+#         LINKER_LANGUAGE CXX
+#         LINK_FLAGS "-Wl,--version-script=${CMAKE_CURRENT_SOURCE_DIR}/ExportedSymbolsList"
+#         SOVERSION 1.0)

 add_executable(moc  main.cpp ${common_srcs})
-target_link_libraries(moc   ${CLANG_LIBS} ${LLVM_LIBS_CORE} ${LLVM_LDFLAGS} )
+target_link_libraries(moc ${CLANG_LIBS} ${LLVM_LIBS_CORE} ${LLVM_LDFLAGS})

 # install(TARGETS mocng_plugin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})
 # install(TARGETS moc LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})

-SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_COMPILE_FLAGS}  -std=c++11 -fno-exceptions -Wall")
+#SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${LLVM_COMPILE_FLAGS}  -std=c++11 -fno-exceptions -Wall")

 if (APPLE)
     SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}  -stdlib=libc++ ")
     SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}  -stdlib=libc++" )
 endif()

-
-
 # Embed the clang header into the binary:
 exec_program(${LLVM_CONFIG_EXECUTABLE} ARGS --version      OUTPUT_VARIABLE LLVM_VERSION )
 string(REPLACE "svn" "" LLVM_VERSION "${LLVM_VERSION}")
@@ -68,5 +66,3 @@

 configure_file(embedded_includes.h.in embedded_includes.h)
 include_directories(${CMAKE_CURRENT_BINARY_DIR})
-
-
diff -Naur moc-ng-master-orig/src/generator.cpp moc-ng-master/src/generator.cpp
--- moc-ng-master-orig/src/generator.cpp    2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/src/generator.cpp 2016-02-26 00:20:46.686911100 +0100
@@ -26,6 +26,7 @@
 #include <clang/AST/DeclTemplate.h>
 #include <clang/Sema/Sema.h>

+#include <cctype>
 #include <iostream>

 // Remove the decltype if possible
@@ -181,7 +182,7 @@

     // Remove the spaces;
     int k = 0;
-    for (uint i = 0; i < TypeString.size(); ++i) {
+    for (int i = 0; i < TypeString.size(); ++i) {
         char C = TypeString[i];
         if (C == ' ') {
             if (k == 0)
@@ -199,7 +200,7 @@
     TypeString.resize(k);

     //adjust unsigned
-    uint UPos = 0;
+    int UPos = 0;
     while ((UPos = TypeString.find("unsigned ", UPos)) < TypeString.size()) {
         const int L = sizeof("unsigned ") - 1; // don't include \0
         llvm::StringRef R(&TypeString[UPos + L],
@@ -758,7 +759,7 @@
         ForEachMethod(CDef->Constructors, [&](const clang::CXXConstructorDecl *MD, int C) {
             OS << "        case " << (CtorIndex++) << ": { " << resultType << " *_r = new " << ClassName << "(";

-            for (uint j = 0 ; j < MD->getNumParams() - C; ++j) {
+            for (int j = 0 ; j < MD->getNumParams() - C; ++j) {
                 if (j) OS << ",";
                 if (j == MD->getNumParams() - 1 && HasPrivateSignal(MD))
                     OS << "QPrivateSignal()";
@@ -813,7 +814,7 @@

             OS << "_t->" << MD->getName() << "(";

-            for (uint j = 0 ; j < MD->getNumParams() - Clone; ++j) {
+            for (int j = 0 ; j < MD->getNumParams() - Clone; ++j) {
                 if (j) OS << ",";
                 if (j == MD->getNumParams() - 1 && HasPrivateSignal(MD))
                     OS << "QPrivateSignal()";
@@ -841,7 +842,7 @@
                 if (!IsVoid)
                     OS << "{ " << P.ReturnType << " _r =  ";
                 OS << "_t->" << P.InPrivateClass << "->" << P.Name << "(";
-                for (uint j = 0 ; j < P.Args.size() - Clone; ++j) {
+                for (int j = 0 ; j < P.Args.size() - Clone; ++j) {
                     if (j) OS << ",";
                     OS << "*reinterpret_cast< " << P.Args[j] << " *>(_a[" << (j+1) << "])";
                 }
@@ -904,7 +905,7 @@
                 continue;
             OS << "        {\n"
                   "            typedef " << getResultType(MD).getAsString(PrintPolicy) << " (" << ClassName << "::*_t)(";
-            for (uint j = 0 ; j < MD->getNumParams(); ++j) {
+            for (int j = 0 ; j < MD->getNumParams(); ++j) {
                 if (j) OS << ",";
                 OS << MD->getParamDecl(j)->getType().getAsString(PrintPolicy);
             }
@@ -1068,7 +1069,7 @@
     else
         OS << ReturnType.getAsString(PrintPolicy) << " ";
     OS << QualName << "::" << MD->getName() + "(";
-    for (uint j = 0 ; j < MD->getNumParams(); ++j) {
+    for (int j = 0 ; j < MD->getNumParams(); ++j) {
         if (j) OS << ",";
         OS << MD->getParamDecl(j)->getType().getAsString(PrintPolicy);
         if (!(j == MD->getNumParams() - 1 && HasPrivateSignal(MD)))
@@ -1100,7 +1101,7 @@
         else OS << "&_t0";

-        for (uint j = 0 ; j < NumParam; ++j) {
+        for (int j = 0 ; j < NumParam; ++j) {
             if (MD->getParamDecl(j)->getType().isVolatileQualified())
                 OS << ", const_cast<void*>(reinterpret_cast<const volatile void*>(&_t" << (j+1) << "))";
             else
diff -Naur moc-ng-master-orig/src/mocng.cpp moc-ng-master/src/mocng.cpp
--- moc-ng-master-orig/src/mocng.cpp    2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/src/mocng.cpp 2016-02-25 22:35:27.278056400 +0100
@@ -486,7 +486,7 @@
     if (TD) {
         if (!TD->hasDefinition())
             return false;
-        for (uint I = 0; I < TD->getTemplateArgs().size(); ++I) {
+        for (int I = 0; I < TD->getTemplateArgs().size(); ++I) {
             const auto &Arg = TD->getTemplateArgs().get(I);
             if (Arg.getKind() == clang::TemplateArgument::Type) {
                 if (!ShouldRegisterMetaType(Arg.getAsType()))
diff -Naur moc-ng-master-orig/src/qbjs.cpp moc-ng-master/src/qbjs.cpp
--- moc-ng-master-orig/src/qbjs.cpp 2016-02-24 10:28:10.000000000 +0100
+++ moc-ng-master/src/qbjs.cpp  2016-02-25 22:59:05.484253900 +0100
@@ -17,6 +17,7 @@
  *  along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */

+#include <cstdint>
 #include "qbjs.h"
 #include <llvm/Support/raw_ostream.h>
 #include <llvm/Support/YAMLParser.h> 

Compilation of Qt Creator stopped with the following error:

release\diffeditorplugin.moc(49) : error C3083: 'DiffEditorController': the symbol to the left of a '::' must be a type
release\diffeditorplugin.moc(61) : error C3083: 'DiffEditorController': the symbol to the left of a '::' must be a type
release\diffeditorplugin.moc(61) : error C2352: 'DiffEditor::Internal::DiffEditor::qt_metacast' : illegal call of non-static member function
        c:\projects\qt-creator\src\plugins\diffeditor\diffeditor.h(54) : see declaration of 'DiffEditor::Internal::DiffEditor::qt_metacast'
release\diffeditorplugin.moc(66) : error C3083: 'DiffEditorController': the symbol to the left of a '::' must be a type
release\diffeditorplugin.moc(66) : error C2352: 'DiffEditor::Internal::DiffEditor::qt_metacall' : illegal call of non-static member function
        c:\projects\qt-creator\src\plugins\diffeditor\diffeditor.h(54) : see declaration of 'DiffEditor::Internal::DiffEditor::qt_metacall'

The difference between moc and moc-ng dor diffeditorplugin.moc:

diff -Naur moc/diffeditorplugin.moc moc-ng/diffeditorplugin.moc
--- moc/diffeditorplugin.moc    2016-02-25 23:37:53.545071100 +0100
+++ moc-ng/diffeditorplugin.moc 2016-02-26 00:34:42.957233600 +0100
@@ -1,88 +1,77 @@
 /****************************************************************************
-** Meta object code from reading C++ file 'diffeditorplugin.cpp'
-**
-** Created by: The Qt Meta Object Compiler version 67 (Qt 5.5.1)
+** Meta object code from reading C++ file 'C:\Projects\qt-creator\src\plugins\diffeditor\diffeditorplugin.cpp'
 **
+** Created by MOC-NG version alpha 1 by Woboq [https://woboq.com]
 ** WARNING! All changes made in this file will be lost!
 *****************************************************************************/

 #include <QtCore/qbytearray.h>
-#include <QtCore/qmetatype.h>
 #if !defined(Q_MOC_OUTPUT_REVISION)
-#error "The header file 'diffeditorplugin.cpp' doesn't include <QObject>."
+#error "The header file 'C:\Projects\qt-creator\src\plugins\diffeditor\diffeditorplugin.cpp' doesn't include <QObject>."
 #elif Q_MOC_OUTPUT_REVISION != 67
-#error "This file was generated using the moc from 5.5.1. It"
-#error "cannot be used with the include files from this version of Qt."
-#error "(The moc has changed too much.)"
+#error "This file was generated using MOC-NG alpha 1."
+#error "It cannot be used with the include files from this version of Qt."
 #endif

 QT_BEGIN_MOC_NAMESPACE
+
+static const uint qt_meta_data_DiffEditor__Internal__FileDiffController[] = {
+    7, // revision
+    0, // classname
+    0, 0, //classinfo
+    0, 0, // methods 
+    0, 0, // properties 
+    0, 0, // enums 
+    0, 0, // constructors 
+    0, // flags 
+    0, // signalCount 
+
+    0    // eod
+};
 struct qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t {
     QByteArrayData data[1];
-    char stringdata0[41];
+    char stringdata[42];
 };
 #define QT_MOC_LITERAL(idx, ofs, len) \
     Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
-    qptrdiff(offsetof(qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t, stringdata0) + ofs \
+    qptrdiff(offsetof(qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t, stringdata) + ofs \
         - idx * sizeof(QByteArrayData)) \
     )
 static const qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t qt_meta_stringdata_DiffEditor__Internal__FileDiffController = {
     {
-QT_MOC_LITERAL(0, 0, 40) // "DiffEditor::Internal::FileDif..."
-
+QT_MOC_LITERAL(0, 0, 40)
     },
-    "DiffEditor::Internal::FileDiffController"
+    "DiffEditor::Internal::FileDiffController\0"
 };
 #undef QT_MOC_LITERAL

-static const uint qt_meta_data_DiffEditor__Internal__FileDiffController[] = {
-
- // content:
-       7,       // revision
-       0,       // classname
-       0,    0, // classinfo
-       0,    0, // methods
-       0,    0, // properties
-       0,    0, // enums/sets
-       0,    0, // constructors
-       0,       // flags
-       0,       // signalCount
-
-       0        // eod
-};
-
-void DiffEditor::Internal::FileDiffController::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
-{
-    Q_UNUSED(_o);
-    Q_UNUSED(_id);
-    Q_UNUSED(_c);
-    Q_UNUSED(_a);
-}
-
 const QMetaObject DiffEditor::Internal::FileDiffController::staticMetaObject = {
-    { &DiffEditorController::staticMetaObject, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.data,
-      qt_meta_data_DiffEditor__Internal__FileDiffController,  qt_static_metacall, Q_NULLPTR, Q_NULLPTR}
+    { &DiffEditor::DiffEditorController::staticMetaObject, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.data,
+      qt_meta_data_DiffEditor__Internal__FileDiffController, qt_static_metacall, 0, 0}
 };
-
-
 const QMetaObject *DiffEditor::Internal::FileDiffController::metaObject() const
 {
     return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
 }
-
 void *DiffEditor::Internal::FileDiffController::qt_metacast(const char *_clname)
 {
-    if (!_clname) return Q_NULLPTR;
-    if (!strcmp(_clname, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.stringdata0))
-        return static_cast<void*>(const_cast< FileDiffController*>(this));
-    return DiffEditorController::qt_metacast(_clname);
+    if (!_clname) return 0;
+    if (!strcmp(_clname, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.stringdata))
+        return static_cast<void*>(this);
+    return DiffEditor::DiffEditorController::qt_metacast(_clname);
 }

 int DiffEditor::Internal::FileDiffController::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
 {
-    _id = DiffEditorController::qt_metacall(_c, _id, _a);
+    _id = DiffEditor::DiffEditorController::qt_metacall(_c, _id, _a);
     if (_id < 0)
         return _id;
-    return _id;
+
+    return _id;}
+
+void DiffEditor::Internal::FileDiffController::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
+{
+    
+    Q_UNUSED(_o); Q_UNUSED(_id); Q_UNUSED(_c); Q_UNUSED(_a);
 }
 QT_END_MOC_NAMESPACE

Complete diffeditorplugin.moc:

/****************************************************************************
** Meta object code from reading C++ file 'C:\Projects\qt-creator\src\plugins\diffeditor\diffeditorplugin.cpp'
**
** Created by MOC-NG version alpha 1 by Woboq [https://woboq.com]
** WARNING! All changes made in this file will be lost!
*****************************************************************************/

#include <QtCore/qbytearray.h>
#if !defined(Q_MOC_OUTPUT_REVISION)
#error "The header file 'C:\Projects\qt-creator\src\plugins\diffeditor\diffeditorplugin.cpp' doesn't include <QObject>."
#elif Q_MOC_OUTPUT_REVISION != 67
#error "This file was generated using MOC-NG alpha 1."
#error "It cannot be used with the include files from this version of Qt."
#endif

QT_BEGIN_MOC_NAMESPACE

static const uint qt_meta_data_DiffEditor__Internal__FileDiffController[] = {
    7, // revision
    0, // classname
    0, 0, //classinfo
    0, 0, // methods 
    0, 0, // properties 
    0, 0, // enums 
    0, 0, // constructors 
    0, // flags 
    0, // signalCount 

    0    // eod
};
struct qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t {
    QByteArrayData data[1];
    char stringdata[42];
};
#define QT_MOC_LITERAL(idx, ofs, len) \
    Q_STATIC_BYTE_ARRAY_DATA_HEADER_INITIALIZER_WITH_OFFSET(len, \
    qptrdiff(offsetof(qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t, stringdata) + ofs \
        - idx * sizeof(QByteArrayData)) \
    )
static const qt_meta_stringdata_DiffEditor__Internal__FileDiffController_t qt_meta_stringdata_DiffEditor__Internal__FileDiffController = {
    {
QT_MOC_LITERAL(0, 0, 40)
    },
    "DiffEditor::Internal::FileDiffController\0"
};
#undef QT_MOC_LITERAL

const QMetaObject DiffEditor::Internal::FileDiffController::staticMetaObject = {
    { &DiffEditor::DiffEditorController::staticMetaObject, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.data,
      qt_meta_data_DiffEditor__Internal__FileDiffController, qt_static_metacall, 0, 0}
};
const QMetaObject *DiffEditor::Internal::FileDiffController::metaObject() const
{
    return QObject::d_ptr->metaObject ? QObject::d_ptr->dynamicMetaObject() : &staticMetaObject;
}
void *DiffEditor::Internal::FileDiffController::qt_metacast(const char *_clname)
{
    if (!_clname) return 0;
    if (!strcmp(_clname, qt_meta_stringdata_DiffEditor__Internal__FileDiffController.stringdata))
        return static_cast<void*>(this);
    return DiffEditor::DiffEditorController::qt_metacast(_clname);
}

int DiffEditor::Internal::FileDiffController::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
    _id = DiffEditor::DiffEditorController::qt_metacall(_c, _id, _a);
    if (_id < 0)
        return _id;

    return _id;}

void DiffEditor::Internal::FileDiffController::qt_static_metacall(QObject *_o, QMetaObject::Call _c, int _id, void **_a)
{

    Q_UNUSED(_o); Q_UNUSED(_id); Q_UNUSED(_c); Q_UNUSED(_a);
}
QT_END_MOC_NAMESPACE

And complete diffeditorplugin.cpp:

/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of Qt Creator.
**
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 3 as published by the Free Software
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
**
****************************************************************************/

#include "diffeditorplugin.h"
#include "diffeditor.h"
#include "diffeditorconstants.h"
#include "diffeditorcontroller.h"
#include "diffeditordocument.h"
#include "diffeditorfactory.h"
#include "differ.h"

#include <QAction>
#include <QFileDialog>
#include <QTextCodec>
#include <QtPlugin>

#include <coreplugin/actionmanager/actioncontainer.h>
#include <coreplugin/actionmanager/actionmanager.h>
#include <coreplugin/coreconstants.h>
#include <coreplugin/editormanager/editormanager.h>
#include <coreplugin/icore.h>

#include <utils/qtcassert.h>

namespace DiffEditor {
namespace Internal {

class FileDiffController : public DiffEditorController
{
    Q_OBJECT
public:
    FileDiffController(Core::IDocument *document, const QString &leftFileName,
                       const QString &rightFileName);

protected:
    void reload();

private:
    QString m_leftFileName;
    QString m_rightFileName;
};

FileDiffController::FileDiffController(Core::IDocument *document, const QString &leftFileName,
                                       const QString &rightFileName) :
    DiffEditorController(document), m_leftFileName(leftFileName), m_rightFileName(rightFileName)
{ }

void FileDiffController::reload()
{
    QString errorString;
    Utils::TextFileFormat format;
    format.codec = Core::EditorManager::defaultTextCodec();

    QString leftText;
    if (Utils::TextFileFormat::readFile(m_leftFileName,
                                    format.codec,
                                    &leftText, &format, &errorString)
            != Utils::TextFileFormat::ReadSuccess) {
        return;
    }

    QString rightText;
    if (Utils::TextFileFormat::readFile(m_rightFileName,
                                    format.codec,
                                    &rightText, &format, &errorString)
            != Utils::TextFileFormat::ReadSuccess) {
        return;
    }

    Differ differ;
    QList<Diff> diffList = differ.cleanupSemantics(differ.diff(leftText, rightText));

    QList<Diff> leftDiffList;
    QList<Diff> rightDiffList;
    Differ::splitDiffList(diffList, &leftDiffList, &rightDiffList);
    QList<Diff> outputLeftDiffList;
    QList<Diff> outputRightDiffList;

    if (ignoreWhitespace()) {
        const QList<Diff> leftIntermediate
                = Differ::moveWhitespaceIntoEqualities(leftDiffList);
        const QList<Diff> rightIntermediate
                = Differ::moveWhitespaceIntoEqualities(rightDiffList);
        Differ::ignoreWhitespaceBetweenEqualities(leftIntermediate, rightIntermediate,
                                                  &outputLeftDiffList, &outputRightDiffList);
    } else {
        outputLeftDiffList = leftDiffList;
        outputRightDiffList = rightDiffList;
    }

    const ChunkData chunkData = DiffUtils::calculateOriginalData(
                outputLeftDiffList, outputRightDiffList);
    FileData fileData = DiffUtils::calculateContextData(chunkData, contextLineCount(), 0);
    fileData.leftFileInfo.fileName = m_leftFileName;
    fileData.rightFileInfo.fileName = m_rightFileName;

    QList<FileData> fileDataList;
    fileDataList << fileData;

    setDiffFiles(fileDataList);
    reloadFinished(true);
}

/////////////////

bool DiffEditorPlugin::initialize(const QStringList &arguments, QString *errorMessage)
{
    Q_UNUSED(arguments)
    Q_UNUSED(errorMessage)

    //register actions
    Core::ActionContainer *toolsContainer
            = Core::ActionManager::actionContainer(Core::Constants::M_TOOLS);
    toolsContainer->insertGroup(Core::Constants::G_TOOLS_OPTIONS, Constants::G_TOOLS_DIFF);

    QAction *diffAction = new QAction(tr("Diff..."), this);
    Core::Command *diffCommand = Core::ActionManager::registerAction(diffAction, "DiffEditor.Diff");
    connect(diffAction, &QAction::triggered, this, &DiffEditorPlugin::diff);
    toolsContainer->addAction(diffCommand, Constants::G_TOOLS_DIFF);

    addAutoReleasedObject(new DiffEditorFactory(this));

    return true;
}

void DiffEditorPlugin::extensionsInitialized()
{ }

void DiffEditorPlugin::diff()
{
    QString fileName1 = QFileDialog::getOpenFileName(Core::ICore::dialogParent(),
                                                     tr("Select First File for Diff"),
                                                     QString());
    if (fileName1.isNull())
        return;

    QString fileName2 = QFileDialog::getOpenFileName(Core::ICore::dialogParent(),
                                                     tr("Select Second File for Diff"),
                                                     QString());
    if (fileName2.isNull())
        return;

    const QString documentId = QLatin1String("Diff ") + fileName1 + QLatin1String(", ") + fileName2;
    QString title = tr("Diff \"%1\", \"%2\"").arg(fileName1).arg(fileName2);
    auto const document = qobject_cast<DiffEditorDocument *>(
                DiffEditorController::findOrCreateDocument(documentId, title));
    if (!document)
        return;

    if (!DiffEditorController::controller(document))
        new FileDiffController(document, fileName1, fileName2);
    Core::EditorManager::activateEditorForDocument(document);
    document->reload();
}

} // namespace Internal
} // namespace DiffEditor

#ifdef WITH_TESTS

#include <QTest>

#include "diffutils.h"

Q_DECLARE_METATYPE(DiffEditor::ChunkData)
Q_DECLARE_METATYPE(DiffEditor::FileData)

static inline QString _(const char *string) { return QString::fromLatin1(string); }

void DiffEditor::Internal::DiffEditorPlugin::testMakePatch_data()
{
    QTest::addColumn<ChunkData>("sourceChunk");
    QTest::addColumn<QString>("leftFileName");
    QTest::addColumn<QString>("rightFileName");
    QTest::addColumn<bool>("lastChunk");
    QTest::addColumn<QString>("patchText");

    const QString fileName = _("a.txt");
    const QString header = _("--- ") + fileName + _("\n+++ ") + fileName + _("\n");

    QList<RowData> rows;
    rows << RowData(_("ABCD"), TextLineData::Separator);
    rows << RowData(_("EFGH"));
    ChunkData chunk;
    chunk.rows = rows;
    QString patchText = header + _("@@ -1,2 +1,1 @@\n"
                                   "-ABCD\n"
                                   " EFGH\n");
    QTest::newRow("Simple not a last chunk") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    // chunk the same here
    patchText = header + _("@@ -1,2 +1,1 @@\n"
                           "-ABCD\n"
                           " EFGH\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("Simple last chunk") << chunk
                            << fileName
                            << fileName
                            << true
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"));
    rows << RowData(_(""), TextLineData::Separator);
    chunk.rows = rows;
    patchText = header + _("@@ -1,1 +1,1 @@\n"
                           "-ABCD\n"
                           "+ABCD\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("EOL in last line removed") << chunk
                            << fileName
                            << fileName
                            << true
                            << patchText;

    ///////////

    // chunk the same here
    patchText = header + _("@@ -1,2 +1,1 @@\n"
                           " ABCD\n"
                           "-\n");

    QTest::newRow("Last empty line removed") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"));
    rows << RowData(_(""), TextLineData::Separator);
    rows << RowData(_(""), TextLineData::Separator);
    chunk.rows = rows;
    patchText = header + _("@@ -1,2 +1,1 @@\n"
                           "-ABCD\n"
                           "-\n"
                           "+ABCD\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("Two last EOLs removed") << chunk
                            << fileName
                            << fileName
                            << true
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"));
    rows << RowData(TextLineData::Separator, _(""));
    chunk.rows = rows;
    patchText = header + _("@@ -1,1 +1,1 @@\n"
                           "-ABCD\n"
                           "\\ No newline at end of file\n"
                           "+ABCD\n");

    QTest::newRow("EOL to last line added") << chunk
                            << fileName
                            << fileName
                            << true
                            << patchText;

    ///////////

    // chunk the same here
    patchText = header + _("@@ -1,1 +1,2 @@\n"
                           " ABCD\n"
                           "+\n");

    QTest::newRow("Last empty line added") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"), _("EFGH"));
    chunk.rows = rows;
    patchText = header + _("@@ -1,1 +1,1 @@\n"
                           "-ABCD\n"
                           "+EFGH\n");

    QTest::newRow("Last line with a newline modified") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"), _("EFGH"));
    rows << RowData(_(""));
    chunk.rows = rows;
    patchText = header + _("@@ -1,2 +1,2 @@\n"
                           "-ABCD\n"
                           "+EFGH\n"
                           " \n");
    QTest::newRow("Not a last line with a newline modified") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"), _("EFGH"));
    chunk.rows = rows;
    patchText = header + _("@@ -1,1 +1,1 @@\n"
                           "-ABCD\n"
                           "\\ No newline at end of file\n"
                           "+EFGH\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("Last line without a newline modified") << chunk
                            << fileName
                            << fileName
                            << true
                            << patchText;

    ///////////

    // chunk the same here
    patchText = header + _("@@ -1,1 +1,1 @@\n"
                           "-ABCD\n"
                           "+EFGH\n");
    QTest::newRow("Not a last line without a newline modified") << chunk
                            << fileName
                            << fileName
                            << false
                            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"), _("EFGH"));
    rows << RowData(_("IJKL"));
    chunk.rows = rows;
    patchText = header + _("@@ -1,2 +1,2 @@\n"
                           "-ABCD\n"
                           "+EFGH\n"
                           " IJKL\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("Last but one line modified, last line without a newline")
            << chunk
            << fileName
            << fileName
            << true
            << patchText;

    ///////////

    // chunk the same here
    patchText = header + _("@@ -1,2 +1,2 @@\n"
                           "-ABCD\n"
                           "+EFGH\n"
                           " IJKL\n");

    QTest::newRow("Last but one line modified, last line with a newline")
            << chunk
            << fileName
            << fileName
            << false
            << patchText;

    ///////////

    rows.clear();
    rows << RowData(_("ABCD"));
    rows << RowData(TextLineData::Separator, _(""));
    rows << RowData(_(""), _("EFGH"));
    chunk.rows = rows;
    patchText = header + _("@@ -1,1 +1,3 @@\n"
                           " ABCD\n"
                           "+\n"
                           "+EFGH\n"
                           "\\ No newline at end of file\n");

    QTest::newRow("Blank line followed by No newline")
            << chunk
            << fileName
            << fileName
            << true
            << patchText;
}

void DiffEditor::Internal::DiffEditorPlugin::testMakePatch()
{
    QFETCH(ChunkData, sourceChunk);
    QFETCH(QString, leftFileName);
    QFETCH(QString, rightFileName);
    QFETCH(bool, lastChunk);
    QFETCH(QString, patchText);

    QString result = DiffUtils::makePatch(sourceChunk, leftFileName, rightFileName, lastChunk);

    QCOMPARE(result, patchText);

    bool ok;
    QList<FileData> resultList = DiffUtils::readPatch(result, &ok);

    QVERIFY(ok);
    QCOMPARE(resultList.count(), 1);
    for (int i = 0; i < resultList.count(); i++) {
        const FileData &resultFileData = resultList.at(i);
        QCOMPARE(resultFileData.leftFileInfo.fileName, leftFileName);
        QCOMPARE(resultFileData.rightFileInfo.fileName, rightFileName);
        QCOMPARE(resultFileData.chunks.count(), 1);
        for (int j = 0; j < resultFileData.chunks.count(); j++) {
            const ChunkData &resultChunkData = resultFileData.chunks.at(j);
            QCOMPARE(resultChunkData.leftStartingLineNumber, sourceChunk.leftStartingLineNumber);
            QCOMPARE(resultChunkData.rightStartingLineNumber, sourceChunk.rightStartingLineNumber);
            QCOMPARE(resultChunkData.contextChunk, sourceChunk.contextChunk);
            QCOMPARE(resultChunkData.rows.count(), sourceChunk.rows.count());
            for (int k = 0; k < sourceChunk.rows.count(); k++) {
                const RowData &sourceRowData = sourceChunk.rows.at(k);
                const RowData &resultRowData = resultChunkData.rows.at(k);
                QCOMPARE(resultRowData.equal, sourceRowData.equal);
                QCOMPARE(resultRowData.leftLine.text, sourceRowData.leftLine.text);
                QCOMPARE(resultRowData.leftLine.textLineType, sourceRowData.leftLine.textLineType);
                QCOMPARE(resultRowData.rightLine.text, sourceRowData.rightLine.text);
                QCOMPARE(resultRowData.rightLine.textLineType, sourceRowData.rightLine.textLineType);
            }
        }
    }
}

void DiffEditor::Internal::DiffEditorPlugin::testReadPatch_data()
{
    QTest::addColumn<QString>("sourcePatch");
    QTest::addColumn<QList<FileData> >("fileDataList");

    QString patch = _("diff --git a/src/plugins/diffeditor/diffeditor.cpp b/src/plugins/diffeditor/diffeditor.cpp\n"
                      "index eab9e9b..082c135 100644\n"
                      "--- a/src/plugins/diffeditor/diffeditor.cpp\n"
                      "+++ b/src/plugins/diffeditor/diffeditor.cpp\n"
                      "@@ -187,9 +187,6 @@ void DiffEditor::ctor()\n"
                      "     m_controller = m_document->controller();\n"
                      "     m_guiController = new DiffEditorGuiController(m_controller, this);\n"
                      " \n"
                      "-//    m_sideBySideEditor->setDiffEditorGuiController(m_guiController);\n"
                      "-//    m_unifiedEditor->setDiffEditorGuiController(m_guiController);\n"
                      "-\n"
                      "     connect(m_controller, SIGNAL(cleared(QString)),\n"
                      "             this, SLOT(slotCleared(QString)));\n"
                      "     connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),\n"
                      "diff --git a/src/plugins/diffeditor/diffutils.cpp b/src/plugins/diffeditor/diffutils.cpp\n"
                      "index 2f641c9..f8ff795 100644\n"
                      "--- a/src/plugins/diffeditor/diffutils.cpp\n"
                      "+++ b/src/plugins/diffeditor/diffutils.cpp\n"
                      "@@ -464,5 +464,12 @@ QString DiffUtils::makePatch(const ChunkData &chunkData,\n"
                      "     return diffText;\n"
                      " }\n"
                      " \n"
                      "+FileData DiffUtils::makeFileData(const QString &patch)\n"
                      "+{\n"
                      "+    FileData fileData;\n"
                      "+\n"
                      "+    return fileData;\n"
                      "+}\n"
                      "+\n"
                      " } // namespace Internal\n"
                      " } // namespace DiffEditor\n"
                      "diff --git a/new b/new\n"
                      "new file mode 100644\n"
                      "index 0000000..257cc56\n"
                      "--- /dev/null\n"
                      "+++ b/new\n"
                      "@@ -0,0 +1 @@\n"
                      "+foo\n"
                      "diff --git a/deleted b/deleted\n"
                      "deleted file mode 100644\n"
                      "index 257cc56..0000000\n"
                      "--- a/deleted\n"
                      "+++ /dev/null\n"
                      "@@ -1 +0,0 @@\n"
                      "-foo\n"
                      "diff --git a/empty b/empty\n"
                      "new file mode 100644\n"
                      "index 0000000..e69de29\n"
                      "diff --git a/empty b/empty\n"
                      "deleted file mode 100644\n"
                      "index e69de29..0000000\n"
                      "diff --git a/file a.txt b/file b.txt\n"
                      "similarity index 99%\n"
                      "copy from file a.txt\n"
                      "copy to file b.txt\n"
                      "index 1234567..9876543\n"
                      "--- a/file a.txt\n"
                      "+++ b/file b.txt\n"
                      "@@ -20,3 +20,3 @@\n"
                      " A\n"
                      "-B\n"
                      "+C\n"
                      " D\n"
                      "diff --git a/file a.txt b/file b.txt\n"
                      "similarity index 99%\n"
                      "rename from file a.txt\n"
                      "rename to file b.txt\n"
                      "diff --git a/file.txt b/file.txt\n"
                      "old mode 100644\n"
                      "new mode 100755\n"
                      "index 1234567..9876543\n"
                      "--- a/file.txt\n"
                      "+++ b/file.txt\n"
                      "@@ -20,3 +20,3 @@\n"
                      " A\n"
                      "-B\n"
                      "+C\n"
                      " D\n"
                      );

    FileData fileData1;
    fileData1.leftFileInfo = DiffFileInfo(_("src/plugins/diffeditor/diffeditor.cpp"), _("eab9e9b"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/plugins/diffeditor/diffeditor.cpp"), _("082c135"));
    ChunkData chunkData1;
    chunkData1.leftStartingLineNumber = 186;
    chunkData1.rightStartingLineNumber = 186;
    QList<RowData> rows1;
    rows1 << RowData(_("    m_controller = m_document->controller();"));
    rows1 << RowData(_("    m_guiController = new DiffEditorGuiController(m_controller, this);"));
    rows1 << RowData(_(""));
    rows1 << RowData(_("//    m_sideBySideEditor->setDiffEditorGuiController(m_guiController);"), TextLineData::Separator);
    rows1 << RowData(_("//    m_unifiedEditor->setDiffEditorGuiController(m_guiController);"), TextLineData::Separator);
    rows1 << RowData(_(""), TextLineData::Separator);
    rows1 << RowData(_("    connect(m_controller, SIGNAL(cleared(QString)),"));
    rows1 << RowData(_("            this, SLOT(slotCleared(QString)));"));
    rows1 << RowData(_("    connect(m_controller, SIGNAL(diffContentsChanged(QList<DiffEditorController::DiffFilesContents>,QString)),"));
    chunkData1.rows = rows1;
    fileData1.chunks << chunkData1;

    FileData fileData2;
    fileData2.leftFileInfo = DiffFileInfo(_("src/plugins/diffeditor/diffutils.cpp"), _("2f641c9"));
    fileData2.rightFileInfo = DiffFileInfo(_("src/plugins/diffeditor/diffutils.cpp"), _("f8ff795"));
    ChunkData chunkData2;
    chunkData2.leftStartingLineNumber = 463;
    chunkData2.rightStartingLineNumber = 463;
    QList<RowData> rows2;
    rows2 << RowData(_("    return diffText;"));
    rows2 << RowData(_("}"));
    rows2 << RowData(_(""));
    rows2 << RowData(TextLineData::Separator, _("FileData DiffUtils::makeFileData(const QString &patch)"));
    rows2 << RowData(TextLineData::Separator, _("{"));
    rows2 << RowData(TextLineData::Separator, _("    FileData fileData;"));
    rows2 << RowData(TextLineData::Separator, _(""));
    rows2 << RowData(TextLineData::Separator, _("    return fileData;"));
    rows2 << RowData(TextLineData::Separator, _("}"));
    rows2 << RowData(TextLineData::Separator, _(""));
    rows2 << RowData(_("} // namespace Internal"));
    rows2 << RowData(_("} // namespace DiffEditor"));
    chunkData2.rows = rows2;
    fileData2.chunks << chunkData2;

    FileData fileData3;
    fileData3.leftFileInfo = DiffFileInfo(_("new"), _("0000000"));
    fileData3.rightFileInfo = DiffFileInfo(_("new"), _("257cc56"));
    fileData3.fileOperation = FileData::NewFile;
    ChunkData chunkData3;
    chunkData3.leftStartingLineNumber = -1;
    chunkData3.rightStartingLineNumber = 0;
    QList<RowData> rows3;
    rows3 << RowData(TextLineData::Separator, _("foo"));
    TextLineData textLineData3(TextLineData::TextLine);
    rows3 << RowData(TextLineData::Separator, textLineData3);
    chunkData3.rows = rows3;
    fileData3.chunks << chunkData3;

    FileData fileData4;
    fileData4.leftFileInfo = DiffFileInfo(_("deleted"), _("257cc56"));
    fileData4.rightFileInfo = DiffFileInfo(_("deleted"), _("0000000"));
    fileData4.fileOperation = FileData::DeleteFile;
    ChunkData chunkData4;
    chunkData4.leftStartingLineNumber = 0;
    chunkData4.rightStartingLineNumber = -1;
    QList<RowData> rows4;
    rows4 << RowData(_("foo"), TextLineData::Separator);
    TextLineData textLineData4(TextLineData::TextLine);
    rows4 << RowData(textLineData4, TextLineData::Separator);
    chunkData4.rows = rows4;
    fileData4.chunks << chunkData4;

    FileData fileData5;
    fileData5.leftFileInfo = DiffFileInfo(_("empty"), _("0000000"));
    fileData5.rightFileInfo = DiffFileInfo(_("empty"), _("e69de29"));
    fileData5.fileOperation = FileData::NewFile;

    FileData fileData6;
    fileData6.leftFileInfo = DiffFileInfo(_("empty"), _("e69de29"));
    fileData6.rightFileInfo = DiffFileInfo(_("empty"), _("0000000"));
    fileData6.fileOperation = FileData::DeleteFile;

    FileData fileData7;
    fileData7.leftFileInfo = DiffFileInfo(_("file a.txt"), _("1234567"));
    fileData7.rightFileInfo = DiffFileInfo(_("file b.txt"), _("9876543"));
    fileData7.fileOperation = FileData::CopyFile;
    ChunkData chunkData7;
    chunkData7.leftStartingLineNumber = 19;
    chunkData7.rightStartingLineNumber = 19;
    QList<RowData> rows7;
    rows7 << RowData(_("A"));
    rows7 << RowData(_("B"), _("C"));
    rows7 << RowData(_("D"));
    chunkData7.rows = rows7;
    fileData7.chunks << chunkData7;

    FileData fileData8;
    fileData8.leftFileInfo = DiffFileInfo(_("file a.txt"));
    fileData8.rightFileInfo = DiffFileInfo(_("file b.txt"));
    fileData8.fileOperation = FileData::RenameFile;

    FileData fileData9;
    fileData9.leftFileInfo = DiffFileInfo(_("file.txt"), _("1234567"));
    fileData9.rightFileInfo = DiffFileInfo(_("file.txt"), _("9876543"));
    fileData9.chunks << chunkData7;
    QList<FileData> fileDataList1;
    fileDataList1 << fileData1 << fileData2 << fileData3 << fileData4 << fileData5
                  << fileData6 << fileData7 << fileData8 << fileData9;

    QTest::newRow("Git patch") << patch
                               << fileDataList1;

    //////////////

    patch = _("diff --git a/file foo.txt b/file foo.txt\n"
              "index 1234567..9876543 100644\n"
              "--- a/file foo.txt\n"
              "+++ b/file foo.txt\n"
              "@@ -50,4 +50,5 @@ void DiffEditor::ctor()\n"
              " A\n"
              " B\n"
              " C\n"
              "+\n");

    fileData1.leftFileInfo = DiffFileInfo(_("file foo.txt"), _("1234567"));
    fileData1.rightFileInfo = DiffFileInfo(_("file foo.txt"), _("9876543"));
    fileData1.fileOperation = FileData::ChangeFile;
    chunkData1.leftStartingLineNumber = 49;
    chunkData1.rightStartingLineNumber = 49;
    rows1.clear();
    rows1 << RowData(_("A"));
    rows1 << RowData(_("B"));
    rows1 << RowData(_("C"));
    rows1 << RowData(TextLineData::Separator, _(""));
    chunkData1.rows = rows1;
    fileData1.chunks.clear();
    fileData1.chunks << chunkData1;

    QList<FileData> fileDataList2;
    fileDataList2 << fileData1;

    QTest::newRow("Added line") << patch
                                << fileDataList2;

    //////////////

    patch = _("diff --git a/file foo.txt b/file foo.txt\n"
              "index 1234567..9876543 100644\n"
              "--- a/file foo.txt\n"
              "+++ b/file foo.txt\n"
              "@@ -1,1 +1,1 @@\n"
              "-ABCD\n"
              "\\ No newline at end of file\n"
              "+ABCD\n");

    fileData1.leftFileInfo = DiffFileInfo(_("file foo.txt"), _("1234567"));
    fileData1.rightFileInfo = DiffFileInfo(_("file foo.txt"), _("9876543"));
    fileData1.fileOperation = FileData::ChangeFile;
    chunkData1.leftStartingLineNumber = 0;
    chunkData1.rightStartingLineNumber = 0;
    rows1.clear();
    rows1 << RowData(_("ABCD"));
    rows1 << RowData(TextLineData::Separator, _(""));
    chunkData1.rows = rows1;
    fileData1.chunks.clear();
    fileData1.chunks << chunkData1;

    QList<FileData> fileDataList3;
    fileDataList3 << fileData1;

    QTest::newRow("Last newline added to a line without newline") << patch
                                << fileDataList3;

    patch = _("diff --git a/difftest.txt b/difftest.txt\n"
              "index 1234567..9876543 100644\n"
              "--- a/difftest.txt\n"
              "+++ b/difftest.txt\n"
              "@@ -2,5 +2,5 @@ void func()\n"
              " A\n"
              " B\n"
              "-C\n"
              "+Z\n"
              " D\n"
              " \n"
              "@@ -9,2 +9,4 @@ void OtherFunc()\n"
              " \n"
              " D\n"
              "+E\n"
              "+F\n"
              );

    fileData1.leftFileInfo = DiffFileInfo(_("difftest.txt"), _("1234567"));
    fileData1.rightFileInfo = DiffFileInfo(_("difftest.txt"), _("9876543"));
    fileData1.fileOperation = FileData::ChangeFile;
    chunkData1.leftStartingLineNumber = 1;
    chunkData1.rightStartingLineNumber = 1;
    rows1.clear();
    rows1 << RowData(_("A"));
    rows1 << RowData(_("B"));
    rows1 << RowData(_("C"), _("Z"));
    rows1 << RowData(_("D"));
    rows1 << RowData(_(""));
    chunkData1.rows = rows1;

    chunkData2.leftStartingLineNumber = 8;
    chunkData2.rightStartingLineNumber = 8;
    rows2.clear();
    rows2 << RowData(_(""));
    rows2 << RowData(_("D"));
    rows2 << RowData(TextLineData::Separator, _("E"));
    rows2 << RowData(TextLineData::Separator, _("F"));
    chunkData2.rows = rows2;
    fileData1.chunks.clear();
    fileData1.chunks << chunkData1;
    fileData1.chunks << chunkData2;

    QList<FileData> fileDataList4;
    fileDataList4 << fileData1;

    QTest::newRow("2 chunks - first ends with blank line") << patch
                                << fileDataList4;

    //////////////

    patch = _("diff --git a/file foo.txt b/file foo.txt\n"
              "index 1234567..9876543 100644\n"
              "--- a/file foo.txt\n"
              "+++ b/file foo.txt\n"
              "@@ -1,1 +1,3 @@ void DiffEditor::ctor()\n"
              " ABCD\n"
              "+\n"
              "+EFGH\n"
              "\\ No newline at end of file\n");

    fileData1.leftFileInfo = DiffFileInfo(_("file foo.txt"), _("1234567"));
    fileData1.rightFileInfo = DiffFileInfo(_("file foo.txt"), _("9876543"));
    fileData1.fileOperation = FileData::ChangeFile;
    chunkData1.leftStartingLineNumber = 0;
    chunkData1.rightStartingLineNumber = 0;
    rows1.clear();
    rows1 << RowData(_("ABCD"));
    rows1 << RowData(TextLineData::Separator, _(""));
    rows1 << RowData(_(""), _("EFGH"));
    chunkData1.rows = rows1;
    fileData1.chunks.clear();
    fileData1.chunks << chunkData1;

    QList<FileData> fileDataList5;
    fileDataList5 << fileData1;

    QTest::newRow("Blank line followed by No newline") << patch
                                << fileDataList5;

    //////////////

    // Based on 953cdb97
    patch = _("diff --git a/src/plugins/texteditor/basetextdocument.h b/src/plugins/texteditor/textdocument.h\n"
              "similarity index 100%\n"
              "rename from src/plugins/texteditor/basetextdocument.h\n"
              "rename to src/plugins/texteditor/textdocument.h\n"
              "diff --git a/src/plugins/texteditor/basetextdocumentlayout.cpp b/src/plugins/texteditor/textdocumentlayout.cpp\n"
              "similarity index 79%\n"
              "rename from src/plugins/texteditor/basetextdocumentlayout.cpp\n"
              "rename to src/plugins/texteditor/textdocumentlayout.cpp\n"
              "index 0121933..01cc3a0 100644\n"
              "--- a/src/plugins/texteditor/basetextdocumentlayout.cpp\n"
              "+++ b/src/plugins/texteditor/textdocumentlayout.cpp\n"
              "@@ -2,5 +2,5 @@ void func()\n"
              " A\n"
              " B\n"
              "-C\n"
              "+Z\n"
              " D\n"
              " \n"
              );

    fileData1 = FileData();
    fileData1.leftFileInfo = DiffFileInfo(_("src/plugins/texteditor/basetextdocument.h"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/plugins/texteditor/textdocument.h"));
    fileData1.fileOperation = FileData::RenameFile;
    fileData2 = FileData();
    fileData2.leftFileInfo = DiffFileInfo(_("src/plugins/texteditor/basetextdocumentlayout.cpp"), _("0121933"));
    fileData2.rightFileInfo = DiffFileInfo(_("src/plugins/texteditor/textdocumentlayout.cpp"), _("01cc3a0"));
    fileData2.fileOperation = FileData::RenameFile;
    chunkData2.leftStartingLineNumber = 1;
    chunkData2.rightStartingLineNumber = 1;
    rows2.clear();
    rows2 << RowData(_("A"));
    rows2 << RowData(_("B"));
    rows2 << RowData(_("C"), _("Z"));
    rows2 << RowData(_("D"));
    rows2 << RowData(_(""));
    chunkData2.rows = rows2;
    fileData2.chunks.clear();
    fileData2.chunks << chunkData2;

    QList<FileData> fileDataList6;
    fileDataList6 << fileData1 << fileData2;

    QTest::newRow("Multiple renames") << patch
                                      << fileDataList6;

    //////////////

    // Dirty submodule
    patch = _("diff --git a/src/shared/qbs b/src/shared/qbs\n"
              "--- a/src/shared/qbs\n"
              "+++ b/src/shared/qbs\n"
              "@@ -1 +1 @@\n"
              "-Subproject commit eda76354077a427d692fee05479910de31040d3f\n"
              "+Subproject commit eda76354077a427d692fee05479910de31040d3f-dirty\n"
              );
    fileData1 = FileData();
    fileData1.leftFileInfo = DiffFileInfo(_("src/shared/qbs"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/shared/qbs"));
    chunkData1.leftStartingLineNumber = 0;
    chunkData1.rightStartingLineNumber = 0;
    rows1.clear();
    rows1 << RowData(_("Subproject commit eda76354077a427d692fee05479910de31040d3f"),
                     _("Subproject commit eda76354077a427d692fee05479910de31040d3f-dirty"));
    chunkData1.rows = rows1;
    fileData1.chunks.clear();
    fileData1.chunks <<  chunkData1;

    QList<FileData> fileDataList7;
    fileDataList7 << fileData1;
    QTest::newRow("Dirty submodule") << patch
                                     << fileDataList7;

    //////////////

    // Subversion New
    patch = _("Index: src/plugins/subversion/subversioneditor.cpp\n"
              "===================================================================\n"
              "--- src/plugins/subversion/subversioneditor.cpp\t(revision 0)\n"
              "+++ src/plugins/subversion/subversioneditor.cpp\t(revision 0)\n"
              "@@ -0,0 +125 @@\n\n");
    fileData1 = FileData();
    fileData1.leftFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    chunkData1 = ChunkData();
    chunkData1.leftStartingLineNumber = -1;
    chunkData1.rightStartingLineNumber = 124;
    fileData1.chunks << chunkData1;
    QList<FileData> fileDataList8;
    fileDataList8 << fileData1;
    QTest::newRow("Subversion New") << patch
                                    << fileDataList8;

    //////////////

    // Subversion Deleted
    patch = _("Index: src/plugins/subversion/subversioneditor.cpp\n"
              "===================================================================\n"
              "--- src/plugins/subversion/subversioneditor.cpp\t(revision 42)\n"
              "+++ src/plugins/subversion/subversioneditor.cpp\t(working copy)\n"
              "@@ -1,125 +0,0 @@\n\n");
    fileData1 = FileData();
    fileData1.leftFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    chunkData1 = ChunkData();
    chunkData1.leftStartingLineNumber = 0;
    chunkData1.rightStartingLineNumber = -1;
    fileData1.chunks << chunkData1;
    QList<FileData> fileDataList9;
    fileDataList9 << fileData1;
    QTest::newRow("Subversion Deleted") << patch
                                        << fileDataList9;

    //////////////

    // Subversion Normal
    patch = _("Index: src/plugins/subversion/subversioneditor.cpp\n"
              "===================================================================\n"
              "--- src/plugins/subversion/subversioneditor.cpp\t(revision 42)\n"
              "+++ src/plugins/subversion/subversioneditor.cpp\t(working copy)\n"
              "@@ -120,7 +120,7 @@\n\n");
    fileData1 = FileData();
    fileData1.leftFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    fileData1.rightFileInfo = DiffFileInfo(_("src/plugins/subversion/subversioneditor.cpp"));
    chunkData1 = ChunkData();
    chunkData1.leftStartingLineNumber = 119;
    chunkData1.rightStartingLineNumber = 119;
    fileData1.chunks << chunkData1;
    QList<FileData> fileDataList10;
    fileDataList10 << fileData1;
    QTest::newRow("Subversion Normal") << patch
                                       << fileDataList10;
}

void DiffEditor::Internal::DiffEditorPlugin::testReadPatch()
{
    QFETCH(QString, sourcePatch);
    QFETCH(QList<FileData>, fileDataList);

    bool ok;
    QList<FileData> result = DiffUtils::readPatch(sourcePatch, &ok);

    QVERIFY(ok);
    QCOMPARE(fileDataList.count(), result.count());
    for (int i = 0; i < fileDataList.count(); i++) {
        const FileData &origFileData = fileDataList.at(i);
        const FileData &resultFileData = result.at(i);
        QCOMPARE(resultFileData.leftFileInfo.fileName, origFileData.leftFileInfo.fileName);
        QCOMPARE(resultFileData.leftFileInfo.typeInfo, origFileData.leftFileInfo.typeInfo);
        QCOMPARE(resultFileData.rightFileInfo.fileName, origFileData.rightFileInfo.fileName);
        QCOMPARE(resultFileData.rightFileInfo.typeInfo, origFileData.rightFileInfo.typeInfo);
        QCOMPARE(resultFileData.chunks.count(), origFileData.chunks.count());
        QCOMPARE(resultFileData.fileOperation, origFileData.fileOperation);
        for (int j = 0; j < origFileData.chunks.count(); j++) {
            const ChunkData &origChunkData = origFileData.chunks.at(j);
            const ChunkData &resultChunkData = resultFileData.chunks.at(j);
            QCOMPARE(resultChunkData.leftStartingLineNumber, origChunkData.leftStartingLineNumber);
            QCOMPARE(resultChunkData.rightStartingLineNumber, origChunkData.rightStartingLineNumber);
            QCOMPARE(resultChunkData.contextChunk, origChunkData.contextChunk);
            QCOMPARE(resultChunkData.rows.count(), origChunkData.rows.count());
            for (int k = 0; k < origChunkData.rows.count(); k++) {
                const RowData &origRowData = origChunkData.rows.at(k);
                const RowData &resultRowData = resultChunkData.rows.at(k);
                QCOMPARE(resultRowData.equal, origRowData.equal);
                QCOMPARE(resultRowData.leftLine.text, origRowData.leftLine.text);
                QCOMPARE(resultRowData.leftLine.textLineType, origRowData.leftLine.textLineType);
                QCOMPARE(resultRowData.rightLine.text, origRowData.rightLine.text);
                QCOMPARE(resultRowData.rightLine.textLineType, origRowData.rightLine.textLineType);
            }
        }
    }
}

#endif // WITH_TESTS

#include "diffeditorplugin.moc" 
ogoffart commented 8 years ago

I have never tried using it on windows.
So thanks for trying. Could you please make a pull request? Of course, the change need to be protected by if(WIN) or something.

I don't know why you changed some unsinged to signed.

I can reproduce the problem in DiffEditor. I'll try to fix it.

ogoffart commented 8 years ago

The problem was that DiffEditor was both a namespace and a class within the namespace. So because we would always use quallified name for the base name, it would be ambigius at the location it might be written. So now i'm using the name as written.