forked from qt-creator/qt-creator
QmlDesigner: Add StringUtils::split_last function
- Utilized QStringView for type and url to improve performance. - Implemented split_last functionality using std::ranges and std::views to efficiently split the fullType string into type and url. - Move all string utils to qml designer utils Change-Id: I627c5b4f03462df8a4f9ab174c34a249b6b8fe87 Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
@@ -239,7 +239,6 @@ extend_qtc_library(QmlDesignerCore
|
||||
rewritingexception.h
|
||||
signalhandlerproperty.h
|
||||
sourcepathids.h
|
||||
stringutils.h
|
||||
synchronousimagecache.h
|
||||
variantproperty.h
|
||||
widgetregistration.h
|
||||
|
@@ -8,6 +8,8 @@
|
||||
#include "model.h"
|
||||
#include "model_p.h"
|
||||
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
namespace QmlDesigner {
|
||||
@@ -133,12 +135,10 @@ AbstractProperty BindingProperty::resolveToProperty() const
|
||||
return {};
|
||||
|
||||
ModelNode node = parentModelNode();
|
||||
auto lastElementBegin = std::ranges::find(binding | std::views::reverse, u'.').base();
|
||||
QStringView lastElement{lastElementBegin, binding.end()};
|
||||
if (binding.begin() != lastElementBegin) {
|
||||
QStringView nodeBinding{binding.begin(), std::prev(lastElementBegin)};
|
||||
|
||||
auto [nodeBinding, lastElement] = StringUtils::split_last(binding, u'.');
|
||||
if (nodeBinding.size())
|
||||
node = resolveBinding(nodeBinding, node);
|
||||
}
|
||||
|
||||
if (node.isValid() && !lastElement.contains(' '))
|
||||
return node.property(lastElement.toUtf8());
|
||||
|
@@ -9,19 +9,21 @@
|
||||
#include <QVector3D>
|
||||
#include <QVector4D>
|
||||
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
|
||||
#include "bindingproperty.h"
|
||||
#include "model.h"
|
||||
#include "nodelistproperty.h"
|
||||
#include "nodeproperty.h"
|
||||
#include "signalhandlerproperty.h"
|
||||
#include "stringutils.h"
|
||||
#include "variantproperty.h"
|
||||
#include <nodemetainfo.h>
|
||||
|
||||
using namespace QmlDesigner;
|
||||
using namespace QmlDesigner::Internal;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
namespace QmlDesigner::Internal {
|
||||
|
||||
static QString properColorName(const QColor &color)
|
||||
{
|
||||
if (color.alpha() == 255)
|
||||
@@ -145,7 +147,7 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept
|
||||
return stringValue;
|
||||
case QMetaType::QString:
|
||||
case QMetaType::QChar:
|
||||
return QStringView(u"\"%1\"").arg(escape(unicodeEscape(stringValue)));
|
||||
return QStringView(u"\"%1\"").arg(StringUtils::escape(unicodeEscape(stringValue)));
|
||||
case QMetaType::QVector2D: {
|
||||
auto vec = value.value<QVector2D>();
|
||||
return QStringLiteral("Qt.vector2d(%1, %2)").arg(vec.x(), vec.y());
|
||||
@@ -160,7 +162,7 @@ QString QmlTextGenerator::toQml(const AbstractProperty &property, int indentDept
|
||||
.arg(vec.x(), vec.y(), vec.z(), vec.w());
|
||||
}
|
||||
default:
|
||||
return QStringView(u"\"%1\"").arg(escape(stringValue));
|
||||
return QStringView(u"\"%1\"").arg(StringUtils::escape(stringValue));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -281,3 +283,5 @@ QString QmlTextGenerator::propertyToQml(const AbstractProperty &property, int in
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner::Internal
|
||||
|
@@ -23,6 +23,8 @@
|
||||
#include <signalhandlerproperty.h>
|
||||
#include <variantproperty.h>
|
||||
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
|
||||
#include <qmljs/parser/qmljsengine_p.h>
|
||||
#include <qmljs/qmljsmodelmanagerinterface.h>
|
||||
#include <qmljs/qmljssimplereader.h>
|
||||
@@ -964,12 +966,11 @@ QmlJS::Document::Ptr RewriterView::document() const
|
||||
|
||||
QString RewriterView::convertTypeToImportAlias(QStringView type) const
|
||||
{
|
||||
auto simplifiedTypeBegin = std::ranges::find(type | std::views::reverse, u'.').base();
|
||||
const auto simplifiedType = QStringView{simplifiedTypeBegin, type.end()}.toString();
|
||||
if (type.begin() == simplifiedTypeBegin)
|
||||
return simplifiedType;
|
||||
auto [url, simplifiedType] = StringUtils::split_last(type, u'.');
|
||||
|
||||
if (url.isEmpty())
|
||||
return simplifiedType.toString();
|
||||
|
||||
QStringView url{type.begin(), std::prev(simplifiedTypeBegin)};
|
||||
auto &&imports = model()->imports();
|
||||
auto projection = [](auto &&import) {
|
||||
return import.isFileImport() ? import.file() : import.url();
|
||||
@@ -977,12 +978,12 @@ QString RewriterView::convertTypeToImportAlias(QStringView type) const
|
||||
auto found = std::ranges::find(imports, url, projection);
|
||||
|
||||
if (found == imports.end())
|
||||
return simplifiedType;
|
||||
return simplifiedType.toString();
|
||||
|
||||
auto &&alias = found->alias();
|
||||
|
||||
if (alias.isEmpty())
|
||||
return simplifiedType;
|
||||
return simplifiedType.toString();
|
||||
|
||||
return alias + '.'_L1 + simplifiedType;
|
||||
}
|
||||
|
@@ -25,6 +25,7 @@
|
||||
#include <import.h>
|
||||
#include <modelutils.h>
|
||||
#include <projectstorage/modulescanner.h>
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
#include <rewritingexception.h>
|
||||
|
||||
#include <enumeration.h>
|
||||
@@ -215,9 +216,8 @@ bool isSignalPropertyName(QStringView signalName)
|
||||
if (signalName.isEmpty())
|
||||
return false;
|
||||
// see QmlCompiler::isSignalPropertyName
|
||||
auto begin = std::ranges::find(signalName | std::views::reverse, u'.').base();
|
||||
auto [dummy, pureSignalName] = QmlDesigner::StringUtils::split_last(signalName, u'.');
|
||||
|
||||
QStringView pureSignalName = {begin, signalName.end()};
|
||||
return pureSignalName.length() >= 3 && pureSignalName.startsWith(u"on")
|
||||
&& pureSignalName.at(2).isLetter();
|
||||
}
|
||||
@@ -1626,26 +1626,6 @@ void TextToModelMerger::syncArrayProperty(AbstractProperty &modelProperty,
|
||||
}
|
||||
}
|
||||
|
||||
static QString fileForFullQrcPath(QStringView string)
|
||||
{
|
||||
auto found = std::ranges::find(string | std::views::reverse, u'/');
|
||||
|
||||
if (found == string.rend())
|
||||
return {};
|
||||
|
||||
return QStringView{found.base(), string.end()}.toString();
|
||||
}
|
||||
|
||||
static QString removeFileFromQrcPath(const QStringView string)
|
||||
{
|
||||
auto found = std::ranges::find(string | std::views::reverse, u'/');
|
||||
|
||||
if (found == string.rend())
|
||||
return {};
|
||||
|
||||
return QStringView{string.begin(), std::prev(found.base())}.toString();
|
||||
}
|
||||
|
||||
void TextToModelMerger::syncVariantProperty(AbstractProperty &modelProperty,
|
||||
const QVariant &astValue,
|
||||
const TypeName &astType,
|
||||
@@ -2303,8 +2283,10 @@ void TextToModelMerger::populateQrcMapping(const QString &filePath)
|
||||
if (!filePath.startsWith(QLatin1String("qrc:")))
|
||||
return;
|
||||
|
||||
QString path = removeFileFromQrcPath(filePath);
|
||||
const QString fileName = fileForFullQrcPath(filePath);
|
||||
auto [pathView, fileNameView] = StringUtils::split_last(filePath, u'/');
|
||||
|
||||
QString path = pathView.toString();
|
||||
const QString fileName = fileNameView.toString();
|
||||
path.remove(QLatin1String("qrc:"));
|
||||
QMap<QString,QStringList> map = ModelManagerInterface::instance()->filesInQrcPath(path);
|
||||
const QStringList qrcFilePaths = map.value(fileName, {});
|
||||
|
@@ -17,6 +17,7 @@ add_qtc_library(QmlDesignerUtils STATIC
|
||||
qmldesignerutils_global.h
|
||||
version.cpp version.h
|
||||
maputils.h
|
||||
stringutils.h
|
||||
)
|
||||
|
||||
extend_qtc_library(QmlDesignerUtils
|
||||
|
@@ -1,21 +1,25 @@
|
||||
// Copyright (C) 2016 The Qt Company Ltd.
|
||||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <QString>
|
||||
#include <QStringView>
|
||||
|
||||
using namespace Qt::StringLiterals;
|
||||
#include <algorithm>
|
||||
#include <ranges>
|
||||
|
||||
namespace QmlDesigner {
|
||||
namespace QmlDesigner::StringUtils {
|
||||
|
||||
inline QString escape(const QString &value)
|
||||
{
|
||||
QString result = value;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
if (value.length() == 6 && value.startsWith("\\u")) //Do not double escape unicode chars
|
||||
return value;
|
||||
|
||||
QString result = value;
|
||||
|
||||
result.replace("\\"_L1, "\\\\"_L1);
|
||||
result.replace("\""_L1, "\\\""_L1);
|
||||
result.replace("\t"_L1, "\\t"_L1);
|
||||
@@ -27,11 +31,13 @@ inline QString escape(const QString &value)
|
||||
|
||||
inline QString deescape(const QString &value)
|
||||
{
|
||||
QString result = value;
|
||||
using namespace Qt::StringLiterals;
|
||||
|
||||
if (value.length() == 6 && value.startsWith("\\u")) //Ignore unicode chars
|
||||
return value;
|
||||
|
||||
QString result = value;
|
||||
|
||||
result.replace("\\\\"_L1, "\\"_L1);
|
||||
result.replace("\\\""_L1, "\""_L1);
|
||||
result.replace("\\t"_L1, "\t"_L1);
|
||||
@@ -41,4 +47,19 @@ inline QString deescape(const QString &value)
|
||||
return result;
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner
|
||||
template<typename T>
|
||||
concept is_object = std::is_object_v<T>;
|
||||
|
||||
std::pair<QStringView, QStringView> split_last(is_object auto &&, QChar c) = delete; // remove rvalue overload
|
||||
|
||||
inline std::pair<QStringView, QStringView> split_last(QStringView text, QChar c)
|
||||
{
|
||||
auto splitPoint = std::ranges::find(text | std::views::reverse, c).base();
|
||||
|
||||
if (splitPoint == text.begin())
|
||||
return {{}, text};
|
||||
|
||||
return {{text.begin(), std::prev(splitPoint)}, {splitPoint, text.end()}};
|
||||
}
|
||||
|
||||
} // namespace QmlDesigner::StringUtils
|
@@ -14,11 +14,11 @@
|
||||
#include "qmlstate.h"
|
||||
#include "qmltimelinekeyframegroup.h"
|
||||
#include "qmlvisualnode.h"
|
||||
#include "stringutils.h"
|
||||
#include "variantproperty.h"
|
||||
|
||||
#include <auxiliarydataproperties.h>
|
||||
#include <designersettings.h>
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
|
||||
#include <qmltimeline.h>
|
||||
|
||||
@@ -255,7 +255,7 @@ QString QmlObjectNode::stripedTranslatableText(PropertyNameView name) const
|
||||
const QRegularExpressionMatch match = regularExpressionPattern.match(
|
||||
modelNode().bindingProperty(name).expression());
|
||||
if (match.hasMatch())
|
||||
return deescape(match.captured(2));
|
||||
return StringUtils::deescape(match.captured(2));
|
||||
return instanceValue(name).toString();
|
||||
}
|
||||
return instanceValue(name).toString();
|
||||
@@ -535,7 +535,7 @@ QVariant QmlObjectNode::instanceValue(const ModelNode &modelNode, PropertyNameVi
|
||||
QString QmlObjectNode::generateTranslatableText([[maybe_unused]] const QString &text,
|
||||
const DesignerSettings &settings)
|
||||
{
|
||||
const QString escapedText = escape(text);
|
||||
const QString escapedText = StringUtils::escape(text);
|
||||
|
||||
if (settings.value(DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt())
|
||||
switch (settings.value(DesignerSettingsKey::TYPE_OF_QSTR_FUNCTION).toInt()) {
|
||||
@@ -557,7 +557,7 @@ QString QmlObjectNode::stripedTranslatableTextFunction(const QString &text)
|
||||
QLatin1String("^qsTr(|Id|anslate)\\(\"(.*)\"\\)$"));
|
||||
const QRegularExpressionMatch match = regularExpressionPattern.match(text);
|
||||
if (match.hasMatch())
|
||||
return deescape(match.captured(2));
|
||||
return StringUtils::deescape(match.captured(2));
|
||||
return text;
|
||||
}
|
||||
|
||||
|
@@ -209,7 +209,6 @@ extend_qtc_library(TestDesignerCore
|
||||
rewritingexception.h
|
||||
signalhandlerproperty.h
|
||||
sourcepathids.h
|
||||
stringutils.h
|
||||
synchronousimagecache.h
|
||||
variantproperty.h
|
||||
)
|
||||
|
@@ -46,6 +46,7 @@ endfunction(unittest_copy_data_folder)
|
||||
|
||||
add_subdirectory(componentcore)
|
||||
add_subdirectory(designercoreutils)
|
||||
add_subdirectory(qmldesignerutils)
|
||||
add_subdirectory(designsystem)
|
||||
add_subdirectory(listmodeleditor)
|
||||
add_subdirectory(imagecache)
|
||||
|
@@ -1,9 +1,6 @@
|
||||
# qmldesigner/designercore/model
|
||||
extend_qtc_test(unittest
|
||||
DEPENDS
|
||||
QmlDesignerUtils
|
||||
SOURCES
|
||||
modelutils-test.cpp
|
||||
uniquename-test.cpp
|
||||
version-test.cpp
|
||||
)
|
||||
|
@@ -0,0 +1,7 @@
|
||||
extend_qtc_test(unittest
|
||||
DEPENDS
|
||||
QmlDesignerUtils
|
||||
SOURCES
|
||||
version-test.cpp
|
||||
stringutils-test.cpp
|
||||
)
|
@@ -0,0 +1,82 @@
|
||||
// Copyright (C) 2025 The Qt Company Ltd.
|
||||
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0
|
||||
|
||||
#include <googletest.h>
|
||||
#include <qmldesignerutils/stringutils.h>
|
||||
|
||||
namespace {
|
||||
|
||||
using QmlDesigner::StringUtils::split_last;
|
||||
|
||||
TEST(StringUtils_split_last, leaf_is_empty_for_empty_input)
|
||||
{
|
||||
QString text;
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(leaf, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, leaf_is_empty_with_ending_dot)
|
||||
{
|
||||
QString text;
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(leaf, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, steam_is_empty_for_last_beginning_dot)
|
||||
{
|
||||
QString text = ".bar";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(steam, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, steam_is_empty)
|
||||
{
|
||||
QString text = ".";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(steam, IsEmpty());
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, leaf)
|
||||
{
|
||||
QString text = "foo.foo.bar";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(leaf, u"bar");
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, leaf_for_not_dot)
|
||||
{
|
||||
QString text = "bar";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(leaf, u"bar");
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, steam)
|
||||
{
|
||||
QString text = "foo.foo.bar";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(steam, u"foo.foo");
|
||||
}
|
||||
|
||||
TEST(StringUtils_split_last, no_steam_for_not_dot)
|
||||
{
|
||||
QString text = "bar";
|
||||
|
||||
auto [steam, leaf] = split_last(text, u'.');
|
||||
|
||||
ASSERT_THAT(steam, IsEmpty());
|
||||
}
|
||||
} // namespace
|
Reference in New Issue
Block a user