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 <thomas.hartmann@qt.io>
Reviewed-by: Burak Hancerli <burak.hancerli@qt.io>
This commit is contained in:
Marco Bubke
2025-04-08 19:27:41 +02:00
parent 974a51ff80
commit 03cbd190e7
11 changed files with 149 additions and 25 deletions

View File

@@ -148,6 +148,8 @@ public:
#endif #endif
Module module(Utils::SmallStringView moduleName, Storage::ModuleKind moduleKind); 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(const TypeName &typeName, int majorVersion = -1, int minorVersion = -1) const;
NodeMetaInfo metaInfo(Module module, NodeMetaInfo metaInfo(Module module,
Utils::SmallStringView typeName, Utils::SmallStringView typeName,

View File

@@ -45,6 +45,8 @@ using EnumerationDeclarationIds = std::vector<EnumerationDeclarationId>;
using ModuleId = Sqlite::BasicId<ProjectStorageIdType::Module, int>; using ModuleId = Sqlite::BasicId<ProjectStorageIdType::Module, int>;
using ModuleIds = std::vector<ModuleId>; using ModuleIds = std::vector<ModuleId>;
using ModuleIdSpan = Utils::span<ModuleId>; using ModuleIdSpan = Utils::span<ModuleId>;
template<std::size_t size>
using SmallModuleIds = QVarLengthArray<ModuleId, size>;
using ProjectPartId = Sqlite::BasicId<ProjectStorageIdType::ProjectPartId>; using ProjectPartId = Sqlite::BasicId<ProjectStorageIdType::ProjectPartId>;
using ProjectPartIds = std::vector<ProjectPartId>; using ProjectPartIds = std::vector<ProjectPartId>;

View File

@@ -2917,6 +2917,15 @@ Module Model::module(Utils::SmallStringView moduleName, Storage::ModuleKind modu
return {}; 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 /*! \name View related functions
*/ */
//\{ //\{

View File

@@ -1424,7 +1424,8 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo
using NanotraceHR::keyValue; using NanotraceHR::keyValue;
NanotraceHR::Tracer tracer{"get module id", NanotraceHR::Tracer tracer{"get module id",
projectStorageCategory(), projectStorageCategory(),
keyValue("module name", moduleName)}; keyValue("module name", moduleName),
keyValue("module kind", kind)};
if (moduleName.empty()) if (moduleName.empty())
return ModuleId{}; return ModuleId{};
@@ -1436,6 +1437,27 @@ ModuleId ProjectStorage::moduleId(Utils::SmallStringView moduleName, Storage::Mo
return moduleId; 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 Storage::Module ProjectStorage::module(ModuleId moduleId) const
{ {
using NanotraceHR::keyValue; using NanotraceHR::keyValue;

View File

@@ -62,6 +62,9 @@ public:
ModuleId moduleId(Utils::SmallStringView moduleName, Storage::ModuleKind kind) const override; 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; Storage::Module module(ModuleId moduleId) const override;
TypeId typeId(ModuleId moduleId, TypeId typeId(ModuleId moduleId,
@@ -252,9 +255,9 @@ private:
Utils::SmallStringView name; Utils::SmallStringView name;
Storage::ModuleKind kind; 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) friend bool operator==(const Storage::Module &first, ModuleView second)

View File

@@ -32,6 +32,8 @@ public:
virtual void removeObserver(ProjectStorageObserver *observer) = 0; virtual void removeObserver(ProjectStorageObserver *observer) = 0;
virtual ModuleId moduleId(::Utils::SmallStringView name, Storage::ModuleKind kind) const = 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 QmlDesigner::Storage::Module module(ModuleId moduleId) const = 0;
virtual std::optional<Storage::Info::PropertyDeclaration> virtual std::optional<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0; propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;

View File

@@ -201,38 +201,50 @@ public:
{ {
std::shared_lock<Mutex> sharedLock(m_mutex); std::shared_lock<Mutex> sharedLock(m_mutex);
auto found = find(view); auto [iter, found] = find(view);
if (found != m_entries.end()) if (found)
return found->id; return iter->id;
sharedLock.unlock(); sharedLock.unlock();
std::lock_guard<Mutex> exclusiveLock(m_mutex); std::lock_guard<Mutex> exclusiveLock(m_mutex);
if constexpr (!std::is_base_of_v<NonLockingMutex, Mutex>) if constexpr (!std::is_base_of_v<NonLockingMutex, Mutex>)
found = find(view); std::tie(iter, found) = find(view);
if (found == m_entries.end()) if (!found)
found = insertEntry(found, view, m_storage.fetchId(view)); iter = insertEntry(iter, view, m_storage.fetchId(view));
return found->id; return iter->id;
} }
template<typename Container> template<typename ValueType = ViewType>
std::vector<IndexType> ids(const Container &values) std::vector<IndexType> ids(Utils::span<const ValueType> values)
{ {
std::vector<IndexType> ids; std::vector<IndexType> ids;
ids.reserve(values.size()); ids.reserve(values.size());
std::ranges::transform(values, std::back_inserter(ids), [&](const auto &values) { std::ranges::transform(values, std::back_inserter(ids), [&](ViewType value) {
return this->id(values); return this->id(value);
}); });
return ids; return ids;
} }
std::vector<IndexType> ids(std::initializer_list<Type> values) template<std::indirectly_readable I, std::indirectly_regular_unary_invocable<I> Proj>
using projected_value_t = std::remove_cvref_t<std::invoke_result_t<Proj &, std::iter_value_t<I> &>>;
template<std::size_t size,
typename Projection = std::identity,
typename Value = projected_value_t<std::ranges::iterator_t<CacheEntries>, Projection>>
QVarLengthArray<IndexType, size> ids(Value value, Projection projection)
{ {
return ids<std::initializer_list<Type>>(values); std::shared_lock<Mutex> sharedLock(m_mutex);
auto range = std::ranges::equal_range(m_entries, value, Compare{}, projection);
QVarLengthArray<IndexType, size> ids;
std::ranges::transform(range, std::back_inserter(ids), &CacheEntry::id);
return ids;
} }
ResultType value(IndexType id) ResultType value(IndexType id)
@@ -250,7 +262,7 @@ public:
std::lock_guard<Mutex> exclusiveLock(m_mutex); std::lock_guard<Mutex> exclusiveLock(m_mutex);
Type value{m_storage.fetchValue(id)}; 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; return interator->value;
} }
@@ -290,24 +302,21 @@ private:
} }
template<typename Entries> template<typename Entries>
static auto find(Entries &&entries, ViewType view) static std::tuple<std::ranges::iterator_t<Entries>, bool> find(Entries &&entries, ViewType view)
{ {
auto found = std::ranges::lower_bound(entries, view, Compare{}); auto found = std::ranges::lower_bound(entries, view, Compare{});
if (found != entries.end() && *found == view) return {found, found != entries.end() && *found == view};
return found;
return entries.end();
} }
IndexType id(ViewType view) const IndexType id(ViewType view) const
{ {
std::shared_lock<Mutex> sharedLock(m_mutex); std::shared_lock<Mutex> sharedLock(m_mutex);
auto found = find(view); auto [iter, found] = find(view);
if (found != m_entries.end()) if (found)
return found->id; return iter->id;
return IndexType(); return IndexType();
} }

View File

@@ -133,6 +133,10 @@ public:
moduleId, moduleId,
(::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind), (::Utils::SmallStringView, QmlDesigner::Storage::ModuleKind moduleKind),
(const, override)); (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(QmlDesigner::Storage::Module, module, (QmlDesigner::ModuleId), (const, override));
MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>, MOCK_METHOD(std::optional<QmlDesigner::Storage::Info::PropertyDeclaration>,

View File

@@ -1137,6 +1137,16 @@ TEST_F(Model_MetaInfo, get_invalid_meta_info_by_module_for_wrong_module)
ASSERT_THAT(metaInfo, IsFalse()); 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) TEST_F(Model_MetaInfo, add_project_storage_observer_to_project_storage)
{ {
EXPECT_CALL(projectStorageMock, addObserver(_)); EXPECT_CALL(projectStorageMock, addObserver(_));

View File

@@ -5827,6 +5827,30 @@ TEST_F(ProjectStorage, populate_module_cache)
ASSERT_THAT(newStorage.module(id), IsModule("Qml", ModuleKind::QmlLibrary)); 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) TEST_F(ProjectStorage, add_directory_infoes)
{ {
Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectDirectoryPathId, Storage::Synchronization::DirectoryInfo directoryInfo1{qmlProjectDirectoryPathId,

View File

@@ -514,4 +514,41 @@ TYPED_TEST(StorageCache, fetch_ids_from_storage_calls)
this->cache.ids({"foo", "bar"}); 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 } // namespace