Revision control
Copy as Markdown
// 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 "qqmldompath_p.h"
#include "qqmldomfilelocations_p.h"
QT_BEGIN_NAMESPACE
namespace QQmlJS {
namespace Dom {
using namespace Qt::StringLiterals;
/*!
\internal
\namespace QQmlJS::Dom::FileLocations
\brief Provides entities to maintain mappings between elements and their location in a file
The location information is associated with the element it refers to via ::Node
There are free functions to simplify the handling of the FileLocations tree.
*/
namespace FileLocations {
/*!
\internal
\namespace QQmlJS::Dom::FileLocations::Info
\brief Contains region information about the item
Attributes:
\list
\li fullRegion: A location guaranteed to include this element 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
\endlist
*/
bool Info::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::fullRegion, [this]() {
return sourceLocationToQCborValue(fullRegion);
});
cont = cont && visitor(PathEls::Field(Fields::regions), [this, &self]() -> DomItem {
const Path pathFromOwner = self.pathFromOwner().withField(Fields::regions);
auto map = Map::fromFileRegionMap(pathFromOwner, regions);
return self.subMapItem(map);
});
return cont;
}
Tree createTree(const Path &basePath)
{
return Node::instantiate(nullptr, basePath);
}
Tree ensure(const Tree &base, const Path &basePath)
{
Path relative = basePath;
Tree res = base;
for (const auto &p : std::as_const(relative)) {
res = res->insertOrReturnChildAt(p);
}
return res;
}
Tree find(const Tree &self, const Path &p)
{
Path rest = p;
Tree res = self;
while (rest) {
if (!res)
break;
res = res->subItems().value(rest.head());
rest = rest.dropFront();
}
return res;
}
bool visitTree(const Tree &base, function_ref<bool(const Path &, const Tree &)> visitor,
const Path &basePath)
{
if (base) {
Path pNow = basePath.withPath(base->path());
if (!visitor(pNow, base)) {
return false;
}
for (const auto &childNode : base->subItems()) {
if (!visitTree(childNode, visitor, pNow))
return false;
}
}
return true;
}
QString canonicalPathForTesting(const Tree &base)
{
QString result;
for (auto *it = base.get(); it; it = it->parent().get()) {
result.prepend(it->path().toString());
}
return result;
}
/*!
\internal
Returns the tree corresponding to a DomItem.
*/
Tree treeOf(const DomItem &item)
{
Path p;
DomItem fLoc = item.field(Fields::fileLocationsTree);
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(Fields::fileLocationsTree);
while (!fLoc && o) {
DomItem c = o.container();
p = c.pathFromOwner().withPath(o.canonicalPath().last()).withPath(p);
o = c.owner();
fLoc = o.field(Fields::fileLocationsTree);
}
}
if (Node::Ptr fLocPtr = fLoc.ownerAs<Node>())
return find(fLocPtr, p);
return {};
}
static void updateFullLocation(const Tree &fLoc, SourceLocation loc)
{
Q_ASSERT(fLoc);
if (loc != SourceLocation()) {
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.
// beware of shrinking and reassigning regions (QTBUG-131288)
void addRegion(const Tree &fLoc, FileLocationRegion region, SourceLocation loc)
{
Q_ASSERT(fLoc);
fLoc->info().regions[region] = loc;
updateFullLocation(fLoc, loc);
}
SourceLocation region(const Tree &fLoc, FileLocationRegion region)
{
Q_ASSERT(fLoc);
const auto ®ions = 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::FileLocations::Node
\brief Represents a Node of FileLocations tree
Attributes:
\list
\li parent: parent Node in tree (might be empty)
\li subItems: subItems of the tree (path -> Node)
\li infoItem: actual FileLocations::Info with regions
\endlist
\sa QQmlJs::Dom::Node
*/
Node::Ptr Node::instantiate(const Ptr &parent, const Path &p)
{
return std::shared_ptr<Node>(new Node(parent, p));
}
bool Node::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
{
bool cont = true;
if (const Ptr p = parent()) {
cont = cont && visitor(PathEls::Field(Fields::parent), [&self, &p]() {
return self.copy(p, self.m_ownerPath.dropTail(2), p.get());
});
}
cont = cont && self.invokeVisitorOnLazyField(visitor, Fields::path, [this]() {
return path().toString();
});
cont = cont && visitor(PathEls::Field(Fields::subItems), [this, &self]() {
return self.subMapItem(Map(
Path::fromField(Fields::subItems),
[this](const DomItem &map, const QString &key) {
Path p = Path::fromString(key);
return map.copy(m_subItems.value(p), map.canonicalPath().withKey(key));
},
[this](const DomItem &) {
QSet<QString> res;
for (const auto &p : m_subItems.keys())
res.insert(p.toString());
return res;
},
QLatin1String("Node")));
});
cont = cont && visitor(PathEls::Field(Fields::infoItem), [&self, this]() {
return self.wrap(PathEls::Field(Fields::infoItem), m_info);
});
return cont;
}
Node::Ptr Node::insertOrReturnChildAt(const Path &path)
{
if (Node::Ptr subEl = m_subItems.value(path)) {
return subEl;
}
return m_subItems.insert(path, Node::instantiate(shared_from_this(), path)).value();
}
std::shared_ptr<OwningItem> Node::doCopy(const DomItem &) const
{
return std::shared_ptr<Node>(new Node(*this));
}
} // namespace FileLocations
} // namespace Dom
} // namespace QQmlJS
QT_END_NAMESPACE