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"
25 #include <kmessagebox.h>
26 #include <ktemporaryfile.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());
62 ProxyObject::ProxyObject(DUChainBase
* _parent
, DUChainBase
* _object
)
63 : DUChainBase(_object
->range())
67 setSmartRange(_object
->smartRange(), DUChainBase::DontOwn
);
70 DUChainModel::DUChainModel(DUChainViewPlugin
* parent
)
71 : QAbstractItemModel(parent
)
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
);
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
)
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
);
107 m_document
= document
->url();
112 void DUChainModel::setTopContext(TopDUContextPointer context
)
114 DUChainReadLocker
readLock(DUChain::lock());
117 m_document
= KUrl(context
->url().str());
121 context
= TopDUContextPointer(getRealContext(context
.data()));
123 if (m_chain
!= context
)
126 qDeleteAll(m_proxyObjects
.values());
127 m_proxyObjects
.clear();
129 m_objectLists
.clear();
135 int DUChainModel::columnCount(const QModelIndex
& parent
) const
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
);
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()));
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
206 return QModelIndex();
209 QVariant
DUChainModel::data(const QModelIndex
& index
, int role
) const
211 if (!index
.isValid())
214 DUChainReadLocker
readLock(DUChain::lock());
216 DUChainBasePointer
* basep
= objectForIndex(index
);
217 if (!basep
|| !basep
->data())
220 DUChainBase
* base
= basep
->data();
222 ProxyObject
* proxy
= dynamic_cast<ProxyObject
*>(base
);
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
)) {
231 case Qt::DisplayRole
:
233 return i18n("Imported Context: %1", context
->localScopeIdentifier().toString());
234 else if (context
== m_chain
.data())
235 return i18n("Top level context");
237 return i18n("Context: %1", context
->localScopeIdentifier().toString());
240 } else if (Declaration
* dec
= dynamic_cast<Declaration
*>(base
)) {
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());
246 return i18n("Declaration: %1", dec
->identifier().toString());
250 }/* else if (Use* use = dynamic_cast<Use*>(base)) {
252 case Qt::DisplayRole:
253 return i18n("Use: %1", use->declaration() ? use->declaration()->identifier().toString() : i18n("[No definition found]"));
258 case Qt::DisplayRole
:
259 return i18n("Unknown object!");
266 int DUChainModel::rowCount(const QModelIndex
& parent
) const
271 if (!parent
.isValid())
274 DUChainReadLocker
readLock(DUChain::lock());
276 DUChainBasePointer
* base
= objectForIndex(parent
);
277 if (!base
|| !base
->data())
280 QList
<DUChainBasePointer
*>* items
= childItems(base
);
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())) { \
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
);
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() )
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;
327 DUChainBase
* currentItem
= 0;
328 Cursor first
= Cursor::invalid(), current
;
331 TEST_NEXT(contexts
, 1)
332 TEST_NEXT(importedParentContexts
, 2)
333 TEST_NEXT(declarations
, 3)
335 if (first
.isValid()) {
338 currentItem
= item(contexts
);
341 currentItem
= proxyItem(context
, importedParentContexts
);
344 currentItem
= item(declarations
);
352 list
= new QList
<DUChainBasePointer
*>();
354 DUChainBasePointer
* currentPointer
= createPointerForObject(currentItem
);
355 m_modelRow
[currentPointer
] = list
->count();
356 list
->append(currentPointer
);
358 first
= Cursor::invalid();
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()) {
374 // list = new QList<DUChainBasePointer*>();
376 // list->append(createPointerForObject(use));
380 // No child items for definitions or uses
381 //kDebug(9500) << "No child items for definitions or uses";
384 m_objectLists
.insert(parentp
, list
);
389 /*bool DUChainModel::hasChildren(const QModelIndex & parent) const
391 if (!parent.isValid())
394 DUChainReadLocker readLock(DUChain::lock());
396 DUChainBase* base = objectForIndex(parent);
400 if (m_objectLists.contains(base))
401 return !m_objectLists[base]->isEmpty();
403 ProxyObject* proxy = dynamic_cast<ProxyObject*>(base);
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();
416 void DUChainModel::branchAdded(DUContextPointer context
)
418 DUChainReadLocker
readLock(DUChain::lock());
423 if (!m_chain
|| !m_chain
->parentContextOf(context
.data()))
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
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
)
442 beginInsertRows(createIndex(m_modelRow
[parent
], 0, parent
), index
, index
);
443 list
->insert(index
, contextPointer
);
446 // Don't worry about children, they will be queried for if the view needs it
449 /*switch (relationship) {
450 case DUChainObserver::ChildContexts:
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());
461 if( *context == m_chain ) {
462 //Top-context was deleted
463 setTopContext(TopDUContextPointer());
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);
474 if (change == DUChainObserver::Removal || change == DUChainObserver::Deletion)
480 KDevelop::DUChainBasePointer
* DUChainModel::pointerForObject(KDevelop::DUChainBase
* object
) const
482 if (m_knownObjects
.contains(object
))
483 return m_knownObjects
[object
];
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
);
497 ret
= m_knownObjects
[object
];
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())
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();
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() ) {
541 KMessageBox::error(0, i18n("Cannot create temporary file \"%1\" with suffix \"%2\"", tempFile
.fileName(), suffix
));
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();
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() ) {
556 proc2
<< "dotty" << fileName
;
557 if( !proc2
.startDetached() ) {
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