forked from qt-creator/qt-creator
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>
This commit is contained in:
@@ -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;
|
||||
}
|
||||
|
||||
// -----------------
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 ¯o, 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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
194
src/plugins/cpptools/cppsnapshotupdater.cpp
Normal file
194
src/plugins/cpptools/cppsnapshotupdater.cpp
Normal file
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
84
src/plugins/cpptools/cppsnapshotupdater.h
Normal file
84
src/plugins/cpptools/cppsnapshotupdater.h
Normal file
@@ -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
|
||||
@@ -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 \
|
||||
|
||||
@@ -121,7 +121,9 @@ QtcPlugin {
|
||||
"cppcodemodelsettings.h",
|
||||
"cppcodemodelsettingspage.cpp",
|
||||
"cppcodemodelsettingspage.h",
|
||||
"cppcodemodelsettingspage.ui"
|
||||
"cppcodemodelsettingspage.ui",
|
||||
"cppsnapshotupdater.cpp",
|
||||
"cppsnapshotupdater.h",
|
||||
]
|
||||
|
||||
Group {
|
||||
|
||||
@@ -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())
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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();
|
||||
|
||||
Reference in New Issue
Block a user