File indexing completed on 2024-05-05 04:40:14

0001 /*
0002     SPDX-FileCopyrightText: 2007 Hamish Rodda <rodda@kde.org>
0003     SPDX-FileCopyrightText: 2015 Laszlo Kis-Adam <laszlo.kis-adam@kdemail.net>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "problemreportermodel.h"
0009 
0010 #include <language/backgroundparser/backgroundparser.h>
0011 #include <language/duchain/duchain.h>
0012 #include <language/duchain/duchainlock.h>
0013 #include <language/duchain/parsingenvironment.h>
0014 #include <language/duchain/topducontext.h>
0015 #include <language/duchain/problem.h>
0016 #include <language/duchain/duchainutils.h>
0017 #include <language/assistant/staticassistantsmanager.h>
0018 
0019 #include <QThread>
0020 #include <QTimer>
0021 
0022 #include <serialization/indexedstring.h>
0023 
0024 #include <shell/watcheddocumentset.h>
0025 #include <shell/filteredproblemstore.h>
0026 
0027 #include <interfaces/icore.h>
0028 #include <interfaces/ilanguagecontroller.h>
0029 #include <interfaces/idocument.h>
0030 
0031 using namespace KDevelop;
0032 
0033 const int ProblemReporterModel::MinTimeout = 1000;
0034 const int ProblemReporterModel::MaxTimeout = 5000;
0035 
0036 ProblemReporterModel::ProblemReporterModel(QObject* parent)
0037     : ProblemModel(parent, new FilteredProblemStore())
0038 {
0039     setFeatures(CanDoFullUpdate | CanShowImports | ScopeFilter | SeverityFilter | ShowSource);
0040 
0041     m_minTimer = new QTimer(this);
0042     m_minTimer->setInterval(MinTimeout);
0043     m_minTimer->setSingleShot(true);
0044     connect(m_minTimer, &QTimer::timeout, this, &ProblemReporterModel::timerExpired);
0045     m_maxTimer = new QTimer(this);
0046     m_maxTimer->setInterval(MaxTimeout);
0047     m_maxTimer->setSingleShot(true);
0048     connect(m_maxTimer, &QTimer::timeout, this, &ProblemReporterModel::timerExpired);
0049     connect(store(), &FilteredProblemStore::changed, this, &ProblemReporterModel::onProblemsChanged);
0050     connect(ICore::self()->languageController()->staticAssistantsManager(), &StaticAssistantsManager::problemsChanged,
0051             this, &ProblemReporterModel::onProblemsChanged);
0052 }
0053 
0054 ProblemReporterModel::~ProblemReporterModel()
0055 {
0056 }
0057 
0058 QVector<KDevelop::IProblem::Ptr> ProblemReporterModel::problems(const QSet<KDevelop::IndexedString>& docs) const
0059 {
0060     QVector<IProblem::Ptr> result;
0061     DUChainReadLocker lock;
0062 
0063     for (const IndexedString& doc : docs) {
0064         if (doc.isEmpty())
0065             continue;
0066 
0067         TopDUContext* ctx = DUChain::self()->chainForDocument(doc);
0068         if (!ctx)
0069             continue;
0070 
0071         const auto allProblems = DUChainUtils::allProblemsForContext(ctx);
0072         result.reserve(result.size() + allProblems.size());
0073         for (const ProblemPointer& p : allProblems) {
0074             result.append(p);
0075         }
0076     }
0077 
0078     return result;
0079 }
0080 
0081 void ProblemReporterModel::forceFullUpdate()
0082 {
0083     Q_ASSERT(thread() == QThread::currentThread());
0084 
0085     QSet<IndexedString> documents = store()->documents()->get();
0086     if (showImports())
0087         documents += store()->documents()->imports();
0088 
0089     DUChainReadLocker lock(DUChain::lock());
0090     for (const IndexedString& document : qAsConst(documents)) {
0091         if (document.isEmpty())
0092             continue;
0093 
0094         TopDUContext::Features updateType = TopDUContext::ForceUpdate;
0095         if (documents.size() == 1)
0096             updateType = TopDUContext::ForceUpdateRecursive;
0097         DUChain::self()->updateContextForUrl(
0098             document, updateType | TopDUContext::VisibleDeclarationsAndContexts);
0099     }
0100 }
0101 
0102 void ProblemReporterModel::onProblemsChanged()
0103 {
0104     rebuildProblemList();
0105 }
0106 
0107 void ProblemReporterModel::timerExpired()
0108 {
0109     m_minTimer->stop();
0110     m_maxTimer->stop();
0111     rebuildProblemList();
0112 }
0113 
0114 void ProblemReporterModel::setCurrentDocument(KDevelop::IDocument* doc)
0115 {
0116     Q_ASSERT(thread() == QThread::currentThread());
0117 
0118     beginResetModel();
0119 
0120     /// Will trigger signal changed() if problems change
0121     store()->setCurrentDocument(IndexedString(doc->url()));
0122     endResetModel();
0123 }
0124 
0125 void ProblemReporterModel::problemsUpdated(const KDevelop::IndexedString& url)
0126 {
0127     Q_ASSERT(thread() == QThread::currentThread());
0128 
0129     // skip update for urls outside current scope
0130     if (!store()->documents()->get().contains(url) &&
0131         !(showImports() && store()->documents()->imports().contains(url)))
0132         return;
0133 
0134     /// m_minTimer will expire in MinTimeout unless some other parsing job finishes in this period.
0135     m_minTimer->start();
0136     /// m_maxTimer will expire unconditionally in MaxTimeout
0137     if (!m_maxTimer->isActive()) {
0138         m_maxTimer->start();
0139     }
0140 }
0141 
0142 void ProblemReporterModel::rebuildProblemList()
0143 {
0144     /// No locking here, because it may be called from an already locked context
0145     beginResetModel();
0146 
0147     QVector<IProblem::Ptr> allProblems = problems(store()->documents()->get());
0148 
0149     if (showImports())
0150         allProblems += problems(store()->documents()->imports());
0151 
0152     store()->setProblems(allProblems);
0153 
0154     endResetModel();
0155 }
0156 
0157 #include "moc_problemreportermodel.cpp"