diff --git a/src/plugins/qmldesigner/libs/designercore/include/model.h b/src/plugins/qmldesigner/libs/designercore/include/model.h index 202f597eda2..286e203e704 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/model.h +++ b/src/plugins/qmldesigner/libs/designercore/include/model.h @@ -148,6 +148,8 @@ public: #endif Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); + SmallModuleIds<128> moduleIdsStartsWith(Utils::SmallStringView startsWith, + Storage::ModuleKind kind) const; NodeMetaInfo metaInfo(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const; NodeMetaInfo metaInfo(Module module, Utils::SmallStringView typeName, diff --git a/src/plugins/qmldesigner/libs/designercore/include/projectstorageids.h b/src/plugins/qmldesigner/libs/designercore/include/projectstorageids.h index 1b875cefbbb..fb6c56a5407 100644 --- a/src/plugins/qmldesigner/libs/designercore/include/projectstorageids.h +++ b/src/plugins/qmldesigner/libs/designercore/include/projectstorageids.h @@ -45,6 +45,8 @@ using EnumerationDeclarationIds = std::vector; using ModuleId = Sqlite::BasicId; using ModuleIds = std::vector; using ModuleIdSpan = Utils::span; +template +using SmallModuleIds = QVarLengthArray; using ProjectPartId = Sqlite::BasicId; using ProjectPartIds = std::vector; diff --git a/src/plugins/qmldesigner/libs/designercore/model/model.cpp b/src/plugins/qmldesigner/libs/designercore/model/model.cpp index d1169fbc3be..f2fbf45c5a9 100644 --- a/src/plugins/qmldesigner/libs/designercore/model/model.cpp +++ b/src/plugins/qmldesigner/libs/designercore/model/model.cpp @@ -2917,6 +2917,15 @@ Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind modu return {}; } +SmallModuleIds<128> Model::moduleIdsStartsWith(Utils::SmallStringView startsWith, + Storage::ModuleKind kind) const +{ + if constexpr (useProjectStorage()) + return d->projectStorage->moduleIdsStartsWith(startsWith, kind); + + return {}; +} + /*! \name View related functions */ //\{ diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp index 402be920f5b..c342d5667a1 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.cpp @@ -1424,7 +1424,8 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo using NanotraceHR::keyValue; NanotraceHR::Tracer tracer{"get module id", projectStorageCategory(), - keyValue("module name", moduleName)}; + keyValue("module name", moduleName), + keyValue("module kind", kind)}; if (moduleName.empty()) return ModuleId{}; @@ -1436,6 +1437,27 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo return moduleId; } +SmallModuleIds<128> ProjectStorage::moduleIdsStartsWith(Utils::SmallStringView startsWith, + Storage::ModuleKind kind) const +{ + using NanotraceHR::keyValue; + NanotraceHR::Tracer tracer{"get module ids that starts with", + projectStorageCategory(), + keyValue("module name starts with", startsWith), + keyValue("module kind", kind)}; + + if (startsWith.isEmpty()) + return {}; + + auto projection = [&](ModuleView view) -> ModuleView { + return {view.name.substr(0, startsWith.size()), view.kind}; + }; + + auto moduleIds = moduleCache.ids<128>({startsWith, kind}, projection); + + return moduleIds; +} + Storage::Module ProjectStorage::module(ModuleId moduleId) const { using NanotraceHR::keyValue; diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h index 441dc72c705..ba09dac0ca5 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorage.h @@ -62,6 +62,9 @@ public: ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override; + SmallModuleIds<128> moduleIdsStartsWith(Utils::SmallStringView startsWith, + Storage::ModuleKind kind) const override; + Storage::Module module(ModuleId moduleId) const override; TypeId typeId(ModuleId moduleId, @@ -252,9 +255,9 @@ private: Utils::SmallStringView name; Storage::ModuleKind kind; - friend bool operator<(ModuleView first, ModuleView second) + friend std::strong_ordering operator<=>(ModuleView first, ModuleView second) { - return std::tie(first.kind, first.name) < std::tie(second.kind, second.name); + return std::tie(first.kind, first.name) <=> std::tie(second.kind, second.name); } friend bool operator==(const Storage::Module &first, ModuleView second) diff --git a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinterface.h b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinterface.h index 4591530e0c8..947bcf73980 100644 --- a/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinterface.h +++ b/src/plugins/qmldesigner/libs/designercore/projectstorage/projectstorageinterface.h @@ -32,6 +32,8 @@ public: virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 0; + virtual SmallModuleIds<128> + moduleIdsStartsWith(Utils::SmallStringView startsWith, Storage::ModuleKind kind) const = 0; virtual QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0; virtual std::optional propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; diff --git a/src/plugins/qmldesigner/libs/designercore/sourcepathstorage/storagecache.h b/src/plugins/qmldesigner/libs/designercore/sourcepathstorage/storagecache.h index 07ac6ff52f9..ef29ab9b08e 100644 --- a/src/plugins/qmldesigner/libs/designercore/sourcepathstorage/storagecache.h +++ b/src/plugins/qmldesigner/libs/designercore/sourcepathstorage/storagecache.h @@ -201,38 +201,50 @@ public: { std::shared_lock sharedLock(m_mutex); - auto found = find(view); + auto [iter, found] = find(view); - if (found != m_entries.end()) - return found->id; + if (found) + return iter->id; sharedLock.unlock(); std::lock_guard exclusiveLock(m_mutex); if constexpr (!std::is_base_of_v) - found = find(view); - if (found == m_entries.end()) - found = insertEntry(found, view, m_storage.fetchId(view)); + std::tie(iter, found) = find(view); + if (!found) + iter = insertEntry(iter, view, m_storage.fetchId(view)); - return found->id; + return iter->id; } - template - std::vector ids(const Container &values) + template + std::vector ids(Utils::span values) { std::vector ids; ids.reserve(values.size()); - std::ranges::transform(values, std::back_inserter(ids), [&](const auto &values) { - return this->id(values); + std::ranges::transform(values, std::back_inserter(ids), [&](ViewType value) { + return this->id(value); }); return ids; } - std::vector ids(std::initializer_list values) + template Proj> + using projected_value_t = std::remove_cvref_t &>>; + + template, Projection>> + QVarLengthArray ids(Value value, Projection projection) { - return ids>(values); + std::shared_lock sharedLock(m_mutex); + + auto range = std::ranges::equal_range(m_entries, value, Compare{}, projection); + + QVarLengthArray ids; + std::ranges::transform(range, std::back_inserter(ids), &CacheEntry::id); + return ids; } ResultType value(IndexType id) @@ -250,7 +262,7 @@ public: std::lock_guard exclusiveLock(m_mutex); Type value{m_storage.fetchValue(id)}; - auto interator = insertEntry(find(value), value, id); + auto interator = insertEntry(std::get<0>(find(value)), value, id); return interator->value; } @@ -290,24 +302,21 @@ private: } template - static auto find(Entries &&entries, ViewType view) + static std::tuple, bool> find(Entries &&entries, ViewType view) { auto found = std::ranges::lower_bound(entries, view, Compare{}); - if (found != entries.end() && *found == view) - return found; - - return entries.end(); + return {found, found != entries.end() && *found == view}; } IndexType id(ViewType view) const { std::shared_lock sharedLock(m_mutex); - auto found = find(view); + auto [iter, found] = find(view); - if (found != m_entries.end()) - return found->id; + if (found) + return iter->id; return IndexType(); } diff --git a/tests/unit/tests/mocks/projectstoragemock.h b/tests/unit/tests/mocks/projectstoragemock.h index 404e00f0e72..8fc77ddd5e8 100644 --- a/tests/unit/tests/mocks/projectstoragemock.h +++ b/tests/unit/tests/mocks/projectstoragemock.h @@ -133,6 +133,10 @@ public: moduleId, (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind), (const, override)); + MOCK_METHOD(QmlDesigner::SmallModuleIds<128>, + moduleIdsStartsWith, + (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind), + (const, override)); MOCK_METHOD(QmlDesigner::Storage::Module, module, (QmlDesigner::ModuleId), (const, override)); MOCK_METHOD(std::optional, diff --git a/tests/unit/tests/unittests/model/model-test.cpp b/tests/unit/tests/unittests/model/model-test.cpp index 3d41c0ff3d5..7066e27782d 100644 --- a/tests/unit/tests/unittests/model/model-test.cpp +++ b/tests/unit/tests/unittests/model/model-test.cpp @@ -1137,6 +1137,16 @@ TEST_F(Model_MetaInfo, get_invalid_meta_info_by_module_for_wrong_module) ASSERT_THAT(metaInfo, IsFalse()); } +TEST_F(Model_MetaInfo, get_module_ids_that_starts_with) +{ + ON_CALL(projectStorageMock, moduleIdsStartsWith(Eq("Q"), ModuleKind::QmlLibrary)) + .WillByDefault(Return(QmlDesigner::SmallModuleIds<128>{qtQuickModuleId})); + + auto moduleIds = model.moduleIdsStartsWith("Q", ModuleKind::QmlLibrary); + + ASSERT_THAT(moduleIds, Contains(qtQuickModuleId)); +} + TEST_F(Model_MetaInfo, add_project_storage_observer_to_project_storage) { EXPECT_CALL(projectStorageMock, addObserver(_)); diff --git a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp index 00db8c92486..aab9d2ff473 100644 --- a/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp +++ b/tests/unit/tests/unittests/projectstorage/projectstorage-test.cpp @@ -5827,6 +5827,30 @@ TEST_F(ProjectStorage, populate_module_cache) ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); } +TEST_F(ProjectStorage, get_no_module_ids_if_they_starts_with_nothing) +{ + storage.moduleId("QtQml", ModuleKind::QmlLibrary); + + auto ids = storage.moduleIdsStartsWith("", ModuleKind::QmlLibrary); + + ASSERT_THAT(ids, IsEmpty()); +} + +TEST_F(ProjectStorage, get_module_ids_if_they_starts_with_New) +{ + auto quickId = storage.moduleId("NewQuick", ModuleKind::QmlLibrary); + storage.moduleId("NewQuick", ModuleKind::CppLibrary); + auto quick3dId = storage.moduleId("NewQuick3D", ModuleKind::QmlLibrary); + storage.moduleId("NewQml", ModuleKind::CppLibrary); + auto qmlId = storage.moduleId("NewQml", ModuleKind::QmlLibrary); + storage.moduleId("Foo", ModuleKind::QmlLibrary); + storage.moduleId("Zoo", ModuleKind::QmlLibrary); + + auto ids = storage.moduleIdsStartsWith("New", ModuleKind::QmlLibrary); + + ASSERT_THAT(ids, UnorderedElementsAre(qmlId, quickId, quick3dId)); +} + TEST_F(ProjectStorage, add_directory_infoes) { Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectDirectoryPathId, diff --git a/tests/unit/tests/unittests/sourcepathstorage/storagecache-test.cpp b/tests/unit/tests/unittests/sourcepathstorage/storagecache-test.cpp index 161aac89f29..feb521ddf52 100644 --- a/tests/unit/tests/unittests/sourcepathstorage/storagecache-test.cpp +++ b/tests/unit/tests/unittests/sourcepathstorage/storagecache-test.cpp @@ -514,4 +514,41 @@ TYPED_TEST(StorageCache, fetch_ids_from_storage_calls) this->cache.ids({"foo", "bar"}); } + +TYPED_TEST(StorageCache, getting_ids_that_start_with_po) +{ + ON_CALL(this->mockStorage, fetchDirectoryPathId(Eq("poh"))).WillByDefault(Return(this->id43)); + this->cache.add({"poo", "foo", "poh", "taa"}); + auto projection = [](Utils::SmallStringView text) -> Utils::SmallStringView { + return text.substr(0, 2); + }; + + auto ids = this->cache.template ids<4>("po", projection); + + ASSERT_THAT(ids, UnorderedElementsAre(this->id43, this->id44)); +} + +TYPED_TEST(StorageCache, getting_no_ids_if_there_is_no_entry_starts_with_oh) +{ + this->cache.add({"poo", "taa", "foo", "bar"}); + auto projection = [](Utils::SmallStringView text) -> Utils::SmallStringView { + return text.substr(0, 2); + }; + + auto ids = this->cache.template ids<4>("oh", projection); + + ASSERT_THAT(ids, IsEmpty()); +} + +TYPED_TEST(StorageCache, get_all_ids_if_the_string_is_empty) +{ + this->cache.add({"foo", "bar", "poo", "taa"}); + auto projection = [](Utils::SmallStringView text) -> Utils::SmallStringView { + return text.substr(0, 0); + }; + + auto ids = this->cache.template ids<4>("", projection); + + ASSERT_THAT(ids, UnorderedElementsAre(this->id42, this->id43, this->id44, this->id45)); +} } // namespace