aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom/qqmldomattachedinfo.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'src/qmldom/qqmldomattachedinfo.cpp')
-rw-r--r--src/qmldom/qqmldomattachedinfo.cpp345
1 files changed, 345 insertions, 0 deletions
diff --git a/src/qmldom/qqmldomattachedinfo.cpp b/src/qmldom/qqmldomattachedinfo.cpp
new file mode 100644
index 0000000000..e86a2782a6
--- /dev/null
+++ b/src/qmldom/qqmldomattachedinfo.cpp
@@ -0,0 +1,345 @@
+// Copyright (C) 2021 The Qt Company Ltd.
+// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
+#include "qqmldom_fwd_p.h"
+#include "qqmldomlinewriter_p.h"
+#include "qqmldomelements_p.h"
+#include "qqmldompath_p.h"
+
+QT_BEGIN_NAMESPACE
+namespace QQmlJS {
+namespace Dom {
+
+using namespace Qt::StringLiterals;
+
+
+/*!
+\internal
+\class QQmlJS::Dom::FileLocations
+\brief Represents and maintains a mapping between elements and their location in a file
+
+The location information is attached to the element it refers to via AttachedInfo
+There are static methods to simplify the handling of the tree of AttachedInfo.
+
+Attributes:
+\list
+\li fullRegion: A location guaranteed to include this element, its comments, and all its sub elements
+\li regions: a map with locations of regions of this element, the empty string is the default region
+ of this element
+\li preCommentLocations: locations of the comments before this element
+\li postCommentLocations: locations of the comments after this element
+\endlist
+
+\sa QQmlJs::Dom::AttachedInfo
+*/
+bool FileLocations::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont = cont && self.dvValueLazyField(visitor, Fields::fullRegion, [this]() {
+ return sourceLocationToQCborValue(fullRegion);
+ });
+ cont = cont && self.dvItemField(visitor, Fields::regions, [this, &self]() -> DomItem {
+ const Path pathFromOwner = self.pathFromOwner().field(Fields::regions);
+ auto map = Map::fromFileRegionMap(pathFromOwner, regions);
+ return self.subMapItem(map);
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::preCommentLocations, [this, &self]() -> DomItem {
+ const Path pathFromOwner =
+ self.pathFromOwner().field(Fields::preCommentLocations);
+ auto map = Map::fromFileRegionListMap(pathFromOwner, preCommentLocations);
+ return self.subMapItem(map);
+ });
+ cont = cont
+ && self.dvItemField(visitor, Fields::postCommentLocations, [this, &self]() -> DomItem {
+ const Path pathFromOwner =
+ self.pathFromOwner().field(Fields::postCommentLocations);
+ auto map = Map::fromFileRegionListMap(pathFromOwner, postCommentLocations);
+ return self.subMapItem(map);
+ });
+ return cont;
+}
+
+FileLocations::Tree FileLocations::createTree(const Path &basePath){
+ return AttachedInfoT<FileLocations>::createTree(basePath);
+}
+
+FileLocations::Tree FileLocations::ensure(
+ const FileLocations::Tree &base, const Path &basePath, AttachedInfo::PathType pType)
+{
+ return AttachedInfoT<FileLocations>::ensure(base, basePath, pType);
+}
+
+/*!
+\internal
+Allows to query information about the FileLocations::Tree obtained from item, such as path of
+the Tree root in the Dom, the path of this item's Tree in the Dom, and so on.
+
+\note You can use \c{qDebug() << item.path(FileLocations::findAttachedInfo(item).foundTreePath)} or
+\c{item.path(FileLocations::findAttachedInfo(item).foundTreePath).toString()} to print out the Tree
+of item, for example, as Tree's cannot be printed when outside the Dom.
+*/
+AttachedInfoLookupResult<FileLocations::Tree>
+FileLocations::findAttachedInfo(const DomItem &item)
+{
+ return AttachedInfoT<FileLocations>::findAttachedInfo(item, Fields::fileLocationsTree);
+}
+
+/*!
+ \internal
+ Returns the tree corresponding to a DomItem.
+ */
+FileLocations::Tree FileLocations::treeOf(const DomItem &item)
+{
+ return findAttachedInfo(item).foundTree;
+}
+
+/*!
+ \internal
+ Returns the filelocation Info corresponding to a DomItem.
+ */
+const FileLocations *FileLocations::fileLocationsOf(const DomItem &item)
+{
+ if (const FileLocations::Tree &t = treeOf(item))
+ return &(t->info());
+ return nullptr;
+}
+
+void FileLocations::updateFullLocation(const FileLocations::Tree &fLoc, SourceLocation loc)
+{
+ Q_ASSERT(fLoc);
+ if (loc != SourceLocation()) {
+ FileLocations::Tree p = fLoc;
+ while (p) {
+ SourceLocation &l = p->info().fullRegion;
+ if (loc.begin() < l.begin() || loc.end() > l.end()) {
+ l = combine(l, loc);
+ p->info().regions[MainRegion] = l;
+ } else {
+ break;
+ }
+ p = p->parent();
+ }
+ }
+}
+
+// Adding a new region to file location regions might break down qmlformat because
+// comments might be linked to new region undesirably. We might need to add an
+// exception to AstRangesVisitor::shouldSkipRegion when confronted those cases.
+void FileLocations::addRegion(const FileLocations::Tree &fLoc, FileLocationRegion region,
+ SourceLocation loc)
+{
+ Q_ASSERT(fLoc);
+ fLoc->info().regions[region] = loc;
+ updateFullLocation(fLoc, loc);
+}
+
+SourceLocation FileLocations::region(const FileLocations::Tree &fLoc, FileLocationRegion region)
+{
+ Q_ASSERT(fLoc);
+ const auto &regions = fLoc->info().regions;
+ if (auto it = regions.constFind(region); it != regions.constEnd() && it->isValid()) {
+ return *it;
+ }
+
+ if (region == MainRegion)
+ return fLoc->info().fullRegion;
+
+ return SourceLocation{};
+}
+
+/*!
+\internal
+\class QQmlJS::Dom::AttachedInfo
+\brief Attached info creates a tree to attach extra info to DomItems
+
+Normally one uses the template AttachedInfoT<SpecificInfoToAttach>
+
+static methods
+Attributes:
+\list
+\li parent: parent AttachedInfo in tree (might be empty)
+\li subItems: subItems of the tree (path -> AttachedInfo)
+\li infoItem: the attached information
+\endlist
+
+\sa QQmlJs::Dom::AttachedInfo
+*/
+
+bool AttachedInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ if (Ptr p = parent())
+ cont = cont && self.dvItemField(visitor, Fields::parent, [&self, p]() {
+ return self.copy(p, self.m_ownerPath.dropTail(2), p.get());
+ });
+ cont = cont
+ && self.dvValueLazyField(visitor, Fields::path, [this]() { return path().toString(); });
+ cont = cont && self.dvItemField(visitor, Fields::subItems, [this, &self]() {
+ return self.subMapItem(Map(
+ Path::Field(Fields::subItems),
+ [this](const DomItem &map, const QString &key) {
+ Path p = Path::fromString(key);
+ return map.copy(m_subItems.value(p), map.canonicalPath().key(key));
+ },
+ [this](const DomItem &) {
+ QSet<QString> res;
+ for (const auto &p : m_subItems.keys())
+ res.insert(p.toString());
+ return res;
+ },
+ QLatin1String("AttachedInfo")));
+ });
+ cont = cont && self.dvItemField(visitor, Fields::infoItem, [&self, this]() {
+ return infoItem(self);
+ });
+ return cont;
+}
+
+AttachedInfo::AttachedInfo(const AttachedInfo &o):
+ OwningItem(o),
+ m_parent(o.m_parent)
+{
+}
+
+/*!
+ \brief
+ Returns that the AttachedInfo corresponding to the given path, creating it if it does not exists.
+
+ The path might be either a relative path or a canonical path, as specified by the PathType
+*/
+AttachedInfo::Ptr AttachedInfo::ensure(
+ const AttachedInfo::Ptr &self, const Path &path, AttachedInfo::PathType pType){
+ Path relative;
+ switch (pType) {
+ case PathType::Canonical: {
+ if (!path)
+ return nullptr;
+ Q_ASSERT(self);
+ Path removed = path.mid(0, self->path().length());
+ Q_ASSERT(removed == self->path());
+ relative = path.mid(self->path().length());
+ } break;
+ case PathType::Relative:
+ Q_ASSERT(self);
+ relative = path;
+ break;
+ }
+ Ptr res = self;
+ for (const auto &p : std::as_const(relative)) {
+ if (AttachedInfo::Ptr subEl = res->m_subItems.value(p)) {
+ res = subEl;
+ } else {
+ AttachedInfo::Ptr newEl = res->instantiate(res, p);
+ res->m_subItems.insert(p, newEl);
+ res = newEl;
+ }
+ }
+ return res;
+}
+
+AttachedInfo::Ptr AttachedInfo::find(
+ const AttachedInfo::Ptr &self, const Path &p, AttachedInfo::PathType pType)
+{
+ Path rest;
+ if (pType == PathType::Canonical) {
+ if (!self) return nullptr;
+ Path removed = p.mid(0, self->path().length());
+ if (removed != self->path())
+ return nullptr;
+ rest = p.dropFront(self->path().length());
+ } else {
+ rest = p;
+ }
+
+ AttachedInfo::Ptr res = self;
+ while (rest) {
+ if (!res)
+ break;
+ res = res->m_subItems.value(rest.head());
+ rest = rest.dropFront();
+ }
+ return res;
+}
+
+AttachedInfoLookupResult<AttachedInfo::Ptr>
+AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName)
+{
+ Path p;
+ DomItem fLoc = item.field(fieldName);
+ if (!fLoc) {
+ // owner or container.owner should be a file, so this works, but we could simply use the
+ // canonical path, and PathType::Canonical instead...
+ DomItem o = item.owner();
+ p = item.pathFromOwner();
+ fLoc = o.field(fieldName);
+ while (!fLoc && o) {
+ DomItem c = o.container();
+ p = c.pathFromOwner().path(o.canonicalPath().last()).path(p);
+ o = c.owner();
+ fLoc = o.field(fieldName);
+ }
+ }
+ AttachedInfoLookupResult<AttachedInfo::Ptr> res;
+ res.lookupPath = p;
+ if (AttachedInfo::Ptr fLocPtr = fLoc.ownerAs<AttachedInfo>())
+ if (AttachedInfo::Ptr foundTree =
+ AttachedInfo::find(fLocPtr, p, AttachedInfo::PathType::Relative))
+ res.foundTree = foundTree;
+ res.rootTreePath = fLoc.canonicalPath();
+
+ res.foundTreePath = res.rootTreePath;
+ for (const Path &pEl : res.lookupPath)
+ res.foundTreePath = res.foundTreePath.field(Fields::subItems).key(pEl.toString());
+ return res;
+}
+
+bool UpdatedScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
+{
+ bool cont = true;
+ cont = cont && self.dvWrapField(visitor, Fields::expr, expr);
+ return cont;
+}
+
+UpdatedScriptExpression::Tree UpdatedScriptExpression::createTree(const Path &basePath)
+{
+ return AttachedInfoT<UpdatedScriptExpression>::createTree(basePath);
+}
+
+UpdatedScriptExpression::Tree UpdatedScriptExpression::ensure(
+ const UpdatedScriptExpression::Tree &base, const Path &basePath,
+ AttachedInfo::PathType pType)
+{
+ return AttachedInfoT<UpdatedScriptExpression>::ensure(base, basePath, pType);
+}
+
+AttachedInfoLookupResult<UpdatedScriptExpression::Tree>
+UpdatedScriptExpression::findAttachedInfo(const DomItem &item)
+{
+ return AttachedInfoT<UpdatedScriptExpression>::findAttachedInfo(
+ item, Fields::updatedScriptExpressions);
+}
+
+UpdatedScriptExpression::Tree UpdatedScriptExpression::treePtr(const DomItem &item)
+{
+ return AttachedInfoT<UpdatedScriptExpression>::treePtr(item, Fields::updatedScriptExpressions);
+}
+
+const UpdatedScriptExpression *UpdatedScriptExpression::exprPtr(const DomItem &item)
+{
+ if (UpdatedScriptExpression::Tree t = treePtr(item))
+ return &(t->info());
+ return nullptr;
+}
+
+bool UpdatedScriptExpression::visitTree(
+ const Tree &base, function_ref<bool(const Path &, const Tree &)> visitor,
+ const Path &basePath)
+{
+ return AttachedInfoT<UpdatedScriptExpression>::visitTree(base, visitor, basePath);
+}
+
+} // namespace Dom
+} // namespace QQmlJS
+QT_END_NAMESPACE
+
+#include "moc_qqmldomattachedinfo_p.cpp"