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;
325 static QSet<
int> res({ AST::Node::Kind_UiObjectMemberList, AST::Node::Kind_UiArrayMemberList,
326 AST::Node::Kind_UiParameterList, AST::Node::Kind_UiHeaderItemList,
327 AST::Node::Kind_UiEnumMemberList, AST::Node::Kind_UiAnnotationList,
329 AST::Node::Kind_UiArrayBinding, AST::Node::Kind_UiImport,
330 AST::Node::Kind_UiObjectBinding, AST::Node::Kind_UiObjectDefinition,
331 AST::Node::Kind_UiInlineComponent, AST::Node::Kind_UiObjectInitializer,
332#if QT_VERSION >= QT_VERSION_CHECK(6
, 6
, 0
)
333 AST::Node::Kind_UiPragmaValueList,
335 AST::Node::Kind_UiPragma, AST::Node::Kind_UiProgram,
336 AST::Node::Kind_UiPublicMember, AST::Node::Kind_UiQualifiedId,
337 AST::Node::Kind_UiScriptBinding, AST::Node::Kind_UiSourceElement,
338 AST::Node::Kind_UiEnumDeclaration, AST::Node::Kind_UiVersionSpecifier,
339 AST::Node::Kind_UiRequired, AST::Node::Kind_UiAnnotation });
507 if (!itemLocations) {
509 qCWarning(commentsLog) <<
"reached item" << item.canonicalPath() <<
"without locations";
512 DomItem comments = item.field(Fields::comments);
514 auto regs = itemLocations->info().regions;
515 for (
auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
516 qsizetype startI = it.value().begin();
517 qsizetype endI = it.value().end();
520 if (!starts.contains(startI))
521 starts.insert(startI, { currentP, it.key(), endI - startI });
522 if (!ends.contains(endI))
523 ends.insert(endI, { currentP, it.key(), endI - startI });
528 auto subMaps = itemLocations->subItems();
529 for (
auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
535const QSet<
int> AstRangesVisitor::kindsToSkip()
537 static QSet<
int> res = QSet<
int>({
538 AST::Node::Kind_ArgumentList,
539 AST::Node::Kind_ElementList,
540 AST::Node::Kind_FormalParameterList,
541 AST::Node::Kind_ImportsList,
542 AST::Node::Kind_ExportsList,
543 AST::Node::Kind_PropertyDefinitionList,
544 AST::Node::Kind_StatementList,
545 AST::Node::Kind_VariableDeclarationList,
546 AST::Node::Kind_ClassElementList,
547 AST::Node::Kind_PatternElementList,
548 AST::Node::Kind_PatternPropertyList,
549 AST::Node::Kind_TypeArgument,
551 .unite(VisitAll::uiKinds());
573 CommentLinker(QStringView code,
ElementRef &commentedElement,
const AstRangesVisitor &ranges, qsizetype &lastPostCommentPostEnd,
574 const SourceLocation &commentLocation)
576 m_commentedElement{ commentedElement },
588 if (m_spaces.preNewline < 1) {
589 checkElementBeforeComment();
590 checkElementAfterComment();
592 checkElementAfterComment();
593 checkElementBeforeComment();
595 if (!m_commentedElement)
596 checkElementInside();
601 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
602 return Comment{ m_code.mid(preSpacesIndex, postSpacesIndex - preSpacesIndex),
603 m_commentLocation,
static_cast<
int>(preNewlineCount), m_commentType };
611 qsizetype preNewline;
615
616
617
618
619
620
621
622 [[
nodiscard]] SpaceTrace findSpacesAroundComment()
const
624 qsizetype iPre = m_commentLocation.begin();
625 qsizetype preNewline = 0;
626 qsizetype postNewline = 0;
627 QStringView commentStartStr;
629 QChar c = m_code.at(iPre - 1);
631 if (commentStartStr.isEmpty() && (c == QLatin1Char(
'*') || c == QLatin1Char(
'/'))
632 && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char(
'/')) {
633 commentStartStr = m_code.mid(iPre - 2, 2);
638 }
else if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
641 qsizetype i = iPre - 1;
642 if (c == QLatin1Char(
'\n') && i > 0 && m_code.at(i - 1) == QLatin1Char(
'\r'))
644 while (i > 0 && m_code.at(--i).isSpace()) {
646 if (c == QLatin1Char(
'\n') || c == QLatin1Char(
'\r')) {
657 qsizetype iPost = m_commentLocation.end();
658 while (iPost < m_code.size()) {
659 QChar c = m_code.at(iPost);
661 if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char(
'*')
662 && c == QLatin1Char(
'*') && iPost + 1 < m_code.size()
663 && m_code.at(iPost + 1) == QLatin1Char(
'/')) {
664 commentStartStr = QStringView();
670 if (c == QLatin1Char(
'\n')) {
672 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
676 }
else if (c == QLatin1Char(
'\r')) {
677 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char(
'\n')) {
688 return {iPre, iPost, preNewline};
692 void checkElementBeforeComment()
694 if (m_commentedElement)
697 auto preEnd = m_endElement;
698 auto preStart = m_startElement;
699 if (preEnd != m_ranges.ends.begin()) {
701 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
708 qsizetype i = m_spaces.iPre;
709 while (i != 0 && m_code.at(--i).isSpace())
711 if (i <= preEnd.key() || i < m_lastPostCommentPostEnd
712 || m_endElement == m_ranges.ends.end()) {
713 m_commentedElement = preEnd.value();
715 m_lastPostCommentPostEnd = m_spaces.iPost + 1;
722 void checkElementAfterComment()
724 if (m_commentedElement)
726 if (m_startElement != m_ranges.starts.end()) {
728 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
732 m_commentedElement = m_startElement.value();
736 if (m_startElement == m_ranges.starts.begin()) {
737 Q_ASSERT(m_startElement != m_ranges.starts.end());
739 m_commentedElement = m_startElement.value();
742 void checkElementInside()
744 if (m_commentedElement)
746 auto preStart = m_startElement;
747 if (m_startElement == m_ranges.starts.begin()) {
748 m_commentedElement = m_startElement.value();
753 if (m_endElement == m_ranges.ends.end()) {
754 m_commentedElement = preStart.value();
763 if (n1.size > n2.size)
764 m_commentedElement = n2;
766 m_commentedElement = n1;
771 qsizetype &m_lastPostCommentPostEnd;
773 const AstRangesVisitor &m_ranges;
774 const SourceLocation &m_commentLocation;
777 const RangesIterator m_startElement;
778 const RangesIterator m_endElement;
824 const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
829 AstRangesVisitor ranges;
830 ranges.addItemRanges(m_rootItem.item(), m_fileLocations, Path());
831 ranges.addNodeRanges(rootNode);
832 QStringView code = engine->code();
833 qsizetype lastPostCommentPostEnd = 0;
834 for (
const SourceLocation &commentLocation : engine->comments()) {
837 ElementRef elementToBeLinked;
838 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
839 linker.linkCommentWithElement();
840 const auto comment = linker.createComment();
842 if (!elementToBeLinked) {
843 qCWarning(commentsLog) <<
"Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
844 <<
"adding before root node";
845 if (m_rootItem && (m_fileLocations || !rootNode)) {
846 elementToBeLinked.element = RegionRef{ Path(), MainRegion };
847 elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
848 }
else if (rootNode) {
849 elementToBeLinked.element = NodeRef{ rootNode, CommentAnchor{} };
850 elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
854 if (
const auto *
const commentNode = std::get_if<NodeRef>(&elementToBeLinked.element)) {
855 astComments->addComment(commentNode->node, commentNode->commentAnchor, comment);
856 }
else if (
const auto *
const regionRef =
857 std::get_if<RegionRef>(&elementToBeLinked.element)) {
858 DomItem currentItem = m_rootItem.item().path(regionRef->path);
859 MutableDomItem regionComments = currentItem.field(Fields::comments);
860 if (
auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>()) {
861 const Path commentPath = regionCommentsPtr->addComment(comment, regionRef->regionName);
864 const auto base = FileLocations::treeOf(currentItem);
865 const auto fileLocations = FileLocations::ensure(
866 base, Path::fromField(Fields::comments).withPath(commentPath));
868 FileLocations::addRegion(fileLocations, MainRegion,
869 comment.info().sourceLocation());
871 Q_ASSERT(
false &&
"Cannot attach to region comments");
873 qCWarning(commentsLog)
874 <<
"Failed: no item or node to attach comment" << comment.rawComment();