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
4#ifndef QQMLDOMCOMMENTS_P_H
5#define QQMLDOMCOMMENTS_P_H
6
7//
8// W A R N I N G
9// -------------
10//
11// This file is not part of the Qt API. It exists purely as an
12// implementation detail. This header file may change from version to
13// version without notice, or even be removed.
14//
15// We mean it.
16//
17
18#include "qqmldom_fwd_p.h"
19#include "qqmldomconstants_p.h"
20#include "qqmldomitem_p.h"
21#include "qqmldomfilelocations_p.h"
22
23#include <QtQml/private/qqmljsast_p.h>
24#include <QtQml/private/qqmljsengine_p.h>
25
26#include <QtCore/QMultiMap>
27#include <QtCore/QHash>
28#include <QtCore/QStack>
29#include <QtCore/QCoreApplication>
30
31#include <memory>
32
33QT_BEGIN_NAMESPACE
34namespace QQmlJS {
35namespace Dom {
36
37class QMLDOM_EXPORT CommentInfo
38{
39 Q_DECLARE_TR_FUNCTIONS(CommentInfo)
40public:
41 CommentInfo(QStringView, QQmlJS::SourceLocation loc);
42
43 QStringView preWhitespace() const { return rawComment.mid(pos: 0, n: commentBegin); }
44
45 QStringView comment() const { return rawComment.mid(pos: commentBegin, n: commentEnd - commentBegin); }
46
47 QStringView commentContent() const
48 {
49 return rawComment.mid(pos: commentContentBegin, n: commentContentEnd - commentContentEnd);
50 }
51
52 QStringView postWhitespace() const
53 {
54 return rawComment.mid(pos: commentEnd, n: rawComment.size() - commentEnd);
55 }
56
57 // Comment source location populated during lexing doesn't include start strings // or /*
58 // Returns the location starting from // or /*
59 QQmlJS::SourceLocation sourceLocation() const { return commentLocation; }
60
61 quint32 commentBegin = 0;
62 quint32 commentEnd = 0;
63 quint32 commentContentBegin = 0;
64 quint32 commentContentEnd = 0;
65 QStringView commentStartStr;
66 QStringView commentEndStr;
67 bool hasStartNewline = false;
68 bool hasEndNewline = false;
69 int nContentNewlines = 0;
70 QStringView rawComment;
71 QStringList warnings;
72 QQmlJS::SourceLocation commentLocation;
73};
74
75/*!
76\internal
77Anchor comments at an AST::Node* (DefaultLocation) or at an QQmlJS::SourceLocation inside of the
78AST::Node*. In the latter case, the member location takes the value of the anchor's
79QQmlJS::SourceLocation::offset, so that the formatter can differentiate between multiple anchors
80inside the same AST::Node*.
81*/
82struct CommentAnchor
83{
84 qsizetype location = -1;
85 static CommentAnchor from(const SourceLocation &sl) { return CommentAnchor{ .location: sl.begin() }; }
86 friend bool comparesEqual(const CommentAnchor &a, const CommentAnchor &b) noexcept
87 {
88 return a.location == b.location;
89 }
90 Q_DECLARE_EQUALITY_COMPARABLE(CommentAnchor)
91};
92
93inline size_t qHash(const CommentAnchor &key, size_t seed = 0) noexcept
94{
95 return qHashMulti(seed, args: key.location);
96}
97
98class QMLDOM_EXPORT Comment
99{
100public:
101 constexpr static DomType kindValue = DomType::Comment;
102 DomType kind() const { return kindValue; }
103
104 enum CommentType {Pre, Post};
105
106 Comment(const QString &c, const QQmlJS::SourceLocation &loc, int newlinesBefore = 1,
107 CommentType type = Pre)
108 : m_comment(c), m_location(loc), m_newlinesBefore(newlinesBefore), m_type(type)
109 {
110 }
111 Comment(QStringView c, const QQmlJS::SourceLocation &loc, int newlinesBefore = 1,
112 CommentType type = Pre)
113 : m_comment(c), m_location(loc), m_newlinesBefore(newlinesBefore), m_type(type)
114 {
115 }
116
117 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
118 int newlinesBefore() const { return m_newlinesBefore; }
119 void setNewlinesBefore(int n) { m_newlinesBefore = n; }
120 QStringView rawComment() const { return m_comment; }
121 CommentInfo info() const { return CommentInfo(m_comment, m_location); }
122 void write(OutWriter &lw) const;
123
124 CommentType type() const { return m_type; }
125
126 friend bool operator==(const Comment &c1, const Comment &c2)
127 {
128 return c1.m_newlinesBefore == c2.m_newlinesBefore && c1.m_comment == c2.m_comment;
129 }
130 friend bool operator!=(const Comment &c1, const Comment &c2) { return !(c1 == c2); }
131
132private:
133 QStringView m_comment;
134 QQmlJS::SourceLocation m_location;
135 int m_newlinesBefore;
136 CommentType m_type;
137};
138
139class QMLDOM_EXPORT CommentedElement
140{
141public:
142 constexpr static DomType kindValue = DomType::CommentedElement;
143 DomType kind() const { return kindValue; }
144
145 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
146 void writePre(OutWriter &lw) const;
147 void writePost(OutWriter &lw) const;
148
149 friend bool operator==(const CommentedElement &c1, const CommentedElement &c2)
150 {
151 return c1.m_preComments == c2.m_preComments && c1.m_postComments == c2.m_postComments;
152 }
153 friend bool operator!=(const CommentedElement &c1, const CommentedElement &c2)
154 {
155 return !(c1 == c2);
156 }
157
158 void addComment(const Comment &comment)
159 {
160 if (comment.type() == Comment::CommentType::Pre)
161 m_preComments.append(t: comment);
162 else
163 m_postComments.append(t: comment);
164 }
165
166 QList<Comment> preComments() const { return m_preComments; }
167 QList<Comment> postComments() const { return m_postComments; }
168
169private:
170 QList<Comment> m_preComments;
171 QList<Comment> m_postComments;
172};
173
174class QMLDOM_EXPORT RegionComments
175{
176public:
177 constexpr static DomType kindValue = DomType::RegionComments;
178 DomType kind() const { return kindValue; }
179
180 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const;
181
182 friend bool operator==(const RegionComments &c1, const RegionComments &c2)
183 {
184 return c1.m_regionComments == c2.m_regionComments;
185 }
186 friend bool operator!=(const RegionComments &c1, const RegionComments &c2)
187 {
188 return !(c1 == c2);
189 }
190
191 const QMap<FileLocationRegion, CommentedElement> &regionComments() const { return m_regionComments;}
192 Path addComment(const Comment &comment, FileLocationRegion region)
193 {
194 if (comment.type() == Comment::CommentType::Pre)
195 return addPreComment(comment, region);
196 else
197 return addPostComment(comment, region);
198 }
199
200private:
201 Path addPreComment(const Comment &comment, FileLocationRegion region)
202 {
203 const auto preList = m_regionComments[region].preComments();
204 index_type idx = preList.size();
205 m_regionComments[region].addComment(comment);
206 return Path::fromField(s: Fields::regionComments)
207 .withKey(name: fileLocationRegionName(region))
208 .withField(name: Fields::preComments)
209 .withIndex(i: idx);
210 }
211
212 Path addPostComment(const Comment &comment, FileLocationRegion region)
213 {
214 const auto postList = m_regionComments[region].postComments();
215 index_type idx = postList.size();
216 m_regionComments[region].addComment(comment);
217 return Path::fromField(s: Fields::regionComments)
218 .withKey(name: fileLocationRegionName(region))
219 .withField(name: Fields::postComments)
220 .withIndex(i: idx);
221 }
222
223 QMap<FileLocationRegion, CommentedElement> m_regionComments;
224};
225
226class QMLDOM_EXPORT AstComments final : public OwningItem
227{
228protected:
229 std::shared_ptr<OwningItem> doCopy(const DomItem &) const override
230 {
231 return std::make_shared<AstComments>(args: *this);
232 }
233
234 using CommentKey = std::pair<AST::Node *, CommentAnchor>;
235
236public:
237 constexpr static DomType kindValue = DomType::AstComments;
238 DomType kind() const override { return kindValue; }
239 bool iterateDirectSubpaths(const DomItem &self, DirectVisitor) const override;
240 std::shared_ptr<AstComments> makeCopy(const DomItem &self) const
241 {
242 return std::static_pointer_cast<AstComments>(r: doCopy(self));
243 }
244
245 Path canonicalPath(const DomItem &self) const override { return self.m_ownerPath; }
246 AstComments(const std::shared_ptr<Engine> &e) : m_engine(e) { }
247 AstComments(const AstComments &o)
248 : OwningItem(o), m_engine(o.m_engine), m_commentedElements(o.m_commentedElements)
249 {
250 }
251
252 const CommentedElement *commentForNode(AST::Node *n, CommentAnchor location) const
253 {
254 const auto it = m_commentedElements.constFind(key: CommentKey{ n, location });
255 return it == m_commentedElements.end() ? nullptr : &*it;
256 }
257
258 std::pair<QList<Comment>, QList<Comment>> collectPreAndPostComments() const
259 {
260 QList<Comment> pre;
261 QList<Comment> post;
262 for (const auto &element : m_commentedElements) {
263 pre.append(other: element.preComments());
264 post.append(other: element.postComments());
265 }
266 return std::make_pair(x&: pre, y&: post);
267 }
268
269 CommentedElement *ensureCommentForNode(AST::Node *n, CommentAnchor location)
270 {
271 const CommentKey key{ n, location };
272 return &m_commentedElements[key];
273 }
274
275 QMultiMap<quint32, const QList<Comment> *> allCommentsInNode(AST::Node *n);
276
277private:
278 std::shared_ptr<Engine> m_engine;
279 QHash<CommentKey, CommentedElement> m_commentedElements;
280};
281
282class CommentCollector
283{
284public:
285 CommentCollector() = default;
286 CommentCollector(MutableDomItem item);
287 void collectComments();
288 void collectComments(const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
289 const std::shared_ptr<AstComments> &astComments);
290
291private:
292 MutableDomItem m_rootItem;
293 FileLocations::Tree m_fileLocations;
294};
295
296class VisitAll : public AST::Visitor
297{
298public:
299 VisitAll() = default;
300
301 static QSet<int> uiKinds();
302
303 void throwRecursionDepthError() override { }
304
305 bool visit(AST::UiPublicMember *el) override
306 {
307 AST::Node::accept(node: el->annotations, visitor: this);
308 AST::Node::accept(node: el->memberType, visitor: this);
309 return true;
310 }
311
312 bool visit(AST::UiSourceElement *el) override
313 {
314 AST::Node::accept(node: el->annotations, visitor: this);
315 return true;
316 }
317
318 bool visit(AST::UiObjectDefinition *el) override
319 {
320 AST::Node::accept(node: el->annotations, visitor: this);
321 return true;
322 }
323
324 bool visit(AST::UiObjectBinding *el) override
325 {
326 AST::Node::accept(node: el->annotations, visitor: this);
327 return true;
328 }
329
330 bool visit(AST::UiScriptBinding *el) override
331 {
332 AST::Node::accept(node: el->annotations, visitor: this);
333 return true;
334 }
335
336 bool visit(AST::UiArrayBinding *el) override
337 {
338 AST::Node::accept(node: el->annotations, visitor: this);
339 return true;
340 }
341
342 bool visit(AST::UiParameterList *el) override
343 {
344 AST::Node::accept(node: el->type, visitor: this);
345 return true;
346 }
347
348 bool visit(AST::UiQualifiedId *el) override
349 {
350 AST::Node::accept(node: el->next, visitor: this);
351 return true;
352 }
353
354 bool visit(AST::UiEnumDeclaration *el) override
355 {
356 AST::Node::accept(node: el->annotations, visitor: this);
357 return true;
358 }
359
360 bool visit(AST::UiInlineComponent *el) override
361 {
362 AST::Node::accept(node: el->annotations, visitor: this);
363 return true;
364 }
365
366 void endVisit(AST::UiImport *el) override { AST::Node::accept(node: el->version, visitor: this); }
367 void endVisit(AST::UiPublicMember *el) override { AST::Node::accept(node: el->parameters, visitor: this); }
368
369 void endVisit(AST::UiParameterList *el) override
370 {
371 AST::Node::accept(node: el->next, visitor: this); // put other args at the same level as this one...
372 }
373
374 void endVisit(AST::UiEnumMemberList *el) override
375 {
376 AST::Node::accept(node: el->next,
377 visitor: this); // put other enum members at the same level as this one...
378 }
379
380 bool visit(AST::TemplateLiteral *el) override
381 {
382 AST::Node::accept(node: el->expression, visitor: this);
383 return true;
384 }
385
386 void endVisit(AST::Elision *el) override
387 {
388 AST::Node::accept(node: el->next, visitor: this); // emit other elisions at the same level
389 }
390};
391} // namespace Dom
392} // namespace QQmlJS
393QT_END_NAMESPACE
394
395#endif // QQMLDOMCOMMENTS_P_H
396

source code of qtdeclarative/src/qmldom/qqmldomcomments_p.h