Details
-
Suggestion
-
Resolution: Unresolved
-
P2: Important
-
6.8
-
None
Description
Preserve the original format using comment NOFMT like this:
1. For JS
function anotherFunction(service) { switch(service) { case "0": return 0; case "11": return 11; } //NOFMT }
2. For bindings
model: [ { key: "firstKey", value: "value1" }, { key: "secondKey", value: "valueTwo" } ] // NOFMT
We were not able to find a way to implement this in a general way but we managed to implement it for 2 main applications.
For JS it can be solved by modifying:
qqmldomreformatter.cpp
bool ScriptFormatter::visit(StatementList *ast) { ++expressionDepth; for (StatementList *it = ast; it; it = it->next) { // ### work around parser bug: skip empty statements with wrong tokens if (EmptyStatement *emptyStatement = cast<EmptyStatement *>(it->statement)) { if (loc2Str(emptyStatement->semicolonToken) != QLatin1String(";")) continue; } auto *commentForCurrentStatement = comments->commentForNode(it->statement); if (commentForCurrentStatement){ auto postComments = commentForCurrentStatement->postComments(); //look for NOFMT special comments in the post comments const bool noFormat = postComments.end() != std::find_if(postComments.begin(), postComments.end(), [&](Comment &comment){ return comment.rawComment().contains(QStringLiteral(u"NOFMT")); }); //if there is a NOFMT then output unformatted source if (noFormat) { //calculate original source location const auto firstLocation = it->statement->firstSourceLocation(); const auto lastLocation = it ->statement->lastSourceLocation(); const quint32 srcLength = lastLocation.offset + lastLocation.length - firstLocation.offset; const SourceLocation src (firstLocation.offset, srcLength, firstLocation.startLine, firstLocation.startColumn); preVisit(it->statement); out(src); postVisit(it->statement); newLine(1); } else { accept(it->statement); } } else { accept(it->statement); } if (it->next) { // There might be a post-comment attached to the current // statement or a pre-comment attached to the next // statmente or both. // If any of those are present they will take care of // handling the spacing between the statements so we // don't need to push any newline. auto *commentForNextStatement = comments->commentForNode(it->next->statement); if ( (commentForCurrentStatement && !commentForCurrentStatement->postComments().empty()) || (commentForNextStatement && !commentForNextStatement->preComments().empty()) ) continue; quint32 lineDelta = it->next->firstSourceLocation().startLine - it->statement->lastSourceLocation().startLine; lineDelta = std::clamp(lineDelta, quint32{ 1 }, quint32{ 2 }); newLine(lineDelta); } } --expressionDepth; return false; }
For bindings:
qqmldomelements.cpp
static QString unescape (const QString& escaped, bool unquote = true) { QString unescaped; unescaped.reserve(escaped.length()); bool escaping = false; // Are there surrounding quote chars, and shall we remove them? const bool needsSliceing = unquote && escaped.length() > 2 && escaped[0] == u'\"' && escaped[escaped.length()-1] == u'\"'; // Remove surrounding quotes const QString escapedUnquoted = needsSliceing ? escaped.sliced(1, escaped.length() - 2) : escaped; // Unescape chars for (auto c: escapedUnquoted) { // The escape char "\" will just enter escaping mode, but will not be copied if (c == u'\\') { escaping = true; continue; } // In escape mode, n, r and t have special meaning if (escaping) { switch (c.toLatin1()) { case u'n': unescaped.append(u'\n'); continue; case u'r': unescaped.append(u'\r'); continue; case u't': unescaped.append(u'\t'); continue; } } // leave escaping mode if needed and output current char escaping = false; unescaped.append(c); } return unescaped; } void Binding::writeOutValue(const DomItem &self, OutWriter &lw) const { QList<DomItem> postComments = self[Fields::comments] [Fields::regionComments] [QStringLiteral(u"MainRegion") /*regionName*/] [Fields::postComments] .values(); const bool noFormat = postComments.end() != std::find_if(postComments.begin(), postComments.end(), [&](DomItem &comment){ return comment[Fields::rawComment].value().toString().contains(QStringLiteral(u"NOFMT")); }); if (noFormat) { // "NOFMT" was found in the post comments, so write out the original code instead of reformatting it: const QString originalCode = unescape(self[Fields::value][Fields::code].toString()); const auto oldIndent = lw.indent; lw.indent = 0; lw.write(originalCode, LineWriter::TextAddType::Eof); lw.indent = oldIndent; return; } DomItem v = valueItem(self); switch (valueKind()) { case BindingValueKind::Empty: qCWarning(writeOutLog()) << "Writing of empty binding " << name(); lw.write(u"{}"); 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; } }
Note: All code based on Qt 6.8
Attachments
Issue Links
- is covered by
-
QTBUG-131686 qmlformat: Provide a way to preserve line breaks in multi-line expressions
- Reported