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

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