13#include <QtCore/qfile.h>
14#include <QtCore/qregularexpression.h>
15#include <QtCore/qtextstream.h>
23using namespace Qt::StringLiterals;
139} cmds[] = { {
"a",
CMD_A,
true },
141 {
"b",
CMD_B,
true },
146 {
"c",
CMD_C,
true },
154 {
"e",
CMD_E,
true },
179 {
"i",
CMD_I,
true },
252
253
254
255
258 qsizetype colonPos = link.indexOf(
':');
259 QString cleaned{link};
260 cleaned.replace(
"\\#"_L1,
"#"_L1);
261 if ((colonPos == -1) || (!link.startsWith(
"file:") && !link.startsWith(
"mailto:")))
263 return cleaned.mid(colonPos + 1).simplified();
273 while (cmds[i]
.name) {
277 Location::internalError(QStringLiteral(
"command %1 missing").arg(i));
283 const auto &outputFormats = config.getOutputFormats();
284 for (
const auto &format : outputFormats)
285 DocParser::s_quoting = DocParser::s_quoting
290 DocParser::file_resolver = &file_resolver;
294
295
296
297
298
299
300
301
303 const QSet<QString> &metaCommandSet,
const QSet<QString> &possibleTopics)
307 m_inputLength = m_input.size();
308 m_cachedLocation = docPrivate->m_start_loc;
309 m_cachedPosition = 0;
310 m_private = docPrivate;
314 m_paragraphState = OutsideParagraph;
315 m_inTableHeader =
false;
316 m_inTableRow =
false;
317 m_inTableItem =
false;
318 m_indexStartedParagraph =
false;
324 m_openedCommands.push(CMD_OMIT);
328 Atom *currentLinkAtom =
nullptr;
330 QStack<
bool> preprocessorSkipping;
331 int numPreprocessorSkipping = 0;
337 QString codeLanguage;
339 while (m_position < m_inputLength) {
340 QChar ch = m_input.at(m_position);
342 switch (ch.unicode()) {
345 m_backslashPosition = m_position;
347 while (m_position < m_inputLength) {
348 ch = m_input.at(m_position);
349 if (ch.isLetterOrNumber()) {
356 m_endPosition = m_position;
357 if (cmdStr.isEmpty()) {
358 if (m_position < m_inputLength) {
359 QChar nextCh = m_input.at(m_position);
360 if (nextCh ==
'\\') {
361 appendEscapedIdentifier();
362 }
else if (nextCh.isSpace()) {
365 appendChar(QLatin1Char(
' '));
368 appendChar(m_input.at(m_position++));
385 m_private->m_params.insert(p1);
389 appendAtom(Atom(Atom::CodeBad,
390 getCode(CMD_BADCODE, marker, getMetaCommandArgument(cmdStr))));
397 location().warning(QStringLiteral(
"'\\bold' is deprecated. Use '\\b'"));
404 enterPara(Atom::BriefLeft, Atom::BriefRight);
408 p1 = untabifyEtc(getArgument(ArgumentParsingOptions::Verbatim));
409 marker = CodeMarker::markerForCode(p1);
410 appendAtom(
Atom(
Atom::C, marker->markedUpCode(p1,
nullptr, location())));
414 enterPara(Atom::CaptionLeft, Atom::CaptionRight);
418 codeLanguage = getLanguageArgument(&marker);
422 appendAtom(Atom(Atom::Code, getCode(CMD_CODE, marker, getMetaCommandArgument(cmdStr)), codeLanguage));
426 appendAtom(Atom(Atom::Qml,
427 getCode(CMD_QML, CodeMarker::markerForLanguage(QLatin1String(
"QML")),
428 getMetaCommandArgument(cmdStr))));
433 if (!isBlankLine()) {
434 if (isLeftBraceAhead()) {
435 enterPara(Atom::DetailsSummaryLeft, Atom::DetailsSummaryRight);
437 location().warning(u"Expected '{' when parsing \\%1 argument"_s.arg(cmdName(cmd)));
438 std::ignore = getRestOfLine();
441 m_openedCommands.push(cmd);
450 p1 = getArgument(ArgumentParsingOptions::Verbatim);
452 m_openedCommands.push(cmd);
464 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
469 QString arg = getOptionalArgument();
476 if (isCode(m_lastAtom) && m_lastAtom->string().endsWith(
"\n\n"))
479 int indent = arg.toInt();
480 for (
int i = 0; i < indent; ++i)
482 appendToCode(
"...\n");
486 if (!preprocessorSkipping.empty()) {
487 if (preprocessorSkipping.top()) {
488 --numPreprocessorSkipping;
490 ++numPreprocessorSkipping;
492 preprocessorSkipping.top() = !preprocessorSkipping.top();
493 (
void)getRestOfLine();
494 if (numPreprocessorSkipping)
495 skipToNextPreprocessorCommand();
498 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ELSE)));
508 if (closeCommand(cmd)) {
514 if (preprocessorSkipping.size() > 0) {
515 if (preprocessorSkipping.pop())
516 --numPreprocessorSkipping;
517 (
void)getRestOfLine();
518 if (numPreprocessorSkipping)
519 skipToNextPreprocessorCommand();
522 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDIF)));
526 if (closeCommand(cmd)) {
532 if (closeCommand(cmd)) {
540 if (closeCommand(cmd)) {
542 if (m_openedLists.top().isStarted()) {
543 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
544 appendAtom(Atom(Atom::ListRight, m_openedLists.top().styleString()));
553 if (closeCommand(cmd)) {
560 QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(
CMD_ENDRAW)));
575 if (closeCommand(cmd)) {
581 if (closeCommand(cmd)) {
587 if (openCommand(cmd)) {
594 if (isLeftBracketAhead())
595 p2 = getBracketedArgument();
598 appendAtom(Atom(Atom::AnnotatedList, getArgument(), p2));
602 appendAtom(Atom(Atom::SinceList, getRestOfLine().simplified()));
606 if (isLeftBracketAhead())
607 p2 = getBracketedArgument();
610 QString arg1 = getArgument();
611 QString arg2 = getOptionalArgument();
614 appendAtom(Atom(Atom::GeneratedList, arg1, p2));
617 if (m_openedCommands.top() == CMD_TABLE) {
620 m_inTableHeader =
true;
622 if (m_openedCommands.contains(CMD_TABLE))
623 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
625 cmdName(m_openedCommands.top())));
628 QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
633 location().warning(QStringLiteral(
634 "'\\i' is deprecated. Use '\\e' for italic or '\\li' for list item"));
644 preprocessorSkipping.push(!Tokenizer::isTrue(getRestOfLine()));
645 if (preprocessorSkipping.top())
646 ++numPreprocessorSkipping;
647 if (numPreprocessorSkipping)
648 skipToNextPreprocessorCommand();
655 enterPara(Atom::ImportantLeft, Atom::ImportantRight);
659 QString fileName = getArgument();
660 QStringList parameters;
662 if (isLeftBraceAhead()) {
663 identifier = getArgument();
664 while (isLeftBraceAhead() && parameters.size() < 9)
665 parameters << getArgument();
667 identifier = getRestOfLine();
669 include(fileName, identifier, parameters);
676 if (m_paragraphState == OutsideParagraph) {
678 m_indexStartedParagraph =
true;
681 if (m_indexStartedParagraph
684 m_indexStartedParagraph =
false;
690 insertKeyword(getRestOfLine());
693 if (m_openedCommands.top() != CMD_TOC) {
694 location().warning(
"Command '\\%1' outside of '\\%2'"_L1
695 .arg(cmdName(cmd), cmdName(CMD_TOC)));
701 if (isLeftBracketAhead())
702 p2 = getBracketedArgument();
706 appendAtom(LinkAtom(p1, p2, location()));
708 if (isLeftBraceAhead()) {
713 appendAtom(Atom(Atom::String, cleanLink(p1)));
722 if (openCommand(cmd))
727 if (openCommand(cmd)) {
732 skipSpacesOrOneEndl();
736 if (openCommand(cmd)) {
738 m_openedLists.push(OpenedList(location(), getOptionalArgument()));
748 enterPara(Atom::NoteLeft, Atom::NoteRight);
750 case CMD_NOTRANSLATE:
754 location().warning(QStringLiteral(
"'\\o' is deprecated. Use '\\li'"));
758 if (m_openedCommands.top() == CMD_LIST) {
759 if (m_openedLists.top().isStarted())
760 appendAtom(Atom(Atom::ListItemRight, m_openedLists.top().styleString()));
762 appendAtom(Atom(Atom::ListLeft, m_openedLists.top().styleString()));
763 m_openedLists.top().next();
764 appendAtom(Atom(Atom::ListItemNumber, m_openedLists.top().numberString()));
765 appendAtom(Atom(Atom::ListItemLeft, m_openedLists.top().styleString()));
767 }
else if (m_openedCommands.top() == CMD_TABLE) {
770 if (isLeftBraceAhead()) {
772 if (isLeftBraceAhead())
776 if (!m_inTableHeader && !m_inTableRow) {
778 QStringLiteral(
"Missing '\\%1' or '\\%2' before '\\%3'")
783 }
else if (m_inTableItem) {
785 m_inTableItem =
false;
788 appendAtom(Atom(Atom::TableItemLeft, p1, p2));
789 m_inTableItem =
true;
792 QStringLiteral(
"Command '\\%1' outside of '\\%2' and '\\%3'")
801 if (!m_private->m_enumItemList.contains(p1))
802 m_private->m_enumItemList.append(p1);
803 if (!m_private->m_omitEnumItemList.contains(p1))
804 m_private->m_omitEnumItemList.append(p1);
805 skipSpacesOrOneEndl();
807 while (m_position < m_inputLength && !isBlankLine()) {
809 if (qsizetype pos = m_position; pos < m_input.size()
810 && m_input.at(pos++).unicode() ==
'\\') {
812 while (pos < m_input.size() && m_input[pos].isLetterOrNumber())
813 nextCmdStr += m_input[pos++];
815 if (nextCmd == cmd || nextCmd ==
CMD_VALUE)
824 p1 = getRestOfLine();
825 if (openCommand(cmd))
829 if (closeCommand(cmd)) {
841 QString rest = getRestOfLine();
846 if (cmd == CMD_PRINTLINE)
847 appendToCode(m_quoter.quoteLine(location(), cmdStr, rest), atomType, codeLanguage);
848 else if (cmd == CMD_PRINTTO)
849 appendToCode(m_quoter.quoteTo(location(), cmdStr, rest), atomType, codeLanguage);
851 appendToCode(m_quoter.quoteUntil(location(), cmdStr, rest), atomType, codeLanguage);
855 if (openCommand(cmd)) {
863 codeLanguage = getLanguageArgument(&marker);
866 QString fileName = getArgument();
872 marker = CodeMarker::markerForFileName(fileName);
874 quoteFromFile(fileName, marker);
876 appendAtom(Atom(Atom::Code, m_quoter.quoteTo(location(), cmdStr, QString()), codeLanguage));
883 p1 = getRestOfLine();
885 location().warning(QStringLiteral(
"Missing format name after '\\%1'")
893 if (m_openedCommands.top() == CMD_TABLE) {
895 if (isLeftBraceAhead())
896 p1 = getArgument(ArgumentParsingOptions::Verbatim);
901 if (m_openedCommands.contains(CMD_TABLE))
902 location().warning(QStringLiteral(
"Cannot use '\\%1' within '\\%2'")
904 cmdName(m_openedCommands.top())));
906 location().warning(QStringLiteral(
"Cannot use '\\%1' outside of '\\%2'")
926 if (openCommand(cmd)) {
933 QString rest = getRestOfLine();
938 m_quoter.quoteLine(location(), cmdStr, rest);
943 QString rest = getRestOfLine();
948 m_quoter.quoteTo(location(), cmdStr, rest);
953 QString rest = getRestOfLine();
958 m_quoter.quoteUntil(location(), cmdStr, rest);
963 startFormat(p1, cmd);
967 codeLanguage = getLanguageArgument(&marker);
970 QString snippet = getArgument();
971 QString identifier = getRestOfLine();
978 marker = CodeMarker::markerForFileName(snippet);
980 quoteFromFile(snippet, marker);
981 appendToCode(m_quoter.quoteSnippet(location(), identifier), marker->atomType(), codeLanguage);
992 p1 = getOptionalArgument();
993 p2 = getOptionalArgument();
994 if (openCommand(cmd)) {
996 appendAtom(Atom(Atom::TableLeft, p1, p2));
997 m_inTableHeader =
false;
998 m_inTableRow =
false;
999 m_inTableItem =
false;
1003 location().report(
"\\%1 is deprecated and will be removed in a future version."_L1.arg(cmdName(cmd)));
1004 if (isLeftBraceAhead())
1008 if (m_openedCommands.top() == CMD_TABLE && !m_inTableItem) {
1009 location().warning(
"Found a \\target command outside table item in a table.\n"
1010 "Move the \\target inside the \\li to resolve this warning.");
1012 insertTarget(getRestOfLine());
1016 enterPara(Atom::TitleLeft, Atom::TitleRight);
1020 if (m_paragraphState != InSingleLineParagraph)
1024 if (openCommand(cmd)) {
1030 if (closeCommand(cmd)) {
1048 uint unicodeChar = p1.toUInt(&ok, 0);
1049 if (!ok || (unicodeChar == 0x0000) || (unicodeChar > 0xFFFE))
1051 QStringLiteral(
"Invalid Unicode character '%1' specified with '%2'")
1059 if (m_openedLists.top().style() == OpenedList::Value) {
1062 if (p1.startsWith(QLatin1String(
"[since "))
1063 && p1.endsWith(QLatin1String(
"]"))) {
1064 p2 = p1.mid(7, p1.size() - 8);
1067 if (!m_private->m_enumItemList.contains(p1))
1068 m_private->m_enumItemList.append(p1);
1070 m_openedLists.top().next();
1074 if (!p2.isEmpty()) {
1081 skipSpacesOrOneEndl();
1090 enterPara(Atom::WarningLeft, Atom::WarningRight);
1096 if (metaCommandSet.contains(cmdStr)) {
1098 QString bracketedArg;
1099 m_private->m_metacommandsUsed.insert(cmdStr);
1100 if (isLeftBracketAhead())
1101 bracketedArg = getBracketedArgument();
1104 if (m_position < m_inputLength
1105 && (cmdStr == QLatin1String(
"obsolete")
1106 || cmdStr == QLatin1String(
"deprecated")))
1107 m_input[m_position] =
'\n';
1109 arg = getMetaCommandArgument(cmdStr);
1111 if (possibleTopics.contains(cmdStr)) {
1112 if (!cmdStr.endsWith(QLatin1String(
"propertygroup")))
1115 }
else if (s_utilities
.macroHash.contains(cmdStr)) {
1117 QStringList macroArgs;
1118 int numPendingFi = 0;
1119 int numFormatDefs = 0;
1120 for (
auto it = macro.m_otherDefs.constBegin();
1121 it != macro.m_otherDefs.constEnd(); ++it) {
1122 if (it.key() !=
"match") {
1123 if (numFormatDefs == 0)
1124 macroArgs = getMacroArguments(cmdStr, macro);
1126 expandMacro(*it, macroArgs);
1128 if (it == macro.m_otherDefs.constEnd()) {
1136 while (numPendingFi-- > 0)
1139 if (!macro.m_defaultDef.isEmpty()) {
1140 if (numFormatDefs > 0) {
1141 macro.m_defaultDefLocation.warning(
1142 QStringLiteral(
"Macro cannot have both "
1143 "format-specific and qdoc-"
1144 "syntax definitions"));
1146 QString expanded = expandMacroToString(cmdStr, macro);
1147 m_input.replace(m_backslashPosition,
1148 m_endPosition - m_backslashPosition, expanded);
1149 m_inputLength = m_input.size();
1150 m_position = m_backslashPosition;
1153 }
else if (isAutoLinkString(cmdStr)) {
1156 if (!cmdStr.endsWith(
"propertygroup")) {
1159 location().warning(QStringLiteral(
"Unknown command '\\%1'").arg(cmdStr),
1160 detailsUnknownCommand(metaCommandSet, cmdStr));
1171 qsizetype dashCount = 1;
1175 while ((m_position < m_inputLength) && (m_input.at(m_position) ==
'-')) {
1180 if (dashCount == 3) {
1182 const QChar emDash(8212);
1184 }
else if (dashCount == 2) {
1186 const QChar enDash(8211);
1192 for (qsizetype i = 0; i < dashCount; ++i)
1201 if (m_paragraphState != InBraceDelimitedParagraph)
1210 auto format = m_pendingFormats.find(m_braceDepth);
1211 if (format == m_pendingFormats.end()) {
1212 if (m_paragraphState == InBraceDelimitedParagraph) {
1222 if (m_indexStartedParagraph)
1227 if (currentLinkAtom && currentLinkAtom->string().endsWith(
"::")) {
1231 currentLinkAtom->concatenateString(suffix);
1233 currentLinkAtom =
nullptr;
1237 m_pendingFormats.erase(format);
1243 if (m_position + 2 < m_inputLength)
1244 if (m_input.at(m_position + 1) ==
'/')
1245 if (m_input.at(m_position + 2) ==
'!') {
1248 if (m_input.at(m_position - 1) ==
'\n')
1264 if (m_paragraphState == OutsideParagraph) {
1276 && (m_paragraphState == InSingleLineParagraph || isBlankLine())) {
1289 qsizetype startPos = m_position;
1291 bool autolink = (!m_pendingFormats.isEmpty() &&
1293 || m_paragraphState == InSingleLineParagraph ?
1294 false : isAutoLinkString(m_input, m_position);
1295 if (m_position == startPos) {
1296 if (!ch.isSpace()) {
1301 QString word = m_input.mid(startPos, m_position - startPos);
1303 if (s_ignoreWords.contains(word) || word.startsWith(QString(
"__")))
1318 if (m_openedCommands.top() == CMD_LEGALESE) {
1320 m_openedCommands.pop();
1323 if (m_openedCommands.top() != CMD_OMIT) {
1325 QStringLiteral(
"Missing '\\%1'").arg(endCmdName(m_openedCommands.top())));
1326 }
else if (preprocessorSkipping.size() > 0) {
1327 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(
CMD_ENDIF)));
1331 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1339
1340
1343 while (!m_openedInputs.isEmpty() && m_openedInputs.top() <= m_position) {
1344 m_cachedLocation.pop();
1345 m_cachedPosition = m_openedInputs.pop();
1347 while (m_cachedPosition < m_position)
1348 m_cachedLocation.advance(m_input.at(m_cachedPosition++));
1349 return m_cachedLocation;
1352QString
DocParser::detailsUnknownCommand(
const QSet<QString> &metaCommandSet,
const QString &str)
1354 QSet<QString> commandSet = metaCommandSet;
1356 while (cmds[i]
.name !=
nullptr) {
1357 commandSet.insert(cmds[i]
.name);
1361 return "Maybe you meant '\\%1'?"_L1.arg(suggestName(str, commandSet));
1365
1366
1367
1368
1369
1370
1371
1373 const QString &cmdString,
const QString &previousDefinition)
1375 if (duplicateDefinition.isEmpty()) {
1376 location.warning(
"Expected an argument for \\%1"_L1.arg(cmdString));
1379 "Duplicate %3 name '%1'. The previous occurrence is here: %2"_L1
1380 .arg(duplicateDefinition, previousDefinition, cmdString));
1385
1386
1387
1388
1389
1390
1391
1392
1393
1394
1395
1396
1397
1398void DocParser::insertTarget(
const QString &target)
1400 if (target.isEmpty() || m_targetMap.contains(target))
1401 return warnAboutEmptyOrPreexistingTarget(location(), target,
1402 s_utilities.cmdHash.key(CMD_TARGET), m_targetMap[target].toString());
1404 m_targetMap.insert(target, location());
1412
1413
1414
1415
1416
1417
1418
1419
1420
1421
1422
1423
1424
1425void DocParser::insertKeyword(
const QString &keyword)
1427 if (keyword.isEmpty() || m_targetMap.contains(keyword))
1428 return warnAboutEmptyOrPreexistingTarget(location(), keyword,
1429 s_utilities.cmdHash.key(CMD_KEYWORD), m_targetMap[keyword].toString());
1431 m_targetMap.insert(keyword, location());
1438void DocParser::include(
const QString &fileName,
const QString &identifier,
const QStringList ¶meters)
1441 location().fatal(QStringLiteral(
"Too many nested '\\%1's").arg(cmdName(
CMD_INCLUDE)));
1442 QString filePath = Config::instance().getIncludeFilePath(fileName);
1443 if (filePath.isEmpty()) {
1444 location().warning(QStringLiteral(
"Cannot find qdoc include file '%1'").arg(fileName));
1446 QFile inFile(filePath);
1447 if (!inFile.open(QFile::ReadOnly)) {
1449 QStringLiteral(
"Cannot open qdoc include file '%1'").arg(filePath));
1451 location().push(fileName);
1452 QTextStream inStream(&inFile);
1453 QString includedContent = inStream.readAll();
1456 if (identifier.isEmpty()) {
1457 expandArgumentsInString(includedContent, parameters);
1458 m_input.insert(m_position, includedContent);
1459 m_inputLength = m_input.size();
1460 m_openedInputs.push(m_position + includedContent.size());
1462 auto isSnippetMarker = [&identifier](QStringView trimmedLine) ->
bool {
1463 if (!trimmedLine.startsWith(QLatin1String(
"//!")))
1465 auto bracketStart = trimmedLine.indexOf(QLatin1Char(
'['));
1466 auto bracketEnd = trimmedLine.indexOf(QLatin1Char(
']'));
1467 if (bracketStart < 0 || bracketEnd <= bracketStart)
1469 auto name = trimmedLine.mid(bracketStart + 1, bracketEnd - bracketStart - 1)
1471 return name == identifier;
1474 QStringList lineBuffer = includedContent.split(QLatin1Char(
'\n'));
1475 qsizetype bufLen{lineBuffer.size()};
1477 QStringView trimmedLine;
1478 for (i = 0; i < bufLen; ++i) {
1479 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1480 if (isSnippetMarker(trimmedLine))
1483 if (i < bufLen - 1) {
1487 QStringLiteral(
"Cannot find '%1' in '%2'").arg(identifier, filePath));
1492 trimmedLine = QStringView{lineBuffer[i]}.trimmed();
1493 if (isSnippetMarker(trimmedLine))
1496 result += lineBuffer[i] + QLatin1Char(
'\n');
1498 }
while (i < bufLen);
1500 expandArgumentsInString(result, parameters);
1501 if (result.isEmpty()) {
1502 location().warning(QStringLiteral(
"Empty qdoc snippet '%1' in '%2'")
1503 .arg(identifier, filePath));
1505 m_input.insert(m_position, result);
1506 m_inputLength = m_input.size();
1507 m_openedInputs.push(m_position + result.size());
1514void DocParser::startFormat(
const QString &format,
int cmd)
1518 for (
const auto &item : std::as_const(m_pendingFormats)) {
1519 if (item == format) {
1520 location().warning(QStringLiteral(
"Cannot nest '\\%1' commands").arg(cmdName(cmd)));
1527 if (isLeftBraceAhead()) {
1528 skipSpacesOrOneEndl();
1529 m_pendingFormats.insert(m_braceDepth, format);
1533 const auto &arg{getArgument()};
1538 m_indexStartedParagraph =
false;
1547 int outer = m_openedCommands.top();
1550 if ((cmd == CMD_COMPARESWITH || cmd == CMD_TOC)
1551 && m_openedCommands.contains(cmd)) {
1552 location().warning(u"Cannot nest '\\%1' commands"_s.arg(cmdName(cmd)));
1569 m_openedCommands.push(cmd);
1572 QStringLiteral(
"Can't use '\\%1' in '\\%2'").arg(cmdName(cmd), cmdName(outer)));
1578
1579
1580
1581
1582
1583
1584
1585
1586
1587
1588
1589
1590
1591
1592
1593
1594
1595
1596
1597
1598
1599
1600
1601
1602
1603
1604
1605
1606
1607
1608
1609
1610
1611
1612
1613
1614
1615
1616
1617
1618
1619
1620
1621
1622
1623
1624
1625
1626
1627
1628
1629
1630
1631
1632
1633
1634
1635
1636
1637
1638
1639
1640
1641
1642
1643
1644
1645
1646
1647
1648
1649
1650
1651
1652
1653
1654inline bool DocParser::isAutoLinkString(
const QString &word)
1656 qsizetype start = 0;
1657 return isAutoLinkString(word, start) && (start == word.size());
1661
1662
1663
1664
1665
1666
1667
1668
1669
1670
1671
1672
1673
1674
1675
1676
1677
1678
1679
1680
1681bool DocParser::isAutoLinkString(
const QString &word, qsizetype &curPos)
1683 qsizetype len = word.size();
1684 qsizetype startPos = curPos;
1685 int numUppercase = 0;
1686 int numLowercase = 0;
1687 int numStrangeSymbols = 0;
1689 while (curPos < len) {
1690 unsigned char latin1Ch = word.at(curPos).toLatin1();
1691 if (islower(latin1Ch)) {
1694 }
else if (isupper(latin1Ch)) {
1695 if (curPos > startPos)
1698 }
else if (isdigit(latin1Ch)) {
1699 if (curPos > startPos)
1703 }
else if (latin1Ch ==
'_' || latin1Ch ==
'@') {
1704 ++numStrangeSymbols;
1706 }
else if ((latin1Ch ==
':') && (curPos < len - 1)
1707 && (word.at(curPos + 1) == QLatin1Char(
':'))) {
1708 ++numStrangeSymbols;
1710 }
else if (latin1Ch ==
'(') {
1711 if ((curPos < len - 1) && (word.at(curPos + 1) == QLatin1Char(
')'))) {
1712 ++numStrangeSymbols;
1722 return ((numUppercase >= 1 && numLowercase >= 2) || (numStrangeSymbols > 0 && (numUppercase + numLowercase >= 1)));
1727 if (endCmdFor(m_openedCommands.top()) == endCmd && m_openedCommands.size() > 1) {
1728 m_openedCommands.pop();
1731 bool contains =
false;
1732 QStack<
int> opened2 = m_openedCommands;
1733 while (opened2.size() > 1) {
1742 while (endCmdFor(m_openedCommands.top()) != endCmd && m_openedCommands.size() > 1) {
1744 QStringLiteral(
"Missing '\\%1' before '\\%2'")
1745 .arg(endCmdName(m_openedCommands.top()), cmdName(endCmd)));
1746 m_openedCommands.pop();
1749 location().warning(QStringLiteral(
"Unexpected '\\%1'").arg(cmdName(endCmd)));
1760 m_currentSection =
static_cast<
Doc::
Sections>(unit);
1763 endSection(unit, cmd);
1766 appendAtom(Atom(Atom::SectionLeft, QString::number(unit)));
1769 m_private
->extra->m_tableOfContentsLevels.append(unit);
1770 enterPara(Atom::SectionHeadingLeft, Atom::SectionHeadingRight, QString::number(unit));
1771 m_currentSection = unit;
1777 appendAtom(Atom(Atom::SectionRight, QString::number(m_currentSection)));
1782
1783
1784
1785
1786
1787
1788
1789
1790
1791
1792
1793
1811 const QString imageFileName = getArgument();
1813 bool hasAltTextArgument{
false};
1814 if (isLeftBraceAhead()) {
1815 hasAltTextArgument =
true;
1816 imageText = getArgument();
1818 imageText = getRestOfLine();
1821 if (imageText.length() > 1) {
1822 if (imageText.front() ==
'"' && imageText.back() ==
'"') {
1823 imageText.removeFirst();
1824 imageText.removeLast();
1828 if (!hasAltTextArgument && imageText.isEmpty() && Config::instance().reportMissingAltTextForImages())
1829 location().report(QStringLiteral(
"\\%1 %2 is without a textual description, "
1830 "QDoc will not generate an alt text for the image.")
1832 .arg(imageFileName));
1833 appendAtom(
Atom(imageAtom, imageFileName));
1838
1839
1840
1841
1842
1843
1844
1845
1846
1847
1848
1849
1852 const QString cmd{
"overload"};
1855 m_private->m_metacommandsUsed.insert(cmd);
1856 QString overloadArgument = isBlankLine() ? getMetaCommandArgument(cmd) : getRestOfLine();
1861 if (overloadArgument.trimmed() ==
"primary")
1862 overloadArgument =
"__qdoc_primary_overload__"_L1;
1868
1869
1870
1871
1872
1873
1874
1875
1876
1877
1878
1879
1882 auto line_comment = [
this]() ->
bool {
1884 if (m_position + 2 > m_inputLength)
1886 if (m_input[m_position].unicode() ==
'/') {
1887 if (m_input[m_position + 1].unicode() ==
'/') {
1888 if (m_input[m_position + 2].unicode() ==
'!') {
1896 auto skip_everything_until_newline = [
this]() ->
void {
1897 while (m_position < m_inputLength && m_input[m_position] !=
'\n')
1903 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
1906 bool skipMe =
false;
1908 if (m_input[m_position] ==
'{') {
1909 target = getArgument();
1910 if (isLeftBraceAhead()) {
1911 str = getArgument();
1914 if (target.endsWith(
"::"))
1920 target = getArgument();
1921 str = cleanLink(target);
1922 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1936 skip_everything_until_newline();
1938 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1941 skip_everything_until_newline();
1942 skipSpacesOrOneEndl();
1943 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1944 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1962 if (ch == QLatin1Char(
' ')) {
1963 if (!atom->string().endsWith(QLatin1Char(
' ')))
1964 atom->appendChar(QLatin1Char(
' '));
1966 atom->appendChar(ch);
1969void DocParser::appendWord(
const QString &word)
1978
1979
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989void DocParser::appendEscapedIdentifier()
1991 Q_ASSERT(m_position < m_inputLength);
1992 Q_ASSERT(m_input.at(m_position) ==
'\\');
1997 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
1998 identifier += m_input.at(m_position);
2003 if (!identifier.isEmpty()) {
2008 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
2011 appendChar(QLatin1Char(
'\\'));
2015void DocParser::appendToCode(
const QString &markedCode)
2017 if (!isCode(m_lastAtom)) {
2021 m_lastAtom->concatenateString(markedCode);
2025
2026
2027
2028
2029
2030void DocParser::appendToCode(
const QString &markedCode,
Atom::
AtomType defaultType,
const QString &language)
2032 if (!isCode(m_lastAtom)) {
2033 appendAtom(
Atom(defaultType, markedCode, language));
2036 m_lastAtom->concatenateString(markedCode);
2042 if (m_paragraphState != OutsideParagraph)
2053 const bool inValueList = !m_openedLists.isEmpty()
2054 && m_openedLists.top().style() == OpenedList::Value;
2058 if (!inValueList || !isAdmonition)
2062 appendAtom(
Atom(leftType, string));
2063 m_indexStartedParagraph =
false;
2064 m_pendingParagraphLeftType = leftType;
2065 m_pendingParagraphRightType = rightType;
2066 m_pendingParagraphString = string;
2068 m_paragraphState = InSingleLineParagraph;
2070 m_paragraphState = InBraceDelimitedParagraph;
2072 m_paragraphState = InMultiLineParagraph;
2074 skipSpacesOrOneEndl();
2079 if (m_paragraphState == OutsideParagraph)
2081 if (!m_pendingFormats.isEmpty() || (m_paragraphState == InBraceDelimitedParagraph && m_braceDepth > 0)) {
2082 location().warning(u"Missing '}'"_s);
2083 m_pendingFormats.clear();
2099 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2102 m_paragraphState = OutsideParagraph;
2103 m_indexStartedParagraph =
false;
2104 m_pendingParagraphRightType =
Atom::Nop;
2105 m_pendingParagraphString.clear();
2111 if (m_openedLists.isEmpty()) {
2112 m_openedLists.push(OpenedList(OpenedList::Value));
2124 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2129 m_openedLists.pop();
2135 if (m_inTableItem) {
2138 m_inTableItem =
false;
2140 if (m_inTableHeader) {
2142 m_inTableHeader =
false;
2146 m_inTableRow =
false;
2151
2152
2153
2154
2168 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2169 if (!maybe_resolved_file) {
2178 QString details =
std::transform_reduce(
2179 (*file_resolver).get_search_directories().cbegin(),
2180 (*file_resolver).get_search_directories().cend(),
2181 u"Searched directories:"_s,
2183 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2186 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2203 marker = CodeMarker::markerForFileName(QString{});
2204 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2210
2211
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234bool DocParser::expandMacro(ArgumentParsingOptions options)
2236 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2238 if (options == ArgumentParsingOptions::Verbatim)
2242 qsizetype backslashPos = m_position++;
2243 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2244 cmdStr += m_input[m_position++];
2246 m_endPosition = m_position;
2247 if (!cmdStr.isEmpty()) {
2248 if (s_utilities
.macroHash.contains(cmdStr)) {
2250 if (!macro.m_defaultDef.isEmpty()) {
2251 QString expanded = expandMacroToString(cmdStr, macro);
2252 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2253 m_inputLength = m_input.size();
2254 m_position = backslashPos;
2257 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2261 m_position = backslashPos;
2262 if (options != ArgumentParsingOptions::MacroArguments
2264 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2268 }
else if (m_input[m_position].isSpace()) {
2270 }
else if (m_input[m_position].unicode() ==
'\\') {
2272 m_input.remove(m_position--, 1);
2278void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2280 if (args.isEmpty()) {
2285 for (
int j = 0; j < def.size(); ++j) {
2286 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2287 if (!rawString.isEmpty()) {
2293 rawString += def[j];
2296 if (!rawString.isEmpty())
2301QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2303 const QString &def{macro.m_defaultDef};
2307 rawString = macro.m_defaultDef;
2309 QStringList args{getMacroArguments(name, macro)};
2311 for (
int j = 0; j < def.size(); ++j) {
2312 int paramNo = def[j].unicode();
2313 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2316 QString matchExpr{macro.m_otherDefs.value(
"match")};
2317 if (matchExpr.isEmpty())
2321 QRegularExpression re(matchExpr);
2322 int capStart = (re.captureCount() > 0) ? 1 : 0;
2324 QRegularExpressionMatch match;
2325 while ((match = re.match(rawString, i)).hasMatch()) {
2326 for (
int c = capStart; c <= re.captureCount(); ++c)
2327 result += match.captured(c);
2328 i = match.capturedEnd();
2336 QString name = getOptionalArgument();
2338 if (name ==
"section1") {
2340 }
else if (name ==
"section2") {
2342 }
else if (name ==
"section3") {
2344 }
else if (name ==
"section4") {
2346 }
else if (name.isEmpty()) {
2349 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2355
2356
2357
2358
2359
2360
2361
2362
2363
2364QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2368 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2370 while (m_position < m_input.size() && delimDepth >= 0) {
2371 switch (m_input[m_position].unicode()) {
2374 arg += QLatin1Char(
'{');
2379 if (delimDepth >= 0)
2380 arg += QLatin1Char(
'}');
2384 if (!expandMacro(options))
2385 arg += m_input[m_position++];
2388 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2391 arg += m_input[m_position];
2396 location().warning(QStringLiteral(
"Missing '}'"));
2398 m_endPosition = m_position;
2403
2404
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418QString
DocParser::getArgument(ArgumentParsingOptions options)
2420 skipSpacesOrOneEndl();
2423 qsizetype startPos = m_position;
2424 QString arg = getBracedArgument(options);
2425 if (arg.isEmpty()) {
2426 while ((m_position < m_input.size())
2427 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2428 switch (m_input[m_position].unicode()) {
2433 arg += m_input[m_position];
2440 if (m_position == startPos || delimDepth >= 0) {
2441 arg += m_input[m_position];
2446 if (!expandMacro(options))
2447 arg += m_input[m_position++];
2450 arg += m_input[m_position];
2454 m_endPosition = m_position;
2455 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2456 && !arg.endsWith(
"...")) {
2457 arg.truncate(arg.size() - 1);
2460 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2461 arg.truncate(arg.size() - 2);
2465 return arg.simplified();
2469
2470
2471
2472
2473
2474QString
DocParser::getBracketedArgument()
2478 skipSpacesOrOneEndl();
2479 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2481 while (m_position < m_input.size() && delimDepth >= 0) {
2482 switch (m_input[m_position].unicode()) {
2485 arg += QLatin1Char(
'[');
2490 if (delimDepth >= 0)
2491 arg += QLatin1Char(
']');
2495 arg += m_input[m_position];
2499 arg += m_input[m_position];
2504 location().warning(QStringLiteral(
"Missing ']'"));
2510
2511
2512
2513
2514
2515
2516
2520 if (isLeftBracketAhead(0)) {
2521 value = getBracketedArgument();
2522 *marker = markerForLanguage(value);
2527 return value.toLower();
2531
2532
2533
2534
2535
2536QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2540 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2541 args << getArgument(ArgumentParsingOptions::MacroArguments);
2543 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2544 " arguments (expected %2, got %3)")
2555
2556
2557
2558
2561 skipSpacesOrOneEndl();
2562 if (m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2563 && m_input[m_position + 1].isLetterOrNumber()) {
2566 return getArgument();
2571
2572
2573
2574
2575
2576
2577
2578
2579
2580
2581
2582
2583
2584
2587 auto lineHasTrailingBackslash = [
this](
bool trailingBackslash) ->
bool {
2588 while (m_position < m_inputLength && m_input[m_position] !=
'\n') {
2589 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2590 trailingBackslash =
true;
2594 trailingBackslash =
false;
2598 return trailingBackslash;
2601 QString rest_of_line;
2603 bool trailing_backslash{
false };
2604 bool return_simplified_string{
false };
2606 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2607 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2609 if (!rest_of_line.isEmpty())
2610 rest_of_line += QLatin1Char(
' ');
2611 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2613 if (trailing_backslash) {
2614 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2615 return_simplified_string =
true;
2618 if (m_position < m_inputLength)
2621 if (!trailing_backslash)
2623 start_position = m_position;
2626 if (return_simplified_string)
2627 return rest_of_line.simplified();
2629 return rest_of_line.trimmed();
2633
2634
2635
2636
2637QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2641 qsizetype begin = m_position;
2644 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2645 if (m_input.at(m_position) ==
'(')
2647 else if (m_input.at(m_position) ==
')')
2649 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2653 if (m_position == m_input.size() && parenDepth > 0) {
2655 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2658 QString t = m_input.mid(begin, m_position - begin).simplified();
2666 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2668 auto match = rx.match(m_input, m_position);
2670 if (!match.hasMatch()) {
2671 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2672 m_position = m_input.size();
2674 qsizetype end = match.capturedStart();
2675 t = m_input.mid(m_position, end - m_position);
2676 m_position = match.capturedEnd();
2681void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2688 while (j < str.size()) {
2689 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2690 && paramNo <= args.size()) {
2691 const QString &r = args[paramNo - 1];
2692 str.replace(j, 2, r);
2693 j += qMin(1, r.size());
2701
2702
2703
2704
2705
2706
2709 QString code = untabifyEtc(getUntilEnd(cmd));
2710 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2712 int indent = indentLevel(code);
2713 code = dedent(indent, code);
2716 if (!marker && !m_private->m_topics.isEmpty()
2717 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2718 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2719 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2721 if (marker ==
nullptr)
2722 marker = CodeMarker::markerForCode(code);
2723 return marker->markedUpCode(code,
nullptr, location());
2728 qsizetype i = m_position;
2730 while (i < m_inputLength && m_input[i].isSpace()) {
2731 if (m_input[i] ==
'\n')
2741 qsizetype i = m_position;
2743 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2745 if (m_input[i] ==
'\n')
2749 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2752bool DocParser::isLeftBracketAhead(
int maxNewlines)
2755 qsizetype i = m_position;
2757 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2759 if (m_input[i] ==
'\n')
2763 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2767
2768
2771 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2772 && (m_input[m_position].unicode() !=
'\n'))
2777
2778
2781 qsizetype firstEndl = -1;
2782 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2783 QChar ch = m_input[m_position];
2785 if (firstEndl == -1) {
2786 firstEndl = m_position;
2788 m_position = firstEndl;
2798 while (m_position < m_inputLength && m_input[m_position].isSpace())
2802void DocParser::skipToNextPreprocessorCommand()
2804 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2805 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2806 auto match = rx.match(m_input, m_position + 1);
2808 if (!match.hasMatch())
2809 m_position = m_input.size();
2811 m_position = match.capturedStart();
2864 return cmds[cmd]
.name;
2869 return cmdName(endCmdFor(cmd));
2875 result.reserve(str.size());
2878 for (
const auto &character : str) {
2879 if (character == QLatin1Char(
'\r'))
2881 if (character == QLatin1Char(
'\t')) {
2882 result += &
" "[column % s_tabSize];
2883 column = ((column / s_tabSize) + 1) * s_tabSize;
2886 if (character == QLatin1Char(
'\n')) {
2887 while (result.endsWith(QLatin1Char(
' ')))
2889 result += character;
2893 result += character;
2897 while (result.endsWith(
"\n\n"))
2898 result.truncate(result.size() - 1);
2899 while (result.startsWith(QLatin1Char(
'\n')))
2900 result = result.mid(1);
2907 int minIndent = INT_MAX;
2910 for (
const auto &character : str) {
2911 if (character ==
'\n') {
2914 if (character !=
' ' && column < minIndent)
2930 for (
const auto &character : str) {
2931 if (character == QLatin1Char(
'\n')) {
2935 if (column >= level)
2936 result += character;
2944
2945
2953
2954
2964
2965
2966
2967
2970 CodeMarker *marker = CodeMarker::markerForLanguage(language.toLower());
2972 if (!marker && !s_allowedLanguages.contains(language, Qt::CaseInsensitive))
2973 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(language));
2979
2980
2981
2982
2983
2984
2985
2986
2989 static auto take_while = [](QStringView input,
auto predicate) {
2990 QStringView::size_type end{0};
2992 while (end < input.size() &&
std::invoke(predicate, input[end]))
2995 return std::make_tuple(input.sliced(0, end), input.sliced(end));
2998 static auto peek = [](QStringView input, QChar c) {
2999 return !input.empty() && input.first() == c;
3002 static auto skip_one = [](QStringView input) {
3003 if (input.empty())
return std::make_tuple(QStringView{}, input);
3004 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
3007 static auto enclosed = [](QStringView input, QChar open, QChar close) {
3008 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
3010 auto [opened, without_open] = skip_one(input);
3011 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
3013 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
3015 auto [closed, without_close] = skip_one(remaining);
3017 return std::make_tuple(parsed.trimmed(), without_close);
3020 static auto one_of = [](
auto first,
auto second) {
3021 return [first, second](QStringView input) {
3022 auto [parsed, remaining] =
std::invoke(first, input);
3024 if (parsed.empty())
return std::invoke(second, input);
3025 else return std::make_tuple(parsed, remaining);
3029 static auto collect = [](QStringView input,
auto parser) {
3030 QStringList collected{};
3033 auto [parsed, remaining] =
std::invoke(parser, input);
3035 if (parsed.empty())
break;
3036 collected.append(parsed.toString());
3044 static auto spaces = [](QStringView input) {
3045 return take_while(input, [](QChar c){
return c.isSpace(); });
3048 static auto word = [](QStringView input) {
3049 return take_while(input, [](QChar c){
return !c.isSpace(); });
3052 static auto parse_argument = [](QStringView input) {
3053 auto [_, without_spaces] = spaces(input);
3056 [](QStringView input){
return enclosed(input,
'{',
'}'); },
3061 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
3065 QStringList segments = collect(atom->string(), parse_argument);
3067 QString categoryString;
3068 if (!segments.isEmpty())
3069 categoryString = segments.takeFirst();
3070 auto category = comparisonCategoryFromString(categoryString.toStdString());
3072 if (category == ComparisonCategory::None) {
3073 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
3074 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3078 if (segments.isEmpty()) {
3079 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3080 u"Provide at least one type name, or a list of types separated by spaces."_s);
3085 segments.removeDuplicates();
3086 atom->setString(segments.join(QLatin1Char(
';')));
3090 priv->m_metacommandsUsed.insert(cmd);
3093 const auto end{priv
->extra->m_comparesWithMap.cend()};
3094 priv
->extra->m_comparesWithMap.insert(end, category, text);
#define ATOM_FORMATTING_TELETYPE
#define ATOM_FORMATTING_UNDERLINE
#define ATOM_FORMATTING_NOTRANSLATE
#define ATOM_FORMATTING_SPAN
#define ATOM_FORMATTING_SUBSCRIPT
#define ATOM_FORMATTING_BOLD
#define ATOM_FORMATTING_TRADEMARK
#define ATOM_FORMATTING_ITALIC
#define ATOM_FORMATTING_LINK
#define ATOM_FORMATTING_SUPERSCRIPT
#define ATOM_FORMATTING_INDEX
#define ATOM_FORMATTING_UICONTROL
#define ATOM_FORMATTING_PARAMETER
The Atom class is the fundamental unit for representing documents internally.
AtomType type() const
Return the type of this atom.
AtomType
\value AnnotatedList \value AutoLink \value BaseName \value BriefLeft \value BriefRight \value C \val...
void chopString()
\also string()
virtual Atom::AtomType atomType() const
The Config class contains the configuration variables for controlling how qdoc produces documentation...
static QStringList s_allowedLanguages
static QStringList s_ignoreWords
void parse(const QString &source, DocPrivate *docPrivate, const QSet< QString > &metaCommandSet, const QSet< QString > &possibleTopics)
Parse the source string to build a Text data structure in docPrivate.
static int endCmdFor(int cmd)
static void initialize(const Config &config, FileResolver &file_resolver)
void addAlso(const Text &also)
CommandMap m_metaCommandMap
static void quoteFromFile(const Location &location, Quoter "er, ResolvedFile resolved_file, CodeMarker *marker=nullptr)
Encapsulate the logic that QDoc uses to find files whose path is provided by the user and that are re...
The Location class provides a way to mark a location in a file.
static Text subText(const Atom *begin, const Atom *end=nullptr)
Text splitAtFirst(Atom::AtomType start)
Splits the current Text from start to end into a new Text object.
Text & operator=(const Text &text)
#define CONFIG_IGNOREWORDS
#define CONFIG_CODELANGUAGES
#define CONFIG_QUOTINGINFORMATION
std::pair< QString, QString > ArgPair
static void warnAboutEmptyOrPreexistingTarget(const Location &location, const QString &duplicateDefinition, const QString &cmdString, const QString &previousDefinition)
bool is_formatting_command
static void processComparesWithCommand(DocPrivate *priv, const Location &location)
static QString cleanLink(const QString &link)
Returns a cleaned version of the given link text.
Combined button and popup list for selecting options.
QHash_QString_Macro macroHash
QHash_QString_int cmdHash
Simple structure used by the Doc and DocParser classes.