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.
21 #include "duchainlock.h"
23 #include <QCoreApplication>
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"
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>
59 #include <qwaitcondition.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;
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
{
82 EnvironmentInformationItem() {
83 initializeAppendedLists(true);
86 ~EnvironmentInformationItem() {
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.
96 unsigned short int itemSize() const {
98 FOREACH_FUNCTION(uint size
, sizes
)
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
{
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
) {
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 {
138 for(QMultiMap
<IndexedString
, ParsingEnvironmentFilePointer
>::iterator info
= m_start
; info
!= m_end
; ++info
) {
139 dataSize
+= DUChainItemSystem::self().dynamicSize( *(*info
)->d_func() );
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
);
153 for(QMultiMap
<IndexedString
, ParsingEnvironmentFilePointer
>::iterator info
= m_start
; info
!= m_end
; ++info
) {
154 *(uint
*)pos
= DUChainItemSystem::self().dynamicSize( *(*info
)->d_func() );
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;
180 class CleanupThread
: public QThread
{
182 CleanupThread(DUChainPrivate
* data
) : m_stopRunning(false), m_data(data
) {
186 m_stopRunning
= true;
187 m_wait
.wakeAll(); //Wakes the thread up, so it notices it should exit
195 m_wait
.wait(&m_waitMutex
, 1000 * 90); //Wait 90s by default
196 m_waitMutex
.unlock();
199 m_data
->doMoreCleanup(SOFT_CLEANUP_STEPS
);
205 QWaitCondition m_wait
;
207 DUChainPrivate
* m_data
;
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);
231 m_cleanup
->stopThread();
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
;
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
;
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
;
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
);
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());
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);
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
));
339 m_environmentInfo
.itemFromIndex(index
);
341 kDebug(9505) << "Did not find stored item for" << url
.str() << "count:" << m_fileEnvironmentInformations
.values(url
);
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();
374 //Here we wait for all parsing-threads to stop their processing
375 foreach(ILanguage
* language
, lockedParseMutexes
)
376 language
->lockAllParseMutexes();
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();
399 //Eventually give other threads a chance to access the duchain
401 //Sleep to give the other threads a realistic chance to get a read-lock in between
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.
420 int hadUnloadable
= 0;
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
);
436 ++hadUnloadable
; //We have found a context that is not referenced
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
)
446 unloadedNames
.insert(unload
->url());
447 instance
->removeDocumentChain(unload
);
448 workOnContexts
.remove(unload
);
451 if(!unloadAllUnreferenced
) {
452 //Eventually give other threads a chance to access the duchain
454 //Sleep to give the other threads a realistic chance to get a read-lock in between
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;
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
);
474 //Eventually give other threads a chance to access the duchain
476 //Sleep to give the other threads a realistic chance to get a read-lock in between
485 globalItemRepositoryRegistry().store(); //Stores all repositories
488 doMoreCleanup(retries
-1, false);
492 if(needLockRepository
) {
493 globalItemRepositoryRegistry().unlockForWriting();
495 int elapsedSeconds
= startTime
.secsTo(QTime::currentTime());
496 kDebug(9505) << "seconds spent doing cleanup: " << elapsedSeconds
;
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();
508 void addRecursiveImports(QSet
<TopDUContext
*>& contexts
, TopDUContext
* current
);
510 void loadInformation(IndexedString url
) {
511 if(m_fileEnvironmentInformations
.find(url
) != m_fileEnvironmentInformations
.end())
514 uint index
= m_environmentInfo
.findIndex(EnvironmentInformationRequest(url
));
516 //No information there yet for this file
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
)));
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
);
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.
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
));
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
))
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
)
597 connect(QCoreApplication::instance(), SIGNAL(aboutToQuit()), this, SLOT(aboutToQuit()));
599 connect(EditorIntegrator::notifier(), SIGNAL(documentAboutToBeDeleted(KTextEditor::Document
*)), SLOT(documentAboutToBeDeleted(KTextEditor::Document
*)));
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
*)));
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
)
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;
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
);
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)
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());
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
);
715 void DUChain::addToEnvironmentManager( TopDUContext
* chain
) {
716 QMutexLocker
l(&sdDUChainPrivate
->m_chainsMutex
);
718 ParsingEnvironmentFilePointer file
= chain
->parsingEnvironmentFile();
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();
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->());
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();
769 p
->m_chainsMutex
.unlock();
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();
780 p
->m_chainsMutex
.unlock();
786 TopDUContext
* DUChain::chainForDocument(const IndexedString
& document
) const
788 if(sdDUChainPrivate
->m_destroyed
)
791 QMutexLocker
l(&sdDUChainPrivate
->m_chainsMutex
);
795 QMap<IdentifiedFile, TopDUContext*>::Iterator it = sdDUChainPrivate->m_chains.lowerBound(document);
796 for( ; it != sdDUChainPrivate->m_chains.end() && it.key().url() == document.url(); ++it )
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();
814 return list
[0]->topContext();
816 // kDebug(9505) << "No chain found for document " << document.toString();
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
)
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();
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())) {
866 return ParsingEnvironmentFilePointer();
869 TopDUContext
* DUChain::chainForDocument( const IndexedString
& document
, const ParsingEnvironment
* environment
, bool onlyProxyContexts
, bool noProxyContexts
) const {
871 if(sdDUChainPrivate
->m_destroyed
)
873 ParsingEnvironmentFilePointer envFile
= environmentFileForDocument(document
, environment
, onlyProxyContexts
, noProxyContexts
);
875 return envFile
->topContext();
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
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());
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);
917 //No update needed, or we don't know since there's no ParsingEnvironmentFile attached
922 void DUChain::documentActivated(KDevelop::IDocument
* doc
)
924 if(sdDUChainPrivate
->m_destroyed
)
926 //Check whether the document has an attached environment-manager, and whether that one thinks the document needs to be updated.
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
)
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
)
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());
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
);
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);
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";
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
) {
1050 QMetaObject::invokeMethod(notifyReady
, "updateReady", Qt::DirectConnection
, Q_ARG(KDevelop::IndexedString
, document
), Q_ARG(KDevelop::ReferencedTopDUContext
, ReferencedTopDUContext(standardContext
)));
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