Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-132058

qmlformat: Preserve the original format using comment NOFMT

    XMLWordPrintable

Details

    • Suggestion
    • Resolution: Unresolved
    • P2: Important
    • 6.11
    • 6.8
    • QML: Tooling
    • None
    • All

    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

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              qtqmlteam Qt Qml Team User
              ovidiu.tepescu Ovidiu Tepescu
              Votes:
              2 Vote for this issue
              Watchers:
              2 Start watching this issue

              Dates

                Created:
                Updated:

                Gerrit Reviews

                  There are no open Gerrit changes