68CommentInfo::CommentInfo(QStringView rawComment, QQmlJS::SourceLocation loc)
69 : rawComment(rawComment), commentLocation(loc)
72 while (commentBegin < quint32(rawComment.size()) && rawComment.at(commentBegin).isSpace()) {
73 if (rawComment.at(commentBegin) == QLatin1Char(
'\n'))
77 if (commentBegin < quint32(rawComment.size())) {
79 switch (rawComment.at(commentBegin).unicode()) {
81 commentStartStr = rawComment.mid(commentBegin, 2);
82 if (commentStartStr == u"/*") {
83 expectedEnd = QStringLiteral(u"*/");
85 if (commentStartStr == u"//") {
86 expectedEnd = QStringLiteral(u"\n");
88 warnings.append(tr(
"Unexpected comment start %1").arg(commentStartStr));
93 commentStartStr = rawComment.mid(commentBegin, 1);
94 expectedEnd = QStringLiteral(u"\n");
97 commentStartStr = rawComment.mid(commentBegin, 1);
98 warnings.append(tr(
"Unexpected comment start %1").arg(commentStartStr));
102 commentEnd = commentBegin + commentStartStr.size();
103 quint32 rawEnd = quint32(rawComment.size());
104 commentContentEnd = commentContentBegin = commentEnd;
105 QChar e1 = ((expectedEnd.isEmpty()) ? QChar::fromLatin1(0) : expectedEnd.at(0));
106 while (commentEnd < rawEnd) {
107 QChar c = rawComment.at(commentEnd);
109 if (expectedEnd.size() > 1) {
110 if (++commentEnd < rawEnd && rawComment.at(commentEnd) == expectedEnd.at(1)) {
111 Q_ASSERT(expectedEnd.size() == 2);
112 commentEndStr = rawComment.mid(++commentEnd - 2, 2);
115 commentContentEnd = commentEnd;
119 commentEndStr = rawComment.mid(commentEnd - 1, 1);
122 }
else if (!c.isSpace()) {
123 commentContentEnd = commentEnd;
124 }
else if (c == QLatin1Char(
'\n')) {
126 }
else if (c == QLatin1Char(
'\r')) {
127 if (expectedEnd == QStringLiteral(u"\n")) {
128 if (commentEnd + 1 < rawEnd
129 && rawComment.at(commentEnd + 1) == QLatin1Char(
'\n')) {
131 commentEndStr = rawComment.mid(++commentEnd - 2, 2);
133 commentEndStr = rawComment.mid(++commentEnd - 1, 1);
136 }
else if (commentEnd + 1 == rawEnd
137 || rawComment.at(commentEnd + 1) != QLatin1Char(
'\n')) {
145 && (rawComment.at(commentEnd - 1) == QLatin1Char(
'\n')
146 || rawComment.at(commentEnd - 1) == QLatin1Char(
'\r')))
148 quint32 i = commentEnd;
149 while (i < rawEnd && rawComment.at(i).isSpace()) {
150 if (rawComment.at(i) == QLatin1Char(
'\n') || rawComment.at(i) == QLatin1Char(
'\r'))
155 warnings.append(tr(
"Non whitespace char %1 after comment end at %2")
156 .arg(rawComment.at(i))
162 commentLocation.offset -= commentStartStr.size();
163 commentLocation.startColumn -= commentStartStr.size();
164 commentLocation.length = commentEnd - commentBegin;
326 static QSet<
int> res({ AST::Node::Kind_UiObjectMemberList, AST::Node::Kind_UiArrayMemberList,
327 AST::Node::Kind_UiParameterList, AST::Node::Kind_UiHeaderItemList,
328 AST::Node::Kind_UiEnumMemberList, AST::Node::Kind_UiAnnotationList,
330 AST::Node::Kind_UiArrayBinding, AST::Node::Kind_UiImport,
331 AST::Node::Kind_UiObjectBinding, AST::Node::Kind_UiObjectDefinition,
332 AST::Node::Kind_UiInlineComponent, AST::Node::Kind_UiObjectInitializer,
333#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
334 AST::Node::Kind_UiPragmaValueList,
336 AST::Node::Kind_UiPragma, AST::Node::Kind_UiProgram,
337 AST::Node::Kind_UiPublicMember, AST::Node::Kind_UiQualifiedId,
338 AST::Node::Kind_UiScriptBinding, AST::Node::Kind_UiSourceElement,
339 AST::Node::Kind_UiEnumDeclaration, AST::Node::Kind_UiVersionSpecifier,
340 AST::Node::Kind_UiRequired, AST::Node::Kind_UiAnnotation });
508 if (!itemLocations) {
510 qCWarning(commentsLog) <<
"reached item" << item
.canonicalPath() <<
"without locations";
513 DomItem comments = item.field(Fields::comments);
515 auto regs = itemLocations->info().regions;
516 for (
auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
517 qsizetype startI = it.value().begin();
518 qsizetype endI = it.value().end();
521 if (!starts.contains(startI))
522 starts.insert(startI, { currentP, it.key(), endI - startI });
523 if (!ends.contains(endI))
524 ends.insert(endI, { currentP, it.key(), endI - startI });
529 auto subMaps = itemLocations->subItems();
530 for (
auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
536const QSet<
int> AstRangesVisitor::kindsToSkip()
538 static QSet<
int> res = QSet<
int>({
539 AST::Node::Kind_ArgumentList,
540 AST::Node::Kind_ElementList,
541 AST::Node::Kind_FormalParameterList,
542 AST::Node::Kind_ImportsList,
543 AST::Node::Kind_ExportsList,
544 AST::Node::Kind_PropertyDefinitionList,
545 AST::Node::Kind_StatementList,
546 AST::Node::Kind_VariableDeclarationList,
547 AST::Node::Kind_ClassElementList,
548 AST::Node::Kind_PatternElementList,
549 AST::Node::Kind_PatternPropertyList,
550 AST::Node::Kind_TypeArgument,
552 .unite(VisitAll::uiKinds());
574 CommentLinker(QStringView code,
ElementRef &commentedElement,
const AstRangesVisitor &ranges, qsizetype &lastPostCommentPostEnd,
575 const SourceLocation &commentLocation)
577 m_commentedElement{ commentedElement },
589 if (m_spaces.preNewline < 1) {
590 checkElementBeforeComment();
591 checkElementAfterComment();
593 checkElementAfterComment();
594 checkElementBeforeComment();
596 if (!m_commentedElement)
597 checkElementInside();
602 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
603 return Comment{ m_code.mid(preSpacesIndex, postSpacesIndex - preSpacesIndex),
604 m_commentLocation,
static_cast<
int>(preNewlineCount), m_commentType };
612 qsizetype preNewline;
616
617
618
619
620
621
622
623 [[nodiscard]] SpaceTrace findSpacesAroundComment()
const
625 qsizetype iPre = m_commentLocation.begin();
626 qsizetype preNewline = 0;
627 qsizetype postNewline = 0;
628 QStringView commentStartStr;
630 QChar c = m_code.at(iPre - 1);
632 if (commentStartStr.isEmpty() && (c == QLatin1Char(
'*') || c == QLatin1Char(
'/'))
633 && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char(
'/')) {
634 commentStartStr = m_code.mid(iPre - 2, 2);
639 }
else if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
642 qsizetype i = iPre - 1;
643 if (c == QLatin1Char(
'\n') && i > 0 && m_code.at(i - 1) == QLatin1Char(
'\r'))
645 while (i > 0 && m_code.at(--i).isSpace()) {
647 if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
658 qsizetype iPost = m_commentLocation.end();
659 while (iPost < m_code.size()) {
660 QChar c = m_code.at(iPost);
662 if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char(
'*')
663 && c == QLatin1Char(
'*') && iPost + 1 < m_code.size()
664 && m_code.at(iPost + 1) == QLatin1Char(
'/')) {
665 commentStartStr = QStringView();
671 if (c == QLatin1Char(
'\n')) {
673 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
677 }
else if (c == QLatin1Char(
'\r')) {
678 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
689 return {iPre, iPost, preNewline};
693 void checkElementBeforeComment()
695 if (m_commentedElement)
698 auto preEnd = m_endElement;
699 auto preStart = m_startElement;
700 if (preEnd != m_ranges.ends.begin()) {
702 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
709 qsizetype i = m_spaces.iPre;
710 while (i != 0 && m_code.at(--i).isSpace())
712 if (i <= preEnd.key() || i < m_lastPostCommentPostEnd
713 || m_endElement == m_ranges.ends.end()) {
714 m_commentedElement = preEnd.value();
716 m_lastPostCommentPostEnd = m_spaces.iPost + 1;
723 void checkElementAfterComment()
725 if (m_commentedElement)
727 if (m_startElement != m_ranges.starts.end()) {
729 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
733 m_commentedElement = m_startElement.value();
737 if (m_startElement == m_ranges.starts.begin()) {
738 Q_ASSERT(m_startElement != m_ranges.starts.end());
740 m_commentedElement = m_startElement.value();
743 void checkElementInside()
745 if (m_commentedElement)
747 auto preStart = m_startElement;
748 if (m_startElement == m_ranges.starts.begin()) {
749 m_commentedElement = m_startElement.value();
754 if (m_endElement == m_ranges.ends.end()) {
755 m_commentedElement = preStart.value();
764 if (n1.size > n2.size)
765 m_commentedElement = n2;
767 m_commentedElement = n1;
772 qsizetype &m_lastPostCommentPostEnd;
774 const AstRangesVisitor &m_ranges;
775 const SourceLocation &m_commentLocation;
778 const RangesIterator m_startElement;
779 const RangesIterator m_endElement;
825 const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
830 AstRangesVisitor ranges;
832 ranges.addNodeRanges(rootNode);
833 QStringView code = engine->code();
834 qsizetype lastPostCommentPostEnd = 0;
835 for (
const SourceLocation &commentLocation : engine->comments()) {
838 ElementRef elementToBeLinked;
839 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
840 linker.linkCommentWithElement();
841 const auto comment = linker.createComment();
843 if (!elementToBeLinked) {
844 qCWarning(commentsLog) <<
"Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
845 <<
"adding before root node";
846 if (m_rootItem && (m_fileLocations || !rootNode)) {
847 elementToBeLinked.element = RegionRef{ Path(), MainRegion };
848 elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
849 }
else if (rootNode) {
850 elementToBeLinked.element = NodeRef{ rootNode, CommentAnchor{} };
851 elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
855 if (
const auto *
const commentNode = std::get_if<NodeRef>(&elementToBeLinked.element)) {
856 astComments->addComment(commentNode->node, commentNode->commentAnchor, comment);
857 }
else if (
const auto *
const regionRef =
858 std::get_if<RegionRef>(&elementToBeLinked.element)) {
859 DomItem currentItem = m_rootItem.item().path(regionRef->path);
860 MutableDomItem regionComments = currentItem.field(Fields::comments);
861 if (
auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>()) {
862 const Path commentPath = regionCommentsPtr->addComment(comment, regionRef->regionName);
865 const auto base = FileLocations::treeOf(currentItem);
866 const auto fileLocations = FileLocations::ensure(
867 base, Path::fromField(Fields::comments).withPath(commentPath));
869 FileLocations::addRegion(fileLocations, MainRegion,
870 comment.info().sourceLocation());
872 Q_ASSERT(
false &&
"Cannot attach to region comments");
874 qCWarning(commentsLog)
875 <<
"Failed: no item or node to attach comment" << comment.rawComment();