diff --git a/plugins/problemreporter/problemreportermodel.h b/plugins/problemreporter/problemreportermodel.h --- a/plugins/problemreporter/problemreportermodel.h +++ b/plugins/problemreporter/problemreportermodel.h @@ -45,21 +45,16 @@ ~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: /** * List of problems for @ref url has been updated */ 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,72 +69,46 @@ { } -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); + foreach (const IndexedString& doc, docs) { + if (doc.isEmpty()) + continue; + + TopDUContext* ctx = DUChain::self()->chainForDocument(doc); + if (!ctx) + continue; + + foreach (ProblemPointer p, DUChainUtils::allProblemsForContext(ctx)) { + result.append(p); + } } + return result; } void ProblemReporterModel::forceFullUpdate() { 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()); + foreach (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(nullptr)); - 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,44 +129,40 @@ 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(); } void ProblemReporterModel::problemsUpdated(const KDevelop::IndexedString& url) { 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 @@ -166,7 +166,7 @@ QVector documentProblems; foreach (const ModelData& modelData, pms->models()) { - documentProblems += modelData.model->problems(url); + documentProblems += modelData.model->problems({url}); } ph->setProblems(documentProblems); diff --git a/plugins/problemreporter/problemsview.cpp b/plugins/problemreporter/problemsview.cpp --- a/plugins/problemreporter/problemsview.cpp +++ b/plugins/problemreporter/problemsview.cpp @@ -249,8 +249,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 @@ -135,6 +135,9 @@ /// Retrieve the supported features Features features() const; + /// Retrieve 'show imports' filter setting + bool showImports(); + /// Set the supported features void setFeatures(Features features); @@ -147,15 +150,15 @@ public slots: /// Show imports - virtual void setShowImports(bool){} + void setShowImports(bool showImports); /// Sets the scope filter. Uses int to be able to use QSignalMapper - virtual void setScope(int scope); + void setScope(int scope); /// Sets the severity filter. Uses int to be able to use QSignalMapper - virtual void setSeverity(int severity);///old-style severity filtering + void setSeverity(int severity);///old-style severity filtering - virtual void setSeverities(KDevelop::IProblem::Severities severities);///new-style severity filtering + void setSeverities(KDevelop::IProblem::Severities severities);///new-style severity filtering void setGrouping(int grouping); diff --git a/shell/problemmodel.cpp b/shell/problemmodel.cpp --- a/shell/problemmodel.cpp +++ b/shell/problemmodel.cpp @@ -310,10 +310,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 @@ -119,6 +119,12 @@ /// Sets the grouping method virtual void setGrouping(int grouping); + /// Set 'show imports' filter value + void setShowImports(bool showImports); + + /// Retrieve 'show imports' filter setting + int showImports() const; + /// 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 @@ -186,15 +186,13 @@ void ProblemStore::setScope(int scope) { ProblemScope cast_scope = static_cast(scope); - - if (cast_scope == BypassScopeFilter) { - return; - } + bool showImports = false; if (d->m_documents) { if(cast_scope == d->m_documents->getScope()) return; + showImports = d->m_documents->showImports(); delete d->m_documents; } @@ -212,10 +210,12 @@ d->m_documents = new AllProjectSet(this); break; case BypassScopeFilter: - // handled above + d->m_documents = new BypassSet(this); break; } + d->m_documents->setShowImports(showImports); + rebuild(); connect(d->m_documents, &WatchedDocumentSet::changed, this, &ProblemStore::onDocumentSetChanged); @@ -235,6 +235,16 @@ Q_UNUSED(grouping); } +void ProblemStore::setShowImports(bool showImports) +{ + d->m_documents->setShowImports(showImports); +} + +int ProblemStore::showImports() const +{ + return d->m_documents->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,27 +35,34 @@ 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. */ class KDEVPLATFORMSHELL_EXPORT WatchedDocumentSet : public QObject { Q_OBJECT public: - typedef QSet DocumentSet; + using DocumentSet = QSet; + explicit WatchedDocumentSet(QObject* parent); + ~WatchedDocumentSet() override; + + bool showImports() const; + 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 +136,15 @@ 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,100 +23,261 @@ #include "watcheddocumentset.h" #include +#include #include #include -#include #include -#include +#include +#include #include +#include namespace KDevelop { +enum ActionFlag { + DoUpdate = 1, + DoEmit = 2 +}; +Q_DECLARE_FLAGS(ActionFlags, ActionFlag); +Q_DECLARE_OPERATORS_FOR_FLAGS(ActionFlags); + +class WatchedDocumentSetPrivate : public QObject +{ + Q_OBJECT + +public: + using DocumentSet = WatchedDocumentSet::DocumentSet; + + WatchedDocumentSetPrivate(WatchedDocumentSet* documentSet) + : m_documentSet(documentSet) + , m_showImports(false) + { + connect(DUChain::self(), &DUChain::updateReady, this, &WatchedDocumentSetPrivate::updateReady); + } + + inline bool showImports() const + { + return m_showImports; + } + + void setShowImports(bool showImports) + { + if (m_showImports == showImports) + return; + + 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; + } + + inline void doUpdate(ActionFlags flags) + { + if (flags.testFlag(DoUpdate)) + updateImports(); + + if (flags.testFlag(DoEmit)) + emit m_documentSet->changed(); + } + + void setDocuments(const DocumentSet& docs, ActionFlags flags = nullptr) + { + m_documents = docs; + doUpdate(flags); + } + + void addDocument(const IndexedString& doc, ActionFlags flags = nullptr) + { + if (m_documents.contains(doc)) + return; + + m_documents.insert(doc); + doUpdate(flags); + } + + void delDocument(const IndexedString& doc, ActionFlags flags = nullptr) + { + if (!m_documents.contains(doc)) + return; + + m_documents.remove(doc); + doUpdate(flags); + } + + 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); + foreach (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(); + foreach (const IndexedString& doc, m_documents) { + TopDUContext* ctx = DUChain::self()->chainForDocument(doc); + getImportsFromDU(ctx, visitedContexts); + visitedContexts.remove(ctx); + } + + foreach (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() +{ +} + +bool WatchedDocumentSet::showImports() const +{ + return d->showImports(); +} + +void WatchedDocumentSet::setShowImports(bool showImports) { + d->setShowImports(showImports); } void WatchedDocumentSet::setCurrentDocument(const IndexedString&) { } WatchedDocumentSet::DocumentSet WatchedDocumentSet::get() const { - return m_documents; + return d->documents(); } -CurrentDocumentSet::CurrentDocumentSet(const IndexedString& document, QObject *parent) +WatchedDocumentSet::DocumentSet WatchedDocumentSet::getImports() const +{ + return d->imports(); +} + +CurrentDocumentSet::CurrentDocumentSet(const IndexedString& document, QObject* parent) : WatchedDocumentSet(parent) { - m_documents.insert(document); + d->setDocuments({document}, DoUpdate); } void CurrentDocumentSet::setCurrentDocument(const IndexedString& url) { - m_documents.clear(); - m_documents.insert(url); - emit changed(); + d->setDocuments({url}, DoUpdate | DoEmit); } ProblemScope CurrentDocumentSet::getScope() const { 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())); + foreach (IDocument* doc, ICore::self()->documentController()->openDocuments()) { + d->addDocument(IndexedString(doc->url())); } + 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()), DoUpdate | DoEmit); } void OpenDocumentSet::documentCreated(IDocument* doc) { - m_documents.insert(IndexedString(doc->url())); - emit changed(); + d->addDocument(IndexedString(doc->url()), DoUpdate | DoEmit); } ProblemScope OpenDocumentSet::getScope() const { 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()), DoUpdate | DoEmit); } void ProjectSet::fileRemoved(ProjectFileItem* file) { - if (m_documents.remove(file->indexedPath())) { - emit changed(); - } + d->delDocument(IndexedString(file->indexedPath()), DoUpdate | DoEmit); } 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())); + d->addDocument(IndexedString(newFile->indexedPath()), DoUpdate | DoEmit); } void ProjectSet::trackProjectFiles(const IProject* project) @@ -136,11 +297,11 @@ } } -CurrentProjectSet::CurrentProjectSet(const IndexedString& document, QObject *parent) - : ProjectSet(parent), m_currentProject(nullptr) +CurrentProjectSet::CurrentProjectSet(const IndexedString& document, QObject* parent) + : ProjectSet(parent) + , m_currentProject(nullptr) { setCurrentDocumentInternal(document); - trackProjectFiles(m_currentProject); } void CurrentProjectSet::setCurrentDocument(const IndexedString& url) @@ -152,36 +313,47 @@ { 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()); + d->addDocument(IndexedString(m_currentProject->path().toLocalFile()), DoUpdate | DoEmit); + trackProjectFiles(m_currentProject); } } ProblemScope CurrentProjectSet::getScope() const { 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); } + d->addDocument(IndexedString(project->path().toLocalFile())); trackProjectFiles(project); } + d->updateImports(); + emit changed(); } ProblemScope AllProjectSet::getScope() const { return AllProjects; } +BypassSet::BypassSet(QObject* parent) + : WatchedDocumentSet(parent) +{ +} + +ProblemScope BypassSet::getScope() const +{ + return BypassScopeFilter; +} + } +#include "watcheddocumentset.moc"