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 });
551 if (!itemLocations) {
553 qCWarning(commentsLog) <<
"reached item" << item
.canonicalPath() <<
"without locations";
556 DomItem comments = item.field(Fields::comments);
558 auto regs = itemLocations->info().regions;
559 for (
auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
560 qsizetype startI = it.value().begin();
561 qsizetype endI = it.value().end();
564 if (!starts.contains(startI))
565 starts.insert(startI, { currentP, it.key(), endI - startI });
566 if (!ends.contains(endI))
567 ends.insert(endI, { currentP, it.key(), endI - startI });
572 auto subMaps = itemLocations->subItems();
573 for (
auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
579const QSet<
int> AstRangesVisitor::kindsToSkip()
581 static QSet<
int> res = QSet<
int>({
582 AST::Node::Kind_ArgumentList,
583 AST::Node::Kind_ElementList,
584 AST::Node::Kind_FormalParameterList,
585 AST::Node::Kind_ImportsList,
586 AST::Node::Kind_ExportsList,
587 AST::Node::Kind_PropertyDefinitionList,
588 AST::Node::Kind_StatementList,
589 AST::Node::Kind_VariableDeclarationList,
590 AST::Node::Kind_ClassElementList,
591 AST::Node::Kind_PatternElementList,
592 AST::Node::Kind_PatternPropertyList,
593 AST::Node::Kind_TypeArgument,
595 .unite(VisitAll::uiKinds());
617 CommentLinker(QStringView code,
ElementRef &commentedElement,
const AstRangesVisitor &ranges, qsizetype &lastPostCommentPostEnd,
618 const SourceLocation &commentLocation)
620 m_commentedElement{ commentedElement },
632 if (m_spaces.preNewline < 1) {
633 checkElementBeforeComment();
634 checkElementAfterComment();
636 checkElementAfterComment();
637 checkElementBeforeComment();
639 if (!m_commentedElement)
640 checkElementInside();
645 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
646 return Comment{ m_code.mid(preSpacesIndex, postSpacesIndex - preSpacesIndex),
647 m_commentLocation,
static_cast<
int>(preNewlineCount), m_commentType };
655 qsizetype preNewline;
659
660
661
662
663
664
665
666 [[nodiscard]] SpaceTrace findSpacesAroundComment()
const
668 qsizetype iPre = m_commentLocation.begin();
669 qsizetype preNewline = 0;
670 qsizetype postNewline = 0;
671 QStringView commentStartStr;
673 QChar c = m_code.at(iPre - 1);
675 if (commentStartStr.isEmpty() && (c == QLatin1Char(
'*') || c == QLatin1Char(
'/'))
676 && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char(
'/')) {
677 commentStartStr = m_code.mid(iPre - 2, 2);
682 }
else if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
685 qsizetype i = iPre - 1;
686 if (c == QLatin1Char(
'\n') && i > 0 && m_code.at(i - 1) == QLatin1Char(
'\r'))
688 while (i > 0 && m_code.at(--i).isSpace()) {
690 if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
701 qsizetype iPost = m_commentLocation.end();
702 while (iPost < m_code.size()) {
703 QChar c = m_code.at(iPost);
705 if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char(
'*')
706 && c == QLatin1Char(
'*') && iPost + 1 < m_code.size()
707 && m_code.at(iPost + 1) == QLatin1Char(
'/')) {
708 commentStartStr = QStringView();
714 if (c == QLatin1Char(
'\n')) {
716 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
720 }
else if (c == QLatin1Char(
'\r')) {
721 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
732 return {iPre, iPost, preNewline};
736 void checkElementBeforeComment()
738 if (m_commentedElement)
741 auto preEnd = m_endElement;
742 auto preStart = m_startElement;
743 if (preEnd != m_ranges.ends.begin()) {
745 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
752 qsizetype i = m_spaces.iPre;
753 while (i != 0 && m_code.at(--i).isSpace())
755 if (i <= preEnd.key() || i < m_lastPostCommentPostEnd
756 || m_endElement == m_ranges.ends.end()) {
757 m_commentedElement = preEnd.value();
759 m_lastPostCommentPostEnd = m_spaces.iPost + 1;
766 void checkElementAfterComment()
768 if (m_commentedElement)
770 if (m_startElement != m_ranges.starts.end()) {
772 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
776 m_commentedElement = m_startElement.value();
780 if (m_startElement == m_ranges.starts.begin()) {
781 Q_ASSERT(m_startElement != m_ranges.starts.end());
783 m_commentedElement = m_startElement.value();
786 void checkElementInside()
788 if (m_commentedElement)
790 auto preStart = m_startElement;
791 if (m_startElement == m_ranges.starts.begin()) {
792 m_commentedElement = m_startElement.value();
797 if (m_endElement == m_ranges.ends.end()) {
798 m_commentedElement = preStart.value();
807 if (n1.size > n2.size)
808 m_commentedElement = n2;
810 m_commentedElement = n1;
815 qsizetype &m_lastPostCommentPostEnd;
817 const AstRangesVisitor &m_ranges;
818 const SourceLocation &m_commentLocation;
821 const RangesIterator m_startElement;
822 const RangesIterator m_endElement;
868 const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
873 AstRangesVisitor ranges;
875 ranges.addNodeRanges(rootNode);
876 QStringView code = engine->code();
877 qsizetype lastPostCommentPostEnd = 0;
878 for (
const SourceLocation &commentLocation : engine->comments()) {
881 ElementRef elementToBeLinked;
882 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
883 linker.linkCommentWithElement();
884 const auto comment = linker.createComment();
886 if (!elementToBeLinked) {
887 qCWarning(commentsLog) <<
"Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
888 <<
"adding before root node";
889 if (m_rootItem && (m_fileLocations || !rootNode)) {
890 elementToBeLinked.element = RegionRef{ Path(), MainRegion };
891 elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
892 }
else if (rootNode) {
893 elementToBeLinked.element = NodeRef{ rootNode, CommentAnchor{} };
894 elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
898 if (
const auto *
const commentNode = std::get_if<NodeRef>(&elementToBeLinked.element)) {
899 astComments->addComment(commentNode->node, commentNode->commentAnchor, comment);
900 }
else if (
const auto *
const regionRef =
901 std::get_if<RegionRef>(&elementToBeLinked.element)) {
902 DomItem currentItem = m_rootItem.item().path(regionRef->path);
903 MutableDomItem regionComments = currentItem.field(Fields::comments);
904 if (
auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>()) {
905 const Path commentPath = regionCommentsPtr->addComment(comment, regionRef->regionName);
908 const auto base = FileLocations::treeOf(currentItem);
909 const auto fileLocations = FileLocations::ensure(
910 base, Path::fromField(Fields::comments).withPath(commentPath));
912 FileLocations::addRegion(fileLocations, MainRegion,
913 comment.info().sourceLocation());
915 Q_ASSERT(
false &&
"Cannot attach to region comments");
917 qCWarning(commentsLog)
918 <<
"Failed: no item or node to attach comment" << comment.rawComment();