diff --git a/plugins/problemreporter/problemreportermodel.h b/plugins/problemreporter/problemreportermodel.h --- a/plugins/problemreporter/problemreportermodel.h +++ b/plugins/problemreporter/problemreportermodel.h @@ -45,13 +45,9 @@ ~ProblemReporterModel() override; /** - * Get problems for @ref url. - */ - QVector problems(const KDevelop::IndexedString& url, bool showImports) const; - /** * Get merged list of problems for all @ref urls. */ - QVector problems(const QSet& urls, bool showImports) const; + QVector problems(const QSet& urls) const; public Q_SLOTS: /** @@ -59,7 +55,6 @@ */ void problemsUpdated(const KDevelop::IndexedString& url); - void setShowImports(bool showImports) override; void forceFullUpdate() override; protected Q_SLOTS: @@ -71,12 +66,8 @@ void setCurrentDocument(KDevelop::IDocument* doc) override; private: - void problemsInternal(KDevelop::TopDUContext* context, bool showImports, - QSet& visitedContexts, - QVector& result) const; void rebuildProblemList(); - bool m_showImports; /// include problems from imported documents QTimer* m_minTimer; QTimer* m_maxTimer; const static int MinTimeout; diff --git a/plugins/problemreporter/problemreportermodel.cpp b/plugins/problemreporter/problemreportermodel.cpp --- a/plugins/problemreporter/problemreportermodel.cpp +++ b/plugins/problemreporter/problemreportermodel.cpp @@ -49,7 +49,6 @@ ProblemReporterModel::ProblemReporterModel(QObject* parent) : ProblemModel(parent, new FilteredProblemStore()) - , m_showImports(false) { setFeatures(CanDoFullUpdate | CanShowImports | ScopeFilter | SeverityFilter); @@ -70,23 +69,24 @@ { } -QVector ProblemReporterModel::problems(const KDevelop::IndexedString& url, bool showImports) const +QVector ProblemReporterModel::problems(const QSet& docs) const { QVector result; - QSet visitedContexts; - KDevelop::DUChainReadLocker lock; - problemsInternal(KDevelop::DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); - return result; -} + DUChainReadLocker lock; -QVector ProblemReporterModel::problems(const QSet& urls, bool showImports) const -{ - QVector result; - QSet visitedContexts; - KDevelop::DUChainReadLocker lock; - foreach (const KDevelop::IndexedString& url, urls) { - problemsInternal(KDevelop::DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); + for (const IndexedString& doc : docs) { + if (doc.isEmpty()) + continue; + + TopDUContext* ctx = DUChain::self()->chainForDocument(doc); + if (!ctx) + continue; + + for (ProblemPointer p : DUChainUtils::allProblemsForContext(ctx)) { + result.append(p); + } } + return result; } @@ -94,48 +94,21 @@ { Q_ASSERT(thread() == QThread::currentThread()); - QSet documents = store()->documents()->get(); - KDevelop::DUChainReadLocker lock(KDevelop::DUChain::lock()); - foreach (const KDevelop::IndexedString& document, documents) { + QSet documents = store()->documents()->get(); + if (showImports()) + documents += store()->documents()->getImports(); + + DUChainReadLocker lock(DUChain::lock()); + for (const IndexedString& document : documents) { if (document.isEmpty()) continue; - KDevelop::TopDUContext::Features updateType = KDevelop::TopDUContext::ForceUpdate; + TopDUContext::Features updateType = TopDUContext::ForceUpdate; if (documents.size() == 1) - updateType = KDevelop::TopDUContext::ForceUpdateRecursive; - KDevelop::DUChain::self()->updateContextForUrl( + updateType = TopDUContext::ForceUpdateRecursive; + DUChain::self()->updateContextForUrl( document, - (KDevelop::TopDUContext::Features)(updateType | KDevelop::TopDUContext::VisibleDeclarationsAndContexts)); - } -} - -void ProblemReporterModel::problemsInternal(KDevelop::TopDUContext* context, bool showImports, - QSet& visitedContexts, - QVector& result) const -{ - if (!context || visitedContexts.contains(context)) { - return; - } - ReferencedTopDUContext top(context); - foreach (KDevelop::ProblemPointer p, DUChainUtils::allProblemsForContext(top)) { - if (p && p->severity() <= store()->severity()) { - result.append(p); - } - } - visitedContexts.insert(context); - if (showImports) { - bool isProxy = context->parsingEnvironmentFile() && context->parsingEnvironmentFile()->isProxyContext(); - foreach (const KDevelop::DUContext::Import& ctx, context->importedParentContexts()) { - if (!ctx.indexedContext().indexedTopContext().isLoaded()) - continue; - KDevelop::TopDUContext* topCtx = dynamic_cast(ctx.context(0)); - if (topCtx) { - /// If we are starting at a proxy-context, only recurse into other proxy-contexts, because those contain the problems. - if (!isProxy - || (topCtx->parsingEnvironmentFile() && topCtx->parsingEnvironmentFile()->isProxyContext())) - problemsInternal(topCtx, showImports, visitedContexts, result); - } - } + (TopDUContext::Features)(updateType | TopDUContext::VisibleDeclarationsAndContexts)); } } @@ -156,10 +129,9 @@ Q_ASSERT(thread() == QThread::currentThread()); beginResetModel(); - QUrl currentDocument = doc->url(); /// Will trigger signal changed() if problems change - store()->setCurrentDocument(KDevelop::IndexedString(currentDocument)); + store()->setCurrentDocument(IndexedString(doc->url())); endResetModel(); } @@ -167,33 +139,30 @@ { Q_ASSERT(thread() == QThread::currentThread()); - if (store()->documents()->get().contains(url)) { - /// m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period. - m_minTimer->start(); - /// m_maxTimer will expire unconditionally in MaxTimeout - if (!m_maxTimer->isActive()) { - m_maxTimer->start(); - } - } -} + // skip update for urls outside current scope + if (!store()->documents()->get().contains(url) && + !(showImports() && store()->documents()->getImports().contains(url))) + return; -void ProblemReporterModel::setShowImports(bool showImports) -{ - if (m_showImports != showImports) { - Q_ASSERT(thread() == QThread::currentThread()); - m_showImports = showImports; - rebuildProblemList(); + /// m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period. + m_minTimer->start(); + /// m_maxTimer will expire unconditionally in MaxTimeout + if (!m_maxTimer->isActive()) { + m_maxTimer->start(); } } void ProblemReporterModel::rebuildProblemList() { - QVector problems; /// No locking here, because it may be called from an already locked context beginResetModel(); - problems = this->problems(store()->documents()->get(), m_showImports); - store()->setProblems(problems); + QVector allProblems = problems(store()->documents()->get()); + + if (showImports()) + allProblems += problems(store()->documents()->getImports()); + + store()->setProblems(allProblems); endResetModel(); } diff --git a/plugins/problemreporter/problemreporterplugin.cpp b/plugins/problemreporter/problemreporterplugin.cpp --- a/plugins/problemreporter/problemreporterplugin.cpp +++ b/plugins/problemreporter/problemreporterplugin.cpp @@ -143,9 +143,9 @@ void ProblemReporterPlugin::updateHighlight(const KDevelop::IndexedString& url) { - ProblemHighlighter* ph = m_highlighters.value(url); + ProblemHighlighter* ph = m_highlighters.value(url); if (ph) { - auto allProblems = m_model->problems(url, false); + auto allProblems = m_model->problems({url}); ph->setProblems(allProblems); } } diff --git a/plugins/problemreporter/problemsview.cpp b/plugins/problemreporter/problemsview.cpp --- a/plugins/problemreporter/problemsview.cpp +++ b/plugins/problemreporter/problemsview.cpp @@ -216,8 +216,8 @@ m_showAllAction->setVisible(problemModel->features().testFlag(ProblemModel::CanByPassScopeFilter)); + m_showImportsAction->setChecked(false); problemModel->setShowImports(false); - setScope(CurrentDocument); // Show All should be default if it's supported. It helps with error messages that are otherwise invisible if (problemModel->features().testFlag(ProblemModel::CanByPassScopeFilter)) { diff --git a/shell/filteredproblemstore.cpp b/shell/filteredproblemstore.cpp --- a/shell/filteredproblemstore.cpp +++ b/shell/filteredproblemstore.cpp @@ -292,6 +292,10 @@ bool FilteredProblemStorePrivate::match(const IProblem::Ptr &problem) const { + if (q->scope() != ProblemScope::BypassScopeFilter && + !q->documents()->get().contains(problem.data()->finalLocation().document) && + !(q->showImports() && q->documents()->getImports().contains(problem.data()->finalLocation().document))) + return false; if(problem->severity()!=IProblem::NoSeverity) { diff --git a/shell/problemmodel.h b/shell/problemmodel.h --- a/shell/problemmodel.h +++ b/shell/problemmodel.h @@ -132,12 +132,15 @@ /// Retrieve the supported features Features features() const; + /// Retrive 'show imports' filter setting + bool showImports(); + /// Set the supported features void setFeatures(Features features); public slots: - /// Show imports - virtual void setShowImports(bool){} + /// Set 'show imports' filter value + void setShowImports(bool showImports); /// Sets the scope filter. Uses int to be able to use QSignalMapper virtual void setScope(int scope); diff --git a/shell/problemmodel.cpp b/shell/problemmodel.cpp --- a/shell/problemmodel.cpp +++ b/shell/problemmodel.cpp @@ -304,10 +304,25 @@ endResetModel(); } +void ProblemModel::setShowImports(bool showImports) +{ + Q_ASSERT(thread() == QThread::currentThread()); + + d->m_problems->setShowImports(showImports); +} + +bool ProblemModel::showImports() +{ + return d->m_problems->showImports(); +} + void ProblemModel::setScope(int scope) { Q_ASSERT(thread() == QThread::currentThread()); + if (!features().testFlag(ScopeFilter)) + scope = ProblemScope::BypassScopeFilter; + /// Will trigger signals beginRebuild(), endRebuild() if problems change and are rebuilt d->m_problems->setScope(scope); } diff --git a/shell/problemstore.h b/shell/problemstore.h --- a/shell/problemstore.h +++ b/shell/problemstore.h @@ -116,6 +116,12 @@ /// Sets the grouping method virtual void setGrouping(int grouping); + /// Set 'show imports' filter value + void setShowImports(bool showImports); + + /// Retrive 'show imports' filter setting + int showImports(); + /// Sets the currently shown document (in the editor, it's triggered by the IDE) void setCurrentDocument(const IndexedString &doc); diff --git a/shell/problemstore.cpp b/shell/problemstore.cpp --- a/shell/problemstore.cpp +++ b/shell/problemstore.cpp @@ -28,6 +28,7 @@ ProblemStorePrivate() : m_documents(nullptr) , m_severities(KDevelop::IProblem::Error | KDevelop::IProblem::Warning | KDevelop::IProblem::Hint) + , m_showImports(false) , m_rootNode(new KDevelop::ProblemStoreNode()) { } @@ -38,6 +39,9 @@ /// The severity filter setting KDevelop::IProblem::Severities m_severities; + /// The 'show imports' filter setting + bool m_showImports; + /// The problems list KDevelop::ProblemStoreNode *m_rootNode; @@ -153,10 +157,6 @@ { ProblemScope cast_scope = static_cast(scope); - if (cast_scope == BypassScopeFilter) { - return; - } - if (d->m_documents) { if(cast_scope == d->m_documents->getScope()) return; @@ -178,10 +178,12 @@ d->m_documents = new AllProjectSet(this); break; case BypassScopeFilter: - // handled above + d->m_documents = new BypassSet(this); break; } + d->m_documents->setShowImports(d->m_showImports); + rebuild(); connect(d->m_documents, &WatchedDocumentSet::changed, this, &ProblemStore::onDocumentSetChanged); @@ -201,6 +203,20 @@ Q_UNUSED(grouping); } +void ProblemStore::setShowImports(bool showImports) +{ + if (d->m_showImports == showImports) + return; + + d->m_showImports = showImports; + d->m_documents->setShowImports(showImports); +} + +int ProblemStore::showImports() +{ + return d->m_showImports; +} + void ProblemStore::setCurrentDocument(const IndexedString &doc) { d->m_currentDocument = doc; diff --git a/shell/watcheddocumentset.h b/shell/watcheddocumentset.h --- a/shell/watcheddocumentset.h +++ b/shell/watcheddocumentset.h @@ -35,7 +35,7 @@ class IProject; class ProjectFileItem; class Path; - +class WatchedDocumentSetPrivate; /** * Helper class that tracks set of documents and notifies its owner whenever this set changes. Derived classes implement different tracking strategies. @@ -44,18 +44,24 @@ { Q_OBJECT public: - typedef QSet DocumentSet; + using DocumentSet = QSet; + explicit WatchedDocumentSet(QObject* parent); + ~WatchedDocumentSet() override; + + void setShowImports(bool showImports); + virtual DocumentSet get() const; + virtual DocumentSet getImports() const; + virtual void setCurrentDocument(const IndexedString& url); virtual ProblemScope getScope() const = 0; - ~WatchedDocumentSet() override {} signals: void changed(); protected: - DocumentSet m_documents; + QScopedPointer d; }; /** @@ -129,6 +135,14 @@ ProblemScope getScope() const override; }; +class BypassSet : public WatchedDocumentSet +{ + Q_OBJECT +public: + explicit BypassSet(QObject* parent); + ProblemScope getScope() const override; +}; + } #endif // KDEVPLATFORM_PLUGIN_WATCHEDDOCUMENTSET_H diff --git a/shell/watcheddocumentset.cpp b/shell/watcheddocumentset.cpp --- a/shell/watcheddocumentset.cpp +++ b/shell/watcheddocumentset.cpp @@ -23,41 +23,191 @@ #include "watcheddocumentset.h" #include +#include #include #include -#include #include -#include +#include +#include #include +#include namespace KDevelop { +class WatchedDocumentSetPrivate : public QObject +{ +public: + using DocumentSet = WatchedDocumentSet::DocumentSet; + + WatchedDocumentSetPrivate(WatchedDocumentSet* documentSet) + : m_documentSet(documentSet) + { + connect(DUChain::self(), &DUChain::updateReady, this, &WatchedDocumentSetPrivate::updateReady); + } + + void setShowImports(bool showImports) + { + DocumentSet oldImports = m_imports; + + m_showImports = showImports; + updateImports(); + + if (m_imports != oldImports) + emit m_documentSet->changed(); + } + + inline const DocumentSet& documents() const + { + return m_documents; + } + + inline const DocumentSet& imports() const + { + return m_imports; + } + + void setDocuments(const DocumentSet& docs, bool doUpdate, bool doEmit) + { + m_documents = docs; + + if (doUpdate) + updateImports(); + + if (doEmit) + emit m_documentSet->changed(); + } + + void addDocument(const IndexedString& doc, bool doUpdate, bool doEmit) + { + if (m_documents.contains(doc)) + return; + + m_documents.insert(doc); + + if (doUpdate) + updateImports(); + + if (doEmit) + emit m_documentSet->changed(); + } + + void delDocument(const IndexedString& doc, bool doUpdate, bool doEmit) + { + if (!m_documents.contains(doc)) + return; + + m_documents.remove(doc); + + if (doUpdate) + updateImports(); + + if (doEmit) + emit m_documentSet->changed(); + } + + void updateImports() + { + if (!m_showImports) { + if (!m_imports.isEmpty()) { + m_imports.clear(); + return; + } + return; + } + + getImportsFromDUChain(); + } + +private: + void getImportsFromDU(TopDUContext* context, QSet& visitedContexts) + { + if (!context || visitedContexts.contains(context)) + return; + + visitedContexts.insert(context); + for (const DUContext::Import& ctx : context->importedParentContexts()) { + TopDUContext* topCtx = dynamic_cast(ctx.context(nullptr)); + + if (topCtx) + getImportsFromDU(topCtx, visitedContexts); + } + } + + void getImportsFromDUChain() + { + QSet visitedContexts; + + m_imports.clear(); + for (const IndexedString& doc : m_documents) { + TopDUContext* ctx = DUChain::self()->chainForDocument(doc); + getImportsFromDU(ctx, visitedContexts); + visitedContexts.remove(ctx); + } + + for (TopDUContext* ctx : visitedContexts) { + m_imports.insert(ctx->url()); + } + } + + void updateReady(const IndexedString& doc, const ReferencedTopDUContext&) + { + if (!m_showImports || !m_documents.contains(doc)) + return; + + DocumentSet oldImports = m_imports; + + updateImports(); + if (m_imports != oldImports) + emit m_documentSet->changed(); + } + + WatchedDocumentSet* m_documentSet; + + DocumentSet m_documents; + DocumentSet m_imports; + + bool m_showImports; +}; + WatchedDocumentSet::WatchedDocumentSet(QObject* parent) - :QObject(parent) + : QObject(parent) + , d(new WatchedDocumentSetPrivate(this)) { } +WatchedDocumentSet::~WatchedDocumentSet() +{ +} + +void WatchedDocumentSet::setShowImports(bool showImports) +{ + d->setShowImports(showImports); +} + void WatchedDocumentSet::setCurrentDocument(const IndexedString&) { } WatchedDocumentSet::DocumentSet WatchedDocumentSet::get() const { - return m_documents; + return d->documents(); +} + +WatchedDocumentSet::DocumentSet WatchedDocumentSet::getImports() const +{ + return d->imports(); } -CurrentDocumentSet::CurrentDocumentSet(const IndexedString& document, QObject *parent) +CurrentDocumentSet::CurrentDocumentSet(const IndexedString& document, QObject* parent) : WatchedDocumentSet(parent) { - m_documents.insert(document); + d->setDocuments({document}, true, false); } void CurrentDocumentSet::setCurrentDocument(const IndexedString& url) { - m_documents.clear(); - m_documents.insert(url); - emit changed(); + d->setDocuments({url}, true, true); } ProblemScope CurrentDocumentSet::getScope() const @@ -65,28 +215,26 @@ return CurrentDocument; } -OpenDocumentSet::OpenDocumentSet(QObject *parent) +OpenDocumentSet::OpenDocumentSet(QObject* parent) : WatchedDocumentSet(parent) { - QList docs = ICore::self()->documentController()->openDocuments(); - foreach (IDocument* doc, docs) { - m_documents.insert(IndexedString(doc->url())); + for (IDocument* doc : ICore::self()->documentController()->openDocuments()) { + d->addDocument(IndexedString(doc->url()), false, false); } + d->updateImports(); + connect(ICore::self()->documentController(), &IDocumentController::documentClosed, this, &OpenDocumentSet::documentClosed); connect(ICore::self()->documentController(), &IDocumentController::textDocumentCreated, this, &OpenDocumentSet::documentCreated); } void OpenDocumentSet::documentClosed(IDocument* doc) { - if (m_documents.remove(IndexedString(doc->url()))) { - emit changed(); - } + d->delDocument(IndexedString(doc->url()), true, true); } void OpenDocumentSet::documentCreated(IDocument* doc) { - m_documents.insert(IndexedString(doc->url())); - emit changed(); + d->addDocument(IndexedString(doc->url()), true, true); } ProblemScope OpenDocumentSet::getScope() const @@ -94,29 +242,25 @@ return OpenDocuments; } -ProjectSet::ProjectSet(QObject *parent) +ProjectSet::ProjectSet(QObject* parent) : WatchedDocumentSet(parent) { } void ProjectSet::fileAdded(ProjectFileItem* file) { - m_documents.insert(file->indexedPath()); - emit changed(); + d->addDocument(IndexedString(file->indexedPath()), true, true); } void ProjectSet::fileRemoved(ProjectFileItem* file) { - if (m_documents.remove(file->indexedPath())) { - emit changed(); - } + d->delDocument(IndexedString(file->indexedPath()), true, true); } void ProjectSet::fileRenamed(const Path& oldFile, ProjectFileItem* newFile) { - if (m_documents.remove(IndexedString(oldFile.pathOrUrl()))) { - m_documents.insert(newFile->indexedPath()); - } + d->delDocument(IndexedString(oldFile.pathOrUrl()), false, false); + d->addDocument(IndexedString(newFile->indexedPath()), true, true); } void ProjectSet::trackProjectFiles(const IProject* project) @@ -136,8 +280,9 @@ } } -CurrentProjectSet::CurrentProjectSet(const IndexedString& document, QObject *parent) - : ProjectSet(parent), m_currentProject(0) +CurrentProjectSet::CurrentProjectSet(const IndexedString& document, QObject* parent) + : ProjectSet(parent) + , m_currentProject(nullptr) { setCurrentDocumentInternal(document); trackProjectFiles(m_currentProject); @@ -152,13 +297,8 @@ { IProject* projectForUrl = ICore::self()->projectController()->findProjectForUrl(url.toUrl()); if (projectForUrl && projectForUrl != m_currentProject) { - m_documents.clear(); m_currentProject = projectForUrl; - - foreach (const IndexedString &indexedString, m_currentProject->fileSet()) { - m_documents.insert(indexedString); - } - emit changed(); + d->setDocuments(m_currentProject->fileSet(), true, true); } } @@ -167,15 +307,16 @@ return CurrentProject; } -AllProjectSet::AllProjectSet(QObject *parent) +AllProjectSet::AllProjectSet(QObject* parent) : ProjectSet(parent) { foreach(const IProject* project, ICore::self()->projectController()->projects()) { foreach (const IndexedString &indexedString, project->fileSet()) { - m_documents.insert(indexedString); + d->addDocument(indexedString, false, false); } trackProjectFiles(project); } + d->updateImports(); } ProblemScope AllProjectSet::getScope() const @@ -183,5 +324,14 @@ return AllProjects; } +BypassSet::BypassSet(QObject* parent) + : WatchedDocumentSet(parent) +{ } +ProblemScope BypassSet::getScope() const +{ + return BypassScopeFilter; +} + +}