diff options
Diffstat (limited to 'src/qmldom/qqmldomoutwriter.cpp')
-rw-r--r-- | src/qmldom/qqmldomoutwriter.cpp | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomoutwriter.cpp b/src/qmldom/qqmldomoutwriter.cpp new file mode 100644 index 0000000000..90c9589b7d --- /dev/null +++ b/src/qmldom/qqmldomoutwriter.cpp @@ -0,0 +1,242 @@ +/**************************************************************************** +** +** Copyright (C) 2020 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of the QtQml module of the Qt Toolkit. +** +** $QT_BEGIN_LICENSE:LGPL$ +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 3 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL3 included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 3 requirements +** will be met: https://www.gnu.org/licenses/lgpl-3.0.html. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 2.0 or (at your option) the GNU General +** Public license version 3 or any later version approved by the KDE Free +** Qt Foundation. The licenses are as published by the Free Software +** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3 +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-2.0.html and +** https://www.gnu.org/licenses/gpl-3.0.html. +** +** $QT_END_LICENSE$ +**/ +#include "qqmldomoutwriter_p.h" +#include "qqmldomattachedinfo_p.h" +#include "qqmldomlinewriter_p.h" +#include "qqmldomitem_p.h" +#include "qqmldomcomments_p.h" +#include "qqmldomexternalitems_p.h" +#include "qqmldomtop_p.h" + +#include <QtCore/QLoggingCategory> + +QT_BEGIN_NAMESPACE +namespace QQmlJS { +namespace Dom { + +OutWriterState::OutWriterState(Path itCanonicalPath, DomItem &it, FileLocations::Tree fLoc) + : itemCanonicalPath(itCanonicalPath), item(it), currentMap(fLoc) +{ + DomItem cRegions = it.field(Fields::comments); + if (const RegionComments *cRegionsPtr = cRegions.as<RegionComments>()) { + pendingComments = cRegionsPtr->regionComments; + fLoc->info().ensureCommentLocations(pendingComments.keys()); + } +} + +void OutWriterState::closeState(OutWriter &w) +{ + if (w.lineWriter.options().updateOptions & LineWriterOptions::Update::Locations) + w.lineWriter.endSourceLocation(fullRegionId); + if (!pendingRegions.isEmpty()) { + qCWarning(writeOutLog) << "PendingRegions non empty when closing item" + << pendingRegions.keys(); + auto iend = pendingRegions.end(); + auto it = pendingRegions.begin(); + while (it == iend) { + w.lineWriter.endSourceLocation(it.value()); + ++it; + } + } + if (!w.skipComments && !pendingComments.isEmpty()) + qCWarning(writeOutLog) << "PendingComments when closing item " + << item.canonicalPath().toString() << "for regions" + << pendingComments.keys(); +} + +OutWriterState &OutWriter::state(int i) +{ + return states[states.size() - 1 - i]; +} + +void OutWriter::itemStart(DomItem &it) +{ + if (!topLocation->path()) + topLocation->setPath(it.canonicalPath()); + bool updateLocs = lineWriter.options().updateOptions & LineWriterOptions::Update::Locations; + FileLocations::Tree newFLoc = topLocation; + Path itP = it.canonicalPath(); + if (updateLocs) { + if (!states.isEmpty() + && states.last().itemCanonicalPath + == itP.mid(0, states.last().itemCanonicalPath.length())) { + int oldL = states.last().itemCanonicalPath.length(); + newFLoc = FileLocations::ensure(states.last().currentMap, + itP.mid(oldL, itP.length() - oldL), + AttachedInfo::PathType::Relative); + + } else { + newFLoc = FileLocations::ensure(topLocation, itP, AttachedInfo::PathType::Canonical); + } + } + states.append(OutWriterState(itP, it, newFLoc)); + if (updateLocs) + state().fullRegionId = lineWriter.startSourceLocation( + [newFLoc](SourceLocation l) { FileLocations::updateFullLocation(newFLoc, l); }); + regionStart(QString()); +} + +void OutWriter::itemEnd(DomItem &it) +{ + Q_ASSERT(states.size() > 0); + Q_ASSERT(state().item == it); + regionEnd(QString()); + state().closeState(*this); + states.removeLast(); +} + +void OutWriter::regionStart(QString rName) +{ + Q_ASSERT(!state().pendingRegions.contains(rName)); + FileLocations::Tree fMap = state().currentMap; + if (!skipComments && state().pendingComments.contains(rName)) { + bool updateLocs = lineWriter.options().updateOptions & LineWriterOptions::Update::Locations; + QList<SourceLocation> *cLocs = + (updateLocs ? &(fMap->info().preCommentLocations[rName]) : nullptr); + state().pendingComments[rName].writePre(*this, cLocs); + } + state().pendingRegions[rName] = lineWriter.startSourceLocation( + [rName, fMap](SourceLocation l) { FileLocations::addRegion(fMap, rName, l); }); +} + +void OutWriter::regionEnd(QString rName) +{ + Q_ASSERT(state().pendingRegions.contains(rName)); + FileLocations::Tree fMap = state().currentMap; + lineWriter.endSourceLocation(state().pendingRegions.value(rName)); + state().pendingRegions.remove(rName); + if (state().pendingComments.contains(rName)) { + if (!skipComments) { + bool updateLocs = + lineWriter.options().updateOptions & LineWriterOptions::Update::Locations; + QList<SourceLocation> *cLocs = + (updateLocs ? &(fMap->info().postCommentLocations[rName]) : nullptr); + state().pendingComments[rName].writePost(*this, cLocs); + } + state().pendingComments.remove(rName); + } +} + +OutWriter &OutWriter::writeRegion(QString rName, QStringView toWrite) +{ + regionStart(rName); + lineWriter.write(toWrite); + regionEnd(rName); + return *this; +} + +DomItem OutWriter::updatedFile(DomItem &qmlFile) +{ + Q_ASSERT(qmlFile.internalKind() == DomType::QmlFile); + if (std::shared_ptr<QmlFile> qmlFilePtr = qmlFile.ownerAs<QmlFile>()) { + std::shared_ptr<QmlFile> copyPtr = qmlFilePtr->makeCopy(qmlFile); + DomItem env = qmlFile.environment(); + std::shared_ptr<DomEnvironment> envPtr = env.ownerAs<DomEnvironment>(); + Q_ASSERT(envPtr); + std::shared_ptr<DomEnvironment> newEnvPtr( + new DomEnvironment(envPtr, envPtr->loadPaths(), envPtr->options())); + newEnvPtr->addQmlFile(copyPtr); + MutableDomItem copy = MutableDomItem(DomItem(newEnvPtr).copy(copyPtr)); + FileLocations::Tree newLoc = topLocation; + Path qmlFilePath = qmlFile.canonicalPath(); + if (newLoc->path() != qmlFilePath) { + if (newLoc->path()) { + if (newLoc->path().length() > qmlFilePath.length() + && newLoc->path().mid(0, qmlFilePath.length()) == qmlFilePath) { + newLoc = FileLocations::createTree(qmlFilePath); + FileLocations::Tree loc = + FileLocations::ensure(newLoc, newLoc->path().mid(qmlFilePath.length()), + AttachedInfo::PathType::Relative); + loc->setSubItems(topLocation->subItems()); + } else { + qCWarning(writeOutLog) + << "failed to base fileLocations in OutWriter (" << newLoc->path() + << ") to current file (" << qmlFilePath << ")"; + } + } else { + newLoc = FileLocations::createTree(qmlFilePath); + Q_ASSERT(newLoc->subItems().isEmpty() && newLoc->info().regions.isEmpty()); + } + } + copyPtr->setFileLocationsTree(newLoc); + UpdatedScriptExpression::visitTree( + reformattedScriptExpressions, + [©, qmlFilePath](Path p, UpdatedScriptExpression::Tree t) { + if (std::shared_ptr<ScriptExpression> exprPtr = t->info().expr) { + Q_ASSERT(p.mid(0, qmlFilePath.length()) == qmlFilePath); + MutableDomItem targetExpr = copy.path(p.mid(qmlFilePath.length())); + if (!targetExpr) + qCWarning(writeOutLog) << "failed to get" << p.mid(qmlFilePath.length()) + << "from" << copy.canonicalPath(); + else if (exprPtr->ast() + || (!targetExpr.as<ScriptExpression>() + || !targetExpr.as<ScriptExpression>()->ast())) + targetExpr.setScript(exprPtr); + else { + qCWarning(writeOutLog).noquote() + << "Skipped update of reformatted ScriptExpression with " + "code:\n---------------\n" + << exprPtr->code() << "\n---------------\n preCode:" << + [exprPtr](Sink s) { sinkEscaped(s, exprPtr->preCode()); } + << "\n postCode: " << + [exprPtr](Sink s) { sinkEscaped(s, exprPtr->postCode()); } + << "\n as it failed standalone reparse with errors:" << + [&targetExpr, exprPtr](Sink s) { + targetExpr.item() + .copy(exprPtr, targetExpr.canonicalPath()) + .iterateErrors( + [s](DomItem, ErrorMessage msg) { + s(u"\n "); + msg.dump(s); + return true; + }, + true); + } + << "\n"; + } + } + return true; + }); + return copy.item(); + } + return DomItem(); +} + +} // namespace Dom +} // namespace QQmlJS +QT_END_NAMESPACE |