From 8c79d5bc23e4b555625dfa368ca9f74e6d14540b Mon Sep 17 00:00:00 2001
From: Erik Verbruggen <erik.verbruggen@digia.com>
Date: Mon, 30 Sep 2013 14:37:50 +0200
Subject: [PATCH] C++: add code-model support for defines-per-editor.

The UI side will come in another commit.

Change-Id: I1038a438587fe4cef70408f7f627d08837fc192d
Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
---
 src/plugins/cpptools/cppmodelmanager_test.cpp | 96 +++++++++++++++++++
 src/plugins/cpptools/cpppreprocessor.cpp      |  7 +-
 src/plugins/cpptools/cppsnapshotupdater.cpp   | 27 +++++-
 src/plugins/cpptools/cppsnapshotupdater.h     |  3 +
 src/plugins/cpptools/cpptoolsplugin.h         |  1 +
 5 files changed, 132 insertions(+), 2 deletions(-)

diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp
index db76d6ecc12..8dd05281c4b 100644
--- a/src/plugins/cpptools/cppmodelmanager_test.cpp
+++ b/src/plugins/cpptools/cppmodelmanager_test.cpp
@@ -970,3 +970,99 @@ void CppToolsPlugin::test_modelmanager_defines_per_project_pch()
         QCOMPARE(decl->name()->identifier()->chars(), "two");
     }
 }
+
+void CppToolsPlugin::test_modelmanager_defines_per_editor()
+{
+    ModelManagerTestHelper helper;
+
+    MyTestDataDir testDataDirectory(QLatin1String("testdata_defines"));
+    const QString main1File = testDataDirectory.file(QLatin1String("main1.cpp"));
+    const QString main2File = testDataDirectory.file(QLatin1String("main2.cpp"));
+    const QString header = testDataDirectory.file(QLatin1String("header.h"));
+
+    CppModelManager *mm = CppModelManager::instance();
+
+    Project *project = helper.createProject(
+                QLatin1String("test_modelmanager_defines_per_editor"));
+
+    ProjectPart::Ptr part1(new ProjectPart);
+    part1->files.append(ProjectFile(main1File, ProjectFile::CXXSource));
+    part1->files.append(ProjectFile(header, ProjectFile::CXXHeader));
+    part1->cxxVersion = ProjectPart::CXX11;
+    part1->qtVersion = ProjectPart::NoQt;
+    part1->includePaths = QStringList() << testDataDirectory.includeDir(false);
+
+    ProjectPart::Ptr part2(new ProjectPart);
+    part2->files.append(ProjectFile(main2File, ProjectFile::CXXSource));
+    part2->files.append(ProjectFile(header, ProjectFile::CXXHeader));
+    part2->cxxVersion = ProjectPart::CXX11;
+    part2->qtVersion = ProjectPart::NoQt;
+    part2->includePaths = QStringList() << testDataDirectory.includeDir(false);
+
+    ProjectInfo pi = mm->projectInfo(project);
+    pi.appendProjectPart(part1);
+    pi.appendProjectPart(part2);
+
+    mm->updateProjectInfo(pi);
+
+    helper.waitForRefreshedSourceFiles();
+
+    QCOMPARE(mm->snapshot().size(), 4);
+
+    // Open a file in the editor
+    QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 0);
+
+    {
+        Core::IEditor *editor = Core::EditorManager::openEditor(main1File);
+        EditorCloser closer(editor);
+        QVERIFY(editor);
+        QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 1);
+        QVERIFY(mm->isCppEditor(editor));
+
+        CppEditorSupport *sup = mm->cppEditorSupport(
+                    qobject_cast<TextEditor::BaseTextEditor *>(editor));
+        while (sup->lastSemanticInfoDocument().isNull())
+            QCoreApplication::processEvents();
+
+        sup->snapshotUpdater()->setEditorDefines(QByteArray("#define SUB1\n"));
+        sup->snapshotUpdater()->update(mm->workingCopy());
+
+        Document::Ptr doc = mm->snapshot().document(main1File);
+        QVERIFY(doc);
+        QVERIFY(doc->globalNamespace());
+        QCOMPARE(doc->globalSymbolCount(), 1U);
+        CPlusPlus::Symbol *s = doc->globalSymbolAt(0);
+        QVERIFY(s);
+        CPlusPlus::Declaration *decl = s->asDeclaration();
+        QVERIFY(decl);
+        QVERIFY(decl->type()->isIntegerType());
+        QCOMPARE(decl->name()->identifier()->chars(), "one");
+    }
+
+    {
+        Core::IEditor *editor = Core::EditorManager::openEditor(main2File);
+        EditorCloser closer(editor);
+        QVERIFY(editor);
+        QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 1);
+        QVERIFY(mm->isCppEditor(editor));
+
+        CppEditorSupport *sup = mm->cppEditorSupport(
+                    qobject_cast<TextEditor::BaseTextEditor *>(editor));
+        while (sup->lastSemanticInfoDocument().isNull())
+            QCoreApplication::processEvents();
+
+        sup->snapshotUpdater()->setEditorDefines(QByteArray("#define SUB2\n"));
+        sup->snapshotUpdater()->update(mm->workingCopy());
+
+        Document::Ptr doc = mm->snapshot().document(main2File);
+        QVERIFY(doc);
+        QVERIFY(doc->globalNamespace());
+        QCOMPARE(doc->globalSymbolCount(), 1U);
+        CPlusPlus::Symbol *s = doc->globalSymbolAt(0);
+        QVERIFY(s);
+        CPlusPlus::Declaration *decl = s->asDeclaration();
+        QVERIFY(decl);
+        QVERIFY(decl->type()->isIntegerType());
+        QCOMPARE(decl->name()->identifier()->chars(), "two");
+    }
+}
diff --git a/src/plugins/cpptools/cpppreprocessor.cpp b/src/plugins/cpptools/cpppreprocessor.cpp
index 1e3f5b1bf02..f60c853b832 100644
--- a/src/plugins/cpptools/cpppreprocessor.cpp
+++ b/src/plugins/cpptools/cpppreprocessor.cpp
@@ -223,10 +223,15 @@ QString CppPreprocessor::cleanPath(const QString &path)
     return result;
 }
 
