qmldom: improve qmldom tool dump
Make the default dump more concise and useful: * remove most large fields non related to the semantics (comments, file location, exports, pre/postCode,...) * allow empty (catch all) filter * call filter.setFiltred() to correctly filter custom filters * dump the loaded files, and not the whole environment (unless requested with --path-to-dump $env) * do not load dependencies by default Change-Id: I5d26dc074bc0cbace31508401e9d08d90c99a254 Pick-to: 6.2 Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
This commit is contained in:
parent
bd1a62be00
commit
4cfb323389
|
@ -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 <op><typeName>:<fieldName> or <op><fieldName>
|
||||
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 <op><typeName>:<fieldName> or <op><fieldName>
|
||||
// as described in this class documentation
|
||||
QRegularExpression fieldRe(QRegularExpression::anchoredPattern(QStringLiteral(
|
||||
uR"((?<op>[-+])?(?:(?<type>[a-zA-Z0-9_]*):)?(?<field>[a-zA-Z0-9_]+))")));
|
||||
uR"((?<op>[-+])?(?:(?<type>[a-zA-Z0-9_]*):)?(?<field>[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<QString, QString> fieldFilterAdd { { QLatin1String("ScriptExpression"),
|
||||
QLatin1String("code") } };
|
||||
QMultiMap<QString, QString> fieldFilterRemove { { QString(), QLatin1String("code") },
|
||||
{ QString(), QLatin1String("propertyInfos") },
|
||||
{ QLatin1String("AttachedInfo"),
|
||||
QLatin1String("parent") } };
|
||||
|
||||
QMultiMap<QString, QString> 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 };
|
||||
}
|
||||
|
||||
|
|
|
@ -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<DomType> 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<Dependencies>();
|
||||
bool found = false;
|
||||
for (int i = 0; i < metaEnum.keyCount(); ++i) {
|
||||
|
@ -188,7 +191,7 @@ int main(int argc, char *argv[])
|
|||
}
|
||||
|
||||
QList<Path> 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<DomItem> 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<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
|
||||
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<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>())
|
||||
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<DomItem>({ 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();
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue