From 442bdbded2b2268efcb3be1a969d432533023366 Mon Sep 17 00:00:00 2001 From: Nikolai Kosjar Date: Tue, 7 Jul 2015 15:02:14 +0200 Subject: [PATCH] CppTools: Avoid unnecessary blocking of main thread Among others, BaseEditorDocumentParser::projectPart() was a blocking operation in case the parser was running. This led to noticeable GUI freezes for the ClangCodeModel since the function was called from the main thread. Rework *EditorDocumentParser to clearly separate the configuration data (input) from the actual object state. Querying/setting configuration or (last) state does not block anymore. update() is supposed to get the necessary configuration and the last state at the beginning and to set the new state at end. Change-Id: Ib4b534fa6ff373c3059826726b3f10ece95acc21 Reviewed-by: Erik Verbruggen --- .../clangeditordocumentparser.cpp | 16 +- src/plugins/cppeditor/cppeditordocument.cpp | 8 +- .../cpptools/baseeditordocumentparser.cpp | 101 +++++------- .../cpptools/baseeditordocumentparser.h | 48 +++--- .../cpptools/builtineditordocumentparser.cpp | 155 ++++++++++-------- .../cpptools/builtineditordocumentparser.h | 33 ++-- .../builtineditordocumentprocessor.cpp | 5 +- src/plugins/cpptools/cppmodelmanager_test.cpp | 46 +++--- .../cpptools/cppsourceprocessor_test.cpp | 1 + src/plugins/cpptools/cpptoolstestcase.cpp | 29 ++++ src/plugins/cpptools/cpptoolstestcase.h | 2 + 11 files changed, 253 insertions(+), 191 deletions(-) diff --git a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp index 9b342bd7d1a..fd2c60dbaee 100644 --- a/src/plugins/clangcodemodel/clangeditordocumentparser.cpp +++ b/src/plugins/clangcodemodel/clangeditordocumentparser.cpp @@ -91,21 +91,25 @@ ClangEditorDocumentParser::ClangEditorDocumentParser(const QString &filePath) void ClangEditorDocumentParser::update(CppTools::WorkingCopy workingCopy) { QTC_ASSERT(m_marker, return); - QMutexLocker lock(m_marker->mutex()); - QMutexLocker lock2(&m_mutex); + QMutexLocker locker(&m_updateIsRunning); - m_projectPart = determineProjectPart(); - const QStringList options = createOptions(filePath(), projectPart(), true); + // Determine project part + State state_ = state(); + state_.projectPart = determineProjectPart(filePath(), configuration(), state_); + setState(state_); + // Determine command line arguments + const QStringList options = createOptions(filePath(), state_.projectPart, true); qCDebug(log, "Reparse options (cmd line equivalent): %s", commandLine(options, filePath()).toUtf8().constData()); - QTime t; t.start(); + // Run + QTime t; t.start(); + QMutexLocker lock(m_marker->mutex()); m_marker->setFileName(filePath()); m_marker->setCompilationOptions(options); const Internal::UnsavedFiles unsavedFiles = Utils::createUnsavedFiles(workingCopy); m_marker->reparse(unsavedFiles); - qCDebug(log) << "Reparse took" << t.elapsed() << "ms."; } diff --git a/src/plugins/cppeditor/cppeditordocument.cpp b/src/plugins/cppeditor/cppeditordocument.cpp index 77bffc7338c..2746303cf0e 100644 --- a/src/plugins/cppeditor/cppeditordocument.cpp +++ b/src/plugins/cppeditor/cppeditordocument.cpp @@ -268,9 +268,11 @@ void CppEditorDocument::setPreprocessorSettings(const CppTools::ProjectPart::Ptr { CppTools::BaseEditorDocumentParser *parser = processor()->parser(); QTC_ASSERT(parser, return); - if (parser->projectPart() != projectPart || parser->editorDefines() != defines) { - parser->setProjectPart(projectPart); - parser->setEditorDefines(defines); + if (parser->projectPart() != projectPart || parser->configuration().editorDefines != defines) { + CppTools::BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.manuallySetProjectPart = projectPart; + config.editorDefines = defines; + parser->setConfiguration(config); emit preprocessorSettingsChanged(!defines.trimmed().isEmpty()); } diff --git a/src/plugins/cpptools/baseeditordocumentparser.cpp b/src/plugins/cpptools/baseeditordocumentparser.cpp index d24e341b1f2..31d08bd4317 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.cpp +++ b/src/plugins/cpptools/baseeditordocumentparser.cpp @@ -44,15 +44,19 @@ namespace CppTools { It's meant to be used in the C++ editor to get precise results by using the "best" project part for a file. - Derived classes are expected to implement update() by using the protected - mutex, determineProjectPart() and by respecting the options set by the client. + Derived classes are expected to implement update() this way: + + \list + \li Get a copy of the configuration and the last state. + \li Acquire the protected m_updateIsRunning for the duration of update(). + \li Work on the data and do whatever is necessary. At least, update + the project part with the help of determineProjectPart(). + \li Ensure the new state is set before update() returns. + \endlist */ BaseEditorDocumentParser::BaseEditorDocumentParser(const QString &filePath) - : m_mutex(QMutex::Recursive) - , m_filePath(filePath) - , m_usePrecompiledHeaders(false) - , m_editorDefinesChangedSinceLastUpdate(false) + : m_filePath(filePath) { } @@ -65,44 +69,33 @@ QString BaseEditorDocumentParser::filePath() const return m_filePath; } +BaseEditorDocumentParser::Configuration BaseEditorDocumentParser::configuration() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_configuration; +} + +void BaseEditorDocumentParser::setConfiguration(const Configuration &configuration) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_configuration = configuration; +} + +BaseEditorDocumentParser::State BaseEditorDocumentParser::state() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_state; +} + +void BaseEditorDocumentParser::setState(const State &state) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_state = state; +} + ProjectPart::Ptr BaseEditorDocumentParser::projectPart() const { - QMutexLocker locker(&m_mutex); - return m_projectPart; -} - -void BaseEditorDocumentParser::setProjectPart(ProjectPart::Ptr projectPart) -{ - QMutexLocker locker(&m_mutex); - m_manuallySetProjectPart = projectPart; -} - -bool BaseEditorDocumentParser::usePrecompiledHeaders() const -{ - QMutexLocker locker(&m_mutex); - return m_usePrecompiledHeaders; -} - -void BaseEditorDocumentParser::setUsePrecompiledHeaders(bool usePrecompiledHeaders) -{ - QMutexLocker locker(&m_mutex); - m_usePrecompiledHeaders = usePrecompiledHeaders; -} - -QByteArray BaseEditorDocumentParser::editorDefines() const -{ - QMutexLocker locker(&m_mutex); - return m_editorDefines; -} - -void BaseEditorDocumentParser::setEditorDefines(const QByteArray &editorDefines) -{ - QMutexLocker locker(&m_mutex); - - if (editorDefines != m_editorDefines) { - m_editorDefines = editorDefines; - m_editorDefinesChangedSinceLastUpdate = true; - } + return state().projectPart; } BaseEditorDocumentParser *BaseEditorDocumentParser::get(const QString &filePath) @@ -115,15 +108,17 @@ BaseEditorDocumentParser *BaseEditorDocumentParser::get(const QString &filePath) return 0; } -ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart() const +ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart(const QString &filePath, + const Configuration &config, + const State &state) { - if (m_manuallySetProjectPart) - return m_manuallySetProjectPart; + if (config.manuallySetProjectPart) + return config.manuallySetProjectPart; - ProjectPart::Ptr projectPart = m_projectPart; + ProjectPart::Ptr projectPart = state.projectPart; CppModelManager *cmm = CppModelManager::instance(); - QList projectParts = cmm->projectPart(m_filePath); + QList projectParts = cmm->projectPart(filePath); if (projectParts.isEmpty()) { if (projectPart) // File is not directly part of any project, but we got one before. We will re-use it, @@ -131,7 +126,7 @@ ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart() const return projectPart; // Fall-back step 1: Get some parts through the dependency table: - projectParts = cmm->projectPartFromDependencies(Utils::FileName::fromString(m_filePath)); + projectParts = cmm->projectPartFromDependencies(Utils::FileName::fromString(filePath)); if (projectParts.isEmpty()) // Fall-back step 2: Use fall-back part from the model manager: projectPart = cmm->fallbackProjectPart(); @@ -146,14 +141,4 @@ ProjectPart::Ptr BaseEditorDocumentParser::determineProjectPart() const return projectPart; } -bool BaseEditorDocumentParser::editorDefinesChanged() const -{ - return m_editorDefinesChangedSinceLastUpdate; -} - -void BaseEditorDocumentParser::resetEditorDefinesChanged() -{ - m_editorDefinesChangedSinceLastUpdate = false; -} - } // namespace CppTools diff --git a/src/plugins/cpptools/baseeditordocumentparser.h b/src/plugins/cpptools/baseeditordocumentparser.h index c8f5704a9dc..42ce6c12b33 100644 --- a/src/plugins/cpptools/baseeditordocumentparser.h +++ b/src/plugins/cpptools/baseeditordocumentparser.h @@ -42,45 +42,49 @@ class CPPTOOLS_EXPORT BaseEditorDocumentParser : public QObject { Q_OBJECT +public: + static BaseEditorDocumentParser *get(const QString &filePath); + + struct Configuration { + bool usePrecompiledHeaders = false; + QByteArray editorDefines; + ProjectPart::Ptr manuallySetProjectPart; + }; + public: BaseEditorDocumentParser(const QString &filePath); virtual ~BaseEditorDocumentParser(); QString filePath() const; + Configuration configuration() const; + void setConfiguration(const Configuration &configuration); + virtual void update(WorkingCopy workingCopy) = 0; ProjectPart::Ptr projectPart() const; - void setProjectPart(ProjectPart::Ptr projectPart); - - bool usePrecompiledHeaders() const; - void setUsePrecompiledHeaders(bool usePrecompiledHeaders); - - QByteArray editorDefines() const; - void setEditorDefines(const QByteArray &editorDefines); - -public: - static BaseEditorDocumentParser *get(const QString &filePath); protected: - ProjectPart::Ptr determineProjectPart() const; + struct State { + QByteArray editorDefines; + ProjectPart::Ptr projectPart; + }; - bool editorDefinesChanged() const; - void resetEditorDefinesChanged(); + State state() const; + void setState(const State &state); -protected: - mutable QMutex m_mutex; - ProjectPart::Ptr m_projectPart; + static ProjectPart::Ptr determineProjectPart(const QString &filePath, + const Configuration &config, + const State &state); + + mutable QMutex m_updateIsRunning; + mutable QMutex m_stateAndConfigurationMutex; private: const QString m_filePath; - ProjectPart::Ptr m_manuallySetProjectPart; - - bool m_usePrecompiledHeaders; - - QByteArray m_editorDefines; - bool m_editorDefinesChangedSinceLastUpdate; + Configuration m_configuration; + State m_state; }; } // namespace CppTools diff --git a/src/plugins/cpptools/builtineditordocumentparser.cpp b/src/plugins/cpptools/builtineditordocumentparser.cpp index 8c2232f1422..3871cb2bb0b 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.cpp +++ b/src/plugins/cpptools/builtineditordocumentparser.cpp @@ -40,15 +40,19 @@ using namespace CppTools::Internal; BuiltinEditorDocumentParser::BuiltinEditorDocumentParser(const QString &filePath) : BaseEditorDocumentParser(filePath) - , m_forceSnapshotInvalidation(false) - , m_releaseSourceAndAST(true) { qRegisterMetaType("CPlusPlus::Snapshot"); } void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) { - QMutexLocker locker(&m_mutex); + QMutexLocker locker(&m_updateIsRunning); + + const Configuration baseConfig = configuration(); + const bool releaseSourceAndAST_ = releaseSourceAndAST(); + + State baseState = state(); + ExtraState state = extraState(); if (filePath().isEmpty()) return; @@ -62,52 +66,52 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) QString projectConfigFile; LanguageFeatures features = LanguageFeatures::defaultFeatures(); - m_projectPart = determineProjectPart(); + baseState.projectPart = determineProjectPart(filePath(), baseConfig, baseState); - if (m_forceSnapshotInvalidation) { + if (state.forceSnapshotInvalidation) { invalidateSnapshot = true; - m_forceSnapshotInvalidation = false; + state.forceSnapshotInvalidation = false; } - if (const ProjectPart::Ptr part = projectPart()) { + if (const ProjectPart::Ptr part = baseState.projectPart) { configFile += part->toolchainDefines; configFile += part->projectDefines; headerPaths = part->headerPaths; projectConfigFile = part->projectConfigFile; - if (usePrecompiledHeaders()) + if (baseConfig.usePrecompiledHeaders) precompiledHeaders = part->precompiledHeaders; features = part->languageFeatures; } - if (configFile != m_configFile) { - m_configFile = configFile; + if (configFile != state.configFile) { + state.configFile = configFile; invalidateSnapshot = true; invalidateConfig = true; } - if (editorDefinesChanged()) { + if (baseConfig.editorDefines != baseState.editorDefines) { + baseState.editorDefines = baseConfig.editorDefines; invalidateSnapshot = true; editorDefinesChanged_ = true; - resetEditorDefinesChanged(); } - if (headerPaths != m_headerPaths) { - m_headerPaths = headerPaths; + if (headerPaths != state.headerPaths) { + state.headerPaths = headerPaths; invalidateSnapshot = true; } - if (projectConfigFile != m_projectConfigFile) { - m_projectConfigFile = projectConfigFile; + if (projectConfigFile != state.projectConfigFile) { + state.projectConfigFile = projectConfigFile; invalidateSnapshot = true; } - if (precompiledHeaders != m_precompiledHeaders) { - m_precompiledHeaders = precompiledHeaders; + if (precompiledHeaders != state.precompiledHeaders) { + state.precompiledHeaders = precompiledHeaders; invalidateSnapshot = true; } unsigned rev = 0; - if (Document::Ptr doc = document()) + if (Document::Ptr doc = state.snapshot.document(filePath())) rev = doc->revision(); else invalidateSnapshot = true; @@ -115,26 +119,26 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) Snapshot globalSnapshot = modelManager->snapshot(); if (invalidateSnapshot) { - m_snapshot = Snapshot(); + state.snapshot = Snapshot(); } else { // Remove changed files from the snapshot QSet toRemove; - foreach (const Document::Ptr &doc, m_snapshot) { + foreach (const Document::Ptr &doc, state.snapshot) { const Utils::FileName fileName = Utils::FileName::fromString(doc->fileName()); if (workingCopy.contains(fileName)) { if (workingCopy.get(fileName).second != doc->editorRevision()) - addFileAndDependencies(&toRemove, fileName); + addFileAndDependencies(&state.snapshot, &toRemove, fileName); continue; } Document::Ptr otherDoc = globalSnapshot.document(fileName); if (!otherDoc.isNull() && otherDoc->revision() != doc->revision()) - addFileAndDependencies(&toRemove, fileName); + addFileAndDependencies(&state.snapshot, &toRemove, fileName); } if (!toRemove.isEmpty()) { invalidateSnapshot = true; foreach (const Utils::FileName &fileName, toRemove) - m_snapshot.remove(fileName); + state.snapshot.remove(fileName); } } @@ -142,19 +146,19 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) 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(filePath()); + state.snapshot.remove(configurationFileName); + if (!state.snapshot.contains(configurationFileName)) + workingCopy.insert(configurationFileName, state.configFile); + state.snapshot.remove(filePath()); static const QString editorDefinesFileName = CppModelManager::editorConfigurationFileName(); if (editorDefinesChanged_) { - m_snapshot.remove(editorDefinesFileName); - workingCopy.insert(editorDefinesFileName, editorDefines()); + state.snapshot.remove(editorDefinesFileName); + workingCopy.insert(editorDefinesFileName, baseState.editorDefines); } - CppSourceProcessor sourceProcessor(m_snapshot, [&](const Document::Ptr &doc) { + CppSourceProcessor sourceProcessor(state.snapshot, [&](const Document::Ptr &doc) { const QString fileName = doc->fileName(); const bool isInEditor = fileName == filePath(); Document::Ptr otherDoc = modelManager->document(fileName); @@ -163,68 +167,64 @@ void BuiltinEditorDocumentParser::update(WorkingCopy workingCopy) newRev = qMax(rev + 1, newRev); doc->setRevision(newRev); modelManager->emitDocumentUpdated(doc); - if (m_releaseSourceAndAST) + if (releaseSourceAndAST_) doc->releaseSourceAndAST(); }); Snapshot globalSnapshot = modelManager->snapshot(); globalSnapshot.remove(filePath()); sourceProcessor.setGlobalSnapshot(globalSnapshot); sourceProcessor.setWorkingCopy(workingCopy); - sourceProcessor.setHeaderPaths(m_headerPaths); + sourceProcessor.setHeaderPaths(state.headerPaths); sourceProcessor.setLanguageFeatures(features); sourceProcessor.run(configurationFileName); - if (!m_projectConfigFile.isEmpty()) - sourceProcessor.run(m_projectConfigFile); - if (usePrecompiledHeaders()) { - foreach (const QString &precompiledHeader, m_precompiledHeaders) + if (!state.projectConfigFile.isEmpty()) + sourceProcessor.run(state.projectConfigFile); + if (baseConfig.usePrecompiledHeaders) { + foreach (const QString &precompiledHeader, state.precompiledHeaders) sourceProcessor.run(precompiledHeader); } - if (!editorDefines().isEmpty()) + if (!baseState.editorDefines.isEmpty()) sourceProcessor.run(editorDefinesFileName); - sourceProcessor.run(filePath(), usePrecompiledHeaders() ? m_precompiledHeaders - : QStringList()); - m_snapshot = sourceProcessor.snapshot(); - Snapshot newSnapshot = m_snapshot.simplified(document()); - for (Snapshot::const_iterator i = m_snapshot.begin(), ei = m_snapshot.end(); i != ei; ++i) { + sourceProcessor.run(filePath(), baseConfig.usePrecompiledHeaders ? state.precompiledHeaders + : QStringList()); + state.snapshot = sourceProcessor.snapshot(); + Snapshot newSnapshot = state.snapshot.simplified(state.snapshot.document(filePath())); + for (Snapshot::const_iterator i = state.snapshot.begin(), ei = state.snapshot.end(); i != ei; ++i) { if (Client::isInjectedFile(i.key().toString())) newSnapshot.insert(i.value()); } - m_snapshot = newSnapshot; - m_snapshot.updateDependencyTable(); - - emit finished(document(), m_snapshot); + state.snapshot = newSnapshot; + state.snapshot.updateDependencyTable(); } + + setState(baseState); + setExtraState(state); + + if (invalidateSnapshot) + emit finished(state.snapshot.document(filePath()), state.snapshot); } void BuiltinEditorDocumentParser::releaseResources() { - QMutexLocker locker(&m_mutex); - m_snapshot = Snapshot(); - m_forceSnapshotInvalidation = true; + ExtraState s = extraState(); + s.snapshot = Snapshot(); + s.forceSnapshotInvalidation = true; + setExtraState(s); } Document::Ptr BuiltinEditorDocumentParser::document() const { - QMutexLocker locker(&m_mutex); - return m_snapshot.document(filePath()); + return extraState().snapshot.document(filePath()); } Snapshot BuiltinEditorDocumentParser::snapshot() const { - QMutexLocker locker(&m_mutex); - return m_snapshot; + return extraState().snapshot; } ProjectPart::HeaderPaths BuiltinEditorDocumentParser::headerPaths() const { - QMutexLocker locker(&m_mutex); - return m_headerPaths; -} - -void BuiltinEditorDocumentParser::setReleaseSourceAndAST(bool onoff) -{ - QMutexLocker locker(&m_mutex); - m_releaseSourceAndAST = onoff; + return extraState().headerPaths; } BuiltinEditorDocumentParser *BuiltinEditorDocumentParser::get(const QString &filePath) @@ -234,12 +234,39 @@ BuiltinEditorDocumentParser *BuiltinEditorDocumentParser::get(const QString &fil return 0; } -void BuiltinEditorDocumentParser::addFileAndDependencies(QSet *toRemove, +void BuiltinEditorDocumentParser::addFileAndDependencies(Snapshot *snapshot, + QSet *toRemove, const Utils::FileName &fileName) const { + QTC_ASSERT(snapshot, return); + toRemove->insert(fileName); if (fileName != Utils::FileName::fromString(filePath())) { - Utils::FileNameList deps = m_snapshot.filesDependingOn(fileName); + Utils::FileNameList deps = snapshot->filesDependingOn(fileName); toRemove->unite(QSet::fromList(deps)); } } + +BuiltinEditorDocumentParser::ExtraState BuiltinEditorDocumentParser::extraState() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_extraState; +} + +void BuiltinEditorDocumentParser::setExtraState(const ExtraState &extraState) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_extraState = extraState; +} + +bool BuiltinEditorDocumentParser::releaseSourceAndAST() const +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + return m_releaseSourceAndAST; +} + +void BuiltinEditorDocumentParser::setReleaseSourceAndAST(bool release) +{ + QMutexLocker locker(&m_stateAndConfigurationMutex); + m_releaseSourceAndAST = release; +} diff --git a/src/plugins/cpptools/builtineditordocumentparser.h b/src/plugins/cpptools/builtineditordocumentparser.h index ebd8cc28fd5..a495be4787a 100644 --- a/src/plugins/cpptools/builtineditordocumentparser.h +++ b/src/plugins/cpptools/builtineditordocumentparser.h @@ -37,7 +37,6 @@ #include -#include #include namespace CppTools { @@ -49,14 +48,16 @@ class CPPTOOLS_EXPORT BuiltinEditorDocumentParser : public BaseEditorDocumentPar public: BuiltinEditorDocumentParser(const QString &filePath); + bool releaseSourceAndAST() const; + void setReleaseSourceAndAST(bool release); + void update(WorkingCopy workingCopy) override; - void releaseResources(); CPlusPlus::Document::Ptr document() const; CPlusPlus::Snapshot snapshot() const; ProjectPart::HeaderPaths headerPaths() const; - void setReleaseSourceAndAST(bool onoff); + void releaseResources(); signals: void finished(CPlusPlus::Document::Ptr document, CPlusPlus::Snapshot snapshot); @@ -65,18 +66,26 @@ public: static BuiltinEditorDocumentParser *get(const QString &filePath); private: - void addFileAndDependencies(QSet *toRemove, const Utils::FileName &fileName) const; + void addFileAndDependencies(CPlusPlus::Snapshot *snapshot, + QSet *toRemove, + const Utils::FileName &fileName) const; -private: - QByteArray m_configFile; + struct ExtraState { + QByteArray configFile; - ProjectPart::HeaderPaths m_headerPaths; - QString m_projectConfigFile; - QStringList m_precompiledHeaders; + ProjectPart::HeaderPaths headerPaths; + QString projectConfigFile; + QStringList precompiledHeaders; - CPlusPlus::Snapshot m_snapshot; - bool m_forceSnapshotInvalidation; - bool m_releaseSourceAndAST; + CPlusPlus::Snapshot snapshot; + bool forceSnapshotInvalidation = false; + }; + + ExtraState extraState() const; + void setExtraState(const ExtraState &extraState); + + bool m_releaseSourceAndAST = true; + ExtraState m_extraState; }; } // namespace CppTools diff --git a/src/plugins/cpptools/builtineditordocumentprocessor.cpp b/src/plugins/cpptools/builtineditordocumentprocessor.cpp index 12bfe1531c3..518fe1a6f6e 100644 --- a/src/plugins/cpptools/builtineditordocumentprocessor.cpp +++ b/src/plugins/cpptools/builtineditordocumentprocessor.cpp @@ -134,7 +134,10 @@ BuiltinEditorDocumentProcessor::BuiltinEditorDocumentProcessor( using namespace Internal; QSharedPointer cms = CppToolsPlugin::instance()->codeModelSettings(); - m_parser.setUsePrecompiledHeaders(cms->pchUsage() != CppCodeModelSettings::PchUse_None); + + BaseEditorDocumentParser::Configuration config = m_parser.configuration(); + config.usePrecompiledHeaders = cms->pchUsage() != CppCodeModelSettings::PchUse_None; + m_parser.setConfiguration(config); if (m_semanticHighlighter) { m_semanticHighlighter->setHighlightingRunner( diff --git a/src/plugins/cpptools/cppmodelmanager_test.cpp b/src/plugins/cpptools/cppmodelmanager_test.cpp index 7140ffecc25..79d70056f74 100644 --- a/src/plugins/cpptools/cppmodelmanager_test.cpp +++ b/src/plugins/cpptools/cppmodelmanager_test.cpp @@ -167,13 +167,11 @@ private: const QString &m_filePath; }; -void waitForProcessedEditorDocument(const QString &filePath) +ProjectPart::Ptr projectPartOfEditorDocument(const QString &filePath) { - CppEditorDocumentHandle *editorDocument - = CppModelManager::instance()->cppEditorDocument(filePath); - QVERIFY(editorDocument); - while (editorDocument->processor()->isParserRunning()) - QCoreApplication::processEvents(); + auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + QTC_ASSERT(editorDocument, return ProjectPart::Ptr()); + return editorDocument->processor()->parser()->projectPart(); } } // anonymous namespace @@ -914,7 +912,9 @@ void CppToolsPlugin::test_modelmanager_precompiled_headers() auto *parser = BuiltinEditorDocumentParser::get(fileName); QVERIFY(parser); - parser->setUsePrecompiledHeaders(true); + BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.usePrecompiledHeaders = true; + parser->setConfiguration(config); parser->update(mm->workingCopy()); // Check if defines from pch are considered @@ -995,7 +995,10 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() const QString filePath = editor->document()->filePath().toString(); BaseEditorDocumentParser *parser = BaseEditorDocumentParser::get(filePath); - parser->setEditorDefines(editorDefines.toUtf8()); + BaseEditorDocumentParser::Configuration config = parser->configuration(); + config.editorDefines = editorDefines.toUtf8(); + parser->setConfiguration(config); + parser->update(mm->workingCopy()); Document::Ptr doc = mm->document(main1File); @@ -1006,7 +1009,6 @@ void CppToolsPlugin::test_modelmanager_defines_per_editor() void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() { ModelManagerTestHelper helper; - CppModelManager *mm = CppModelManager::instance(); MyTestDataDir testDataDirectory(_("testdata_defines")); const QString fileA = testDataDirectory.file(_("main1.cpp")); // content not relevant @@ -1017,10 +1019,8 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QVERIFY(editorA); EditorCloser closerA(editorA); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 1); - - CppEditorDocumentHandle *editorDocumentA = mm->cppEditorDocument(fileA); - QVERIFY(editorDocumentA); - ProjectPart::Ptr documentAProjectPart = editorDocumentA->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); + ProjectPart::Ptr documentAProjectPart = projectPartOfEditorDocument(fileA); QVERIFY(!documentAProjectPart->project); // Open file B in editor @@ -1028,10 +1028,8 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() QVERIFY(editorB); EditorCloser closerB(editorB); QCOMPARE(Core::DocumentModel::openedDocuments().size(), 2); - - CppEditorDocumentHandle *editorDocumentB = mm->cppEditorDocument(fileB); - QVERIFY(editorDocumentB); - ProjectPart::Ptr documentBProjectPart = editorDocumentB->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); + ProjectPart::Ptr documentBProjectPart = projectPartOfEditorDocument(fileB); QVERIFY(!documentBProjectPart->project); // Switch back to document A @@ -1053,16 +1051,14 @@ void CppToolsPlugin::test_modelmanager_updateEditorsAfterProjectUpdate() helper.updateProjectInfo(pi); // ... and check for updated editor document A - while (editorDocumentA->processor()->isParserRunning()) - QCoreApplication::processEvents(); - documentAProjectPart = editorDocumentA->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileA)); + documentAProjectPart = projectPartOfEditorDocument(fileA); QCOMPARE(documentAProjectPart->project, project); // Switch back to document B and check if that's updated, too Core::EditorManager::activateEditor(editorB); - while (editorDocumentB->processor()->isParserRunning()) - QCoreApplication::processEvents(); - documentBProjectPart = editorDocumentB->processor()->parser()->projectPart(); + QVERIFY(TestCase::waitForProcessedEditorDocument(fileB)); + documentBProjectPart = projectPartOfEditorDocument(fileB); QCOMPARE(documentBProjectPart->project, project); } @@ -1131,7 +1127,7 @@ void CppToolsPlugin::test_modelmanager_documentsAndRevisions() TextEditor::BaseTextEditor *editor1; QVERIFY(helper.openBaseTextEditor(filePath1, &editor1)); helper.closeEditorAtEndOfTestCase(editor1); - waitForProcessedEditorDocument(filePath1); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath1)); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 2U); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 1U); @@ -1144,7 +1140,7 @@ void CppToolsPlugin::test_modelmanager_documentsAndRevisions() TextEditor::BaseTextEditor *editor2; QVERIFY(helper.openBaseTextEditor(filePath2, &editor2)); helper.closeEditorAtEndOfTestCase(editor2); - waitForProcessedEditorDocument(filePath2); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath2)); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath1), 3U); VERIFY_DOCUMENT_REVISION(modelManager->document(filePath2), 3U); diff --git a/src/plugins/cpptools/cppsourceprocessor_test.cpp b/src/plugins/cpptools/cppsourceprocessor_test.cpp index 378d1ae4134..518eece2262 100644 --- a/src/plugins/cpptools/cppsourceprocessor_test.cpp +++ b/src/plugins/cpptools/cppsourceprocessor_test.cpp @@ -137,6 +137,7 @@ void CppToolsPlugin::test_cppsourceprocessor_includes_cyclic() const QString filePath = editor->document()->filePath().toString(); auto *processor = BaseEditorDocumentProcessor::get(filePath); QVERIFY(processor); + QVERIFY(TestCase::waitForProcessedEditorDocument(filePath)); Snapshot snapshot = processor->snapshot(); QCOMPARE(snapshot.size(), 3); // Configuration file included diff --git a/src/plugins/cpptools/cpptoolstestcase.cpp b/src/plugins/cpptools/cpptoolstestcase.cpp index 8d2a3684a0c..8cd7953320e 100644 --- a/src/plugins/cpptools/cpptoolstestcase.cpp +++ b/src/plugins/cpptools/cpptoolstestcase.cpp @@ -29,6 +29,10 @@ ****************************************************************************/ #include "cpptoolstestcase.h" + +#include "baseeditordocumentparser.h" +#include "baseeditordocumentprocessor.h" +#include "editordocumenthandle.h" #include "cppmodelmanager.h" #include "cppworkingcopy.h" @@ -140,6 +144,31 @@ bool TestCase::garbageCollectGlobalSnapshot() return globalSnapshot().isEmpty(); } +static bool waitForProcessedEditorDocument_internal(CppEditorDocumentHandle *editorDocument, + int timeOutInMs) +{ + QTC_ASSERT(editorDocument, return false); + + QTime timer; + timer.start(); + + forever { + if (!editorDocument->processor()->isParserRunning()) + return true; + if (timer.elapsed() > timeOutInMs) + return false; + + QCoreApplication::processEvents(); + QThread::msleep(20); + } +} + +bool TestCase::waitForProcessedEditorDocument(const QString &filePath, int timeOutInMs) +{ + auto *editorDocument = CppModelManager::instance()->cppEditorDocument(filePath); + return waitForProcessedEditorDocument_internal(editorDocument, timeOutInMs); +} + bool TestCase::parseFiles(const QSet &filePaths) { CppModelManager::instance()->updateSourceFiles(filePaths).waitForFinished(); diff --git a/src/plugins/cpptools/cpptoolstestcase.h b/src/plugins/cpptools/cpptoolstestcase.h index 5a95952365d..d35e93c3b4c 100644 --- a/src/plugins/cpptools/cpptoolstestcase.h +++ b/src/plugins/cpptools/cpptoolstestcase.h @@ -95,6 +95,8 @@ public: static CPlusPlus::Snapshot globalSnapshot(); static bool garbageCollectGlobalSnapshot(); + static bool waitForProcessedEditorDocument(const QString &filePath, int timeOutInMs = 5000); + enum { defaultTimeOutInMs = 30 * 1000 /*= 30 secs*/ }; static bool waitUntilCppModelManagerIsAwareOf( ProjectExplorer::Project *project,