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 });
567 if (!itemLocations) {
569 qCWarning(commentsLog) <<
"reached item" << item
.canonicalPath() <<
"without locations";
572 DomItem comments = item.field(Fields::comments);
574 auto regs = itemLocations->info().regions;
575 for (
auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
576 qsizetype startI = it.value().begin();
577 qsizetype endI = it.value().end();
580 if (!starts.contains(startI))
581 starts.insert(startI, { currentP, it.key(), endI - startI });
582 if (!ends.contains(endI))
583 ends.insert(endI, { currentP, it.key(), endI - startI });
588 auto subMaps = itemLocations->subItems();
589 for (
auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
595const QSet<
int> AstRangesVisitor::kindsToSkip()
597 static QSet<
int> res = QSet<
int>({
598 AST::Node::Kind_ArgumentList,
599 AST::Node::Kind_ElementList,
600 AST::Node::Kind_FormalParameterList,
601 AST::Node::Kind_ImportsList,
602 AST::Node::Kind_ExportsList,
603 AST::Node::Kind_PropertyDefinitionList,
604 AST::Node::Kind_StatementList,
605 AST::Node::Kind_VariableDeclarationList,
606 AST::Node::Kind_ClassElementList,
607 AST::Node::Kind_PatternElementList,
608 AST::Node::Kind_PatternPropertyList,
609 AST::Node::Kind_TypeArgument,
611 .unite(VisitAll::uiKinds());
633 CommentLinker(QStringView code,
ElementRef &commentedElement,
const AstRangesVisitor &ranges, qsizetype &lastPostCommentPostEnd,
634 const SourceLocation &commentLocation)
636 m_commentedElement{ commentedElement },
648 if (m_spaces.preNewline < 1) {
649 checkElementBeforeComment();
650 checkElementAfterComment();
652 checkElementAfterComment();
653 checkElementBeforeComment();
655 if (!m_commentedElement)
656 checkElementInside();
661 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
662 return Comment{ m_code.mid(preSpacesIndex, postSpacesIndex - preSpacesIndex),
663 m_commentLocation,
static_cast<
int>(preNewlineCount), m_commentType };
671 qsizetype preNewline;
675
676
677
678
679
680
681
682 [[nodiscard]] SpaceTrace findSpacesAroundComment()
const
684 qsizetype iPre = m_commentLocation.begin();
685 qsizetype preNewline = 0;
686 qsizetype postNewline = 0;
687 QStringView commentStartStr;
689 QChar c = m_code.at(iPre - 1);
691 if (commentStartStr.isEmpty() && (c == QLatin1Char(
'*') || c == QLatin1Char(
'/'))
692 && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char(
'/')) {
693 commentStartStr = m_code.mid(iPre - 2, 2);
698 }
else if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
701 qsizetype i = iPre - 1;
702 if (c == QLatin1Char(
'\n') && i > 0 && m_code.at(i - 1) == QLatin1Char(
'\r'))
704 while (i > 0 && m_code.at(--i).isSpace()) {
706 if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
717 qsizetype iPost = m_commentLocation.end();
718 while (iPost < m_code.size()) {
719 QChar c = m_code.at(iPost);
721 if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char(
'*')
722 && c == QLatin1Char(
'*') && iPost + 1 < m_code.size()
723 && m_code.at(iPost + 1) == QLatin1Char(
'/')) {
724 commentStartStr = QStringView();
730 if (c == QLatin1Char(
'\n')) {
732 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
736 }
else if (c == QLatin1Char(
'\r')) {
737 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
748 return {iPre, iPost, preNewline};
752 void checkElementBeforeComment()
754 if (m_commentedElement)
757 auto preEnd = m_endElement;
758 auto preStart = m_startElement;
759 if (preEnd != m_ranges.ends.begin()) {
761 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
768 qsizetype i = m_spaces.iPre;
769 while (i != 0 && m_code.at(--i).isSpace())
771 if (i <= preEnd.key() || i < m_lastPostCommentPostEnd
772 || m_endElement == m_ranges.ends.end()) {
773 m_commentedElement = preEnd.value();
775 m_lastPostCommentPostEnd = m_spaces.iPost + 1;
782 void checkElementAfterComment()
784 if (m_commentedElement)
786 if (m_startElement != m_ranges.starts.end()) {
788 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
792 m_commentedElement = m_startElement.value();
796 if (m_startElement == m_ranges.starts.begin()) {
797 Q_ASSERT(m_startElement != m_ranges.starts.end());
799 m_commentedElement = m_startElement.value();
802 void checkElementInside()
804 if (m_commentedElement)
806 auto preStart = m_startElement;
807 if (m_startElement == m_ranges.starts.begin()) {
808 m_commentedElement = m_startElement.value();
813 if (m_endElement == m_ranges.ends.end()) {
814 m_commentedElement = preStart.value();
823 if (n1.size > n2.size)
824 m_commentedElement = n2;
826 m_commentedElement = n1;
831 qsizetype &m_lastPostCommentPostEnd;
833 const AstRangesVisitor &m_ranges;
834 const SourceLocation &m_commentLocation;
837 const RangesIterator m_startElement;
838 const RangesIterator m_endElement;
884 const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
889 AstRangesVisitor ranges;
891 ranges.addNodeRanges(rootNode);
892 QStringView code = engine->code();
893 qsizetype lastPostCommentPostEnd = 0;
894 for (
const SourceLocation &commentLocation : engine->comments()) {
897 ElementRef elementToBeLinked;
898 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
899 linker.linkCommentWithElement();
900 const auto comment = linker.createComment();
902 if (!elementToBeLinked) {
903 qCWarning(commentsLog) <<
"Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
904 <<
"adding before root node";
905 if (m_rootItem && (m_fileLocations || !rootNode)) {
906 elementToBeLinked.element = RegionRef{ Path(), MainRegion };
907 elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
908 }
else if (rootNode) {
909 elementToBeLinked.element = NodeRef{ rootNode, CommentAnchor{} };
910 elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
914 if (
const auto *
const commentNode = std::get_if<NodeRef>(&elementToBeLinked.element)) {
915 astComments->addComment(commentNode->node, commentNode->commentAnchor, comment);
916 }
else if (
const auto *
const regionRef =
917 std::get_if<RegionRef>(&elementToBeLinked.element)) {
918 DomItem currentItem = m_rootItem.item().path(regionRef->path);
919 MutableDomItem regionComments = currentItem.field(Fields::comments);
920 if (
auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>()) {
921 const Path commentPath = regionCommentsPtr->addComment(comment, regionRef->regionName);
924 const auto base = FileLocations::treeOf(currentItem);
925 const auto fileLocations = FileLocations::ensure(
926 base, Path::fromField(Fields::comments).withPath(commentPath));
928 FileLocations::addRegion(fileLocations, MainRegion,
929 comment.info().sourceLocation());
931 Q_ASSERT(
false &&
"Cannot attach to region comments");
933 qCWarning(commentsLog)
934 <<
"Failed: no item or node to attach comment" << comment.rawComment();