summaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorErik Verbruggen <erik.verbruggen@digia.com>2013-08-19 16:05:29 +0200
committerErik Verbruggen <erik.verbruggen@digia.com>2013-10-01 10:33:51 +0200
commitba2d7a4fa7c29ea2d62da3d6f6835a091f604656 (patch)
tree240f8484a13829478d3b7c586eec0598c45d0872 /src
parent447c4ed37f8904ca733d6e6253ad19bb0388f209 (diff)
downloadqt-creator-ba2d7a4fa7c29ea2d62da3d6f6835a091f604656.tar.gz
C++: Only parse with appropriate defines for open editors.
If two files from different (sub-)projects include the same header file, and the defined macros differ for both files, the header file will be parsed with only the appropriate macros for the including file. Task-number: QTCREATORBUG-9802 Task-number: QTCREATORBUG-1249 Change-Id: I560490afa287b3bb1e863bce1bb4f57af36ad56e Reviewed-by: Nikolai Kosjar <nikolai.kosjar@digia.com>
Diffstat (limited to 'src')
-rw-r--r--src/libs/cplusplus/CppDocument.h6
-rw-r--r--src/libs/cplusplus/DependencyTable.cpp4
-rw-r--r--src/plugins/cppeditor/cppeditor.cpp7
-rw-r--r--src/plugins/cpptools/cppcompletionassist.cpp33
-rw-r--r--src/plugins/cpptools/cppcompletionassist.h4
-rw-r--r--src/plugins/cpptools/cppcompletionassistprovider.h5
-rw-r--r--src/plugins/cpptools/cppmodelmanager.cpp24
-rw-r--r--src/plugins/cpptools/cppmodelmanager.h10
-rw-r--r--src/plugins/cpptools/cppmodelmanager_test.cpp113
-rw-r--r--src/plugins/cpptools/cppmodelmanagerinterface.h2
-rw-r--r--src/plugins/cpptools/cpppreprocessor.cpp47
-rw-r--r--src/plugins/cpptools/cpppreprocessor.h8
-rw-r--r--src/plugins/cpptools/cppsnapshotupdater.cpp194
-rw-r--r--src/plugins/cpptools/cppsnapshotupdater.h84
-rw-r--r--src/plugins/cpptools/cpptools.pro2
-rw-r--r--src/plugins/cpptools/cpptools.qbs4
-rw-r--r--src/plugins/cpptools/cpptoolseditorsupport.cpp37
-rw-r--r--src/plugins/cpptools/cpptoolseditorsupport.h8
-rw-r--r--src/plugins/cpptools/cpptoolsplugin.h1
19 files changed, 545 insertions, 48 deletions
diff --git a/src/libs/cplusplus/CppDocument.h b/src/libs/cplusplus/CppDocument.h
index 071f9485e8..886f2bed42 100644
--- a/src/libs/cplusplus/CppDocument.h
+++ b/src/libs/cplusplus/CppDocument.h
@@ -104,6 +104,10 @@ public:
QByteArray utf8Source() const;
void setUtf8Source(const QByteArray &utf8Source);
+ QByteArray fingerprint() const { return m_fingerprint; }
+ void setFingerprint(const QByteArray &fingerprint)
+ { m_fingerprint = fingerprint; }
+
void startSkippingBlocks(unsigned offset);
void stopSkippingBlocks(unsigned offset);
@@ -361,6 +365,8 @@ private:
/// the macro name of the include guard, if there is one.
QByteArray _includeGuardMacroName;
+ QByteArray m_fingerprint;
+
QByteArray _source;
QDateTime _lastModified;
QAtomicInt _keepSourceAndASTCount;
diff --git a/src/libs/cplusplus/DependencyTable.cpp b/src/libs/cplusplus/DependencyTable.cpp
index e1a4bf3b73..6d3cc0651e 100644
--- a/src/libs/cplusplus/DependencyTable.cpp
+++ b/src/libs/cplusplus/DependencyTable.cpp
@@ -37,10 +37,8 @@ using namespace CPlusPlus;
QStringList DependencyTable::filesDependingOn(const QString &fileName) const
{
int index = fileIndex.value(fileName, -1);
- if (index == -1) {
- qWarning() << fileName << "not in the snapshot";
+ if (index == -1)
return QStringList();
- }
QStringList deps;
for (int i = 0; i < files.size(); ++i) {
diff --git a/src/plugins/cppeditor/cppeditor.cpp b/src/plugins/cppeditor/cppeditor.cpp
index 8475ce10d1..f037abc8d3 100644
--- a/src/plugins/cppeditor/cppeditor.cpp
+++ b/src/plugins/cppeditor/cppeditor.cpp
@@ -2107,10 +2107,11 @@ TextEditor::IAssistInterface *CPPEditorWidget::createAssistInterface(
if (kind == TextEditor::Completion) {
CppEditorSupport *ces = CppModelManagerInterface::instance()->cppEditorSupport(editor());
CppCompletionAssistProvider *cap = ces->completionAssistProvider();
- if (cap)
+ if (cap) {
return cap->createAssistInterface(
- ProjectExplorer::ProjectExplorerPlugin::currentProject(),
- editor()->document()->filePath(), document(), position(), reason);
+ ProjectExplorer::ProjectExplorerPlugin::currentProject(),
+ editor(), document(), position(), reason);
+ }
} else if (kind == TextEditor::QuickFix) {
if (!semanticInfo().doc || isOutdated())
return 0;
diff --git a/src/plugins/cpptools/cppcompletionassist.cpp b/src/plugins/cpptools/cppcompletionassist.cpp
index 4a9ea06122..59d2b8aa53 100644
--- a/src/plugins/cpptools/cppcompletionassist.cpp
+++ b/src/plugins/cpptools/cppcompletionassist.cpp
@@ -30,6 +30,7 @@
#include "cppcompletionassist.h"
#include "cppmodelmanager.h"
#include "cpptoolsconstants.h"
+#include "cpptoolseditorsupport.h"
#include "cppdoxygen.h"
#include <coreplugin/icore.h>
@@ -416,24 +417,28 @@ IAssistProcessor *InternalCompletionAssistProvider::createProcessor() const
}
TextEditor::IAssistInterface *InternalCompletionAssistProvider::createAssistInterface(
- ProjectExplorer::Project *project, const QString &filePath, QTextDocument *document,
+ ProjectExplorer::Project *project, BaseTextEditor *editor, QTextDocument *document,
int position, TextEditor::AssistReason reason) const
{
+ Q_UNUSED(project);
+
CppModelManagerInterface *modelManager = CppModelManagerInterface::instance();
- QStringList includePaths;
- QStringList frameworkPaths;
- if (project) {
- includePaths = modelManager->projectInfo(project).includePaths();
- frameworkPaths = modelManager->projectInfo(project).frameworkPaths();
+
+ if (CppEditorSupport *supp = modelManager->cppEditorSupport(editor)) {
+ if (QSharedPointer<SnapshotUpdater> updater = supp->snapshotUpdater()) {
+ updater->update(modelManager->workingCopy());
+ return new CppTools::Internal::CppCompletionAssistInterface(
+ document,
+ position,
+ editor->document()->filePath(),
+ reason,
+ updater->snapshot(),
+ updater->includePaths(),
+ updater->frameworkPaths());
+ }
}
- return new CppTools::Internal::CppCompletionAssistInterface(
- document,
- position,
- filePath,
- reason,
- modelManager->snapshot(),
- includePaths,
- frameworkPaths);
+
+ return 0;
}
// -----------------
diff --git a/src/plugins/cpptools/cppcompletionassist.h b/src/plugins/cpptools/cppcompletionassist.h
index a78777a863..6345d895b2 100644
--- a/src/plugins/cpptools/cppcompletionassist.h
+++ b/src/plugins/cpptools/cppcompletionassist.h
@@ -91,8 +91,8 @@ public:
virtual TextEditor::IAssistProcessor *createProcessor() const;
virtual TextEditor::IAssistInterface *createAssistInterface(
- ProjectExplorer::Project *project, const QString &filePath, QTextDocument *document,
- int position, TextEditor::AssistReason reason) const;
+ ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor,
+ QTextDocument *document, int position, TextEditor::AssistReason reason) const;
};
diff --git a/src/plugins/cpptools/cppcompletionassistprovider.h b/src/plugins/cpptools/cppcompletionassistprovider.h
index e45b20946f..e13a473e94 100644
--- a/src/plugins/cpptools/cppcompletionassistprovider.h
+++ b/src/plugins/cpptools/cppcompletionassistprovider.h
@@ -44,6 +44,7 @@ class Project;
}
namespace TextEditor {
+class BaseTextEditor;
class IAssistInterface;
}
@@ -59,8 +60,8 @@ public:
virtual bool isActivationCharSequence(const QString &sequence) const;
virtual TextEditor::IAssistInterface *createAssistInterface(
- ProjectExplorer::Project *project, const QString &filePath, QTextDocument *document,
- int position, TextEditor::AssistReason reason) const = 0;
+ ProjectExplorer::Project *project, TextEditor::BaseTextEditor *editor,
+ QTextDocument *document, int position, TextEditor::AssistReason reason) const = 0;
static int activationSequenceChar(const QChar &ch, const QChar &ch2,
const QChar &ch3, unsigned *kind,
diff --git a/src/plugins/cpptools/cppmodelmanager.cpp b/src/plugins/cpptools/cppmodelmanager.cpp
index 4fb3f381e2..4c0c5ecba7 100644
--- a/src/plugins/cpptools/cppmodelmanager.cpp
+++ b/src/plugins/cpptools/cppmodelmanager.cpp
@@ -549,7 +549,7 @@ CppModelManager::WorkingCopy CppModelManager::buildWorkingCopyList()
}
// Add the project configuration file
- QByteArray conf = QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
+ QByteArray conf = codeModelConfiguration();
conf += definedMacros();
workingCopy.insert(configurationFileName(), conf);
@@ -561,6 +561,11 @@ CppModelManager::WorkingCopy CppModelManager::workingCopy() const
return const_cast<CppModelManager *>(this)->buildWorkingCopyList();
}
+QByteArray CppModelManager::codeModelConfiguration() const
+{
+ return QByteArray::fromRawData(pp_configuration, qstrlen(pp_configuration));
+}
+
QFuture<void> CppModelManager::updateSourceFiles(const QStringList &sourceFiles,
ProgressNotificationMode mode)
{
@@ -760,20 +765,19 @@ QFuture<void> CppModelManager::updateProjectInfo(const ProjectInfo &newProjectIn
QList<ProjectPart::Ptr> CppModelManager::projectPart(const QString &fileName) const
{
- QList<ProjectPart::Ptr> parts = m_fileToProjectParts.value(fileName);
- if (!parts.isEmpty())
- return parts;
+ return m_fileToProjectParts.value(fileName);
+}
+QList<ProjectPart::Ptr> CppModelManager::projectPartFromDependencies(const QString &fileName) const
+{
+ QSet<ProjectPart::Ptr> parts;
DependencyTable table;
table.build(snapshot());
const QStringList deps = table.filesDependingOn(fileName);
- foreach (const QString &dep, deps) {
- parts = m_fileToProjectParts.value(dep);
- if (!parts.isEmpty())
- return parts;
- }
+ foreach (const QString &dep, deps)
+ parts.unite(QSet<ProjectPart::Ptr>::fromList(m_fileToProjectParts.value(dep)));
- return parts;
+ return parts.values();
}
ProjectPart::Ptr CppModelManager::fallbackProjectPart() const
diff --git a/src/plugins/cpptools/cppmodelmanager.h b/src/plugins/cpptools/cppmodelmanager.h
index 2ec1423e3a..e86875a63f 100644
--- a/src/plugins/cpptools/cppmodelmanager.h
+++ b/src/plugins/cpptools/cppmodelmanager.h
@@ -69,11 +69,19 @@ public:
virtual QFuture<void> updateSourceFiles(const QStringList &sourceFiles,
ProgressNotificationMode mode = ReservedProgressNotification);
virtual WorkingCopy workingCopy() const;
+ virtual QByteArray codeModelConfiguration() const;
virtual QList<ProjectInfo> projectInfos() const;
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const;
virtual QFuture<void> updateProjectInfo(const ProjectInfo &newProjectInfo);
- virtual QList<CppTools::ProjectPart::Ptr> projectPart(const QString &fileName) const;
+
+ /// \return All project parts that mention the given file name as one of the sources/headers.
+ virtual QList<ProjectPart::Ptr> projectPart(const QString &fileName) const;
+ /// This is a fall-back function: find all files that includes the file directly or indirectly,
+ /// and return its \c ProjectPart list for use with this file.
+ virtual QList<ProjectPart::Ptr> projectPartFromDependencies(const QString &fileName) const;
+ /// \return A synthetic \c ProjectPart which consists of all defines/includes/frameworks from
+ /// all loaded projects.
virtual ProjectPart::Ptr fallbackProjectPart() const;
virtual CPlusPlus::Snapshot snapshot() const;
diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp
index 7482cd3222..d9e8676ec0 100644
--- a/src/plugins/cpptools/cppmodelmanager_test.cpp
+++ b/src/plugins/cpptools/cppmodelmanager_test.cpp
@@ -28,8 +28,8 @@
****************************************************************************/
#include "cpptoolsplugin.h"
-
#include "cpppreprocessor.h"
+#include "cpptoolseditorsupport.h"
#include "modelmanagertesthelper.h"
#include <coreplugin/editormanager/editormanager.h>
@@ -722,8 +722,8 @@ void CppToolsPlugin::test_modelmanager_gc_if_last_cppeditor_closed()
QVERIFY(mm->isCppEditor(editor));
QVERIFY(mm->workingCopy().contains(file));
- // Check: File is in the snapshot
- QVERIFY(mm->snapshot().contains(file));
+ // Wait until the file is refreshed
+ helper.waitForRefreshedSourceFiles();
// Close file/editor
Core::Tests::closeAndDeleteEditor(editor);
@@ -751,9 +751,9 @@ void CppToolsPlugin::test_modelmanager_dont_gc_opened_files()
QCOMPARE(Core::EditorManager::documentModel()->openedDocuments().size(), 1);
QVERIFY(mm->isCppEditor(editor));
- // Check: File is in the working copy and snapshot
+ // Wait until the file is refreshed and check whether it is in the working copy
+ helper.waitForRefreshedSourceFiles();
QVERIFY(mm->workingCopy().contains(file));
- QVERIFY(mm->snapshot().contains(file));
// Run the garbage collector
mm->GC();
@@ -767,3 +767,106 @@ void CppToolsPlugin::test_modelmanager_dont_gc_opened_files()
helper.waitForFinishedGc();
QVERIFY(mm->snapshot().isEmpty());
}
+
+namespace {
+struct EditorCloser {
+ Core::IEditor *editor;
+ EditorCloser(Core::IEditor *editor): editor(editor) {}
+ ~EditorCloser()
+ {
+ if (editor)
+ Core::EditorManager::closeEditors(QList<Core::IEditor*>() << editor);
+ }
+};
+}
+
+void CppToolsPlugin::test_modelmanager_defines_per_project()
+{
+ 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_project"));
+
+ 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->defines = QByteArray("#define SUB1\n");
+ 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->defines = QByteArray("#define SUB2\n");
+ 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();
+
+ 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();
+
+ 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/cppmodelmanagerinterface.h b/src/plugins/cpptools/cppmodelmanagerinterface.h
index 4bcf1c6059..e327a9d0ae 100644
--- a/src/plugins/cpptools/cppmodelmanagerinterface.h
+++ b/src/plugins/cpptools/cppmodelmanagerinterface.h
@@ -223,12 +223,14 @@ public:
virtual bool isCppEditor(Core::IEditor *editor) const = 0;
virtual WorkingCopy workingCopy() const = 0;
+ virtual QByteArray codeModelConfiguration() const = 0;
virtual CPlusPlus::Snapshot snapshot() const = 0;
virtual QList<ProjectInfo> projectInfos() const = 0;
virtual ProjectInfo projectInfo(ProjectExplorer::Project *project) const = 0;
virtual QFuture<void> updateProjectInfo(const ProjectInfo &pinfo) = 0;
virtual QList<ProjectPart::Ptr> projectPart(const QString &fileName) const = 0;
+ virtual QList<ProjectPart::Ptr> projectPartFromDependencies(const QString &fileName) const = 0;
virtual ProjectPart::Ptr fallbackProjectPart() const = 0;
virtual QStringList includePaths() = 0;
diff --git a/src/plugins/cpptools/cpppreprocessor.cpp b/src/plugins/cpptools/cpppreprocessor.cpp
index 48cb7fc77c..1e3f5b1bf0 100644
--- a/src/plugins/cpptools/cpppreprocessor.cpp
+++ b/src/plugins/cpptools/cpppreprocessor.cpp
@@ -6,6 +6,7 @@
#include <utils/textfileformat.h>
#include <QCoreApplication>
+#include <QCryptographicHash>
/*!
* \class CppTools::Internal::CppPreprocessor
@@ -34,6 +35,17 @@ CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager,
m_preprocess.setKeepComments(true);
}
+CppPreprocessor::CppPreprocessor(QPointer<CppModelManager> modelManager, const Snapshot &snapshot,
+ bool dumpFileNameWhileParsing)
+ : m_snapshot(snapshot),
+ m_modelManager(modelManager),
+ m_dumpFileNameWhileParsing(dumpFileNameWhileParsing),
+ m_preprocess(this, &m_env),
+ m_revision(0)
+{
+ m_preprocess.setKeepComments(true);
+}
+
CppPreprocessor::~CppPreprocessor()
{ }
@@ -129,10 +141,10 @@ public:
{
_doc->check(_mode);
- if (_modelManager)
+ if (_modelManager) {
_modelManager->emitDocumentUpdated(_doc);
-
- _doc->releaseSourceAndAST();
+ _doc->releaseSourceAndAST();
+ }
}
};
} // end of anonymous namespace
@@ -398,7 +410,7 @@ void CppPreprocessor::sourceNeeded(unsigned line, const QString &fileName, Inclu
if (m_dumpFileNameWhileParsing) {
qDebug() << "Parsing file:" << absoluteFileName
- << "contents:" << contents.size()
+ << "contents:" << contents.size() << "bytes";
;
}
@@ -426,6 +438,33 @@ void CppPreprocessor::sourceNeeded(unsigned line, const QString &fileName, Inclu
// b.constData());
// }
+ QCryptographicHash hash(QCryptographicHash::Sha1);
+ hash.addData(preprocessedCode);
+ foreach (const Macro &macro, doc->definedMacros()) {
+ if (macro.isHidden()) {
+ static const QByteArray undef("#undef ");
+ hash.addData(undef);
+ hash.addData(macro.name());
+ } else {
+ static const QByteArray def("#define ");
+ hash.addData(macro.name());
+ hash.addData(" ", 1);
+ hash.addData(def);
+ hash.addData(macro.definitionText());
+ }
+ hash.addData("\n", 1);
+ }
+ doc->setFingerprint(hash.result());
+
+ Document::Ptr anotherDoc = m_globalSnapshot.document(absoluteFileName);
+ if (anotherDoc && anotherDoc->fingerprint() == doc->fingerprint()) {
+ switchDocument(previousDoc);
+ mergeEnvironment(anotherDoc);
+ m_snapshot.insert(anotherDoc);
+ m_todo.remove(absoluteFileName);
+ return;
+ }
+
doc->setUtf8Source(preprocessedCode);
doc->keepSourceAndAST();
doc->tokenize();
diff --git a/src/plugins/cpptools/cpppreprocessor.h b/src/plugins/cpptools/cpppreprocessor.h
index e1e3f228d1..d0d84ac8e3 100644
--- a/src/plugins/cpptools/cpppreprocessor.h
+++ b/src/plugins/cpptools/cpppreprocessor.h
@@ -23,6 +23,8 @@ public:
static QString cleanPath(const QString &path);
CppPreprocessor(QPointer<CppModelManager> modelManager, bool dumpFileNameWhileParsing = false);
+ CppPreprocessor(QPointer<CppModelManager> modelManager, const CPlusPlus::Snapshot &snapshot,
+ bool dumpFileNameWhileParsing = false);
virtual ~CppPreprocessor();
void setRevision(unsigned revision);
@@ -35,12 +37,17 @@ public:
void removeFromCache(const QString &fileName);
void resetEnvironment();
+ CPlusPlus::Snapshot snapshot() const
+ { return m_snapshot; }
+
const QSet<QString> &todo() const
{ return m_todo; }
CppModelManager *modelManager() const
{ return m_modelManager.data(); }
+ void setGlobalSnapshot(const CPlusPlus::Snapshot &snapshot) { m_globalSnapshot = snapshot; }
+
protected:
CPlusPlus::Document::Ptr switchDocument(CPlusPlus::Document::Ptr doc);
@@ -71,6 +78,7 @@ private:
void addFrameworkPath(const QString &frameworkPath);
CPlusPlus::Snapshot m_snapshot;
+ CPlusPlus::Snapshot m_globalSnapshot;
QPointer<CppModelManager> m_modelManager;
bool m_dumpFileNameWhileParsing;
CPlusPlus::Environment m_env;
diff --git a/src/plugins/cpptools/cppsnapshotupdater.cpp b/src/plugins/cpptools/cppsnapshotupdater.cpp
new file mode 100644
index 0000000000..f12f888af7
--- /dev/null
+++ b/src/plugins/cpptools/cppsnapshotupdater.cpp
@@ -0,0 +1,194 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#include "cpppreprocessor.h"
+#include "cppsnapshotupdater.h"
+
+#include <utils/qtcassert.h>
+
+using namespace CPlusPlus;
+using namespace CppTools;
+using namespace CppTools::Internal;
+
+SnapshotUpdater::SnapshotUpdater(const QString &fileInEditor)
+ : m_mutex(QMutex::Recursive)
+ , m_fileInEditor(fileInEditor)
+{
+}
+
+void SnapshotUpdater::update(CppModelManager::WorkingCopy workingCopy)
+{
+ QMutexLocker locker(&m_mutex);
+
+ if (m_fileInEditor.isEmpty())
+ return;
+
+ bool invalidateSnapshot = false, invalidateConfig = false;
+
+ CppModelManager *modelManager
+ = dynamic_cast<CppModelManager *>(CppModelManagerInterface::instance());
+ QByteArray configFile = modelManager->codeModelConfiguration();
+ QStringList includePaths;
+ QStringList frameworkPaths;
+
+ updateProjectPart();
+
+ if (m_projectPart) {
+ configFile += m_projectPart->defines;
+ includePaths = m_projectPart->includePaths;
+ frameworkPaths = m_projectPart->frameworkPaths;
+ }
+
+ if (configFile != m_configFile) {
+ m_configFile = configFile;
+ invalidateSnapshot = true;
+ invalidateConfig = true;
+ }
+
+ if (includePaths != m_includePaths) {
+ m_includePaths = includePaths;
+ invalidateSnapshot = true;
+ }
+
+ if (frameworkPaths != m_frameworkPaths) {
+ m_frameworkPaths = frameworkPaths;
+ invalidateSnapshot = true;
+ }
+
+ unsigned rev = 0;
+ if (Document::Ptr doc = document())
+ rev = doc->revision();
+ else
+ invalidateSnapshot = true;
+
+ Snapshot globalSnapshot = modelManager->snapshot();
+
+ if (invalidateSnapshot) {
+ m_snapshot = Snapshot();
+ } else {
+ // Remove changed files from the snapshot
+ QSet<QString> toRemove;
+ foreach (const Document::Ptr &doc, m_snapshot) {
+ QString fileName = doc->fileName();
+ if (workingCopy.contains(fileName)) {
+ if (workingCopy.get(fileName).second != doc->editorRevision())
+ addFileAndDependencies(&toRemove, fileName);
+ continue;
+ }
+ Document::Ptr otherDoc = globalSnapshot.document(fileName);
+ if (!otherDoc.isNull() && otherDoc->revision() != doc->revision())
+ addFileAndDependencies(&toRemove, fileName);
+ }
+
+ if (!toRemove.isEmpty()) {
+ invalidateSnapshot = true;
+ foreach (const QString &fileName, toRemove)
+ m_snapshot.remove(fileName);
+ }
+ }
+
+ // Update the snapshot
+ if (invalidateSnapshot) {
+ const QString configurationFileName = modelManager->configurationFileName();
+ if (invalidateConfig)
+ m_snapshot.remove(configurationFileName);
+ if (!m_snapshot.contains(configurationFileName))
+ workingCopy.insert(configurationFileName, m_configFile);
+ m_snapshot.remove(m_fileInEditor);
+
+ CppPreprocessor preproc(modelManager, m_snapshot);
+ Snapshot globalSnapshot = modelManager->snapshot();
+ globalSnapshot.remove(fileInEditor());
+ preproc.setGlobalSnapshot(globalSnapshot);
+ preproc.setWorkingCopy(workingCopy);
+ preproc.setIncludePaths(m_includePaths);
+ preproc.setFrameworkPaths(m_frameworkPaths);
+ preproc.run(configurationFileName);
+ preproc.run(m_fileInEditor);
+
+ m_snapshot = preproc.snapshot();
+ m_snapshot = m_snapshot.simplified(document());
+ m_deps.build(m_snapshot);
+
+ foreach (Document::Ptr doc, m_snapshot) {
+ QString fileName = doc->fileName();
+ if (doc->revision() == 0) {
+ Document::Ptr otherDoc = globalSnapshot.document(fileName);
+ doc->setRevision(otherDoc.isNull() ? 0 : otherDoc->revision());
+ }
+ if (fileName != fileInEditor())
+ doc->releaseSourceAndAST();
+ }
+
+ QTC_CHECK(document());
+ if (Document::Ptr doc = document())
+ doc->setRevision(rev + 1);
+ }
+}
+
+Document::Ptr SnapshotUpdater::document() const
+{
+ QMutexLocker locker(&m_mutex);
+
+ return m_snapshot.document(m_fileInEditor);
+}
+
+void SnapshotUpdater::updateProjectPart()
+{
+ CppModelManager *cmm = dynamic_cast<CppModelManager *>(CppModelManagerInterface::instance());
+ QList<ProjectPart::Ptr> pParts = cmm->projectPart(m_fileInEditor);
+ if (pParts.isEmpty()) {
+ if (m_projectPart)
+ // File is not directly part of any project, but we got one before. We will re-use it,
+ // because re-calculating this can be expensive when the dependency table is big.
+ return;
+
+ // Fall-back step 1: Get some parts through the dependency table:
+ pParts = cmm->projectPartFromDependencies(m_fileInEditor);
+ if (pParts.isEmpty())
+ // Fall-back step 2: Use fall-back part from the model manager:
+ m_projectPart = cmm->fallbackProjectPart();
+ else
+ m_projectPart = pParts.first();
+ } else {
+ if (!pParts.contains(m_projectPart))
+ // Apparently the project file changed, so update our project part.
+ m_projectPart = pParts.first();
+ }
+}
+
+void SnapshotUpdater::addFileAndDependencies(QSet<QString> *toRemove, const QString &fileName) const
+{
+ toRemove->insert(fileName);
+ if (fileName != m_fileInEditor) {
+ QStringList deps = m_deps.filesDependingOn(fileName);
+ toRemove->unite(QSet<QString>::fromList(deps));
+ }
+}
+
diff --git a/src/plugins/cpptools/cppsnapshotupdater.h b/src/plugins/cpptools/cppsnapshotupdater.h
new file mode 100644
index 0000000000..8db54def7e
--- /dev/null
+++ b/src/plugins/cpptools/cppsnapshotupdater.h
@@ -0,0 +1,84 @@
+/****************************************************************************
+**
+** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
+** Contact: http://www.qt-project.org/legal
+**
+** 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 Digia. For licensing terms and
+** conditions see http://qt.digia.com/licensing. For further information
+** use the contact form at http://qt.digia.com/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 2.1 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 2.1 requirements
+** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
+**
+** In addition, as a special exception, Digia gives you certain additional
+** rights. These rights are described in the Digia Qt LGPL Exception
+** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
+**
+****************************************************************************/
+
+#ifndef CPPTOOLS_INTERNAL_SNAPSHOTUPDATER_H
+#define CPPTOOLS_INTERNAL_SNAPSHOTUPDATER_H
+
+#include "cpptools_global.h"
+#include "cppmodelmanager.h"
+
+#include <cplusplus/CppDocument.h>
+#include <cplusplus/DependencyTable.h>
+
+#include <QMutex>
+#include <QString>
+
+namespace CppTools {
+
+class CPPTOOLS_EXPORT SnapshotUpdater
+{
+ Q_DISABLE_COPY(SnapshotUpdater)
+
+public:
+ SnapshotUpdater(const QString &fileInEditor = QString());
+
+ QString fileInEditor() const
+ { return m_fileInEditor; }
+
+ void update(CppModelManagerInterface::WorkingCopy workingCopy);
+
+ CPlusPlus::Document::Ptr document() const;
+
+ CPlusPlus::Snapshot snapshot() const
+ { return m_snapshot; }
+
+ QStringList includePaths() const
+ { return m_includePaths; }
+
+ QStringList frameworkPaths() const
+ { return m_frameworkPaths; }
+
+private:
+ void updateProjectPart();
+ void addFileAndDependencies(QSet<QString> *toRemove, const QString &fileName) const;
+
+private:
+ mutable QMutex m_mutex;
+ QString m_fileInEditor;
+ ProjectPart::Ptr m_projectPart;
+ QByteArray m_configFile;
+ QStringList m_includePaths;
+ QStringList m_frameworkPaths;
+ CPlusPlus::Snapshot m_snapshot;
+ CPlusPlus::DependencyTable m_deps;
+};
+
+} // namespace CppTools
+
+#endif // CPPTOOLS_INTERNAL_SNAPSHOTUPDATER_H
diff --git a/src/plugins/cpptools/cpptools.pro b/src/plugins/cpptools/cpptools.pro
index b27d4f46fe..7c3d94fc91 100644
--- a/src/plugins/cpptools/cpptools.pro
+++ b/src/plugins/cpptools/cpptools.pro
@@ -12,6 +12,7 @@ HEADERS += completionsettingspage.h \
cpptools_global.h \
cpptoolsconstants.h \
cpptoolseditorsupport.h \
+ cppsnapshotupdater.h \
cpptoolsplugin.h \
cppqtstyleindenter.h \
searchsymbols.h \
@@ -60,6 +61,7 @@ SOURCES += completionsettingspage.cpp \
cppmodelmanagerinterface.cpp \
cpplocatorfilter.cpp \
cpptoolseditorsupport.cpp \
+ cppsnapshotupdater.cpp \
cpptoolsplugin.cpp \
cppqtstyleindenter.cpp \
searchsymbols.cpp \
diff --git a/src/plugins/cpptools/cpptools.qbs b/src/plugins/cpptools/cpptools.qbs
index e78d40ee19..16584b457f 100644
--- a/src/plugins/cpptools/cpptools.qbs
+++ b/src/plugins/cpptools/cpptools.qbs
@@ -121,7 +121,9 @@ QtcPlugin {
"cppcodemodelsettings.h",
"cppcodemodelsettingspage.cpp",
"cppcodemodelsettingspage.h",
- "cppcodemodelsettingspage.ui"
+ "cppcodemodelsettingspage.ui",
+ "cppsnapshotupdater.cpp",
+ "cppsnapshotupdater.h",
]
Group {
diff --git a/src/plugins/cpptools/cpptoolseditorsupport.cpp b/src/plugins/cpptools/cpptoolseditorsupport.cpp
index fc4d64b3c9..c33a4241b9 100644
--- a/src/plugins/cpptools/cpptoolseditorsupport.cpp
+++ b/src/plugins/cpptools/cpptoolseditorsupport.cpp
@@ -169,6 +169,8 @@ QString CppEditorSupport::fileName() const
QByteArray CppEditorSupport::contents() const
{
+ QMutexLocker locker(&m_cachedContentsLock);
+
const int editorRev = editorRevision();
if (m_cachedContentsEditorRevision != editorRev && !m_fileIsBeingReloaded) {
m_cachedContentsEditorRevision = editorRev;
@@ -215,6 +217,13 @@ SemanticInfo CppEditorSupport::recalculateSemanticInfo(bool emitSignalWhenFinish
return m_lastSemanticInfo;
}
+Document::Ptr CppEditorSupport::lastSemanticInfoDocument() const
+{
+ QMutexLocker locker(&m_lastSemanticInfoLock);
+
+ return m_lastSemanticInfo.doc;
+}
+
void CppEditorSupport::recalculateSemanticInfoDetached(bool force)
{
// Block premature calculation caused by CppEditorPlugin::currentEditorChanged
@@ -236,6 +245,16 @@ CppCompletionAssistProvider *CppEditorSupport::completionAssistProvider() const
return m_completionAssistProvider;
}
+QSharedPointer<SnapshotUpdater> CppEditorSupport::snapshotUpdater()
+{
+ QSharedPointer<SnapshotUpdater> updater = m_snapshotUpdater;
+ if (!updater) {
+ updater.reset(new SnapshotUpdater(fileName()));
+ m_snapshotUpdater = updater;
+ }
+ return updater;
+}
+
void CppEditorSupport::updateDocument()
{
m_revision = editorRevision();
@@ -246,6 +265,19 @@ void CppEditorSupport::updateDocument()
m_updateDocumentTimer->start(m_updateDocumentInterval);
}
+static void parse(QFutureInterface<void> &future, CppEditorSupport *support)
+{
+ future.setProgressRange(0, 1);
+
+ CppModelManager *cmm = qobject_cast<CppModelManager *>(CppModelManager::instance());
+ QSharedPointer<SnapshotUpdater> updater = support->snapshotUpdater();
+
+ updater->update(cmm->workingCopy());
+ cmm->finishedRefreshingSourceFiles(QStringList(updater->document()->fileName()));
+
+ future.setProgressValue(1);
+}
+
void CppEditorSupport::updateDocumentNow()
{
if (m_documentParser.isRunning() || m_revision != editorRevision()) {
@@ -259,8 +291,7 @@ void CppEditorSupport::updateDocumentNow()
if (m_highlightingSupport && !m_highlightingSupport->requiresSemanticInfo())
startHighlighting();
- const QStringList sourceFiles(m_textEditor->document()->filePath());
- m_documentParser = m_modelManager->updateSourceFiles(sourceFiles);
+ m_documentParser = QtConcurrent::run(&parse, this);
}
}
@@ -429,7 +460,7 @@ SemanticInfo::Source CppEditorSupport::currentSource(bool force)
int line = 0, column = 0;
m_textEditor->convertPosition(m_textEditor->editorWidget()->position(), &line, &column);
- const Snapshot snapshot = m_modelManager->snapshot();
+ const Snapshot snapshot = m_snapshotUpdater->snapshot();
QByteArray code;
if (force || m_lastSemanticInfo.revision != editorRevision())
diff --git a/src/plugins/cpptools/cpptoolseditorsupport.h b/src/plugins/cpptools/cpptoolseditorsupport.h
index 46e9ee3d51..838b8cab4f 100644
--- a/src/plugins/cpptools/cpptoolseditorsupport.h
+++ b/src/plugins/cpptools/cpptoolseditorsupport.h
@@ -33,12 +33,14 @@
#include "cpphighlightingsupport.h"
#include "cppmodelmanager.h"
#include "cppsemanticinfo.h"
+#include "cppsnapshotupdater.h"
#include <cplusplus/CppDocument.h>
#include <QFuture>
#include <QObject>
#include <QPointer>
+#include <QSharedPointer>
#include <QTimer>
namespace CPlusPlus { class AST; }
@@ -111,6 +113,8 @@ public:
/// thread if it is outdate.
SemanticInfo recalculateSemanticInfo(bool emitSignalWhenFinished = true);
+ CPlusPlus::Document::Ptr lastSemanticInfoDocument() const;
+
/// Recalculates the semantic info in a future, and will emit the
/// semanticInfoUpdated() signal when finished.
/// Requires that initialized() is true.
@@ -119,6 +123,8 @@ public:
CppCompletionAssistProvider *completionAssistProvider() const;
+ QSharedPointer<SnapshotUpdater> snapshotUpdater();
+
signals:
void documentUpdated();
void diagnosticsChanged();
@@ -173,6 +179,7 @@ private:
QFuture<void> m_documentParser;
// content caching
+ mutable QMutex m_cachedContentsLock;
mutable QByteArray m_cachedContents;
mutable int m_cachedContentsEditorRevision;
bool m_fileIsBeingReloaded;
@@ -188,6 +195,7 @@ private:
mutable QMutex m_lastSemanticInfoLock;
SemanticInfo m_lastSemanticInfo;
QFuture<void> m_futureSemanticInfo;
+ QSharedPointer<SnapshotUpdater> m_snapshotUpdater;
// Highlighting:
unsigned m_lastHighlightRevision;
diff --git a/src/plugins/cpptools/cpptoolsplugin.h b/src/plugins/cpptools/cpptoolsplugin.h
index 4f7653ddaf..b45631ab8d 100644
--- a/src/plugins/cpptools/cpptoolsplugin.h
+++ b/src/plugins/cpptools/cpptoolsplugin.h
@@ -211,6 +211,7 @@ private slots:
void test_modelmanager_extraeditorsupport_uiFiles();
void test_modelmanager_gc_if_last_cppeditor_closed();
void test_modelmanager_dont_gc_opened_files();
+ void test_modelmanager_defines_per_project();
void test_cpplocatorfilters_CppLocatorFilter();
void test_cpplocatorfilters_CppLocatorFilter_data();