Add AbstractDeclarationNavigationContext, and move the html-method from
[kdevelopdvcssupport.git] / language / duchain / duchain.cpp
blob45890ae00424aae628611e5663573ff4fca36afe
1 /* This is part of KDevelop
2 Copyright 2006-2008 Hamish Rodda <rodda@kde.org>
3 Copyright 2007-2008 David Nolden <david.nolden.kdevelop@art-master.de>
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Library General Public
7 License version 2 as published by the Free Software Foundation.
9 This library is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 Library General Public License for more details.
14 You should have received a copy of the GNU Library General Public License
15 along with this library; see the file COPYING.LIB. If not, write to
16 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17 Boston, MA 02110-1301, USA.
20 #include "duchain.h"
21 #include "duchainlock.h"
23 #include <QCoreApplication>
24 #include <QHash>
25 #include <QMultiMap>
26 #include <QTimer>
27 #include <qatomic.h>
29 #include <kglobal.h>
31 #include <KTextEditor/Document>
33 #include <interfaces/idocumentcontroller.h>
34 #include <interfaces/icore.h>
35 #include <interfaces/ilanguage.h>
36 #include <interfaces/ilanguagecontroller.h>
38 #include "../editor/editorintegrator.h"
39 #include "../interfaces/ilanguagesupport.h"
40 #include "../interfaces/icodehighlighting.h"
41 #include "../backgroundparser/backgroundparser.h"
43 #include "topducontext.h"
44 #include "topducontextdata.h"
45 #include "topducontextdynamicdata.h"
46 #include "parsingenvironment.h"
47 #include "declaration.h"
48 #include "definitions.h"
49 #include "duchainutils.h"
50 #include "use.h"
51 #include "uses.h"
52 #include "abstractfunctiondeclaration.h"
53 #include "smartconverter.h"
54 #include "duchainutils.h"
55 #include "duchainregister.h"
56 #include "repositories/itemrepository.h"
57 #include <util/google/dense_hash_map>
58 #include <qthread.h>
59 #include <qwaitcondition.h>
60 #include <qmutex.h>
61 #include <unistd.h>
63 Q_DECLARE_METATYPE(KDevelop::IndexedString)
64 Q_DECLARE_METATYPE(KDevelop::IndexedTopDUContext)
65 Q_DECLARE_METATYPE(KDevelop::ReferencedTopDUContext)
67 //Additional "soft" cleanup steps that are done before the actual cleanup.
68 //During "soft" cleanup, the consistency is not guaranteed. The repository is
69 //marked to be updating during soft cleanup, so if kdevelop crashes, it will be cleared.
70 //The big advantage of the soft cleanup steps is, that the duchain is always only locked for
71 //short times, which leads to no lockup in the UI.
72 const int SOFT_CLEANUP_STEPS = 1;
74 namespace KDevelop
76 //This thing is not actually used, but it's needed for compiling
77 DEFINE_LIST_MEMBER_HASH(EnvironmentInformationItem, sizes, uint)
79 ///Represtens the environment-information for exactly one file in the repository, holding all known instances
80 class EnvironmentInformationItem {
81 public:
82 EnvironmentInformationItem() {
83 initializeAppendedLists(true);
86 ~EnvironmentInformationItem() {
87 freeAppendedLists();
90 unsigned int hash() const {
91 //We only compare the declaration. This allows us implementing a map, although the item-repository
92 //originally represents a set.
93 return m_file.hash();
96 unsigned short int itemSize() const {
97 uint dataSize = 0;
98 FOREACH_FUNCTION(uint size, sizes)
99 dataSize += size;
100 return dynamicSize() + dataSize;
103 IndexedString m_file;
105 uint classSize() const {
106 return sizeof(*this);
109 START_APPENDED_LISTS(EnvironmentInformationItem);
110 ///Contains a size for each contained data item. The items are stored behind the end of this list.
111 APPENDED_LIST_FIRST(EnvironmentInformationItem, uint, sizes);
112 END_APPENDED_LISTS(EnvironmentInformationItem, sizes);
115 class EnvironmentInformationRequest {
116 public:
118 ///This constructor should only be used for lookup
119 EnvironmentInformationRequest(const IndexedString& file) : m_file(file) {
121 ///This is used to actually construct the information in the repository
122 EnvironmentInformationRequest(const IndexedString& file, QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator start, QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator end) : m_file(file), m_start(start), m_end(end) {
125 enum {
126 AverageSize = 45 //This should be the approximate average size of an Item
129 unsigned int hash() const {
130 return m_file.hash();
133 size_t itemSize() const {
134 uint dataSize = 0;
136 uint count = 0;
138 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator info = m_start; info != m_end; ++info) {
139 dataSize += DUChainItemSystem::self().dynamicSize( *(*info)->d_func() );
140 ++count;
143 return sizeof(EnvironmentInformationItem) + sizeof(uint) * count + dataSize;
146 void createItem(EnvironmentInformationItem* item) const {
147 item->m_file = m_file;
148 item->initializeAppendedLists(false);
149 char* pos = ((char*)item) + sizeof(EnvironmentInformationItem);
151 uint count = 0;
152 //Store the sizes
153 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator info = m_start; info != m_end; ++info) {
154 *(uint*)pos = DUChainItemSystem::self().dynamicSize( *(*info)->d_func() );
155 pos += sizeof(uint);
156 ++count;
158 //Store the data
159 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator info = m_start; info != m_end; ++info) {
160 DUChainItemSystem::self().copy(*(*info)->d_func(), *(DUChainBaseData*)pos, true);
161 pos += DUChainItemSystem::self().dynamicSize( *(*info)->d_func() );;
164 item->sizesData = count;
167 bool equals(const EnvironmentInformationItem* item) const {
168 return m_file == item->m_file;
171 IndexedString m_file;
172 QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator m_start;
173 QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator m_end;
176 class DUChainPrivate;
177 static DUChainPrivate* duChainPrivateSelf = 0;
178 class DUChainPrivate
180 class CleanupThread : public QThread {
181 public:
182 CleanupThread(DUChainPrivate* data) : m_stopRunning(false), m_data(data) {
185 void stopThread() {
186 m_stopRunning = true;
187 m_wait.wakeAll(); //Wakes the thread up, so it notices it should exit
188 wait();
191 private:
192 void run() {
193 while(1) {
194 m_waitMutex.lock();
195 m_wait.wait(&m_waitMutex, 1000 * 90); //Wait 90s by default
196 m_waitMutex.unlock();
197 if(m_stopRunning)
198 break;
199 m_data->doMoreCleanup(SOFT_CLEANUP_STEPS);
200 if(m_stopRunning)
201 break;
204 bool m_stopRunning;
205 QWaitCondition m_wait;
206 QMutex m_waitMutex;
207 DUChainPrivate* m_data;
209 public:
210 DUChainPrivate() : m_chainsMutex(QMutex::Recursive), instance(0), m_destroyed(false), m_environmentInfo("Environment Information")
212 m_chainsByIndex.set_empty_key(0);
213 m_chainsByIndex.set_deleted_key(0xffffffff);
214 duChainPrivateSelf = this;
215 qRegisterMetaType<DUChainBasePointer>("KDevelop::DUChainBasePointer");
216 qRegisterMetaType<DUContextPointer>("KDevelop::DUContextPointer");
217 qRegisterMetaType<TopDUContextPointer>("KDevelop::TopDUContextPointer");
218 qRegisterMetaType<DeclarationPointer>("KDevelop::DeclarationPointer");
219 qRegisterMetaType<FunctionDeclarationPointer>("KDevelop::FunctionDeclarationPointer");
220 qRegisterMetaType<Problem>("KDevelop::Problem");
221 qRegisterMetaType<KDevelop::IndexedString>("KDevelop::IndexedString");
222 qRegisterMetaType<KDevelop::IndexedTopDUContext>("KDevelop::IndexedTopDUContext");
223 qRegisterMetaType<KDevelop::ReferencedTopDUContext>("KDevelop::ReferencedTopDUContext");
225 notifier = new DUChainObserver();
226 instance = new DUChain();
227 m_cleanup = new CleanupThread(this);
228 m_cleanup->start();
230 ~DUChainPrivate() {
231 m_cleanup->stopThread();
232 delete m_cleanup;
233 delete instance;
236 void clear() {
237 QMutexLocker l(&m_chainsMutex);
239 //Store all top-contexts to disk
240 for(google::dense_hash_map<uint, TopDUContext*>::const_iterator it = m_chainsByIndex.begin(); it != m_chainsByIndex.end(); ++it)
241 (*it).second->m_dynamicData->store();
243 for(google::dense_hash_map<uint, TopDUContext*>::const_iterator it = m_chainsByIndex.begin(); it != m_chainsByIndex.end(); ++it)
244 instance->removeDocumentChain((*it).second);
246 //Store all the environment-information to disk
247 while(!m_fileEnvironmentInformations.isEmpty())
248 unloadInformation(m_fileEnvironmentInformations.begin().key());
250 Q_ASSERT(m_fileEnvironmentInformations.isEmpty());
251 Q_ASSERT(m_chainsByUrl.isEmpty());
252 Q_ASSERT(m_chainsByIndex.empty());
255 ///Must be locked before accessing content of this class
256 QMutex m_chainsMutex;
258 CleanupThread* m_cleanup;
260 DUChain* instance;
261 DUChainLock lock;
262 QMultiMap<IndexedString, TopDUContext*> m_chainsByUrl;
263 google::dense_hash_map<uint, TopDUContext*> m_chainsByIndex;
264 QHash<TopDUContext*, uint> m_referenceCounts;
265 DUChainObserver* notifier;
266 Definitions m_definitions;
267 Uses m_uses;
268 QSet<uint> m_loading;
270 ///Used to keep alive the top-context that belong to documents loaded in the editor
271 QSet<ReferencedTopDUContext> m_openDocumentContexts;
273 bool m_destroyed;
275 void addEnvironmentInformation(IndexedString url, ParsingEnvironmentFilePointer info) {
276 loadInformation(url);
277 m_fileEnvironmentInformations.insert(url, info);
280 void removeEnvironmentInformation(IndexedString url, ParsingEnvironmentFilePointer info) {
281 loadInformation(url);
282 m_fileEnvironmentInformations.remove(url, info);
285 QList<ParsingEnvironmentFilePointer> getEnvironmentInformation(IndexedString url) {
286 loadInformation(url);
287 return m_fileEnvironmentInformations.values(url);
290 ///Makes sure that the chain with the given index is loaded
291 ///@warning m_chainsMutex must NOT be locked when this is called
292 void loadChain(uint index, QSet<uint>& loaded) {
294 QMutexLocker l(&m_chainsMutex);
296 if(m_chainsByIndex.find(index) == m_chainsByIndex.end()) {
297 Q_ASSERT(!m_loading.contains(index)); //When this asserts, a chain was requested that is already being loaded. Must not happen.
298 m_loading.insert(index);
299 loaded.insert(index);
300 TopDUContext* chain = TopDUContextDynamicData::load(index);
301 if(chain) {
302 // kDebug() << "url" << chain->url().str();
303 loadInformation(chain->url());
304 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = m_fileEnvironmentInformations.lowerBound(chain->url()); it != m_fileEnvironmentInformations.end() && it.key() == chain->url(); ++it) {
305 if((*it)->indexedTopContext() == IndexedTopDUContext(index))
306 chain->setParsingEnvironmentFile((*it).data());
309 l.unlock();
310 //Also load all the imported chains, so they are in the symbol table and the import-structure is built
311 foreach(DUContext::Import import, chain->DUContext::importedParentContexts()) {
312 if(!loaded.contains(import.topContextIndex())) {
313 loadChain(import.topContextIndex(), loaded);
316 chain->rebuildDynamicImportStructure();
318 chain->setInDuChain(true);
319 l.relock();
320 instance->addDocumentChain(chain);
322 m_loading.remove(index);
327 ///Stores all environment-information
328 ///Also makes sure that all information that stays is referenced, so it stays alive.
329 ///@param atomic If this is false, the write-lock will be released time by time
330 void storeAllInformation(bool atomic, DUChainWriteLocker& locker) {
331 // QMutexLocker l(&m_chainsMutex);
332 QList<IndexedString> urls = m_fileEnvironmentInformations.keys();
333 foreach(IndexedString url, urls) {
334 storeInformation(url);
336 //Access the data in the repository, so the bucket isn't unloaded
337 uint index = m_environmentInfo.findIndex(EnvironmentInformationRequest(url));
338 if(index) {
339 m_environmentInfo.itemFromIndex(index);
340 }else{
341 kDebug(9505) << "Did not find stored item for" << url.str() << "count:" << m_fileEnvironmentInformations.values(url);
343 if(!atomic) {
344 locker.unlock();
345 locker.lock();
350 ///@param retries When this is nonzero, then doMoreCleanup will do the specified amount of cycles
351 ///doing the cleanup without permanently locking the du-chain. During these steps the consistency
352 ///of the disk-storage is not guaranteed, but only few changes will be done during these steps,
353 ///so the final step where the duchain is permanetly locked is much faster.
354 void doMoreCleanup(int retries = 0, bool needLockRepository = true) {
357 //This mutex makes sure that there's never 2 threads at he same time trying to clean up
358 static QMutex cleanupMutex(QMutex::Recursive);
359 QMutexLocker lockCleanupMutex(&cleanupMutex);
361 Q_ASSERT(!instance->lock()->currentThreadHasReadLock() && !instance->lock()->currentThreadHasWriteLock());
362 DUChainWriteLocker writeLock(instance->lock());
364 //This is used to stop all parsing before starting to do the cleanup. This way less happens during the
365 //soft cleanups, and we have a good chance that during the "hard" cleanup only few data has to be wriitten.
366 QList<ILanguage*> lockedParseMutexes;
368 if(needLockRepository) {
370 lockedParseMutexes = ICore::self()->languageController()->activeLanguages();
372 writeLock.unlock();
374 //Here we wait for all parsing-threads to stop their processing
375 foreach(ILanguage* language, lockedParseMutexes)
376 language->lockAllParseMutexes();
378 writeLock.lock();
380 globalItemRepositoryRegistry().lockForWriting();
381 kDebug(9505) << "starting cleanup";
384 QTime startTime = QTime::currentTime();
386 storeAllInformation(!retries, writeLock); //Puts environment-information into a repository
388 //We don't need to increase the reference-count, since the cleanup-mutex is locked
389 QSet<TopDUContext*> workOnContexts;
391 for(google::dense_hash_map<uint, TopDUContext*>::const_iterator it = m_chainsByIndex.begin(); it != m_chainsByIndex.end(); ++it)
392 workOnContexts << (*it).second;
394 foreach(TopDUContext* context, workOnContexts) {
396 context->m_dynamicData->store();
398 if(retries) {
399 //Eventually give other threads a chance to access the duchain
400 writeLock.unlock();
401 //Sleep to give the other threads a realistic chance to get a read-lock in between
402 usleep(500);
403 writeLock.lock();
407 //Unload all top-contexts that don't have a reference-count and that are not imported by a referenced one
409 QSet<IndexedString> unloadedNames;
410 bool unloadedOne = true;
412 bool unloadAllUnreferenced = !retries;
414 //Now unload contexts, but only ones that are not imported by any other currently loaded context
415 //The complication: Since during the lock-break new references may be added, we must never keep
416 //the du-chain in an invalid state. Thus we can only unload contexts that are not imported by any
417 //currently loaded contexts. In case of loops, we have to unload everything at once.
418 while(unloadedOne) {
419 unloadedOne = false;
420 int hadUnloadable = 0;
422 unloadContexts:
424 foreach(TopDUContext* unload, workOnContexts) {
426 bool hasReference = false;
428 //Test if the context is imported by a referenced one
429 foreach(TopDUContext* context, m_referenceCounts.keys())
430 if(context == unload || context->imports(unload, SimpleCursor())) {
431 workOnContexts.remove(unload);
432 hasReference = true;
435 if(!hasReference)
436 ++hadUnloadable; //We have found a context that is not referenced
437 else
438 continue; //This context is referenced
440 bool isImportedByLoaded = !unload->loadedImporters().isEmpty();
442 //If we unload a context that is imported by other contexts, we create a bad loaded state
443 if(isImportedByLoaded && !unloadAllUnreferenced)
444 continue;
446 unloadedNames.insert(unload->url());
447 instance->removeDocumentChain(unload);
448 workOnContexts.remove(unload);
449 unloadedOne = true;
451 if(!unloadAllUnreferenced) {
452 //Eventually give other threads a chance to access the duchain
453 writeLock.unlock();
454 //Sleep to give the other threads a realistic chance to get a read-lock in between
455 usleep(500);
456 writeLock.lock();
459 if(hadUnloadable && !unloadedOne) {
460 Q_ASSERT(!unloadAllUnreferenced);
461 //This can happen in case of loops. We have o unload everything at one time.
462 kDebug(9505) << "found" << hadUnloadable << "unloadable contexts, but could not unload separately. Unloading atomically.";
463 unloadAllUnreferenced = true;
464 goto unloadContexts;
468 foreach(IndexedString unloaded, unloadedNames) {
469 if(!m_chainsByUrl.contains(unloaded))
470 //No more top-contexts with the url are loaded, so also unload the environment-info
471 unloadInformation(unloaded);
473 if(retries) {
474 //Eventually give other threads a chance to access the duchain
475 writeLock.unlock();
476 //Sleep to give the other threads a realistic chance to get a read-lock in between
477 usleep(500);
478 writeLock.lock();
482 if(retries)
483 writeLock.unlock();
485 globalItemRepositoryRegistry().store(); //Stores all repositories
487 if(retries) {
488 doMoreCleanup(retries-1, false);
489 writeLock.lock();
492 if(needLockRepository) {
493 globalItemRepositoryRegistry().unlockForWriting();
495 int elapsedSeconds = startTime.secsTo(QTime::currentTime());
496 kDebug(9505) << "seconds spent doing cleanup: " << elapsedSeconds;
498 if(!retries) {
499 int elapesedMilliSeconds = startTime.msecsTo(QTime::currentTime());
500 kDebug(9505) << "milliseconds spent doing cleanup with locked duchain: " << elapesedMilliSeconds;
503 foreach(ILanguage* language, lockedParseMutexes)
504 language->unlockAllParseMutexes();
507 private:
508 void addRecursiveImports(QSet<TopDUContext*>& contexts, TopDUContext* current);
510 void loadInformation(IndexedString url) {
511 if(m_fileEnvironmentInformations.find(url) != m_fileEnvironmentInformations.end())
512 return;
514 uint index = m_environmentInfo.findIndex(EnvironmentInformationRequest(url));
515 if(!index) {
516 //No information there yet for this file
517 return;
520 const EnvironmentInformationItem& item(*m_environmentInfo.itemFromIndex(index));
521 char* pos = (char*)&item;
522 pos += item.dynamicSize();
523 FOREACH_FUNCTION(uint size, item.sizes) {
524 DUChainBase* data = DUChainItemSystem::self().create((DUChainBaseData*)pos);
525 Q_ASSERT(dynamic_cast<ParsingEnvironmentFile*>(data));
526 m_fileEnvironmentInformations.insert(url, ParsingEnvironmentFilePointer(static_cast<ParsingEnvironmentFile*>(data)));
527 pos += size;
531 ///Stores the environment-information for the given url
532 void storeInformation(IndexedString url) {
534 QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator start = m_fileEnvironmentInformations.lowerBound(url);
535 QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator end = m_fileEnvironmentInformations.upperBound(url);
537 if(start == end)
538 return;
540 //Here we check whether any of the stored items have been changed. If none have been changed(all data is still constant),
541 //then we don't need to update.
542 bool allInfosConstant = true;
543 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = start; it != end; ++it) {
544 (*it)->aboutToSave();
545 if((*it)->d_func()->isDynamic())
546 allInfosConstant = false;
549 if(allInfosConstant && m_environmentInfo.findIndex(EnvironmentInformationRequest(url))) {
550 ///@todo Find out why sometimes the data is constant although it's not in the repository(might be leaking memory)
551 //None of the informations have data that is marked "dynamic". This means it hasn't been changed, and thus we don't
552 //need to save anything.
553 }else{
554 for(QMultiMap<IndexedString, ParsingEnvironmentFilePointer>::iterator it = start; it != end; ++it) {
555 ///@todo Don't do this, instead directly copy data from repository to repository
556 (*it)->makeDynamic(); //We have to make everything dynamic, so it survives when we remove the index
559 uint index = m_environmentInfo.findIndex(EnvironmentInformationRequest(url));
560 if(index)
561 m_environmentInfo.deleteItem(index); //Remove the previous item
563 Q_ASSERT(m_environmentInfo.findIndex(EnvironmentInformationRequest(url)) == 0);
565 //Insert the new item
566 m_environmentInfo.index(EnvironmentInformationRequest(url, start, end));
569 Q_ASSERT(m_environmentInfo.findIndex(EnvironmentInformationRequest(url)));
572 //Stores the information into the repository, and removes it from m_fileEnvironmentInformations
573 void unloadInformation(IndexedString url) {
574 storeInformation(url);
576 m_fileEnvironmentInformations.remove(url);
579 QMultiMap<IndexedString, ParsingEnvironmentFilePointer> m_fileEnvironmentInformations;
580 //Persistent version of m_fileEnvironmentInformations
581 ItemRepository<EnvironmentInformationItem, EnvironmentInformationRequest> m_environmentInfo;
584 void DUChainPrivate::addRecursiveImports(QSet<TopDUContext*>& contexts, TopDUContext* current) {
585 if(contexts.contains(current))
586 return;
587 contexts.insert(current);
588 for(RecursiveImports::const_iterator it = current->recursiveImports().constBegin(); it != current->recursiveImports().constEnd(); ++it)
589 addRecursiveImports(contexts, const_cast<TopDUContext*>(it.key()));
592 K_GLOBAL_STATIC(DUChainPrivate, sdDUChainPrivate)
595 DUChain::DUChain()
597 connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
599 connect(EditorIntegrator::notifier(), SIGNAL(documentAboutToBeDeleted(KTextEditor::Document*)), SLOT(documentAboutToBeDeleted(KTextEditor::Document*)));
600 if(ICore::self()) {
601 Q_ASSERT(ICore::self()->documentController());
602 connect(ICore::self()->documentController(), SIGNAL(documentLoadedPrepare(KDevelop::IDocument*)), this, SLOT(documentLoadedPrepare(KDevelop::IDocument*)));
603 connect(ICore::self()->documentController(), SIGNAL(documentActivated(KDevelop::IDocument*)), this, SLOT(documentActivated(KDevelop::IDocument*)));
607 DUChain::~DUChain()
611 DUChain* DUChain::self()
613 return sdDUChainPrivate->instance;
616 DUChainLock* DUChain::lock()
618 return &sdDUChainPrivate->lock;
621 QList<TopDUContext*> DUChain::allChains() const
623 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
624 QList<TopDUContext*> ret;
625 for(google::dense_hash_map<uint, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.begin(); it != sdDUChainPrivate->m_chainsByIndex.end(); ++it)
626 ret << (*it).second;
628 return ret;
631 void DUChain::updateContextEnvironment( TopDUContext* context, ParsingEnvironmentFile* file ) {
633 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
635 removeFromEnvironmentManager( context );
637 context->setParsingEnvironmentFile( file );
639 addToEnvironmentManager( context );
641 branchModified(context);
645 void DUChain::removeDocumentChain( TopDUContext* context )
647 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
649 uint index = context->ownIndex();
651 // kDebug(9505) << "duchain: removing document" << context->url().str();
652 google::dense_hash_map<uint, TopDUContext*>::iterator it = sdDUChainPrivate->m_chainsByIndex.find(index);
654 if (it != sdDUChainPrivate->m_chainsByIndex.end()) {
655 sdDUChainPrivate->m_chainsByUrl.remove(context->url(), context);
657 if (context->smartRange())
658 ICore::self()->languageController()->backgroundParser()->removeManagedTopRange(context->smartRange());
660 branchRemoved(context);
662 if(!context->isOnDisk())
663 removeFromEnvironmentManager(context);
665 context->deleteSelf();
667 sdDUChainPrivate->m_chainsByIndex.erase(it);
671 void DUChain::addDocumentChain( TopDUContext * chain )
673 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
675 // kDebug(9505) << "duchain: adding document" << chain->url().str() << " " << chain;
676 Q_ASSERT(chain);
677 if (chain->smartRange()) {
678 Q_ASSERT(!chain->smartRange()->parentRange());
679 ICore::self()->languageController()->backgroundParser()->addManagedTopRange(KUrl(chain->url().str()), chain->smartRange());
682 Q_ASSERT(sdDUChainPrivate->m_chainsByIndex.find(chain->ownIndex()) == sdDUChainPrivate->m_chainsByIndex.end());
684 sdDUChainPrivate->m_chainsByIndex.insert(std::make_pair(chain->ownIndex(), chain));
685 sdDUChainPrivate->m_chainsByUrl.insert(chain->url(), chain);
687 /* {
688 //This is just for debugging, and should be disabled later.
689 int realChainCount = 0;
690 int proxyChainCount = 0;
691 for(QMap<IdentifiedFile, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chains.begin(); it != sdDUChainPrivate->m_chains.end(); ++it) {
692 if((*it)->flags() & TopDUContext::ProxyContextFlag)
693 ++proxyChainCount;
694 else
695 ++realChainCount;
698 kDebug(9505) << "new count of real chains: " << realChainCount << " proxy-chains: " << proxyChainCount;
700 chain->setInDuChain(true);
701 addToEnvironmentManager(chain);
703 //contextChanged(0L, DUChainObserver::Addition, DUChainObserver::ChildContexts, chain);
705 KTextEditor::Document* doc = EditorIntegrator::documentForUrl(chain->url());
706 if(doc) {
707 //Make sure the context stays alive at least as long as the context is open
708 ReferencedTopDUContext ctx(chain);
709 sdDUChainPrivate->m_openDocumentContexts.insert(ctx);
712 branchAdded(chain);
715 void DUChain::addToEnvironmentManager( TopDUContext * chain ) {
716 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
718 ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
719 if( !file )
720 return; //We don't need to manage
722 sdDUChainPrivate->addEnvironmentInformation(chain->url(), file);
725 void DUChain::removeFromEnvironmentManager( TopDUContext * chain ) {
726 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
728 ParsingEnvironmentFilePointer file = chain->parsingEnvironmentFile();
729 if( !file )
730 return; //We don't need to manage
732 sdDUChainPrivate->removeEnvironmentInformation(chain->url(), file);
735 TopDUContext* DUChain::chainForDocument(const KUrl& document) const {
736 return chainForDocument(IndexedString(document.pathOrUrl()));
739 bool DUChain::isInMemory(uint topContextIndex) const {
740 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
742 google::dense_hash_map<uint, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.find(topContextIndex);
743 return it != sdDUChainPrivate->m_chainsByIndex.end();
746 IndexedString DUChain::urlForIndex(uint index) const {
747 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
748 google::dense_hash_map<uint, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.find(index);
749 if(it != sdDUChainPrivate->m_chainsByIndex.end())
750 return (*it).second->url();
751 return TopDUContextDynamicData::loadUrl(index);
755 TopDUContext* DUChain::chainForIndex(uint index) const {
757 DUChainPrivate* p = (sdDUChainPrivate.operator->());
758 if(p->m_destroyed)
759 return 0;
761 p->m_chainsMutex.lock();
763 google::dense_hash_map<uint, TopDUContext*>::const_iterator it = p->m_chainsByIndex.find(index);
764 if(it != p->m_chainsByIndex.end()) {
765 TopDUContext* ret = (*it).second;
766 p->m_chainsMutex.unlock();
767 return ret;
768 } else {
769 p->m_chainsMutex.unlock();
770 QSet<uint> loaded;
771 p->loadChain(index, loaded);
772 p->m_chainsMutex.lock();
774 it = p->m_chainsByIndex.find(index);
775 if(it != p->m_chainsByIndex.end()) {
776 TopDUContext* ret = (*it).second;
777 p->m_chainsMutex.unlock();
778 return ret;
779 } else {
780 p->m_chainsMutex.unlock();
781 return 0;
786 TopDUContext* DUChain::chainForDocument(const IndexedString& document) const
788 if(sdDUChainPrivate->m_destroyed)
789 return 0;
791 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
793 /* {
794 int count = 0;
795 QMap<IdentifiedFile, TopDUContext*>::Iterator it = sdDUChainPrivate->m_chains.lowerBound(document);
796 for( ; it != sdDUChainPrivate->m_chains.end() && it.key().url() == document.url(); ++it )
797 ++count;
798 if( count > 1 )
799 kDebug(9505) << "found " << count << " chains for " << document.url().str();
803 // Match any parsed version of this document
804 if(sdDUChainPrivate->m_chainsByUrl.contains(document))
805 return *sdDUChainPrivate->m_chainsByUrl.find(document);
807 //Eventually load an existing chain from disk
808 QList<ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
809 foreach(ParsingEnvironmentFilePointer file, list) {
810 if(isInMemory(file->indexedTopContext().index()))
811 return file->indexedTopContext().data();
813 if(!list.isEmpty())
814 return list[0]->topContext();
816 // kDebug(9505) << "No chain found for document " << document.toString();
818 return 0;
821 QList<TopDUContext*> DUChain::chainsForDocument(const KUrl& document) const
823 return chainsForDocument(IndexedString(document));
826 QList<TopDUContext*> DUChain::chainsForDocument(const IndexedString& document) const
828 QList<TopDUContext*> chains;
830 if(sdDUChainPrivate->m_destroyed)
831 return chains;
833 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
835 // Match all parsed versions of this document
836 for (QMultiMap<IndexedString, TopDUContext*>::Iterator it = sdDUChainPrivate->m_chainsByUrl.lowerBound(document); it != sdDUChainPrivate->m_chainsByUrl.constEnd(); ++it) {
837 if (it.key() == document)
838 chains << it.value();
839 else
840 break;
843 return chains;
846 TopDUContext* DUChain::chainForDocument( const KUrl& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
847 return chainForDocument( IndexedString(document), environment, onlyProxyContexts, noProxyContexts );
850 ParsingEnvironmentFilePointer DUChain::environmentFileForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
852 if(sdDUChainPrivate->m_destroyed)
853 return ParsingEnvironmentFilePointer();
855 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
857 QList< ParsingEnvironmentFilePointer> list = sdDUChainPrivate->getEnvironmentInformation(document);
858 QList< ParsingEnvironmentFilePointer>::const_iterator it = list.constBegin();
859 while(it != list.constEnd()) {
860 if(*it && (*it)->matchEnvironment(environment) && (!onlyProxyContexts || (*it)->isProxyContext()) && (!noProxyContexts || !(*it)->isProxyContext())) {
861 return *it;
863 ++it;
866 return ParsingEnvironmentFilePointer();
869 TopDUContext* DUChain::chainForDocument( const IndexedString& document, const ParsingEnvironment* environment, bool onlyProxyContexts, bool noProxyContexts ) const {
871 if(sdDUChainPrivate->m_destroyed)
872 return 0;
873 ParsingEnvironmentFilePointer envFile = environmentFileForDocument(document, environment, onlyProxyContexts, noProxyContexts);
874 if(envFile) {
875 return envFile->topContext();
876 }else{
877 return 0;
881 DUChainObserver* DUChain::notifier()
883 return sdDUChainPrivate->notifier;
886 void DUChain::branchAdded(DUContext* context)
888 emit sdDUChainPrivate->notifier->branchAdded(DUContextPointer(context));
891 void DUChain::branchModified(DUContext* context)
893 emit sdDUChainPrivate->notifier->branchModified(DUContextPointer(context));
896 void DUChain::branchRemoved(DUContext* context)
898 emit sdDUChainPrivate->notifier->branchRemoved(DUContextPointer(context));
901 QList<KUrl> DUChain::documents() const
903 QList<KUrl> ret;
904 for(google::dense_hash_map<uint, TopDUContext*>::const_iterator it = sdDUChainPrivate->m_chainsByIndex.begin(); it != sdDUChainPrivate->m_chainsByIndex.end(); ++it) {
905 ret << KUrl((*it).second->url().str());
908 return ret;
911 /*Q_SCRIPTABLE bool DUChain::updateContext(TopDUContext* topContext, TopDUContext::Features minFeatures, QObject* notifyReady) const
913 if( (topContext->features() & minFeatures) != minFeatures || (topContext->parsingEnvironmentFile() && topContext->parsingEnvironmentFile()->needsUpdate()) ) {
914 ICore::self()->languageController()->backgroundParser()->addUpdateJob(topContext, minFeatures, notifyReady);
915 return true;
916 }else{
917 //No update needed, or we don't know since there's no ParsingEnvironmentFile attached
918 return false;
922 void DUChain::documentActivated(KDevelop::IDocument* doc)
924 if(sdDUChainPrivate->m_destroyed)
925 return;
926 //Check whether the document has an attached environment-manager, and whether that one thinks the document needs to be updated.
927 //If yes, update it.
928 DUChainWriteLocker lock( DUChain::lock() );
929 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
930 TopDUContext* ctx = DUChainUtils::standardContextForUrl(doc->url());
931 if(ctx && ctx->parsingEnvironmentFile())
932 if(ctx->parsingEnvironmentFile()->needsUpdate())
933 ICore::self()->languageController()->backgroundParser()->addDocument(doc->url());
936 void DUChain::documentAboutToBeDeleted(KTextEditor::Document* doc)
938 if(sdDUChainPrivate->m_destroyed)
939 return;
940 QList<TopDUContext*> chains = chainsForDocument(doc->url());
942 EditorIntegrator editor;
943 SmartConverter sc(&editor);
945 foreach (TopDUContext* top, chains) {
946 DUChainWriteLocker lock( DUChain::lock() );
947 sc.deconvertDUChain( top );
950 foreach(ReferencedTopDUContext top, sdDUChainPrivate->m_openDocumentContexts) {
951 if(top->url().str() == doc->url().pathOrUrl())
952 sdDUChainPrivate->m_openDocumentContexts.remove(top);
956 void DUChain::documentLoadedPrepare(KDevelop::IDocument* doc)
958 if(sdDUChainPrivate->m_destroyed)
959 return;
960 DUChainWriteLocker lock( DUChain::lock() );
961 QMutexLocker l(&sdDUChainPrivate->m_chainsMutex);
963 // Convert any duchains to the smart equivalent first
964 EditorIntegrator editor;
965 if(doc->textDocument())
966 editor.insertLoadedDocument(doc->textDocument()); //Make sure the editor-integrator knows the document
968 TopDUContext* standardContext = DUChainUtils::standardContextForUrl(doc->url());
969 QList<TopDUContext*> chains = chainsForDocument(doc->url());
971 QList<KDevelop::ILanguage*> languages = ICore::self()->languageController()->languagesForUrl(doc->url());
973 if(standardContext) {
974 Q_ASSERT(chains.contains(standardContext)); //We have just loaded it
977 ///Make the standard-context editor-smart
978 SmartConverter sc(&editor);
979 if(!standardContext->smartRange()) {
980 sc.convertDUChain(standardContext);
981 Q_ASSERT(standardContext->smartRange());
982 }else{
983 kWarning() << "Strange: context already has smart-range";
985 ICore::self()->languageController()->backgroundParser()->addManagedTopRange(doc->url(), standardContext->smartRange());
988 sdDUChainPrivate->m_openDocumentContexts.insert(standardContext);
990 foreach( KDevelop::ILanguage* language, languages)
991 if(language->languageSupport() && language->languageSupport()->codeHighlighting())
992 language->languageSupport()->codeHighlighting()->highlightDUChain(standardContext);
994 if(!standardContext->smartRange() || (standardContext->parsingEnvironmentFile() && standardContext->parsingEnvironmentFile()->needsUpdate()) || (standardContext->features() != TopDUContext::AllDeclarationsContextsAndUses))
995 ICore::self()->languageController()->backgroundParser()->addDocument(doc->url(), TopDUContext::AllDeclarationsContextsAndUses);
996 }else{
997 ICore::self()->languageController()->backgroundParser()->addDocument(doc->url(), TopDUContext::AllDeclarationsContextsAndUses);
1001 Uses* DUChain::uses()
1003 return &sdDUChainPrivate->m_uses;
1006 Definitions* DUChain::definitions()
1008 return &sdDUChainPrivate->m_definitions;
1011 void DUChain::aboutToQuit()
1013 sdDUChainPrivate->doMoreCleanup();;
1014 DUChainWriteLocker writeLock(lock());
1015 sdDUChainPrivate->m_openDocumentContexts.clear();
1016 sdDUChainPrivate->clear();
1017 sdDUChainPrivate->m_destroyed = true;
1020 uint DUChain::newTopContextIndex() {
1021 static QAtomicInt& currentId( globalItemRepositoryRegistry().getCustomCounter("Top-Context Counter", 1) );
1022 return currentId.fetchAndAddRelaxed(1);
1025 void DUChain::refCountUp(TopDUContext* top) {
1026 if(!sdDUChainPrivate->m_referenceCounts.contains(top))
1027 sdDUChainPrivate->m_referenceCounts.insert(top, 1);
1028 else
1029 ++sdDUChainPrivate->m_referenceCounts[top];
1032 void DUChain::refCountDown(TopDUContext* top) {
1033 if(!sdDUChainPrivate->m_referenceCounts.contains(top)) {
1034 kWarning() << "tried to decrease reference-count for" << top->url().str() << "but this top-context is not referenced";
1035 return;
1037 --sdDUChainPrivate->m_referenceCounts[top];
1038 if(!sdDUChainPrivate->m_referenceCounts[top])
1039 sdDUChainPrivate->m_referenceCounts.remove(top);
1042 void DUChain::emitDeclarationSelected(DeclarationPointer decl) {
1043 emit declarationSelected(decl);
1046 void DUChain::updateContextForUrl(const IndexedString& document, TopDUContext::Features minFeatures, QObject* notifyReady) const {
1047 TopDUContext* standardContext = DUChainUtils::standardContextForUrl(document.toUrl());
1048 if(standardContext && standardContext->parsingEnvironmentFile() && !standardContext->parsingEnvironmentFile()->needsUpdate() && (standardContext->features() & minFeatures) == minFeatures) {
1049 if(notifyReady)
1050 QMetaObject::invokeMethod(notifyReady, "updateReady", Qt::DirectConnection, Q_ARG(KDevelop::IndexedString, document), Q_ARG(KDevelop::ReferencedTopDUContext, ReferencedTopDUContext(standardContext)));
1051 }else{
1052 ///Start a parse-job for the given document
1053 ICore::self()->languageController()->backgroundParser()->addDocument(document.toUrl(), minFeatures, 1, notifyReady);
1059 #include "duchain.moc"
1061 // kate: space-indent on; indent-width 2; tab-width 4; replace-tabs on; auto-insert-doxygen on