Warning, file /kdevelop/kdevelop/kdevplatform/language/classmodel/classmodelnode.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).

0001 /*
0002     SPDX-FileCopyrightText: 2007-2009 Hamish Rodda <rodda@kde.org>
0003     SPDX-FileCopyrightText: 2009 Lior Mualem <lior.m.kde@gmail.com>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "classmodelnode.h"
0009 
0010 #include <typeinfo>
0011 #include <KLocalizedString>
0012 
0013 #include "../duchain/duchainlock.h"
0014 #include "../duchain/duchain.h"
0015 #include "../duchain/persistentsymboltable.h"
0016 #include "../duchain/duchainutils.h"
0017 #include "../duchain/classdeclaration.h"
0018 #include "../duchain/classfunctiondeclaration.h"
0019 #include "../duchain/types/functiontype.h"
0020 #include "../duchain/types/enumerationtype.h"
0021 
0022 #include <debug.h>
0023 
0024 using namespace KDevelop;
0025 using namespace ClassModelNodes;
0026 
0027 IdentifierNode::IdentifierNode(KDevelop::Declaration* a_decl,
0028                                NodesModelInterface* a_model,
0029                                const QString& a_displayName)
0030     : DynamicNode(a_displayName.isEmpty() ? a_decl->identifier().toString() : a_displayName, a_model)
0031     , m_identifier(a_decl->qualifiedIdentifier())
0032     , m_indexedDeclaration(a_decl)
0033     , m_cachedDeclaration(a_decl)
0034 {
0035 }
0036 
0037 Declaration* IdentifierNode::declaration()
0038 {
0039     if (!m_cachedDeclaration)
0040         m_cachedDeclaration = m_indexedDeclaration.declaration();
0041 
0042     return m_cachedDeclaration.data();
0043 }
0044 
0045 bool IdentifierNode::getIcon(QIcon& a_resultIcon)
0046 {
0047     DUChainReadLocker readLock(DUChain::lock());
0048 
0049     Declaration* decl = declaration();
0050     if (decl)
0051         a_resultIcon = DUChainUtils::iconForDeclaration(decl);
0052 
0053     return !a_resultIcon.isNull();
0054 }
0055 
0056 //////////////////////////////////////////////////////////////////////////////
0057 //////////////////////////////////////////////////////////////////////////////
0058 
0059 EnumNode::EnumNode(KDevelop::Declaration* a_decl, NodesModelInterface* a_model)
0060     : IdentifierNode(a_decl, a_model)
0061 {
0062     // Set display name for anonymous enums
0063     if (m_displayName.isEmpty())
0064         m_displayName = QStringLiteral("*Anonymous*");
0065 }
0066 
0067 bool EnumNode::getIcon(QIcon& a_resultIcon)
0068 {
0069     DUChainReadLocker readLock(DUChain::lock());
0070 
0071     auto* decl = dynamic_cast<ClassMemberDeclaration*>(declaration());
0072     if (decl == nullptr) {
0073         a_resultIcon = QIcon::fromTheme(QStringLiteral("enum"));
0074     } else
0075     {
0076         if (decl->accessPolicy() == Declaration::Protected) {
0077             a_resultIcon = QIcon::fromTheme(QStringLiteral("protected_enum"));
0078         } else if (decl->accessPolicy() == Declaration::Private) {
0079             a_resultIcon = QIcon::fromTheme(QStringLiteral("private_enum"));
0080         } else
0081         {
0082             a_resultIcon = QIcon::fromTheme(QStringLiteral("enum"));
0083         }
0084     }
0085 
0086     return true;
0087 }
0088 
0089 void EnumNode::populateNode()
0090 {
0091     DUChainReadLocker readLock(DUChain::lock());
0092 
0093     Declaration* decl = declaration();
0094 
0095     if (decl->internalContext()) {
0096         const auto localDeclarations = decl->internalContext()->localDeclarations();
0097         for (Declaration* enumDecl : localDeclarations) {
0098             addNode(new EnumNode(enumDecl, m_model));
0099         }
0100     }
0101 }
0102 
0103 //////////////////////////////////////////////////////////////////////////////
0104 //////////////////////////////////////////////////////////////////////////////
0105 
0106 ClassNode::ClassNode(Declaration* a_decl, NodesModelInterface* a_model)
0107     : IdentifierNode(a_decl, a_model)
0108 {
0109 }
0110 
0111 ClassNode::~ClassNode()
0112 {
0113     if (!m_cachedUrl.isEmpty()) {
0114         ClassModelNodesController::self().unregisterForChanges(m_cachedUrl, this);
0115         m_cachedUrl = IndexedString();
0116     }
0117 }
0118 
0119 void ClassNode::populateNode()
0120 {
0121     DUChainReadLocker readLock(DUChain::lock());
0122 
0123     if (m_model->features().testFlag(NodesModelInterface::ClassInternals)) {
0124         if (updateClassDeclarations()) {
0125             m_cachedUrl = declaration()->url();
0126             ClassModelNodesController::self().registerForChanges(m_cachedUrl, this);
0127         }
0128     }
0129 
0130     // Add special folders
0131     if (m_model->features().testFlag(NodesModelInterface::BaseAndDerivedClasses))
0132         addBaseAndDerived();
0133 }
0134 
0135 template <> inline bool qMapLessThanKey(const IndexedIdentifier& key1, const IndexedIdentifier& key2)
0136 {
0137     return key1.index() < key2.index();
0138 }
0139 
0140 bool ClassNode::updateClassDeclarations()
0141 {
0142     bool hadChanges = false;
0143     SubIdentifiersMap existingIdentifiers = m_subIdentifiers;
0144 
0145     auto* klass = dynamic_cast<ClassDeclaration*>(declaration());
0146 
0147     if (klass) {
0148         const auto localDeclarations = klass->internalContext()->localDeclarations();
0149         for (Declaration* decl : localDeclarations) {
0150             // Ignore forward declarations.
0151             if (decl->isForwardDeclaration())
0152                 continue;
0153 
0154             // Don't add existing declarations.
0155             const auto identifierIt = existingIdentifiers.find(decl->ownIndex());
0156             if (identifierIt != existingIdentifiers.end()) {
0157                 existingIdentifiers.erase(identifierIt);
0158                 continue;
0159             }
0160 
0161             Node* newNode = nullptr;
0162 
0163             if (EnumerationType::Ptr enumType = decl->type<EnumerationType>())
0164                 newNode = new EnumNode(decl, m_model);
0165             else if (decl->isFunctionDeclaration())
0166                 newNode = new FunctionNode(decl, m_model);
0167             else if (auto* classDecl = dynamic_cast<ClassDeclaration*>(decl))
0168                 newNode = new ClassNode(classDecl, m_model);
0169             else if (auto* memDecl = dynamic_cast<ClassMemberDeclaration*>(decl))
0170                 newNode = new ClassMemberNode(memDecl, m_model);
0171             else
0172             {
0173                 // Debug - for reference.
0174                 qCDebug(LANGUAGE) << "class: " << klass->toString() << "name: " << decl->toString() <<
0175                     " - unknown declaration type: " << typeid(*decl).name();
0176             }
0177 
0178             if (newNode) {
0179                 addNode(newNode);
0180 
0181                 // Also remember the identifier.
0182                 m_subIdentifiers.insert(decl->ownIndex(), newNode);
0183 
0184                 hadChanges = true;
0185             }
0186         }
0187     }
0188 
0189     // Remove old existing identifiers
0190     for (SubIdentifiersMap::iterator iter = existingIdentifiers.begin();
0191          iter != existingIdentifiers.end();
0192          ++iter) {
0193         iter.value()->removeSelf();
0194         m_subIdentifiers.remove(iter.key());
0195         hadChanges = true;
0196     }
0197 
0198     return hadChanges;
0199 }
0200 
0201 bool ClassNode::addBaseAndDerived()
0202 {
0203     bool added = false;
0204 
0205     auto* baseClassesNode = new BaseClassesFolderNode(m_model);
0206     addNode(baseClassesNode);
0207     if (!baseClassesNode->hasChildren())
0208         removeNode(baseClassesNode);
0209     else
0210         added = true;
0211 
0212     auto* derivedClassesNode = new DerivedClassesFolderNode(m_model);
0213     addNode(derivedClassesNode);
0214     if (!derivedClassesNode->hasChildren())
0215         removeNode(derivedClassesNode);
0216     else
0217         added = true;
0218 
0219     return added;
0220 }
0221 
0222 void ClassNode::nodeCleared()
0223 {
0224     if (!m_cachedUrl.isEmpty()) {
0225         ClassModelNodesController::self().unregisterForChanges(m_cachedUrl, this);
0226         m_cachedUrl = IndexedString();
0227     }
0228 
0229     m_subIdentifiers.clear();
0230 }
0231 
0232 void ClassModelNodes::ClassNode::documentChanged(const KDevelop::IndexedString&)
0233 {
0234     DUChainReadLocker readLock(DUChain::lock());
0235 
0236     if (updateClassDeclarations())
0237         recursiveSort();
0238 }
0239 
0240 ClassNode* ClassNode::findSubClass(const KDevelop::IndexedQualifiedIdentifier& a_id)
0241 {
0242     // Make sure we have sub nodes.
0243     performPopulateNode();
0244 
0245     /// @todo This is slow - we go over all the sub identifiers but the assumption is that
0246     ///       this function call is rare and the list is not that long.
0247     for (Node* item : qAsConst(m_subIdentifiers)) {
0248         auto* classNode = dynamic_cast<ClassNode*>(item);
0249         if (classNode == nullptr)
0250             continue;
0251 
0252         if (classNode->identifier() == a_id)
0253             return classNode;
0254     }
0255 
0256     return nullptr;
0257 }
0258 
0259 //////////////////////////////////////////////////////////////////////////////
0260 //////////////////////////////////////////////////////////////////////////////
0261 
0262 FunctionNode::FunctionNode(Declaration* a_decl, NodesModelInterface* a_model)
0263     : IdentifierNode(a_decl, a_model)
0264 {
0265     // Append the argument signature to the identifier's name (which is what the displayName is.
0266     if (FunctionType::Ptr type = a_decl->type<FunctionType>())
0267         m_displayName += type->partToString(FunctionType::SignatureArguments);
0268 
0269     // Add special values for ctor / dtor to sort first
0270     auto* classmember = dynamic_cast<ClassFunctionDeclaration*>(a_decl);
0271     if (classmember) {
0272         if (classmember->isConstructor() || classmember->isDestructor())
0273             m_sortableString = QLatin1Char('0') + m_displayName;
0274         else
0275             m_sortableString = QLatin1Char('1') + m_displayName;
0276     } else {
0277         m_sortableString = m_displayName;
0278     }
0279 }
0280 
0281 //////////////////////////////////////////////////////////////////////////////
0282 //////////////////////////////////////////////////////////////////////////////
0283 
0284 ClassMemberNode::ClassMemberNode(KDevelop::ClassMemberDeclaration* a_decl, NodesModelInterface* a_model)
0285     : IdentifierNode(a_decl, a_model)
0286 {
0287 }
0288 
0289 bool ClassMemberNode::getIcon(QIcon& a_resultIcon)
0290 {
0291     DUChainReadLocker readLock(DUChain::lock());
0292 
0293     auto* decl = dynamic_cast<ClassMemberDeclaration*>(declaration());
0294     if (decl == nullptr)
0295         return false;
0296 
0297     if (decl->isTypeAlias()) {
0298         a_resultIcon = QIcon::fromTheme(QStringLiteral("typedef"));
0299     } else if (decl->accessPolicy() == Declaration::Protected) {
0300         a_resultIcon = QIcon::fromTheme(QStringLiteral("protected_field"));
0301     } else if (decl->accessPolicy() == Declaration::Private) {
0302         a_resultIcon = QIcon::fromTheme(QStringLiteral("private_field"));
0303     } else
0304     {
0305         a_resultIcon = QIcon::fromTheme(QStringLiteral("field"));
0306     }
0307 
0308     return true;
0309 }
0310 
0311 //////////////////////////////////////////////////////////////////////////////
0312 //////////////////////////////////////////////////////////////////////////////
0313 
0314 DynamicFolderNode::DynamicFolderNode(const QString& a_displayName, NodesModelInterface* a_model)
0315     : DynamicNode(a_displayName, a_model)
0316 {
0317 }
0318 
0319 bool DynamicFolderNode::getIcon(QIcon& a_resultIcon)
0320 {
0321     a_resultIcon = QIcon::fromTheme(QStringLiteral("folder"));
0322     return true;
0323 }
0324 
0325 //////////////////////////////////////////////////////////////////////////////
0326 //////////////////////////////////////////////////////////////////////////////
0327 
0328 FolderNode::FolderNode(const QString& a_displayName, NodesModelInterface* a_model)
0329     : Node(a_displayName, a_model)
0330 {
0331 }
0332 
0333 bool FolderNode::getIcon(QIcon& a_resultIcon)
0334 {
0335     a_resultIcon = QIcon::fromTheme(QStringLiteral("folder"));
0336     return true;
0337 }
0338 
0339 //////////////////////////////////////////////////////////////////////////////
0340 //////////////////////////////////////////////////////////////////////////////
0341 
0342 BaseClassesFolderNode::BaseClassesFolderNode(NodesModelInterface* a_model)
0343     : DynamicFolderNode(i18n("Base classes"), a_model)
0344 {
0345 }
0346 
0347 void BaseClassesFolderNode::populateNode()
0348 {
0349     DUChainReadLocker readLock(DUChain::lock());
0350 
0351     auto* klass = dynamic_cast<ClassDeclaration*>(static_cast<ClassNode*>(parent())->declaration());
0352     if (klass) {
0353         // I use the imports instead of the baseClasses in the ClassDeclaration because I need
0354         // to get to the base class identifier which is not directly accessible through the
0355         // baseClasses function.
0356         const auto imports = klass->internalContext()->importedParentContexts();
0357         for (const DUContext::Import& import : imports) {
0358             DUContext* baseContext = import.context(klass->topContext());
0359             if (baseContext && baseContext->type() == DUContext::Class) {
0360                 Declaration* baseClassDeclaration = baseContext->owner();
0361                 if (baseClassDeclaration) {
0362                     // Add the base class.
0363                     addNode(new ClassNode(baseClassDeclaration, m_model));
0364                 }
0365             }
0366         }
0367     }
0368 }
0369 
0370 //////////////////////////////////////////////////////////////////////////////
0371 //////////////////////////////////////////////////////////////////////////////
0372 
0373 DerivedClassesFolderNode::DerivedClassesFolderNode(NodesModelInterface* a_model)
0374     : DynamicFolderNode(i18n("Derived classes"), a_model)
0375 {
0376 }
0377 
0378 void DerivedClassesFolderNode::populateNode()
0379 {
0380     DUChainReadLocker readLock(DUChain::lock());
0381 
0382     auto* klass = dynamic_cast<ClassDeclaration*>(static_cast<ClassNode*>(parent())->declaration());
0383     if (klass) {
0384         uint steps = 10000;
0385         const QList<Declaration*> inheriters = DUChainUtils::inheriters(klass, steps, true);
0386 
0387         for (Declaration* decl : inheriters) {
0388             addNode(new ClassNode(decl, m_model));
0389         }
0390     }
0391 }
0392 
0393 //////////////////////////////////////////////////////////////////////////////
0394 //////////////////////////////////////////////////////////////////////////////
0395 
0396 Node::Node(const QString& a_displayName, NodesModelInterface* a_model)
0397     : m_parentNode(nullptr)
0398     , m_displayName(a_displayName)
0399     , m_model(a_model)
0400 {
0401 }
0402 
0403 Node::~Node()
0404 {
0405     // Notify the model about the removal of this nodes' children.
0406     if (!m_children.empty() && m_model) {
0407         m_model->nodesAboutToBeRemoved(this, 0, m_children.size() - 1);
0408         clear();
0409         m_model->nodesRemoved(this);
0410     }
0411 }
0412 
0413 void Node::clear()
0414 {
0415     qDeleteAll(m_children);
0416     m_children.clear();
0417 }
0418 
0419 void Node::addNode(Node* a_child)
0420 {
0421 /// @note This is disabled for performance reasons - we add them to the bottom and a
0422 ///       sort usually follows which causes a layout change to be fired.
0423 //   m_model->nodesAboutToBeAdded(this, m_children.size(), 1);
0424     a_child->m_parentNode = this;
0425     m_children.push_back(a_child);
0426 //   m_model->nodesAdded(this);
0427 }
0428 
0429 void Node::removeNode(Node* a_child)
0430 {
0431     int row = a_child->row();
0432     m_model->nodesAboutToBeRemoved(this, row, row);
0433     m_children.removeAt(row);
0434     delete a_child;
0435     m_model->nodesRemoved(this);
0436 }
0437 
0438 // Sort algorithm for the nodes.
0439 struct SortNodesFunctor
0440 {
0441     bool operator()(Node* a_lhs, Node* a_rhs)
0442     {
0443         if (a_lhs->score() == a_rhs->score()) {
0444             return a_lhs->sortableString() < a_rhs->sortableString();
0445         } else
0446             return a_lhs->score() < a_rhs->score();
0447     }
0448 };
0449 
0450 void Node::recursiveSortInternal()
0451 {
0452     // Sort my nodes.
0453     std::sort(m_children.begin(), m_children.end(), SortNodesFunctor());
0454 
0455     // Tell each node to sort it self.
0456     for (Node* node : qAsConst(m_children)) {
0457         node->recursiveSortInternal();
0458     }
0459 }
0460 
0461 void Node::recursiveSort()
0462 {
0463     m_model->nodesLayoutAboutToBeChanged(this);
0464 
0465     recursiveSortInternal();
0466 
0467     m_model->nodesLayoutChanged(this);
0468 }
0469 
0470 int Node::row()
0471 {
0472     if (m_parentNode == nullptr)
0473         return -1;
0474 
0475     return m_parentNode->m_children.indexOf(this);
0476 }
0477 
0478 QIcon ClassModelNodes::Node::cachedIcon()
0479 {
0480     // Load the cached icon if it's null.
0481     if (m_cachedIcon.isNull()) {
0482         if (!getIcon(m_cachedIcon))
0483             m_cachedIcon = QIcon();
0484     }
0485 
0486     return m_cachedIcon;
0487 }
0488 
0489 //////////////////////////////////////////////////////////////////////////////
0490 //////////////////////////////////////////////////////////////////////////////
0491 
0492 DynamicNode::DynamicNode(const QString& a_displayName, NodesModelInterface* a_model)
0493     : Node(a_displayName, a_model)
0494     , m_populated(false)
0495 {
0496 }
0497 
0498 void DynamicNode::collapse()
0499 {
0500     performNodeCleanup();
0501 }
0502 
0503 void DynamicNode::expand()
0504 {
0505     performPopulateNode();
0506 }
0507 
0508 void DynamicNode::performNodeCleanup()
0509 {
0510     if (!m_populated)
0511         return;
0512 
0513     if (!m_children.empty()) {
0514         // Notify model for this node.
0515         m_model->nodesAboutToBeRemoved(this, 0, m_children.size() - 1);
0516 
0517         // Clear sub-nodes.
0518         clear();
0519 
0520         m_model->nodesRemoved(this);
0521     }
0522 
0523     // This shouldn't be called from clear since clear is called also from the d-tor
0524     // and the function is virtual.
0525     nodeCleared();
0526 
0527     // Mark the fact that we've been collapsed
0528     m_populated = false;
0529 }
0530 
0531 void DynamicNode::performPopulateNode(bool a_forceRepopulate)
0532 {
0533     if (m_populated) {
0534         if (a_forceRepopulate)
0535             performNodeCleanup();
0536         else
0537             return;
0538     }
0539 
0540     populateNode();
0541 
0542     // We're populated.
0543     m_populated = true;
0544 
0545     // Sort the list.
0546     recursiveSort();
0547 }
0548 
0549 bool DynamicNode::hasChildren() const
0550 {
0551     // To get a true status, we'll need to populate the node.
0552     const_cast<DynamicNode*>(this)->performPopulateNode();
0553 
0554     return !m_children.empty();
0555 }