From 03cbd190e7988672c8e0a081e01c1200fb8ac799 Mon Sep 17 00:00:00 2001 From: Marco Bubke Date: Tue, 8 Apr 2025 19:27:41 +0200 Subject: [PATCH] QmlDesigner: Add Model::moduleIdsStartsWith() Get modul ids that start with that string and are of that kind. If no string is given there are no ids returned. The returned ids are not sorted. auto moduleIds = model->moduleIdsStartsWith("QtQuick", Storage::ModuleKind::QmlLibrary); std::ranges::sort(moduleIds); auto isQtQuickModule = Utils::set_has_common_element(otherSortedModuleIds, moduleIds); Change-Id: I85f7dadca5be88885e54237b5d560ba02b61c75b Reviewed-by: Thomas Hartmann Reviewed-by: Burak Hancerli --- .../libs/designercore/include/model.h | 2 + .../designercore/include/projectstorageids.h | 2 + .../libs/designercore/model/model.cpp | 9 ++++ .../projectstorage/projectstorage.cpp | 24 ++++++++- .../projectstorage/projectstorage.h | 7 ++- .../projectstorage/projectstorageinterface.h | 2 + .../sourcepathstorage/storagecache.h | 53 +++++++++++-------- tests/unit/tests/mocks/projectstoragemock.h | 4 ++ .../unit/tests/unittests/model/model-test.cpp | 10 ++++ .../projectstorage/projectstorage-test.cpp | 24 +++++++++ .../sourcepathstorage/storagecache-test.cpp | 37 +++++++++++++ 11 files changed, 149 insertions(+), 25 deletions(-) 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