1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3#include "qqmldom_fwd_p.h"
4#include "qqmldomlinewriter_p.h"
5#include "qqmldomelements_p.h"
6#include "qqmldompath_p.h"
7
8QT_BEGIN_NAMESPACE
9namespace QQmlJS {
10namespace Dom {
11
12using namespace Qt::StringLiterals;
13
14
15/*!
16\internal
17\class QQmlJS::Dom::FileLocations
18\brief Represents and maintains a mapping between elements and their location in a file
19
20The location information is attached to the element it refers to via AttachedInfo
21There are static methods to simplify the handling of the tree of AttachedInfo.
22
23Attributes:
24\list
25\li fullRegion: A location guaranteed to include this element, its comments, and all its sub elements
26\li regions: a map with locations of regions of this element, the empty string is the default region
27 of this element
28\li preCommentLocations: locations of the comments before this element
29\li postCommentLocations: locations of the comments after this element
30\endlist
31
32\sa QQmlJs::Dom::AttachedInfo
33*/
34bool FileLocations::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
35{
36 bool cont = true;
37 cont = cont && self.dvValueLazyField(visitor, f: Fields::fullRegion, valueF: [this]() {
38 return sourceLocationToQCborValue(loc: fullRegion);
39 });
40 cont = cont && self.dvItemField(visitor, f: Fields::regions, it: [this, &self]() -> DomItem {
41 const Path pathFromOwner = self.pathFromOwner().field(name: Fields::regions);
42 auto map = Map::fromFileRegionMap(pathFromOwner, map: regions);
43 return self.subMapItem(map);
44 });
45 cont = cont
46 && self.dvItemField(visitor, f: Fields::preCommentLocations, it: [this, &self]() -> DomItem {
47 const Path pathFromOwner =
48 self.pathFromOwner().field(name: Fields::preCommentLocations);
49 auto map = Map::fromFileRegionListMap(pathFromOwner, map: preCommentLocations);
50 return self.subMapItem(map);
51 });
52 cont = cont
53 && self.dvItemField(visitor, f: Fields::postCommentLocations, it: [this, &self]() -> DomItem {
54 const Path pathFromOwner =
55 self.pathFromOwner().field(name: Fields::postCommentLocations);
56 auto map = Map::fromFileRegionListMap(pathFromOwner, map: postCommentLocations);
57 return self.subMapItem(map);
58 });
59 return cont;
60}
61
62FileLocations::Tree FileLocations::createTree(const Path &basePath){
63 return AttachedInfoT<FileLocations>::createTree(p: basePath);
64}
65
66FileLocations::Tree FileLocations::ensure(
67 const FileLocations::Tree &base, const Path &basePath, AttachedInfo::PathType pType)
68{
69 return AttachedInfoT<FileLocations>::ensure(self: base, path: basePath, pType);
70}
71
72/*!
73\internal
74Allows to query information about the FileLocations::Tree obtained from item, such as path of
75the Tree root in the Dom, the path of this item's Tree in the Dom, and so on.
76
77\note You can use \c{qDebug() << item.path(FileLocations::findAttachedInfo(item).foundTreePath)} or
78\c{item.path(FileLocations::findAttachedInfo(item).foundTreePath).toString()} to print out the Tree
79of item, for example, as Tree's cannot be printed when outside the Dom.
80*/
81AttachedInfoLookupResult<FileLocations::Tree>
82FileLocations::findAttachedInfo(const DomItem &item)
83{
84 return AttachedInfoT<FileLocations>::findAttachedInfo(item, fieldName: Fields::fileLocationsTree);
85}
86
87/*!
88 \internal
89 Returns the tree corresponding to a DomItem.
90 */
91FileLocations::Tree FileLocations::treeOf(const DomItem &item)
92{
93 return findAttachedInfo(item).foundTree;
94}
95
96/*!
97 \internal
98 Returns the filelocation Info corresponding to a DomItem.
99 */
100const FileLocations *FileLocations::fileLocationsOf(const DomItem &item)
101{
102 if (const FileLocations::Tree &t = treeOf(item))
103 return &(t->info());
104 return nullptr;
105}
106
107void FileLocations::updateFullLocation(const FileLocations::Tree &fLoc, SourceLocation loc)
108{
109 Q_ASSERT(fLoc);
110 if (loc != SourceLocation()) {
111 FileLocations::Tree p = fLoc;
112 while (p) {
113 SourceLocation &l = p->info().fullRegion;
114 if (loc.begin() < l.begin() || loc.end() > l.end()) {
115 l = combine(l1: l, l2: loc);
116 p->info().regions[MainRegion] = l;
117 } else {
118 break;
119 }
120 p = p->parent();
121 }
122 }
123}
124
125// Adding a new region to file location regions might break down qmlformat because
126// comments might be linked to new region undesirably. We might need to add an
127// exception to AstRangesVisitor::shouldSkipRegion when confronted those cases.
128void FileLocations::addRegion(const FileLocations::Tree &fLoc, FileLocationRegion region,
129 SourceLocation loc)
130{
131 Q_ASSERT(fLoc);
132 fLoc->info().regions[region] = loc;
133 updateFullLocation(fLoc, loc);
134}
135
136SourceLocation FileLocations::region(const FileLocations::Tree &fLoc, FileLocationRegion region)
137{
138 Q_ASSERT(fLoc);
139 const auto &regions = fLoc->info().regions;
140 if (auto it = regions.constFind(key: region); it != regions.constEnd() && it->isValid()) {
141 return *it;
142 }
143
144 if (region == MainRegion)
145 return fLoc->info().fullRegion;
146
147 return SourceLocation{};
148}
149
150/*!
151\internal
152\class QQmlJS::Dom::AttachedInfo
153\brief Attached info creates a tree to attach extra info to DomItems
154
155Normally one uses the template AttachedInfoT<SpecificInfoToAttach>
156
157static methods
158Attributes:
159\list
160\li parent: parent AttachedInfo in tree (might be empty)
161\li subItems: subItems of the tree (path -> AttachedInfo)
162\li infoItem: the attached information
163\endlist
164
165\sa QQmlJs::Dom::AttachedInfo
166*/
167
168bool AttachedInfo::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
169{
170 bool cont = true;
171 if (Ptr p = parent())
172 cont = cont && self.dvItemField(visitor, f: Fields::parent, it: [&self, p]() {
173 return self.copy(owner: p, ownerPath: self.m_ownerPath.dropTail(n: 2), base: p.get());
174 });
175 cont = cont
176 && self.dvValueLazyField(visitor, f: Fields::path, valueF: [this]() { return path().toString(); });
177 cont = cont && self.dvItemField(visitor, f: Fields::subItems, it: [this, &self]() {
178 return self.subMapItem(map: Map(
179 Path::Field(s: Fields::subItems),
180 [this](const DomItem &map, const QString &key) {
181 Path p = Path::fromString(s: key);
182 return map.copy(owner: m_subItems.value(key: p), ownerPath: map.canonicalPath().key(name: key));
183 },
184 [this](const DomItem &) {
185 QSet<QString> res;
186 for (const auto &p : m_subItems.keys())
187 res.insert(value: p.toString());
188 return res;
189 },
190 QLatin1String("AttachedInfo")));
191 });
192 cont = cont && self.dvItemField(visitor, f: Fields::infoItem, it: [&self, this]() {
193 return infoItem(self);
194 });
195 return cont;
196}
197
198AttachedInfo::AttachedInfo(const AttachedInfo &o):
199 OwningItem(o),
200 m_parent(o.m_parent)
201{
202}
203
204/*!
205 \brief
206 Returns that the AttachedInfo corresponding to the given path, creating it if it does not exists.
207
208 The path might be either a relative path or a canonical path, as specified by the PathType
209*/
210AttachedInfo::Ptr AttachedInfo::ensure(
211 const AttachedInfo::Ptr &self, const Path &path, AttachedInfo::PathType pType){
212 Path relative;
213 switch (pType) {
214 case PathType::Canonical: {
215 if (!path)
216 return nullptr;
217 Q_ASSERT(self);
218 Path removed = path.mid(offset: 0, length: self->path().length());
219 Q_ASSERT(removed == self->path());
220 relative = path.mid(offset: self->path().length());
221 } break;
222 case PathType::Relative:
223 Q_ASSERT(self);
224 relative = path;
225 break;
226 }
227 Ptr res = self;
228 for (const auto &p : std::as_const(t&: relative)) {
229 if (AttachedInfo::Ptr subEl = res->m_subItems.value(key: p)) {
230 res = subEl;
231 } else {
232 AttachedInfo::Ptr newEl = res->instantiate(parent: res, p);
233 res->m_subItems.insert(key: p, value: newEl);
234 res = newEl;
235 }
236 }
237 return res;
238}
239
240AttachedInfo::Ptr AttachedInfo::find(
241 const AttachedInfo::Ptr &self, const Path &p, AttachedInfo::PathType pType)
242{
243 Path rest;
244 if (pType == PathType::Canonical) {
245 if (!self) return nullptr;
246 Path removed = p.mid(offset: 0, length: self->path().length());
247 if (removed != self->path())
248 return nullptr;
249 rest = p.dropFront(n: self->path().length());
250 } else {
251 rest = p;
252 }
253
254 AttachedInfo::Ptr res = self;
255 while (rest) {
256 if (!res)
257 break;
258 res = res->m_subItems.value(key: rest.head());
259 rest = rest.dropFront();
260 }
261 return res;
262}
263
264AttachedInfoLookupResult<AttachedInfo::Ptr>
265AttachedInfo::findAttachedInfo(const DomItem &item, QStringView fieldName)
266{
267 Path p;
268 DomItem fLoc = item.field(name: fieldName);
269 if (!fLoc) {
270 // owner or container.owner should be a file, so this works, but we could simply use the
271 // canonical path, and PathType::Canonical instead...
272 DomItem o = item.owner();
273 p = item.pathFromOwner();
274 fLoc = o.field(name: fieldName);
275 while (!fLoc && o) {
276 DomItem c = o.container();
277 p = c.pathFromOwner().path(toAdd: o.canonicalPath().last()).path(toAdd: p);
278 o = c.owner();
279 fLoc = o.field(name: fieldName);
280 }
281 }
282 AttachedInfoLookupResult<AttachedInfo::Ptr> res;
283 res.lookupPath = p;
284 if (AttachedInfo::Ptr fLocPtr = fLoc.ownerAs<AttachedInfo>())
285 if (AttachedInfo::Ptr foundTree =
286 AttachedInfo::find(self: fLocPtr, p, pType: AttachedInfo::PathType::Relative))
287 res.foundTree = foundTree;
288 res.rootTreePath = fLoc.canonicalPath();
289
290 res.foundTreePath = res.rootTreePath;
291 for (const Path &pEl : res.lookupPath)
292 res.foundTreePath = res.foundTreePath.field(name: Fields::subItems).key(name: pEl.toString());
293 return res;
294}
295
296bool UpdatedScriptExpression::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
297{
298 bool cont = true;
299 cont = cont && self.dvWrapField(visitor, f: Fields::expr, obj: expr);
300 return cont;
301}
302
303UpdatedScriptExpression::Tree UpdatedScriptExpression::createTree(const Path &basePath)
304{
305 return AttachedInfoT<UpdatedScriptExpression>::createTree(p: basePath);
306}
307
308UpdatedScriptExpression::Tree UpdatedScriptExpression::ensure(
309 const UpdatedScriptExpression::Tree &base, const Path &basePath,
310 AttachedInfo::PathType pType)
311{
312 return AttachedInfoT<UpdatedScriptExpression>::ensure(self: base, path: basePath, pType);
313}
314
315AttachedInfoLookupResult<UpdatedScriptExpression::Tree>
316UpdatedScriptExpression::findAttachedInfo(const DomItem &item)
317{
318 return AttachedInfoT<UpdatedScriptExpression>::findAttachedInfo(
319 item, fieldName: Fields::updatedScriptExpressions);
320}
321
322UpdatedScriptExpression::Tree UpdatedScriptExpression::treePtr(const DomItem &item)
323{
324 return AttachedInfoT<UpdatedScriptExpression>::treePtr(item, fieldName: Fields::updatedScriptExpressions);
325}
326
327const UpdatedScriptExpression *UpdatedScriptExpression::exprPtr(const DomItem &item)
328{
329 if (UpdatedScriptExpression::Tree t = treePtr(item))
330 return &(t->info());
331 return nullptr;
332}
333
334bool UpdatedScriptExpression::visitTree(
335 const Tree &base, function_ref<bool(const Path &, const Tree &)> visitor,
336 const Path &basePath)
337{
338 return AttachedInfoT<UpdatedScriptExpression>::visitTree(base, visitor, basePath);
339}
340
341} // namespace Dom
342} // namespace QQmlJS
343QT_END_NAMESPACE
344
345#include "moc_qqmldomattachedinfo_p.cpp"
346

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qmldom/qqmldomattachedinfo.cpp