Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qqmldomcomments.cpp
Go to the documentation of this file.
1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomcomments_p.h"
7#include "qqmldomelements_p.h"
11
12#include <QtQml/private/qqmljsastvisitor_p.h>
13#include <QtQml/private/qqmljsast_p.h>
14#include <QtQml/private/qqmljslexer_p.h>
15
16#include <QtCore/QSet>
17
18#include <variant>
19
20static Q_LOGGING_CATEGORY(commentsLog, "qt.qmldom.comments", QtWarningMsg);
21
23namespace QQmlJS {
24namespace Dom {
25
69 : rawComment(rawComment), commentLocation(loc)
70{
71 commentBegin = 0;
72 while (commentBegin < quint32(rawComment.size()) && rawComment.at(commentBegin).isSpace()) {
74 hasStartNewline = true;
76 }
78 QString expectedEnd;
79 switch (rawComment.at(commentBegin).unicode()) {
80 case '/':
82 if (commentStartStr == u"/*") {
83 expectedEnd = QStringLiteral(u"*/");
84 } else {
85 if (commentStartStr == u"//") {
86 expectedEnd = QStringLiteral(u"\n");
87 } else {
88 warnings.append(tr("Unexpected comment start %1").arg(commentStartStr));
89 }
90 }
91 break;
92 case '#':
94 expectedEnd = QStringLiteral(u"\n");
95 break;
96 default:
98 warnings.append(tr("Unexpected comment start %1").arg(commentStartStr));
99 break;
100 }
101
103 quint32 rawEnd = quint32(rawComment.size());
105 QChar e1 = ((expectedEnd.isEmpty()) ? QChar::fromLatin1(0) : expectedEnd.at(0));
106 while (commentEnd < rawEnd) {
108 if (c == e1) {
109 if (expectedEnd.size() > 1) {
110 if (++commentEnd < rawEnd && rawComment.at(commentEnd) == expectedEnd.at(1)) {
111 Q_ASSERT(expectedEnd.size() == 2);
113 break;
114 } else {
116 }
117 } else {
118 // Comment ends with \n, treat as it is not part of the comment but post whitespace
120 break;
121 }
122 } else if (!c.isSpace()) {
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')) {
130 ++commentEnd;
132 } else {
134 }
135 break;
136 } else if (commentEnd + 1 == rawEnd
137 || rawComment.at(commentEnd + 1) != QLatin1Char('\n')) {
139 }
140 }
141 ++commentEnd;
142 }
143
144 if (commentEnd > 0
145 && (rawComment.at(commentEnd - 1) == QLatin1Char('\n')
146 || rawComment.at(commentEnd - 1) == QLatin1Char('\r')))
147 hasEndNewline = true;
149 while (i < rawEnd && rawComment.at(i).isSpace()) {
150 if (rawComment.at(i) == QLatin1Char('\n') || rawComment.at(i) == QLatin1Char('\r'))
151 hasEndNewline = true;
152 ++i;
153 }
154 if (i < rawEnd) {
155 warnings.append(tr("Non whitespace char %1 after comment end at %2")
156 .arg(rawComment.at(i))
157 .arg(i));
158 }
159 }
160
161 // Post process comment source location
165}
166
201{
202 bool cont = true;
203 cont = cont && self.dvValueField(visitor, Fields::rawComment, rawComment());
204 cont = cont && self.dvValueField(visitor, Fields::newlinesBefore, newlinesBefore());
205 return cont;
206}
207
208void Comment::write(OutWriter &lw, SourceLocation *commentLocation) const
209{
210 if (newlinesBefore())
212 CommentInfo cInfo = info();
213 lw.ensureSpace(cInfo.preWhitespace());
214 QStringView cBody = cInfo.comment();
215 PendingSourceLocationId cLoc = lw.lineWriter.startSourceLocation(commentLocation);
216 lw.write(cBody.mid(0, 1));
217 bool indentOn = lw.indentNextlines;
218 lw.indentNextlines = false;
219 lw.write(cBody.mid(1));
220 lw.indentNextlines = indentOn;
222 lw.write(cInfo.postWhitespace());
223}
224
246{
247 bool cont = true;
248 cont = cont && self.dvWrapField(visitor, Fields::preComments, m_preComments);
249 cont = cont && self.dvWrapField(visitor, Fields::postComments, m_postComments);
250 return cont;
251}
252
253void CommentedElement::writePre(OutWriter &lw, QList<SourceLocation> *locs) const
254{
255 if (locs)
256 locs->resize(m_preComments.size());
257 int i = 0;
258 for (const Comment &c : m_preComments)
259 c.write(lw, (locs ? &((*locs)[i++]) : nullptr));
260}
261
262void CommentedElement::writePost(OutWriter &lw, QList<SourceLocation> *locs) const
263{
264 if (locs)
265 locs->resize(m_postComments.size());
266 int i = 0;
267 for (const Comment &c : m_postComments)
268 c.write(lw, (locs ? &((*locs)[i++]) : nullptr));
269}
270
271using namespace QQmlJS::AST;
272
274{
275public:
276 Path path; // store the MutableDomItem instead?
278};
279
280// internal class to keep a reference either to an AST::Node* or a region of a DomItem and the
281// size of that region
283{
284public:
287 : element(RegionRef{ path, region }), size(size)
288 {
289 }
290 operator bool() const
291 {
292 return (element.index() == 0 && std::get<0>(element)) || element.index() == 1 || size != 0;
293 }
294 ElementRef() = default;
295
296 std::variant<AST::Node *, RegionRef> element;
298};
299
334
335// internal private class to set all the starts/ends of the nodes/regions
336class AstRangesVisitor final : protected VisitAll
337{
338public:
339 AstRangesVisitor() = default;
340
341 void addNodeRanges(AST::Node *rootNode);
342 void addItemRanges(
343 const DomItem &item, const FileLocations::Tree &itemLocations, const Path &currentP);
344
345 void throwRecursionDepthError() override { }
346
347 static const QSet<int> kindsToSkip();
348 static bool shouldSkipRegion(const DomItem &item, FileLocationRegion region);
349
350 bool preVisit(Node *n) override
351 {
352 if (!kindsToSkip().contains(n->kind)) {
353 quint32 start = n->firstSourceLocation().begin();
354 quint32 end = n->lastSourceLocation().end();
355 if (!starts.contains(start))
356 starts.insert(start, { n, end - start });
357 if (!ends.contains(end))
358 ends.insert(end, { n, end - start });
359 }
360 return true;
361 }
362
363 QMap<quint32, ElementRef> starts;
364 QMap<quint32, ElementRef> ends;
365};
366
368{
369 AST::Node::accept(rootNode, this);
370}
371
373 const DomItem &item, const FileLocations::Tree &itemLocations, const Path &currentP)
374{
375 if (!itemLocations) {
376 if (item)
377 qCWarning(commentsLog) << "reached item" << item.canonicalPath() << "without locations";
378 return;
379 }
380 DomItem comments = item.field(Fields::comments);
381 if (comments) {
382 auto regs = itemLocations->info().regions;
383 for (auto it = regs.cbegin(), end = regs.cend(); it != end; ++it) {
384 quint32 startI = it.value().begin();
385 quint32 endI = it.value().end();
386
387 if (!shouldSkipRegion(item, it.key())) {
388 if (!starts.contains(startI))
389 starts.insert(startI, { currentP, it.key(), quint32(endI - startI) });
390 if (!ends.contains(endI))
391 ends.insert(endI, { currentP, it.key(), endI - startI });
392 }
393 }
394 }
395 {
396 auto subMaps = itemLocations->subItems();
397 for (auto it = subMaps.begin(), end = subMaps.end(); it != end; ++it) {
398 addItemRanges(item.path(it.key()),
399 std::static_pointer_cast<AttachedInfoT<FileLocations>>(it.value()),
400 currentP.path(it.key()));
401 }
402 }
403}
404
405const QSet<int> AstRangesVisitor::kindsToSkip()
406{
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,
420 })
421 .unite(VisitAll::uiKinds());
422 return res;
423}
424
428bool AstRangesVisitor::shouldSkipRegion(const DomItem &item, FileLocationRegion region)
429{
430 switch (item.internalKind()) {
431 case DomType::EnumDecl: {
432 return (region == FileLocationRegion::IdentifierRegion)
433 || (region == FileLocationRegion::EnumKeywordRegion);
434 }
435 case DomType::EnumItem: {
436 return (region == FileLocationRegion::IdentifierRegion)
437 || (region == FileLocationRegion::EnumValueRegion);
438 }
439 case DomType::QmlObject: {
440 return (region == FileLocationRegion::RightBraceRegion
441 || region == FileLocationRegion::LeftBraceRegion);
442 }
443 case DomType::Import:
444 case DomType::ImportScope:
445 return region == FileLocationRegion::IdentifierRegion;
446 default:
447 return false;
448 }
449 Q_UNREACHABLE_RETURN(false);
450}
451
453{
454public:
455 CommentLinker(QStringView code, ElementRef &commentedElement, const AstRangesVisitor &ranges, quint32 &lastPostCommentPostEnd,
456 const SourceLocation &commentLocation)
457 : m_code{ code },
458 m_commentedElement{ commentedElement },
459 m_lastPostCommentPostEnd{ lastPostCommentPostEnd },
460 m_ranges{ ranges },
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()}
465 {
466 }
467
469 {
470 if (m_spaces.preNewline < 1) {
471 checkElementBeforeComment();
472 checkElementAfterComment();
473 } else {
474 checkElementAfterComment();
475 checkElementBeforeComment();
476 }
477 if (!m_commentedElement)
478 checkElementInside();
479 }
480
481 [[nodiscard]] Comment createComment() const
482 {
483 const auto [preSpacesIndex, postSpacesIndex, preNewlineCount] = m_spaces;
484 return Comment{ m_code.mid(preSpacesIndex, quint32(postSpacesIndex) - preSpacesIndex),
485 m_commentLocation,
486 static_cast<int>(preNewlineCount),
487 m_commentType};
488 }
489
490private:
491 struct SpaceTrace
492 {
493 quint32 iPre;
494 qsizetype iPost;
495 int preNewline;
496 };
497
506 [[nodiscard]] SpaceTrace findSpacesAroundComment() const
507 {
508 quint32 iPre = m_commentLocation.begin();
509 int preNewline = 0;
510 int postNewline = 0;
511 QStringView commentStartStr;
512 while (iPre > 0) {
513 QChar c = m_code.at(iPre - 1);
514 if (!c.isSpace()) {
515 if (commentStartStr.isEmpty() && (c == QLatin1Char('*') || c == QLatin1Char('/'))
516 && iPre - 1 > 0 && m_code.at(iPre - 2) == QLatin1Char('/')) {
517 commentStartStr = m_code.mid(iPre - 2, 2);
518 --iPre;
519 } else {
520 break;
521 }
522 } else if (c == QLatin1Char('\n') || c == QLatin1Char('\r')) {
523 preNewline = 1;
524 // possibly add an empty line if it was there (but never more than one)
525 int i = iPre - 1;
526 if (c == QLatin1Char('\n') && i > 0 && m_code.at(i - 1) == QLatin1Char('\r'))
527 --i;
528 while (i > 0 && m_code.at(--i).isSpace()) {
529 c = m_code.at(i);
530 if (c == QLatin1Char('\n') || c == QLatin1Char('\r')) {
531 ++preNewline;
532 break;
533 }
534 }
535 break;
536 }
537 --iPre;
538 }
539 if (iPre == 0)
540 preNewline = 1;
541 qsizetype iPost = m_commentLocation.end();
542 while (iPost < m_code.size()) {
543 QChar c = m_code.at(iPost);
544 if (!c.isSpace()) {
545 if (!commentStartStr.isEmpty() && commentStartStr.at(1) == QLatin1Char('*')
546 && c == QLatin1Char('*') && iPost + 1 < m_code.size()
547 && m_code.at(iPost + 1) == QLatin1Char('/')) {
548 commentStartStr = QStringView();
549 ++iPost;
550 } else {
551 break;
552 }
553 } else {
554 if (c == QLatin1Char('\n')) {
555 ++postNewline;
556 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char('\n')) {
557 ++iPost;
558 ++postNewline;
559 }
560 } else if (c == QLatin1Char('\r')) {
561 if (iPost + 1 < m_code.size() && m_code.at(iPost + 1) == QLatin1Char('\n')) {
562 ++iPost;
563 ++postNewline;
564 }
565 }
566 }
567 ++iPost;
568 if (postNewline > 1)
569 break;
570 }
571
572 return {iPre, iPost, preNewline};
573 }
574
575 // tries to associate comment as a postComment to currentElement
576 void checkElementBeforeComment()
577 {
578 if (m_commentedElement)
579 return;
580 // prefer post comment attached to preceding element
581 auto preEnd = m_endElement;
582 auto preStart = m_startElement;
583 if (preEnd != m_ranges.ends.begin()) {
584 --preEnd;
585 if (m_startElement == m_ranges.starts.begin() || (--preStart).key() < preEnd.key()) {
586 // iStart == begin should never happen
587 // check that we do not have operators (or in general other things) between
588 // preEnd and this because inserting a newline too ealy might invalidate the
589 // expression (think a + //comment\n b ==> a // comment\n + b), in this
590 // case attaching as preComment of iStart (b in the example) should be
591 // preferred as it is safe
592 quint32 i = m_spaces.iPre;
593 while (i != 0 && m_code.at(--i).isSpace())
594 ;
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; // ensure the previous check works
600 // with multiple post comments
601 }
602 }
603 }
604 }
605 // tries to associate comment as a preComment to currentElement
606 void checkElementAfterComment()
607 {
608 if (m_commentedElement)
609 return;
610 if (m_startElement != m_ranges.starts.end()) {
611 // try to add a pre comment of following element
612 if (m_endElement == m_ranges.ends.end() || m_endElement.key() > m_startElement.key()) {
613 // there is no end of element before iStart begins
614 // associate the comment as preComment of iStart
615 // (btw iEnd == end should never happen here)
616 m_commentedElement = m_startElement.value();
617 return;
618 }
619 }
620 if (m_startElement == m_ranges.starts.begin()) {
621 Q_ASSERT(m_startElement != m_ranges.starts.end());
622 // we are before the first node (should be handled already by previous case)
623 m_commentedElement = m_startElement.value();
624 }
625 }
626 void checkElementInside()
627 {
628 if (m_commentedElement)
629 return;
630 auto preStart = m_startElement;
631 if (m_startElement == m_ranges.starts.begin()) {
632 m_commentedElement = m_startElement.value(); // checkElementAfter should have handled this
633 return;
634 } else {
635 --preStart;
636 }
637 // we are inside a node, actually inside both n1 and n2 (which might be the same)
638 // add to pre of the smallest between n1 and n2.
639 // This is needed because if there are multiple nodes starting/ending at the same
640 // place we store only the first (i.e. largest)
641 ElementRef n1 = preStart.value();
642 ElementRef n2 = m_endElement.value();
643 if (n1.size > n2.size)
644 m_commentedElement = n2;
645 else
646 m_commentedElement = n1;
647 }
648private:
649 QStringView m_code;
650 ElementRef &m_commentedElement;
651 quint32 &m_lastPostCommentPostEnd;
652 Comment::CommentType m_commentType = Comment::Pre;
653 const AstRangesVisitor &m_ranges;
654 const SourceLocation &m_commentLocation;
655
656 using RangesIterator = decltype(m_ranges.starts.begin());
657 const RangesIterator m_startElement;
658 const RangesIterator m_endElement;
659 SpaceTrace m_spaces;
660};
661
666bool AstComments::iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
667{
668 // TODO: QTBUG-123645
669 // Revert this commit to reproduce crash with tst_qmldomitem::doNotCrashAtAstComments
670 QList<Comment> pre;
671 QList<Comment> post;
672 for (const auto &commentedElement : commentedElements().values()) {
673 pre.append(commentedElement.preComments());
674 post.append(commentedElement.postComments());
675 }
676 if (!pre.isEmpty())
677 self.dvWrapField(visitor, Fields::preComments, pre);
678 if (!post.isEmpty())
679 self.dvWrapField(visitor, Fields::postComments, post);
680
681 return false;
682}
683
684CommentCollector::CommentCollector(MutableDomItem item)
685 : m_rootItem{ std::move(item) },
686 m_fileLocations{ FileLocations::treeOf(m_rootItem.item()) }
687{
688}
689
691{
692 if (std::shared_ptr<ScriptExpression> scriptPtr = m_rootItem.ownerAs<ScriptExpression>()) {
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());
696 } else {
697 qCWarning(commentsLog)
698 << "collectComments works with QmlFile and ScriptExpression, not with"
699 << m_rootItem.item().internalKindStr();
700 }
701}
702
708 const std::shared_ptr<Engine> &engine, AST::Node *rootNode,
709 const std::shared_ptr<AstComments> &astComments)
710{
711 if (!rootNode)
712 return;
713 AstRangesVisitor ranges;
714 ranges.addItemRanges(m_rootItem.item(), m_fileLocations, Path());
715 ranges.addNodeRanges(rootNode);
716 QStringView code = engine->code();
717 quint32 lastPostCommentPostEnd = 0;
718 for (const SourceLocation &commentLocation : engine->comments()) {
719 // collect whitespace before and after cLoc -> iPre..iPost contains whitespace,
720 // do not add newline before, but add the one after
721 ElementRef elementToBeLinked;
722 CommentLinker linker(code, elementToBeLinked, ranges, lastPostCommentPostEnd, commentLocation);
723 linker.linkCommentWithElement();
724 const auto comment = linker.createComment();
725
726 if (!elementToBeLinked) {
727 qCWarning(commentsLog) << "Could not assign comment at" << sourceLocationToQCborValue(commentLocation)
728 << "adding before root node";
729 if (m_rootItem && (m_fileLocations || !rootNode)) {
730 elementToBeLinked.element = RegionRef{ Path(), MainRegion };
731 elementToBeLinked.size = FileLocations::region(m_fileLocations, MainRegion).length;
732 } else if (rootNode) {
733 elementToBeLinked.element = rootNode;
734 elementToBeLinked.size = rootNode->lastSourceLocation().end() - rootNode->firstSourceLocation().begin();
735 }
736 }
737
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)) {
742 MutableDomItem regionComments = m_rootItem.item()
743 .path(regionRef->path)
744 .field(Fields::comments);
745 if (auto *regionCommentsPtr = regionComments.mutableAs<RegionComments>())
746 regionCommentsPtr->addComment(comment, regionRef->regionName);
747 else
748 Q_ASSERT(false && "Cannot attach to region comments");
749 } else {
750 qCWarning(commentsLog)
751 << "Failed: no item or node to attach comment" << comment.rawComment();
752 }
753 }
754}
755
757{
758 bool cont = true;
759 if (!m_regionComments.isEmpty()) {
760 cont = cont
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);
766 });
767 }
768 return cont;
769}
770
771} // namespace Dom
772} // namespace QQmlJS
\inmodule QtCore
virtual SourceLocation firstSourceLocation() const =0
void accept(BaseVisitor *visitor)
virtual SourceLocation lastSourceLocation() const =0
void addNodeRanges(AST::Node *rootNode)
bool preVisit(Node *n) override
QMap< quint32, ElementRef > ends
void addItemRanges(const DomItem &item, const FileLocations::Tree &itemLocations, const Path &currentP)
static const QSet< int > kindsToSkip()
static bool shouldSkipRegion(const DomItem &item, FileLocationRegion region)
returns true if comments should skip attaching to this region
QMap< quint32, ElementRef > starts
Extracts various pieces and information out of a rawComment string.
CommentInfo(QStringView, QQmlJS::SourceLocation loc)
QQmlJS::SourceLocation commentLocation
CommentLinker(QStringView code, ElementRef &commentedElement, const AstRangesVisitor &ranges, quint32 &lastPostCommentPostEnd, const SourceLocation &commentLocation)
Represents a comment.
void write(OutWriter &lw, SourceLocation *commentLocation=nullptr) const
QStringView rawComment() const
CommentInfo info() const
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
Expose attributes to the Dom.
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
void writePre(OutWriter &lw, QList< SourceLocation > *locations=nullptr) const
void writePost(OutWriter &lw, QList< SourceLocation > *locations=nullptr) const
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 & write(QStringView v, LineWriter::TextAddType t=LineWriter::TextAddType::Normal)
A QmlFile, when loaded in a DomEnvironment that has the DomCreationOption::WithSemanticAnalysis,...
Keeps the comments associated with a DomItem.
bool iterateDirectSubpaths(const DomItem &self, DirectVisitor visitor) const
FileLocationRegion regionName
A vistor that visits all the AST:Node.
static QSet< int > uiKinds()
returns a set with all Ui* Nodes (i.e.
iterator begin()
Definition qset.h:136
iterator end()
Definition qset.h:140
const_iterator cbegin() const noexcept
Definition qset.h:138
\inmodule QtCore
Definition qstringview.h:78
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
Definition qstring.h:129
QMap< QString, QString > map
[6]
QSet< QString >::iterator it
QCborValue sourceLocationToQCborValue(QQmlJS::SourceLocation loc)
Combined button and popup list for selecting options.
@ QtWarningMsg
Definition qlogging.h:31
#define Q_LOGGING_CATEGORY(name,...)
#define qCWarning(category,...)
static bool contains(const QJsonArray &haystack, unsigned needle)
Definition qopengl.cpp:116
GLenum GLuint GLintptr GLsizeiptr size
[1]
GLuint GLuint end
GLuint start
GLfloat n
GLuint res
const GLubyte * c
GLsizei const GLchar *const * path
#define Q_ASSERT(cond)
Definition qrandom.cpp:47
QtPrivate::QRegularExpressionMatchIteratorRangeBasedForIterator begin(const QRegularExpressionMatchIterator &iterator)
SSL_CTX int void * arg
#define QStringLiteral(str)
#define tr(X)
unsigned int quint32
Definition qtypes.h:50
ptrdiff_t qsizetype
Definition qtypes.h:165
QGraphicsItem * item
manager post(request, myJson, this, [this](QRestReply &reply) { if(!reply.isSuccess()) { } if(std::optional json=reply.readJson()) { } })
QJSEngine engine
[0]
\inmodule QtCore \reentrant
Definition qchar.h:18