diff --git a/src/qmldom/qqmldomfieldfilter.cpp b/src/qmldom/qqmldomfieldfilter.cpp index 3fa39b5b49..f1c94f1bfd 100644 --- a/src/qmldom/qqmldomfieldfilter.cpp +++ b/src/qmldom/qqmldomfieldfilter.cpp @@ -37,6 +37,7 @@ **/ #include "qqmldomfieldfilter_p.h" +#include "qqmldompath_p.h" #include "QtCore/qglobal.h" QT_BEGIN_NAMESPACE @@ -44,6 +45,34 @@ QT_BEGIN_NAMESPACE namespace QQmlJS { namespace Dom { +/*! +\internal +\class QQmljs::Dom::FieldFilter + +\brief Class that represent a filter on DomItem, when dumping or comparing + +DomItem can be duped or compared, but often one is interested only in a subset +of them, FieldFilter is a simple way to select a subset of them. +It uses two basic elements: the type of the object (internalKind) and the +name of fields. + +A basic filter can be represented by : or +where op is either + or - (if the matching elements should be added or removed) +Both typeName and fieldName can be the empty string (meaning any value matches). + +Basic filters are ordered from the most specific to the least specific as follow: +type+field > type > field > empty. +When combining several filters the most specific always wins, so +-code,+ScriptExpression:code is the same as +ScriptExpression:code,-code and means +that normally the field code is not outputted but for a ScriptExpression DomItem +it is. + +It is possible to get the string representation of the current filter with +FieldFilter::describeFieldsFilter(), and change the current filter with +FieldFilter::addFilter(), but after it one should call FieldFilter::setFiltred() +to ensure that the internal cache used to speed up comparisons is correct. +*/ + QString FieldFilter::describeFieldsFilter() const { QString fieldFilterStr; @@ -104,9 +133,11 @@ bool FieldFilter::operator()(DomItem &base, const PathEls::PathComponent &c, Dom bool FieldFilter::addFilter(QString fFields) { - for (QString fField : fFields.split(QLatin1Char(','))) { + for (const QString &fField : fFields.split(QLatin1Char(','))) { + // parses a base filter of the form : or + // as described in this class documentation QRegularExpression fieldRe(QRegularExpression::anchoredPattern(QStringLiteral( - uR"((?[-+])?(?:(?[a-zA-Z0-9_]*):)?(?[a-zA-Z0-9_]+))"))); + uR"((?[-+])?(?:(?[a-zA-Z0-9_]*):)?(?[a-zA-Z0-9_]*))"))); QRegularExpressionMatch m = fieldRe.match(fField); if (m.hasMatch()) { if (m.captured(u"op") == u"+") { @@ -128,11 +159,18 @@ FieldFilter FieldFilter::defaultFilter() { QMultiMap fieldFilterAdd { { QLatin1String("ScriptExpression"), QLatin1String("code") } }; - QMultiMap fieldFilterRemove { { QString(), QLatin1String("code") }, - { QString(), QLatin1String("propertyInfos") }, - { QLatin1String("AttachedInfo"), - QLatin1String("parent") } }; - + QMultiMap fieldFilterRemove { + { QString(), QString::fromUtf16(Fields::code) }, + { QString(), QString::fromUtf16(Fields::postCode) }, + { QString(), QString::fromUtf16(Fields::preCode) }, + { QString(), QString::fromUtf16(Fields::importScope) }, + { QString(), QString::fromUtf16(Fields::fileLocationsTree) }, + { QString(), QString::fromUtf16(Fields::astComments) }, + { QString(), QString::fromUtf16(Fields::comments) }, + { QString(), QString::fromUtf16(Fields::exports) }, + { QString(), QString::fromUtf16(Fields::propertyInfos) }, + { QLatin1String("AttachedInfo"), QString::fromUtf16(Fields::parent) } + }; return FieldFilter { fieldFilterAdd, fieldFilterRemove }; } diff --git a/tools/qmldom/qmldomtool.cpp b/tools/qmldom/qmldomtool.cpp index 2fa11e5822..c883790812 100644 --- a/tools/qmldom/qmldomtool.cpp +++ b/tools/qmldom/qmldomtool.cpp @@ -108,7 +108,9 @@ int main(int argc, char *argv[]) QCommandLineOption pathToDumpOption( QStringList() << "path-to-dump", - QLatin1String("adds a path to dump (by default the root path is dumped)"), + QLatin1String("adds a path to dump. By default the base path of each file is dumped. " + "If any path starts with $ ($env for example) then the environment (and " + "not the loaded files) is used as basis."), QLatin1String("pathToDump")); parser.addOption(pathToDumpOption); @@ -116,7 +118,7 @@ int main(int argc, char *argv[]) QStringList() << "D" << "dependencies", QLatin1String("Dependencies to load: none, required, reachable"), - QLatin1String("dependenciesToLoad"), QLatin1String("required")); + QLatin1String("dependenciesToLoad"), QLatin1String("none")); parser.addOption(dependenciesOption); QCommandLineOption reformatDirOption( @@ -146,11 +148,12 @@ int main(int argc, char *argv[]) if (parser.isSet(filterOption)) { qDebug() << "filters: " << parser.values(filterOption); - for (QString fFields : parser.values(filterOption)) { + for (const QString &fFields : parser.values(filterOption)) { if (!filter.addFilter(fFields)) { return 1; } } + filter.setFiltred(); } std::optional fileType; @@ -158,7 +161,7 @@ int main(int argc, char *argv[]) fileType = DomType::QmlFile; Dependencies dep = Dependencies::None; - for (QString depName : parser.values(dependenciesOption)) { + for (const QString &depName : parser.values(dependenciesOption)) { QMetaEnum metaEnum = QMetaEnum::fromType(); bool found = false; for (int i = 0; i < metaEnum.keyCount(); ++i) { @@ -188,7 +191,7 @@ int main(int argc, char *argv[]) } QList pathsToDump; - for (QString pStr : parser.values(pathToDumpOption)) { + for (const QString &pStr : parser.values(pathToDumpOption)) { pathsToDump.append(Path::fromString(pStr)); } if (pathsToDump.isEmpty()) @@ -216,10 +219,10 @@ int main(int argc, char *argv[]) { QDebug dbg = qDebug(); dbg << "dirs:\n"; - foreach (QString d, qmltypeDirs) + for (const QString &d : qAsConst(qmltypeDirs)) dbg << " '" << d << "'\n"; dbg << "files:\n"; - foreach (QString f, positionalArguments) + for (const QString &f : qAsConst(positionalArguments)) dbg << " '" << f << "'\n"; dbg << "fieldFilter: " << filter.describeFieldsFilter(); dbg << "\n"; @@ -232,44 +235,55 @@ int main(int argc, char *argv[]) qDebug() << "will load\n"; if (dep != Dependencies::None) env.loadBuiltins(); - foreach (QString s, positionalArguments) { - env.loadFile(s, QString(), nullptr, LoadOption::DefaultLoad, fileType); + QList loadedFiles(positionalArguments.size()); + qsizetype iPos = 0; + for (const QString &s : qAsConst(positionalArguments)) { + env.loadFile( + s, QString(), + [&loadedFiles, iPos](Path, const DomItem &, const DomItem &newIt) { + loadedFiles[iPos] = newIt; + }, + LoadOption::DefaultLoad, fileType); } envPtr->loadPendingDependencies(env); bool hadFailures = false; const qsizetype largestFileSizeToCheck = 32000; + if (parser.isSet(reformatOption)) { - for (auto s : positionalArguments) { - DomItem qmlFile = env.path(Paths::qmlFilePath(QFileInfo(s).canonicalFilePath())); - if (qmlFile) { - qDebug() << "reformatting" << s; - FileWriter fw; - LineWriterOptions lwOptions; - WriteOutChecks checks = WriteOutCheck::Default; - if (std::shared_ptr qmlFilePtr = qmlFile.ownerAs()) - if (qmlFilePtr->code().size() > largestFileSizeToCheck) - checks = WriteOutCheck::None; - QString target = s; - QString rDir = parser.value(reformatDirOption); - if (!rDir.isEmpty()) { - QFileInfo f(s); - QDir d(rDir); - target = d.filePath(f.fileName()); - } - MutableDomItem res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks); - switch (fw.status) { - case FileWriter::Status::ShouldWrite: - case FileWriter::Status::SkippedDueToFailure: - qWarning() << "failure reformatting " << s; - break; - case FileWriter::Status::DidWrite: - qDebug() << "success"; - break; - case FileWriter::Status::SkippedEqual: - qDebug() << "no change"; - } - hadFailures = hadFailures || !bool(res); + for (auto &qmlFile : loadedFiles) { + QString qmlFilePath = qmlFile.canonicalFilePath(); + if (qmlFile.internalKind() != DomType::QmlFile) { + qWarning() << "cannot reformat" << qmlFile.internalKindStr() << "(" << qmlFilePath + << ")"; + continue; } + qDebug() << "reformatting" << qmlFilePath; + FileWriter fw; + LineWriterOptions lwOptions; + WriteOutChecks checks = WriteOutCheck::Default; + if (std::shared_ptr qmlFilePtr = qmlFile.ownerAs()) + if (qmlFilePtr->code().size() > largestFileSizeToCheck) + checks = WriteOutCheck::None; + QString target = qmlFilePath; + QString rDir = parser.value(reformatDirOption); + if (!rDir.isEmpty()) { + QFileInfo f(qmlFilePath); + QDir d(rDir); + target = d.filePath(f.fileName()); + } + MutableDomItem res = qmlFile.writeOut(target, nBackups, lwOptions, &fw, checks); + switch (fw.status) { + case FileWriter::Status::ShouldWrite: + case FileWriter::Status::SkippedDueToFailure: + qWarning() << "failure reformatting " << qmlFilePath; + break; + case FileWriter::Status::DidWrite: + qDebug() << "success"; + break; + case FileWriter::Status::SkippedEqual: + qDebug() << "no change"; + } + hadFailures = hadFailures || !bool(res); } } else if (parser.isSet(dumpOption) || !parser.isSet(reformatOption)) { qDebug() << "will dump\n"; @@ -277,21 +291,35 @@ int main(int argc, char *argv[]) auto sink = [&ts](QStringView v) { ts << v; /* ts.flush(); */ }; - if (pathsToDump.length() > 1) + qsizetype iPathToDump = 0; + bool globalPaths = false; + for (auto p : pathsToDump) + if (p.headKind() == Path::Kind::Root) + globalPaths = true; + if (globalPaths) + loadedFiles = QList({ env }); + bool dumpDict = pathsToDump.size() > 1 || loadedFiles.size() > 1; + if (dumpDict) sink(u"{\n"); - bool first = true; - for (Path p : pathsToDump) { - if (pathsToDump.length() > 1) { - if (first) - first = false; - else - sink(u",\n"); - sinkEscaped(sink, p.toString()); - sink(u":\n"); + while (iPathToDump < pathsToDump.size()) { + for (auto &fileItem : loadedFiles) { + Path p = pathsToDump.at(iPathToDump++ % pathsToDump.size()); + if (dumpDict) { + if (iPathToDump > 1) + sink(u",\n"); + sink(u"\""); + if (fileItem.internalKind() != DomType::DomEnvironment) { + sinkEscaped(sink, fileItem.canonicalFilePath(), + EscapeOptions::NoOuterQuotes); + sink(u"/"); + } + sinkEscaped(sink, p.toString(), EscapeOptions::NoOuterQuotes); + sink(u"\":\n"); + } + fileItem.path(p).dump(sink, 0, filter); } - env.path(p).dump(sink, 0, filter); } - if (pathsToDump.length() > 1) + if (dumpDict) sink(u"}\n"); Qt::endl(ts).flush(); }