+static inline bool isInjectedFile(const QString &fileName)
+{
+    return fileName.startsWith(QLatin1Char('<')) && fileName.endsWith(QLatin1Char('>'));
+}
+
 QString CppPreprocessor::resolveFile_helper(const QString &fileName, IncludeType type)
 {
     const QFileInfo fileInfo(fileName);
-    if (fileName == Preprocessor::configurationFileName || fileInfo.isAbsolute())
+    if (isInjectedFile(fileName) || fileInfo.isAbsolute())
         return fileName;
 
     if (type == IncludeLocal && m_currentDoc) {
diff --git a/src/plugins/cpptools/cppsnapshotupdater.cpp b/src/plugins/cpptools/cppsnapshotupdater.cpp
index 473ec66b607..6d9a4d3a6f1 100644
--- a/src/plugins/cpptools/cppsnapshotupdater.cpp
+++ b/src/plugins/cpptools/cppsnapshotupdater.cpp
@@ -39,6 +39,7 @@ using namespace CppTools::Internal;
 SnapshotUpdater::SnapshotUpdater(const QString &fileInEditor)
     : m_mutex(QMutex::Recursive)
     , m_fileInEditor(fileInEditor)
+    , m_editorDefinesChangedSinceLastUpdate(false)
     , m_usePrecompiledHeaders(false)
 {
 }
@@ -50,7 +51,7 @@ void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy)
     if (m_fileInEditor.isEmpty())
         return;
 
-    bool invalidateSnapshot = false, invalidateConfig = false;
+    bool invalidateSnapshot = false, invalidateConfig = false, editorDefinesChanged = false;
 
     CppModelManager *modelManager
         = dynamic_cast<CppModelManager *>(CppModelManagerInterface::instance());
@@ -75,6 +76,12 @@ void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy)
         invalidateConfig = true;
     }
 
