File indexing completed on 2024-04-28 04:37:17

0001 /*
0002     SPDX-FileCopyrightText: 2015 Laszlo Kis-Adam <laszlo.kis-adam@kdemail.net>
0003 
0004     SPDX-License-Identifier: LGPL-2.0-or-later
0005 */
0006 
0007 #include "filteredproblemstore.h"
0008 
0009 #include "problem.h"
0010 #include "watcheddocumentset.h"
0011 #include "problemstorenode.h"
0012 
0013 #include <language/editor/documentrange.h>
0014 
0015 #include <KLocalizedString>
0016 
0017 using namespace KDevelop;
0018 
0019 namespace
0020 {
0021 
0022 /// Adds diagnostics as sub-nodes
0023 void addDiagnostics(ProblemStoreNode *node, const QVector<IProblem::Ptr> &diagnostics)
0024 {
0025     for (const IProblem::Ptr& ptr : diagnostics) {
0026         auto *child = new ProblemNode(node, ptr);
0027         node->addChild(child);
0028 
0029         addDiagnostics(child, ptr->diagnostics());
0030     }
0031 }
0032 
0033 /**
0034  * @brief Base class for grouping strategy classes
0035  *
0036  * These classes build the problem tree based on the respective strategies
0037  */
0038 class GroupingStrategy
0039 {
0040 public:
0041     explicit GroupingStrategy( ProblemStoreNode *root )
0042         : m_rootNode(root)
0043         , m_groupedRootNode(new ProblemStoreNode())
0044     {
0045     }
0046 
0047     virtual ~GroupingStrategy(){
0048     }
0049 
0050     /// Add a problem to the appropriate group
0051     virtual void addProblem(const IProblem::Ptr &problem) = 0;
0052 
0053     /// Find the specified noe
0054     const ProblemStoreNode* findNode(int row, ProblemStoreNode *parent = nullptr) const
0055     {
0056         if (parent == nullptr)
0057             return m_groupedRootNode->child(row);
0058         else
0059             return parent->child(row);
0060     }
0061 
0062     /// Returns the number of children nodes
0063     int count(ProblemStoreNode *parent = nullptr)
0064     {
0065         if (parent == nullptr)
0066             return m_groupedRootNode->count();
0067         else
0068             return parent->count();
0069     }
0070 
0071     /// Clears the problems
0072     virtual void clear()
0073     {
0074         m_groupedRootNode->clear();
0075     }
0076 
0077 protected:
0078     ProblemStoreNode* const m_rootNode;
0079     QScopedPointer<ProblemStoreNode> m_groupedRootNode;
0080 };
0081 
0082 /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0083 
0084 /// Implements no grouping strategy, that is just stores the problems without any grouping
0085 class NoGroupingStrategy final : public GroupingStrategy
0086 {
0087 public:
0088     explicit NoGroupingStrategy(ProblemStoreNode *root)
0089         : GroupingStrategy(root)
0090     {
0091     }
0092 
0093     void addProblem(const IProblem::Ptr &problem) override
0094     {
0095         auto *node = new ProblemNode(m_groupedRootNode.data(), problem);
0096         addDiagnostics(node, problem->diagnostics());
0097         m_groupedRootNode->addChild(node);
0098 
0099     }
0100 
0101 };
0102 
0103 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0104 
0105 /// Implements grouping based on path
0106 class PathGroupingStrategy final : public GroupingStrategy
0107 {
0108 public:
0109     explicit PathGroupingStrategy(ProblemStoreNode *root)
0110         : GroupingStrategy(root)
0111     {
0112     }
0113 
0114     void addProblem(const IProblem::Ptr &problem) override
0115     {
0116         QString path = problem->finalLocation().document.str();
0117 
0118         /// See if we already have this path
0119         const auto childrenNodes = m_groupedRootNode->children();
0120         auto it = std::find_if(childrenNodes.begin(), childrenNodes.end(), [&](ProblemStoreNode* node) {
0121             return (node->label() == path);
0122         });
0123         ProblemStoreNode* parent = (it != childrenNodes.end()) ? *it : nullptr;
0124 
0125         /// If not add it!
0126         if (parent == nullptr) {
0127             parent = new LabelNode(m_groupedRootNode.data(), path);
0128             m_groupedRootNode->addChild(parent);
0129         }
0130 
0131         auto *node = new ProblemNode(parent, problem);
0132         addDiagnostics(node, problem->diagnostics());
0133         parent->addChild(node);
0134     }
0135 
0136 };
0137 
0138 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0139 
0140 /// Implements grouping based on severity
0141 class SeverityGroupingStrategy final : public GroupingStrategy
0142 {
0143 public:
0144     enum SeverityGroups
0145     {
0146         GroupError          = 0,
0147         GroupWarning        = 1,
0148         GroupHint           = 2
0149     };
0150 
0151     explicit SeverityGroupingStrategy(ProblemStoreNode *root)
0152         : GroupingStrategy(root)
0153     {
0154         /// Create the groups on construction, so there's no need to search for them on addition
0155         m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Error")));
0156         m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Warning")));
0157         m_groupedRootNode->addChild(new LabelNode(m_groupedRootNode.data(), i18n("Hint")));
0158     }
0159 
0160     void addProblem(const IProblem::Ptr &problem) override
0161     {
0162         ProblemStoreNode *parent = nullptr;
0163 
0164         switch (problem->severity()) {
0165             case IProblem::Error: parent = m_groupedRootNode->child(GroupError); break;
0166             case IProblem::Warning: parent = m_groupedRootNode->child(GroupWarning); break;
0167             case IProblem::Hint: parent = m_groupedRootNode->child(GroupHint); break;
0168             default: break;
0169         }
0170 
0171         auto *node = new ProblemNode(m_groupedRootNode.data(), problem);
0172         addDiagnostics(node, problem->diagnostics());
0173         parent->addChild(node);
0174     }
0175 
0176     void clear() override
0177     {
0178         m_groupedRootNode->child(GroupError)->clear();
0179         m_groupedRootNode->child(GroupWarning)->clear();
0180         m_groupedRootNode->child(GroupHint)->clear();
0181     }
0182 };
0183 
0184 }
0185 
0186 //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
0187 
0188 namespace KDevelop
0189 {
0190 
0191 class FilteredProblemStorePrivate
0192 {
0193 public:
0194     explicit FilteredProblemStorePrivate(FilteredProblemStore* q)
0195         : q(q)
0196         , m_strategy(new NoGroupingStrategy(q->rootNode()))
0197         , m_grouping(NoGrouping)
0198     {
0199     }
0200 
0201     /// Tells if the problem matches the filters
0202     bool match(const IProblem::Ptr &problem) const;
0203 
0204     FilteredProblemStore* const q;
0205     QScopedPointer<GroupingStrategy> m_strategy;
0206     GroupingMethod m_grouping;
0207 };
0208 
0209 FilteredProblemStore::FilteredProblemStore(QObject *parent)
0210     : ProblemStore(parent)
0211     , d_ptr(new FilteredProblemStorePrivate(this))
0212 {
0213 }
0214 
0215 FilteredProblemStore::~FilteredProblemStore()
0216 {
0217 }
0218 
0219 void FilteredProblemStore::addProblem(const IProblem::Ptr &problem)
0220 {
0221     Q_D(FilteredProblemStore);
0222 
0223     ProblemStore::addProblem(problem);
0224 
0225     if (d->match(problem))
0226         d->m_strategy->addProblem(problem);
0227 }
0228 
0229 const ProblemStoreNode* FilteredProblemStore::findNode(int row, ProblemStoreNode *parent) const
0230 {
0231     Q_D(const FilteredProblemStore);
0232 
0233     return d->m_strategy->findNode(row, parent);
0234 }
0235 
0236 int FilteredProblemStore::count(ProblemStoreNode *parent) const
0237 {
0238     Q_D(const FilteredProblemStore);
0239 
0240     return d->m_strategy->count(parent);
0241 }
0242 
0243 void FilteredProblemStore::clear()
0244 {
0245     Q_D(FilteredProblemStore);
0246 
0247     d->m_strategy->clear();
0248     ProblemStore::clear();
0249 }
0250 
0251 void FilteredProblemStore::rebuild()
0252 {
0253     Q_D(FilteredProblemStore);
0254 
0255     emit beginRebuild();
0256 
0257     d->m_strategy->clear();
0258 
0259     const auto childrenNodes = rootNode()->children();
0260     for (ProblemStoreNode* node : childrenNodes) {
0261         IProblem::Ptr problem = node->problem();
0262         if (d->match(problem)) {
0263             d->m_strategy->addProblem(problem);
0264         }
0265     }
0266 
0267     emit endRebuild();
0268 }
0269 
0270 void FilteredProblemStore::setGrouping(int grouping)
0271 {
0272     Q_D(FilteredProblemStore);
0273 
0274     auto g = GroupingMethod(grouping);
0275     if(g == d->m_grouping)
0276         return;
0277 
0278     d->m_grouping = g;
0279 
0280     switch (g) {
0281         case NoGrouping: d->m_strategy.reset(new NoGroupingStrategy(rootNode())); break;
0282         case PathGrouping: d->m_strategy.reset(new PathGroupingStrategy(rootNode())); break;
0283         case SeverityGrouping: d->m_strategy.reset(new SeverityGroupingStrategy(rootNode())); break;
0284     }
0285 
0286     rebuild();
0287     emit changed();
0288 }
0289 
0290 int FilteredProblemStore::grouping() const
0291 {
0292     Q_D(const FilteredProblemStore);
0293 
0294     return d->m_grouping;
0295 }
0296 
0297 bool FilteredProblemStorePrivate::match(const IProblem::Ptr &problem) const
0298 {
0299     if (q->scope() != ProblemScope::BypassScopeFilter &&
0300         !q->documents()->get().contains(problem.data()->finalLocation().document) &&
0301         !(q->showImports() && q->documents()->imports().contains(problem.data()->finalLocation().document)))
0302         return false;
0303 
0304     if(problem->severity()!=IProblem::NoSeverity)
0305     {
0306         /// If the problem severity isn't in the filter severities it's discarded
0307         if(!q->severities().testFlag(problem->severity()))
0308             return false;
0309     }
0310     else
0311     {
0312         if(!q->severities().testFlag(IProblem::Hint))//workaround for problems without correctly set severity
0313             return false;
0314     }
0315 
0316     return true;
0317 }
0318 
0319 }
0320 
0321 #include "moc_filteredproblemstore.cpp"