File indexing completed on 2024-05-05 16:46:03
0001 /* 0002 SPDX-FileCopyrightText: 2010, 2015 Alex Richardson <alex.richardson@gmx.de> 0003 0004 SPDX-License-Identifier: LGPL-2.1-or-later 0005 */ 0006 0007 #include "outlinemodel.h" 0008 0009 #include <language/duchain/duchain.h> 0010 #include <language/duchain/duchainlock.h> 0011 #include <language/duchain/duchainutils.h> 0012 #include <language/duchain/topducontext.h> 0013 #include <language/duchain/ducontext.h> 0014 #include <language/duchain/declaration.h> 0015 #include <interfaces/icore.h> 0016 #include <interfaces/idocument.h> 0017 #include <interfaces/idocumentcontroller.h> 0018 0019 #include <debug.h> 0020 #include "outlinenode.h" 0021 0022 using namespace KDevelop; 0023 0024 OutlineModel::OutlineModel(QObject* parent) 0025 : QAbstractItemModel(parent) 0026 , m_lastDoc(nullptr) 0027 { 0028 auto docController = ICore::self()->documentController(); 0029 // build the initial outline now 0030 rebuildOutline(docController->activeDocument()); 0031 // we must always have a valid root node 0032 Q_ASSERT(m_rootNode); 0033 0034 // we want to rebuild the outline whenever the current document has been reparsed 0035 connect(DUChain::self(), &DUChain::updateReady, 0036 this, [this] (const IndexedString& document, const ReferencedTopDUContext& /*topContext*/) { 0037 if (document == m_lastUrl) { 0038 rebuildOutline(m_lastDoc); 0039 } 0040 }); 0041 // and also when we switch the current document 0042 connect(docController, &IDocumentController::documentActivated, 0043 this, &OutlineModel::rebuildOutline); 0044 connect(docController, &IDocumentController::documentClosed, 0045 this, [this](IDocument* doc) { 0046 if (doc == m_lastDoc) { 0047 m_lastDoc = nullptr; 0048 m_lastUrl = IndexedString(); 0049 rebuildOutline(nullptr); 0050 } 0051 }); 0052 connect(docController, &IDocumentController::documentUrlChanged, 0053 this, [this](IDocument* doc) { 0054 if (doc == m_lastDoc) { 0055 m_lastUrl = IndexedString(doc->url()); 0056 } 0057 }); 0058 0059 } 0060 0061 OutlineModel::~OutlineModel() 0062 { 0063 } 0064 0065 Qt::ItemFlags OutlineModel::flags(const QModelIndex& index) const 0066 { 0067 if (!index.isValid()) { 0068 return Qt::NoItemFlags; 0069 } else { 0070 return Qt::ItemIsEnabled | Qt::ItemIsSelectable; 0071 } 0072 } 0073 0074 int OutlineModel::columnCount(const QModelIndex& parent) const 0075 { 0076 Q_UNUSED(parent) 0077 return 1; 0078 } 0079 0080 QVariant OutlineModel::data(const QModelIndex& index, int role) const 0081 { 0082 if (!index.isValid()) { 0083 return QVariant(); 0084 } 0085 if (index.column() != 0) { 0086 return QVariant(); 0087 } 0088 0089 auto* node = static_cast<OutlineNode*>(index.internalPointer()); 0090 Q_ASSERT(node); 0091 if (role == Qt::DecorationRole) { 0092 return node->icon(); 0093 } 0094 if (role == Qt::DisplayRole) { 0095 return node->text(); 0096 } 0097 return QVariant(); 0098 } 0099 0100 bool OutlineModel::hasChildren(const QModelIndex& parent) const 0101 { 0102 return rowCount(parent) > 0; 0103 } 0104 0105 int OutlineModel::rowCount(const QModelIndex& parent) const 0106 { 0107 if (!parent.isValid()) { 0108 Q_ASSERT(m_rootNode); 0109 return m_rootNode->childCount(); 0110 } else if (parent.column() != 0) { 0111 return 0; 0112 } else { 0113 const auto* node = static_cast<const OutlineNode*>(parent.internalPointer()); 0114 return node->childCount(); 0115 } 0116 } 0117 0118 QModelIndex OutlineModel::index(int row, int column, const QModelIndex &parent) const 0119 { 0120 if (!hasIndex(row, column, parent)) { 0121 return QModelIndex(); 0122 } 0123 if (!parent.isValid()) { 0124 // topLevelItem 0125 if (row < m_rootNode->childCount()) { 0126 return createIndex(row, column, const_cast<OutlineNode*>(m_rootNode->childAt(row))); 0127 } 0128 return QModelIndex(); 0129 } else { 0130 if (parent.column() != 0) { 0131 return QModelIndex(); //only column 0 should have children 0132 } 0133 auto* node = static_cast<OutlineNode*>(parent.internalPointer()); 0134 if (row < node->childCount()) { 0135 return createIndex(row, column, const_cast<OutlineNode*>(node->childAt(row))); 0136 } 0137 return QModelIndex(); // out of range 0138 } 0139 return QModelIndex(); 0140 } 0141 0142 QModelIndex OutlineModel::parent(const QModelIndex& index) const 0143 { 0144 if (!index.isValid()) { 0145 return QModelIndex(); 0146 } 0147 0148 const auto* node = static_cast<const OutlineNode*>(index.internalPointer()); 0149 0150 const OutlineNode* parentNode = node->parent(); 0151 Q_ASSERT(parentNode); 0152 if (parentNode == m_rootNode.get()) { 0153 return QModelIndex(); //node is a top level item 0154 } 0155 0156 // parent node was not m_rootNode -> parent() must be valid 0157 const OutlineNode* parentParentNode = parentNode->parent(); 0158 Q_ASSERT(parentNode); 0159 const int row = parentParentNode->indexOf(parentNode); 0160 return createIndex(row, 0, const_cast<OutlineNode*>(parentNode)); 0161 } 0162 0163 void OutlineModel::rebuildOutline(IDocument* doc) 0164 { 0165 beginResetModel(); 0166 if (!doc) { 0167 m_rootNode = OutlineNode::dummyNode(); 0168 } else { 0169 // TODO: do this in a separate thread? Might take a while for large documents 0170 // and we really shouldn't be blocking the GUI thread! 0171 DUChainReadLocker lock; 0172 TopDUContext* topContext = DUChainUtils::standardContextForUrl(doc->url()); 0173 if (topContext) { 0174 m_rootNode = OutlineNode::fromTopContext(topContext); 0175 } else { 0176 m_rootNode = OutlineNode::dummyNode(); 0177 } 0178 } 0179 if (doc != m_lastDoc) { 0180 m_lastUrl = doc ? IndexedString(doc->url()) : IndexedString(); 0181 m_lastDoc = doc; 0182 } 0183 endResetModel(); 0184 } 0185 0186 void OutlineModel::activate(const QModelIndex& realIndex) 0187 { 0188 if (!realIndex.isValid()) { 0189 qCWarning(PLUGIN_OUTLINE) << "attempting to activate invalid item!"; 0190 return; 0191 } 0192 auto* node = static_cast<OutlineNode*>(realIndex.internalPointer()); 0193 KTextEditor::Range range; 0194 { 0195 DUChainReadLocker lock; 0196 const DUChainBase* dcb = node->duChainObject(); 0197 if (!dcb) { 0198 qCDebug(PLUGIN_OUTLINE) << "No declaration exists for node:" << node->text(); 0199 return; 0200 } 0201 //foreground thread == GUI thread? if so then we are fine 0202 range = dcb->rangeInCurrentRevision(); 0203 //outline view should ALWAYS correspond to currently active document 0204 Q_ASSERT(dcb->url().toUrl() == ICore::self()->documentController()->activeDocument()->url()); 0205 // lock should be released before activating the document 0206 } 0207 ICore::self()->documentController()->activateDocument(m_lastDoc, range); 0208 } 0209 0210 #include "moc_outlinemodel.cpp"