aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomoutwriter.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomoutwriter.cpp')
-rw-r--r--src/qmldom/qqmldomoutwriter.cpp363
1 files changed, 264 insertions, 99 deletions
diff --git a/src/qmldom/qqmldomoutwriter.cpp b/src/qmldom/qqmldomoutwriter.cpp
index 7bdbf505e2..7f4f219cbf 100644
--- a/src/qmldom/qqmldomoutwriter.cpp
+++ b/src/qmldom/qqmldomoutwriter.cpp
@@ -15,14 +15,13 @@ QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
-OutWriterState::OutWriterState(Path itCanonicalPath, DomItem &it, FileLocations::Tree fLoc)
+OutWriterState::OutWriterState(
+ const Path &itCanonicalPath, const DomItem &it, const 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());
- }
+ if (const RegionComments *cRegionsPtr = cRegions.as<RegionComments>())
+ pendingComments = cRegionsPtr->regionComments();
}
void OutWriterState::closeState(OutWriter &w)
@@ -50,7 +49,7 @@ OutWriterState &OutWriter::state(int i)
return states[states.size() - 1 - i];
}
-void OutWriter::itemStart(DomItem &it)
+void OutWriter::itemStart(const DomItem &it)
{
if (!topLocation->path())
topLocation->setPath(it.canonicalPath());
@@ -74,135 +73,301 @@ void OutWriter::itemStart(DomItem &it)
if (updateLocs)
state().fullRegionId = lineWriter.startSourceLocation(
[newFLoc](SourceLocation l) { FileLocations::updateFullLocation(newFLoc, l); });
- regionStart(QString());
+ regionStart(MainRegion);
}
-void OutWriter::itemEnd(DomItem &it)
+void OutWriter::itemEnd(const DomItem &it)
{
Q_ASSERT(states.size() > 0);
Q_ASSERT(state().item == it);
- regionEnd(QString());
+ regionEnd(MainRegion);
state().closeState(*this);
states.removeLast();
}
-void OutWriter::regionStart(QString rName)
+void OutWriter::regionStart(FileLocationRegion region)
{
- Q_ASSERT(!state().pendingRegions.contains(rName));
+ Q_ASSERT(!state().pendingRegions.contains(region));
FileLocations::Tree fMap = state().currentMap;
- if (!skipComments && state().pendingComments.contains(rName)) {
+ if (!skipComments && state().pendingComments.contains(region)) {
bool updateLocs = lineWriter.options().updateOptions & LineWriterOptions::Update::Locations;
QList<SourceLocation> *cLocs =
- (updateLocs ? &(fMap->info().preCommentLocations[rName]) : nullptr);
- state().pendingComments[rName].writePre(*this, cLocs);
+ (updateLocs ? &(fMap->info().preCommentLocations[region]) : nullptr);
+ state().pendingComments[region].writePre(*this, cLocs);
}
- state().pendingRegions[rName] = lineWriter.startSourceLocation(
- [rName, fMap](SourceLocation l) { FileLocations::addRegion(fMap, rName, l); });
+ state().pendingRegions[region] = lineWriter.startSourceLocation(
+ [region, fMap](SourceLocation l) { FileLocations::addRegion(fMap, region, l); });
}
-void OutWriter::regionEnd(QString rName)
+void OutWriter::regionEnd(FileLocationRegion region)
{
- Q_ASSERT(state().pendingRegions.contains(rName));
+ Q_ASSERT(state().pendingRegions.contains(region));
FileLocations::Tree fMap = state().currentMap;
- lineWriter.endSourceLocation(state().pendingRegions.value(rName));
- state().pendingRegions.remove(rName);
- if (state().pendingComments.contains(rName)) {
+ lineWriter.endSourceLocation(state().pendingRegions.value(region));
+ state().pendingRegions.remove(region);
+ if (state().pendingComments.contains(region)) {
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);
+ (updateLocs ? &(fMap->info().postCommentLocations[region]) : nullptr);
+ state().pendingComments[region].writePost(*this, cLocs);
}
- state().pendingComments.remove(rName);
+ state().pendingComments.remove(region);
}
}
-OutWriter &OutWriter::writeRegion(QString rName, QStringView toWrite)
+/*!
+\internal
+Helper method for writeRegion(FileLocationRegion region) that allows to use
+\c{writeRegion(ColonTokenRegion);} instead of having to write out the more error-prone
+\c{writeRegion(ColonTokenRegion, ":");} for tokens and keywords.
+*/
+OutWriter &OutWriter::writeRegion(FileLocationRegion region)
{
- regionStart(rName);
+ QString codeForRegion;
+ switch (region) {
+ case ComponentKeywordRegion:
+ codeForRegion = u"component"_s;
+ break;
+ case IdColonTokenRegion:
+ case ColonTokenRegion:
+ codeForRegion = u":"_s;
+ break;
+ case ImportTokenRegion:
+ codeForRegion = u"import"_s;
+ break;
+ case AsTokenRegion:
+ codeForRegion = u"as"_s;
+ break;
+ case OnTokenRegion:
+ codeForRegion = u"on"_s;
+ break;
+ case IdTokenRegion:
+ codeForRegion = u"id"_s;
+ break;
+ case LeftBraceRegion:
+ codeForRegion = u"{"_s;
+ break;
+ case RightBraceRegion:
+ codeForRegion = u"}"_s;
+ break;
+ case LeftBracketRegion:
+ codeForRegion = u"["_s;
+ break;
+ case RightBracketRegion:
+ codeForRegion = u"]"_s;
+ break;
+ case LeftParenthesisRegion:
+ codeForRegion = u"("_s;
+ break;
+ case RightParenthesisRegion:
+ codeForRegion = u")"_s;
+ break;
+ case EnumKeywordRegion:
+ codeForRegion = u"enum"_s;
+ break;
+ case DefaultKeywordRegion:
+ codeForRegion = u"default"_s;
+ break;
+ case RequiredKeywordRegion:
+ codeForRegion = u"required"_s;
+ break;
+ case ReadonlyKeywordRegion:
+ codeForRegion = u"readonly"_s;
+ break;
+ case PropertyKeywordRegion:
+ codeForRegion = u"property"_s;
+ break;
+ case FunctionKeywordRegion:
+ codeForRegion = u"function"_s;
+ break;
+ case SignalKeywordRegion:
+ codeForRegion = u"signal"_s;
+ break;
+ case ReturnKeywordRegion:
+ codeForRegion = u"return"_s;
+ break;
+ case EllipsisTokenRegion:
+ codeForRegion = u"..."_s;
+ break;
+ case EqualTokenRegion:
+ codeForRegion = u"="_s;
+ break;
+ case PragmaKeywordRegion:
+ codeForRegion = u"pragma"_s;
+ break;
+ case CommaTokenRegion:
+ codeForRegion = u","_s;
+ break;
+ case ForKeywordRegion:
+ codeForRegion = u"for"_s;
+ break;
+ case ElseKeywordRegion:
+ codeForRegion = u"else"_s;
+ break;
+ case DoKeywordRegion:
+ codeForRegion = u"do"_s;
+ break;
+ case WhileKeywordRegion:
+ codeForRegion = u"while"_s;
+ break;
+ case TryKeywordRegion:
+ codeForRegion = u"try"_s;
+ break;
+ case CatchKeywordRegion:
+ codeForRegion = u"catch"_s;
+ break;
+ case FinallyKeywordRegion:
+ codeForRegion = u"finally"_s;
+ break;
+ case CaseKeywordRegion:
+ codeForRegion = u"case"_s;
+ break;
+ case ThrowKeywordRegion:
+ codeForRegion = u"throw"_s;
+ break;
+ case ContinueKeywordRegion:
+ codeForRegion = u"continue"_s;
+ break;
+ case BreakKeywordRegion:
+ codeForRegion = u"break"_s;
+ break;
+ case QuestionMarkTokenRegion:
+ codeForRegion = u"?"_s;
+ break;
+ case SemicolonTokenRegion:
+ codeForRegion = u";"_s;
+ break;
+ case IfKeywordRegion:
+ codeForRegion = u"if"_s;
+ break;
+ case SwitchKeywordRegion:
+ codeForRegion = u"switch"_s;
+ break;
+ // not keywords:
+ case ImportUriRegion:
+ case IdNameRegion:
+ case IdentifierRegion:
+ case PragmaValuesRegion:
+ case MainRegion:
+ case OnTargetRegion:
+ case TypeIdentifierRegion:
+ case FirstSemicolonTokenRegion:
+ case SecondSemicolonRegion:
+ case InOfTokenRegion:
+ case OperatorTokenRegion:
+ case VersionRegion:
+ case EnumValueRegion:
+ Q_ASSERT_X(false, "regionToString", "Using regionToString on a value or an identifier!");
+ return *this;
+ }
+
+ return writeRegion(region, codeForRegion);
+}
+
+OutWriter &OutWriter::writeRegion(FileLocationRegion region, QStringView toWrite)
+{
+ regionStart(region);
lineWriter.write(toWrite);
- regionEnd(rName);
+ regionEnd(region);
return *this;
}
+/*!
+ \internal
+ Restores written out FileItem using intermediate information saved during DOM traversal.
+ It enables verifying DOM consistency of the written item later.
-DomItem OutWriter::updatedFile(DomItem &qmlFile)
+ At the moment of writing, intermediate information consisting only of UpdatedScriptExpression,
+ however this is subject for change. The process of restoration is the following:
+ 1. Creating copy of the initial fileItem
+ 2. Updating relevant data/subitems modified during the WriteOut
+ 3. Returning an item containing updates.
+ */
+DomItem OutWriter::restoreWrittenFileItem(const DomItem &fileItem)
{
- 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);
- auto newEnvPtr = std::make_shared<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());
+ switch (fileItem.internalKind()) {
+ case DomType::QmlFile:
+ return writtenQmlFileItem(fileItem, fileItem.canonicalPath());
+ case DomType::JsFile:
+ return writtenJsFileItem(fileItem, fileItem.canonicalPath());
+ default:
+ qCWarning(writeOutLog) << fileItem.internalKind() << " is not supported";
+ return DomItem{};
+ }
+}
+
+DomItem OutWriter::writtenQmlFileItem(const DomItem &fileItem, const Path &filePath)
+{
+ Q_ASSERT(fileItem.internalKind() == DomType::QmlFile);
+ auto mutableFile = fileItem.makeCopy(DomItem::CopyOption::EnvDisconnected);
+ // QmlFile specific visitor for reformattedScriptExpressions tree
+ // lambda function responsible for the update of the initial expression by the formatted one
+ auto exprUpdater = [&mutableFile, filePath](
+ const Path &p, const UpdatedScriptExpression::Tree &t) {
+ if (std::shared_ptr<ScriptExpression> formattedExpr = t->info().expr) {
+ Q_ASSERT(p.mid(0, filePath.length()) == filePath);
+ MutableDomItem originalExprItem = mutableFile.path(p.mid(filePath.length()));
+ if (!originalExprItem)
+ qCWarning(writeOutLog) << "failed to get" << p.mid(filePath.length()) << "from"
+ << mutableFile.canonicalPath();
+ // Verifying originalExprItem.as<ScriptExpression>() == false is handy
+ // because we can't call setScript on the ScriptExpression itself and it needs to
+ // be called on the container / parent item. See setScript for details
+ else if (formattedExpr->ast()
+ || (!originalExprItem.as<ScriptExpression>()
+ || !originalExprItem.as<ScriptExpression>()->ast()))
+ originalExprItem.setScript(formattedExpr);
+ else {
+ logScriptExprUpdateSkipped(originalExprItem.item(),
+ originalExprItem.canonicalPath(), formattedExpr);
}
}
- copyPtr->setFileLocationsTree(newLoc);
- UpdatedScriptExpression::visitTree(
- reformattedScriptExpressions,
- [&copy, 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();
+ return true;
+ };
+ // update relevant formatted expressions
+ UpdatedScriptExpression::visitTree(reformattedScriptExpressions, exprUpdater);
+ return mutableFile.item();
}
+DomItem OutWriter::writtenJsFileItem(const DomItem &fileItem, const Path &filePath)
+{
+ Q_ASSERT(fileItem.internalKind() == DomType::JsFile);
+ auto mutableFile = fileItem.makeCopy(DomItem::CopyOption::EnvDisconnected);
+ UpdatedScriptExpression::visitTree(
+ reformattedScriptExpressions,
+ [&mutableFile, filePath](const Path &p, const UpdatedScriptExpression::Tree &t) {
+ if (std::shared_ptr<ScriptExpression> formattedExpr = t->info().expr) {
+ Q_ASSERT(p.mid(0, filePath.length()) == filePath);
+ mutableFile.mutableAs<JsFile>()->setExpression(formattedExpr);
+ }
+ return true;
+ });
+ return mutableFile.item();
+}
+
+void OutWriter::logScriptExprUpdateSkipped(
+ const DomItem &exprItem, const Path &exprPath,
+ const std::shared_ptr<ScriptExpression> &formattedExpr)
+{
+ qCWarning(writeOutLog).noquote() << "Skipped update of reformatted ScriptExpression with "
+ "code:\n---------------\n"
+ << formattedExpr->code() << "\n---------------\n preCode:" <<
+ [&formattedExpr](Sink s) { sinkEscaped(s, formattedExpr->preCode()); }
+ << "\n postCode: " <<
+ [&formattedExpr](Sink s) { sinkEscaped(s, formattedExpr->postCode()); }
+ << "\n as it failed standalone reparse with errors:" <<
+ [&exprItem, &exprPath, &formattedExpr](Sink s) {
+ exprItem.copy(formattedExpr, exprPath)
+ .iterateErrors(
+ [s](const DomItem &, const ErrorMessage &msg) {
+ s(u"\n ");
+ msg.dump(s);
+ return true;
+ },
+ true);
+ } << "\n";
+}
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE