2022-05-13 13:12:05 +00:00
|
|
|
// Copyright (C) 2021 The Qt Company Ltd.
|
|
|
|
// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
|
Suppress GCC11 warning about maybe-uninitialized data access
I don't *think* the compiler is correct. It's complaining that the
copy-construction of PathComponent in the lambda here:
bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path referencedObject)
{
return dvItem(visitor, c, [c, this, referencedObject]() {
return this->subReferenceItem(c, referencedObject);
});
}
which was created as a temporary in dvReferenceField:
return dvReference(visitor, PathEls::Field(f), referencedObject);
could be copying garbage. Note that the warning is about copying a
PathComponent::Data of kind Key, whereas this inline sequence is of
Field.
Of course, the whole PathComponent::Data is a major ill-advised disaster
waiting to happen...
In copy constructor ‘QArrayDataPointer<T>::QArrayDataPointer(const QArrayDataPointer<T>&) [with T = char16_t]’,
inlined from ‘QString::QString(const QString&)’ at qstring.h:1232:58,
inlined from ‘QQmlJS::Dom::PathEls::Key::Key(const QQmlJS::Dom::PathEls::Key&)’ at src/qmldom/qqmldompath_p.h:164:7,
inlined from ‘QQmlJS::Dom::PathEls::PathComponent::Data::Data(const QQmlJS::Dom::PathEls::PathComponent::Data&)’ at src/qmldom/qqmldompath_p.h:369:37,
inlined from ‘QQmlJS::Dom::PathEls::PathComponent::PathComponent(const QQmlJS::Dom::PathEls::PathComponent&)’ at src/qmldom/qqmldompath_p.h:311:7,
inlined from ‘bool QQmlJS::Dom::DomItem::dvReference(QQmlJS::Dom::DirectVisitor, const QQmlJS::Dom::PathEls::PathComponent&, QQmlJS::Dom::Path)’ at src/qmldom/qqmldomitem_p.h:981:22,
inlined from ‘bool QQmlJS::Dom::DomItem::dvReferenceField(QQmlJS::Dom::DirectVisitor, QStringView, QQmlJS::Dom::Path)’ at src/qmldom/qqmldomitem_p.h:991:27:
qarraydatapointer.h:69:50: error: ‘*(const QArrayDataPointer<char16_t>*)((char*)&<unnamed> + offsetof(const QQmlJS::Dom::PathEls::PathComponent, QQmlJS::Dom::PathEls::PathComponent::data) + 8).QArrayDataPointer<char16_t>::size’ may be used uninitialized [-Werror=maybe-uninitialized]
69 | : d(other.d), ptr(other.ptr), size(other.size)
| ~~~~~~^~~~
In file included from src/qmldom/qqmldomelements_p.h:52,
from src/qmldom/qqmldomelements.cpp:38:
src/qmldom/qqmldomitem_p.h: In member function ‘bool QQmlJS::Dom::DomItem::dvReferenceField(QQmlJS::Dom::DirectVisitor, QStringView, QQmlJS::Dom::Path)’:
src/qmldom/qqmldomitem_p.h:991:27: note: ‘<anonymous>’ declared here
991 | return dvReference(visitor, PathEls::Field(f), referencedObject);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Change-Id: Iddb933f281024939b6acfffd1689d2fdea0adc61
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2021-06-18 23:58:21 +00:00
|
|
|
// Suppress GCC 11 warning about maybe-uninitialized copy of
|
|
|
|
// another Data. We're not sure if the compiler is actually right,
|
|
|
|
// but in this type of warning, it often isn't.
|
|
|
|
//#if defined(Q_CC_GNU) && Q_CC_GNU >= 1100
|
|
|
|
//QT_WARNING_DISABLE_GCC("-Wmaybe-uninitialized")
|
2023-09-29 13:32:33 +00:00
|
|
|
#include "qqmldomconstants_p.h"
|
2023-04-05 14:43:50 +00:00
|
|
|
#include "qqmldompath_p.h"
|
Suppress GCC11 warning about maybe-uninitialized data access
I don't *think* the compiler is correct. It's complaining that the
copy-construction of PathComponent in the lambda here:
bool dvReference(DirectVisitor visitor, const PathEls::PathComponent &c, Path referencedObject)
{
return dvItem(visitor, c, [c, this, referencedObject]() {
return this->subReferenceItem(c, referencedObject);
});
}
which was created as a temporary in dvReferenceField:
return dvReference(visitor, PathEls::Field(f), referencedObject);
could be copying garbage. Note that the warning is about copying a
PathComponent::Data of kind Key, whereas this inline sequence is of
Field.
Of course, the whole PathComponent::Data is a major ill-advised disaster
waiting to happen...
In copy constructor ‘QArrayDataPointer<T>::QArrayDataPointer(const QArrayDataPointer<T>&) [with T = char16_t]’,
inlined from ‘QString::QString(const QString&)’ at qstring.h:1232:58,
inlined from ‘QQmlJS::Dom::PathEls::Key::Key(const QQmlJS::Dom::PathEls::Key&)’ at src/qmldom/qqmldompath_p.h:164:7,
inlined from ‘QQmlJS::Dom::PathEls::PathComponent::Data::Data(const QQmlJS::Dom::PathEls::PathComponent::Data&)’ at src/qmldom/qqmldompath_p.h:369:37,
inlined from ‘QQmlJS::Dom::PathEls::PathComponent::PathComponent(const QQmlJS::Dom::PathEls::PathComponent&)’ at src/qmldom/qqmldompath_p.h:311:7,
inlined from ‘bool QQmlJS::Dom::DomItem::dvReference(QQmlJS::Dom::DirectVisitor, const QQmlJS::Dom::PathEls::PathComponent&, QQmlJS::Dom::Path)’ at src/qmldom/qqmldomitem_p.h:981:22,
inlined from ‘bool QQmlJS::Dom::DomItem::dvReferenceField(QQmlJS::Dom::DirectVisitor, QStringView, QQmlJS::Dom::Path)’ at src/qmldom/qqmldomitem_p.h:991:27:
qarraydatapointer.h:69:50: error: ‘*(const QArrayDataPointer<char16_t>*)((char*)&<unnamed> + offsetof(const QQmlJS::Dom::PathEls::PathComponent, QQmlJS::Dom::PathEls::PathComponent::data) + 8).QArrayDataPointer<char16_t>::size’ may be used uninitialized [-Werror=maybe-uninitialized]
69 | : d(other.d), ptr(other.ptr), size(other.size)
| ~~~~~~^~~~
In file included from src/qmldom/qqmldomelements_p.h:52,
from src/qmldom/qqmldomelements.cpp:38:
src/qmldom/qqmldomitem_p.h: In member function ‘bool QQmlJS::Dom::DomItem::dvReferenceField(QQmlJS::Dom::DirectVisitor, QStringView, QQmlJS::Dom::Path)’:
src/qmldom/qqmldomitem_p.h:991:27: note: ‘<anonymous>’ declared here
991 | return dvReference(visitor, PathEls::Field(f), referencedObject);
| ~~~~~~~~~~~^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Change-Id: Iddb933f281024939b6acfffd1689d2fdea0adc61
Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
2021-06-18 23:58:21 +00:00
|
|
|
#if defined(__GNUC__) && __GNUC__ >= 11
|
|
|
|
# pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
|
|
|
|
#endif
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
#include "qqmldomelements_p.h"
|
|
|
|
#include "qqmldomcomments_p.h"
|
|
|
|
#include "qqmldomastdumper_p.h"
|
|
|
|
#include "qqmldommock_p.h"
|
2021-03-23 14:37:32 +00:00
|
|
|
#include "qqmldomreformatter_p.h"
|
|
|
|
#include "qqmldomoutwriter_p.h"
|
|
|
|
#include "qqmldomlinewriter_p.h"
|
2021-03-23 10:02:18 +00:00
|
|
|
#include "qqmldomtop_p.h"
|
|
|
|
#include "qqmldomexternalitems_p.h"
|
|
|
|
|
|
|
|
#include <QtQml/private/qqmljslexer_p.h>
|
|
|
|
#include <QtQml/private/qqmljsparser_p.h>
|
|
|
|
#include <QtQml/private/qqmljsengine_p.h>
|
|
|
|
#include <QtQml/private/qqmljsastvisitor_p.h>
|
|
|
|
#include <QtQml/private/qqmljsast_p.h>
|
|
|
|
|
|
|
|
#include <QtCore/QScopeGuard>
|
|
|
|
#include <QtCore/QRegularExpression>
|
|
|
|
#include <QtCore/QDir>
|
|
|
|
#include <QtCore/QBasicMutex>
|
2021-12-06 21:18:28 +00:00
|
|
|
#include <QtCore/QUrl>
|
2021-03-23 10:02:18 +00:00
|
|
|
|
|
|
|
#include <optional>
|
2021-09-03 09:13:07 +00:00
|
|
|
#include <limits>
|
2021-03-23 10:02:18 +00:00
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE
|
|
|
|
|
2022-03-21 09:21:18 +00:00
|
|
|
using namespace Qt::StringLiterals;
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
namespace QQmlJS {
|
|
|
|
namespace Dom {
|
|
|
|
|
|
|
|
namespace Paths {
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path moduleIndexPath(const QString &uri, int majorVersion, const ErrorHandler &errorHandler)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
QString version = QString::number(majorVersion);
|
|
|
|
if (majorVersion == Version::Latest)
|
|
|
|
version = QLatin1String("Latest");
|
|
|
|
else if (majorVersion == Version::Undefined)
|
|
|
|
version = QString();
|
2021-12-06 21:18:28 +00:00
|
|
|
QRegularExpression moduleRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
|
|
|
|
auto m = moduleRe.match(uri);
|
|
|
|
if (!m.isValid())
|
|
|
|
Path::myErrors()
|
|
|
|
.error(Path::tr("Invalid module name in import %1").arg(uri))
|
|
|
|
.handle(errorHandler);
|
2025-01-10 14:54:53 +00:00
|
|
|
return Path::fromRoot(PathRoot::Env).withField(Fields::moduleIndexWithUri).withKey(uri).withKey(version);
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path moduleScopePath(const QString &uri, Version version, const ErrorHandler &)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return Path::fromRoot(PathRoot::Env)
|
|
|
|
.withField(Fields::moduleIndexWithUri)
|
|
|
|
.withKey(uri)
|
|
|
|
.withKey(version.majorSymbolicString())
|
|
|
|
.withField(Fields::moduleScope)
|
|
|
|
.withKey(version.minorString());
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path moduleScopePath(const QString &uri, const QString &version, const ErrorHandler &errorHandler)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
Version v = Version::fromString(version);
|
|
|
|
if (!version.isEmpty() && !(v.isValid() || v.isLatest()))
|
|
|
|
Path::myErrors().error(Path::tr("Invalid Version %1").arg(version)).handle(errorHandler);
|
|
|
|
return moduleScopePath(uri, v, errorHandler);
|
|
|
|
}
|
|
|
|
|
|
|
|
} // end namespace Paths
|
|
|
|
|
|
|
|
static ErrorGroups domParsingErrors()
|
|
|
|
{
|
|
|
|
static ErrorGroups res = { { DomItem::domErrorGroup, NewErrorGroup("Parsing") } };
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool CommentableDomElement::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void Component::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
DomElement::updatePathFromOwner(newPath);
|
2025-01-10 14:54:53 +00:00
|
|
|
updatePathFromOwnerMultiMap(m_enumerations, newPath.withField(Fields::enumerations));
|
|
|
|
updatePathFromOwnerQList(m_objects, newPath.withField(Fields::objects));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Component::Component(const QString &name) : CommentableDomElement(Path()), m_name(name) { }
|
2021-03-23 10:02:18 +00:00
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Component::Component(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
|
2021-03-23 10:02:18 +00:00
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool Component::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name());
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::enumerations, m_enumerations);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::objects, m_objects);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isSingleton, isSingleton());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isCreatable, isCreatable());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isComposite, isComposite());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::attachedTypeName, attachedTypeName());
|
|
|
|
cont = cont && self.dvReferenceField(visitor, Fields::attachedType, attachedTypePath(self));
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
DomItem Component::field(const DomItem &self, QStringView name) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2024-01-02 14:56:03 +00:00
|
|
|
if (name == Fields::name)
|
|
|
|
return self.wrapField(Fields::name, m_name);
|
|
|
|
if (name == Fields::objects)
|
|
|
|
return self.wrapField(Fields::objects, m_objects);
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
return DomBase::field(self, name);
|
|
|
|
}
|
|
|
|
|
|
|
|
Path Component::addObject(const QmlObject &object, QmlObject **oPtr)
|
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return appendUpdatableElementInQList(pathFromOwner().withField(Fields::objects), m_objects, object,
|
2021-03-23 10:02:18 +00:00
|
|
|
oPtr);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool QmlComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = Component::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::ids, m_ids);
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::subComponents, [this, &self]() {
|
|
|
|
return this->subComponents(self);
|
|
|
|
});
|
2024-01-19 17:31:07 +00:00
|
|
|
if (m_nameIdentifiers) {
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
|
|
|
|
return self.subScriptElementWrapperItem(m_nameIdentifiers);
|
|
|
|
});
|
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void QmlComponent::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
Component::updatePathFromOwner(newPath);
|
2025-01-10 14:54:53 +00:00
|
|
|
updatePathFromOwnerMultiMap(m_ids, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void QmlComponent::writeOut(const DomItem &self, OutWriter &lw) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
if (name().contains(QLatin1Char('.'))) {
|
|
|
|
// inline component
|
|
|
|
lw.ensureNewline()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(ComponentKeywordRegion)
|
2025-01-28 15:45:28 +00:00
|
|
|
.ensureSpace()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(IdentifierRegion, name().split(QLatin1Char('.')).last())
|
|
|
|
.writeRegion(ColonTokenRegion)
|
2025-01-28 15:45:28 +00:00
|
|
|
.ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
self.field(Fields::objects).index(0).writeOut(lw);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QList<QString> QmlComponent::subComponentsNames(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
DomItem components = self.owner().field(Fields::components);
|
2023-09-13 15:36:59 +00:00
|
|
|
const QSet<QString> cNames = components.keys();
|
2021-03-23 10:02:18 +00:00
|
|
|
QString myNameDot = self.pathFromOwner()[1].headName();
|
|
|
|
if (!myNameDot.isEmpty())
|
|
|
|
myNameDot += QLatin1Char('.');
|
|
|
|
QList<QString> subNames;
|
2023-09-13 15:36:59 +00:00
|
|
|
for (const QString &cName : cNames)
|
2021-03-23 10:02:18 +00:00
|
|
|
if (cName.startsWith(myNameDot)
|
2022-10-05 05:29:16 +00:00
|
|
|
&& !QStringView(cName).mid(myNameDot.size()).contains(QLatin1Char('.'))
|
2021-03-23 10:02:18 +00:00
|
|
|
&& !cName.isEmpty())
|
|
|
|
subNames.append(cName);
|
|
|
|
std::sort(subNames.begin(), subNames.end());
|
|
|
|
return subNames;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QList<DomItem> QmlComponent::subComponents(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
DomItem components = self.owner().field(Fields::components);
|
|
|
|
QList<DomItem> res;
|
2023-09-13 15:36:59 +00:00
|
|
|
for (const QString &cName : subComponentsNames(self))
|
|
|
|
for (const DomItem &comp : components.key(cName).values())
|
2021-03-23 10:02:18 +00:00
|
|
|
res.append(comp);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
|
|
|
Version Version::fromString(QStringView v)
|
|
|
|
{
|
|
|
|
if (v.isEmpty())
|
|
|
|
return Version(Latest, Latest);
|
|
|
|
QRegularExpression r(
|
|
|
|
QRegularExpression::anchoredPattern(QStringLiteral(uR"(([0-9]*)(?:\.([0-9]*))?)")));
|
2022-08-01 09:03:27 +00:00
|
|
|
auto m = r.matchView(v);
|
2021-03-23 10:02:18 +00:00
|
|
|
if (m.hasMatch()) {
|
|
|
|
bool ok;
|
2022-08-01 09:03:27 +00:00
|
|
|
int majorV = m.capturedView(1).toInt(&ok);
|
2021-03-23 10:02:18 +00:00
|
|
|
if (!ok)
|
|
|
|
majorV = Version::Undefined;
|
2022-08-01 09:03:27 +00:00
|
|
|
int minorV = m.capturedView(2).toInt(&ok);
|
2021-03-23 10:02:18 +00:00
|
|
|
if (!ok)
|
|
|
|
minorV = Version::Undefined;
|
|
|
|
return Version(majorV, minorV);
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
Version::Version(qint32 majorV, qint32 minorV) : majorVersion(majorV), minorVersion(minorV) { }
|
|
|
|
|
|
|
|
bool Version::isLatest() const
|
|
|
|
{
|
|
|
|
return majorVersion == Latest && minorVersion == Latest;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Version::isValid() const
|
|
|
|
{
|
|
|
|
return majorVersion >= 0 && minorVersion >= 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString Version::stringValue() const
|
|
|
|
{
|
|
|
|
if (isLatest())
|
|
|
|
return QString();
|
|
|
|
if (minorVersion < 0) {
|
|
|
|
if (majorVersion < 0)
|
|
|
|
return QLatin1String(".");
|
|
|
|
else
|
|
|
|
return QString::number(majorVersion);
|
|
|
|
}
|
|
|
|
if (majorVersion < 0)
|
|
|
|
return QLatin1String(".") + QString::number(minorVersion);
|
|
|
|
return QString::number(majorVersion) + QChar::fromLatin1('.') + QString::number(minorVersion);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool Version::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::majorVersion, majorVersion);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::minorVersion, minorVersion);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isLatest, isLatest());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isValid, isValid());
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::stringValue, [this]() {
|
|
|
|
return this->stringValue();
|
|
|
|
});
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
QRegularExpression Import::importRe()
|
|
|
|
{
|
|
|
|
static QRegularExpression res(QRegularExpression::anchoredPattern(QStringLiteral(
|
|
|
|
uR"((?<uri>\w+(?:\.\w+)*)(?:\W+(?<version>[0-9]+(?:\.[0-9]*)?))?(?:\W+as\W+(?<id>\w+))?$)")));
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Import Import::fromUriString(
|
|
|
|
const QString &importStr, Version v, const QString &importId, const ErrorHandler &handler)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2021-12-06 21:18:28 +00:00
|
|
|
auto m = importRe().match(importStr);
|
|
|
|
if (m.hasMatch()) {
|
|
|
|
if (v.majorVersion == Version::Undefined && v.minorVersion == Version::Undefined)
|
|
|
|
v = Version::fromString(m.captured(2));
|
|
|
|
else if (!m.captured(u"version").isEmpty())
|
|
|
|
domParsingErrors()
|
|
|
|
.warning(tr("Version %1 in import string '%2' overridden by explicit "
|
|
|
|
"version %3")
|
|
|
|
.arg(m.captured(2), importStr, v.stringValue()))
|
|
|
|
.handle(handler);
|
2023-12-13 14:24:34 +00:00
|
|
|
QString resolvedImportId;
|
|
|
|
if (importId.isEmpty()) {
|
|
|
|
resolvedImportId = m.captured(u"importId");
|
|
|
|
} else {
|
|
|
|
if (!m.captured(u"importId").isEmpty()) {
|
|
|
|
domParsingErrors()
|
|
|
|
.warning(tr("namespace %1 in import string '%2' overridden by explicit "
|
|
|
|
"importId %3")
|
|
|
|
.arg(m.captured(u"importId"), importStr, importId))
|
|
|
|
.handle(handler);
|
|
|
|
}
|
|
|
|
resolvedImportId = importId;
|
|
|
|
}
|
|
|
|
|
|
|
|
return Import(QmlUri::fromUriString(m.captured(u"uri").trimmed()), v, resolvedImportId);
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
2021-12-06 21:18:28 +00:00
|
|
|
domParsingErrors()
|
|
|
|
.error(tr("Unexpected URI format in import '%1'").arg(importStr))
|
|
|
|
.handle(handler);
|
|
|
|
return Import();
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Import Import::fromFileString(
|
|
|
|
const QString &importStr, const QString &importId, const ErrorHandler &)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2021-12-06 21:18:28 +00:00
|
|
|
return Import(QmlUri::fromDirectoryString(importStr), Version(), importId);
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool Import::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
2021-12-06 21:18:28 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::uri, uri.toString());
|
2021-03-23 10:02:18 +00:00
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::version, version);
|
|
|
|
if (!importId.isEmpty())
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::importId, importId);
|
|
|
|
if (implicit)
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::implicit, implicit);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 18:21:24 +00:00
|
|
|
void Import::writeOut(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
if (implicit)
|
|
|
|
return;
|
2023-09-13 18:21:24 +00:00
|
|
|
|
|
|
|
QString code;
|
|
|
|
const DomItem owner = self.owner();
|
|
|
|
if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
|
|
|
|
code = qmlFilePtr->code();
|
|
|
|
|
|
|
|
// check for an empty line before the import, and preserve it
|
|
|
|
int preNewlines = 0;
|
|
|
|
|
2024-11-04 23:38:51 +00:00
|
|
|
const FileLocations::Tree elLoc = FileLocations::treeOf(self);
|
2023-09-13 18:21:24 +00:00
|
|
|
|
|
|
|
quint32 start = elLoc->info().fullRegion.offset;
|
|
|
|
if (size_t(code.size()) >= start) {
|
|
|
|
while (start != 0) {
|
|
|
|
QChar c = code.at(--start);
|
|
|
|
if (c == u'\n') {
|
|
|
|
if (++preNewlines == 2)
|
|
|
|
break;
|
|
|
|
} else if (!c.isSpace())
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (preNewlines == 0)
|
|
|
|
++preNewlines;
|
|
|
|
|
|
|
|
ow.ensureNewline(preNewlines);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(ImportTokenRegion).ensureSpace();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(ImportUriRegion, uri.toString());
|
2021-12-06 21:18:28 +00:00
|
|
|
if (uri.isModule()) {
|
2021-03-23 14:37:32 +00:00
|
|
|
QString vString = version.stringValue();
|
|
|
|
if (!vString.isEmpty())
|
2025-06-20 14:10:26 +00:00
|
|
|
ow.ensureSpace().writeRegion(VersionRegion, vString);
|
2025-01-28 15:45:28 +00:00
|
|
|
}
|
|
|
|
if (!importId.isEmpty()) {
|
|
|
|
ow.ensureSpace().writeRegion(AsTokenRegion).ensureSpace().writeRegion(IdNameRegion,
|
|
|
|
importId);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Id::Id(const QString &idName, const Path &referredObject) : name(idName), referredObjectPath(referredObject) { }
|
2021-03-23 10:02:18 +00:00
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool Id::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name);
|
|
|
|
cont = cont && self.dvReferenceField(visitor, Fields::referredObject, referredObjectPath);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
|
2023-05-04 13:17:58 +00:00
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::value, value);
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void Id::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
updatePathFromOwnerQList(annotations, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path Id::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations), annotations,
|
2021-03-23 10:02:18 +00:00
|
|
|
annotation, aPtr);
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
QmlObject::QmlObject(const Path &pathFromOwner) : CommentableDomElement(pathFromOwner) { }
|
2021-03-23 10:02:18 +00:00
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool QmlObject::iterateBaseDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
|
|
|
|
if (!idStr().isEmpty())
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::idStr, idStr());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name());
|
|
|
|
if (!prototypePaths().isEmpty())
|
|
|
|
cont = cont && self.dvReferencesField(visitor, Fields::prototypes, m_prototypePaths);
|
|
|
|
if (nextScopePath())
|
|
|
|
cont = cont && self.dvReferenceField(visitor, Fields::nextScope, nextScopePath());
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::propertyDefs, m_propertyDefs);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::bindings, m_bindings);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::methods, m_methods);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::children, m_children);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::propertyInfos, [this, &self]() {
|
|
|
|
return self.subMapItem(Map(
|
2025-01-10 14:54:53 +00:00
|
|
|
pathFromOwner().withField(Fields::propertyInfos),
|
2023-12-13 14:24:34 +00:00
|
|
|
[&self](const DomItem &map, const QString &k) {
|
2021-03-23 10:02:18 +00:00
|
|
|
auto pInfo = self.propertyInfoWithName(k);
|
|
|
|
return map.wrap(PathEls::Key(k), pInfo);
|
|
|
|
},
|
2023-09-13 13:05:12 +00:00
|
|
|
[&self](const DomItem &) { return self.propertyInfoNames(); },
|
2021-03-23 10:02:18 +00:00
|
|
|
QLatin1String("PropertyInfo")));
|
|
|
|
});
|
qmldom: make type a FieldMemberExpression
This commit prepares the Dom to be able to provide correct
completions on binding scripts ending with a `.`.
In addition to saving qualified types as a string, like `QQ.Item` for
example, save them also as a FieldMemberExpression. Do this for
QmlObjects, like `QQ.Item {}` for example, and type annotations, like
`function f(): QQ.Item {}` for example.
This will allow qmlls to suggest completions after `root.` in cases
like
```
x: root.
SomeQualifiedModule.Item {}
```
for example, or after `QQ.` in cases like
```
(x as QQ.Item)
```
for example. The latter magically makes the asCompletion test work
(which was previously QEXPECT_FAIL'd).
Prior to this commit, the Dom did not contain enough information to
know where exactly the completion gets requested inside of
`root.SomeQualifiedModule.Item` and therefore could not suggest any
meaningful suggestion.
Basically reuses a44f21f19462cc79f82080404515c4322b7728ee to model the
nameIdentifiers, a FieldMemberExpression that contains the current type
of the QmlObject.
Adapt existing tests propertyDefinitionBinding and
ignoreNonRelatedTypesForpropertyDefinitionBinding to the changes in the
Dom: the qml object type in a binding, for example `Item` in
`myBinding: Item {}`, is now part of the QmlObject. It was part of the
Binding prior to this commit.
Adapt the qmllsutils method to the change in structure.
Pick-to: 6.7
Task-number: QTBUG-119839
Change-Id: Ie7bc7692f731a01467392dc1dffdf7e67c4d7c46
Reviewed-by: Ulf Hermann <ulf.hermann@qt.io>
2023-12-19 11:05:42 +00:00
|
|
|
if (m_nameIdentifiers) {
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::nameIdentifiers, [this, &self]() {
|
|
|
|
return self.subScriptElementWrapperItem(m_nameIdentifiers);
|
|
|
|
});
|
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QString> QmlObject::fields() const
|
|
|
|
{
|
|
|
|
static QList<QString> myFields(
|
2025-03-14 12:22:59 +00:00
|
|
|
{ Fields::comments.toString(), Fields::idStr.toString(),
|
|
|
|
Fields::name.toString(), Fields::prototypes.toString(),
|
|
|
|
Fields::nextScope.toString(), Fields::propertyDefs.toString(),
|
|
|
|
Fields::bindings.toString(), Fields::methods.toString(),
|
|
|
|
Fields::children.toString(), Fields::annotations.toString(),
|
|
|
|
Fields::propertyInfos.toString() });
|
2021-03-23 10:02:18 +00:00
|
|
|
return myFields;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool QmlObject::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = iterateBaseDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::defaultPropertyName, [this, &self]() {
|
|
|
|
return defaultPropertyName(self);
|
|
|
|
});
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
DomItem QmlObject::field(const DomItem &self, QStringView name) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2024-01-02 14:56:03 +00:00
|
|
|
if (name == Fields::name)
|
|
|
|
return self.subDataItem(PathEls::Field(Fields::name), this->name());
|
|
|
|
if (name == Fields::idStr) {
|
|
|
|
if (idStr().isEmpty())
|
|
|
|
return DomItem();
|
|
|
|
return self.subDataItem(PathEls::Field(Fields::idStr), idStr());
|
|
|
|
}
|
|
|
|
if (name == Fields::methods)
|
|
|
|
return self.wrapField(Fields::methods, m_methods);
|
|
|
|
if (name == Fields::bindings)
|
|
|
|
return self.wrapField(Fields::bindings, m_bindings);
|
|
|
|
if (name == Fields::comments)
|
|
|
|
return CommentableDomElement::field(self, name);
|
|
|
|
if (name == Fields::children)
|
|
|
|
return self.wrapField(Fields::children, m_children);
|
|
|
|
|
|
|
|
if (name == Fields::nextScope) {
|
|
|
|
if (nextScopePath())
|
|
|
|
return self.subReferenceItem(PathEls::Field(Fields::nextScope), nextScopePath());
|
|
|
|
else
|
|
|
|
return DomItem();
|
|
|
|
}
|
|
|
|
if (name == Fields::prototypes) {
|
|
|
|
if (prototypePaths().isEmpty())
|
|
|
|
return DomItem();
|
|
|
|
return self.subReferencesItem(PathEls::Field(Fields::prototypes), m_prototypePaths);
|
|
|
|
}
|
|
|
|
if (name == Fields::annotations)
|
|
|
|
return self.wrapField(Fields::annotations, m_annotations);
|
|
|
|
if (name == Fields::propertyDefs)
|
2021-03-23 10:02:18 +00:00
|
|
|
return self.wrapField(Fields::propertyDefs, m_propertyDefs);
|
2024-01-02 14:56:03 +00:00
|
|
|
if (name == Fields::propertyInfos) {
|
|
|
|
// Need to explicitly copy self here since we might store this and call it later.
|
|
|
|
return self.subMapItem(Map(
|
2025-01-10 14:54:53 +00:00
|
|
|
pathFromOwner().withField(Fields::propertyInfos),
|
2024-01-02 14:56:03 +00:00
|
|
|
[copiedSelf = self](const DomItem &map, const QString &k) {
|
|
|
|
return map.wrap(PathEls::Key(k), copiedSelf.propertyInfoWithName(k));
|
|
|
|
},
|
|
|
|
[copiedSelf = self](const DomItem &) { return copiedSelf.propertyInfoNames(); },
|
|
|
|
QLatin1String("PropertyInfo")));
|
|
|
|
}
|
|
|
|
if (name == Fields::nameIdentifiers && m_nameIdentifiers) {
|
|
|
|
return self.subScriptElementWrapperItem(m_nameIdentifiers);
|
|
|
|
}
|
|
|
|
if (name == Fields::defaultPropertyName) {
|
|
|
|
return self.subDataItem(PathEls::Field(Fields::defaultPropertyName),
|
|
|
|
defaultPropertyName(self));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
2025-03-14 12:22:59 +00:00
|
|
|
static QStringList knownLookups({ Fields::fileLocationsTree.toString() });
|
2024-01-02 14:56:03 +00:00
|
|
|
if (!knownLookups.contains(name)) {
|
2021-03-23 10:02:18 +00:00
|
|
|
qCWarning(domLog()) << "Asked non existing field " << name << " in QmlObject "
|
|
|
|
<< pathFromOwner();
|
2024-01-02 14:56:03 +00:00
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
return DomItem();
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void QmlObject::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
DomElement::updatePathFromOwner(newPath);
|
2025-01-10 14:54:53 +00:00
|
|
|
updatePathFromOwnerMultiMap(m_propertyDefs, newPath.withField(Fields::propertyDefs));
|
|
|
|
updatePathFromOwnerMultiMap(m_bindings, newPath.withField(Fields::bindings));
|
|
|
|
updatePathFromOwnerMultiMap(m_methods, newPath.withField(Fields::methods));
|
|
|
|
updatePathFromOwnerQList(m_children, newPath.withField(Fields::children));
|
|
|
|
updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlObject::localDefaultPropertyName() const
|
|
|
|
{
|
|
|
|
if (!m_defaultPropertyName.isEmpty())
|
|
|
|
return m_defaultPropertyName;
|
|
|
|
for (const PropertyDefinition &pDef : m_propertyDefs)
|
|
|
|
if (pDef.isDefaultMember)
|
|
|
|
return pDef.name;
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QString QmlObject::defaultPropertyName(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
QString dProp = localDefaultPropertyName();
|
|
|
|
if (!dProp.isEmpty())
|
|
|
|
return dProp;
|
|
|
|
QString res = QStringLiteral(u"data");
|
|
|
|
self.visitPrototypeChain(
|
2023-09-13 13:05:12 +00:00
|
|
|
[&res](const DomItem &obj) {
|
2021-03-23 10:02:18 +00:00
|
|
|
if (const QmlObject *objPtr = obj.as<QmlObject>()) {
|
|
|
|
QString dProp = objPtr->localDefaultPropertyName();
|
|
|
|
if (!dProp.isEmpty()) {
|
|
|
|
res = dProp;
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
},
|
|
|
|
VisitPrototypesOption::SkipFirst);
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool QmlObject::iterateSubOwners(const DomItem &self, function_ref<bool(const DomItem &)> visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2023-12-13 14:24:34 +00:00
|
|
|
bool cont = self.field(Fields::bindings).visitKeys([visitor](const QString &, const DomItem &bs) {
|
2023-09-13 13:05:12 +00:00
|
|
|
return bs.visitIndexes([visitor](const DomItem &b) {
|
2021-03-23 10:02:18 +00:00
|
|
|
DomItem v = b.field(Fields::value);
|
|
|
|
if (std::shared_ptr<ScriptExpression> vPtr = v.ownerAs<ScriptExpression>()) {
|
|
|
|
if (!visitor(v))
|
|
|
|
return false;
|
|
|
|
return v.iterateSubOwners(visitor); // currently not needed, avoid?
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
});
|
2023-09-13 13:05:12 +00:00
|
|
|
cont = cont && self.field(Fields::children).visitIndexes([visitor](const DomItem &qmlObj) {
|
2021-03-23 10:02:18 +00:00
|
|
|
if (const QmlObject *qmlObjPtr = qmlObj.as<QmlObject>()) {
|
|
|
|
return qmlObjPtr->iterateSubOwners(qmlObj, visitor);
|
|
|
|
}
|
|
|
|
Q_ASSERT(false);
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
static QStringList dotExpressionToList(const std::shared_ptr<ScriptExpression> &expr)
|
2022-01-20 10:01:54 +00:00
|
|
|
{
|
|
|
|
QStringList res;
|
|
|
|
AST::Node *node = (expr ? expr->ast() : nullptr);
|
|
|
|
while (node) {
|
|
|
|
switch (node->kind) {
|
|
|
|
case AST::Node::Kind_IdentifierExpression: {
|
|
|
|
AST::IdentifierExpression *id = AST::cast<AST::IdentifierExpression *>(node);
|
|
|
|
res.prepend(id->name.toString());
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
case AST::Node::Kind_FieldMemberExpression: {
|
|
|
|
AST::FieldMemberExpression *id = AST::cast<AST::FieldMemberExpression *>(node);
|
|
|
|
res.prepend(id->name.toString());
|
|
|
|
node = id->base;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
qCDebug(writeOutLog).noquote() << "Could not convert dot expression to list for:\n"
|
|
|
|
<< expr->astRelocatableDump();
|
|
|
|
return QStringList();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self,
|
2022-01-20 10:01:54 +00:00
|
|
|
std::shared_ptr<ScriptExpression> accessSequence) const
|
|
|
|
{
|
|
|
|
QStringList accessSequenceList = dotExpressionToList(accessSequence);
|
|
|
|
return resolveAlias(self, accessSequenceList);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
LocallyResolvedAlias QmlObject::resolveAlias(const DomItem &self, const QStringList &accessSequence) const
|
2022-01-20 10:01:54 +00:00
|
|
|
{
|
|
|
|
LocallyResolvedAlias res;
|
|
|
|
QSet<QString> visitedAlias;
|
|
|
|
if (accessSequence.isEmpty()) {
|
|
|
|
return res;
|
|
|
|
} else if (accessSequence.size() > 3) {
|
|
|
|
res.status = LocallyResolvedAlias::Status::TooDeep;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
QString idName = accessSequence.first();
|
|
|
|
DomItem idTarget = self.component()
|
|
|
|
.field(Fields::ids)
|
|
|
|
.key(idName)
|
|
|
|
.index(0)
|
|
|
|
.field(Fields::referredObject)
|
|
|
|
.get();
|
|
|
|
if (!idTarget)
|
|
|
|
return res;
|
|
|
|
res.baseObject = idTarget;
|
|
|
|
res.accessedPath = accessSequence.mid(1);
|
|
|
|
res.typeName = idTarget.name();
|
|
|
|
res.status = LocallyResolvedAlias::Status::ResolvedObject;
|
|
|
|
// check if it refers to locally defined props/objs
|
|
|
|
while (!res.accessedPath.isEmpty()) {
|
|
|
|
QString pNow = res.accessedPath.first();
|
|
|
|
DomItem defNow = res.baseObject.propertyDefs().key(pNow).index(0);
|
|
|
|
if (const PropertyDefinition *defNowPtr = defNow.as<PropertyDefinition>()) {
|
|
|
|
if (defNowPtr->isAlias()) {
|
|
|
|
res.typeName = QString();
|
|
|
|
++res.nAliases;
|
|
|
|
QString aliasPath = defNow.canonicalPath().toString();
|
|
|
|
if (visitedAlias.contains(aliasPath)) {
|
|
|
|
res.status = LocallyResolvedAlias::Status::Loop;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
visitedAlias.insert(aliasPath);
|
|
|
|
DomItem valNow = res.baseObject.bindings().key(pNow).index(0);
|
|
|
|
if (std::shared_ptr<ScriptExpression> exp =
|
|
|
|
valNow.field(Fields::value).ownerAs<ScriptExpression>()) {
|
|
|
|
QStringList expList = dotExpressionToList(exp);
|
|
|
|
if (expList.isEmpty()) {
|
|
|
|
res.status = LocallyResolvedAlias::Status::Invalid;
|
|
|
|
return res;
|
|
|
|
} else if (expList.size() > 3) {
|
|
|
|
res.status = LocallyResolvedAlias::Status::TooDeep;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
idName = expList.first();
|
|
|
|
idTarget = self.component()
|
|
|
|
.field(Fields::ids)
|
|
|
|
.key(idName)
|
|
|
|
.index(0)
|
|
|
|
.field(Fields::referredObject)
|
|
|
|
.get();
|
|
|
|
res.baseObject = idTarget;
|
|
|
|
res.accessedPath = expList.mid(1) + res.accessedPath.mid(1);
|
|
|
|
if (!idTarget) {
|
|
|
|
res.status = LocallyResolvedAlias::Status::Invalid;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
res.status = LocallyResolvedAlias::Status::ResolvedObject;
|
|
|
|
res.typeName = idTarget.name();
|
|
|
|
} else {
|
|
|
|
res.status = LocallyResolvedAlias::Status::Invalid;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
res.localPropertyDef = defNow;
|
|
|
|
res.typeName = defNowPtr->typeName;
|
|
|
|
res.accessedPath = res.accessedPath.mid(1);
|
|
|
|
DomItem valNow = res.baseObject.bindings().key(pNow).index(0).field(Fields::value);
|
|
|
|
if (valNow.internalKind() == DomType::QmlObject) {
|
|
|
|
res.baseObject = valNow;
|
|
|
|
res.typeName = valNow.name();
|
|
|
|
res.status = LocallyResolvedAlias::Status::ResolvedObject;
|
|
|
|
} else {
|
|
|
|
res.status = LocallyResolvedAlias::Status::ResolvedProperty;
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 15:36:59 +00:00
|
|
|
MutableDomItem QmlObject::addPropertyDef(
|
|
|
|
MutableDomItem &self, const PropertyDefinition &propertyDef, AddOption option)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
Path p = addPropertyDef(propertyDef, option);
|
|
|
|
if (p.last().headIndex(0) > 1)
|
|
|
|
self.owningItemPtr()->addErrorLocal(domParsingErrors().error(
|
|
|
|
tr("Repeated PropertyDefinition with name %1").arg(propertyDef.name)));
|
|
|
|
return self.owner().path(p);
|
|
|
|
}
|
|
|
|
|
|
|
|
MutableDomItem QmlObject::addBinding(MutableDomItem &self, Binding binding, AddOption option)
|
|
|
|
{
|
|
|
|
Path p = addBinding(binding, option);
|
|
|
|
if (p && p.last().headIndex(0) > 1)
|
|
|
|
self.owningItemPtr()->addErrorLocal(
|
|
|
|
domParsingErrors().error(tr("Repeated binding with name %1").arg(binding.name())));
|
|
|
|
return self.owner().path(p);
|
|
|
|
}
|
|
|
|
|
2023-09-13 15:36:59 +00:00
|
|
|
MutableDomItem QmlObject::addMethod(
|
|
|
|
MutableDomItem &self, const MethodInfo &functionDef, AddOption option)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
Path p = addMethod(functionDef, option);
|
|
|
|
if (p.last().headIndex(0) > 1)
|
|
|
|
self.owningItemPtr()->addErrorLocal(
|
|
|
|
domParsingErrors().error(tr("Repeated Method with name %1").arg(functionDef.name)));
|
|
|
|
return self.owner().path(p);
|
|
|
|
}
|
|
|
|
|
2024-11-04 09:58:49 +00:00
|
|
|
void QmlObject::writeOutId(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
if (!idStr().isEmpty()) { // *always* put id first
|
|
|
|
DomItem myId = self.component().field(Fields::ids).key(idStr()).index(0);
|
|
|
|
if (myId)
|
|
|
|
myId.writeOutPre(ow);
|
|
|
|
ow.ensureNewline()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(IdTokenRegion)
|
|
|
|
.writeRegion(IdColonTokenRegion)
|
2025-01-28 15:45:28 +00:00
|
|
|
.ensureSpace()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(IdNameRegion, idStr());
|
2022-11-06 19:42:07 +00:00
|
|
|
if (ow.lineWriter.options().attributesSequence
|
|
|
|
== LineWriterOptions::AttributesSequence::Normalize) {
|
|
|
|
ow.ensureNewline(2);
|
|
|
|
}
|
2023-09-10 18:17:44 +00:00
|
|
|
if (myId) {
|
2021-03-23 14:37:32 +00:00
|
|
|
myId.writeOutPost(ow);
|
2023-09-10 18:17:44 +00:00
|
|
|
ow.ensureNewline(1);
|
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
|
2025-04-10 08:20:35 +00:00
|
|
|
QList<std::pair<SourceLocation, DomItem>> QmlObject::orderOfAttributes(const DomItem &self,
|
2024-11-04 09:58:49 +00:00
|
|
|
const DomItem &component) const
|
|
|
|
{
|
2023-12-13 14:24:34 +00:00
|
|
|
auto startLoc = [&](const FileLocations::Tree &l) {
|
2021-03-23 14:37:32 +00:00
|
|
|
if (l)
|
|
|
|
return l->info().fullRegion;
|
2021-09-03 09:13:07 +00:00
|
|
|
return SourceLocation(posOfNewElements, 0, 0, 0);
|
2021-03-23 14:37:32 +00:00
|
|
|
};
|
2024-11-04 09:58:49 +00:00
|
|
|
|
2025-04-10 08:20:35 +00:00
|
|
|
QList<std::pair<SourceLocation, DomItem>> attribs;
|
2024-11-04 23:38:51 +00:00
|
|
|
const auto objLocPtr = FileLocations::treeOf(self);
|
2024-11-04 09:58:49 +00:00
|
|
|
FileLocations::Tree componentLoc;
|
2024-11-04 23:38:51 +00:00
|
|
|
if (component && objLocPtr)
|
|
|
|
componentLoc = objLocPtr->parent()->parent();
|
2024-11-04 09:58:49 +00:00
|
|
|
auto addMMap = [&attribs, &startLoc](const DomItem &base, const FileLocations::Tree &baseLoc) {
|
|
|
|
if (!base)
|
|
|
|
return;
|
|
|
|
const auto values = base.values();
|
|
|
|
for (const auto &els : values) {
|
|
|
|
FileLocations::Tree elsLoc = FileLocations::find(baseLoc, els.pathFromOwner().last());
|
|
|
|
const auto elsValues = els.values();
|
|
|
|
for (const auto &el : elsValues) {
|
|
|
|
FileLocations::Tree elLoc = FileLocations::find(elsLoc, el.pathFromOwner().last());
|
2021-03-23 14:37:32 +00:00
|
|
|
attribs.append(std::make_pair(startLoc(elLoc), el));
|
|
|
|
}
|
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
};
|
2024-11-04 23:38:51 +00:00
|
|
|
auto addMyMMap = [this, &objLocPtr, &self, &addMMap](QStringView fieldName) {
|
2024-11-04 09:58:49 +00:00
|
|
|
DomItem base = this->field(self, fieldName);
|
2024-11-04 23:38:51 +00:00
|
|
|
addMMap(base, FileLocations::find(objLocPtr, base.pathFromOwner().last()));
|
2024-11-04 09:58:49 +00:00
|
|
|
};
|
|
|
|
auto addSingleLevel = [&attribs, &startLoc](const DomItem &base,
|
|
|
|
const FileLocations::Tree &baseLoc) {
|
|
|
|
if (!base)
|
|
|
|
return;
|
|
|
|
const auto baseValues = base.values();
|
|
|
|
for (const auto &el : baseValues) {
|
|
|
|
FileLocations::Tree elLoc = FileLocations::find(baseLoc, el.pathFromOwner().last());
|
|
|
|
attribs.append(std::make_pair(startLoc(elLoc), el));
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
};
|
|
|
|
if (component) {
|
|
|
|
DomItem enums = component.field(Fields::enumerations);
|
|
|
|
addMMap(enums, FileLocations::find(componentLoc, enums.pathFromOwner().last()));
|
|
|
|
}
|
|
|
|
addMyMMap(Fields::propertyDefs);
|
|
|
|
addMyMMap(Fields::bindings);
|
|
|
|
addMyMMap(Fields::methods);
|
|
|
|
DomItem children = field(self, Fields::children);
|
2024-11-04 23:38:51 +00:00
|
|
|
addSingleLevel(children, FileLocations::find(objLocPtr, children.pathFromOwner().last()));
|
2024-11-04 09:58:49 +00:00
|
|
|
if (component) {
|
|
|
|
DomItem subCs = component.field(Fields::subComponents);
|
|
|
|
for (const DomItem &c : subCs.values()) {
|
2024-11-04 23:38:51 +00:00
|
|
|
const auto subLocPtr = FileLocations::treeOf(c);
|
|
|
|
Q_ASSERT(subLocPtr);
|
|
|
|
attribs.append(std::make_pair(startLoc(subLocPtr), c));
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
std::stable_sort(attribs.begin(), attribs.end(),
|
|
|
|
[](const std::pair<SourceLocation, DomItem> &el1,
|
|
|
|
const std::pair<SourceLocation, DomItem> &el2) {
|
|
|
|
if (el1.first.offset < el2.first.offset)
|
|
|
|
return true;
|
|
|
|
if (el1.first.offset > el2.first.offset)
|
|
|
|
return false;
|
|
|
|
int i = int(el1.second.internalKind()) - int(el2.second.internalKind());
|
|
|
|
return i < 0;
|
|
|
|
});
|
|
|
|
return attribs;
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlObject::writeOutAttributes(const DomItem &self, OutWriter &ow, const DomItem &component,
|
|
|
|
const QString &code) const
|
|
|
|
{
|
2025-04-10 08:20:35 +00:00
|
|
|
const QList<std::pair<SourceLocation, DomItem>> attribs = orderOfAttributes(self, component);
|
2024-11-04 09:58:49 +00:00
|
|
|
qsizetype iAttr = 0;
|
|
|
|
while (iAttr != attribs.size()) {
|
|
|
|
auto &el = attribs[iAttr++];
|
|
|
|
// check for an empty line before the current element, and preserve it
|
|
|
|
int preNewlines = 0;
|
|
|
|
quint32 start = el.first.offset;
|
|
|
|
if (start != posOfNewElements && size_t(code.size()) >= start) {
|
|
|
|
while (start != 0) {
|
|
|
|
QChar c = code.at(--start);
|
|
|
|
if (c == u'\n') {
|
|
|
|
if (++preNewlines == 2)
|
2021-09-03 09:13:07 +00:00
|
|
|
break;
|
2024-11-04 09:58:49 +00:00
|
|
|
} else if (!c.isSpace())
|
|
|
|
break;
|
2021-09-03 09:13:07 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
if (preNewlines == 0)
|
|
|
|
++preNewlines;
|
|
|
|
ow.ensureNewline(preNewlines);
|
|
|
|
if (el.second.internalKind() == DomType::PropertyDefinition && iAttr != attribs.size()
|
|
|
|
&& el.first.offset != ~quint32(0)) {
|
|
|
|
DomItem b;
|
|
|
|
auto &bPair = attribs[iAttr];
|
|
|
|
if (bPair.second.internalKind() == DomType::Binding
|
|
|
|
&& bPair.first.begin() < el.first.end()
|
|
|
|
&& bPair.second.name() == el.second.name()) {
|
|
|
|
b = bPair.second;
|
|
|
|
++iAttr;
|
|
|
|
b.writeOutPre(ow);
|
|
|
|
}
|
|
|
|
el.second.writeOut(ow);
|
|
|
|
if (b) {
|
2025-06-20 14:10:26 +00:00
|
|
|
ow.writeRegion(ColonTokenRegion);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace();
|
2024-11-04 09:58:49 +00:00
|
|
|
if (const Binding *bPtr = b.as<Binding>())
|
|
|
|
bPtr->writeOutValue(b, ow);
|
|
|
|
else {
|
|
|
|
qWarning() << "Internal error casting binding to Binding in"
|
|
|
|
<< b.canonicalPath();
|
|
|
|
ow.writeRegion(LeftBraceRegion).writeRegion(RightBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
b.writeOutPost(ow);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
} else {
|
|
|
|
el.second.writeOut(ow);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
ow.ensureNewline();
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
|
2024-11-04 09:58:49 +00:00
|
|
|
void QmlObject::writeOutSortedEnumerations(const DomItem &component, OutWriter &ow) const
|
|
|
|
{
|
|
|
|
const auto descs = component.field(Fields::enumerations).values();
|
|
|
|
for (const auto &enumDescs : descs) {
|
|
|
|
const auto values = enumDescs.values();
|
|
|
|
for (const auto &enumDesc : values) {
|
|
|
|
ow.ensureNewline(1);
|
|
|
|
enumDesc.writeOut(ow);
|
|
|
|
ow.ensureNewline(1);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmlObject::writeOutSortedPropertyDefinition(const DomItem &self, OutWriter &ow,
|
|
|
|
QSet<QString> &mergedDefBinding) const
|
|
|
|
{
|
|
|
|
DomItem propertyDefs = field(self, Fields::propertyDefs);
|
|
|
|
DomItem bindings = field(self, Fields::bindings);
|
|
|
|
|
2021-09-28 02:54:30 +00:00
|
|
|
for (const QString &defName : propertyDefs.sortedKeys()) {
|
2023-09-13 15:36:59 +00:00
|
|
|
const auto pDefs = propertyDefs.key(defName).values();
|
|
|
|
for (const auto &pDef : pDefs) {
|
2021-09-28 02:54:30 +00:00
|
|
|
const PropertyDefinition *pDefPtr = pDef.as<PropertyDefinition>();
|
|
|
|
Q_ASSERT(pDefPtr);
|
2021-03-23 14:37:32 +00:00
|
|
|
DomItem b;
|
2022-10-05 05:29:16 +00:00
|
|
|
bool uniqueDeclarationWithThisName = pDefs.size() == 1;
|
2021-09-28 02:54:30 +00:00
|
|
|
if (uniqueDeclarationWithThisName && !pDefPtr->isRequired)
|
2023-09-13 13:05:12 +00:00
|
|
|
bindings.key(pDef.name()).visitIndexes([&b, pDefPtr](const DomItem &el) {
|
2021-09-28 02:54:30 +00:00
|
|
|
const Binding *elPtr = el.as<Binding>();
|
|
|
|
if (elPtr && elPtr->bindingType() == BindingType::Normal) {
|
|
|
|
switch (elPtr->valueKind()) {
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
b = el;
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
2024-11-04 09:58:49 +00:00
|
|
|
if (!pDefPtr->isDefaultMember && pDefPtr->isParametricType())
|
2021-09-28 02:54:30 +00:00
|
|
|
b = el;
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
2024-11-04 09:58:49 +00:00
|
|
|
if (!pDefPtr->isDefaultMember && !pDefPtr->isParametricType())
|
2021-09-28 02:54:30 +00:00
|
|
|
b = el;
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
});
|
|
|
|
if (b) {
|
|
|
|
mergedDefBinding.insert(defName);
|
2021-03-23 14:37:32 +00:00
|
|
|
b.writeOutPre(ow);
|
2021-09-28 02:54:30 +00:00
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
pDef.writeOut(ow);
|
|
|
|
if (b) {
|
2025-06-20 14:10:26 +00:00
|
|
|
ow.writeRegion(ColonTokenRegion);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
if (const Binding *bPtr = b.as<Binding>())
|
|
|
|
bPtr->writeOutValue(b, ow);
|
|
|
|
else {
|
|
|
|
qWarning() << "Internal error casting binding to Binding in"
|
|
|
|
<< b.canonicalPath();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(LeftBraceRegion).writeRegion(RightBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
b.writeOutPost(ow);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
static std::pair<QList<DomItem>, QList<DomItem>> splitSignalsAndMethods(const DomItem &methods)
|
|
|
|
{
|
2021-09-03 09:17:24 +00:00
|
|
|
QList<DomItem> signalList, methodList;
|
2024-11-04 09:58:49 +00:00
|
|
|
const auto fields = methods.values();
|
2023-09-13 15:36:59 +00:00
|
|
|
for (const auto &ms : fields) {
|
|
|
|
const auto values = ms.values();
|
|
|
|
for (const auto &m : values) {
|
2021-09-03 09:17:24 +00:00
|
|
|
const MethodInfo *mPtr = m.as<MethodInfo>();
|
|
|
|
if (mPtr && mPtr->methodType == MethodInfo::MethodType::Signal)
|
|
|
|
signalList.append(m);
|
|
|
|
else
|
|
|
|
methodList.append(m);
|
|
|
|
}
|
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
return std::make_pair(signalList, methodList);
|
|
|
|
}
|
|
|
|
|
|
|
|
static std::tuple<QList<DomItem>, QList<DomItem>, QList<DomItem>>
|
|
|
|
splitBindings(const DomItem &bindings, const QSet<QString> &mergedDefBinding)
|
|
|
|
{
|
2021-03-23 14:37:32 +00:00
|
|
|
QList<DomItem> normalBindings, signalHandlers, delayedBindings;
|
2023-09-13 15:36:59 +00:00
|
|
|
for (const auto &bName : bindings.sortedKeys()) {
|
2021-09-28 02:54:30 +00:00
|
|
|
bool skipFirstNormal = mergedDefBinding.contains(bName);
|
2023-09-13 15:36:59 +00:00
|
|
|
const auto values = bindings.key(bName).values();
|
|
|
|
for (const auto &b : values) {
|
2021-03-23 14:37:32 +00:00
|
|
|
const Binding *bPtr = b.as<Binding>();
|
|
|
|
if (skipFirstNormal) {
|
|
|
|
if (bPtr && bPtr->bindingType() == BindingType::Normal) {
|
|
|
|
skipFirstNormal = false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (bPtr->valueKind() == BindingValueKind::Array
|
|
|
|
|| bPtr->valueKind() == BindingValueKind::Object)
|
|
|
|
delayedBindings.append(b);
|
|
|
|
else if (b.field(Fields::isSignalHandler).value().toBool(false))
|
|
|
|
signalHandlers.append(b);
|
|
|
|
else
|
|
|
|
normalBindings.append(b);
|
|
|
|
}
|
|
|
|
}
|
2024-11-04 09:58:49 +00:00
|
|
|
return std::make_tuple(normalBindings, signalHandlers, delayedBindings);
|
|
|
|
}
|
|
|
|
|
|
|
|
void QmlObject::writeOutSortedAttributes(const DomItem &self, OutWriter &ow,
|
|
|
|
const DomItem &component) const
|
|
|
|
{
|
|
|
|
int spacerId = 0;
|
|
|
|
quint32 counter = ow.counter();
|
|
|
|
|
|
|
|
if (component)
|
|
|
|
writeOutSortedEnumerations(component, ow);
|
|
|
|
|
|
|
|
if (counter != ow.counter() || !idStr().isEmpty())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
|
|
|
|
|
|
|
QSet<QString> mergedDefBinding;
|
|
|
|
writeOutSortedPropertyDefinition(self, ow, mergedDefBinding);
|
|
|
|
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
|
|
|
|
|
|
|
const auto [signalList, methodList] = splitSignalsAndMethods(field(self, Fields::methods));
|
|
|
|
for (const auto &sig : std::as_const(signalList)) {
|
|
|
|
ow.ensureNewline();
|
|
|
|
sig.writeOut(ow);
|
|
|
|
ow.ensureNewline();
|
|
|
|
}
|
|
|
|
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
|
|
|
|
|
|
|
bool first = true;
|
|
|
|
for (const auto &method : std::as_const(methodList)) {
|
|
|
|
if (!first && ow.lineWriter.options().functionsSpacing) {
|
|
|
|
ow.newline();
|
|
|
|
}
|
|
|
|
ow.ensureNewline();
|
|
|
|
first = false;
|
|
|
|
method.writeOut(ow);
|
|
|
|
ow.ensureNewline();
|
|
|
|
}
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
|
|
|
|
|
|
|
DomItem bindings = field(self, Fields::bindings);
|
|
|
|
const auto [normalBindings, signalHandlers, delayedBindings] =
|
|
|
|
splitBindings(bindings, mergedDefBinding);
|
|
|
|
|
2021-03-23 14:37:32 +00:00
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
2023-09-18 09:10:51 +00:00
|
|
|
for (const auto &b : std::as_const(normalBindings))
|
2021-03-23 14:37:32 +00:00
|
|
|
b.writeOut(ow);
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
2024-11-04 09:58:49 +00:00
|
|
|
|
2021-03-23 14:37:32 +00:00
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
2023-09-18 09:10:51 +00:00
|
|
|
for (const auto &b : std::as_const(delayedBindings))
|
2021-09-03 09:17:24 +00:00
|
|
|
b.writeOut(ow);
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.removeTextAddCallback(spacerId);
|
2024-11-04 09:58:49 +00:00
|
|
|
|
2021-03-23 14:37:32 +00:00
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
2023-09-18 09:10:51 +00:00
|
|
|
for (const auto &b : std::as_const(signalHandlers))
|
2021-03-23 14:37:32 +00:00
|
|
|
b.writeOut(ow);
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
2024-11-04 09:58:49 +00:00
|
|
|
|
2021-03-23 14:37:32 +00:00
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
2022-11-23 18:33:40 +00:00
|
|
|
first = true;
|
2024-11-04 09:58:49 +00:00
|
|
|
|
2023-09-13 15:36:59 +00:00
|
|
|
const auto values = field(self, Fields::children).values();
|
|
|
|
for (const auto &c : values) {
|
2022-11-06 20:40:50 +00:00
|
|
|
if (!first && ow.lineWriter.options().objectsSpacing) {
|
|
|
|
ow.newline().newline();
|
|
|
|
}
|
|
|
|
first = false;
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.ensureNewline();
|
|
|
|
c.writeOut(ow);
|
|
|
|
}
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
2024-11-04 09:58:49 +00:00
|
|
|
|
|
|
|
if (component) {
|
2021-03-23 14:37:32 +00:00
|
|
|
// we are a root object, possibly add components
|
|
|
|
DomItem subComps = component.field(Fields::subComponents);
|
|
|
|
if (counter != ow.counter())
|
|
|
|
spacerId = ow.addNewlinesAutospacerCallback(2);
|
2023-09-13 15:36:59 +00:00
|
|
|
const auto values = subComps.values();
|
|
|
|
for (const auto &subC : values) {
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.ensureNewline();
|
|
|
|
subC.writeOut(ow);
|
|
|
|
}
|
|
|
|
ow.removeTextAddCallback(spacerId);
|
|
|
|
}
|
2025-08-24 19:36:06 +00:00
|
|
|
if (counter != ow.counter() || !ow.lineWriter.options().singleLineEmptyObjects)
|
|
|
|
ow.ensureNewline();
|
2024-11-04 09:58:49 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void QmlObject::writeOut(const DomItem &self, OutWriter &ow, const QString &onTarget) const
|
|
|
|
{
|
|
|
|
bool isRootObject = pathFromOwner().length() == 5
|
2025-01-10 14:54:53 +00:00
|
|
|
&& pathFromOwner()[0] == Path::fromField(Fields::components)
|
|
|
|
&& pathFromOwner()[3] == Path::fromField(Fields::objects);
|
2024-11-04 09:58:49 +00:00
|
|
|
ow.writeRegion(IdentifierRegion, name());
|
2025-01-28 15:45:28 +00:00
|
|
|
if (!onTarget.isEmpty()) {
|
|
|
|
ow.ensureSpace().writeRegion(OnTokenRegion).ensureSpace().writeRegion(OnTargetRegion,
|
|
|
|
onTarget);
|
|
|
|
}
|
2025-06-17 14:49:58 +00:00
|
|
|
ow.ensureSpace();
|
|
|
|
ow.writeRegion(LeftBraceRegion);
|
2024-11-04 09:58:49 +00:00
|
|
|
int baseIndent = ow.increaseIndent();
|
|
|
|
|
|
|
|
// *always* put id first
|
|
|
|
writeOutId(self, ow);
|
|
|
|
|
|
|
|
DomItem component;
|
|
|
|
if (isRootObject)
|
|
|
|
component = self.containingObject();
|
|
|
|
if (ow.lineWriter.options().attributesSequence
|
|
|
|
== LineWriterOptions::AttributesSequence::Preserve) {
|
|
|
|
QString code;
|
|
|
|
if (std::shared_ptr<QmlFile> qmlFilePtr = self.ownerAs<QmlFile>())
|
|
|
|
code = qmlFilePtr->code();
|
|
|
|
writeOutAttributes(self, ow, component, code);
|
|
|
|
} else {
|
|
|
|
writeOutSortedAttributes(self, ow, component);
|
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.decreaseIndent(1, baseIndent);
|
2024-11-04 09:58:49 +00:00
|
|
|
ow.writeRegion(RightBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2024-10-08 11:44:16 +00:00
|
|
|
Binding::Binding(const QString &name) : Binding(name, std::unique_ptr<BindingValue>()) { }
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Binding::Binding(const QString &name, std::unique_ptr<BindingValue> value, BindingType bindingType)
|
2021-03-23 10:02:18 +00:00
|
|
|
: m_bindingType(bindingType), m_name(name), m_value(std::move(value))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Binding::Binding(
|
|
|
|
const QString &name, const std::shared_ptr<ScriptExpression> &value,
|
|
|
|
BindingType bindingType)
|
2021-03-23 10:02:18 +00:00
|
|
|
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Binding::Binding(const QString &name, const QString &scriptCode, BindingType bindingType)
|
2021-03-23 10:02:18 +00:00
|
|
|
: Binding(name,
|
2022-06-29 09:08:45 +00:00
|
|
|
std::make_unique<BindingValue>(std::make_shared<ScriptExpression>(
|
2022-03-17 12:51:27 +00:00
|
|
|
scriptCode, ScriptExpression::ExpressionType::BindingExpression, 0,
|
2022-06-29 09:08:45 +00:00
|
|
|
Binding::preCodeForName(name), Binding::postCodeForName(name))),
|
2021-03-23 10:02:18 +00:00
|
|
|
bindingType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Binding::Binding(const QString &name, const QmlObject &value, BindingType bindingType)
|
2021-03-23 10:02:18 +00:00
|
|
|
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Binding::Binding(const QString &name, const QList<QmlObject> &value, BindingType bindingType)
|
2021-03-23 10:02:18 +00:00
|
|
|
: Binding(name, std::make_unique<BindingValue>(value), bindingType)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
Binding::Binding(const Binding &o)
|
|
|
|
: m_bindingType(o.m_bindingType),
|
|
|
|
m_name(o.m_name),
|
|
|
|
m_annotations(o.m_annotations),
|
2023-10-18 20:10:13 +00:00
|
|
|
m_comments(o.m_comments),
|
|
|
|
m_bindingIdentifiers(o.m_bindingIdentifiers)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
if (o.m_value) {
|
|
|
|
m_value = std::make_unique<BindingValue>(*o.m_value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
Binding::~Binding() { }
|
|
|
|
|
|
|
|
Binding &Binding::operator=(const Binding &o)
|
|
|
|
{
|
|
|
|
m_name = o.m_name;
|
|
|
|
m_bindingType = o.m_bindingType;
|
|
|
|
m_annotations = o.m_annotations;
|
|
|
|
m_comments = o.m_comments;
|
2023-10-18 20:10:13 +00:00
|
|
|
m_bindingIdentifiers = o.m_bindingIdentifiers;
|
2021-03-23 10:02:18 +00:00
|
|
|
if (o.m_value) {
|
|
|
|
if (!m_value)
|
|
|
|
m_value = std::make_unique<BindingValue>(*o.m_value);
|
|
|
|
else
|
|
|
|
*m_value = *o.m_value;
|
|
|
|
} else {
|
|
|
|
m_value.reset();
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool Binding::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, m_name);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isSignalHandler, isSignalHandler());
|
|
|
|
if (!m_value)
|
|
|
|
cont = cont && visitor(PathEls::Field(Fields::value), []() { return DomItem(); });
|
|
|
|
else
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::value, [this, &self]() {
|
|
|
|
return m_value->value(self);
|
|
|
|
});
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::bindingType, int(m_bindingType));
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::preCode, [this]() {
|
|
|
|
return this->preCode();
|
|
|
|
});
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::postCode, [this]() {
|
|
|
|
return this->postCode();
|
|
|
|
});
|
2023-10-18 20:10:13 +00:00
|
|
|
if (m_bindingIdentifiers) {
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::bindingIdentifiers, [this, &self]() {
|
|
|
|
return self.subScriptElementWrapperItem(m_bindingIdentifiers);
|
|
|
|
});
|
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
DomItem Binding::valueItem(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
if (!m_value)
|
|
|
|
return DomItem();
|
|
|
|
return m_value->value(self);
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValueKind Binding::valueKind() const
|
|
|
|
{
|
|
|
|
if (!m_value)
|
|
|
|
return BindingValueKind::Empty;
|
|
|
|
return m_value->kind;
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlObject const *Binding::objectValue() const
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::Object)
|
|
|
|
return &(m_value->object);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlObject *Binding::objectValue()
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::Object)
|
|
|
|
return &(m_value->object);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QmlObject> const *Binding::arrayValue() const
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::Array)
|
|
|
|
return &(m_value->array);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
QList<QmlObject> *Binding::arrayValue()
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::Array)
|
|
|
|
return &(m_value->array);
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue() const
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::ScriptExpression)
|
|
|
|
return m_value->scriptExpression;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
std::shared_ptr<ScriptExpression> Binding::scriptExpressionValue()
|
|
|
|
{
|
|
|
|
if (valueKind() == BindingValueKind::ScriptExpression)
|
|
|
|
return m_value->scriptExpression;
|
|
|
|
return nullptr;
|
|
|
|
}
|
|
|
|
|
2025-01-17 20:56:37 +00:00
|
|
|
void Binding::setValue(std::unique_ptr<BindingValue> &&value)
|
|
|
|
{
|
|
|
|
m_value = std::move(value);
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path Binding::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation, QmlObject **aPtr)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations),
|
2021-03-23 10:02:18 +00:00
|
|
|
m_annotations, annotation, aPtr);
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void Binding::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
Path base = newPath.withField(Fields::annotations);
|
2021-03-23 10:02:18 +00:00
|
|
|
if (m_value)
|
2025-01-10 14:54:53 +00:00
|
|
|
m_value->updatePathFromOwner(newPath.withField(Fields::value));
|
|
|
|
updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void Binding::writeOut(const DomItem &self, OutWriter &lw) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
lw.ensureNewline();
|
|
|
|
if (m_bindingType == BindingType::Normal) {
|
2023-09-29 13:32:33 +00:00
|
|
|
lw.writeRegion(IdentifierRegion, name());
|
2025-01-28 15:45:28 +00:00
|
|
|
lw.writeRegion(ColonTokenRegion).ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
writeOutValue(self, lw);
|
|
|
|
} else {
|
|
|
|
DomItem v = valueItem(self);
|
|
|
|
if (const QmlObject *vPtr = v.as<QmlObject>()) {
|
|
|
|
v.writeOutPre(lw);
|
|
|
|
vPtr->writeOut(v, lw, name());
|
|
|
|
v.writeOutPost(lw);
|
|
|
|
} else {
|
|
|
|
qCWarning(writeOutLog()) << "On Binding requires an QmlObject Value, not "
|
|
|
|
<< v.internalKindStr() << " at " << self.canonicalPath();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
DomItem v = valueItem(self);
|
|
|
|
switch (valueKind()) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
qCWarning(writeOutLog()) << "Writing of empty binding " << name();
|
2025-06-20 14:10:26 +00:00
|
|
|
lw.writeRegion(LeftBraceRegion);
|
|
|
|
lw.writeRegion(RightBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
if (const List *vPtr = v.as<List>()) {
|
|
|
|
v.writeOutPre(lw);
|
|
|
|
vPtr->writeOut(v, lw, false);
|
|
|
|
v.writeOutPost(lw);
|
|
|
|
}
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
v.writeOut(lw);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool QmltypesComponent::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = Component::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::exports, m_exports);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::metaRevisions, m_metaRevisions);
|
|
|
|
if (!fileName().isEmpty())
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::fileName, fileName()); // remove?
|
2021-06-26 22:56:14 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::interfaceNames, m_interfaceNames);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::hasCustomParser, m_hasCustomParser);
|
2025-08-25 09:00:35 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::elementTypeName, m_elementTypeName);
|
2021-06-26 22:56:14 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::extensionTypeName, m_extensionTypeName);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::accessSemantics, int(m_accessSemantics));
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Export Export::fromString(
|
|
|
|
const Path &source, QStringView exp, const Path &typePath, const ErrorHandler &h)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
Export res;
|
|
|
|
res.exportSourcePath = source;
|
|
|
|
res.typePath = typePath;
|
|
|
|
int slashIdx = exp.indexOf(QLatin1Char('/'));
|
|
|
|
int spaceIdx = exp.indexOf(QLatin1Char(' '));
|
|
|
|
if (spaceIdx == -1)
|
2022-10-05 05:29:16 +00:00
|
|
|
spaceIdx = exp.size();
|
2021-03-23 10:02:18 +00:00
|
|
|
else
|
|
|
|
res.version = Version::fromString(exp.mid(spaceIdx + 1));
|
|
|
|
if (!res.version.isValid())
|
|
|
|
domParsingErrors()
|
|
|
|
.error(tr("Expected string literal to contain 'Package/Name major.minor' "
|
|
|
|
"or 'Name major.minor' not '%1'.")
|
|
|
|
.arg(exp))
|
|
|
|
.handle(h);
|
|
|
|
if (slashIdx != -1)
|
|
|
|
res.uri = exp.left(slashIdx).toString();
|
|
|
|
res.typeName = exp.mid(slashIdx + 1, spaceIdx - (slashIdx + 1)).toString();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool AttributeInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::access, int(access));
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isList, isList);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
Path AttributeInfo::addAnnotation(const Path &selfPathFromOwner, const QmlObject &annotation,
|
2021-03-23 10:02:18 +00:00
|
|
|
QmlObject **aPtr)
|
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return appendUpdatableElementInQList(selfPathFromOwner.withField(Fields::annotations), annotations,
|
2021-03-23 10:02:18 +00:00
|
|
|
annotation, aPtr);
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void AttributeInfo::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
Path base = newPath.withField(Fields::annotations);
|
|
|
|
updatePathFromOwnerQList(annotations, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool EnumDecl::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = CommentableDomElement::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name());
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::values, m_values);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, m_annotations);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void EnumDecl::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
DomElement::updatePathFromOwner(newPath);
|
2025-01-10 14:54:53 +00:00
|
|
|
updatePathFromOwnerQList(m_annotations, newPath.withField(Fields::annotations));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void EnumDecl::setAnnotations(const QList<QmlObject> &annotations)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
m_annotations = annotations;
|
|
|
|
}
|
|
|
|
|
|
|
|
Path EnumDecl::addAnnotation(const QmlObject &annotation, QmlObject **aPtr)
|
|
|
|
{
|
2025-01-10 14:54:53 +00:00
|
|
|
return appendUpdatableElementInQList(pathFromOwner().withField(Fields::annotations), m_annotations,
|
2021-03-23 10:02:18 +00:00
|
|
|
annotation, aPtr);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void EnumDecl::writeOut(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(EnumKeywordRegion)
|
2025-01-28 15:45:28 +00:00
|
|
|
.ensureSpace()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(IdentifierRegion, name())
|
2025-01-28 15:45:28 +00:00
|
|
|
.ensureSpace()
|
2023-09-29 13:32:33 +00:00
|
|
|
.writeRegion(LeftBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
int iLevel = ow.increaseIndent(1);
|
2023-09-13 15:36:59 +00:00
|
|
|
const auto values = self.field(Fields::values).values();
|
2025-06-20 10:34:53 +00:00
|
|
|
for (const auto &value : values)
|
2021-03-23 14:37:32 +00:00
|
|
|
value.writeOut(ow);
|
|
|
|
ow.decreaseIndent(1, iLevel);
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.ensureNewline().writeRegion(RightBraceRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QList<Path> ImportScope::allSources(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2021-06-08 08:38:20 +00:00
|
|
|
DomItem top = self.top();
|
|
|
|
DomItem env = top.environment();
|
2025-01-10 14:54:53 +00:00
|
|
|
Path selfPath = self.canonicalPath().withField(Fields::allSources);
|
2021-06-08 08:38:20 +00:00
|
|
|
RefCacheEntry cached = (env ? RefCacheEntry::forPath(env, selfPath) : RefCacheEntry());
|
2021-03-23 10:02:18 +00:00
|
|
|
if (cached.cached == RefCacheEntry::Cached::All)
|
|
|
|
return cached.canonicalPaths;
|
|
|
|
QList<Path> res;
|
|
|
|
QSet<Path> knownPaths;
|
|
|
|
QList<Path> toDo(m_importSourcePaths.rbegin(), m_importSourcePaths.rend());
|
|
|
|
while (!toDo.isEmpty()) {
|
|
|
|
Path pNow = toDo.takeLast();
|
|
|
|
if (knownPaths.contains(pNow))
|
|
|
|
continue;
|
|
|
|
knownPaths.insert(pNow);
|
|
|
|
res.append(pNow);
|
2021-06-08 08:38:20 +00:00
|
|
|
DomItem sourceBase = top.path(pNow);
|
2023-09-13 15:36:59 +00:00
|
|
|
for (const DomItem &autoExp : sourceBase.field(Fields::autoExports).values()) {
|
2021-03-23 10:02:18 +00:00
|
|
|
if (const ModuleAutoExport *autoExpPtr = autoExp.as<ModuleAutoExport>()) {
|
|
|
|
Path newSource;
|
|
|
|
if (autoExpPtr->inheritVersion) {
|
|
|
|
Version v = autoExpPtr->import.version;
|
|
|
|
DomItem sourceVersion = sourceBase.field(Fields::version);
|
|
|
|
if (const Version *sourceVersionPtr = sourceVersion.as<Version>()) {
|
|
|
|
if (v.majorVersion < 0)
|
|
|
|
v.majorVersion = sourceVersionPtr->majorVersion;
|
|
|
|
if (v.minorVersion < 0)
|
|
|
|
v.minorVersion = sourceVersionPtr->minorVersion;
|
|
|
|
} else {
|
|
|
|
qWarning() << "autoExport with inherited version " << autoExp
|
|
|
|
<< " but missing version in source" << pNow;
|
|
|
|
}
|
|
|
|
Import toImport(autoExpPtr->import.uri, v);
|
|
|
|
newSource = toImport.importedPath();
|
|
|
|
} else {
|
|
|
|
newSource = autoExpPtr->import.importedPath();
|
|
|
|
}
|
|
|
|
if (newSource && !knownPaths.contains(newSource))
|
|
|
|
toDo.append(newSource);
|
|
|
|
} else {
|
|
|
|
qWarning() << "expected ModuleAutoExport not " << autoExp.internalKindStr()
|
|
|
|
<< "looking up autoExports of" << sourceBase;
|
|
|
|
Q_ASSERT(false);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2021-06-08 08:38:20 +00:00
|
|
|
if (env)
|
|
|
|
RefCacheEntry::addForPath(env, selfPath, RefCacheEntry { RefCacheEntry::Cached::All, res });
|
2021-03-23 10:02:18 +00:00
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool ImportScope::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvReferencesField(visitor, Fields::importSources, m_importSourcePaths);
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::allSources, [this, &self]() -> DomItem {
|
|
|
|
return self.subListItem(List::fromQList<Path>(
|
2025-01-10 14:54:53 +00:00
|
|
|
self.pathFromOwner().withField(Fields::allSources), allSources(self),
|
2023-09-13 13:05:12 +00:00
|
|
|
[](const DomItem &list, const PathEls::PathComponent &p, const Path &el) {
|
2021-03-23 10:02:18 +00:00
|
|
|
return list.subDataItem(p, el.toString());
|
|
|
|
}));
|
|
|
|
});
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::qualifiedImports, m_subImports);
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::imported, [this, &self]() -> DomItem {
|
|
|
|
return self.subMapItem(Map(
|
2025-01-10 14:54:53 +00:00
|
|
|
self.pathFromOwner().withField(Fields::imported),
|
2023-12-13 14:24:34 +00:00
|
|
|
[this, &self](const DomItem &map, const QString &key) {
|
2021-03-23 10:02:18 +00:00
|
|
|
return map.subListItem(List::fromQList<DomItem>(
|
2025-01-10 14:54:53 +00:00
|
|
|
map.pathFromOwner().withKey(key), importedItemsWithName(self, key),
|
2023-09-13 13:05:12 +00:00
|
|
|
[](const DomItem &, const PathEls::PathComponent &, const DomItem &el) {
|
2021-03-23 10:02:18 +00:00
|
|
|
return el;
|
|
|
|
}));
|
|
|
|
},
|
2023-09-13 13:05:12 +00:00
|
|
|
[this, &self](const DomItem &) { return this->importedNames(self); },
|
2021-03-23 10:02:18 +00:00
|
|
|
QLatin1String("List<Export>")));
|
|
|
|
});
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool PropertyInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::propertyDefs, propertyDefs);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::bindings, bindings);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValue::BindingValue() : kind(BindingValueKind::Empty) { }
|
|
|
|
|
|
|
|
BindingValue::BindingValue(const QmlObject &o) : kind(BindingValueKind::Object)
|
|
|
|
{
|
|
|
|
new (&object) QmlObject(o);
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
BindingValue::BindingValue(const std::shared_ptr<ScriptExpression> &o)
|
2021-03-23 10:02:18 +00:00
|
|
|
: kind(BindingValueKind::ScriptExpression)
|
|
|
|
{
|
|
|
|
new (&scriptExpression) std::shared_ptr<ScriptExpression>(o);
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValue::BindingValue(const QList<QmlObject> &l) : kind(BindingValueKind::Array)
|
|
|
|
{
|
|
|
|
new (&array) QList<QmlObject>(l);
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValue::~BindingValue()
|
|
|
|
{
|
|
|
|
clearValue();
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValue::BindingValue(const BindingValue &o) : kind(o.kind)
|
|
|
|
{
|
|
|
|
switch (o.kind) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
new (&object) QmlObject(o.object);
|
|
|
|
break;
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
new (&array) QList<QmlObject>(o.array);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
BindingValue &BindingValue::operator=(const BindingValue &o)
|
|
|
|
{
|
|
|
|
clearValue();
|
|
|
|
kind = o.kind;
|
|
|
|
switch (o.kind) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
new (&object) QmlObject(o.object);
|
|
|
|
break;
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
new (&scriptExpression) std::shared_ptr<ScriptExpression>(o.scriptExpression);
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
new (&array) QList<QmlObject>(o.array);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
DomItem BindingValue::value(const DomItem &binding) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
switch (kind) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
return binding.copy(&object);
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
return binding.subOwnerItem(PathEls::Field(Fields::value), scriptExpression);
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
return binding.subListItem(List::fromQListRef<QmlObject>(
|
2025-01-10 14:54:53 +00:00
|
|
|
binding.pathFromOwner().withField(u"value"), array,
|
2023-09-18 08:53:19 +00:00
|
|
|
[](const DomItem &self, const PathEls::PathComponent &, const QmlObject &obj) {
|
2021-03-23 10:02:18 +00:00
|
|
|
return self.copy(&obj);
|
|
|
|
}));
|
|
|
|
}
|
|
|
|
return DomItem();
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void BindingValue::updatePathFromOwner(const Path &newPath)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
switch (kind) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
object.updatePathFromOwner(newPath);
|
|
|
|
break;
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
updatePathFromOwnerQList(array, newPath);
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
void BindingValue::clearValue()
|
|
|
|
{
|
|
|
|
switch (kind) {
|
|
|
|
case BindingValueKind::Empty:
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Object:
|
|
|
|
object.~QmlObject();
|
|
|
|
break;
|
|
|
|
case BindingValueKind::ScriptExpression:
|
|
|
|
scriptExpression.~shared_ptr();
|
|
|
|
break;
|
|
|
|
case BindingValueKind::Array:
|
|
|
|
array.~QList<QmlObject>();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
kind = BindingValueKind::Empty;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
ScriptExpression::ScriptExpression(
|
|
|
|
QStringView code, const std::shared_ptr<QQmlJS::Engine> &engine, AST::Node *ast,
|
|
|
|
const std::shared_ptr<AstComments> &comments, ExpressionType expressionType,
|
|
|
|
SourceLocation localOffset, int derivedFrom, QStringView preCode, QStringView postCode)
|
2021-09-14 16:49:53 +00:00
|
|
|
: OwningItem(derivedFrom),
|
|
|
|
m_expressionType(expressionType),
|
|
|
|
m_code(code),
|
|
|
|
m_preCode(preCode),
|
|
|
|
m_postCode(postCode),
|
|
|
|
m_engine(engine),
|
|
|
|
m_ast(ast),
|
|
|
|
m_astComments(comments),
|
|
|
|
m_localOffset(localOffset)
|
|
|
|
{
|
|
|
|
if (m_expressionType == ExpressionType::BindingExpression)
|
|
|
|
if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(m_ast))
|
|
|
|
m_ast = exp->expression;
|
|
|
|
Q_ASSERT(m_astComments);
|
|
|
|
}
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
ScriptExpression::ScriptExpression(const ScriptExpression &e) : OwningItem(e)
|
|
|
|
{
|
|
|
|
QMutexLocker l(mutex());
|
|
|
|
m_expressionType = e.m_expressionType;
|
|
|
|
m_engine = e.m_engine;
|
|
|
|
m_ast = e.m_ast;
|
|
|
|
if (m_codeStr.isEmpty()) {
|
|
|
|
m_code = e.m_code;
|
|
|
|
} else {
|
|
|
|
m_codeStr = e.m_codeStr;
|
|
|
|
m_code = m_codeStr;
|
|
|
|
}
|
|
|
|
m_localOffset = e.m_localOffset;
|
|
|
|
m_astComments = e.m_astComments;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
std::shared_ptr<ScriptExpression> ScriptExpression::copyWithUpdatedCode(
|
|
|
|
const DomItem &self, const QString &code) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
std::shared_ptr<ScriptExpression> copy = makeCopy(self);
|
|
|
|
DomItem container = self.containingObject();
|
|
|
|
QString preCodeStr = container.field(Fields::preCode).value().toString(m_preCode.toString());
|
|
|
|
QString postCodeStr = container.field(Fields::postCode).value().toString(m_postCode.toString());
|
|
|
|
copy->setCode(code, preCodeStr, postCodeStr);
|
|
|
|
return copy;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool ScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::code, code());
|
|
|
|
if (!preCode().isEmpty())
|
|
|
|
cont = cont
|
|
|
|
&& self.dvValueField(visitor, Fields::preCode, preCode(),
|
|
|
|
ConstantData::Options::MapIsMap);
|
|
|
|
if (!postCode().isEmpty())
|
|
|
|
cont = cont
|
|
|
|
&& self.dvValueField(visitor, Fields::postCode, postCode(),
|
|
|
|
ConstantData::Options::MapIsMap);
|
|
|
|
cont = cont
|
|
|
|
&& self.dvValueLazyField(
|
|
|
|
visitor, Fields::localOffset,
|
2023-09-29 13:32:33 +00:00
|
|
|
[this]() { return sourceLocationToQCborValue(localOffset()); },
|
2021-03-23 10:02:18 +00:00
|
|
|
ConstantData::Options::MapIsMap);
|
|
|
|
cont = cont && self.dvValueLazyField(visitor, Fields::astRelocatableDump, [this]() {
|
|
|
|
return astRelocatableDump();
|
|
|
|
});
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::expressionType, int(expressionType()));
|
2023-04-05 14:43:50 +00:00
|
|
|
if (m_element) {
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::scriptElement, [this, &self]() {
|
|
|
|
return self.subScriptElementWrapperItem(m_element);
|
|
|
|
});
|
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
|
|
|
class FirstNodeVisitor : public VisitAll
|
|
|
|
{
|
|
|
|
public:
|
2024-09-16 09:53:17 +00:00
|
|
|
qsizetype minStart = 0;
|
|
|
|
qsizetype maxEnd = std::numeric_limits<qint32>::max(); // see also Lexer::checkFileLength().
|
2021-03-23 10:02:18 +00:00
|
|
|
AST::Node *firstNodeInRange = nullptr;
|
|
|
|
|
2024-09-16 09:53:17 +00:00
|
|
|
FirstNodeVisitor(qsizetype minStart, qsizetype maxEnd) : minStart(minStart), maxEnd(maxEnd) { }
|
2021-03-23 10:02:18 +00:00
|
|
|
|
|
|
|
bool preVisit(AST::Node *n) override
|
|
|
|
{
|
|
|
|
if (!VisitAll::uiKinds().contains(n->kind)) {
|
2024-09-16 09:53:17 +00:00
|
|
|
qsizetype start = n->firstSourceLocation().begin();
|
|
|
|
qsizetype end = n->lastSourceLocation().end();
|
2021-03-23 10:02:18 +00:00
|
|
|
if (!firstNodeInRange && minStart <= start && end <= maxEnd && start < end)
|
|
|
|
firstNodeInRange = n;
|
|
|
|
}
|
|
|
|
return !firstNodeInRange;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2024-09-16 09:53:17 +00:00
|
|
|
AST::Node *firstNodeInRange(AST::Node *n, qsizetype minStart = 0, qsizetype maxEnd = std::numeric_limits<qint32>::max())
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
FirstNodeVisitor visitor(minStart, maxEnd);
|
|
|
|
AST::Node::accept(n, &visitor);
|
|
|
|
return visitor.firstNodeInRange;
|
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void ScriptExpression::setCode(const QString &code, const QString &preCode, const QString &postCode)
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2024-02-06 15:30:31 +00:00
|
|
|
// TODO QTBUG-121933
|
2021-03-23 10:02:18 +00:00
|
|
|
m_codeStr = code;
|
2023-12-13 14:24:34 +00:00
|
|
|
QString resolvedPreCode, resolvedPostCode;
|
2024-02-06 15:30:31 +00:00
|
|
|
if (m_expressionType == ExpressionType::BindingExpression && preCode.isEmpty()) {
|
2023-12-13 14:24:34 +00:00
|
|
|
resolvedPreCode = Binding::preCodeForName(u"binding");
|
|
|
|
resolvedPostCode = Binding::postCodeForName(u"binding");
|
|
|
|
} else {
|
|
|
|
resolvedPreCode = preCode;
|
|
|
|
resolvedPostCode = postCode;
|
2022-03-17 12:51:27 +00:00
|
|
|
}
|
2023-12-13 14:24:34 +00:00
|
|
|
if (!resolvedPreCode.isEmpty() || !resolvedPostCode.isEmpty())
|
|
|
|
m_codeStr = resolvedPreCode + code + resolvedPostCode;
|
|
|
|
m_code = QStringView(m_codeStr).mid(resolvedPreCode.size(), code.size());
|
|
|
|
m_preCode = QStringView(m_codeStr).mid(0, resolvedPreCode.size());
|
|
|
|
m_postCode = QStringView(m_codeStr).mid(
|
|
|
|
resolvedPreCode.size() + code.size(), resolvedPostCode.size());
|
2021-03-23 10:02:18 +00:00
|
|
|
m_engine = nullptr;
|
|
|
|
m_ast = nullptr;
|
|
|
|
m_localOffset = SourceLocation();
|
|
|
|
if (!m_code.isEmpty()) {
|
|
|
|
IndentInfo preChange(m_preCode, 4);
|
2022-10-05 05:29:16 +00:00
|
|
|
m_localOffset.offset = m_preCode.size();
|
|
|
|
m_localOffset.length = m_code.size();
|
|
|
|
m_localOffset.startColumn = preChange.trailingString.size();
|
2021-03-23 10:02:18 +00:00
|
|
|
m_localOffset.startLine = preChange.nNewlines;
|
2022-06-29 09:08:45 +00:00
|
|
|
m_engine = std::make_shared<QQmlJS::Engine>();
|
|
|
|
m_astComments = std::make_shared<AstComments>(m_engine);
|
2024-02-06 15:30:31 +00:00
|
|
|
m_ast = parse(resolveParseMode());
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
if (AST::Program *programPtr = AST::cast<AST::Program *>(m_ast)) {
|
|
|
|
m_ast = programPtr->statements;
|
|
|
|
}
|
|
|
|
if (!m_preCode.isEmpty())
|
2022-10-05 05:29:16 +00:00
|
|
|
m_ast = firstNodeInRange(m_ast, m_preCode.size(),
|
|
|
|
m_preCode.size() + m_code.size());
|
2023-09-06 15:07:21 +00:00
|
|
|
if (auto *sList = AST::cast<AST::FormalParameterList *>(m_ast)) {
|
|
|
|
m_ast = sList->element;
|
|
|
|
}
|
2021-03-23 10:02:18 +00:00
|
|
|
if (m_expressionType != ExpressionType::FunctionBody) {
|
|
|
|
if (AST::StatementList *sList = AST::cast<AST::StatementList *>(m_ast)) {
|
|
|
|
if (!sList->next)
|
|
|
|
m_ast = sList->statement;
|
|
|
|
}
|
|
|
|
}
|
2021-09-14 16:49:53 +00:00
|
|
|
if (m_expressionType == ExpressionType::BindingExpression)
|
|
|
|
if (AST::ExpressionStatement *exp = AST::cast<AST::ExpressionStatement *>(m_ast))
|
|
|
|
m_ast = exp->expression;
|
2024-01-15 20:36:22 +00:00
|
|
|
|
|
|
|
CommentCollector collector;
|
2024-02-06 15:30:31 +00:00
|
|
|
collector.collectComments(m_engine, m_ast, m_astComments);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
AST::Node *ScriptExpression::parse(const ParseMode mode)
|
|
|
|
{
|
|
|
|
QQmlJS::Lexer lexer(m_engine.get());
|
|
|
|
lexer.setCode(m_codeStr, /*lineno = */ 1, /*qmlMode=*/mode == ParseMode::QML);
|
|
|
|
QQmlJS::Parser parser(m_engine.get());
|
|
|
|
const bool parserSucceeded = [mode, &parser]() {
|
|
|
|
switch (mode) {
|
|
|
|
case ParseMode::QML:
|
|
|
|
return parser.parse();
|
|
|
|
case ParseMode::JS:
|
|
|
|
return parser.parseScript();
|
2023-11-20 16:39:49 +00:00
|
|
|
case ParseMode::ESM:
|
2024-02-06 15:46:52 +00:00
|
|
|
return parser.parseModule();
|
2024-02-06 15:30:31 +00:00
|
|
|
default:
|
|
|
|
Q_UNREACHABLE_RETURN(false);
|
|
|
|
}
|
|
|
|
}();
|
|
|
|
if (!parserSucceeded) {
|
|
|
|
addErrorLocal(domParsingErrors().error(tr("Parsing of code failed")));
|
|
|
|
}
|
|
|
|
const auto messages = parser.diagnosticMessages();
|
|
|
|
for (const DiagnosticMessage &msg : messages) {
|
|
|
|
ErrorMessage err = domParsingErrors().errorMessage(msg);
|
|
|
|
err.location.offset -= m_localOffset.offset;
|
|
|
|
err.location.startLine -= m_localOffset.startLine;
|
|
|
|
if (err.location.startLine == 1)
|
|
|
|
err.location.startColumn -= m_localOffset.startColumn;
|
|
|
|
addErrorLocal(std::move(err));
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
2024-02-06 15:30:31 +00:00
|
|
|
return parser.rootNode();
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
|
2023-12-13 14:24:34 +00:00
|
|
|
void ScriptExpression::astDumper(const Sink &s, AstDumperOptions options) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
astNodeDumper(s, ast(), options, 1, 0, [this](SourceLocation astL) {
|
|
|
|
SourceLocation l = this->locationToLocal(astL);
|
|
|
|
return this->code().mid(l.offset, l.length);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ScriptExpression::astRelocatableDump() const
|
|
|
|
{
|
2023-12-13 14:24:34 +00:00
|
|
|
return dumperToString([this](const Sink &s) {
|
2021-03-23 10:02:18 +00:00
|
|
|
this->astDumper(s, AstDumperOption::NoLocations | AstDumperOption::SloppyCompare);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2024-10-31 15:23:21 +00:00
|
|
|
void ScriptExpression::writeOut(const DomItem &, OutWriter &lw) const
|
|
|
|
{
|
qmlformat: customizable semicolon
Add semicolon option to qmlformat. While --Always always appends
semicolons to the JS statements, --essential removes the semicolons
unless it is not safe to rely on ASI once semicolons are removed.
Change the way EmptyStatements are handled. Prior to this
commit, semicolons following if, for, foreach, while statements
without bodies were added to the new line with some indentation.
Make the semicolon following no-body items stick to the right
paranthesis. If there is a chain of empty statements, write out a
single one.
[ChangeLog][qmlformat] New option semicolon-rule is added and
EmptyStatement formatting behavior has changed.
* Added --semicolon-rule option with modes:
- --semicolon-rule=always: Always appends semicolons to JS statements.
- --semicolon-rule=essential: Removes semicolons unless ASI makes it unsafe.
* Changed handling of EmptyStatements:
- Semicolons after control structures without a body (e.g., if, for, while)
now appear directly after the closing parenthesis, instead of on a new line.
- Consecutive empty statements are collapsed into a single semicolon.
Fixes: QTBUG-107152
Change-Id: Ic95047a1f0077937d4c1f01328d77a3e6a4f22d6
Reviewed-by: Olivier De Cannière <olivier.decanniere@qt.io>
2025-03-24 11:52:33 +00:00
|
|
|
reformatAst(lw, this);
|
|
|
|
}
|
|
|
|
|
|
|
|
QStringView ScriptExpression::loc2Str(SourceLocation astL) const
|
|
|
|
{
|
|
|
|
SourceLocation l = this->locationToLocal(astL); // use engine->code() instead?
|
|
|
|
return this->code().mid(l.offset, l.length);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
SourceLocation ScriptExpression::globalLocation(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2023-09-29 13:32:33 +00:00
|
|
|
if (const FileLocations::Tree tree = FileLocations::treeOf(self)) {
|
|
|
|
return FileLocations::region(tree, MainRegion);
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
|
|
|
return SourceLocation();
|
|
|
|
}
|
|
|
|
|
2021-09-28 02:54:30 +00:00
|
|
|
bool PropertyDefinition::isParametricType() const
|
|
|
|
{
|
|
|
|
return typeName.contains(QChar(u'<'));
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void PropertyDefinition::writeOut(const DomItem &, OutWriter &lw) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
lw.ensureNewline();
|
|
|
|
if (isDefaultMember)
|
2025-01-28 15:45:28 +00:00
|
|
|
lw.writeRegion(DefaultKeywordRegion).ensureSpace();
|
2025-03-04 15:50:17 +00:00
|
|
|
if (isFinal)
|
|
|
|
lw.writeRegion(FinalKeywordRegion).ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
if (isRequired)
|
2025-01-28 15:45:28 +00:00
|
|
|
lw.writeRegion(RequiredKeywordRegion).ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
if (isReadonly)
|
2025-01-28 15:45:28 +00:00
|
|
|
lw.writeRegion(ReadonlyKeywordRegion).ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
if (!typeName.isEmpty()) {
|
2025-01-28 15:45:28 +00:00
|
|
|
lw.writeRegion(PropertyKeywordRegion).ensureSpace();
|
|
|
|
lw.writeRegion(TypeIdentifierRegion, typeName).ensureSpace();
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2023-09-29 13:32:33 +00:00
|
|
|
lw.writeRegion(IdentifierRegion, name);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool MethodInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = AttributeInfo::iterateDirectSubpaths(self, visitor);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::parameters, parameters);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::methodType, int(methodType));
|
|
|
|
if (!typeName.isEmpty())
|
|
|
|
cont = cont && self.dvReferenceField(visitor, Fields::type, typePath(self));
|
|
|
|
if (methodType == MethodType::Method) {
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::preCode, preCode(self));
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::postCode, postCode(self));
|
2021-06-26 22:56:14 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isConstructor, isConstructor);
|
2021-03-23 10:02:18 +00:00
|
|
|
}
|
2023-05-30 14:04:01 +00:00
|
|
|
if (returnType)
|
|
|
|
cont = cont && self.dvItemField(visitor, Fields::returnType, [this, &self]() {
|
|
|
|
return self.subOwnerItem(PathEls::Field(Fields::returnType), returnType);
|
|
|
|
});
|
2021-03-23 10:02:18 +00:00
|
|
|
if (body)
|
2023-05-10 19:58:28 +00:00
|
|
|
cont = cont && self.dvItemField(visitor, Fields::body, [this, &self]() {
|
|
|
|
return self.subOwnerItem(PathEls::Field(Fields::body), body);
|
|
|
|
});
|
2021-03-23 10:02:18 +00:00
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QString MethodInfo::preCode(const DomItem &self) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
2021-03-23 14:37:32 +00:00
|
|
|
QString res;
|
|
|
|
LineWriter lw([&res](QStringView s) { res.append(s); }, QLatin1String("*preCode*"));
|
|
|
|
OutWriter ow(lw);
|
|
|
|
ow.indentNextlines = true;
|
|
|
|
ow.skipComments = true;
|
|
|
|
MockObject standinObj(self.pathFromOwner());
|
|
|
|
DomItem standin = self.copy(&standinObj);
|
|
|
|
ow.itemStart(standin);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(FunctionKeywordRegion).ensureSpace().writeRegion(IdentifierRegion, name);
|
2021-03-23 14:37:32 +00:00
|
|
|
bool first = true;
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(LeftParenthesisRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
for (const MethodParameter &mp : parameters) {
|
2025-01-28 15:45:28 +00:00
|
|
|
if (first) {
|
2021-03-23 14:37:32 +00:00
|
|
|
first = false;
|
2025-01-28 15:45:28 +00:00
|
|
|
} else {
|
2025-06-20 14:10:26 +00:00
|
|
|
ow.writeRegion(CommaTokenRegion);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace();
|
|
|
|
}
|
2023-09-08 10:19:24 +00:00
|
|
|
ow.write(mp.value->code());
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(RightParenthesisRegion);
|
|
|
|
ow.ensureSpace().writeRegion(LeftBraceRegion);
|
2025-03-20 18:03:31 +00:00
|
|
|
ow.itemEnd();
|
2021-09-14 16:49:53 +00:00
|
|
|
ow.eof();
|
|
|
|
return res;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
QString MethodInfo::postCode(const DomItem &) const
|
2021-09-14 16:49:53 +00:00
|
|
|
{
|
|
|
|
return QLatin1String("\n}\n");
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 11:39:09 +00:00
|
|
|
void MethodInfo::writeOutArguments(const DomItem &self, OutWriter &ow) const
|
|
|
|
{
|
2024-09-10 15:15:39 +00:00
|
|
|
if (parameters.isEmpty() && methodType == MethodType::Signal)
|
|
|
|
return;
|
|
|
|
|
|
|
|
ow.writeRegion(LeftParenthesisRegion);
|
2024-09-10 11:39:09 +00:00
|
|
|
bool first = true;
|
|
|
|
for (const DomItem &arg : self.field(Fields::parameters).values()) {
|
|
|
|
if (first)
|
|
|
|
first = false;
|
|
|
|
else
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(CommaTokenRegion).ensureSpace();
|
2024-09-10 11:39:09 +00:00
|
|
|
arg.writeOut(ow);
|
|
|
|
}
|
2024-09-10 15:15:39 +00:00
|
|
|
ow.writeRegion(RightParenthesisRegion);
|
2024-09-10 11:39:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
void MethodInfo::writeOutReturnType(OutWriter &ow) const
|
|
|
|
{
|
|
|
|
if (typeName.isEmpty())
|
|
|
|
return;
|
|
|
|
|
|
|
|
ow.writeRegion(ColonTokenRegion);
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace();
|
2024-09-10 11:39:09 +00:00
|
|
|
ow.writeRegion(TypeIdentifierRegion, typeName);
|
|
|
|
}
|
|
|
|
|
2024-09-10 15:15:39 +00:00
|
|
|
void MethodInfo::writeOutBody(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
2024-09-10 15:15:39 +00:00
|
|
|
ow.ensureSpace().writeRegion(LeftBraceRegion);
|
|
|
|
int baseIndent = ow.increaseIndent();
|
|
|
|
if (DomItem b = self.field(Fields::body)) {
|
|
|
|
ow.ensureNewline();
|
|
|
|
b.writeOut(ow);
|
|
|
|
}
|
|
|
|
ow.decreaseIndent(1, baseIndent);
|
|
|
|
ow.ensureNewline().writeRegion(RightBraceRegion);
|
|
|
|
}
|
2024-09-10 11:39:09 +00:00
|
|
|
|
2024-09-10 15:15:39 +00:00
|
|
|
void MethodInfo::writeOut(const DomItem &self, OutWriter &ow) const
|
|
|
|
{
|
|
|
|
if (methodType == MethodType::Signal) {
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(SignalKeywordRegion).ensureSpace();
|
2024-09-10 15:15:39 +00:00
|
|
|
} else {
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(FunctionKeywordRegion).ensureSpace();
|
2024-09-10 15:15:39 +00:00
|
|
|
}
|
|
|
|
ow.writeRegion(IdentifierRegion, name);
|
|
|
|
writeOutArguments(self, ow);
|
|
|
|
if (methodType == MethodType::Signal) {
|
|
|
|
// signal doesn't have returnType or body
|
2021-03-23 14:37:32 +00:00
|
|
|
return;
|
|
|
|
}
|
2024-09-10 15:15:39 +00:00
|
|
|
writeOutReturnType(ow);
|
|
|
|
writeOutBody(self, ow);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
|
2024-09-10 11:39:09 +00:00
|
|
|
QString MethodInfo::signature(const DomItem &self) const
|
|
|
|
{
|
|
|
|
QString resultStr;
|
|
|
|
QTextStream res(&resultStr);
|
|
|
|
LineWriter lw([&res](QStringView s) { res << s; }, QLatin1String("*testStream*"));
|
|
|
|
OutWriter ow(lw);
|
|
|
|
ow.indentNextlines = true;
|
|
|
|
ow.skipComments = true;
|
|
|
|
|
|
|
|
ow.itemStart(self);
|
|
|
|
writeOutArguments(self, ow);
|
|
|
|
|
|
|
|
writeOutReturnType(ow);
|
|
|
|
|
2025-03-20 18:03:31 +00:00
|
|
|
ow.itemEnd();
|
2024-09-10 11:39:09 +00:00
|
|
|
lw.eof(false);
|
|
|
|
res.flush();
|
|
|
|
return resultStr.simplified();
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool MethodParameter::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name);
|
|
|
|
if (!typeName.isEmpty()) {
|
|
|
|
cont = cont
|
2023-02-07 15:35:40 +00:00
|
|
|
&& self.dvReferenceField(visitor, Fields::type, Paths::lookupTypePath(typeName));
|
2021-03-23 10:02:18 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::typeName, typeName);
|
|
|
|
}
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isPointer, isPointer);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isReadonly, isReadonly);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::isList, isList);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::defaultValue, defaultValue);
|
2023-05-30 14:04:01 +00:00
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::value, value);
|
|
|
|
|
2023-09-06 15:07:21 +00:00
|
|
|
cont = cont && self.dvValueField(visitor, Fields::preCode, u"function f("_s);
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::postCode, u") {}"_s);
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
if (!annotations.isEmpty())
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::annotations, annotations);
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, comments);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void MethodParameter::writeOut(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
2024-09-10 11:39:09 +00:00
|
|
|
if (typeAnnotationStyle == MethodParameter::TypeAnnotationStyle::Prefix) {
|
|
|
|
writeOutSignal(self, ow);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2023-08-30 12:50:11 +00:00
|
|
|
if (!name.isEmpty()) {
|
2023-08-31 09:38:00 +00:00
|
|
|
if (isRestElement)
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(EllipsisTokenRegion);
|
|
|
|
ow.writeRegion(IdentifierRegion, name);
|
2023-08-30 12:50:11 +00:00
|
|
|
if (!typeName.isEmpty())
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(ColonTokenRegion).ensureSpace().writeRegion(TypeIdentifierRegion, typeName);
|
2023-08-30 12:50:11 +00:00
|
|
|
if (defaultValue) {
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace().writeRegion(EqualTokenRegion).ensureSpace();
|
2023-08-30 12:50:11 +00:00
|
|
|
self.subOwnerItem(PathEls::Field(Fields::defaultValue), defaultValue).writeOut(ow);
|
|
|
|
}
|
|
|
|
} else {
|
|
|
|
if (value) {
|
|
|
|
self.subOwnerItem(PathEls::Field(Fields::value), value).writeOut(ow);
|
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void MethodParameter::writeOutSignal(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
self.writeOutPre(ow);
|
|
|
|
if (!typeName.isEmpty())
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(TypeIdentifierRegion, typeName).ensureSpace();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(IdentifierRegion, name);
|
2021-03-23 14:37:32 +00:00
|
|
|
self.writeOutPost(ow);
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void Pragma::writeOut(const DomItem &, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
|
|
|
ow.ensureNewline();
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(PragmaKeywordRegion).ensureSpace().writeRegion(IdentifierRegion, name);
|
2023-06-08 07:30:36 +00:00
|
|
|
|
|
|
|
bool isFirst = true;
|
|
|
|
for (const auto &value : values) {
|
|
|
|
if (isFirst) {
|
|
|
|
isFirst = false;
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(ColonTokenRegion).ensureSpace();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(PragmaValuesRegion, value);
|
2023-06-08 07:30:36 +00:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.writeRegion(CommaTokenRegion).ensureSpace();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(PragmaValuesRegion, value);
|
2023-06-08 07:30:36 +00:00
|
|
|
}
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.ensureNewline();
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
bool EnumItem::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
|
2021-03-23 10:02:18 +00:00
|
|
|
{
|
|
|
|
bool cont = true;
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::name, name());
|
|
|
|
cont = cont && self.dvValueField(visitor, Fields::value, value());
|
|
|
|
cont = cont && self.dvWrapField(visitor, Fields::comments, m_comments);
|
|
|
|
return cont;
|
|
|
|
}
|
|
|
|
|
2023-09-13 13:05:12 +00:00
|
|
|
void EnumItem::writeOut(const DomItem &self, OutWriter &ow) const
|
2021-03-23 14:37:32 +00:00
|
|
|
{
|
2025-06-20 10:34:53 +00:00
|
|
|
index_type myIndex = self.pathFromOwner().last().headIndex();
|
|
|
|
if (myIndex != 0)
|
|
|
|
ow.writeRegion(CommaTokenRegion);
|
2021-03-23 14:37:32 +00:00
|
|
|
ow.ensureNewline();
|
2023-09-29 13:32:33 +00:00
|
|
|
ow.writeRegion(IdentifierRegion, name());
|
2024-05-31 09:06:27 +00:00
|
|
|
if (m_valueKind == ValueKind::ExplicitValue) {
|
2021-03-23 14:37:32 +00:00
|
|
|
QString v = QString::number(value(), 'f', 0);
|
|
|
|
if (abs(value() - v.toDouble()) > 1.e-10)
|
|
|
|
v = QString::number(value());
|
2025-01-28 15:45:28 +00:00
|
|
|
ow.ensureSpace().writeRegion(EqualTokenRegion).ensureSpace().writeRegion(EnumValueRegion, v);
|
2021-03-23 14:37:32 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2021-12-06 21:18:28 +00:00
|
|
|
QmlUri QmlUri::fromString(const QString &str)
|
|
|
|
{
|
|
|
|
if (str.startsWith(u'"'))
|
2022-10-05 05:29:16 +00:00
|
|
|
return fromDirectoryString(str.mid(1, str.size() - 2)
|
2022-03-21 09:21:18 +00:00
|
|
|
.replace(u"\\\""_s, u"\""_s)
|
|
|
|
.replace(u"\\\\"_s, u"\\"_s));
|
2021-12-06 21:18:28 +00:00
|
|
|
else
|
|
|
|
return fromUriString(str);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlUri QmlUri::fromUriString(const QString &str)
|
|
|
|
{
|
|
|
|
QRegularExpression moduleUriRe(QLatin1String(R"(\A\w+(?:\.\w+)*\Z)"));
|
|
|
|
return QmlUri((moduleUriRe.match(str).hasMatch() ? Kind::ModuleUri : Kind::Invalid), str);
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlUri QmlUri::fromDirectoryString(const QString &str)
|
|
|
|
{
|
|
|
|
QUrl url(str);
|
2022-10-05 05:29:16 +00:00
|
|
|
if (url.isValid() && url.scheme().size() > 1)
|
2021-12-06 21:18:28 +00:00
|
|
|
return QmlUri(url);
|
|
|
|
if (!str.isEmpty()) {
|
|
|
|
QFileInfo path(str);
|
|
|
|
return QmlUri((path.isRelative() ? Kind::RelativePath : Kind::AbsolutePath), str);
|
|
|
|
}
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QmlUri::isValid() const
|
|
|
|
{
|
|
|
|
return m_kind != Kind::Invalid;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QmlUri::isDirectory() const
|
|
|
|
{
|
|
|
|
switch (m_kind) {
|
|
|
|
case Kind::Invalid:
|
|
|
|
case Kind::ModuleUri:
|
|
|
|
break;
|
|
|
|
case Kind::DirectoryUrl:
|
|
|
|
case Kind::RelativePath:
|
|
|
|
case Kind::AbsolutePath:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool QmlUri::isModule() const
|
|
|
|
{
|
|
|
|
return m_kind == Kind::ModuleUri;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlUri::moduleUri() const
|
|
|
|
{
|
|
|
|
if (m_kind == Kind::ModuleUri)
|
|
|
|
return std::get<QString>(m_value);
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlUri::localPath() const
|
|
|
|
{
|
|
|
|
switch (m_kind) {
|
|
|
|
case Kind::Invalid:
|
|
|
|
case Kind::ModuleUri:
|
|
|
|
break;
|
|
|
|
case Kind::DirectoryUrl: {
|
|
|
|
const QUrl &url = std::get<QUrl>(m_value);
|
|
|
|
if (url.scheme().compare(u"file", Qt::CaseInsensitive) == 0)
|
|
|
|
return url.path();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Kind::RelativePath:
|
|
|
|
case Kind::AbsolutePath:
|
|
|
|
return std::get<QString>(m_value);
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlUri::absoluteLocalPath(const QString &basePath) const
|
|
|
|
{
|
|
|
|
switch (m_kind) {
|
|
|
|
case Kind::Invalid:
|
|
|
|
case Kind::ModuleUri:
|
|
|
|
break;
|
|
|
|
case Kind::DirectoryUrl: {
|
|
|
|
const QUrl &url = std::get<QUrl>(m_value);
|
|
|
|
if (url.scheme().compare(u"file", Qt::CaseInsensitive) == 0)
|
|
|
|
return url.path();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Kind::RelativePath: {
|
|
|
|
if (!basePath.isEmpty())
|
|
|
|
return QDir(basePath).filePath(std::get<QString>(m_value));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
case Kind::AbsolutePath:
|
|
|
|
return std::get<QString>(m_value);
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QUrl QmlUri::directoryUrl() const
|
|
|
|
{
|
|
|
|
if (m_kind == Kind::DirectoryUrl)
|
|
|
|
return std::get<QUrl>(m_value);
|
|
|
|
return QUrl {};
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlUri::directoryString() const
|
|
|
|
{
|
|
|
|
switch (m_kind) {
|
|
|
|
case Kind::Invalid:
|
|
|
|
case Kind::ModuleUri:
|
|
|
|
break;
|
|
|
|
case Kind::DirectoryUrl:
|
|
|
|
return std::get<QUrl>(m_value).toString(); // set formatting? options?
|
|
|
|
case Kind::RelativePath:
|
|
|
|
case Kind::AbsolutePath:
|
|
|
|
return std::get<QString>(m_value);
|
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QString QmlUri::toString() const
|
|
|
|
{
|
|
|
|
switch (m_kind) {
|
|
|
|
case Kind::Invalid:
|
|
|
|
break;
|
|
|
|
case Kind::ModuleUri:
|
|
|
|
return std::get<QString>(m_value);
|
|
|
|
case Kind::DirectoryUrl:
|
|
|
|
case Kind::RelativePath:
|
|
|
|
case Kind::AbsolutePath:
|
2022-03-21 09:21:18 +00:00
|
|
|
return u"\""_s + directoryString().replace(u'\\', u"\\\\"_s).replace(u'"', u"\\\""_s)
|
|
|
|
+ u"\""_s;
|
2021-12-06 21:18:28 +00:00
|
|
|
}
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
QmlUri::Kind QmlUri::kind() const
|
|
|
|
{
|
|
|
|
return m_kind;
|
|
|
|
}
|
|
|
|
|
2023-04-05 14:43:50 +00:00
|
|
|
void ScriptExpression::setScriptElement(const ScriptElementVariant &p)
|
|
|
|
{
|
|
|
|
m_element = p;
|
|
|
|
}
|
|
|
|
|
2021-03-23 10:02:18 +00:00
|
|
|
} // end namespace Dom
|
|
|
|
} // end namespace QQmlJS
|
|
|
|
|
|
|
|
QT_END_NAMESPACE
|
2022-04-28 14:10:10 +00:00
|
|
|
|
|
|
|
#include "moc_qqmldomelements_p.cpp"
|