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 }