Even though the last alpha was .91 this one is going to be 82 as I'd like to have...
[kdevelopdvcssupport.git] / plugins / duchainviewer / duchainmodel.cpp
blob28fc37592eeb902e5dff0134e1b1c504fdd777d1
1 /*
2 * KDevelop DUChain viewer
4 * Copyright 2006 Hamish Rodda <rodda@kde.org>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU Library General Public License as
8 * published by the Free Software Foundation; either version 2 of the
9 * License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public
17 * License along with this program; if not, write to the
18 * Free Software Foundation, Inc.,
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
22 #include "duchainmodel.h"
24 #include <klocale.h>
25 #include <kmessagebox.h>
26 #include <ktemporaryfile.h>
27 #include <kprocess.h>
29 #include <interfaces/idocument.h>
30 #include <interfaces/icore.h>
31 #include <interfaces/ilanguagecontroller.h>
32 #include <language/backgroundparser/backgroundparser.h>
33 #include <language/backgroundparser/parsejob.h>
35 #include <language/editor/hashedstring.h>
37 #include <language/duchain/dumpdotgraph.h>
38 #include <language/duchain/topducontext.h>
39 #include <language/duchain/declaration.h>
40 #include <language/duchain/parsingenvironment.h>
41 #include <language/duchain/use.h>
42 #include <language/duchain/duchain.h>
43 #include <language/duchain/duchainlock.h>
44 #include <language/duchain/duchainpointer.h>
46 #include "duchainviewplugin.h"
47 #include <language/duchain/functiondefinition.h>
49 //#include "modeltest.h"
51 using namespace KTextEditor;
52 using namespace KDevelop;
54 ///When the context is a proxy-context, returns the assigned content-context.
55 TopDUContext* getRealContext(TopDUContext* ctx) {
56 if(ctx && ctx->parsingEnvironmentFile() && ctx->parsingEnvironmentFile()->isProxyContext() && !ctx->importedParentContexts().isEmpty())
57 return dynamic_cast<TopDUContext*>(ctx->importedParentContexts()[0].context());
58 else
59 return ctx;
62 ProxyObject::ProxyObject(DUChainBase* _parent, DUChainBase* _object)
63 : DUChainBase(_object->range())
64 , parent(_parent)
65 , object(_object)
67 setSmartRange(_object->smartRange(), DUChainBase::DontOwn);
70 DUChainModel::DUChainModel(DUChainViewPlugin* parent)
71 : QAbstractItemModel(parent)
72 , m_chain(0)
74 //new ModelTest(this);
75 connect( plugin()->core()->languageController()->backgroundParser(), SIGNAL(parseJobFinished(KDevelop::ParseJob*)), this, SLOT(parseJobFinished(KDevelop::ParseJob*)));
77 bool success = connect(DUChain::self()->notifier(), SIGNAL(branchAdded(KDevelop::DUContextPointer)), SLOT(branchAdded(KDevelop::DUContextPointer)), Qt::QueuedConnection);
78 Q_ASSERT(success);
81 DUChainViewPlugin* DUChainModel::plugin() const {
82 return qobject_cast<DUChainViewPlugin*>(QObject::parent());
85 DUChainModel::~DUChainModel()
87 qDeleteAll(m_knownObjects.values());
90 void DUChainModel::parseJobFinished(KDevelop::ParseJob* job)
92 if( KUrl(job->document().str()) == m_document && job->duChain() ) {
93 setTopContext(TopDUContextPointer(job->duChain()->weakPointer()));
98 void DUChainModel::documentActivated(KDevelop::IDocument* document)
100 if (document) {
101 DUChainReadLocker readLock(DUChain::lock());
102 TopDUContext* ptr = DUChain::self()->chainForDocument(document->url());
103 TopDUContextPointer chain(ptr);
104 if (chain && chain != m_chain)
105 setTopContext(chain);
106 else {
107 m_document = document->url();
112 void DUChainModel::setTopContext(TopDUContextPointer context)
114 DUChainReadLocker readLock(DUChain::lock());
116 if( context )
117 m_document = KUrl(context->url().str());
118 else
119 m_document = KUrl();
121 context = TopDUContextPointer(getRealContext(context.data()));
123 if (m_chain != context)
124 m_chain = context;
126 qDeleteAll(m_proxyObjects.values());
127 m_proxyObjects.clear();
129 m_objectLists.clear();
130 m_modelRow.clear();
132 reset();
135 int DUChainModel::columnCount(const QModelIndex & parent) const
137 Q_UNUSED(parent);
139 return 1;
142 DUChainBasePointer* DUChainModel::objectForIndex(const QModelIndex& index) const
144 return static_cast<DUChainBasePointer*>(index.internalPointer());
147 QModelIndex DUChainModel::index(int row, int column, const QModelIndex & parent) const
149 if (row < 0 || column < 0 || column > 0 || !m_chain)
150 return QModelIndex();
152 if (!parent.isValid()) {
153 if (parent.row() > 0 || parent.column() > 0)
154 return QModelIndex();
156 return createIndex(row,column,createPointerForObject(m_chain.data()));
159 DUChainReadLocker readLock(DUChain::lock());
161 DUChainBasePointer* base = objectForIndex(parent);
162 if (!base || !base->data())
163 return QModelIndex();
165 QList<DUChainBasePointer*>* items = childItems(base);
167 if (!items)
168 return QModelIndex();
170 if (row >= items->count())
171 return QModelIndex();
173 return createIndex(row, column, items->at(row));
176 QModelIndex DUChainModel::parent(const QModelIndex & index) const
178 if (!index.isValid())
179 return QModelIndex();
181 DUChainReadLocker readLock(DUChain::lock());
183 DUChainBasePointer* basep = objectForIndex(index);
184 if (!basep || !basep->data())
185 return QModelIndex();
187 DUChainBase* base = basep->data();
189 if (ProxyObject* proxy = dynamic_cast<ProxyObject*>(base))
190 return createParentIndex(createPointerForObject(proxy->parent));
192 if (DUContext* context = dynamic_cast<DUContext*>(base))
193 if (context && context->parentContext())
194 return createParentIndex(createPointerForObject(context->parentContext()));
195 else
196 return QModelIndex();
198 if (Declaration* dec = dynamic_cast<Declaration*>(base))
199 return createParentIndex(createPointerForObject(dec->context()));
201 // if (Use* use = dynamic_cast<Use*>(base))
202 // return createParentIndex(createPointerForObject(use->declaration()));
204 // Shouldn't really hit this
205 //Q_ASSERT(false);
206 return QModelIndex();
209 QVariant DUChainModel::data(const QModelIndex& index, int role) const
211 if (!index.isValid())
212 return QVariant();
214 DUChainReadLocker readLock(DUChain::lock());
216 DUChainBasePointer* basep = objectForIndex(index);
217 if (!basep || !basep->data())
218 return QVariant();
220 DUChainBase* base = basep->data();
222 ProxyObject* proxy = dynamic_cast<ProxyObject*>(base);
223 if (proxy)
224 base = proxy->object;
226 if(role == Qt::ToolTipRole)
227 return i18n("Range: %1:%2 -> %3 %4", base->range().start.line, base->range().start.column, base->range().end.line, base->range().start.column);
229 if (DUContext* context = dynamic_cast<DUContext*>(base)) {
230 switch (role) {
231 case Qt::DisplayRole:
232 if (proxy)
233 return i18n("Imported Context: %1", context->localScopeIdentifier().toString());
234 else if (context == m_chain.data())
235 return i18n("Top level context");
236 else
237 return i18n("Context: %1", context->localScopeIdentifier().toString());
240 } else if (Declaration* dec = dynamic_cast<Declaration*>(base)) {
241 switch (role) {
242 case Qt::DisplayRole: {
243 if(dynamic_cast<FunctionDefinition*>(dec) && static_cast<FunctionDefinition*>(dec)->declaration(m_chain.data()))
244 return i18n("Definition: %1", static_cast<FunctionDefinition*>(dec)->declaration(m_chain.data())->identifier().toString());
245 else
246 return i18n("Declaration: %1", dec->identifier().toString());
250 }/* else if (Use* use = dynamic_cast<Use*>(base)) {
251 switch (role) {
252 case Qt::DisplayRole:
253 return i18n("Use: %1", use->declaration() ? use->declaration()->identifier().toString() : i18n("[No definition found]"));
256 }*/ else {
257 switch (role) {
258 case Qt::DisplayRole:
259 return i18n("Unknown object!");
263 return QVariant();
266 int DUChainModel::rowCount(const QModelIndex & parent) const
268 if (!m_chain)
269 return 0;
271 if (!parent.isValid())
272 return 1;
274 DUChainReadLocker readLock(DUChain::lock());
276 DUChainBasePointer* base = objectForIndex(parent);
277 if (!base || !base->data())
278 return 0;
280 QList<DUChainBasePointer*>* items = childItems(base);
281 if (!items)
282 return 0;
284 return items->count();
287 #define TEST_NEXT(iterator, index) \
288 if (!first.isValid()) { \
289 current = nextItem(iterator, firstInit); \
290 if (current.isValid() && (current < first || !first.isValid())) { \
291 first = current; \
292 found = index; \
296 QList<DUChainBasePointer*>* DUChainModel::childItems(DUChainBasePointer* parentp) const
298 Q_ASSERT(parentp->data());
300 if (m_objectLists.contains(parentp))
301 return m_objectLists[parentp];
303 DUChainBase* parent = parentp->data();
305 ProxyObject* proxy = dynamic_cast<ProxyObject*>(parent);
306 if (proxy)
307 parent = proxy->object;
309 QList<DUChainBasePointer*>* list = 0;
311 if (DUContext* context = dynamic_cast<DUContext*>(parent)) {
313 QVector<DUContext*> importedParentContextsData;
314 ///@todo Think whether this can be called for top-contexts, and if it can, care about endless recursion because of loops.
315 foreach( DUContext::Import p, context->importedParentContexts() )
316 if( p.context() )
317 importedParentContextsData << p.context();
319 QVectorIterator<DUContext*> contexts = context->childContexts();
320 QVectorIterator<DUContext*> importedParentContexts = importedParentContextsData;
321 QVectorIterator<Declaration*> declarations = context->localDeclarations();
322 // QListIterator<Use*> uses = context->uses();
325 bool firstInit = true;
326 forever {
327 DUChainBase* currentItem = 0;
328 Cursor first = Cursor::invalid(), current;
329 int found = 0;
331 TEST_NEXT(contexts, 1)
332 TEST_NEXT(importedParentContexts, 2)
333 TEST_NEXT(declarations, 3)
335 if (first.isValid()) {
336 switch (found) {
337 case 1:
338 currentItem = item(contexts);
339 break;
340 case 2:
341 currentItem = proxyItem(context, importedParentContexts);
342 break;
343 case 3:
344 currentItem = item(declarations);
345 break;
346 default:
347 Q_ASSERT(false);
348 break;
351 if (!list)
352 list = new QList<DUChainBasePointer*>();
354 DUChainBasePointer* currentPointer = createPointerForObject(currentItem);
355 m_modelRow[currentPointer] = list->count();
356 list->append(currentPointer);
358 first = Cursor::invalid();
359 } else {
360 break;
363 firstInit = false;
366 } else if (Declaration* dec = dynamic_cast<Declaration*>(parent)) {
367 if (!dec->isDefinition() && FunctionDefinition::definition(dec)) {
368 list = new QList<DUChainBasePointer*>();
369 list->append(createPointerForObject(FunctionDefinition::definition(dec)));
372 // foreach (Use* use, dec->uses()) {
373 // if (!list)
374 // list = new QList<DUChainBasePointer*>();
376 // list->append(createPointerForObject(use));
377 // }
379 } else {
380 // No child items for definitions or uses
381 //kDebug(9500) << "No child items for definitions or uses";
384 m_objectLists.insert(parentp, list);
386 return list;
389 /*bool DUChainModel::hasChildren(const QModelIndex & parent) const
391 if (!parent.isValid())
392 return true;
394 DUChainReadLocker readLock(DUChain::lock());
396 DUChainBase* base = objectForIndex(parent);
397 if (!base)
398 return false;
400 if (m_objectLists.contains(base))
401 return !m_objectLists[base]->isEmpty();
403 ProxyObject* proxy = dynamic_cast<ProxyObject*>(base);
404 if (proxy)
405 base = proxy->object;
407 if (DUContext* context = dynamic_cast<DUContext*>(base))
408 return !context->childContexts().isEmpty() || !context->importedParentContexts().isEmpty() || !context->localDeclarations().isEmpty() || !context->localDefinitions().isEmpty() || !context->uses().isEmpty();
410 else if (Declaration* dec = dynamic_cast<Declaration*>(base))
411 return dec->definition() || !dec->uses().isEmpty();
413 return false;
416 void DUChainModel::branchAdded(DUContextPointer context)
418 DUChainReadLocker readLock(DUChain::lock());
420 if (!context)
421 return;
423 if (!m_chain || !m_chain->parentContextOf(context.data()))
424 return;
426 DUChainBasePointer* parent = pointerForObject(context->parentContext());
428 if (!parent || !m_objectLists.contains(parent) || !m_modelRow.contains(parent))
429 // No entry for parent, ok - it will be created if the view interrogates for it
430 return;
432 QList<DUChainBasePointer*>* list = childItems(parent);
434 DUChainBasePointer* contextPointer = createPointerForObject(context.data());
436 int index = findInsertIndex(*list, context.data());
438 if (list->at(index) == contextPointer)
439 // Already added...
440 return;
442 beginInsertRows(createIndex(m_modelRow[parent], 0, parent), index, index);
443 list->insert(index, contextPointer);
444 endInsertRows();
446 // Don't worry about children, they will be queried for if the view needs it
449 /*switch (relationship) {
450 case DUChainObserver::ChildContexts:
451 switch (change) {
452 case DUChainObserver::Deletion:
453 case DUChainObserver::Removal: {
454 if (m_objectLists.contains(context)) {
455 m_objectLists.remove(context);
456 m_knownObjects.remove(ro->data());
457 delete ro;
459 // fallthrough
461 if( *context == m_chain ) {
462 //Top-context was deleted
463 setTopContext(TopDUContextPointer());
464 return;
468 case DUChainObserver::Change: {
469 int index = list->indexOf(ro);
470 Q_ASSERT(index != -1);
471 beginRemoveRows(createIndex(m_modelRow[context], 0, context), index, index);
472 list->removeAt(index);
473 endRemoveRows();
474 if (change == DUChainObserver::Removal || change == DUChainObserver::Deletion)
475 break;
476 // else fallthrough
480 KDevelop::DUChainBasePointer* DUChainModel::pointerForObject(KDevelop::DUChainBase* object) const
482 if (m_knownObjects.contains(object))
483 return m_knownObjects[object];
485 return 0L;
488 KDevelop::DUChainBasePointer* DUChainModel::createPointerForObject(KDevelop::DUChainBase* object) const
490 KDevelop::DUChainBasePointer* ret = 0L;
492 if (!m_knownObjects.contains(object)) {
493 ret = new KDevelop::DUChainBasePointer(object->weakPointer());
494 m_knownObjects.insert(object, ret);
496 } else {
497 ret = m_knownObjects[object];
500 return ret;
503 QModelIndex DUChainModel::createParentIndex(DUChainBasePointer* type) const
505 return createIndex(m_modelRow[type], 0, type);
508 int DUChainModel::findInsertIndex(QList<DUChainBasePointer*>& list, DUChainBase* object) const
510 for (int i = 0; i < list.count(); ++i)
511 if (DUChainBase* at = list.at(i)->data())
512 if (at->range().textRange().start() > object->range().textRange().start())
513 return i;
515 return list.count();
518 void DUChainModel::doubleClicked ( const QModelIndex & index ) {
519 DUChainReadLocker readLock(DUChain::lock());
520 if( index.isValid() ) {
521 DUChainBase* base = objectForIndex(index)->data();
522 DUContext* ctx = dynamic_cast<DUContext*>(base);
523 if( base && !ctx && dynamic_cast<Declaration*>(base) )
524 ctx = static_cast<Declaration*>(base)->internalContext();
526 if(ctx) {
527 KTemporaryFile tempFile;
530 QString suffix = (dynamic_cast<TopDUContext*>(ctx) ?
531 static_cast<TopDUContext*>(ctx)->url().str()
532 : ctx->localScopeIdentifier().toString());
533 suffix = suffix.replace('/', '_');
534 suffix = suffix.replace(':', '.');
535 suffix = suffix.replace(' ', '_');
536 suffix += ".temp.dot";
537 tempFile.setSuffix( suffix );
539 if( !tempFile.open() ) {
540 readLock.unlock();
541 KMessageBox::error(0, i18n("Cannot create temporary file \"%1\" with suffix \"%2\"", tempFile.fileName(), suffix));
542 return;
544 DumpDotGraph dump;
545 tempFile.write( dump.dotGraph( ctx ).toLocal8Bit() ); //Shorten if it is a top-context, because it would become too much output
548 tempFile.setAutoRemove(false);
549 QString fileName = tempFile.fileName();
550 tempFile.close();
551 kDebug(9500) << "Wrote dot-graph of context " << ctx << " into " << fileName;
552 KProcess proc; ///@todo this is a simple hack. Maybe do it with mime-types etc.
553 proc << "kgraphviewer" << fileName;
554 if( !proc.startDetached() ) {
555 KProcess proc2;
556 proc2 << "dotty" << fileName;
557 if( !proc2.startDetached() ) {
558 readLock.unlock();
559 KMessageBox::error(0, i18n("Could not open %1 with kgraphviewer or dotty.", fileName));
567 #include "duchainmodel.moc"
569 // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on