diff --git a/language/duchain/duchainutils.cpp b/language/duchain/duchainutils.cpp index 83f8d71646..32381f238d 100644 --- a/language/duchain/duchainutils.cpp +++ b/language/duchain/duchainutils.cpp @@ -1,627 +1,627 @@ /* * DUChain Utilities * * Copyright 2007 Hamish Rodda * Copyright 2007-2008 David Nolden * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU Library General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public * License along with this program; if not, write to the * Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "duchainutils.h" #include #include #include #include "../interfaces/ilanguagesupport.h" #include "../assistant/staticassistantsmanager.h" #include "util/debug.h" #include "declaration.h" #include "classfunctiondeclaration.h" #include "ducontext.h" #include "duchain.h" #include "use.h" #include "duchainlock.h" #include "classmemberdeclaration.h" #include "functiondefinition.h" #include "specializationstore.h" #include "persistentsymboltable.h" #include "classdeclaration.h" #include "parsingenvironment.h" #include using namespace KDevelop; using namespace KTextEditor; CodeCompletionModel::CompletionProperties DUChainUtils::completionProperties(const Declaration* dec) { CodeCompletionModel::CompletionProperties p; if(dec->context()->type() == DUContext::Class) { if (const ClassMemberDeclaration* member = dynamic_cast(dec)) { switch (member->accessPolicy()) { case Declaration::Public: p |= CodeCompletionModel::Public; break; case Declaration::Protected: p |= CodeCompletionModel::Protected; break; case Declaration::Private: p |= CodeCompletionModel::Private; break; default: break; } if (member->isStatic()) p |= CodeCompletionModel::Static; if (member->isAuto()) {}//TODO if (member->isFriend()) p |= CodeCompletionModel::Friend; if (member->isRegister()) {}//TODO if (member->isExtern()) {}//TODO if (member->isMutable()) {}//TODO } } if (const AbstractFunctionDeclaration* function = dynamic_cast(dec)) { p |= CodeCompletionModel::Function; if (function->isVirtual()) p |= CodeCompletionModel::Virtual; if (function->isInline()) p |= CodeCompletionModel::Inline; if (function->isExplicit()) {}//TODO } if( dec->isTypeAlias() ) p |= CodeCompletionModel::TypeAlias; if (dec->abstractType()) { switch (dec->abstractType()->whichType()) { case AbstractType::TypeIntegral: p |= CodeCompletionModel::Variable; break; case AbstractType::TypePointer: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeReference: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeFunction: p |= CodeCompletionModel::Function; break; case AbstractType::TypeStructure: p |= CodeCompletionModel::Class; break; case AbstractType::TypeArray: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeEnumeration: p |= CodeCompletionModel::Enum; break; case AbstractType::TypeEnumerator: p |= CodeCompletionModel::Variable; break; case AbstractType::TypeAbstract: case AbstractType::TypeDelayed: case AbstractType::TypeUnsure: case AbstractType::TypeAlias: // TODO break; } if( dec->abstractType()->modifiers() & AbstractType::ConstModifier ) p |= CodeCompletionModel::Const; if( dec->kind() == Declaration::Instance && !dec->isFunctionDeclaration() ) p |= CodeCompletionModel::Variable; } if (dec->context()) { if( dec->context()->type() == DUContext::Global ) p |= CodeCompletionModel::GlobalScope; else if( dec->context()->type() == DUContext::Namespace ) p |= CodeCompletionModel::NamespaceScope; else if( dec->context()->type() != DUContext::Class && dec->context()->type() != DUContext::Enum ) p |= CodeCompletionModel::LocalScope; } return p; } /**We have to construct the item from the pixmap, else the icon will be marked as "load on demand", * and for some reason will be loaded every time it's used(this function returns a QIcon marked "load on demand" * each time this is called). And the loading is very slow. Seems like a bug somewhere, it cannot be ment to be that slow. */ #define RETURN_CACHED_ICON(name) {static QIcon icon(QIcon( \ QStandardPaths::locate(QStandardPaths::GenericDataLocation, QStringLiteral("kdevelop/pics/" name ".png"))\ ).pixmap(QSize(16, 16)));\ return icon;} QIcon DUChainUtils::iconForProperties(KTextEditor::CodeCompletionModel::CompletionProperties p) { if( (p & CodeCompletionModel::Variable) ) if( (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("CVprotected_var") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("CVprivate_var") else RETURN_CACHED_ICON("CVpublic_var") else if( (p & CodeCompletionModel::Union) && (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("protected_union") else if( p & CodeCompletionModel::Enum ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_enum") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_enum") else RETURN_CACHED_ICON("enum") else if( p & CodeCompletionModel::Struct ) if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_struct") else RETURN_CACHED_ICON("struct") else if( p & CodeCompletionModel::Slot ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("CVprotected_slot") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("CVprivate_slot") else if(p & CodeCompletionModel::Public ) RETURN_CACHED_ICON("CVpublic_slot") else RETURN_CACHED_ICON("slot") else if( p & CodeCompletionModel::Signal ) if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("CVprotected_signal") else RETURN_CACHED_ICON("signal") else if( p & CodeCompletionModel::Class ) if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Protected) ) RETURN_CACHED_ICON("protected_class") else if( (p & CodeCompletionModel::Class) && (p & CodeCompletionModel::Private) ) RETURN_CACHED_ICON("private_class") else RETURN_CACHED_ICON("code-class") else if( p & CodeCompletionModel::Union ) if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_union") else RETURN_CACHED_ICON("union") else if( p & CodeCompletionModel::TypeAlias ) if ((p & CodeCompletionModel::Const) /*|| (p & CodeCompletionModel::Volatile)*/) RETURN_CACHED_ICON("CVtypedef") else RETURN_CACHED_ICON("typedef") else if( p & CodeCompletionModel::Function ) { if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_function") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_function") else RETURN_CACHED_ICON("code-function") } if( p & CodeCompletionModel::Protected ) RETURN_CACHED_ICON("protected_field") else if( p & CodeCompletionModel::Private ) RETURN_CACHED_ICON("private_field") else RETURN_CACHED_ICON("field") return QIcon(); } QIcon DUChainUtils::iconForDeclaration(const Declaration* dec) { return iconForProperties(completionProperties(dec)); } TopDUContext* DUChainUtils::contentContextFromProxyContext(TopDUContext* top) { if(!top) return 0; if(top->parsingEnvironmentFile() && top->parsingEnvironmentFile()->isProxyContext()) { if(!top->importedParentContexts().isEmpty()) { DUContext* ctx = top->importedParentContexts().at(0).context(0); if(!ctx) return 0; TopDUContext* ret = ctx->topContext(); if(!ret) return 0; if(ret->url() != top->url()) qCDebug(LANGUAGE) << "url-mismatch between content and proxy:" << top->url().toUrl() << ret->url().toUrl(); if(ret->url() == top->url() && !ret->parsingEnvironmentFile()->isProxyContext()) return ret; } else { qCDebug(LANGUAGE) << "Proxy-context imports no content-context"; } } else return top; return 0; } TopDUContext* DUChainUtils::standardContextForUrl(const QUrl& url, bool preferProxyContext) { KDevelop::TopDUContext* chosen = 0; auto languages = ICore::self()->languageController()->languagesForUrl(url); foreach(const auto language, languages) { if(!chosen) { chosen = language->standardContext(url, preferProxyContext); } } if(!chosen) chosen = DUChain::self()->chainForDocument(IndexedString(url), preferProxyContext); if(!chosen && preferProxyContext) return standardContextForUrl(url, false); // Fall back to a normal context return chosen; } struct ItemUnderCursorInternal { Declaration* declaration; DUContext* context; RangeInRevision range; }; ItemUnderCursorInternal itemUnderCursorInternal(const CursorInRevision& c, DUContext* ctx, RangeInRevision::ContainsBehavior behavior) { //Search all collapsed sub-contexts. In C++, those can contain declarations that have ranges out of the context foreach(DUContext* subCtx, ctx->childContexts()) { //This is a little hacky, but we need it in case of foreach macros and similar stuff if(subCtx->range().contains(c, behavior) || subCtx->range().isEmpty() || subCtx->range().start.line == c.line || subCtx->range().end.line == c.line) { ItemUnderCursorInternal sub = itemUnderCursorInternal(c, subCtx, behavior); if(sub.declaration) { return sub; } } } foreach(Declaration* decl, ctx->localDeclarations()) { if(decl->range().contains(c, behavior)) { return {decl, ctx, decl->range()}; } } //Try finding a use under the cursor for(int a = 0; a < ctx->usesCount(); ++a) { if(ctx->uses()[a].m_range.contains(c, behavior)) { return {ctx->topContext()->usedDeclarationForIndex(ctx->uses()[a].m_declarationIndex), ctx, ctx->uses()[a].m_range}; } } return {nullptr, nullptr, RangeInRevision()}; } DUChainUtils::ItemUnderCursor DUChainUtils::itemUnderCursor(const QUrl& url, const KTextEditor::Cursor& cursor) { - KDevelop::TopDUContext* top = standardContextForUrl(url); + KDevelop::TopDUContext* top = standardContextForUrl(url.adjusted(QUrl::NormalizePathSegments)); if(!top) { return {nullptr, nullptr, KTextEditor::Range()}; } ItemUnderCursorInternal decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::Default); if (decl.declaration == nullptr) { decl = itemUnderCursorInternal(top->transformToLocalRevision(cursor), top, RangeInRevision::IncludeBackEdge); } return {decl.declaration, decl.context, top->transformFromLocalRevision(decl.range)}; } Declaration* DUChainUtils::declarationForDefinition(Declaration* definition, TopDUContext* topContext) { if(!definition) return 0; if(!topContext) topContext = definition->topContext(); if(dynamic_cast(definition)) { Declaration* ret = static_cast(definition)->declaration(); if(ret) return ret; } return definition; } Declaration* DUChainUtils::declarationInLine(const KTextEditor::Cursor& _cursor, DUContext* ctx) { if(!ctx) return 0; CursorInRevision cursor = ctx->transformToLocalRevision(_cursor); foreach(Declaration* decl, ctx->localDeclarations()) { if(decl->range().start.line == cursor.line) return decl; DUContext* funCtx = getFunctionContext(decl); if(funCtx && funCtx->range().contains(cursor)) return decl; } foreach(DUContext* child, ctx->childContexts()){ Declaration* decl = declarationInLine(_cursor, child); if(decl) return decl; } return 0; } DUChainUtils::DUChainItemFilter::~DUChainItemFilter() { } void DUChainUtils::collectItems( DUContext* context, DUChainItemFilter& filter ) { QVector children = context->childContexts(); QVector localDeclarations = context->localDeclarations(); QVector::const_iterator childIt = children.constBegin(); QVector::const_iterator declIt = localDeclarations.constBegin(); while(childIt != children.constEnd() || declIt != localDeclarations.constEnd()) { DUContext* child = 0; if(childIt != children.constEnd()) child = *childIt; Declaration* decl = 0; if(declIt != localDeclarations.constEnd()) decl = *declIt; if(decl) { if(child && child->range().start.line >= decl->range().start.line) child = 0; } if(child) { if(decl && decl->range().start >= child->range().start) decl = 0; } if(decl) { if( filter.accept(decl) ) { //Action is done in the filter } ++declIt; continue; } if(child) { if( filter.accept(child) ) collectItems(child, filter); ++childIt; continue; } } } KDevelop::DUContext* DUChainUtils::getArgumentContext(KDevelop::Declaration* decl) { DUContext* internal = decl->internalContext(); if( !internal ) return 0; if( internal->type() == DUContext::Function ) return internal; foreach( const DUContext::Import &ctx, internal->importedParentContexts() ) { if( ctx.context(decl->topContext()) ) if( ctx.context(decl->topContext())->type() == DUContext::Function ) return ctx.context(decl->topContext()); } return 0; } QList DUChainUtils::collectAllVersions(Declaration* decl) { QList ret; ret << IndexedDeclaration(decl); if(decl->inSymbolTable()) { uint count; const IndexedDeclaration* allDeclarations; PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations); for(uint a = 0; a < count; ++a) if(!(allDeclarations[a] == IndexedDeclaration(decl))) ret << allDeclarations[a]; } return ret; } static QList getInheritersInternal(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions) { QList ret; if(!dynamic_cast(decl)) return ret; if(maxAllowedSteps == 0) return ret; if(decl->internalContext() && decl->internalContext()->type() == DUContext::Class) { foreach (const IndexedDUContext importer, decl->internalContext()->indexedImporters()) { DUContext* imp = importer.data(); if(!imp) continue; if(imp->type() == DUContext::Class && imp->owner()) ret << imp->owner(); --maxAllowedSteps; if(maxAllowedSteps == 0) return ret; } } if(collectVersions && decl->inSymbolTable()) { uint count; const IndexedDeclaration* allDeclarations; PersistentSymbolTable::self().declarations(decl->qualifiedIdentifier(), count, allDeclarations); for(uint a = 0; a < count; ++a) { ++maxAllowedSteps; if(allDeclarations[a].data() && allDeclarations[a].data() != decl) { ret += getInheritersInternal(allDeclarations[a].data(), maxAllowedSteps, false); } if(maxAllowedSteps == 0) return ret; } } return ret; } QList DUChainUtils::getInheriters(const Declaration* decl, uint& maxAllowedSteps, bool collectVersions) { auto inheriters = getInheritersInternal(decl, maxAllowedSteps, collectVersions); // remove duplicates std::sort(inheriters.begin(), inheriters.end()); inheriters.erase(std::unique(inheriters.begin(), inheriters.end()), inheriters.end()); return inheriters; } QList DUChainUtils::getOverriders(const Declaration* currentClass, const Declaration* overriddenDeclaration, uint& maxAllowedSteps) { QList ret; if(maxAllowedSteps == 0) return ret; if(currentClass != overriddenDeclaration->context()->owner() && currentClass->internalContext()) ret += currentClass->internalContext()->findLocalDeclarations(overriddenDeclaration->identifier(), CursorInRevision::invalid(), currentClass->topContext(), overriddenDeclaration->abstractType()); foreach(Declaration* inheriter, getInheriters(currentClass, maxAllowedSteps)) ret += getOverriders(inheriter, overriddenDeclaration, maxAllowedSteps); return ret; } static bool hasUse(DUContext* context, int usedDeclarationIndex) { if(usedDeclarationIndex == std::numeric_limits::max()) return false; for(int a = 0; a < context->usesCount(); ++a) if(context->uses()[a].m_declarationIndex == usedDeclarationIndex) return true; foreach(DUContext* child, context->childContexts()) if(hasUse(child, usedDeclarationIndex)) return true; return false; } bool DUChainUtils::contextHasUse(DUContext* context, Declaration* declaration) { return hasUse(context, context->topContext()->indexForUsedDeclaration(declaration, false)); } static uint countUses(DUContext* context, int usedDeclarationIndex) { if(usedDeclarationIndex == std::numeric_limits::max()) return 0; uint ret = 0; for(int a = 0; a < context->usesCount(); ++a) if(context->uses()[a].m_declarationIndex == usedDeclarationIndex) ++ret; foreach(DUContext* child, context->childContexts()) ret += countUses(child, usedDeclarationIndex); return ret; } uint DUChainUtils::contextCountUses(DUContext* context, Declaration* declaration) { return countUses(context, context->topContext()->indexForUsedDeclaration(declaration, false)); } Declaration* DUChainUtils::getOverridden(const Declaration* decl) { const ClassFunctionDeclaration* classFunDecl = dynamic_cast(decl); if(!classFunDecl || !classFunDecl->isVirtual()) return 0; QList decls; foreach(const DUContext::Import &import, decl->context()->importedParentContexts()) { DUContext* ctx = import.context(decl->topContext()); if(ctx) decls += ctx->findDeclarations(QualifiedIdentifier(decl->identifier()), CursorInRevision::invalid(), decl->abstractType(), decl->topContext(), DUContext::DontSearchInParent); } foreach(Declaration* found, decls) { const ClassFunctionDeclaration* foundClassFunDecl = dynamic_cast(found); if(foundClassFunDecl && foundClassFunDecl->isVirtual()) return found; } return 0; } DUContext* DUChainUtils::getFunctionContext(Declaration* decl) { DUContext* functionContext = decl->internalContext(); if(functionContext && functionContext->type() != DUContext::Function) { foreach(const DUContext::Import& import, functionContext->importedParentContexts()) { DUContext* ctx = import.context(decl->topContext()); if(ctx && ctx->type() == DUContext::Function) functionContext = ctx; } } if(functionContext && functionContext->type() == DUContext::Function) return functionContext; return 0; } QVector KDevelop::DUChainUtils::allProblemsForContext(KDevelop::ReferencedTopDUContext top) { QVector ret; Q_FOREACH ( const auto& p, top->problems() ) { ret << p; } Q_FOREACH ( const auto& p, ICore::self()->languageController()->staticAssistantsManager()->problemsForContext(top) ) { ret << p; } return ret; } diff --git a/plugins/welcomepage/CMakeLists.txt b/plugins/welcomepage/CMakeLists.txt index caa418562a..2b3083f07e 100644 --- a/plugins/welcomepage/CMakeLists.txt +++ b/plugins/welcomepage/CMakeLists.txt @@ -1,20 +1,21 @@ +add_definitions(-DTRANSLATION_DOMAIN=\"kdevwelcomepage\") add_subdirectory(declarative) set(welcomepage_SRCS welcomepageplugin.cpp sessionsmodel.cpp welcomepageview.cpp uihelper.cpp) qt5_add_resources(welcomepage_SRCS welcomepage.qrc) kdevplatform_add_plugin(kdevwelcomepage JSON kdevwelcomepage.json SOURCES ${welcomepage_SRCS}) target_link_libraries(kdevwelcomepage KDev::Interfaces KDev::Sublime KDev::Shell KDev::Project Qt5::QuickWidgets KF5::Declarative ) diff --git a/plugins/welcomepage/Messages.sh b/plugins/welcomepage/Messages.sh new file mode 100644 index 0000000000..a5e1774301 --- /dev/null +++ b/plugins/welcomepage/Messages.sh @@ -0,0 +1,3 @@ +#!/bin/sh +$XGETTEXT `find . -name \*.qml -o -name \*.cpp` -o $podir/kdevwelcomepage.pot +rm -f rc.cpp diff --git a/plugins/welcomepage/welcomepageview.cpp b/plugins/welcomepage/welcomepageview.cpp index 1948e469ba..79d0605646 100644 --- a/plugins/welcomepage/welcomepageview.cpp +++ b/plugins/welcomepage/welcomepageview.cpp @@ -1,73 +1,74 @@ /* This file is part of KDevelop Copyright 2011 Aleix Pol Gonzalez This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License version 2 as published by the Free Software Foundation. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "welcomepageview.h" #include "uihelper.h" #include "sessionsmodel.h" #include #include #include #include #include #include #include #include #include #include #include #include using namespace KDevelop; WelcomePageWidget::WelcomePageWidget(const QList & /*projects*/, QWidget* parent) : QQuickWidget(parent) { qRegisterMetaType("KDevelop::IProjectController*"); qRegisterMetaType("KDevelop::IPluginController*"); qRegisterMetaType("PatchReviewPlugin*"); qRegisterMetaType(); qmlRegisterType("org.kdevelop.welcomepage", 4, 3, "SessionsModel"); //setup kdeclarative library KDeclarative::KDeclarative kdeclarative; kdeclarative.setDeclarativeEngine(engine()); + kdeclarative.setTranslationDomain("kdevwelcomepage"); kdeclarative.setupBindings(); setResizeMode(QQuickWidget::SizeRootObjectToView); UiHelper* helper = new UiHelper(this); rootContext()->setContextProperty(QStringLiteral("kdev"), helper); rootContext()->setContextProperty(QStringLiteral("ICore"), KDevelop::ICore::self()); areaChanged(ICore::self()->uiController()->activeArea()); setSource(QUrl(QStringLiteral("qrc:/qml/main.qml"))); if(!errors().isEmpty()) { qWarning() << "welcomepage errors:" << errors(); } connect(Core::self()->uiControllerInternal()->activeSublimeWindow(), &Sublime::MainWindow::areaChanged, this, &WelcomePageWidget::areaChanged); } void WelcomePageWidget::areaChanged(Sublime::Area* area) { rootContext()->setContextProperty(QStringLiteral("area"), area->objectName()); } diff --git a/serialization/indexedstring.cpp b/serialization/indexedstring.cpp index 4365433ad1..d4673a8a4c 100644 --- a/serialization/indexedstring.cpp +++ b/serialization/indexedstring.cpp @@ -1,401 +1,402 @@ /* This file is part of KDevelop Copyright 2008 David Nolden Copyright 2016 Milian Wolff This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "indexedstring.h" #include "serialization/stringrepository.h" #include "referencecounting.h" using namespace KDevelop; namespace { struct IndexedStringData { unsigned short length; uint refCount; uint itemSize() const { return sizeof(IndexedStringData) + length; } uint hash() const { IndexedString::RunningHash running; const char* str = ((const char*)this) + sizeof(IndexedStringData); for (int a = length - 1; a >= 0; --a) { running.append(*str); ++str; } return running.hash; } }; inline void increase(uint& val) { ++val; } inline void decrease(uint& val) { --val; } struct IndexedStringRepositoryItemRequest { //The text is supposed to be utf8 encoded IndexedStringRepositoryItemRequest(const char* text, uint hash, unsigned short length) : m_hash(hash) , m_length(length) , m_text(text) { } enum { AverageSize = 10 //This should be the approximate average size of an Item }; typedef uint HashType; //Should return the hash-value associated with this request(For example the hash of a string) HashType hash() const { return m_hash; } //Should return the size of an item created with createItem uint itemSize() const { return sizeof(IndexedStringData) + m_length; } //Should create an item where the information of the requested item is permanently stored. The pointer //@param item equals an allocated range with the size of itemSize(). void createItem(IndexedStringData* item) const { item->length = m_length; item->refCount = 0; ++item; memcpy(item, m_text, m_length); } static void destroy(IndexedStringData* item, AbstractItemRepository&) { Q_UNUSED(item); //Nothing to do here (The object is not intelligent) } static bool persistent(const IndexedStringData* item) { return (bool)item->refCount; } //Should return whether the here requested item equals the given item bool equals(const IndexedStringData* item) const { return item->length == m_length && (memcmp(++item, m_text, m_length) == 0); } uint m_hash; unsigned short m_length; const char* m_text; }; inline const char* c_strFromItem(const IndexedStringData* item) { return reinterpret_cast(item + 1); } ///@param item must be valid(nonzero) inline QString stringFromItem(const IndexedStringData* item) { return QString::fromUtf8(c_strFromItem(item), item->length); } inline QByteArray arrayFromItem(const IndexedStringData* item) { return QByteArray(c_strFromItem(item), item->length); } inline bool isSingleCharIndex(uint index) { return (index & 0xffff0000) == 0xffff0000; } inline uint charToIndex(char c) { return 0xffff0000 | c; } inline char indexToChar(uint index) { Q_ASSERT(isSingleCharIndex(index)); return static_cast(index & 0xff); } using IndexedStringRepository = ItemRepository; using IndexedStringRepositoryManagerBase = RepositoryManager; class IndexedStringRepositoryManager : public IndexedStringRepositoryManagerBase { public: IndexedStringRepositoryManager() : IndexedStringRepositoryManagerBase(QStringLiteral("String Index")) { repository()->setMutex(&m_mutex); } private: // non-recursive mutex to increase speed QMutex m_mutex; }; IndexedStringRepository* globalIndexedStringRepository() { static IndexedStringRepositoryManager manager; return manager.repository(); } template auto readRepo(ReadAction action) -> decltype(action(globalIndexedStringRepository())) { const auto* repo = globalIndexedStringRepository(); QMutexLocker lock(repo->mutex()); return action(repo); } template auto editRepo(EditAction action) -> decltype(action(globalIndexedStringRepository())) { auto* repo = globalIndexedStringRepository(); QMutexLocker lock(repo->mutex()); return action(repo); } inline void ref(IndexedString* string) { const uint index = string->index(); if (index && !isSingleCharIndex(index)) { if (shouldDoDUChainReferenceCounting(string)) { editRepo([index] (IndexedStringRepository* repo) { increase(repo->dynamicItemFromIndexSimple(index)->refCount); }); } } } inline void deref(IndexedString* string) { const uint index = string->index(); if (index && !isSingleCharIndex(index)) { if (shouldDoDUChainReferenceCounting(string)) { editRepo([index] (IndexedStringRepository* repo) { decrease(repo->dynamicItemFromIndexSimple(index)->refCount); }); } } } } IndexedString::IndexedString() : m_index(0) { } ///@param str must be a utf8 encoded string, does not need to be 0-terminated. ///@param length must be its length in bytes. IndexedString::IndexedString(const char* str, unsigned short length, uint hash) { if (!length) { m_index = 0; } else if (length == 1) { m_index = charToIndex(str[0]); } else { const auto request = IndexedStringRepositoryItemRequest(str, hash ? hash : hashString(str, length), length); bool refcount = shouldDoDUChainReferenceCounting(this); m_index = editRepo([request, refcount] (IndexedStringRepository* repo) { auto index = repo->index(request); if (refcount) { increase(repo->dynamicItemFromIndexSimple(index)->refCount); } return index; }); } } IndexedString::IndexedString(char c) : m_index(charToIndex(c)) {} IndexedString::IndexedString(const QUrl& url) : IndexedString(url.isLocalFile() ? url.toLocalFile() : url.toString()) { Q_ASSERT(url.isEmpty() || !url.isRelative()); + Q_ASSERT(url == url.adjusted(QUrl::NormalizePathSegments)); } IndexedString::IndexedString(const QString& string) : IndexedString(string.toUtf8()) {} IndexedString::IndexedString(const char* str) : IndexedString(str, str ? strlen(str) : 0) {} IndexedString::IndexedString(const QByteArray& str) : IndexedString(str.constData(), str.length()) {} IndexedString::~IndexedString() { deref(this); } IndexedString::IndexedString(const IndexedString& rhs) : m_index(rhs.m_index) { ref(this); } IndexedString& IndexedString::operator=(const IndexedString& rhs) { if (m_index == rhs.m_index) { return *this; } deref(this); m_index = rhs.m_index; ref(this); return *this; } QUrl IndexedString::toUrl() const { if (isEmpty()) { return {}; } QUrl ret = QUrl::fromUserInput(str()); Q_ASSERT(!ret.isRelative()); return ret; } QString IndexedString::str() const { if (!m_index) { return QString(); } else if (isSingleCharIndex(m_index)) { return QString(QLatin1Char(indexToChar(m_index))); } else { const uint index = m_index; return readRepo([index] (const IndexedStringRepository* repo) { return stringFromItem(repo->itemFromIndex(index)); }); } } int IndexedString::length() const { return lengthFromIndex(m_index); } int IndexedString::lengthFromIndex(uint index) { if (!index) { return 0; } else if (isSingleCharIndex(index)) { return 1; } else { return readRepo([index] (const IndexedStringRepository* repo) { return repo->itemFromIndex(index)->length; }); } } const char* IndexedString::c_str() const { if (!m_index) { return 0; } else if (isSingleCharIndex(m_index)) { #if Q_BYTE_ORDER == Q_LITTLE_ENDIAN const uint offset = 0; #else const uint offset = 3; #endif return reinterpret_cast(&m_index) + offset; } else { const uint index = m_index; return readRepo([index] (const IndexedStringRepository* repo) { return c_strFromItem(repo->itemFromIndex(index)); }); } } QByteArray IndexedString::byteArray() const { if (!m_index) { return QByteArray(); } else if (isSingleCharIndex(m_index)) { return QByteArray(1, indexToChar(m_index)); } else { const uint index = m_index; return readRepo([index] (const IndexedStringRepository* repo) { return arrayFromItem(repo->itemFromIndex(index)); }); } } uint IndexedString::hashString(const char* str, unsigned short length) { RunningHash running; for (int a = length - 1; a >= 0; --a) { running.append(*str); ++str; } return running.hash; } uint IndexedString::indexForString(const char* str, short unsigned length, uint hash) { if (!length) { return 0; } else if (length == 1) { return charToIndex(str[0]); } else { const auto request = IndexedStringRepositoryItemRequest(str, hash ? hash : hashString(str, length), length); return editRepo([request] (IndexedStringRepository* repo) { return repo->index(request); }); } } uint IndexedString::indexForString(const QString& str, uint hash) { const QByteArray array(str.toUtf8()); return indexForString(array.constBegin(), array.size(), hash); } QDebug operator<<(QDebug s, const IndexedString& string) { s.nospace() << string.str(); return s.space(); }