diff --git a/plugins/problemreporter/problemmodel.cpp b/plugins/problemreporter/problemmodel.cpp index f9e857f85c..dd5f63167f 100644 --- a/plugins/problemreporter/problemmodel.cpp +++ b/plugins/problemreporter/problemmodel.cpp @@ -1,397 +1,397 @@ /* * KDevelop Problem Reporter * * Copyright 2007 Hamish Rodda * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "problemmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include "problemreporterplugin.h" #include "watcheddocumentset.h" using namespace KDevelop; ProblemModel::ProblemModel(ProblemReporterPlugin * parent) : QAbstractItemModel(parent), m_plugin(parent), m_lock(QReadWriteLock::Recursive), m_showImports(false), m_severity(ProblemData::Hint), m_documentSet(0) { m_minTimer = new QTimer(this); m_minTimer->setInterval(MinTimeout); m_minTimer->setSingleShot(true); connect(m_minTimer, SIGNAL(timeout()), SLOT(timerExpired())); m_maxTimer = new QTimer(this); m_maxTimer->setInterval(MaxTimeout); m_maxTimer->setSingleShot(true); connect(m_maxTimer, SIGNAL(timeout()), SLOT(timerExpired())); setScope(CurrentDocument); connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), SLOT(setCurrentDocument(KDevelop::IDocument*))); // CompletionSettings include a list of todo markers we care for, so need to update connect(ICore::self()->languageController()->completionSettings(), SIGNAL(settingsChanged(ICompletionSettings*)), SLOT(forceFullUpdate())); if (ICore::self()->documentController()->activeDocument()) { setCurrentDocument(ICore::self()->documentController()->activeDocument()); } } const int ProblemModel::MinTimeout = 1000; const int ProblemModel::MaxTimeout = 5000; ProblemModel::~ ProblemModel() { m_problems.clear(); } int ProblemModel::rowCount(const QModelIndex & parent) const { if (!parent.isValid()) return m_problems.count(); if (parent.internalId() && parent.column() == 0) return m_problems.at(parent.row())->locationStack().count(); return 0; } QString getDisplayUrl(const QString &url, const KUrl &base) { KUrl location(url); QString displayedUrl; if ( location.protocol() == base.protocol() && location.user() == base.user() && location.host() == base.host() ) { bool isParent; displayedUrl = KUrl::relativePath(base.path(), location.path(), &isParent ); if ( !isParent ) { displayedUrl = location.pathOrUrl(); } } else { displayedUrl = location.pathOrUrl(); } return displayedUrl; } QVariant ProblemModel::data(const QModelIndex & index, int role) const { if (!index.isValid()) return QVariant(); // Locking the duchain here leads to a deadlock, because kate triggers some paint to the outside while holding the smart-lock // DUChainReadLocker lock(DUChain::lock()); ProblemPointer p = problemForIndex(index); KUrl baseDirectory = m_currentDocument.upUrl(); if (!index.internalId()) { // Top level switch (role) { case Qt::DisplayRole: switch (index.column()) { case Source: return p->sourceString(); break; case Error: return p->description(); case File: { return getDisplayUrl(p->finalLocation().document.str(), baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; case Qt::ToolTipRole: return p->explanation(); default: break; } } else { switch (role) { case Qt::DisplayRole: switch (index.column()) { case Error: return i18n("In file included from:"); case File: { return getDisplayUrl(p->locationStack().at(index.row()).document.str(), baseDirectory); } case Line: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.line + 1); break; case Column: if (p->finalLocation().isValid()) return QString::number(p->finalLocation().start.column + 1); break; } break; default: break; } } return QVariant(); } QModelIndex ProblemModel::parent(const QModelIndex & index) const { if (index.internalId()) return createIndex(m_problems.indexOf(problemForIndex(index)), 0, 0); return QModelIndex(); } QModelIndex ProblemModel::index(int row, int column, const QModelIndex & parent) const { DUChainReadLocker lock(DUChain::lock()); if (row < 0 || column < 0 || column >= LastColumn) return QModelIndex(); if (parent.isValid()) { if (parent.internalId()) return QModelIndex(); if (parent.column() != 0) return QModelIndex(); ProblemPointer problem = problemForIndex(parent); if (row >= problem->locationStack().count()) return QModelIndex(); ///@todo Make location-stack work again return createIndex(row, column, row); } if (row < m_problems.count()) return createIndex(row, column, 0); return QModelIndex(); } int ProblemModel::columnCount(const QModelIndex & parent) const { Q_UNUSED(parent) return LastColumn; } KDevelop::ProblemPointer ProblemModel::problemForIndex(const QModelIndex & index) const { if (index.internalId()) return m_problems.at(index.internalId()); else return m_problems.at(index.row()); } ProblemReporterPlugin* ProblemModel::plugin() { return m_plugin; } QVariant ProblemModel::headerData(int section, Qt::Orientation orientation, int role) const { Q_UNUSED(orientation); if (role != Qt::DisplayRole) return QVariant(); switch (section) { case Source: return i18nc("@title:column source of problem", "Source"); case Error: - return i18n("Problem"); + return i18nc("@title:column problem description", "Problem"); case File: - return i18n("File"); + return i18nc("@title:column file where problem was found", "File"); case Line: - return i18n("Line"); + return i18nc("@title:column line number with problem", "Line"); case Column: - return i18n("Column"); + return i18nc("@title:column column number with problem", "Column"); } return QVariant(); } void ProblemModel::problemsUpdated(const KDevelop::IndexedString& url) { QReadLocker locker(&m_lock); if (m_documentSet->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(); } } } void ProblemModel::timerExpired() { m_minTimer->stop(); m_maxTimer->stop(); rebuildProblemList(); } QList ProblemModel::getProblems(IndexedString url, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); return result; } QList< ProblemPointer > ProblemModel::getProblems(QSet< IndexedString > urls, bool showImports) { QList result; QSet visitedContexts; DUChainReadLocker lock; foreach(const IndexedString& url, urls) { getProblemsInternal(DUChain::self()->chainForDocument(url), showImports, visitedContexts, result); } return result; } void ProblemModel::getProblemsInternal(TopDUContext* context, bool showImports, QSet& visitedContexts, QList& result) { if (!context || visitedContexts.contains(context)) { return; } foreach(ProblemPointer p, context->problems()) { if (p->severity() <= m_severity) { result.append(p); } } visitedContexts.insert(context); if (showImports) { bool isProxy = context->parsingEnvironmentFile() && context->parsingEnvironmentFile()->isProxyContext(); foreach(const DUContext::Import &ctx, context->importedParentContexts()) { if(!ctx.indexedContext().indexedTopContext().isLoaded()) continue; 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())) getProblemsInternal(topCtx, showImports, visitedContexts, result); } } } } void ProblemModel::rebuildProblemList() { // No locking here, because it may be called from an already locked context m_problems = getProblems(m_documentSet->get(), m_showImports); reset(); } void ProblemModel::setCurrentDocument(KDevelop::IDocument* document) { QWriteLocker locker(&m_lock); m_currentDocument = document->url(); m_documentSet->setCurrentDocument(IndexedString(m_currentDocument)); reset(); } void ProblemModel::setShowImports(bool showImports) { if (m_showImports != showImports) { QWriteLocker locker(&m_lock); m_showImports = showImports; rebuildProblemList(); } } void ProblemModel::setScope(int scope) { Scope cast_scope = static_cast(scope); QWriteLocker locker(&m_lock); if (!m_documentSet || m_documentSet->getScope() != cast_scope) { if (m_documentSet) { delete m_documentSet; } switch (cast_scope) { case CurrentDocument: m_documentSet = new CurrentDocumentSet(IndexedString(m_currentDocument), this); break; case OpenDocuments: m_documentSet = new OpenDocumentSet(this); break; case CurrentProject: m_documentSet = new CurrentProjectSet(IndexedString(m_currentDocument), this); break; case AllProjects: m_documentSet = new AllProjectSet(this); break; } connect(m_documentSet, SIGNAL(changed()), this, SLOT(documentSetChanged())); rebuildProblemList(); } } void ProblemModel::setSeverity(int severity) { ProblemData::Severity cast_severity = static_cast(severity); if (m_severity != cast_severity) { QWriteLocker locker(&m_lock); m_severity = cast_severity; rebuildProblemList(); } } void ProblemModel::documentSetChanged() { rebuildProblemList(); } void ProblemModel::forceFullUpdate() { m_lock.lockForRead(); QSet documents = m_documentSet->get(); m_lock.unlock(); DUChainReadLocker lock(DUChain::lock()); foreach(const IndexedString& document, documents) { if (document.isEmpty()) continue; TopDUContext::Features updateType = TopDUContext::ForceUpdate; if(documents.size() == 1) updateType = TopDUContext::ForceUpdateRecursive; DUChain::self()->updateContextForUrl(document, (TopDUContext::Features)(updateType | TopDUContext::VisibleDeclarationsAndContexts)); } }