Details
-
Suggestion
-
Resolution: Unresolved
-
P2: Important
-
6.8
-
None
Description
When calling qmlformat using "-n" not only qml elements get sorted by category but also within the categories alphabetically, this will for example separate width and height properties.
This can be done by sorting by internal type first and source locations second.
// sort attributes by file location std::stable_sort(attribs.begin(), attribs.end(), [propertyDefKeys](const std::pair<SourceLocation, DomItem> &el1, const std::pair<SourceLocation, DomItem> &el2) { auto item1 = const_cast<DomItem&>(el1.second); auto item2 = const_cast<DomItem&>(el2.second); int prio1 = sortPrio(item1, propertyDefKeys); int prio2 = sortPrio(item2, propertyDefKeys); const int prioDiff = prio1 - prio2; if (prioDiff != 0) { return prioDiff < 0; } // Compare original source location return el1.first.offset < el2.first.offset; });
By providing a sortPrio method, we can fine tune the order:
// TODO: Maybe we need 2 defaults, one being the highest, one being the lowest? enum SortPrio { defaultPrio = 0, importPrio = 1, enumPrio, idPrio, objectNamePrio, propPrio, signalPrio, methodPrio, bindingSimplePrio, bindingObjectPrio, signalHandlerPrio, childPrio, componentPrio }; static int sortPrio (DomItem &item, const QStringList &propDefKeys) { DomType type = item.internalKind(); switch (type) { case DomType::Empty: case DomType::ExternalItemInfo: case DomType::ExternalItemPair: return defaultPrio; // ExternalOwningItems refer to an external path and can be shared between environments case DomType::QmlDirectory: // dir case DomType::QmldirFile: // qmldir case DomType::JsFile: // file case DomType::QmlFile: // file case DomType::QmltypesFile: // qmltypes case DomType::GlobalScope: // language dependent return importPrio; case DomType::EnumItem: case DomType::EnumDecl: return enumPrio; case DomType::JsResource: return importPrio; case DomType::QmltypesComponent: case DomType::QmlComponent: case DomType::GlobalComponent: case DomType::ModuleAutoExport: // dependent imports to automatically load when a module is imported case DomType::ModuleIndex: // index for all the imports of a major version case DomType::ModuleScope: // a specific import with full version case DomType::ImportScope: // the scope including the types coming from one or more imports case DomType::Export: // An exported type // header stuff case DomType::Import: // wrapped case DomType::Pragma: return importPrio; // qml elements case DomType::Id: return idPrio; case DomType::QmlObject: return childPrio; case DomType::ConstantData: case DomType::SimpleObjectWrap: case DomType::ScriptExpression: case DomType::Reference: case DomType::PropertyDefinition: return propPrio; case DomType::Binding: { // Keep bindings to objectNames right after id if (item.name() == QStringLiteral(u"objectName")) { return objectNamePrio; } // Keep bindings to property definitions along with them if (propDefKeys.contains(item.name())) { return propPrio; } // Try to detect different kind of binding values: simple, complex and signal handlers const auto binding = item.as<Binding>(); if (binding->valueKind() == BindingValueKind::Array || binding->valueKind() == BindingValueKind::Object) { return bindingObjectPrio; } if (item.field(Fields::isSignalHandler).value().toBool(false)) { return signalHandlerPrio; } // Just a simnple binding return bindingSimplePrio; } case DomType::MethodParameter: case DomType::MethodInfo: { const auto methodInfo = item.as<MethodInfo>(); return (methodInfo && methodInfo->methodType == MethodInfo::MethodType::Signal) ? signalPrio : methodPrio; } case DomType::Version: // wrapped case DomType::Comment: case DomType::CommentedElement: case DomType::RegionComments: case DomType::AstComments: case DomType::FileLocations: case DomType::UpdatedScriptExpression: return defaultPrio; // convenience collecting types case DomType::PropertyInfo: return propPrio; // Moc objects, mainly for testing case DomType::MockObject: case DomType::MockOwner: // containers case DomType::Map: case DomType::List: case DomType::ListP: return bindingObjectPrio; // supporting objects case DomType::LoadInfo: // owning case DomType::ErrorMessage: // wrapped case DomType::AttachedInfo: // owning // Dom top level case DomType::DomEnvironment: case DomType::DomUniverse: return defaultPrio; } return defaultPrio; }
Empty lines can be added between categories, objects and functions like this:
... qsizetype iAttr = 0; int previousPrio = -1; 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) break; } else if (!c.isSpace()) break; } } //id might have already been written if (previousPrio == -1 && !idStr().isEmpty()) { previousPrio = SortPrio::idPrio; } int currentPrio = sortPrio(el.second, propertyDefKeys); //keep objectName together with id if (currentPrio == SortPrio::objectNamePrio) { currentPrio = SortPrio::idPrio; } //define elements which also require new lines in-between const QList<SortPrio> requireNewLine = {SortPrio::methodPrio, SortPrio::bindingObjectPrio, SortPrio::childPrio}; //insert a new line if // - there is no new line already // - either changing the category or the category requires a new line in between // - is not the first element in the current object if (preNewlines < 2 && (currentPrio != previousPrio || requireNewLine.contains(currentPrio)) && previousPrio != -1 ) { ++preNewlines; } previousPrio = currentPrio; if (preNewlines == 0) ++preNewlines; ...
We also placed the objectName right after the id.
Attachments
Gerrit Reviews
For Gerrit Dashboard: QTBUG-132060 | ||||||
---|---|---|---|---|---|---|
# | Subject | Branch | Project | Status | CR | V |
625732,7 | qmlformat: Relax normalization | dev | qt/qtdeclarative | Status: NEW | 0 | 0 |