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)) {
730 appendAtom(
LinkAtom(p1, {}, location()));
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();
1915 if (target.endsWith(
"::"))
1921 target = getArgument();
1922 str = cleanLink(target);
1923 if (target == QLatin1String(
"and") || target == QLatin1String(
"."))
1929 also <<
LinkAtom(target, {}, location())
1938 skip_everything_until_newline();
1940 if (m_position < m_inputLength && m_input[m_position] ==
',') {
1943 skip_everything_until_newline();
1944 skipSpacesOrOneEndl();
1945 }
else if (m_position >= m_inputLength || m_input[m_position] !=
'\n') {
1946 location().warning(QStringLiteral(
"Missing comma in '\\%1'").arg(cmdName(
CMD_SA)));
1964 if (ch == QLatin1Char(
' ')) {
1965 if (!atom->string().endsWith(QLatin1Char(
' ')))
1966 atom->appendChar(QLatin1Char(
' '));
1968 atom->appendChar(ch);
1971void DocParser::appendWord(
const QString &word)
1980
1981
1982
1983
1984
1985
1986
1987
1988
1989
1990
1991void DocParser::appendEscapedIdentifier()
1993 Q_ASSERT(m_position < m_inputLength);
1994 Q_ASSERT(m_input.at(m_position) ==
'\\');
1999 while (m_position < m_inputLength && m_input.at(m_position).isLetterOrNumber()) {
2000 identifier += m_input.at(m_position);
2005 if (!identifier.isEmpty()) {
2010 if (m_position + 1 < m_inputLength && m_input.at(m_position) ==
'\r' && m_input.at(m_position + 1) ==
'\n')
2013 appendChar(QLatin1Char(
'\\'));
2017void DocParser::appendToCode(
const QString &markedCode)
2019 if (!isCode(m_lastAtom)) {
2023 m_lastAtom->concatenateString(markedCode);
2027
2028
2029
2030
2031
2032void DocParser::appendToCode(
const QString &markedCode,
Atom::
AtomType defaultType,
const QString &language)
2034 if (!isCode(m_lastAtom)) {
2035 appendAtom(
Atom(defaultType, markedCode, language));
2038 m_lastAtom->concatenateString(markedCode);
2044 if (m_paragraphState != OutsideParagraph)
2055 const bool inValueList = !m_openedLists.isEmpty()
2056 && m_openedLists.top().style() == OpenedList::Value;
2060 if (!inValueList || !isAdmonition)
2064 appendAtom(
Atom(leftType, string));
2065 m_indexStartedParagraph =
false;
2066 m_pendingParagraphLeftType = leftType;
2067 m_pendingParagraphRightType = rightType;
2068 m_pendingParagraphString = string;
2070 m_paragraphState = InSingleLineParagraph;
2072 m_paragraphState = InBraceDelimitedParagraph;
2074 m_paragraphState = InMultiLineParagraph;
2076 skipSpacesOrOneEndl();
2081 if (m_paragraphState == OutsideParagraph)
2083 if (!m_pendingFormats.isEmpty() || (m_paragraphState == InBraceDelimitedParagraph && m_braceDepth > 0)) {
2084 location().warning(u"Missing '}'"_s);
2085 m_pendingFormats.clear();
2101 appendAtom(Atom(m_pendingParagraphRightType, m_pendingParagraphString));
2104 m_paragraphState = OutsideParagraph;
2105 m_indexStartedParagraph =
false;
2106 m_pendingParagraphRightType =
Atom::Nop;
2107 m_pendingParagraphString.clear();
2113 if (m_openedLists.isEmpty()) {
2114 m_openedLists.push(OpenedList(OpenedList::Value));
2126 if (!m_openedLists.isEmpty() && (m_openedLists.top().style() == OpenedList::Value)) {
2131 m_openedLists.pop();
2137 if (m_inTableItem) {
2140 m_inTableItem =
false;
2142 if (m_inTableHeader) {
2144 m_inTableHeader =
false;
2148 m_inTableRow =
false;
2153
2154
2155
2156
2170 auto maybe_resolved_file{(*file_resolver).resolve(filename)};
2171 if (!maybe_resolved_file) {
2180 QString details =
std::transform_reduce(
2181 (*file_resolver).get_search_directories().cbegin(),
2182 (*file_resolver).get_search_directories().cend(),
2183 u"Searched directories:"_s,
2185 [](
const DirectoryPath &directory_path) -> QString {
return u' ' + directory_path.value(); }
2188 location().warning(u"Cannot find file to quote from: %1"_s.arg(filename), details);
2205 marker = CodeMarker::markerForFileName(QString{});
2206 m_quoter.quoteFromFile(filename, QString{}, marker->markedUpCode(QString{},
nullptr, location()));
2212
2213
2214
2215
2216
2217
2218
2219
2220
2221
2222
2223
2224
2225
2226
2227
2228
2229
2230
2231
2232
2233
2234
2235
2236bool DocParser::expandMacro(ArgumentParsingOptions options)
2238 Q_ASSERT(m_input[m_position].unicode() ==
'\\');
2240 if (options == ArgumentParsingOptions::Verbatim)
2244 qsizetype backslashPos = m_position++;
2245 while (m_position < m_input.size() && m_input[m_position].isLetterOrNumber())
2246 cmdStr += m_input[m_position++];
2248 m_endPosition = m_position;
2249 if (!cmdStr.isEmpty()) {
2250 if (s_utilities
.macroHash.contains(cmdStr)) {
2252 if (!macro.m_defaultDef.isEmpty()) {
2253 QString expanded = expandMacroToString(cmdStr, macro);
2254 m_input.replace(backslashPos, m_position - backslashPos, expanded);
2255 m_inputLength = m_input.size();
2256 m_position = backslashPos;
2259 location().warning(
"Macro '%1' does not have a default definition"_L1.arg(cmdStr));
2263 m_position = backslashPos;
2264 if (options != ArgumentParsingOptions::MacroArguments
2266 location().warning(
"Unknown macro '%1'"_L1.arg(cmdStr));
2270 }
else if (m_input[m_position].isSpace()) {
2272 }
else if (m_input[m_position].unicode() ==
'\\') {
2274 m_input.remove(m_position--, 1);
2280void DocParser::expandMacro(
const QString &def,
const QStringList &args)
2282 if (args.isEmpty()) {
2287 for (
int j = 0; j < def.size(); ++j) {
2288 if (
int paramNo = def[j].unicode(); paramNo >= 1 && paramNo <= args.length()) {
2289 if (!rawString.isEmpty()) {
2295 rawString += def[j];
2298 if (!rawString.isEmpty())
2303QString
DocParser::expandMacroToString(
const QString &name,
const Macro ¯o)
2305 const QString &def{macro.m_defaultDef};
2309 rawString = macro.m_defaultDef;
2311 QStringList args{getMacroArguments(name, macro)};
2313 for (
int j = 0; j < def.size(); ++j) {
2314 int paramNo = def[j].unicode();
2315 rawString += (paramNo >= 1 && paramNo <= args.length()) ? args[paramNo - 1] : def[j];
2318 QString matchExpr{macro.m_otherDefs.value(
"match")};
2319 if (matchExpr.isEmpty())
2323 QRegularExpression re(matchExpr);
2324 int capStart = (re.captureCount() > 0) ? 1 : 0;
2326 QRegularExpressionMatch match;
2327 while ((match = re.match(rawString, i)).hasMatch()) {
2328 for (
int c = capStart; c <= re.captureCount(); ++c)
2329 result += match.captured(c);
2330 i = match.capturedEnd();
2338 QString name = getOptionalArgument();
2340 if (name ==
"section1") {
2342 }
else if (name ==
"section2") {
2344 }
else if (name ==
"section3") {
2346 }
else if (name ==
"section4") {
2348 }
else if (name.isEmpty()) {
2351 location().warning(QStringLiteral(
"Invalid section '%1'").arg(name));
2357
2358
2359
2360
2361
2362
2363
2364
2365
2366QString
DocParser::getBracedArgument(ArgumentParsingOptions options)
2370 if (m_position < m_input.size() && m_input[m_position] ==
'{') {
2372 while (m_position < m_input.size() && delimDepth >= 0) {
2373 switch (m_input[m_position].unicode()) {
2376 arg += QLatin1Char(
'{');
2381 if (delimDepth >= 0)
2382 arg += QLatin1Char(
'}');
2386 if (!expandMacro(options))
2387 arg += m_input[m_position++];
2390 if (m_input[m_position].isSpace() && options != ArgumentParsingOptions::Verbatim)
2393 arg += m_input[m_position];
2398 location().warning(QStringLiteral(
"Missing '}'"));
2400 m_endPosition = m_position;
2405
2406
2407
2408
2409
2410
2411
2412
2413
2414
2415
2416
2417
2418
2419
2420QString
DocParser::getArgument(ArgumentParsingOptions options)
2422 skipSpacesOrOneEndl();
2425 qsizetype startPos = m_position;
2426 QString arg = getBracedArgument(options);
2427 if (arg.isEmpty()) {
2428 while ((m_position < m_input.size())
2429 && ((delimDepth > 0) || ((delimDepth == 0) && !m_input[m_position].isSpace()))) {
2430 switch (m_input[m_position].unicode()) {
2435 arg += m_input[m_position];
2442 if (m_position == startPos || delimDepth >= 0) {
2443 arg += m_input[m_position];
2448 if (!expandMacro(options))
2449 arg += m_input[m_position++];
2452 arg += m_input[m_position];
2456 m_endPosition = m_position;
2457 if ((arg.size() > 1) && (QString(
".,:;!?").indexOf(m_input[m_position - 1]) != -1)
2458 && !arg.endsWith(
"...")) {
2459 arg.truncate(arg.size() - 1);
2462 if (arg.size() > 2 && m_input.mid(m_position - 2, 2) ==
"'s") {
2463 arg.truncate(arg.size() - 2);
2467 return arg.simplified();
2471
2472
2473
2474
2475
2476QString
DocParser::getBracketedArgument()
2480 skipSpacesOrOneEndl();
2481 if (m_position < m_input.size() && m_input[m_position] ==
'[') {
2483 while (m_position < m_input.size() && delimDepth >= 0) {
2484 switch (m_input[m_position].unicode()) {
2487 arg += QLatin1Char(
'[');
2492 if (delimDepth >= 0)
2493 arg += QLatin1Char(
']');
2497 arg += m_input[m_position];
2501 arg += m_input[m_position];
2506 location().warning(QStringLiteral(
"Missing ']'"));
2512
2513
2514
2515
2516
2517
2518
2522 if (isLeftBracketAhead(0)) {
2523 value = getBracketedArgument();
2524 *marker = markerForLanguage(value);
2529 return value.toLower();
2533
2534
2535
2536
2537
2538QStringList
DocParser::getMacroArguments(
const QString &name,
const Macro ¯o)
2542 if (macro
.numParams == 1 || isLeftBraceAhead()) {
2543 args << getArgument(ArgumentParsingOptions::MacroArguments);
2545 location().warning(QStringLiteral(
"Macro '\\%1' invoked with too few"
2546 " arguments (expected %2, got %3)")
2557
2558
2559
2560
2563 skipSpacesOrOneEndl();
2564 const bool nextIsCommand = m_position + 1 < m_input.size() && m_input[m_position] ==
'\\'
2565 && m_input[m_position + 1].isLetterOrNumber();
2566 const bool nextIsComment = m_position + 2 < m_input.size() && m_input[m_position] ==
'/'
2567 && m_input[m_position + 1] ==
'/' && m_input[m_position + 2] ==
'!';
2568 if (nextIsCommand || nextIsComment) {
2571 return getArgument();
2576
2577
2578
2579
2580
2581
2582
2583
2584
2585
2586
2587
2588
2589
2592 auto atLineEnd = [
this]() ->
bool {
2593 return m_position >= m_inputLength || m_input[m_position] ==
'\n'
2594 || m_input[m_position] ==
'\r';
2597 auto lineHasTrailingBackslash = [
this, &atLineEnd](
bool trailingBackslash) ->
bool {
2598 while (!atLineEnd()) {
2599 if (m_input[m_position] ==
'\\' && !trailingBackslash) {
2600 trailingBackslash =
true;
2604 trailingBackslash =
false;
2608 return trailingBackslash;
2611 QString rest_of_line;
2613 bool trailing_backslash{
false };
2614 bool return_simplified_string{
false };
2616 for (qsizetype start_position = m_position; m_position < m_inputLength; ++m_position) {
2617 trailing_backslash = lineHasTrailingBackslash(trailing_backslash);
2619 if (!rest_of_line.isEmpty())
2620 rest_of_line += QLatin1Char(
' ');
2621 rest_of_line += m_input.sliced(start_position, m_position - start_position);
2623 if (trailing_backslash) {
2624 rest_of_line.truncate(rest_of_line.lastIndexOf(
'\\'));
2625 return_simplified_string =
true;
2628 if (m_position < m_inputLength && m_input[m_position] ==
'\r')
2630 if (m_position < m_inputLength && m_input[m_position] ==
'\n')
2633 if (!trailing_backslash)
2636 while (m_position < m_inputLength
2637 && (m_input[m_position] ==
'\r' || m_input[m_position] ==
'\n'))
2640 start_position = m_position;
2643 if (return_simplified_string)
2644 return rest_of_line.simplified();
2646 return rest_of_line.trimmed();
2650
2651
2652
2653
2654QString
DocParser::getMetaCommandArgument(
const QString &cmdStr)
2658 qsizetype begin = m_position;
2661 while (m_position < m_input.size() && (m_input[m_position] !=
'\n' || parenDepth > 0)) {
2662 if (m_input.at(m_position) ==
'(')
2664 else if (m_input.at(m_position) ==
')')
2666 else if (m_input.at(m_position) ==
'\\' && expandMacro(ArgumentParsingOptions::Default))
2670 if (m_position == m_input.size() && parenDepth > 0) {
2672 location().warning(QStringLiteral(
"Unbalanced parentheses in '%1'").arg(cmdStr));
2675 QString t = m_input.mid(begin, m_position - begin).simplified();
2683 QRegularExpression rx(
"\\\\" + cmdName(endCmd) +
"\\b");
2685 auto match = rx.match(m_input, m_position);
2687 if (!match.hasMatch()) {
2688 location().warning(QStringLiteral(
"Missing '\\%1'").arg(cmdName(endCmd)));
2689 m_position = m_input.size();
2691 qsizetype end = match.capturedStart();
2692 t = m_input.mid(m_position, end - m_position);
2693 m_position = match.capturedEnd();
2698void DocParser::expandArgumentsInString(QString &str,
const QStringList &args)
2705 while (j < str.size()) {
2706 if (str[j] ==
'\\' && j < str.size() - 1 && (paramNo = str[j + 1].digitValue()) >= 1
2707 && paramNo <= args.size()) {
2708 const QString &r = args[paramNo - 1];
2709 str.replace(j, 2, r);
2710 j += qMin(1, r.size());
2718
2719
2720
2721
2722
2723
2726 QString code = untabifyEtc(getUntilEnd(cmd));
2727 expandArgumentsInString(code, argStr.split(
" ", Qt::SkipEmptyParts));
2729 int indent = indentLevel(code);
2730 code = dedent(indent, code);
2733 if (!marker && !m_private->m_topics.isEmpty()
2734 && m_private->m_topics[0].m_topic.startsWith(
"qml")) {
2735 auto qmlMarker = CodeMarker::markerForLanguage(
"QML");
2736 marker = (qmlMarker && qmlMarker->recognizeCode(code)) ? qmlMarker :
nullptr;
2738 if (marker ==
nullptr)
2739 marker = CodeMarker::markerForCode(code);
2740 return marker->markedUpCode(code,
nullptr, location());
2745 qsizetype i = m_position;
2747 while (i < m_inputLength && m_input[i].isSpace()) {
2748 if (m_input[i] ==
'\n')
2758 qsizetype i = m_position;
2760 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2762 if (m_input[i] ==
'\n')
2766 return numEndl < 2 && i < m_inputLength && m_input[i] ==
'{';
2769bool DocParser::isLeftBracketAhead(
int maxNewlines)
2772 qsizetype i = m_position;
2774 while (i < m_inputLength && m_input[i].isSpace() && numEndl < 2) {
2776 if (m_input[i] ==
'\n')
2780 return numEndl <= maxNewlines && i < m_inputLength && m_input[i] ==
'[';
2784
2785
2788 while ((m_position < m_input.size()) && m_input[m_position].isSpace()
2789 && (m_input[m_position].unicode() !=
'\n'))
2794
2795
2798 qsizetype firstEndl = -1;
2799 while (m_position < m_input.size() && m_input[m_position].isSpace()) {
2800 QChar ch = m_input[m_position];
2802 if (firstEndl == -1) {
2803 firstEndl = m_position;
2805 m_position = firstEndl;
2815 while (m_position < m_inputLength && m_input[m_position].isSpace())
2819void DocParser::skipToNextPreprocessorCommand()
2821 QRegularExpression rx(
"\\\\(?:" + cmdName(CMD_IF) + QLatin1Char(
'|') + cmdName(CMD_ELSE)
2822 + QLatin1Char(
'|') + cmdName(CMD_ENDIF) +
")\\b");
2823 auto match = rx.match(m_input, m_position + 1);
2825 if (!match.hasMatch())
2826 m_position = m_input.size();
2828 m_position = match.capturedStart();
2881 return cmds[cmd]
.name;
2886 return cmdName(endCmdFor(cmd));
2892 result.reserve(str.size());
2895 for (
const auto &character : str) {
2896 if (character == QLatin1Char(
'\r'))
2898 if (character == QLatin1Char(
'\t')) {
2899 result += &
" "[column % s_tabSize];
2900 column = ((column / s_tabSize) + 1) * s_tabSize;
2903 if (character == QLatin1Char(
'\n')) {
2904 while (result.endsWith(QLatin1Char(
' ')))
2906 result += character;
2910 result += character;
2914 while (result.endsWith(
"\n\n"))
2915 result.truncate(result.size() - 1);
2916 while (result.startsWith(QLatin1Char(
'\n')))
2917 result = result.mid(1);
2924 int minIndent = INT_MAX;
2927 for (
const auto &character : str) {
2928 if (character ==
'\n') {
2931 if (character !=
' ' && column < minIndent)
2947 for (
const auto &character : str) {
2948 if (character == QLatin1Char(
'\n')) {
2952 if (column >= level)
2953 result += character;
2961
2962
2970
2971
2981
2982
2983
2984
2987 CodeMarker *marker = CodeMarker::markerForLanguage(language.toLower());
2989 if (!marker && !s_allowedLanguages.contains(language, Qt::CaseInsensitive))
2990 location().warning(QStringLiteral(
"Unrecognized markup language '%1'").arg(language));
2996
2997
2998
2999
3000
3001
3002
3003
3006 static auto take_while = [](QStringView input,
auto predicate) {
3007 QStringView::size_type end{0};
3009 while (end < input.size() &&
std::invoke(predicate, input[end]))
3012 return std::make_tuple(input.sliced(0, end), input.sliced(end));
3015 static auto peek = [](QStringView input, QChar c) {
3016 return !input.empty() && input.first() == c;
3019 static auto skip_one = [](QStringView input) {
3020 if (input.empty())
return std::make_tuple(QStringView{}, input);
3021 else return std::make_tuple(input.sliced(0, 1), input.sliced(1));
3024 static auto enclosed = [](QStringView input, QChar open, QChar close) {
3025 if (!peek(input, open))
return std::make_tuple(QStringView{}, input);
3027 auto [opened, without_open] = skip_one(input);
3028 auto [parsed, remaining] = take_while(without_open, [close](QChar c){
return c != close; });
3030 if (remaining.empty())
return std::make_tuple(QStringView{}, input);
3032 auto [closed, without_close] = skip_one(remaining);
3034 return std::make_tuple(parsed.trimmed(), without_close);
3037 static auto one_of = [](
auto first,
auto second) {
3038 return [first, second](QStringView input) {
3039 auto [parsed, remaining] =
std::invoke(first, input);
3041 if (parsed.empty())
return std::invoke(second, input);
3042 else return std::make_tuple(parsed, remaining);
3046 static auto collect = [](QStringView input,
auto parser) {
3047 QStringList collected{};
3050 auto [parsed, remaining] =
std::invoke(parser, input);
3052 if (parsed.empty())
break;
3053 collected.append(parsed.toString());
3061 static auto spaces = [](QStringView input) {
3062 return take_while(input, [](QChar c){
return c.isSpace(); });
3065 static auto word = [](QStringView input) {
3066 return take_while(input, [](QChar c){
return !c.isSpace(); });
3069 static auto parse_argument = [](QStringView input) {
3070 auto [_, without_spaces] = spaces(input);
3073 [](QStringView input){
return enclosed(input,
'{',
'}'); },
3078 const QString cmd{DocParser::cmdName(CMD_COMPARESWITH)};
3082 QStringList segments = collect(atom->string(), parse_argument);
3084 QString categoryString;
3085 if (!segments.isEmpty())
3086 categoryString = segments.takeFirst();
3087 auto category = comparisonCategoryFromString(categoryString.toStdString());
3089 if (category == ComparisonCategory::None) {
3090 location.warning(u"Invalid argument to \\%1 command: `%2`"_s.arg(cmd, categoryString),
3091 u"Valid arguments are `strong`, `weak`, `partial`, or `equality`."_s);
3095 if (segments.isEmpty()) {
3096 location.warning(u"Missing argument to \\%1 command."_s.arg(cmd),
3097 u"Provide at least one type name, or a list of types separated by spaces."_s);
3102 segments.removeDuplicates();
3103 atom->setString(segments.join(QLatin1Char(
';')));
3107 priv->m_metacommandsUsed.insert(cmd);
3110 const auto end{priv
->extra->m_comparesWithMap.cend()};
3111 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.