12#include <QtQml/private/qqmljsastvisitor_p.h>
13#include <QtQml/private/qqmljsast_p.h>
14#include <QtQml/private/qqmljslexer_p.h>
69 : rawComment(rawComment), commentLocation(loc)
105 QChar e1 = ((expectedEnd.isEmpty()) ? QChar::fromLatin1(0) : expectedEnd.at(0));
109 if (expectedEnd.size() > 1) {
122 }
else if (!
c.isSpace()) {
155 warnings.append(
tr(
"Non whitespace char %1 after comment end at %2")
203 cont = cont && self.dvValueField(visitor, Fields::rawComment,
rawComment());
204 cont = cont && self.dvValueField(visitor, Fields::newlinesBefore,
newlinesBefore());
216 lw.
write(cBody.mid(0, 1));
219 lw.
write(cBody.mid(1));
222 lw.
write(cInfo.postWhitespace());
248 cont = cont && self.dvWrapField(visitor, Fields::preComments, m_preComments);
249 cont = cont && self.dvWrapField(visitor, Fields::postComments, m_postComments);
256 locs->resize(m_preComments.size());
258 for (
const Comment &
c : m_preComments)
259 c.write(lw, (locs ? &((*locs)[
i++]) :
nullptr));
265 locs->resize(m_postComments.size());
267 for (
const Comment &
c : m_postComments)
268 c.write(lw, (locs ? &((*locs)[
i++]) :
nullptr));
290 operator bool()
const
324#if QT_VERSION >= QT_VERSION_CHECK(6, 6, 0)
364 QMap<quint32, ElementRef>
ends;
375 if (!itemLocations) {
377 qCWarning(commentsLog) <<
"reached item" <<
item.canonicalPath() <<
"without locations";
382 auto regs = itemLocations->info().regions;
388 if (!
starts.contains(startI))
390 if (!
ends.contains(endI))
391 ends.insert(endI, { currentP,
it.key(), endI - startI });
396 auto subMaps = itemLocations->subItems();
398 addItemRanges(
item.path(
it.key()),
399 std::static_pointer_cast<AttachedInfoT<FileLocations>>(
it.value()),
400 currentP.path(
it.key()));
405const QSet<int> AstRangesVisitor::kindsToSkip()
407 static QSet<int>
res = QSet<int>({
408 AST::Node::Kind_ArgumentList,
409 AST::Node::Kind_ElementList,
410 AST::Node::Kind_FormalParameterList,
411 AST::Node::Kind_ImportsList,
412 AST::Node::Kind_ExportsList,
413 AST::Node::Kind_PropertyDefinitionList,
414 AST::Node::Kind_StatementList,
415 AST::Node::Kind_VariableDeclarationList,
416 AST::Node::Kind_ClassElementList,
417 AST::Node::Kind_PatternElementList,
418 AST::Node::Kind_PatternPropertyList,
419 AST::Node::Kind_TypeArgument,
421 .unite(VisitAll::uiKinds());
430 switch (
item.internalKind()) {
431 case DomType::EnumDecl: {
432 return (region == FileLocationRegion::IdentifierRegion)
433 || (region == FileLocationRegion::EnumKeywordRegion);
435 case DomType::EnumItem: {
436 return (region == FileLocationRegion::IdentifierRegion)
437 || (region == FileLocationRegion::EnumValueRegion);
439 case DomType::QmlObject: {
440 return (region == FileLocationRegion::RightBraceRegion
441 || region == FileLocationRegion::LeftBraceRegion);
443 case DomType::Import:
444 case DomType::ImportScope:
445 return region == FileLocationRegion::IdentifierRegion;
449 Q_UNREACHABLE_RETURN(
false);
458 m_commentedElement{ commentedElement },
459 m_lastPostCommentPostEnd{ lastPostCommentPostEnd },
461 m_commentLocation { commentLocation },
462 m_startElement{ m_ranges.starts.lowerBound(commentLocation.
begin()) },
463 m_endElement{ m_ranges.ends.lowerBound(commentLocation.
end()) },
464 m_spaces{findSpacesAroundComment()}
470 if (m_spaces.preNewline < 1) {
471 checkElementBeforeComment();
472 checkElementAfterComment();
474 checkElementAfterComment();
475 checkElementBeforeComment();
477 if (!m_commentedElement)
478 checkElementInside();
483 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
484 return Comment{ m_code.mid(preSpacesIndex,
quint32(postSpacesIndex) - preSpacesIndex),
486 static_cast<int>(preNewlineCount),
506 [[nodiscard]] SpaceTrace findSpacesAroundComment()
const
508 quint32 iPre = m_commentLocation.begin();
513 QChar c = m_code.at(iPre - 1);
516 && iPre - 1 > 0 && m_code.at(iPre - 2) ==
QLatin1Char(
'/')) {
517 commentStartStr = m_code.
mid(iPre - 2, 2);
528 while (
i > 0 && m_code.at(--
i).isSpace()) {
541 qsizetype iPost = m_commentLocation.end();
542 while (iPost < m_code.size()) {
543 QChar c = m_code.at(iPost);
556 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) ==
QLatin1Char(
'\n')) {
561 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) ==
QLatin1Char(
'\n')) {
572 return {iPre, iPost, preNewline};
576 void checkElementBeforeComment()
578 if (m_commentedElement)
581 auto preEnd = m_endElement;
582 auto preStart = m_startElement;
583 if (preEnd != m_ranges.ends.begin()) {
585 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
593 while (
i != 0 && m_code.at(--
i).isSpace())
595 if (
i <= preEnd.key() ||
i < m_lastPostCommentPostEnd
596 || m_endElement == m_ranges.ends.end()) {
597 m_commentedElement = preEnd.value();
598 m_commentType = Comment::Post;
599 m_lastPostCommentPostEnd = m_spaces.iPost + 1;
606 void checkElementAfterComment()
608 if (m_commentedElement)
610 if (m_startElement != m_ranges.starts.end()) {
612 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
616 m_commentedElement = m_startElement.value();
620 if (m_startElement == m_ranges.starts.begin()) {
621 Q_ASSERT(m_startElement != m_ranges.starts.end());
623 m_commentedElement = m_startElement.value();
626 void checkElementInside()
628 if (m_commentedElement)
630 auto preStart = m_startElement;
631 if (m_startElement == m_ranges.starts.begin()) {
632 m_commentedElement = m_startElement.value();
641 ElementRef n1 = preStart.value();
642 ElementRef n2 = m_endElement.value();
643 if (n1.size > n2.size)
644 m_commentedElement = n2;
646 m_commentedElement = n1;
650 ElementRef &m_commentedElement;
651 quint32 &m_lastPostCommentPostEnd;
652 Comment::CommentType m_commentType = Comment::Pre;
653 const AstRangesVisitor &m_ranges;
656 using RangesIterator =
decltype(m_ranges.starts.begin());
657 const RangesIterator m_startElement;
658 const RangesIterator m_endElement;
672 for (
const auto &commentedElement : commentedElements().values()) {
673 pre.append(commentedElement.preComments());
674 post.append(commentedElement.postComments());
677 self.dvWrapField(visitor, Fields::preComments, pre);
679 self.dvWrapField(visitor, Fields::postComments,
post);
685 : m_rootItem{
std::move(
item) },
693 return collectComments(scriptPtr->engine(), scriptPtr->ast(), scriptPtr->astComments());
694 }
else if (std::shared_ptr<QmlFile> qmlFilePtr = m_rootItem.
ownerAs<
QmlFile>()) {
695 return collectComments(qmlFilePtr->engine(), qmlFilePtr->ast(), qmlFilePtr->astComments());
698 <<
"collectComments works with QmlFile and ScriptExpression, not with"
709 const std::shared_ptr<AstComments> &astComments)
717 quint32 lastPostCommentPostEnd = 0;
722 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
723 linker.linkCommentWithElement();
724 const auto comment = linker.createComment();
726 if (!elementToBeLinked) {
728 <<
"adding before root node";
729 if (m_rootItem && (m_fileLocations || !rootNode)) {
732 }
else if (rootNode) {
733 elementToBeLinked.element = rootNode;
738 if (
const auto *
const commentNode = std::get_if<AST::Node *>(&elementToBeLinked.element)) {
739 auto &commentedElement = astComments->commentedElements()[*commentNode];
740 commentedElement.addComment(comment);
741 }
else if (
const auto *
const regionRef = std::get_if<RegionRef>(&elementToBeLinked.element)) {
743 .
path(regionRef->path)
744 .
field(Fields::comments);
746 regionCommentsPtr->addComment(comment, regionRef->regionName);
748 Q_ASSERT(
false &&
"Cannot attach to region comments");
751 <<
"Failed: no item or node to attach comment" << comment.rawComment();
759 if (!m_regionComments.isEmpty()) {
761 && self.dvItemField(visitor, Fields::regionComments, [
this, &self]() ->
DomItem {
762 const Path pathFromOwner =
763 self.pathFromOwner().field(Fields::regionComments);
764 auto map = Map::fromFileRegionMap(pathFromOwner, m_regionComments);
765 return self.subMapItem(
map);
virtual SourceLocation firstSourceLocation() const =0
void accept(BaseVisitor *visitor)
virtual SourceLocation lastSourceLocation() const =0
@ Kind_UiObjectMemberList
@ Kind_UiObjectDefinition
@ Kind_UiVersionSpecifier
@ Kind_UiObjectInitializer
void addNodeRanges(AST::Node *rootNode)
bool preVisit(Node *n) override
QMap< quint32, ElementRef > ends
AstRangesVisitor()=default
void addItemRanges(const DomItem &item, const FileLocations::Tree &itemLocations, const Path ¤tP)
static const QSet< int > kindsToSkip()
static bool shouldSkipRegion(const DomItem &item, FileLocationRegion region)
returns true if comments should skip attaching to this region
void throwRecursionDepthError() override
QMap< quint32, ElementRef > starts
QString internalKindStr() const
DomItem field(QStringView name) const
DomItem path(const Path &p, const ErrorHandler &h=&defaultErrorHandler) const
ElementRef(AST::Node *node, quint32 size)
std::variant< AST::Node *, RegionRef > element
ElementRef(const Path &path, FileLocationRegion region, quint32 size)
Represents and maintains a mapping between elements and their location in a file.
static QQmlJS::SourceLocation region(const Tree &fLoc, FileLocationRegion region)
std::shared_ptr< AttachedInfoT< FileLocations > > Tree
void endSourceLocation(PendingSourceLocationId)
PendingSourceLocationId startSourceLocation(SourceLocation *)
std::shared_ptr< T > ownerAs() const
OutWriter & ensureNewline(int nNewlines=1)
OutWriter & ensureSpace()
OutWriter & write(QStringView v, LineWriter::TextAddType t=LineWriter::TextAddType::Normal)
A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,...
FileLocationRegion regionName
A vistor that visits all the AST:Node.
static QSet< int > uiKinds()
returns a set with all Ui* Nodes (i.e.
const_iterator cbegin() const noexcept
constexpr bool isEmpty() const noexcept
Returns whether this string view is empty - that is, whether {size() == 0}.
constexpr qsizetype size() const noexcept
Returns the size of this string view, in UTF-16 code units (that is, surrogate pairs count as two for...
constexpr QChar at(qsizetype n) const noexcept
Returns the character at position n in this string view.
constexpr QStringView mid(qsizetype pos, qsizetype n=-1) const noexcept
Returns the substring of length length starting at position start in this object.
\macro QT_RESTRICTED_CAST_FROM_ASCII
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
int PendingSourceLocationId
QCborValue sourceLocationToQCborValue(QQmlJS::SourceLocation loc)
Combined button and popup list for selecting options.
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
static bool contains(const QJsonArray &haystack, unsigned needle)
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLsizei const GLchar *const * path
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
#define QStringLiteral(str)
manager post(request, myJson, this, [this](QRestReply &reply) { if(!reply.isSuccess()) { } if(std::optional json=reply.readJson()) { } })
\inmodule QtCore \reentrant