File indexing completed on 2024-04-21 15:24:16
0001 /* 0002 SPDX-FileCopyrightText: 2008 David Nolden <david.nolden.kdevelop@art-master.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-only 0005 */ 0006 0007 #include "completioncodemodel.h" 0008 0009 #include <QHash> 0010 #include <QVector> 0011 0012 #include <language/duchain/appendedlist.h> 0013 #include <serialization/itemrepository.h> 0014 #include <language/duchain/identifier.h> 0015 #include <serialization/indexedstring.h> 0016 #include <util/embeddedfreetree.h> 0017 #include <serialization/referencecounting.h> 0018 #include "duchaindebug.h" 0019 0020 #define ifDebug(x) 0021 0022 namespace Php { 0023 0024 class CompletionCodeModelItemHandler { 0025 public: 0026 static int leftChild(const CompletionCodeModelItem& m_data) { 0027 return (int)m_data.referenceCount; 0028 } 0029 static void setLeftChild(CompletionCodeModelItem& m_data, int child) { 0030 m_data.referenceCount = (uint)child; 0031 } 0032 static int rightChild(const CompletionCodeModelItem& m_data) { 0033 return (int)m_data.uKind; 0034 } 0035 static void setRightChild(CompletionCodeModelItem& m_data, int child) { 0036 m_data.uKind = (uint)child; 0037 } 0038 //Copies this item into the given one 0039 static void copyTo(const CompletionCodeModelItem& m_data, CompletionCodeModelItem& data) { 0040 data = m_data; 0041 } 0042 0043 static void createFreeItem(CompletionCodeModelItem& data) { 0044 data = CompletionCodeModelItem(); 0045 data.referenceCount = (uint)-1; 0046 data.uKind = (uint)-1; 0047 } 0048 0049 static bool isFree(const CompletionCodeModelItem& m_data) { 0050 return !m_data.id.isValid(); 0051 } 0052 0053 static const CompletionCodeModelItem& data(const CompletionCodeModelItem& m_data) { 0054 return m_data; 0055 } 0056 0057 static bool equals(const CompletionCodeModelItem& m_data, const CompletionCodeModelItem& rhs) { 0058 return m_data.id == rhs.id; 0059 } 0060 }; 0061 0062 0063 DEFINE_LIST_MEMBER_HASH(CompletionCodeModelRepositoryItem, items, CompletionCodeModelItem) 0064 0065 class CompletionCodeModelRepositoryItem { 0066 public: 0067 CompletionCodeModelRepositoryItem() : centralFreeItem(-1) { 0068 initializeAppendedLists(); 0069 } 0070 CompletionCodeModelRepositoryItem(const CompletionCodeModelRepositoryItem& rhs, bool dynamic = true) : file(rhs.file), centralFreeItem(rhs.centralFreeItem) { 0071 initializeAppendedLists(dynamic); 0072 copyListsFrom(rhs); 0073 } 0074 0075 ~CompletionCodeModelRepositoryItem() { 0076 freeAppendedLists(); 0077 } 0078 0079 unsigned int hash() const { 0080 //We only compare the declaration. This allows us implementing a map, although the item-repository 0081 //originally represents a set. 0082 return file.index(); 0083 } 0084 0085 uint itemSize() const { 0086 return dynamicSize(); 0087 } 0088 0089 uint classSize() const { 0090 return sizeof(CompletionCodeModelRepositoryItem); 0091 } 0092 0093 KDevelop::IndexedString file; 0094 int centralFreeItem; 0095 0096 START_APPENDED_LISTS(CompletionCodeModelRepositoryItem); 0097 APPENDED_LIST_FIRST(CompletionCodeModelRepositoryItem, CompletionCodeModelItem, items); 0098 END_APPENDED_LISTS(CompletionCodeModelRepositoryItem, items); 0099 }; 0100 0101 class CodeModelRequestItem { 0102 public: 0103 0104 CodeModelRequestItem(const CompletionCodeModelRepositoryItem& item) : m_item(item) { 0105 } 0106 enum { 0107 AverageSize = 30+8 //This should be the approximate average size of an Item 0108 }; 0109 0110 unsigned int hash() const { 0111 return m_item.hash(); 0112 } 0113 0114 uint itemSize() const { 0115 return m_item.itemSize(); 0116 } 0117 0118 void createItem(CompletionCodeModelRepositoryItem* item) const { 0119 Q_ASSERT(KDevelop::shouldDoDUChainReferenceCounting(item)); 0120 Q_ASSERT(KDevelop::shouldDoDUChainReferenceCounting(((char*)item) + (itemSize()-1))); 0121 new (item) CompletionCodeModelRepositoryItem(m_item, false); 0122 } 0123 0124 static void destroy(CompletionCodeModelRepositoryItem* item, KDevelop::AbstractItemRepository&) { 0125 Q_ASSERT(KDevelop::shouldDoDUChainReferenceCounting(item)); 0126 // Q_ASSERT(shouldDoDUChainReferenceCounting(((char*)item) + (itemSize()-1))); 0127 item->~CompletionCodeModelRepositoryItem(); 0128 } 0129 0130 static bool persistent(const CompletionCodeModelRepositoryItem* item) { 0131 Q_UNUSED(item); 0132 return true; 0133 } 0134 0135 bool equals(const CompletionCodeModelRepositoryItem* item) const { 0136 return m_item.file == item->file; 0137 } 0138 0139 const CompletionCodeModelRepositoryItem& m_item; 0140 }; 0141 0142 // Maps declaration-ids to items 0143 using CompletionCodeModelRepo = KDevelop::ItemRepository<Php::CompletionCodeModelRepositoryItem, Php::CodeModelRequestItem>; 0144 } 0145 0146 namespace KDevelop { 0147 template <> 0148 class ItemRepositoryFor<Php::CompletionCodeModel> 0149 { 0150 friend struct LockedItemRepository; 0151 static Php::CompletionCodeModelRepo& repo() 0152 { 0153 static QMutex mutex; 0154 static Php::CompletionCodeModelRepo repo(QStringLiteral("Php Completion Code Model"), &mutex); 0155 return repo; 0156 } 0157 public: 0158 static void init() 0159 { 0160 repo(); 0161 } 0162 }; 0163 } 0164 0165 namespace Php { 0166 CompletionCodeModel::CompletionCodeModel() 0167 { 0168 KDevelop::ItemRepositoryFor<CompletionCodeModel>::init(); 0169 } 0170 0171 CompletionCodeModel::~CompletionCodeModel() = default; 0172 0173 void CompletionCodeModel::addItem(const KDevelop::IndexedString& file, const KDevelop::IndexedQualifiedIdentifier& id, const KDevelop::IndexedString & prettyName, CompletionCodeModelItem::Kind kind) 0174 { 0175 ifDebug( qCDebug(DUCHAIN) << "addItem" << file.str() << id.identifier().toString() << id.index; ) 0176 0177 if(!id.isValid()) 0178 return; 0179 CompletionCodeModelRepositoryItem item; 0180 item.file = file; 0181 CodeModelRequestItem request(item); 0182 0183 KDevelop::LockedItemRepository::write<CompletionCodeModel>([&](CompletionCodeModelRepo& repo) { 0184 uint index = repo.findIndex(item); 0185 0186 CompletionCodeModelItem newItem; 0187 newItem.id = id; 0188 newItem.kind = kind; 0189 newItem.prettyName = prettyName; 0190 newItem.referenceCount = 1; 0191 0192 if(index) { 0193 const CompletionCodeModelRepositoryItem* oldItem = repo.itemFromIndex(index); 0194 KDevelop::EmbeddedTreeAlgorithms<CompletionCodeModelItem, CompletionCodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem); 0195 0196 int listIndex = alg.indexOf(newItem); 0197 0198 KDevelop::DynamicItem<CompletionCodeModelRepositoryItem, true> editableItem = repo.dynamicItemFromIndex(index); 0199 CompletionCodeModelItem* items = const_cast<CompletionCodeModelItem*>(editableItem->items()); 0200 0201 if(listIndex != -1) { 0202 //Only update the reference-count 0203 ++items[listIndex].referenceCount; 0204 items[listIndex].kind = kind; 0205 items[listIndex].prettyName = prettyName; 0206 return; 0207 }else{ 0208 //Add the item to the list 0209 KDevelop::EmbeddedTreeAddItem<CompletionCodeModelItem, CompletionCodeModelItemHandler> add(items, editableItem->itemsSize(), editableItem->centralFreeItem, newItem); 0210 0211 if(add.newItemCount() != editableItem->itemsSize()) { 0212 //The data needs to be transferred into a bigger list. That list is within "item". 0213 0214 item.itemsList().resize(add.newItemCount()); 0215 add.transferData(item.itemsList().data(), item.itemsList().size(), &item.centralFreeItem); 0216 0217 repo.deleteItem(index); 0218 }else{ 0219 //We're fine: The item fits into the existing list. 0220 return; 0221 } 0222 } 0223 }else{ 0224 //We're creating a new index 0225 item.itemsList().append(newItem); 0226 } 0227 0228 Q_ASSERT(!repo.findIndex(request)); 0229 0230 //This inserts the changed item 0231 volatile uint newIndex = repo.index(request); 0232 Q_UNUSED(newIndex); 0233 ifDebug( qCDebug(DUCHAIN) << "new index" << newIndex; ) 0234 0235 Q_ASSERT(repo.findIndex(request)); 0236 }); 0237 } 0238 0239 void CompletionCodeModel::updateItem(const KDevelop::IndexedString& file, const KDevelop::IndexedQualifiedIdentifier& id, const KDevelop::IndexedString & prettyName, CompletionCodeModelItem::Kind kind) 0240 { 0241 ifDebug( qCDebug(DUCHAIN) << file.str() << id.identifier().toString() << kind; ) 0242 0243 if(!id.isValid()) 0244 return; 0245 0246 CompletionCodeModelRepositoryItem item; 0247 item.file = file; 0248 CodeModelRequestItem request(item); 0249 0250 CompletionCodeModelItem newItem; 0251 newItem.id = id; 0252 newItem.kind = kind; 0253 newItem.prettyName = prettyName; 0254 newItem.referenceCount = 1; 0255 0256 KDevelop::LockedItemRepository::write<CompletionCodeModel>([&](CompletionCodeModelRepo& repo) { 0257 uint index = repo.findIndex(item); 0258 0259 if(index) { 0260 //Check whether the item is already in the mapped list, else copy the list into the new created item 0261 KDevelop::DynamicItem<CompletionCodeModelRepositoryItem, true> oldItem = repo.dynamicItemFromIndex(index); 0262 0263 KDevelop::EmbeddedTreeAlgorithms<CompletionCodeModelItem, CompletionCodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem); 0264 int listIndex = alg.indexOf(newItem); 0265 Q_ASSERT(listIndex != -1); 0266 0267 CompletionCodeModelItem* items = const_cast<CompletionCodeModelItem*>(oldItem->items()); 0268 0269 Q_ASSERT(items[listIndex].id == id); 0270 items[listIndex].kind = kind; 0271 items[listIndex].prettyName = prettyName; 0272 0273 return; 0274 } 0275 0276 Q_ASSERT(0); //The updated item was not in the symbol table! 0277 }); 0278 } 0279 0280 void CompletionCodeModel::removeItem(const KDevelop::IndexedString& file, const KDevelop::IndexedQualifiedIdentifier& id) 0281 { 0282 if(!id.isValid()) 0283 return; 0284 ifDebug( qCDebug(DUCHAIN) << "removeItem" << file.str() << id.identifier().toString(); ) 0285 CompletionCodeModelRepositoryItem item; 0286 item.file = file; 0287 CodeModelRequestItem request(item); 0288 0289 KDevelop::LockedItemRepository::write<CompletionCodeModel>([&](CompletionCodeModelRepo& repo) { 0290 uint index = repo.findIndex(item); 0291 0292 if(index) { 0293 0294 CompletionCodeModelItem searchItem; 0295 searchItem.id = id; 0296 0297 KDevelop::DynamicItem<CompletionCodeModelRepositoryItem, true> oldItem = repo.dynamicItemFromIndex(index); 0298 0299 KDevelop::EmbeddedTreeAlgorithms<CompletionCodeModelItem, CompletionCodeModelItemHandler> alg(oldItem->items(), oldItem->itemsSize(), oldItem->centralFreeItem); 0300 0301 int listIndex = alg.indexOf(searchItem); 0302 if(listIndex == -1) 0303 return; 0304 0305 CompletionCodeModelItem* items = const_cast<CompletionCodeModelItem*>(oldItem->items()); 0306 0307 --items[listIndex].referenceCount; 0308 0309 if(oldItem->items()[listIndex].referenceCount) 0310 return; //Nothing to remove, there's still a reference-count left 0311 0312 //We have reduced the reference-count to zero, so remove the item from the list 0313 0314 KDevelop::EmbeddedTreeRemoveItem<CompletionCodeModelItem, CompletionCodeModelItemHandler> remove(items, oldItem->itemsSize(), oldItem->centralFreeItem, searchItem); 0315 0316 uint newItemCount = remove.newItemCount(); 0317 if(newItemCount != oldItem->itemsSize()) { 0318 if(newItemCount == 0) { 0319 //Has become empty, delete the item 0320 repo.deleteItem(index); 0321 return; 0322 }else{ 0323 //Make smaller 0324 item.itemsList().resize(newItemCount); 0325 remove.transferData(item.itemsList().data(), item.itemsSize(), &item.centralFreeItem); 0326 0327 //Delete the old list 0328 repo.deleteItem(index); 0329 //Add the new list 0330 repo.index(request); 0331 return; 0332 } 0333 } 0334 } 0335 }); 0336 } 0337 0338 void CompletionCodeModel::items(const KDevelop::IndexedString& file, uint& count, const CompletionCodeModelItem*& items) const 0339 { 0340 ifDebug( qCDebug(DUCHAIN) << "items" << file.str(); ) 0341 0342 CompletionCodeModelRepositoryItem item; 0343 item.file = file; 0344 CodeModelRequestItem request(item); 0345 0346 KDevelop::LockedItemRepository::read<CompletionCodeModel>([&](const CompletionCodeModelRepo& repo) { 0347 uint index = repo.findIndex(item); 0348 0349 if(index) { 0350 const CompletionCodeModelRepositoryItem* repositoryItem = repo.itemFromIndex(index); 0351 ifDebug( qCDebug(DUCHAIN) << "found index" << index << repositoryItem->itemsSize(); ) 0352 count = repositoryItem->itemsSize(); 0353 items = repositoryItem->items(); 0354 }else{ 0355 ifDebug( qCDebug(DUCHAIN) << "found no index"; ) 0356 count = 0; 0357 items = nullptr; 0358 } 0359 }); 0360 } 0361 0362 CompletionCodeModel& CompletionCodeModel::self() { 0363 static CompletionCodeModel ret; 0364 return ret; 0365 } 0366 0367 } 0368