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
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,

View File

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

View File

@@ -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
*/
//\{

View File

@@ -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;

View File

@@ -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)

View File

@@ -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<Storage::Info::PropertyDeclaration>
propertyDeclaration(PropertyDeclarationId propertyDeclarationId) const = 0;

View File

@@ -201,38 +201,50 @@ public:
{
std::shared_lock<Mutex> 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<Mutex> exclusiveLock(m_mutex);
if constexpr (!std::is_base_of_v<NonLockingMutex, Mutex>)
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<typename Container>
std::vector<IndexType> ids(const Container &values)
template<typename ValueType = ViewType>
std::vector<IndexType> ids(Utils::span<const ValueType> values)
{
std::vector<IndexType> 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<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)
@@ -250,7 +262,7 @@ public:
std::lock_guard<Mutex> 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<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{});
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<Mutex> 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();
}

View File

@@ -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<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());
}
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(_));

View File

@@ -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,

View File

@@ -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