+    if (m_editorDefinesChangedSinceLastUpdate) {
+        invalidateSnapshot = true;
+        editorDefinesChanged = true;
+        m_editorDefinesChangedSinceLastUpdate = false;
+    }
+
     if (includePaths != m_includePaths) {
         m_includePaths = includePaths;
         invalidateSnapshot = true;
@@ -131,6 +138,12 @@ void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy)
             workingCopy.insert(configurationFileName, m_configFile);
         m_snapshot.remove(m_fileInEditor);
 
+        static const QString editorDefinesFileName = QLatin1String("<per-editor-defines>");
+        if (editorDefinesChanged) {
+            m_snapshot.remove(editorDefinesFileName);
+            workingCopy.insert(editorDefinesFileName, m_editorDefines);
+        }
+
         CppPreprocessor preproc(modelManager, m_snapshot);
         Snapshot globalSnapshot = modelManager->snapshot();
         globalSnapshot.remove(fileInEditor());
@@ -142,6 +155,8 @@ void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy)
         if (m_usePrecompiledHeaders)
             foreach (const QString &precompiledHeader, m_precompiledHeaders)
                 preproc.run(precompiledHeader);
+        if (!m_editorDefines.isEmpty())
+            preproc.run(editorDefinesFileName);
         preproc.run(m_fileInEditor);
 
         m_snapshot = preproc.snapshot();
@@ -178,6 +193,16 @@ void SnapshotUpdater::setUsePrecompiledHeaders(bool usePrecompiledHeaders)
     m_usePrecompiledHeaders = usePrecompiledHeaders;
 }
 
+void SnapshotUpdater::setEditorDefines(const QByteArray &editorDefines)
+{
+    QMutexLocker locker(&m_mutex);
+
+    if (editorDefines != m_editorDefines) {
+        m_editorDefines = editorDefines;
+        m_editorDefinesChangedSinceLastUpdate = true;
+    }
+}
+
 void SnapshotUpdater::updateProjectPart()
 {
     CppModelManager *cmm = dynamic_cast<CppModelManager *>(CppModelManagerInterface::instance());
diff --git a/src/plugins/cpptools/cppsnapshotupdater.h b/src/plugins/cpptools/cppsnapshotupdater.h
index 6f0f90ac4f9..1cc8772709d 100644
--- a/src/plugins/cpptools/cppsnapshotupdater.h
+++ b/src/plugins/cpptools/cppsnapshotupdater.h
@@ -65,6 +65,7 @@ public:
     { return m_frameworkPaths; }
 
     void setUsePrecompiledHeaders(bool usePrecompiledHeaders);
+    void setEditorDefines(const QByteArray &editorDefines);
 
 private:
     void updateProjectPart();
@@ -75,6 +76,8 @@ private:
     QString m_fileInEditor;
     ProjectPart::Ptr m_projectPart;
     QByteArray m_configFile;
+    bool m_editorDefinesChangedSinceLastUpdate;
+    QByteArray m_editorDefines;
     QStringList m_includePaths;
     QStringList m_frameworkPaths;
     QStringList m_precompiledHeaders;
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 3a0c4b90241..da09cbe15eb 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -213,6 +213,7 @@ private slots:
     void test_modelmanager_dont_gc_opened_files();
     void test_modelmanager_defines_per_project();
     void test_modelmanager_defines_per_project_pch();
+    void test_modelmanager_defines_per_editor();
 
     void test_cpplocatorfilters_CppLocatorFilter();
     void test_cpplocatorfilters_CppLocatorFilter_data();
-- 
GitLab